No changes, just remove an unused line of code.
[wxWidgets.git] / src / richtext / richtextbuffer.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/richtext/richtextbuffer.cpp
3 // Purpose: Buffer for wxRichTextCtrl
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 2005-09-30
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #if wxUSE_RICHTEXT
20
21 #include "wx/richtext/richtextbuffer.h"
22
23 #ifndef WX_PRECOMP
24 #include "wx/dc.h"
25 #include "wx/intl.h"
26 #include "wx/log.h"
27 #include "wx/dataobj.h"
28 #include "wx/module.h"
29 #endif
30
31 #include "wx/settings.h"
32 #include "wx/filename.h"
33 #include "wx/clipbrd.h"
34 #include "wx/wfstream.h"
35 #include "wx/mstream.h"
36 #include "wx/sstream.h"
37 #include "wx/textfile.h"
38 #include "wx/hashmap.h"
39 #include "wx/dynarray.h"
40
41 #include "wx/richtext/richtextctrl.h"
42 #include "wx/richtext/richtextstyles.h"
43 #include "wx/richtext/richtextimagedlg.h"
44 #include "wx/richtext/richtextsizepage.h"
45 #include "wx/richtext/richtextxml.h"
46
47 #include "wx/listimpl.cpp"
48 #include "wx/arrimpl.cpp"
49
50 WX_DEFINE_LIST(wxRichTextObjectList)
51 WX_DEFINE_LIST(wxRichTextLineList)
52
53 // Switch off if the platform doesn't like it for some reason
54 #define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
55
56 // Use GetPartialTextExtents for platforms that support it natively
57 #define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
58
59 const wxChar wxRichTextLineBreakChar = (wxChar) 29;
60
61 // Helper classes for floating layout
62 struct wxRichTextFloatRectMap
63 {
64 wxRichTextFloatRectMap(int sY, int eY, int w, wxRichTextObject* obj)
65 {
66 startY = sY;
67 endY = eY;
68 width = w;
69 anchor = obj;
70 }
71
72 int startY, endY;
73 int width;
74 wxRichTextObject* anchor;
75 };
76
77 WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap*, wxRichTextFloatRectMapArray);
78
79 int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap* r1, wxRichTextFloatRectMap* r2)
80 {
81 return r1->startY - r2->startY;
82 }
83
84 class wxRichTextFloatCollector
85 {
86 public:
87 wxRichTextFloatCollector(const wxRect& availableRect);
88 ~wxRichTextFloatCollector();
89
90 // Collect the floating objects info in the given paragraph
91 void CollectFloat(wxRichTextParagraph* para);
92 void CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating);
93
94 // Return the last paragraph we collected
95 wxRichTextParagraph* LastParagraph();
96
97 // Given the start y position and the height of the line,
98 // find out how wide the line can be
99 wxRect GetAvailableRect(int startY, int endY);
100
101 // Given a floating box, find its fit position
102 int GetFitPosition(int direction, int start, int height) const;
103 int GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const;
104
105 // Find the last y position
106 int GetLastRectBottom();
107
108 // Draw the floats inside a rect
109 void Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
110
111 // HitTest the floats
112 int HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
113
114 // Get floating object count
115 int GetFloatingObjectCount() const { return m_left.GetCount() + m_right.GetCount(); }
116
117 // Get floating objects
118 bool GetFloatingObjects(wxRichTextObjectList& objects) const;
119
120 // Delete a float
121 bool DeleteFloat(wxRichTextObject* obj);
122
123 // Do we have this float already?
124 bool HasFloat(wxRichTextObject* obj);
125
126 bool HasFloats() const { return m_left.GetCount() >0 || m_right.GetCount() > 0; }
127
128 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point);
129
130 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY);
131
132 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array);
133
134 static void DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
135
136 static int HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
137
138 private:
139 wxRichTextFloatRectMapArray m_left;
140 wxRichTextFloatRectMapArray m_right;
141 //int m_width;
142 wxRect m_availableRect;
143 wxRichTextParagraph* m_para;
144 };
145
146 // Delete a float
147 bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject* obj)
148 {
149 size_t i;
150 for (i = 0; i < m_left.GetCount(); i++)
151 {
152 if (m_left[i]->anchor == obj)
153 {
154 m_left.RemoveAt(i);
155 return true;
156 }
157 }
158 for (i = 0; i < m_right.GetCount(); i++)
159 {
160 if (m_right[i]->anchor == obj)
161 {
162 m_right.RemoveAt(i);
163 return true;
164 }
165 }
166 return false;
167 }
168
169 // Do we have this float already?
170 bool wxRichTextFloatCollector::HasFloat(wxRichTextObject* obj)
171 {
172 size_t i;
173 for (i = 0; i < m_left.GetCount(); i++)
174 {
175 if (m_left[i]->anchor == obj)
176 {
177 return true;
178 }
179 }
180 for (i = 0; i < m_right.GetCount(); i++)
181 {
182 if (m_right[i]->anchor == obj)
183 {
184 return true;
185 }
186 }
187 return false;
188 }
189
190 // Get floating objects
191 bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList& objects) const
192 {
193 size_t i;
194 for (i = 0; i < m_left.GetCount(); i++)
195 objects.Append(m_left[i]->anchor);
196 for (i = 0; i < m_right.GetCount(); i++)
197 objects.Append(m_right[i]->anchor);
198 return true;
199 }
200
201
202 /*
203 * Binary search helper function
204 * The argument point is the Y coordinate, and this fuction
205 * always return the floating rect that contain this coordinate
206 * or under this coordinate.
207 */
208 int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point)
209 {
210 int end = array.GetCount() - 1;
211 int start = 0;
212 int ret = 0;
213
214 wxASSERT(end >= 0);
215
216 while (true)
217 {
218 if (start > end)
219 {
220 break;
221 }
222
223 int mid = (start + end) / 2;
224 if (array[mid]->startY <= point && array[mid]->endY >= point)
225 return mid;
226 else if (array[mid]->startY > point)
227 {
228 end = mid - 1;
229 ret = mid;
230 }
231 else if (array[mid]->endY < point)
232 {
233 start = mid + 1;
234 ret = start;
235 }
236 }
237
238 return ret;
239 }
240
241 int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY)
242 {
243 int ret = 0;
244 int len = array.GetCount();
245
246 wxASSERT(index >= 0 && index < len);
247
248 if (array[index]->startY < startY && array[index]->endY > startY)
249 ret = ret < array[index]->width ? array[index]->width : ret;
250 while (index < len && array[index]->startY <= endY)
251 {
252 ret = ret < array[index]->width ? array[index]->width : ret;
253 index++;
254 }
255
256 return ret;
257 }
258
259 wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect& rect) : m_left(wxRichTextFloatRectMapCmp), m_right(wxRichTextFloatRectMapCmp)
260 {
261 m_availableRect = rect;
262 m_para = NULL;
263 }
264
265 void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array)
266 {
267 int len = array.GetCount();
268 for (int i = 0; i < len; i++)
269 delete array[i];
270 }
271
272 wxRichTextFloatCollector::~wxRichTextFloatCollector()
273 {
274 FreeFloatRectMapArray(m_left);
275 FreeFloatRectMapArray(m_right);
276 }
277
278 int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const
279 {
280 if (array.GetCount() == 0)
281 return start;
282
283 int i = SearchAdjacentRect(array, start);
284 int last = start;
285 while (i < (int) array.GetCount())
286 {
287 if (array[i]->startY - last >= height)
288 return last + 1;
289 last = array[i]->endY;
290 i++;
291 }
292
293 return last + 1;
294 }
295
296 int wxRichTextFloatCollector::GetFitPosition(int direction, int start, int height) const
297 {
298 if (direction == wxTEXT_BOX_ATTR_FLOAT_LEFT)
299 return GetFitPosition(m_left, start, height);
300 else if (direction == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
301 return GetFitPosition(m_right, start, height);
302 else
303 {
304 wxASSERT("Never should be here");
305 return start;
306 }
307 }
308
309 // Adds a floating image to the float collector.
310 // The actual positioning is done by wxRichTextParagraph::LayoutFloat.
311 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating)
312 {
313 int direction = floating->GetFloatDirection();
314
315 wxPoint pos = floating->GetPosition();
316 wxSize size = floating->GetCachedSize();
317 wxRichTextFloatRectMap *map = new wxRichTextFloatRectMap(pos.y, pos.y + size.y, size.x, floating);
318 switch (direction)
319 {
320 case wxTEXT_BOX_ATTR_FLOAT_NONE:
321 delete map;
322 break;
323 case wxTEXT_BOX_ATTR_FLOAT_LEFT:
324 // Just a not-enough simple assertion
325 wxASSERT (m_left.Index(map) == wxNOT_FOUND);
326 m_left.Add(map);
327 break;
328 case wxTEXT_BOX_ATTR_FLOAT_RIGHT:
329 wxASSERT (m_right.Index(map) == wxNOT_FOUND);
330 m_right.Add(map);
331 break;
332 default:
333 delete map;
334 wxASSERT("Unrecognised float attribute.");
335 }
336
337 m_para = para;
338 }
339
340 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para)
341 {
342 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
343 while (node)
344 {
345 wxRichTextObject* floating = node->GetData();
346
347 if (floating->IsFloating())
348 {
349 CollectFloat(para, floating);
350 }
351
352 node = node->GetNext();
353 }
354
355 m_para = para;
356 }
357
358 wxRichTextParagraph* wxRichTextFloatCollector::LastParagraph()
359 {
360 return m_para;
361 }
362
363 wxRect wxRichTextFloatCollector::GetAvailableRect(int startY, int endY)
364 {
365 int widthLeft = 0, widthRight = 0;
366 if (m_left.GetCount() != 0)
367 {
368 int i = SearchAdjacentRect(m_left, startY);
369 if (i < (int) m_left.GetCount())
370 widthLeft = GetWidthFromFloatRect(m_left, i, startY, endY);
371 }
372 if (m_right.GetCount() != 0)
373 {
374 int j = SearchAdjacentRect(m_right, startY);
375 if (j < (int) m_right.GetCount())
376 widthRight = GetWidthFromFloatRect(m_right, j, startY, endY);
377 }
378
379 // TODO: actually we want to use the actual image positions to find the
380 // available remaining space, since the image might not be right up against
381 // the left or right edge of the container.
382 return wxRect(widthLeft + m_availableRect.x, 0, m_availableRect.width - widthLeft - widthRight, 0);
383 }
384
385 int wxRichTextFloatCollector::GetLastRectBottom()
386 {
387 int ret = 0;
388 int len = m_left.GetCount();
389 if (len) {
390 ret = ret > m_left[len-1]->endY ? ret : m_left[len-1]->endY;
391 }
392 len = m_right.GetCount();
393 if (len) {
394 ret = ret > m_right[len-1]->endY ? ret : m_right[len-1]->endY;
395 }
396
397 return ret;
398 }
399
400 void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
401 {
402 int start = rect.y;
403 int end = rect.y + rect.height;
404 int i, j;
405 i = SearchAdjacentRect(array, start);
406 if (i < 0 || i >= (int) array.GetCount())
407 return;
408 j = SearchAdjacentRect(array, end);
409 if (j < 0 || j >= (int) array.GetCount())
410 j = array.GetCount() - 1;
411 while (i <= j)
412 {
413 wxRichTextObject* obj = array[i]->anchor;
414 wxRichTextRange r = obj->GetRange();
415 obj->Draw(dc, context, r, selection, wxRect(obj->GetPosition(), obj->GetCachedSize()), descent, style);
416 i++;
417 }
418 }
419
420 void wxRichTextFloatCollector::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
421 {
422 if (m_left.GetCount() > 0)
423 DrawFloat(m_left, dc, context, range, selection, rect, descent, style);
424 if (m_right.GetCount() > 0)
425 DrawFloat(m_right, dc, context, range, selection, rect, descent, style);
426 }
427
428 int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int WXUNUSED(flags))
429 {
430 int i;
431 if (array.GetCount() == 0)
432 return wxRICHTEXT_HITTEST_NONE;
433 i = SearchAdjacentRect(array, pt.y);
434 if (i < 0 || i >= (int) array.GetCount())
435 return wxRICHTEXT_HITTEST_NONE;
436 if (!array[i]->anchor->IsShown())
437 return wxRICHTEXT_HITTEST_NONE;
438
439 wxPoint point = array[i]->anchor->GetPosition();
440 wxSize size = array[i]->anchor->GetCachedSize();
441 if (point.x <= pt.x && point.x + size.x >= pt.x
442 && point.y <= pt.y && point.y + size.y >= pt.y)
443 {
444 textPosition = array[i]->anchor->GetRange().GetStart();
445 * obj = array[i]->anchor;
446 if (pt.x > (pt.x + pt.x + size.x) / 2)
447 return wxRICHTEXT_HITTEST_BEFORE;
448 else
449 return wxRICHTEXT_HITTEST_AFTER;
450 }
451
452 return wxRICHTEXT_HITTEST_NONE;
453 }
454
455 int wxRichTextFloatCollector::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags)
456 {
457 int ret = HitTestFloat(m_left, dc, context, pt, textPosition, obj, flags);
458 if (ret == wxRICHTEXT_HITTEST_NONE)
459 {
460 ret = HitTestFloat(m_right, dc, context, pt, textPosition, obj, flags);
461 }
462 return ret;
463 }
464
465 // Helpers for efficiency
466 inline void wxCheckSetFont(wxDC& dc, const wxFont& font)
467 {
468 dc.SetFont(font);
469 }
470
471 inline void wxCheckSetPen(wxDC& dc, const wxPen& pen)
472 {
473 const wxPen& pen1 = dc.GetPen();
474 if (pen1.IsOk() && pen.IsOk())
475 {
476 if (pen1.GetWidth() == pen.GetWidth() &&
477 pen1.GetStyle() == pen.GetStyle() &&
478 pen1.GetColour() == pen.GetColour())
479 return;
480 }
481 dc.SetPen(pen);
482 }
483
484 inline void wxCheckSetBrush(wxDC& dc, const wxBrush& brush)
485 {
486 const wxBrush& brush1 = dc.GetBrush();
487 if (brush1.IsOk() && brush.IsOk())
488 {
489 if (brush1.GetStyle() == brush.GetStyle() &&
490 brush1.GetColour() == brush.GetColour())
491 return;
492 }
493 dc.SetBrush(brush);
494 }
495
496 /*!
497 * wxRichTextObject
498 * This is the base for drawable objects.
499 */
500
501 IMPLEMENT_CLASS(wxRichTextObject, wxObject)
502
503 wxRichTextObject::wxRichTextObject(wxRichTextObject* parent)
504 {
505 m_refCount = 1;
506 m_parent = parent;
507 m_descent = 0;
508 m_show = true;
509 }
510
511 wxRichTextObject::~wxRichTextObject()
512 {
513 }
514
515 void wxRichTextObject::Dereference()
516 {
517 m_refCount --;
518 if (m_refCount <= 0)
519 delete this;
520 }
521
522 /// Copy
523 void wxRichTextObject::Copy(const wxRichTextObject& obj)
524 {
525 m_size = obj.m_size;
526 m_maxSize = obj.m_maxSize;
527 m_minSize = obj.m_minSize;
528 m_pos = obj.m_pos;
529 m_range = obj.m_range;
530 m_ownRange = obj.m_ownRange;
531 m_attributes = obj.m_attributes;
532 m_properties = obj.m_properties;
533 m_descent = obj.m_descent;
534 m_show = obj.m_show;
535 }
536
537 // Get/set the top-level container of this object.
538 wxRichTextParagraphLayoutBox* wxRichTextObject::GetContainer() const
539 {
540 const wxRichTextObject* p = this;
541 while (p)
542 {
543 if (p->IsTopLevel())
544 {
545 return wxDynamicCast(p, wxRichTextParagraphLayoutBox);
546 }
547 p = p->GetParent();
548 }
549 return NULL;
550 }
551
552 void wxRichTextObject::SetMargins(int margin)
553 {
554 SetMargins(margin, margin, margin, margin);
555 }
556
557 void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin, int bottomMargin)
558 {
559 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin, wxTEXT_ATTR_UNITS_PIXELS);
560 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin, wxTEXT_ATTR_UNITS_PIXELS);
561 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin, wxTEXT_ATTR_UNITS_PIXELS);
562 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin, wxTEXT_ATTR_UNITS_PIXELS);
563 }
564
565 int wxRichTextObject::GetLeftMargin() const
566 {
567 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
568 }
569
570 int wxRichTextObject::GetRightMargin() const
571 {
572 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
573 }
574
575 int wxRichTextObject::GetTopMargin() const
576 {
577 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
578 }
579
580 int wxRichTextObject::GetBottomMargin() const
581 {
582 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
583 }
584
585 // Calculate the available content space in the given rectangle, given the
586 // margins, border and padding specified in the object's attributes.
587 wxRect wxRichTextObject::GetAvailableContentArea(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& outerRect) const
588 {
589 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
590 marginRect = outerRect;
591 wxRichTextAttr attr(GetAttributes());
592 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
593 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
594 return contentRect;
595 }
596
597 // Invalidate the buffer. With no argument, invalidates whole buffer.
598 void wxRichTextObject::Invalidate(const wxRichTextRange& invalidRange)
599 {
600 if (invalidRange != wxRICHTEXT_NONE)
601 {
602 // If this is a floating object, size may not be recalculated
603 // after floats have been collected in an early stage of Layout.
604 // So avoid resetting the cache for floating objects during layout.
605 if (!IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode())
606 SetCachedSize(wxDefaultSize);
607 SetMaxSize(wxDefaultSize);
608 SetMinSize(wxDefaultSize);
609 }
610 }
611
612 // Convert units in tenths of a millimetre to device units
613 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units) const
614 {
615 // Unscale
616 double scale = 1.0;
617 if (GetBuffer())
618 scale = GetBuffer()->GetScale() / GetBuffer()->GetDimensionScale();
619 int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units, scale);
620
621 return p;
622 }
623
624 // Convert units in tenths of a millimetre to device units
625 int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units, double scale)
626 {
627 // There are ppi pixels in 254.1 "1/10 mm"
628
629 double pixels = ((double) units * (double)ppi) / 254.1;
630 if (scale != 1.0)
631 pixels /= scale;
632
633 // If the result is very small, make it at least one pixel in size.
634 if (pixels == 0 && units > 0)
635 pixels = 1;
636
637 return (int) pixels;
638 }
639
640 // Convert units in pixels to tenths of a millimetre
641 int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC& dc, int pixels) const
642 {
643 int p = pixels;
644 double scale = 1.0;
645 if (GetBuffer())
646 scale = GetBuffer()->GetScale();
647
648 return ConvertPixelsToTenthsMM(dc.GetPPI().x, p, scale);
649 }
650
651 int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi, int pixels, double scale)
652 {
653 // There are ppi pixels in 254.1 "1/10 mm"
654
655 double p = double(pixels);
656
657 if (scale != 1.0)
658 p *= scale;
659
660 int units = int( p * 254.1 / (double) ppi );
661 return units;
662 }
663
664 // Draw the borders and background for the given rectangle and attributes.
665 // Width and height are taken to be the outer margin size, not the content.
666 bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, const wxRect& boxRect, int flags)
667 {
668 // Assume boxRect is the area around the content
669 wxRect marginRect = boxRect;
670 wxRect contentRect, borderRect, paddingRect, outlineRect;
671
672 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
673
674 // Margin is transparent. Draw background from margin.
675 if (attr.HasBackgroundColour() || (flags & wxRICHTEXT_DRAW_SELECTED))
676 {
677 wxColour colour;
678 if (flags & wxRICHTEXT_DRAW_SELECTED)
679 {
680 // TODO: get selection colour from control?
681 colour = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
682 }
683 else
684 colour = attr.GetBackgroundColour();
685
686 wxPen pen(colour);
687 wxBrush brush(colour);
688
689 dc.SetPen(pen);
690 dc.SetBrush(brush);
691 dc.DrawRectangle(borderRect);
692 }
693
694 if (flags & wxRICHTEXT_DRAW_GUIDELINES)
695 {
696 wxRichTextAttr editBorderAttr = attr;
697 // TODO: make guideline colour configurable
698 editBorderAttr.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY);
699 editBorderAttr.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS);
700 editBorderAttr.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID);
701
702 DrawBorder(dc, buffer, editBorderAttr.GetTextBoxAttr().GetBorder(), borderRect, flags);
703 }
704
705 if (attr.GetTextBoxAttr().GetBorder().IsValid())
706 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetBorder(), borderRect);
707
708 if (attr.GetTextBoxAttr().GetOutline().IsValid())
709 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetOutline(), outlineRect);
710
711 return true;
712 }
713
714 // Draw a border
715 bool wxRichTextObject::DrawBorder(wxDC& dc, wxRichTextBuffer* buffer, const wxTextAttrBorders& attr, const wxRect& rect, int WXUNUSED(flags))
716 {
717 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
718 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
719
720 if (attr.GetLeft().IsValid() && attr.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
721 {
722 borderLeft = converter.GetPixels(attr.GetLeft().GetWidth());
723 wxColour col(attr.GetLeft().GetColour());
724
725 // If pen width is > 1, resorts to a solid rectangle.
726 if (borderLeft == 1)
727 {
728 int penStyle = wxSOLID;
729 if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
730 penStyle = wxDOT;
731 else if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
732 penStyle = wxLONG_DASH;
733 wxPen pen(col, 1, penStyle);
734 dc.SetPen(pen);
735 dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height);
736
737 }
738 else if (borderLeft > 1)
739 {
740 wxPen pen(col);
741 wxBrush brush(col);
742 dc.SetPen(pen);
743 dc.SetBrush(brush);
744 dc.DrawRectangle(rect.x, rect.y, borderLeft, rect.height);
745 }
746 }
747
748 if (attr.GetRight().IsValid() && attr.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
749 {
750 borderRight = converter.GetPixels(attr.GetRight().GetWidth());
751
752 wxColour col(attr.GetRight().GetColour());
753
754 // If pen width is > 1, resorts to a solid rectangle.
755 if (borderRight == 1)
756 {
757 int penStyle = wxSOLID;
758 if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
759 penStyle = wxDOT;
760 else if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
761 penStyle = wxLONG_DASH;
762 wxPen pen(col, 1, penStyle);
763 dc.SetPen(pen);
764 dc.DrawLine(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height + 1);
765
766 }
767 else if (borderRight > 1)
768 {
769 wxPen pen(col);
770 wxBrush brush(col);
771 dc.SetPen(pen);
772 dc.SetBrush(brush);
773 dc.DrawRectangle(rect.x + rect.width - borderRight, rect.y, borderRight, rect.height);
774 }
775 }
776
777 if (attr.GetTop().IsValid() && attr.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
778 {
779 borderTop = converter.GetPixels(attr.GetTop().GetWidth());
780
781 wxColour col(attr.GetTop().GetColour());
782
783 // If pen width is > 1, resorts to a solid rectangle.
784 if (borderTop == 1)
785 {
786 int penStyle = wxSOLID;
787 if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
788 penStyle = wxDOT;
789 else if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
790 penStyle = wxLONG_DASH;
791 wxPen pen(col, 1, penStyle);
792 dc.SetPen(pen);
793 dc.DrawLine(rect.x, rect.y, rect.x + rect.width, rect.y);
794
795 }
796 else if (borderTop > 1)
797 {
798 wxPen pen(col);
799 wxBrush brush(col);
800 dc.SetPen(pen);
801 dc.SetBrush(brush);
802 dc.DrawRectangle(rect.x, rect.y, rect.width, borderTop);
803 }
804 }
805
806 if (attr.GetBottom().IsValid() && attr.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
807 {
808 borderBottom = converter.GetPixels(attr.GetBottom().GetWidth());
809 wxColour col(attr.GetBottom().GetColour());
810
811 // If pen width is > 1, resorts to a solid rectangle.
812 if (borderBottom == 1)
813 {
814 int penStyle = wxSOLID;
815 if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
816 penStyle = wxDOT;
817 else if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
818 penStyle = wxLONG_DASH;
819 wxPen pen(col, 1, penStyle);
820 dc.SetPen(pen);
821 dc.DrawLine(rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height);
822
823 }
824 else if (borderBottom > 1)
825 {
826 wxPen pen(col);
827 wxBrush brush(col);
828 dc.SetPen(pen);
829 dc.SetBrush(brush);
830 dc.DrawRectangle(rect.x, rect.y + rect.height - borderBottom, rect.width, borderBottom);
831 }
832 }
833
834 return true;
835 }
836
837 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
838 // or marginRect (outer), and the other must be the default rectangle (no width or height).
839 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
840 // is available.
841 //
842 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
843
844 bool wxRichTextObject::GetBoxRects(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, wxRect& marginRect, wxRect& borderRect, wxRect& contentRect, wxRect& paddingRect, wxRect& outlineRect)
845 {
846 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
847 int outlineLeft = 0, outlineRight = 0, outlineTop = 0, outlineBottom = 0;
848 int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
849 int marginLeft = 0, marginRight = 0, marginTop = 0, marginBottom = 0;
850
851 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
852
853 if (attr.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
854 marginLeft = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetLeft());
855 if (attr.GetTextBoxAttr().GetMargins().GetRight().IsValid())
856 marginRight = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetRight());
857 if (attr.GetTextBoxAttr().GetMargins().GetTop().IsValid())
858 marginTop = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetTop());
859 if (attr.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
860 marginBottom = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetBottom());
861
862 if (attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
863 borderLeft = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
864 if (attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
865 borderRight = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
866 if (attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
867 borderTop = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
868 if (attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
869 borderBottom = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
870
871 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
872 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
873 if (attr.GetTextBoxAttr().GetPadding().GetRight().IsValid())
874 paddingRight = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetRight());
875 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
876 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
877 if (attr.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
878 paddingBottom = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetBottom());
879
880 if (attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
881 outlineLeft = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
882 if (attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
883 outlineRight = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
884 if (attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
885 outlineTop = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
886 if (attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
887 outlineBottom = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
888
889 int leftTotal = marginLeft + borderLeft + paddingLeft;
890 int rightTotal = marginRight + borderRight + paddingRight;
891 int topTotal = marginTop + borderTop + paddingTop;
892 int bottomTotal = marginBottom + borderBottom + paddingBottom;
893
894 if (marginRect != wxRect())
895 {
896 contentRect.x = marginRect.x + leftTotal;
897 contentRect.y = marginRect.y + topTotal;
898 contentRect.width = marginRect.width - (leftTotal + rightTotal);
899 contentRect.height = marginRect.height - (topTotal + bottomTotal);
900 }
901 else
902 {
903 marginRect.x = contentRect.x - leftTotal;
904 marginRect.y = contentRect.y - topTotal;
905 marginRect.width = contentRect.width + (leftTotal + rightTotal);
906 marginRect.height = contentRect.height + (topTotal + bottomTotal);
907 }
908
909 borderRect.x = marginRect.x + marginLeft;
910 borderRect.y = marginRect.y + marginTop;
911 borderRect.width = marginRect.width - (marginLeft + marginRight);
912 borderRect.height = marginRect.height - (marginTop + marginBottom);
913
914 paddingRect.x = marginRect.x + marginLeft + borderLeft;
915 paddingRect.y = marginRect.y + marginTop + borderTop;
916 paddingRect.width = marginRect.width - (marginLeft + marginRight + borderLeft + borderRight);
917 paddingRect.height = marginRect.height - (marginTop + marginBottom + borderTop + borderBottom);
918
919 // The outline is outside the margin and doesn't influence the overall box position or content size.
920 outlineRect.x = marginRect.x - outlineLeft;
921 outlineRect.y = marginRect.y - outlineTop;
922 outlineRect.width = marginRect.width + (outlineLeft + outlineRight);
923 outlineRect.height = marginRect.height + (outlineTop + outlineBottom);
924
925 return true;
926 }
927
928 // Get the total margin for the object in pixels, taking into account margin, padding and border size
929 bool wxRichTextObject::GetTotalMargin(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, int& leftMargin, int& rightMargin,
930 int& topMargin, int& bottomMargin)
931 {
932 // Assume boxRect is the area around the content
933 wxRect contentRect, marginRect, borderRect, paddingRect, outlineRect;
934 marginRect = wxRect(0, 0, 1000, 1000);
935
936 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
937
938 leftMargin = contentRect.GetLeft() - marginRect.GetLeft();
939 rightMargin = marginRect.GetRight() - contentRect.GetRight();
940 topMargin = contentRect.GetTop() - marginRect.GetTop();
941 bottomMargin = marginRect.GetBottom() - contentRect.GetBottom();
942
943 return true;
944 }
945
946 // Returns the rectangle which the child has available to it given restrictions specified in the
947 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
948 // availableContainerSpace might be a parent that the cell has to compute its width relative to.
949 // E.g. a cell that's 50% of its parent.
950 wxRect wxRichTextObject::AdjustAvailableSpace(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& WXUNUSED(parentAttr), const wxRichTextAttr& childAttr, const wxRect& availableParentSpace, const wxRect& availableContainerSpace)
951 {
952 wxRect rect = availableParentSpace;
953 double scale = 1.0;
954 if (buffer)
955 scale = buffer->GetScale();
956
957 wxTextAttrDimensionConverter converter(dc, scale, availableContainerSpace.GetSize());
958
959 if (childAttr.GetTextBoxAttr().GetWidth().IsValid())
960 rect.width = converter.GetPixels(childAttr.GetTextBoxAttr().GetWidth());
961
962 if (childAttr.GetTextBoxAttr().GetHeight().IsValid())
963 rect.height = converter.GetPixels(childAttr.GetTextBoxAttr().GetHeight());
964
965 // Can specify either left or right for the position (we're assuming we can't
966 // set the left and right edges to effectively set the size. Would we want to do that?)
967 if (childAttr.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
968 {
969 rect.x = rect.x + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetLeft());
970 }
971 else if (childAttr.GetTextBoxAttr().GetPosition().GetRight().IsValid())
972 {
973 int x = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetRight());
974 if (childAttr.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
975 rect.x = availableContainerSpace.x + availableContainerSpace.width - rect.width;
976 else
977 rect.x += x;
978 }
979
980 if (childAttr.GetTextBoxAttr().GetPosition().GetTop().IsValid())
981 {
982 rect.y = rect.y + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetTop());
983 }
984 else if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
985 {
986 int y = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetBottom());
987 if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
988 rect.y = availableContainerSpace.y + availableContainerSpace.height - rect.height;
989 else
990 rect.y += y;
991 }
992
993 if (rect.GetWidth() > availableParentSpace.GetWidth())
994 rect.SetWidth(availableParentSpace.GetWidth());
995
996 return rect;
997 }
998
999 // Dump to output stream for debugging
1000 void wxRichTextObject::Dump(wxTextOutputStream& stream)
1001 {
1002 stream << GetClassInfo()->GetClassName() << wxT("\n");
1003 stream << wxString::Format(wxT("Size: %d,%d. Position: %d,%d, Range: %ld,%ld"), m_size.x, m_size.y, m_pos.x, m_pos.y, m_range.GetStart(), m_range.GetEnd()) << wxT("\n");
1004 stream << wxString::Format(wxT("Text colour: %d,%d,%d."), (int) m_attributes.GetTextColour().Red(), (int) m_attributes.GetTextColour().Green(), (int) m_attributes.GetTextColour().Blue()) << wxT("\n");
1005 }
1006
1007 // Gets the containing buffer
1008 wxRichTextBuffer* wxRichTextObject::GetBuffer() const
1009 {
1010 const wxRichTextObject* obj = this;
1011 while (obj && !wxDynamicCast(obj, wxRichTextBuffer))
1012 obj = obj->GetParent();
1013 return wxDynamicCast(obj, wxRichTextBuffer);
1014 }
1015
1016 // Get the absolute object position, by traversing up the child/parent hierarchy
1017 wxPoint wxRichTextObject::GetAbsolutePosition() const
1018 {
1019 wxPoint pt = GetPosition();
1020
1021 wxRichTextObject* p = GetParent();
1022 while (p)
1023 {
1024 pt = pt + p->GetPosition();
1025 p = p->GetParent();
1026 }
1027
1028 return pt;
1029 }
1030
1031 // Hit-testing: returns a flag indicating hit test details, plus
1032 // information about position
1033 int wxRichTextObject::HitTest(wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int WXUNUSED(flags))
1034 {
1035 if (!IsShown())
1036 return wxRICHTEXT_HITTEST_NONE;
1037
1038 wxRect rect = GetRect();
1039 if (pt.x >= rect.x && pt.x < rect.x + rect.width &&
1040 pt.y >= rect.y && pt.y < rect.y + rect.height)
1041 {
1042 *obj = this;
1043 *contextObj = GetParentContainer();
1044 textPosition = GetRange().GetStart();
1045 return wxRICHTEXT_HITTEST_ON;
1046 }
1047 else
1048 return wxRICHTEXT_HITTEST_NONE;
1049 }
1050
1051 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1052 // lays out the object again using the maximum ('best') size
1053 bool wxRichTextObject::LayoutToBestSize(wxDC& dc, wxRichTextDrawingContext& context, wxRichTextBuffer* buffer,
1054 const wxRichTextAttr& parentAttr, const wxRichTextAttr& attr,
1055 const wxRect& availableParentSpace, const wxRect& availableContainerSpace,
1056 int style)
1057 {
1058 wxRect availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, attr, availableParentSpace, availableContainerSpace);
1059 wxRect originalAvailableRect = availableChildRect;
1060 Layout(dc, context, availableChildRect, availableContainerSpace, style);
1061
1062 wxSize maxSize = GetMaxSize();
1063
1064 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1065 // on this basis
1066 if (!attr.GetTextBoxAttr().GetWidth().IsValid() && maxSize.x < availableChildRect.width)
1067 {
1068 // Redo the layout with a fixed, minimum size this time.
1069 Invalidate(wxRICHTEXT_ALL);
1070 wxRichTextAttr newAttr(attr);
1071 newAttr.GetTextBoxAttr().GetWidth().SetValue(maxSize.x, wxTEXT_ATTR_UNITS_PIXELS);
1072 newAttr.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE);
1073
1074 availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, newAttr, availableParentSpace, availableContainerSpace);
1075
1076 // If a paragraph, align the whole paragraph.
1077 // Problem with this: if we're limited by a floating object, a line may be centered
1078 // w.r.t. the smaller resulting box rather than the actual available width.
1079 // FIXME: aligning whole paragraph not compatible with floating objects
1080 if (attr.HasAlignment() && (!wxRichTextBuffer::GetFloatingLayoutMode() || (GetContainer()->GetFloatCollector() && !GetContainer()->GetFloatCollector()->HasFloats())))
1081 {
1082 // centering, right-justification
1083 if (attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
1084 {
1085 availableChildRect.x = (originalAvailableRect.GetWidth() - availableChildRect.GetWidth())/2 + availableChildRect.x;
1086 }
1087 else if (attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
1088 {
1089 availableChildRect.x = availableChildRect.x + originalAvailableRect.GetWidth() - availableChildRect.GetWidth();
1090 }
1091 }
1092
1093 Layout(dc, context, availableChildRect, availableContainerSpace, style);
1094 }
1095
1096 /*
1097 __________________
1098 | ____________ |
1099 | | | |
1100
1101
1102 */
1103
1104 return true;
1105 }
1106
1107 // Move the object recursively, by adding the offset from old to new
1108 void wxRichTextObject::Move(const wxPoint& pt)
1109 {
1110 SetPosition(pt);
1111 }
1112
1113
1114 /*!
1115 * wxRichTextCompositeObject
1116 * This is the base for drawable objects.
1117 */
1118
1119 IMPLEMENT_CLASS(wxRichTextCompositeObject, wxRichTextObject)
1120
1121 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject* parent):
1122 wxRichTextObject(parent)
1123 {
1124 }
1125
1126 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1127 {
1128 DeleteChildren();
1129 }
1130
1131 /// Get the nth child
1132 wxRichTextObject* wxRichTextCompositeObject::GetChild(size_t n) const
1133 {
1134 wxASSERT ( n < m_children.GetCount() );
1135
1136 return m_children.Item(n)->GetData();
1137 }
1138
1139 /// Append a child, returning the position
1140 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject* child)
1141 {
1142 m_children.Append(child);
1143 child->SetParent(this);
1144 return m_children.GetCount() - 1;
1145 }
1146
1147 /// Insert the child in front of the given object, or at the beginning
1148 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject* child, wxRichTextObject* inFrontOf)
1149 {
1150 if (inFrontOf)
1151 {
1152 wxRichTextObjectList::compatibility_iterator node = m_children.Find(inFrontOf);
1153 m_children.Insert(node, child);
1154 }
1155 else
1156 m_children.Insert(child);
1157 child->SetParent(this);
1158
1159 return true;
1160 }
1161
1162 /// Delete the child
1163 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject* child, bool deleteChild)
1164 {
1165 wxRichTextObjectList::compatibility_iterator node = m_children.Find(child);
1166 if (node)
1167 {
1168 wxRichTextObject* obj = node->GetData();
1169 m_children.Erase(node);
1170 if (deleteChild)
1171 delete obj;
1172
1173 return true;
1174 }
1175 return false;
1176 }
1177
1178 /// Delete all children
1179 bool wxRichTextCompositeObject::DeleteChildren()
1180 {
1181 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1182 while (node)
1183 {
1184 wxRichTextObjectList::compatibility_iterator oldNode = node;
1185
1186 wxRichTextObject* child = node->GetData();
1187 child->Dereference(); // Only delete if reference count is zero
1188
1189 node = node->GetNext();
1190 m_children.Erase(oldNode);
1191 }
1192
1193 return true;
1194 }
1195
1196 /// Get the child count
1197 size_t wxRichTextCompositeObject::GetChildCount() const
1198 {
1199 return m_children.GetCount();
1200 }
1201
1202 /// Copy
1203 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject& obj)
1204 {
1205 wxRichTextObject::Copy(obj);
1206
1207 DeleteChildren();
1208
1209 wxRichTextObjectList::compatibility_iterator node = obj.m_children.GetFirst();
1210 while (node)
1211 {
1212 wxRichTextObject* child = node->GetData();
1213 wxRichTextObject* newChild = child->Clone();
1214 newChild->SetParent(this);
1215 m_children.Append(newChild);
1216
1217 node = node->GetNext();
1218 }
1219 }
1220
1221 /// Hit-testing: returns a flag indicating hit test details, plus
1222 /// information about position
1223 int wxRichTextCompositeObject::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
1224 {
1225 if (!IsShown())
1226 return wxRICHTEXT_HITTEST_NONE;
1227
1228 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1229 while (node)
1230 {
1231 wxRichTextObject* child = node->GetData();
1232
1233 if (child->IsShown() && child->IsTopLevel() && (flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS))
1234 {
1235 // Just check if we hit the overall object
1236 int ret = child->wxRichTextObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
1237 if (ret != wxRICHTEXT_HITTEST_NONE)
1238 return ret;
1239 }
1240 else if (child->IsShown())
1241 {
1242 int ret = child->HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
1243 if (ret != wxRICHTEXT_HITTEST_NONE)
1244 return ret;
1245 }
1246
1247 node = node->GetNext();
1248 }
1249
1250 return wxRICHTEXT_HITTEST_NONE;
1251 }
1252
1253 /// Finds the absolute position and row height for the given character position
1254 bool wxRichTextCompositeObject::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
1255 {
1256 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1257 while (node)
1258 {
1259 wxRichTextObject* child = node->GetData();
1260
1261 // Don't recurse if the child is a top-level object,
1262 // such as a text box, because the character position will no longer
1263 // apply. By definition, a top-level object has its own range of
1264 // character positions.
1265 if (!child->IsTopLevel() && child->FindPosition(dc, context, index, pt, height, forceLineStart))
1266 return true;
1267
1268 node = node->GetNext();
1269 }
1270
1271 return false;
1272 }
1273
1274 /// Calculate range
1275 void wxRichTextCompositeObject::CalculateRange(long start, long& end)
1276 {
1277 long current = start;
1278 long lastEnd = current;
1279
1280 if (IsTopLevel())
1281 {
1282 current = 0;
1283 lastEnd = 0;
1284 }
1285
1286 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1287 while (node)
1288 {
1289 wxRichTextObject* child = node->GetData();
1290 long childEnd = 0;
1291
1292 child->CalculateRange(current, childEnd);
1293 lastEnd = childEnd;
1294
1295 current = childEnd + 1;
1296
1297 node = node->GetNext();
1298 }
1299
1300 if (IsTopLevel())
1301 {
1302 // A top-level object always has a range of size 1,
1303 // because its children don't count at this level.
1304 end = start;
1305 m_range.SetRange(start, start);
1306
1307 // An object with no children has zero length
1308 if (m_children.GetCount() == 0)
1309 lastEnd --;
1310 m_ownRange.SetRange(0, lastEnd);
1311 }
1312 else
1313 {
1314 end = lastEnd;
1315
1316 // An object with no children has zero length
1317 if (m_children.GetCount() == 0)
1318 end --;
1319
1320 m_range.SetRange(start, end);
1321 }
1322 }
1323
1324 /// Delete range from layout.
1325 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange& range)
1326 {
1327 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1328
1329 while (node)
1330 {
1331 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
1332 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1333
1334 // Delete the range in each paragraph
1335
1336 // When a chunk has been deleted, internally the content does not
1337 // now match the ranges.
1338 // However, so long as deletion is not done on the same object twice this is OK.
1339 // If you may delete content from the same object twice, recalculate
1340 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1341 // adjust the range you're deleting accordingly.
1342
1343 if (!obj->GetRange().IsOutside(range))
1344 {
1345 // No need to delete within a top-level object; just removing this object will do fine
1346 if (!obj->IsTopLevel())
1347 obj->DeleteRange(range);
1348
1349 // Delete an empty object, or paragraph within this range.
1350 if (obj->IsEmpty() ||
1351 (range.GetStart() <= obj->GetRange().GetStart() && range.GetEnd() >= obj->GetRange().GetEnd()))
1352 {
1353 // An empty paragraph has length 1, so won't be deleted unless the
1354 // whole range is deleted.
1355 RemoveChild(obj, true);
1356 }
1357 }
1358
1359 node = next;
1360 }
1361
1362 return true;
1363 }
1364
1365 /// Get any text in this object for the given range
1366 wxString wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange& range) const
1367 {
1368 wxString text;
1369 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1370 while (node)
1371 {
1372 wxRichTextObject* child = node->GetData();
1373 wxRichTextRange childRange = range;
1374 if (!child->GetRange().IsOutside(range))
1375 {
1376 childRange.LimitTo(child->GetRange());
1377
1378 wxString childText = child->GetTextForRange(childRange);
1379
1380 text += childText;
1381 }
1382 node = node->GetNext();
1383 }
1384
1385 return text;
1386 }
1387
1388 /// Get the child object at the given character position
1389 wxRichTextObject* wxRichTextCompositeObject::GetChildAtPosition(long pos) const
1390 {
1391 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1392 while (node)
1393 {
1394 wxRichTextObject* child = node->GetData();
1395 if (child->GetRange().GetStart() == pos)
1396 return child;
1397 node = node->GetNext();
1398 }
1399 return NULL;
1400 }
1401
1402 /// Recursively merge all pieces that can be merged.
1403 bool wxRichTextCompositeObject::Defragment(wxRichTextDrawingContext& context, const wxRichTextRange& range)
1404 {
1405 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1406 while (node)
1407 {
1408 wxRichTextObject* child = node->GetData();
1409 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
1410 {
1411 wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject);
1412 if (composite)
1413 composite->Defragment(context);
1414
1415 // Optimization: if there are no virtual attributes, we won't need to
1416 // to split objects in order to paint individually attributed chunks.
1417 // So only merge in this case.
1418 if (!context.GetVirtualAttributesEnabled())
1419 {
1420 if (node->GetNext())
1421 {
1422 wxRichTextObject* nextChild = node->GetNext()->GetData();
1423 if (child->CanMerge(nextChild, context) && child->Merge(nextChild, context))
1424 {
1425 nextChild->Dereference();
1426 m_children.Erase(node->GetNext());
1427 }
1428 else
1429 node = node->GetNext();
1430 }
1431 else
1432 node = node->GetNext();
1433 }
1434 else
1435 {
1436 // If we might have virtual attributes, we first see if we have to split
1437 // objects so that they may be painted with potential virtual attributes,
1438 // since text objects can only draw or measure with a single attributes object
1439 // at a time.
1440 wxRichTextObject* childAfterSplit = child;
1441 if (child->CanSplit(context))
1442 {
1443 childAfterSplit = child->Split(context);
1444 node = m_children.Find(childAfterSplit);
1445 }
1446
1447 if (node->GetNext())
1448 {
1449 wxRichTextObject* nextChild = node->GetNext()->GetData();
1450
1451 // First split child and nextChild so we have smaller fragments to merge.
1452 // Then Merge only has to test per-object virtual attributes
1453 // because for an object with all the same sub-object attributes,
1454 // then any general virtual attributes should be merged with sub-objects by
1455 // the implementation.
1456
1457 wxRichTextObject* nextChildAfterSplit = nextChild;
1458
1459 if (nextChildAfterSplit->CanSplit(context))
1460 nextChildAfterSplit = nextChild->Split(context);
1461
1462 bool splitNextChild = nextChild != nextChildAfterSplit;
1463
1464 // See if we can merge this new fragment with (perhaps the first part of) the next object.
1465 // Note that we use nextChild because if we had split nextChild, the first object always
1466 // remains (and further parts are appended). However we must use childAfterSplit since
1467 // it's the last part of a possibly split child.
1468
1469 if (childAfterSplit->CanMerge(nextChild, context) && childAfterSplit->Merge(nextChild, context))
1470 {
1471 nextChild->Dereference();
1472 m_children.Erase(node->GetNext());
1473
1474 // Don't set node -- we'll see if we can merge again with the next
1475 // child. UNLESS we split this or the next child, in which case we know we have to
1476 // move on to the end of the next child.
1477 if (splitNextChild)
1478 node = m_children.Find(nextChildAfterSplit);
1479 }
1480 else
1481 {
1482 if (splitNextChild)
1483 node = m_children.Find(nextChildAfterSplit); // start from the last object in the split
1484 else
1485 node = node->GetNext();
1486 }
1487 }
1488 else
1489 node = node->GetNext();
1490 }
1491 }
1492 else
1493 node = node->GetNext();
1494 }
1495
1496 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1497 if (GetChildCount() > 1)
1498 {
1499 node = m_children.GetFirst();
1500 while (node)
1501 {
1502 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1503 wxRichTextObject* child = node->GetData();
1504 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
1505 {
1506 if (child->IsEmpty())
1507 {
1508 child->Dereference();
1509 m_children.Erase(node);
1510 }
1511 node = next;
1512 }
1513 else
1514 node = node->GetNext();
1515 }
1516 }
1517
1518 return true;
1519 }
1520
1521 /// Dump to output stream for debugging
1522 void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
1523 {
1524 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1525 while (node)
1526 {
1527 wxRichTextObject* child = node->GetData();
1528 child->Dump(stream);
1529 node = node->GetNext();
1530 }
1531 }
1532
1533 /// Get/set the object size for the given range. Returns false if the range
1534 /// is invalid for this object.
1535 bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const
1536 {
1537 if (!range.IsWithin(GetRange()))
1538 return false;
1539
1540 wxSize sz;
1541
1542 wxArrayInt childExtents;
1543 wxArrayInt* p;
1544 if (partialExtents)
1545 p = & childExtents;
1546 else
1547 p = NULL;
1548
1549 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1550 while (node)
1551 {
1552 wxRichTextObject* child = node->GetData();
1553 if (!child->GetRange().IsOutside(range))
1554 {
1555 // Floating objects have a zero size within the paragraph.
1556 if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
1557 {
1558 if (partialExtents)
1559 {
1560 int lastSize;
1561 if (partialExtents->GetCount() > 0)
1562 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1563 else
1564 lastSize = 0;
1565
1566 partialExtents->Add(0 /* zero size */ + lastSize);
1567 }
1568 }
1569 else
1570 {
1571 wxSize childSize;
1572
1573 wxRichTextRange rangeToUse = range;
1574 rangeToUse.LimitTo(child->GetRange());
1575 if (child->IsTopLevel())
1576 rangeToUse = child->GetOwnRange();
1577
1578 int childDescent = 0;
1579
1580 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1581 // but it's only going to be used after caching has taken place.
1582 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
1583 {
1584 childDescent = child->GetDescent();
1585 childSize = child->GetCachedSize();
1586
1587 sz.y = wxMax(sz.y, childSize.y);
1588 sz.x += childSize.x;
1589 descent = wxMax(descent, childDescent);
1590 }
1591 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize, p))
1592 {
1593 sz.y = wxMax(sz.y, childSize.y);
1594 sz.x += childSize.x;
1595 descent = wxMax(descent, childDescent);
1596
1597 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange() || child->IsTopLevel()))
1598 {
1599 child->SetCachedSize(childSize);
1600 child->SetDescent(childDescent);
1601 }
1602
1603 if (partialExtents)
1604 {
1605 int lastSize;
1606 if (partialExtents->GetCount() > 0)
1607 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1608 else
1609 lastSize = 0;
1610
1611 size_t i;
1612 for (i = 0; i < childExtents.GetCount(); i++)
1613 {
1614 partialExtents->Add(childExtents[i] + lastSize);
1615 }
1616 }
1617 }
1618 }
1619
1620 if (p)
1621 p->Clear();
1622 }
1623
1624 node = node->GetNext();
1625 }
1626 size = sz;
1627 return true;
1628 }
1629
1630 // Invalidate the buffer. With no argument, invalidates whole buffer.
1631 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange& invalidRange)
1632 {
1633 wxRichTextObject::Invalidate(invalidRange);
1634
1635 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1636 while (node)
1637 {
1638 wxRichTextObject* child = node->GetData();
1639 if (invalidRange != wxRICHTEXT_ALL && invalidRange != wxRICHTEXT_NONE && child->GetRange().IsOutside(invalidRange))
1640 {
1641 // Skip
1642 }
1643 else if (child->IsTopLevel())
1644 {
1645 if (wxRichTextBuffer::GetFloatingLayoutMode() && child->IsFloating() && GetBuffer()->GetFloatCollector() && GetBuffer()->GetFloatCollector()->HasFloat(child))
1646 {
1647 // Don't invalidate subhierarchy if we've already been laid out
1648 }
1649 else
1650 {
1651 if (invalidRange == wxRICHTEXT_NONE)
1652 child->Invalidate(wxRICHTEXT_NONE);
1653 else
1654 child->Invalidate(wxRICHTEXT_ALL); // All children must be invalidated if within parent range
1655 }
1656 }
1657 else
1658 child->Invalidate(invalidRange);
1659 node = node->GetNext();
1660 }
1661 }
1662
1663 // Move the object recursively, by adding the offset from old to new
1664 void wxRichTextCompositeObject::Move(const wxPoint& pt)
1665 {
1666 wxPoint oldPos = GetPosition();
1667 SetPosition(pt);
1668 wxPoint offset = pt - oldPos;
1669
1670 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1671 while (node)
1672 {
1673 wxRichTextObject* child = node->GetData();
1674 wxPoint childPos = child->GetPosition() + offset;
1675 child->Move(childPos);
1676 node = node->GetNext();
1677 }
1678 }
1679
1680
1681 /*!
1682 * wxRichTextParagraphLayoutBox
1683 * This box knows how to lay out paragraphs.
1684 */
1685
1686 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextCompositeObject)
1687
1688 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject* parent):
1689 wxRichTextCompositeObject(parent)
1690 {
1691 Init();
1692 }
1693
1694 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1695 {
1696 if (m_floatCollector)
1697 {
1698 delete m_floatCollector;
1699 m_floatCollector = NULL;
1700 }
1701 }
1702
1703 /// Initialize the object.
1704 void wxRichTextParagraphLayoutBox::Init()
1705 {
1706 m_ctrl = NULL;
1707
1708 // For now, assume is the only box and has no initial size.
1709 m_range = wxRichTextRange(0, -1);
1710 m_ownRange = wxRichTextRange(0, -1);
1711
1712 m_invalidRange = wxRICHTEXT_ALL;
1713
1714 m_partialParagraph = false;
1715 m_floatCollector = NULL;
1716 }
1717
1718 void wxRichTextParagraphLayoutBox::Clear()
1719 {
1720 DeleteChildren();
1721
1722 if (m_floatCollector)
1723 delete m_floatCollector;
1724 m_floatCollector = NULL;
1725 m_partialParagraph = false;
1726 }
1727
1728 /// Copy
1729 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
1730 {
1731 Clear();
1732
1733 wxRichTextCompositeObject::Copy(obj);
1734
1735 m_partialParagraph = obj.m_partialParagraph;
1736 m_defaultAttributes = obj.m_defaultAttributes;
1737 }
1738
1739 // Gather information about floating objects; only gather floats for those paragraphs that
1740 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1741 // during layout.
1742 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect& availableRect, wxRichTextObject* untilObj)
1743 {
1744 if (m_floatCollector != NULL)
1745 delete m_floatCollector;
1746 m_floatCollector = new wxRichTextFloatCollector(availableRect);
1747 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1748 // Only gather floats up to the point we'll start formatting paragraphs.
1749 while (untilObj && node && node->GetData() != untilObj)
1750 {
1751 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1752 wxASSERT (child != NULL);
1753 if (child)
1754 m_floatCollector->CollectFloat(child);
1755 node = node->GetNext();
1756 }
1757
1758 return true;
1759 }
1760
1761 // Returns the style sheet associated with the overall buffer.
1762 wxRichTextStyleSheet* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1763 {
1764 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet*) NULL;
1765 }
1766
1767 // Get the number of floating objects at this level
1768 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1769 {
1770 if (m_floatCollector)
1771 return m_floatCollector->GetFloatingObjectCount();
1772 else
1773 return 0;
1774 }
1775
1776 // Get a list of floating objects
1777 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList& objects) const
1778 {
1779 if (m_floatCollector)
1780 {
1781 return m_floatCollector->GetFloatingObjects(objects);
1782 }
1783 else
1784 return false;
1785 }
1786
1787 // Calculate ranges
1788 void wxRichTextParagraphLayoutBox::UpdateRanges()
1789 {
1790 long start = 0;
1791 if (GetParent())
1792 start = GetRange().GetStart();
1793 long end;
1794 CalculateRange(start, end);
1795 }
1796
1797 // HitTest
1798 int wxRichTextParagraphLayoutBox::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
1799 {
1800 if (!IsShown())
1801 return wxRICHTEXT_HITTEST_NONE;
1802
1803 int ret = wxRICHTEXT_HITTEST_NONE;
1804 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector && (flags & wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS) == 0)
1805 ret = m_floatCollector->HitTest(dc, context, pt, textPosition, obj, flags);
1806
1807 if (ret == wxRICHTEXT_HITTEST_NONE)
1808 return wxRichTextCompositeObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
1809 else
1810 {
1811 *contextObj = this;
1812 return ret;
1813 }
1814 }
1815
1816 /// Draw the floating objects
1817 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
1818 {
1819 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector)
1820 m_floatCollector->Draw(dc, context, range, selection, rect, descent, style);
1821 }
1822
1823 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextObject* obj)
1824 {
1825 if (from == to)
1826 return;
1827
1828 from->RemoveChild(obj);
1829 to->AppendChild(obj);
1830 }
1831
1832 /// Draw the item
1833 bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
1834 {
1835 if (!IsShown())
1836 return true;
1837
1838 wxRect thisRect(GetPosition(), GetCachedSize());
1839
1840 wxRichTextAttr attr(GetAttributes());
1841 context.ApplyVirtualAttributes(attr, this);
1842
1843 int flags = style;
1844 if (selection.IsValid() && GetParentContainer() != this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1845 flags |= wxRICHTEXT_DRAW_SELECTED;
1846
1847 // Don't draw guidelines if at top level
1848 int theseFlags = flags;
1849 if (!GetParent())
1850 theseFlags &= ~wxRICHTEXT_DRAW_GUIDELINES;
1851 DrawBoxAttributes(dc, GetBuffer(), attr, thisRect, theseFlags);
1852
1853 if (wxRichTextBuffer::GetFloatingLayoutMode())
1854 DrawFloats(dc, context, range, selection, rect, descent, style);
1855
1856 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1857 while (node)
1858 {
1859 wxRichTextObject* child = node->GetData();
1860
1861 if (child && !child->GetRange().IsOutside(range))
1862 {
1863 wxRect childRect(child->GetPosition(), child->GetCachedSize());
1864 wxRichTextRange childRange = range;
1865 if (child->IsTopLevel())
1866 {
1867 childRange = child->GetOwnRange();
1868 }
1869
1870 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetTop() > rect.GetBottom())
1871 {
1872 // Stop drawing
1873 break;
1874 }
1875 else if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetBottom() < rect.GetTop())
1876 {
1877 // Skip
1878 }
1879 else
1880 child->Draw(dc, context, childRange, selection, rect, descent, style);
1881 }
1882
1883 node = node->GetNext();
1884 }
1885 return true;
1886 }
1887
1888 /// Lay the item out
1889 bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
1890 {
1891 SetPosition(rect.GetPosition());
1892
1893 if (!IsShown())
1894 return true;
1895
1896 wxRect availableSpace;
1897 bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
1898
1899 wxRichTextAttr attr(GetAttributes());
1900 context.ApplyVirtualAttributes(attr, this);
1901
1902 // If only laying out a specific area, the passed rect has a different meaning:
1903 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1904 // so that during a size, only the visible part will be relaid out, or
1905 // it would take too long causing flicker. As an approximation, we assume that
1906 // everything up to the start of the visible area is laid out correctly.
1907 if (formatRect)
1908 {
1909 wxRect rect2(0, 0, rect.width, rect.height);
1910 availableSpace = GetAvailableContentArea(dc, context, rect2);
1911
1912 // Invalidate the part of the buffer from the first visible line
1913 // to the end. If other parts of the buffer are currently invalid,
1914 // then they too will be taken into account if they are above
1915 // the visible point.
1916 long startPos = 0;
1917 wxRichTextLine* line = GetLineAtYPosition(rect.y);
1918 if (line)
1919 startPos = line->GetAbsoluteRange().GetStart();
1920
1921 Invalidate(wxRichTextRange(startPos, GetOwnRange().GetEnd()));
1922 }
1923 else
1924 {
1925 availableSpace = GetAvailableContentArea(dc, context, rect);
1926 }
1927
1928 // Fix the width if we're at the top level
1929 if (!GetParent())
1930 attr.GetTextBoxAttr().GetWidth().SetValue(rect.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
1931
1932 int leftMargin, rightMargin, topMargin, bottomMargin;
1933 wxRichTextObject::GetTotalMargin(dc, GetBuffer(), attr, leftMargin, rightMargin,
1934 topMargin, bottomMargin);
1935
1936 int maxWidth = 0;
1937 int maxHeight = 0;
1938
1939 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1940 int maxMaxWidth = 0;
1941
1942 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1943 int maxMinWidth = 0;
1944
1945 // If we have vertical alignment, we must recalculate everything.
1946 bool hasVerticalAlignment = (attr.GetTextBoxAttr().HasVerticalAlignment() &&
1947 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP));
1948
1949 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1950
1951 bool layoutAll = true;
1952
1953 // Get invalid range, rounding to paragraph start/end.
1954 wxRichTextRange invalidRange = GetInvalidRange(true);
1955
1956 if (invalidRange == wxRICHTEXT_NONE && !formatRect)
1957 return true;
1958
1959 if (invalidRange == wxRICHTEXT_ALL || hasVerticalAlignment)
1960 layoutAll = true;
1961 else // If we know what range is affected, start laying out from that point on.
1962 if (invalidRange.GetStart() >= GetOwnRange().GetStart())
1963 {
1964 wxRichTextParagraph* firstParagraph = GetParagraphAtPosition(invalidRange.GetStart());
1965 if (firstParagraph)
1966 {
1967 wxRichTextObjectList::compatibility_iterator firstNode = m_children.Find(firstParagraph);
1968 wxRichTextObjectList::compatibility_iterator previousNode;
1969 if ( firstNode )
1970 previousNode = firstNode->GetPrevious();
1971 if (firstNode)
1972 {
1973 if (previousNode)
1974 {
1975 wxRichTextParagraph* previousParagraph = wxDynamicCast(previousNode->GetData(), wxRichTextParagraph);
1976 availableSpace.y = previousParagraph->GetPosition().y + previousParagraph->GetCachedSize().y;
1977 }
1978
1979 // Now we're going to start iterating from the first affected paragraph.
1980 node = firstNode;
1981
1982 layoutAll = false;
1983 }
1984 }
1985 }
1986
1987 // Gather information about only those floating objects that will not be formatted,
1988 // after which floats will be gathered per-paragraph during layout.
1989 if (wxRichTextBuffer::GetFloatingLayoutMode())
1990 UpdateFloatingObjects(availableSpace, node ? node->GetData() : (wxRichTextObject*) NULL);
1991
1992 // A way to force speedy rest-of-buffer layout (the 'else' below)
1993 bool forceQuickLayout = false;
1994
1995 // First get the size of the paragraphs we won't be laying out
1996 wxRichTextObjectList::compatibility_iterator n = m_children.GetFirst();
1997 while (n && n != node)
1998 {
1999 wxRichTextParagraph* child = wxDynamicCast(n->GetData(), wxRichTextParagraph);
2000 if (child)
2001 {
2002 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2003 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2004 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2005 }
2006 n = n->GetNext();
2007 }
2008
2009 while (node)
2010 {
2011 // Assume this box only contains paragraphs
2012
2013 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2014 // Unsure if this is needed
2015 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
2016
2017 if (child && child->IsShown())
2018 {
2019 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
2020 if ( !forceQuickLayout &&
2021 (layoutAll ||
2022 child->GetLines().IsEmpty() ||
2023 !child->GetRange().IsOutside(invalidRange)) )
2024 {
2025 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2026 // lays out the object again using the minimum size
2027 child->LayoutToBestSize(dc, context, GetBuffer(),
2028 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
2029
2030 // Layout must set the cached size
2031 availableSpace.y += child->GetCachedSize().y;
2032 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2033 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2034 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2035
2036 // If we're just formatting the visible part of the buffer,
2037 // and we're now past the bottom of the window, and we don't have any
2038 // floating objects (since they may cause wrapping to change for the rest of the
2039 // the buffer), start quick layout.
2040 if (!hasVerticalAlignment && formatRect && child->GetPosition().y > rect.GetBottom() && GetFloatingObjectCount() == 0)
2041 forceQuickLayout = true;
2042 }
2043 else
2044 {
2045 // We're outside the immediately affected range, so now let's just
2046 // move everything up or down. This assumes that all the children have previously
2047 // been laid out and have wrapped line lists associated with them.
2048 // TODO: check all paragraphs before the affected range.
2049
2050 int inc = availableSpace.y - child->GetPosition().y;
2051
2052 while (node)
2053 {
2054 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2055 if (child)
2056 {
2057 if (child->GetLines().GetCount() == 0)
2058 {
2059 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2060 // lays out the object again using the minimum size
2061 child->LayoutToBestSize(dc, context, GetBuffer(),
2062 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
2063
2064 //child->Layout(dc, availableChildRect, style);
2065 }
2066 else
2067 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + inc));
2068
2069 availableSpace.y += child->GetCachedSize().y;
2070 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2071 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2072 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2073 }
2074
2075 node = node->GetNext();
2076 }
2077 break;
2078 }
2079 }
2080
2081 node = node->GetNext();
2082 }
2083
2084 node = m_children.GetLast();
2085 if (node && node->GetData()->IsShown())
2086 {
2087 wxRichTextObject* child = node->GetData();
2088 maxHeight = child->GetPosition().y - (GetPosition().y + topMargin) + child->GetCachedSize().y;
2089 }
2090 else
2091 maxHeight = 0; // topMargin + bottomMargin;
2092
2093 // Check the bottom edge of any floating object
2094 if (wxRichTextBuffer::GetFloatingLayoutMode() && GetFloatCollector() && GetFloatCollector()->HasFloats())
2095 {
2096 int bottom = GetFloatCollector()->GetLastRectBottom();
2097 if (bottom > maxHeight)
2098 maxHeight = bottom;
2099 }
2100
2101 if (attr.GetTextBoxAttr().GetSize().GetWidth().IsValid())
2102 {
2103 wxRect r = AdjustAvailableSpace(dc, GetBuffer(), wxRichTextAttr() /* not used */, attr, parentRect, parentRect);
2104 int w = r.GetWidth();
2105
2106 // Convert external to content rect
2107 w = w - leftMargin - rightMargin;
2108 maxWidth = wxMax(maxWidth, w);
2109 maxMaxWidth = wxMax(maxMaxWidth, w);
2110 }
2111 else
2112 {
2113 // TODO: Make sure the layout box's position reflects
2114 // the position of the children, but without
2115 // breaking layout of a box within a paragraph.
2116 }
2117
2118 // TODO: (also in para layout) should set the
2119 // object's size to an absolute one if specified,
2120 // but if not specified, calculate it from content.
2121
2122 // We need to add back the margins etc.
2123 {
2124 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2125 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, maxHeight));
2126 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
2127 SetCachedSize(marginRect.GetSize());
2128 }
2129
2130 // The maximum size is the greatest of all maximum widths for all paragraphs.
2131 {
2132 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2133 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMaxWidth, maxHeight)); // Actually max height is a lie, we can't know it
2134 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
2135 SetMaxSize(marginRect.GetSize());
2136 }
2137
2138 // The minimum size is the greatest of all minimum widths for all paragraphs.
2139 {
2140 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2141 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMinWidth, maxHeight)); // Actually max height is a lie, we can't know it
2142 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
2143 SetMinSize(marginRect.GetSize());
2144 }
2145
2146 if (attr.GetTextBoxAttr().HasVerticalAlignment() &&
2147 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP))
2148 {
2149 int yOffset = 0;
2150 int leftOverSpace = availableSpace.height - topMargin - bottomMargin - maxHeight;
2151 if (leftOverSpace > 0)
2152 {
2153 if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE)
2154 {
2155 yOffset = (leftOverSpace/2);
2156 }
2157 else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM)
2158 {
2159 yOffset = leftOverSpace;
2160 }
2161 }
2162
2163 // Move all the children to vertically align the content
2164 // This doesn't take into account floating objects, unfortunately.
2165 if (yOffset != 0)
2166 {
2167 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2168 while (node)
2169 {
2170 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2171 if (child)
2172 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + yOffset));
2173
2174 node = node->GetNext();
2175 }
2176 }
2177 }
2178
2179 m_invalidRange = wxRICHTEXT_NONE;
2180
2181 return true;
2182 }
2183
2184 /// Get/set the size for the given range.
2185 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* WXUNUSED(partialExtents)) const
2186 {
2187 wxSize sz;
2188
2189 wxRichTextObjectList::compatibility_iterator startPara = wxRichTextObjectList::compatibility_iterator();
2190 wxRichTextObjectList::compatibility_iterator endPara = wxRichTextObjectList::compatibility_iterator();
2191
2192 // First find the first paragraph whose starting position is within the range.
2193 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2194 while (node)
2195 {
2196 // child is a paragraph
2197 wxRichTextObject* child = node->GetData();
2198 const wxRichTextRange& r = child->GetRange();
2199
2200 if (r.GetStart() <= range.GetStart() && r.GetEnd() >= range.GetStart())
2201 {
2202 startPara = node;
2203 break;
2204 }
2205
2206 node = node->GetNext();
2207 }
2208
2209 // Next find the last paragraph containing part of the range
2210 node = m_children.GetFirst();
2211 while (node)
2212 {
2213 // child is a paragraph
2214 wxRichTextObject* child = node->GetData();
2215 const wxRichTextRange& r = child->GetRange();
2216
2217 if (r.GetStart() <= range.GetEnd() && r.GetEnd() >= range.GetEnd())
2218 {
2219 endPara = node;
2220 break;
2221 }
2222
2223 node = node->GetNext();
2224 }
2225
2226 if (!startPara || !endPara)
2227 return false;
2228
2229 // Now we can add up the sizes
2230 for (node = startPara; node ; node = node->GetNext())
2231 {
2232 // child is a paragraph
2233 wxRichTextObject* child = node->GetData();
2234 const wxRichTextRange& childRange = child->GetRange();
2235 wxRichTextRange rangeToFind = range;
2236 rangeToFind.LimitTo(childRange);
2237
2238 if (child->IsTopLevel())
2239 rangeToFind = child->GetOwnRange();
2240
2241 wxSize childSize;
2242
2243 int childDescent = 0;
2244 child->GetRangeSize(rangeToFind, childSize, childDescent, dc, context, flags, position, parentSize);
2245
2246 descent = wxMax(childDescent, descent);
2247
2248 sz.x = wxMax(sz.x, childSize.x);
2249 sz.y += childSize.y;
2250
2251 if (node == endPara)
2252 break;
2253 }
2254
2255 size = sz;
2256
2257 return true;
2258 }
2259
2260 /// Get the paragraph at the given position
2261 wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos, bool caretPosition) const
2262 {
2263 if (caretPosition)
2264 pos ++;
2265
2266 // First find the first paragraph whose starting position is within the range.
2267 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2268 while (node)
2269 {
2270 // child is a paragraph
2271 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2272 // wxASSERT (child != NULL);
2273
2274 if (child)
2275 {
2276 // Return first child in buffer if position is -1
2277 // if (pos == -1)
2278 // return child;
2279
2280 if (child->GetRange().Contains(pos))
2281 return child;
2282 }
2283
2284 node = node->GetNext();
2285 }
2286 return NULL;
2287 }
2288
2289 /// Get the line at the given position
2290 wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos, bool caretPosition) const
2291 {
2292 if (caretPosition)
2293 pos ++;
2294
2295 // First find the first paragraph whose starting position is within the range.
2296 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2297 while (node)
2298 {
2299 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
2300 if (obj->GetRange().Contains(pos))
2301 {
2302 // child is a paragraph
2303 wxRichTextParagraph* child = wxDynamicCast(obj, wxRichTextParagraph);
2304 // wxASSERT (child != NULL);
2305
2306 if (child)
2307 {
2308 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2309 while (node2)
2310 {
2311 wxRichTextLine* line = node2->GetData();
2312
2313 wxRichTextRange range = line->GetAbsoluteRange();
2314
2315 if (range.Contains(pos) ||
2316
2317 // If the position is end-of-paragraph, then return the last line of
2318 // of the paragraph.
2319 ((range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd())))
2320 return line;
2321
2322 node2 = node2->GetNext();
2323 }
2324 }
2325 }
2326
2327 node = node->GetNext();
2328 }
2329
2330 int lineCount = GetLineCount();
2331 if (lineCount > 0)
2332 return GetLineForVisibleLineNumber(lineCount-1);
2333 else
2334 return NULL;
2335 }
2336
2337 /// Get the line at the given y pixel position, or the last line.
2338 wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y) const
2339 {
2340 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2341 while (node)
2342 {
2343 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2344 // wxASSERT (child != NULL);
2345
2346 if (child)
2347 {
2348 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2349 while (node2)
2350 {
2351 wxRichTextLine* line = node2->GetData();
2352
2353 wxRect rect(line->GetRect());
2354
2355 if (y <= rect.GetBottom())
2356 return line;
2357
2358 node2 = node2->GetNext();
2359 }
2360 }
2361
2362 node = node->GetNext();
2363 }
2364
2365 // Return last line
2366 int lineCount = GetLineCount();
2367 if (lineCount > 0)
2368 return GetLineForVisibleLineNumber(lineCount-1);
2369 else
2370 return NULL;
2371 }
2372
2373 /// Get the number of visible lines
2374 int wxRichTextParagraphLayoutBox::GetLineCount() const
2375 {
2376 int count = 0;
2377
2378 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2379 while (node)
2380 {
2381 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2382 // wxASSERT (child != NULL);
2383
2384 if (child)
2385 count += child->GetLines().GetCount();
2386
2387 node = node->GetNext();
2388 }
2389 return count;
2390 }
2391
2392
2393 /// Get the paragraph for a given line
2394 wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine* line) const
2395 {
2396 return GetParagraphAtPosition(line->GetAbsoluteRange().GetStart());
2397 }
2398
2399 /// Get the line size at the given position
2400 wxSize wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos, bool caretPosition) const
2401 {
2402 wxRichTextLine* line = GetLineAtPosition(pos, caretPosition);
2403 if (line)
2404 {
2405 return line->GetSize();
2406 }
2407 else
2408 return wxSize(0, 0);
2409 }
2410
2411
2412 /// Convenience function to add a paragraph of text
2413 wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph(const wxString& text, wxRichTextAttr* paraStyle)
2414 {
2415 // Don't use the base style, just the default style, and the base style will
2416 // be combined at display time.
2417 // Divide into paragraph and character styles.
2418
2419 wxRichTextAttr defaultCharStyle;
2420 wxRichTextAttr defaultParaStyle;
2421
2422 // If the default style is a named paragraph style, don't apply any character formatting
2423 // to the initial text string.
2424 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2425 {
2426 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2427 if (def)
2428 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2429 }
2430 else
2431 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2432
2433 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2434 wxRichTextAttr* cStyle = & defaultCharStyle;
2435
2436 wxRichTextParagraph* para = new wxRichTextParagraph(text, this, pStyle, cStyle);
2437 para->GetAttributes().GetTextBoxAttr().Reset();
2438
2439 AppendChild(para);
2440
2441 UpdateRanges();
2442
2443 return para->GetRange();
2444 }
2445
2446 /// Adds multiple paragraphs, based on newlines.
2447 wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text, wxRichTextAttr* paraStyle)
2448 {
2449 // Don't use the base style, just the default style, and the base style will
2450 // be combined at display time.
2451 // Divide into paragraph and character styles.
2452
2453 wxRichTextAttr defaultCharStyle;
2454 wxRichTextAttr defaultParaStyle;
2455
2456 // If the default style is a named paragraph style, don't apply any character formatting
2457 // to the initial text string.
2458 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2459 {
2460 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2461 if (def)
2462 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2463 }
2464 else
2465 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2466
2467 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2468 wxRichTextAttr* cStyle = & defaultCharStyle;
2469
2470 wxRichTextParagraph* firstPara = NULL;
2471 wxRichTextParagraph* lastPara = NULL;
2472
2473 wxRichTextRange range(-1, -1);
2474
2475 size_t i = 0;
2476 size_t len = text.length();
2477 wxString line;
2478 wxRichTextParagraph* para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
2479 para->GetAttributes().GetTextBoxAttr().Reset();
2480
2481 AppendChild(para);
2482
2483 firstPara = para;
2484 lastPara = para;
2485
2486 while (i < len)
2487 {
2488 wxChar ch = text[i];
2489 if (ch == wxT('\n') || ch == wxT('\r'))
2490 {
2491 if (i != (len-1))
2492 {
2493 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2494 plainText->SetText(line);
2495
2496 para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
2497 para->GetAttributes().GetTextBoxAttr().Reset();
2498
2499 AppendChild(para);
2500
2501 lastPara = para;
2502 line = wxEmptyString;
2503 }
2504 }
2505 else
2506 line += ch;
2507
2508 i ++;
2509 }
2510
2511 if (!line.empty())
2512 {
2513 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2514 plainText->SetText(line);
2515 }
2516
2517 UpdateRanges();
2518
2519 return wxRichTextRange(firstPara->GetRange().GetStart(), lastPara->GetRange().GetEnd());
2520 }
2521
2522 /// Convenience function to add an image
2523 wxRichTextRange wxRichTextParagraphLayoutBox::AddImage(const wxImage& image, wxRichTextAttr* paraStyle)
2524 {
2525 // Don't use the base style, just the default style, and the base style will
2526 // be combined at display time.
2527 // Divide into paragraph and character styles.
2528
2529 wxRichTextAttr defaultCharStyle;
2530 wxRichTextAttr defaultParaStyle;
2531
2532 // If the default style is a named paragraph style, don't apply any character formatting
2533 // to the initial text string.
2534 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2535 {
2536 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2537 if (def)
2538 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2539 }
2540 else
2541 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2542
2543 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2544 wxRichTextAttr* cStyle = & defaultCharStyle;
2545
2546 wxRichTextParagraph* para = new wxRichTextParagraph(this, pStyle);
2547 para->GetAttributes().GetTextBoxAttr().Reset();
2548 AppendChild(para);
2549 para->AppendChild(new wxRichTextImage(image, this, cStyle));
2550
2551 UpdateRanges();
2552
2553 return para->GetRange();
2554 }
2555
2556
2557 /// Insert fragment into this box at the given position. If partialParagraph is true,
2558 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2559 /// marker.
2560
2561 bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextParagraphLayoutBox& fragment)
2562 {
2563 // First, find the first paragraph whose starting position is within the range.
2564 wxRichTextParagraph* para = GetParagraphAtPosition(position);
2565 if (para)
2566 {
2567 wxRichTextAttr originalAttr = para->GetAttributes();
2568
2569 wxRichTextObjectList::compatibility_iterator node = m_children.Find(para);
2570
2571 // Now split at this position, returning the object to insert the new
2572 // ones in front of.
2573 wxRichTextObject* nextObject = para->SplitAt(position);
2574
2575 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2576 // text, for example, so let's optimize.
2577
2578 if (fragment.GetPartialParagraph() && fragment.GetChildren().GetCount() == 1)
2579 {
2580 // Add the first para to this para...
2581 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2582 if (!firstParaNode)
2583 return false;
2584
2585 // Iterate through the fragment paragraph inserting the content into this paragraph.
2586 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2587 wxASSERT (firstPara != NULL);
2588
2589 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
2590 while (objectNode)
2591 {
2592 wxRichTextObject* newObj = objectNode->GetData()->Clone();
2593
2594 if (!nextObject)
2595 {
2596 // Append
2597 para->AppendChild(newObj);
2598 }
2599 else
2600 {
2601 // Insert before nextObject
2602 para->InsertChild(newObj, nextObject);
2603 }
2604
2605 objectNode = objectNode->GetNext();
2606 }
2607
2608 return true;
2609 }
2610 else
2611 {
2612 // Procedure for inserting a fragment consisting of a number of
2613 // paragraphs:
2614 //
2615 // 1. Remove and save the content that's after the insertion point, for adding
2616 // back once we've added the fragment.
2617 // 2. Add the content from the first fragment paragraph to the current
2618 // paragraph.
2619 // 3. Add remaining fragment paragraphs after the current paragraph.
2620 // 4. Add back the saved content from the first paragraph. If partialParagraph
2621 // is true, add it to the last paragraph added and not a new one.
2622
2623 // 1. Remove and save objects after split point.
2624 wxList savedObjects;
2625 if (nextObject)
2626 para->MoveToList(nextObject, savedObjects);
2627
2628 // 2. Add the content from the 1st fragment paragraph.
2629 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2630 if (!firstParaNode)
2631 return false;
2632
2633 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2634 wxASSERT(firstPara != NULL);
2635
2636 if (!(fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE))
2637 para->SetAttributes(firstPara->GetAttributes());
2638
2639 // Save empty paragraph attributes for appending later
2640 // These are character attributes deliberately set for a new paragraph. Without this,
2641 // we couldn't pass default attributes when appending a new paragraph.
2642 wxRichTextAttr emptyParagraphAttributes;
2643
2644 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
2645
2646 if (objectNode && firstPara->GetChildren().GetCount() == 1 && objectNode->GetData()->IsEmpty())
2647 emptyParagraphAttributes = objectNode->GetData()->GetAttributes();
2648
2649 while (objectNode)
2650 {
2651 wxRichTextObject* newObj = objectNode->GetData()->Clone();
2652
2653 // Append
2654 para->AppendChild(newObj);
2655
2656 objectNode = objectNode->GetNext();
2657 }
2658
2659 // 3. Add remaining fragment paragraphs after the current paragraph.
2660 wxRichTextObjectList::compatibility_iterator nextParagraphNode = node->GetNext();
2661 wxRichTextObject* nextParagraph = NULL;
2662 if (nextParagraphNode)
2663 nextParagraph = nextParagraphNode->GetData();
2664
2665 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst()->GetNext();
2666 wxRichTextParagraph* finalPara = para;
2667
2668 bool needExtraPara = (!i || !fragment.GetPartialParagraph());
2669
2670 // If there was only one paragraph, we need to insert a new one.
2671 while (i)
2672 {
2673 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2674 wxASSERT( para != NULL );
2675
2676 finalPara = (wxRichTextParagraph*) para->Clone();
2677
2678 if (nextParagraph)
2679 InsertChild(finalPara, nextParagraph);
2680 else
2681 AppendChild(finalPara);
2682
2683 i = i->GetNext();
2684 }
2685
2686 // If there was only one paragraph, or we have full paragraphs in our fragment,
2687 // we need to insert a new one.
2688 if (needExtraPara)
2689 {
2690 finalPara = new wxRichTextParagraph;
2691
2692 if (nextParagraph)
2693 InsertChild(finalPara, nextParagraph);
2694 else
2695 AppendChild(finalPara);
2696 }
2697
2698 // 4. Add back the remaining content.
2699 if (finalPara)
2700 {
2701 if (nextObject)
2702 finalPara->MoveFromList(savedObjects);
2703
2704 // Ensure there's at least one object
2705 if (finalPara->GetChildCount() == 0)
2706 {
2707 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
2708 text->SetAttributes(emptyParagraphAttributes);
2709
2710 finalPara->AppendChild(text);
2711 }
2712 }
2713
2714 if ((fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE) && firstPara)
2715 finalPara->SetAttributes(firstPara->GetAttributes());
2716 else if (finalPara && finalPara != para)
2717 finalPara->SetAttributes(originalAttr);
2718
2719 return true;
2720 }
2721 }
2722 else
2723 {
2724 // Append
2725 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst();
2726 while (i)
2727 {
2728 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2729 wxASSERT( para != NULL );
2730
2731 AppendChild(para->Clone());
2732
2733 i = i->GetNext();
2734 }
2735
2736 return true;
2737 }
2738 }
2739
2740 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2741 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2742 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange& range, wxRichTextParagraphLayoutBox& fragment)
2743 {
2744 wxRichTextObjectList::compatibility_iterator i = GetChildren().GetFirst();
2745 while (i)
2746 {
2747 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2748 wxASSERT( para != NULL );
2749
2750 if (!para->GetRange().IsOutside(range))
2751 {
2752 fragment.AppendChild(para->Clone());
2753 }
2754 i = i->GetNext();
2755 }
2756
2757 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2758 if (!fragment.IsEmpty())
2759 {
2760 wxRichTextParagraph* firstPara = wxDynamicCast(fragment.GetChildren().GetFirst()->GetData(), wxRichTextParagraph);
2761 wxASSERT( firstPara != NULL );
2762
2763 wxRichTextParagraph* lastPara = wxDynamicCast(fragment.GetChildren().GetLast()->GetData(), wxRichTextParagraph);
2764 wxASSERT( lastPara != NULL );
2765
2766 if (!firstPara || !lastPara)
2767 return false;
2768
2769 bool isFragment = (range.GetEnd() < lastPara->GetRange().GetEnd());
2770
2771 long firstPos = firstPara->GetRange().GetStart();
2772
2773 // Adjust for renumbering from zero
2774 wxRichTextRange topTailRange(range.GetStart() - firstPos, range.GetEnd() - firstPos);
2775
2776 long end;
2777 fragment.CalculateRange(0, end);
2778
2779 // Chop off the start of the paragraph
2780 if (topTailRange.GetStart() > 0)
2781 {
2782 wxRichTextRange r(0, topTailRange.GetStart()-1);
2783 firstPara->DeleteRange(r);
2784
2785 // Make sure the numbering is correct
2786 fragment.CalculateRange(0, end);
2787
2788 // Now, we've deleted some positions, so adjust the range
2789 // accordingly.
2790 topTailRange.SetStart(range.GetLength());
2791 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
2792 }
2793 else
2794 {
2795 topTailRange.SetStart(range.GetLength());
2796 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
2797 }
2798
2799 if (topTailRange.GetStart() < lastPara->GetRange().GetEnd())
2800 {
2801 lastPara->DeleteRange(topTailRange);
2802
2803 // Make sure the numbering is correct
2804 long end;
2805 fragment.CalculateRange(0, end);
2806
2807 // We only have part of a paragraph at the end
2808 fragment.SetPartialParagraph(true);
2809 }
2810 else
2811 {
2812 // We have a partial paragraph (don't save last new paragraph marker)
2813 // or complete paragraph
2814 fragment.SetPartialParagraph(isFragment);
2815 }
2816 }
2817
2818 return true;
2819 }
2820
2821 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2822 /// starting from zero at the start of the buffer.
2823 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos, bool caretPosition, bool startOfLine) const
2824 {
2825 if (caretPosition)
2826 pos ++;
2827
2828 int lineCount = 0;
2829
2830 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2831 while (node)
2832 {
2833 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2834 // wxASSERT( child != NULL );
2835
2836 if (child)
2837 {
2838 if (child->GetRange().Contains(pos))
2839 {
2840 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2841 while (node2)
2842 {
2843 wxRichTextLine* line = node2->GetData();
2844 wxRichTextRange lineRange = line->GetAbsoluteRange();
2845
2846 if (lineRange.Contains(pos) || pos == lineRange.GetStart())
2847 {
2848 // If the caret is displayed at the end of the previous wrapped line,
2849 // we want to return the line it's _displayed_ at (not the actual line
2850 // containing the position).
2851 if (lineRange.GetStart() == pos && !startOfLine && child->GetRange().GetStart() != pos)
2852 return lineCount - 1;
2853 else
2854 return lineCount;
2855 }
2856
2857 lineCount ++;
2858
2859 node2 = node2->GetNext();
2860 }
2861 // If we didn't find it in the lines, it must be
2862 // the last position of the paragraph. So return the last line.
2863 return lineCount-1;
2864 }
2865 else
2866 lineCount += child->GetLines().GetCount();
2867 }
2868
2869 node = node->GetNext();
2870 }
2871
2872 // Not found
2873 return -1;
2874 }
2875
2876 /// Given a line number, get the corresponding wxRichTextLine object.
2877 wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber) const
2878 {
2879 int lineCount = 0;
2880
2881 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2882 while (node)
2883 {
2884 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2885 // wxASSERT(child != NULL);
2886
2887 if (child)
2888 {
2889 if (lineNumber < (int) (child->GetLines().GetCount() + lineCount))
2890 {
2891 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2892 while (node2)
2893 {
2894 wxRichTextLine* line = node2->GetData();
2895
2896 if (lineCount == lineNumber)
2897 return line;
2898
2899 lineCount ++;
2900
2901 node2 = node2->GetNext();
2902 }
2903 }
2904 else
2905 lineCount += child->GetLines().GetCount();
2906 }
2907
2908 node = node->GetNext();
2909 }
2910
2911 // Didn't find it
2912 return NULL;
2913 }
2914
2915 /// Delete range from layout.
2916 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange& range)
2917 {
2918 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2919
2920 wxRichTextParagraph* firstPara = NULL;
2921 while (node)
2922 {
2923 wxRichTextParagraph* obj = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2924 // wxASSERT (obj != NULL);
2925
2926 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
2927
2928 if (obj)
2929 {
2930 // Delete the range in each paragraph
2931
2932 if (!obj->GetRange().IsOutside(range))
2933 {
2934 // Deletes the content of this object within the given range
2935 obj->DeleteRange(range);
2936
2937 wxRichTextRange thisRange = obj->GetRange();
2938 wxRichTextAttr thisAttr = obj->GetAttributes();
2939
2940 // If the whole paragraph is within the range to delete,
2941 // delete the whole thing.
2942 if (range.GetStart() <= thisRange.GetStart() && range.GetEnd() >= thisRange.GetEnd())
2943 {
2944 // Delete the whole object
2945 RemoveChild(obj, true);
2946 obj = NULL;
2947 }
2948 else if (!firstPara)
2949 firstPara = obj;
2950
2951 // If the range includes the paragraph end, we need to join this
2952 // and the next paragraph.
2953 if (range.GetEnd() <= thisRange.GetEnd())
2954 {
2955 // We need to move the objects from the next paragraph
2956 // to this paragraph
2957
2958 wxRichTextParagraph* nextParagraph = NULL;
2959 if ((range.GetEnd() < thisRange.GetEnd()) && obj)
2960 nextParagraph = obj;
2961 else
2962 {
2963 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2964 if (next)
2965 nextParagraph = wxDynamicCast(next->GetData(), wxRichTextParagraph);
2966 }
2967
2968 bool applyFinalParagraphStyle = firstPara && nextParagraph && nextParagraph != firstPara;
2969
2970 wxRichTextAttr nextParaAttr;
2971 if (applyFinalParagraphStyle)
2972 {
2973 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2974 // not the next one.
2975 if (range.GetStart() == range.GetEnd() && range.GetStart() == thisRange.GetEnd())
2976 nextParaAttr = thisAttr;
2977 else
2978 nextParaAttr = nextParagraph->GetAttributes();
2979 }
2980
2981 if (firstPara && nextParagraph && firstPara != nextParagraph)
2982 {
2983 // Move the objects to the previous para
2984 wxRichTextObjectList::compatibility_iterator node1 = nextParagraph->GetChildren().GetFirst();
2985
2986 while (node1)
2987 {
2988 wxRichTextObject* obj1 = node1->GetData();
2989
2990 firstPara->AppendChild(obj1);
2991
2992 wxRichTextObjectList::compatibility_iterator next1 = node1->GetNext();
2993 nextParagraph->GetChildren().Erase(node1);
2994
2995 node1 = next1;
2996 }
2997
2998 // Delete the paragraph
2999 RemoveChild(nextParagraph, true);
3000 }
3001
3002 // Avoid empty paragraphs
3003 if (firstPara && firstPara->GetChildren().GetCount() == 0)
3004 {
3005 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
3006 firstPara->AppendChild(text);
3007 }
3008
3009 if (applyFinalParagraphStyle)
3010 firstPara->SetAttributes(nextParaAttr);
3011
3012 return true;
3013 }
3014 }
3015 }
3016
3017 node = next;
3018 }
3019
3020 return true;
3021 }
3022
3023 /// Get any text in this object for the given range
3024 wxString wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange& range) const
3025 {
3026 int lineCount = 0;
3027 wxString text;
3028 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3029 while (node)
3030 {
3031 wxRichTextObject* child = node->GetData();
3032 if (!child->GetRange().IsOutside(range))
3033 {
3034 wxRichTextRange childRange = range;
3035 childRange.LimitTo(child->GetRange());
3036
3037 wxString childText = child->GetTextForRange(childRange);
3038
3039 text += childText;
3040
3041 if ((childRange.GetEnd() == child->GetRange().GetEnd()) && node->GetNext())
3042 text += wxT("\n");
3043
3044 lineCount ++;
3045 }
3046 node = node->GetNext();
3047 }
3048
3049 return text;
3050 }
3051
3052 /// Get all the text
3053 wxString wxRichTextParagraphLayoutBox::GetText() const
3054 {
3055 return GetTextForRange(GetOwnRange());
3056 }
3057
3058 /// Get the paragraph by number
3059 wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber) const
3060 {
3061 if ((size_t) paragraphNumber >= GetChildCount())
3062 return NULL;
3063
3064 return (wxRichTextParagraph*) GetChild((size_t) paragraphNumber);
3065 }
3066
3067 /// Get the length of the paragraph
3068 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber) const
3069 {
3070 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3071 if (para)
3072 return para->GetRange().GetLength() - 1; // don't include newline
3073 else
3074 return 0;
3075 }
3076
3077 /// Get the text of the paragraph
3078 wxString wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber) const
3079 {
3080 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3081 if (para)
3082 return para->GetTextForRange(para->GetRange());
3083 else
3084 return wxEmptyString;
3085 }
3086
3087 /// Convert zero-based line column and paragraph number to a position.
3088 long wxRichTextParagraphLayoutBox::XYToPosition(long x, long y) const
3089 {
3090 wxRichTextParagraph* para = GetParagraphAtLine(y);
3091 if (para)
3092 {
3093 return para->GetRange().GetStart() + x;
3094 }
3095 else
3096 return -1;
3097 }
3098
3099 /// Convert zero-based position to line column and paragraph number
3100 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos, long* x, long* y) const
3101 {
3102 wxRichTextParagraph* para = GetParagraphAtPosition(pos);
3103 if (para)
3104 {
3105 int count = 0;
3106 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3107 while (node)
3108 {
3109 wxRichTextObject* child = node->GetData();
3110 if (child == para)
3111 break;
3112 count ++;
3113 node = node->GetNext();
3114 }
3115
3116 *y = count;
3117 *x = pos - para->GetRange().GetStart();
3118
3119 return true;
3120 }
3121 else
3122 return false;
3123 }
3124
3125 /// Get the leaf object in a paragraph at this position.
3126 /// Given a line number, get the corresponding wxRichTextLine object.
3127 wxRichTextObject* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position) const
3128 {
3129 wxRichTextParagraph* para = GetParagraphAtPosition(position);
3130 if (para)
3131 {
3132 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
3133
3134 while (node)
3135 {
3136 wxRichTextObject* child = node->GetData();
3137 if (child->GetRange().Contains(position))
3138 return child;
3139
3140 node = node->GetNext();
3141 }
3142 if (position == para->GetRange().GetEnd() && para->GetChildCount() > 0)
3143 return para->GetChildren().GetLast()->GetData();
3144 }
3145 return NULL;
3146 }
3147
3148 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3149 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, int flags)
3150 {
3151 bool characterStyle = false;
3152 bool paragraphStyle = false;
3153
3154 if (style.IsCharacterStyle())
3155 characterStyle = true;
3156 if (style.IsParagraphStyle())
3157 paragraphStyle = true;
3158
3159 wxRichTextBuffer* buffer = GetBuffer();
3160
3161 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3162 bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3163 bool parasOnly = ((flags & wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY) != 0);
3164 bool charactersOnly = ((flags & wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY) != 0);
3165 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
3166 bool removeStyle = ((flags & wxRICHTEXT_SETSTYLE_REMOVE) != 0);
3167
3168 // Apply paragraph style first, if any
3169 wxRichTextAttr wholeStyle(style);
3170
3171 if (!removeStyle && wholeStyle.HasParagraphStyleName() && buffer->GetStyleSheet())
3172 {
3173 wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->FindParagraphStyle(wholeStyle.GetParagraphStyleName());
3174 if (def)
3175 wxRichTextApplyStyle(wholeStyle, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
3176 }
3177
3178 // Limit the attributes to be set to the content to only character attributes.
3179 wxRichTextAttr characterAttributes(wholeStyle);
3180 characterAttributes.SetFlags(characterAttributes.GetFlags() & (wxTEXT_ATTR_CHARACTER));
3181
3182 if (!removeStyle && characterAttributes.HasCharacterStyleName() && buffer->GetStyleSheet())
3183 {
3184 wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->FindCharacterStyle(characterAttributes.GetCharacterStyleName());
3185 if (def)
3186 wxRichTextApplyStyle(characterAttributes, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
3187 }
3188
3189 // If we are associated with a control, make undoable; otherwise, apply immediately
3190 // to the data.
3191
3192 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3193
3194 wxRichTextAction* action = NULL;
3195
3196 if (haveControl && withUndo)
3197 {
3198 action = new wxRichTextAction(NULL, _("Change Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
3199 action->SetRange(range);
3200 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3201 }
3202
3203 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3204 while (node)
3205 {
3206 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3207 // wxASSERT (para != NULL);
3208
3209 if (para && para->GetChildCount() > 0)
3210 {
3211 // Stop searching if we're beyond the range of interest
3212 if (para->GetRange().GetStart() > range.GetEnd())
3213 break;
3214
3215 if (!para->GetRange().IsOutside(range))
3216 {
3217 // We'll be using a copy of the paragraph to make style changes,
3218 // not updating the buffer directly.
3219 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3220
3221 if (haveControl && withUndo)
3222 {
3223 newPara = new wxRichTextParagraph(*para);
3224 action->GetNewParagraphs().AppendChild(newPara);
3225
3226 // Also store the old ones for Undo
3227 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3228 }
3229 else
3230 newPara = para;
3231
3232 // If we're specifying paragraphs only, then we really mean character formatting
3233 // to be included in the paragraph style
3234 if ((paragraphStyle || parasOnly) && !charactersOnly)
3235 {
3236 if (removeStyle)
3237 {
3238 // Removes the given style from the paragraph
3239 wxRichTextRemoveStyle(newPara->GetAttributes(), style);
3240 }
3241 else if (resetExistingStyle)
3242 newPara->GetAttributes() = wholeStyle;
3243 else
3244 {
3245 if (applyMinimal)
3246 {
3247 // Only apply attributes that will make a difference to the combined
3248 // style as seen on the display
3249 wxRichTextAttr combinedAttr(para->GetCombinedAttributes(true));
3250 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle, & combinedAttr);
3251 }
3252 else
3253 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle);
3254 }
3255 }
3256
3257 // When applying paragraph styles dynamically, don't change the text objects' attributes
3258 // since they will computed as needed. Only apply the character styling if it's _only_
3259 // character styling. This policy is subject to change and might be put under user control.
3260
3261 // Hm. we might well be applying a mix of paragraph and character styles, in which
3262 // case we _do_ want to apply character styles regardless of what para styles are set.
3263 // But if we're applying a paragraph style, which has some character attributes, but
3264 // we only want the paragraphs to hold this character style, then we _don't_ want to
3265 // apply the character style. So we need to be able to choose.
3266
3267 if (!parasOnly && (characterStyle|charactersOnly) && range.GetStart() != newPara->GetRange().GetEnd())
3268 {
3269 wxRichTextRange childRange(range);
3270 childRange.LimitTo(newPara->GetRange());
3271
3272 // Find the starting position and if necessary split it so
3273 // we can start applying a different style.
3274 // TODO: check that the style actually changes or is different
3275 // from style outside of range
3276 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3277 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
3278
3279 if (childRange.GetStart() == newPara->GetRange().GetStart())
3280 firstObject = newPara->GetChildren().GetFirst()->GetData();
3281 else
3282 firstObject = newPara->SplitAt(range.GetStart());
3283
3284 // Increment by 1 because we're apply the style one _after_ the split point
3285 long splitPoint = childRange.GetEnd();
3286 if (splitPoint != newPara->GetRange().GetEnd())
3287 splitPoint ++;
3288
3289 // Find last object
3290 if (splitPoint == newPara->GetRange().GetEnd())
3291 lastObject = newPara->GetChildren().GetLast()->GetData();
3292 else
3293 // lastObject is set as a side-effect of splitting. It's
3294 // returned as the object before the new object.
3295 (void) newPara->SplitAt(splitPoint, & lastObject);
3296
3297 wxASSERT(firstObject != NULL);
3298 wxASSERT(lastObject != NULL);
3299
3300 if (!firstObject || !lastObject)
3301 continue;
3302
3303 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3304 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
3305
3306 wxASSERT(firstNode);
3307 wxASSERT(lastNode);
3308
3309 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
3310
3311 while (node2)
3312 {
3313 wxRichTextObject* child = node2->GetData();
3314
3315 if (removeStyle)
3316 {
3317 // Removes the given style from the paragraph
3318 wxRichTextRemoveStyle(child->GetAttributes(), style);
3319 }
3320 else if (resetExistingStyle)
3321 child->GetAttributes() = characterAttributes;
3322 else
3323 {
3324 if (applyMinimal)
3325 {
3326 // Only apply attributes that will make a difference to the combined
3327 // style as seen on the display
3328 wxRichTextAttr combinedAttr(newPara->GetCombinedAttributes(child->GetAttributes(), true));
3329 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes, & combinedAttr);
3330 }
3331 else
3332 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes);
3333 }
3334
3335 if (node2 == lastNode)
3336 break;
3337
3338 node2 = node2->GetNext();
3339 }
3340 }
3341 }
3342 }
3343
3344 node = node->GetNext();
3345 }
3346
3347 // Do action, or delay it until end of batch.
3348 if (haveControl && withUndo)
3349 buffer->SubmitAction(action);
3350
3351 return true;
3352 }
3353
3354 // Just change the attributes for this single object.
3355 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject* obj, const wxRichTextAttr& textAttr, int flags)
3356 {
3357 wxRichTextBuffer* buffer = GetBuffer();
3358 bool withUndo = flags & wxRICHTEXT_SETSTYLE_WITH_UNDO;
3359 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
3360 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3361
3362 wxRichTextAction *action = NULL;
3363 wxRichTextAttr newAttr = obj->GetAttributes();
3364 if (resetExistingStyle)
3365 newAttr = textAttr;
3366 else
3367 newAttr.Apply(textAttr);
3368
3369 if (haveControl && withUndo)
3370 {
3371 action = new wxRichTextAction(NULL, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES, buffer, obj->GetContainer(), buffer->GetRichTextCtrl());
3372 action->SetRange(obj->GetRange().FromInternal());
3373 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3374 action->MakeObject(obj);
3375
3376 action->GetAttributes() = newAttr;
3377 }
3378 else
3379 obj->GetAttributes() = newAttr;
3380
3381 if (haveControl && withUndo)
3382 buffer->SubmitAction(action);
3383 }
3384
3385 /// Get the text attributes for this position.
3386 bool wxRichTextParagraphLayoutBox::GetStyle(long position, wxRichTextAttr& style)
3387 {
3388 return DoGetStyle(position, style, true);
3389 }
3390
3391 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position, wxRichTextAttr& style)
3392 {
3393 return DoGetStyle(position, style, false);
3394 }
3395
3396 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3397 /// context attributes.
3398 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position, wxRichTextAttr& style, bool combineStyles)
3399 {
3400 wxRichTextObject* obj wxDUMMY_INITIALIZE(NULL);
3401
3402 if (style.IsParagraphStyle())
3403 {
3404 obj = GetParagraphAtPosition(position);
3405 if (obj)
3406 {
3407 if (combineStyles)
3408 {
3409 // Start with the base style
3410 style = GetAttributes();
3411 style.GetTextBoxAttr().Reset();
3412
3413 // Apply the paragraph style
3414 wxRichTextApplyStyle(style, obj->GetAttributes());
3415 }
3416 else
3417 style = obj->GetAttributes();
3418
3419 return true;
3420 }
3421 }
3422 else
3423 {
3424 obj = GetLeafObjectAtPosition(position);
3425 if (obj)
3426 {
3427 if (combineStyles)
3428 {
3429 wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph);
3430 style = para ? para->GetCombinedAttributes(obj->GetAttributes()) : obj->GetAttributes();
3431 }
3432 else
3433 style = obj->GetAttributes();
3434
3435 return true;
3436 }
3437 }
3438 return false;
3439 }
3440
3441 static bool wxHasStyle(long flags, long style)
3442 {
3443 return (flags & style) != 0;
3444 }
3445
3446 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3447 /// content.
3448 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr& currentStyle, const wxRichTextAttr& style, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
3449 {
3450 currentStyle.CollectCommonAttributes(style, clashingAttr, absentAttr);
3451
3452 return true;
3453 }
3454
3455 /// Get the combined style for a range - if any attribute is different within the range,
3456 /// that attribute is not present within the flags.
3457 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3458 /// nested.
3459 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange& range, wxRichTextAttr& style)
3460 {
3461 style = wxRichTextAttr();
3462
3463 wxRichTextAttr clashingAttrPara, clashingAttrChar;
3464 wxRichTextAttr absentAttrPara, absentAttrChar;
3465
3466 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
3467 while (node)
3468 {
3469 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3470 if (para && !(para->GetRange().GetStart() > range.GetEnd() || para->GetRange().GetEnd() < range.GetStart()))
3471 {
3472 if (para->GetChildren().GetCount() == 0)
3473 {
3474 wxRichTextAttr paraStyle = para->GetCombinedAttributes(true /* use box attributes */);
3475
3476 CollectStyle(style, paraStyle, clashingAttrPara, absentAttrPara);
3477 }
3478 else
3479 {
3480 wxRichTextRange paraRange(para->GetRange());
3481 paraRange.LimitTo(range);
3482
3483 // First collect paragraph attributes only
3484 wxRichTextAttr paraStyle = para->GetCombinedAttributes();
3485 paraStyle.SetFlags(paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH);
3486 CollectStyle(style, paraStyle, clashingAttrPara, absentAttrPara);
3487
3488 wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
3489
3490 while (childNode)
3491 {
3492 wxRichTextObject* child = childNode->GetData();
3493 if (!(child->GetRange().GetStart() > range.GetEnd() || child->GetRange().GetEnd() < range.GetStart()))
3494 {
3495 wxRichTextAttr childStyle = para->GetCombinedAttributes(child->GetAttributes(), true /* include box attributes */);
3496
3497 // Now collect character attributes only
3498 childStyle.SetFlags(childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER);
3499
3500 CollectStyle(style, childStyle, clashingAttrChar, absentAttrChar);
3501 }
3502
3503 childNode = childNode->GetNext();
3504 }
3505 }
3506 }
3507 node = node->GetNext();
3508 }
3509 return true;
3510 }
3511
3512 /// Set default style
3513 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr& style)
3514 {
3515 m_defaultAttributes = style;
3516 return true;
3517 }
3518
3519 /// Test if this whole range has character attributes of the specified kind. If any
3520 /// of the attributes are different within the range, the test fails. You
3521 /// can use this to implement, for example, bold button updating. style must have
3522 /// flags indicating which attributes are of interest.
3523 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3524 {
3525 int foundCount = 0;
3526 int matchingCount = 0;
3527
3528 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3529 while (node)
3530 {
3531 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3532 // wxASSERT (para != NULL);
3533
3534 if (para)
3535 {
3536 // Stop searching if we're beyond the range of interest
3537 if (para->GetRange().GetStart() > range.GetEnd())
3538 return foundCount == matchingCount && foundCount != 0;
3539
3540 if (!para->GetRange().IsOutside(range))
3541 {
3542 wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
3543
3544 while (node2)
3545 {
3546 wxRichTextObject* child = node2->GetData();
3547 // Allow for empty string if no buffer
3548 wxRichTextRange childRange = child->GetRange();
3549 if (childRange.GetLength() == 0 && GetRange().GetLength() == 1)
3550 childRange.SetEnd(childRange.GetEnd()+1);
3551
3552 if (!childRange.IsOutside(range) && wxDynamicCast(child, wxRichTextPlainText))
3553 {
3554 foundCount ++;
3555 wxRichTextAttr textAttr = para->GetCombinedAttributes(child->GetAttributes());
3556
3557 if (textAttr.EqPartial(style, false /* strong test - attributes must be valid in both objects */))
3558 matchingCount ++;
3559 }
3560
3561 node2 = node2->GetNext();
3562 }
3563 }
3564 }
3565
3566 node = node->GetNext();
3567 }
3568
3569 return foundCount == matchingCount && foundCount != 0;
3570 }
3571
3572 /// Test if this whole range has paragraph attributes of the specified kind. If any
3573 /// of the attributes are different within the range, the test fails. You
3574 /// can use this to implement, for example, centering button updating. style must have
3575 /// flags indicating which attributes are of interest.
3576 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3577 {
3578 int foundCount = 0;
3579 int matchingCount = 0;
3580
3581 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3582 while (node)
3583 {
3584 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3585 // wxASSERT (para != NULL);
3586
3587 if (para)
3588 {
3589 // Stop searching if we're beyond the range of interest
3590 if (para->GetRange().GetStart() > range.GetEnd())
3591 return foundCount == matchingCount && foundCount != 0;
3592
3593 if (!para->GetRange().IsOutside(range))
3594 {
3595 wxRichTextAttr textAttr = GetAttributes();
3596 // Apply the paragraph style
3597 wxRichTextApplyStyle(textAttr, para->GetAttributes());
3598
3599 foundCount ++;
3600 if (textAttr.EqPartial(style, false /* strong test */))
3601 matchingCount ++;
3602 }
3603 }
3604
3605 node = node->GetNext();
3606 }
3607 return foundCount == matchingCount && foundCount != 0;
3608 }
3609
3610 void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox& container)
3611 {
3612 wxRichTextBuffer* buffer = GetBuffer();
3613 if (buffer && buffer->GetRichTextCtrl())
3614 buffer->GetRichTextCtrl()->PrepareContent(container);
3615 }
3616
3617 /// Set character or paragraph properties
3618 bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange& range, const wxRichTextProperties& properties, int flags)
3619 {
3620 wxRichTextBuffer* buffer = GetBuffer();
3621
3622 bool withUndo = ((flags & wxRICHTEXT_SETPROPERTIES_WITH_UNDO) != 0);
3623 bool parasOnly = ((flags & wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY) != 0);
3624 bool charactersOnly = ((flags & wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY) != 0);
3625 bool resetExistingProperties = ((flags & wxRICHTEXT_SETPROPERTIES_RESET) != 0);
3626 bool removeProperties = ((flags & wxRICHTEXT_SETPROPERTIES_REMOVE) != 0);
3627
3628 // If we are associated with a control, make undoable; otherwise, apply immediately
3629 // to the data.
3630
3631 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3632
3633 wxRichTextAction* action = NULL;
3634
3635 if (haveControl && withUndo)
3636 {
3637 action = new wxRichTextAction(NULL, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES, buffer, this, buffer->GetRichTextCtrl());
3638 action->SetRange(range);
3639 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3640 }
3641
3642 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3643 while (node)
3644 {
3645 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3646 // wxASSERT (para != NULL);
3647
3648 if (para && para->GetChildCount() > 0)
3649 {
3650 // Stop searching if we're beyond the range of interest
3651 if (para->GetRange().GetStart() > range.GetEnd())
3652 break;
3653
3654 if (!para->GetRange().IsOutside(range))
3655 {
3656 // We'll be using a copy of the paragraph to make style changes,
3657 // not updating the buffer directly.
3658 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3659
3660 if (haveControl && withUndo)
3661 {
3662 newPara = new wxRichTextParagraph(*para);
3663 action->GetNewParagraphs().AppendChild(newPara);
3664
3665 // Also store the old ones for Undo
3666 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3667 }
3668 else
3669 newPara = para;
3670
3671 if (parasOnly)
3672 {
3673 if (removeProperties)
3674 {
3675 // Removes the given style from the paragraph
3676 // TODO
3677 newPara->GetProperties().RemoveProperties(properties);
3678 }
3679 else if (resetExistingProperties)
3680 newPara->GetProperties() = properties;
3681 else
3682 newPara->GetProperties().MergeProperties(properties);
3683 }
3684
3685 // When applying paragraph styles dynamically, don't change the text objects' attributes
3686 // since they will computed as needed. Only apply the character styling if it's _only_
3687 // character styling. This policy is subject to change and might be put under user control.
3688
3689 // Hm. we might well be applying a mix of paragraph and character styles, in which
3690 // case we _do_ want to apply character styles regardless of what para styles are set.
3691 // But if we're applying a paragraph style, which has some character attributes, but
3692 // we only want the paragraphs to hold this character style, then we _don't_ want to
3693 // apply the character style. So we need to be able to choose.
3694
3695 if (!parasOnly && charactersOnly && range.GetStart() != newPara->GetRange().GetEnd())
3696 {
3697 wxRichTextRange childRange(range);
3698 childRange.LimitTo(newPara->GetRange());
3699
3700 // Find the starting position and if necessary split it so
3701 // we can start applying different properties.
3702 // TODO: check that the properties actually change or are different
3703 // from properties outside of range
3704 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3705 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
3706
3707 if (childRange.GetStart() == newPara->GetRange().GetStart())
3708 firstObject = newPara->GetChildren().GetFirst()->GetData();
3709 else
3710 firstObject = newPara->SplitAt(range.GetStart());
3711
3712 // Increment by 1 because we're apply the style one _after_ the split point
3713 long splitPoint = childRange.GetEnd();
3714 if (splitPoint != newPara->GetRange().GetEnd())
3715 splitPoint ++;
3716
3717 // Find last object
3718 if (splitPoint == newPara->GetRange().GetEnd())
3719 lastObject = newPara->GetChildren().GetLast()->GetData();
3720 else
3721 // lastObject is set as a side-effect of splitting. It's
3722 // returned as the object before the new object.
3723 (void) newPara->SplitAt(splitPoint, & lastObject);
3724
3725 wxASSERT(firstObject != NULL);
3726 wxASSERT(lastObject != NULL);
3727
3728 if (!firstObject || !lastObject)
3729 continue;
3730
3731 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3732 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
3733
3734 wxASSERT(firstNode);
3735 wxASSERT(lastNode);
3736
3737 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
3738
3739 while (node2)
3740 {
3741 wxRichTextObject* child = node2->GetData();
3742
3743 if (removeProperties)
3744 {
3745 // Removes the given properties from the paragraph
3746 child->GetProperties().RemoveProperties(properties);
3747 }
3748 else if (resetExistingProperties)
3749 child->GetProperties() = properties;
3750 else
3751 {
3752 child->GetProperties().MergeProperties(properties);
3753 }
3754
3755 if (node2 == lastNode)
3756 break;
3757
3758 node2 = node2->GetNext();
3759 }
3760 }
3761 }
3762 }
3763
3764 node = node->GetNext();
3765 }
3766
3767 // Do action, or delay it until end of batch.
3768 if (haveControl && withUndo)
3769 buffer->SubmitAction(action);
3770
3771 return true;
3772 }
3773
3774 void wxRichTextParagraphLayoutBox::Reset()
3775 {
3776 Clear();
3777
3778 wxRichTextBuffer* buffer = GetBuffer();
3779 if (buffer && buffer->GetRichTextCtrl())
3780 {
3781 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET, buffer->GetRichTextCtrl()->GetId());
3782 event.SetEventObject(buffer->GetRichTextCtrl());
3783 event.SetContainer(this);
3784
3785 buffer->SendEvent(event, true);
3786 }
3787
3788 AddParagraph(wxEmptyString);
3789
3790 PrepareContent(*this);
3791
3792 InvalidateHierarchy(wxRICHTEXT_ALL);
3793 }
3794
3795 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3796 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange& invalidRange)
3797 {
3798 wxRichTextCompositeObject::Invalidate(invalidRange);
3799
3800 DoInvalidate(invalidRange);
3801 }
3802
3803 // Do the (in)validation for this object only
3804 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange& invalidRange)
3805 {
3806 if (invalidRange == wxRICHTEXT_ALL)
3807 {
3808 m_invalidRange = wxRICHTEXT_ALL;
3809 }
3810 // Already invalidating everything
3811 else if (m_invalidRange == wxRICHTEXT_ALL)
3812 {
3813 }
3814 else
3815 {
3816 if ((invalidRange.GetStart() < m_invalidRange.GetStart()) || m_invalidRange.GetStart() == -1)
3817 m_invalidRange.SetStart(invalidRange.GetStart());
3818 if (invalidRange.GetEnd() > m_invalidRange.GetEnd())
3819 m_invalidRange.SetEnd(invalidRange.GetEnd());
3820 }
3821 }
3822
3823 // Do the (in)validation both up and down the hierarchy
3824 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange& invalidRange)
3825 {
3826 Invalidate(invalidRange);
3827
3828 if (invalidRange != wxRICHTEXT_NONE)
3829 {
3830 // Now go up the hierarchy
3831 wxRichTextObject* thisObj = this;
3832 wxRichTextObject* p = GetParent();
3833 while (p)
3834 {
3835 wxRichTextParagraphLayoutBox* l = wxDynamicCast(p, wxRichTextParagraphLayoutBox);
3836 if (l)
3837 l->DoInvalidate(thisObj->GetRange());
3838
3839 thisObj = p;
3840 p = p->GetParent();
3841 }
3842 }
3843 }
3844
3845 /// Get invalid range, rounding to entire paragraphs if argument is true.
3846 wxRichTextRange wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs) const
3847 {
3848 if (m_invalidRange == wxRICHTEXT_ALL || m_invalidRange == wxRICHTEXT_NONE)
3849 return m_invalidRange;
3850
3851 wxRichTextRange range = m_invalidRange;
3852
3853 if (wholeParagraphs)
3854 {
3855 wxRichTextParagraph* para1 = GetParagraphAtPosition(range.GetStart());
3856 if (para1)
3857 range.SetStart(para1->GetRange().GetStart());
3858
3859 // FIXME: be more intelligent about this. Check if we have floating objects
3860 // before the end of the range. But it's not clear how we can in general
3861 // tell where it's safe to stop laying out.
3862 // Anyway, this code is central to efficiency when laying in floating mode.
3863 if (!wxRichTextBuffer::GetFloatingLayoutMode())
3864 {
3865 wxRichTextParagraph* para2 = GetParagraphAtPosition(range.GetEnd());
3866 if (para2)
3867 range.SetEnd(para2->GetRange().GetEnd());
3868 }
3869 else
3870 // Floating layout means that all children should be laid out,
3871 // because we can't tell how the whole buffer will be affected.
3872 range.SetEnd(GetOwnRange().GetEnd());
3873 }
3874 return range;
3875 }
3876
3877 /// Apply the style sheet to the buffer, for example if the styles have changed.
3878 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet* styleSheet)
3879 {
3880 wxASSERT(styleSheet != NULL);
3881 if (!styleSheet)
3882 return false;
3883
3884 int foundCount = 0;
3885
3886 wxRichTextAttr attr(GetBasicStyle());
3887 if (GetBasicStyle().HasParagraphStyleName())
3888 {
3889 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3890 if (paraDef)
3891 {
3892 attr.Apply(paraDef->GetStyleMergedWithBase(styleSheet));
3893 SetBasicStyle(attr);
3894 foundCount ++;
3895 }
3896 }
3897
3898 if (GetBasicStyle().HasCharacterStyleName())
3899 {
3900 wxRichTextCharacterStyleDefinition* charDef = styleSheet->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3901 if (charDef)
3902 {
3903 attr.Apply(charDef->GetStyleMergedWithBase(styleSheet));
3904 SetBasicStyle(attr);
3905 foundCount ++;
3906 }
3907 }
3908
3909 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3910 while (node)
3911 {
3912 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3913 // wxASSERT (para != NULL);
3914
3915 if (para)
3916 {
3917 // Combine paragraph and list styles. If there is a list style in the original attributes,
3918 // the current indentation overrides anything else and is used to find the item indentation.
3919 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3920 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3921 // exception as above).
3922 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3923 // So when changing a list style interactively, could retrieve level based on current style, then
3924 // set appropriate indent and apply new style.
3925
3926 int outline = -1;
3927 int num = -1;
3928 if (para->GetAttributes().HasOutlineLevel())
3929 outline = para->GetAttributes().GetOutlineLevel();
3930 if (para->GetAttributes().HasBulletNumber())
3931 num = para->GetAttributes().GetBulletNumber();
3932
3933 if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3934 {
3935 int currentIndent = para->GetAttributes().GetLeftIndent();
3936
3937 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3938 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3939 if (paraDef && !listDef)
3940 {
3941 para->GetAttributes() = paraDef->GetStyleMergedWithBase(styleSheet);
3942 foundCount ++;
3943 }
3944 else if (listDef && !paraDef)
3945 {
3946 // Set overall style defined for the list style definition
3947 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
3948
3949 // Apply the style for this level
3950 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3951 foundCount ++;
3952 }
3953 else if (listDef && paraDef)
3954 {
3955 // Combines overall list style, style for level, and paragraph style
3956 para->GetAttributes() = listDef->CombineWithParagraphStyle(currentIndent, paraDef->GetStyleMergedWithBase(styleSheet));
3957 foundCount ++;
3958 }
3959 }
3960 else if (para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3961 {
3962 int currentIndent = para->GetAttributes().GetLeftIndent();
3963
3964 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3965
3966 // Overall list definition style
3967 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
3968
3969 // Style for this level
3970 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3971
3972 foundCount ++;
3973 }
3974 else if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && para->GetAttributes().GetListStyleName().IsEmpty())
3975 {
3976 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3977 if (def)
3978 {
3979 para->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
3980 foundCount ++;
3981 }
3982 }
3983
3984 if (outline != -1)
3985 para->GetAttributes().SetOutlineLevel(outline);
3986 if (num != -1)
3987 para->GetAttributes().SetBulletNumber(num);
3988 }
3989
3990 node = node->GetNext();
3991 }
3992 return foundCount != 0;
3993 }
3994
3995 /// Set list style
3996 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
3997 {
3998 wxRichTextBuffer* buffer = GetBuffer();
3999 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
4000
4001 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
4002 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4003 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
4004 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
4005
4006 // Current number, if numbering
4007 int n = startFrom;
4008
4009 wxASSERT (!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
4010
4011 // If we are associated with a control, make undoable; otherwise, apply immediately
4012 // to the data.
4013
4014 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
4015
4016 wxRichTextAction* action = NULL;
4017
4018 if (haveControl && withUndo)
4019 {
4020 action = new wxRichTextAction(NULL, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
4021 action->SetRange(range);
4022 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
4023 }
4024
4025 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4026 while (node)
4027 {
4028 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
4029 // wxASSERT (para != NULL);
4030
4031 if (para && para->GetChildCount() > 0)
4032 {
4033 // Stop searching if we're beyond the range of interest
4034 if (para->GetRange().GetStart() > range.GetEnd())
4035 break;
4036
4037 if (!para->GetRange().IsOutside(range))
4038 {
4039 // We'll be using a copy of the paragraph to make style changes,
4040 // not updating the buffer directly.
4041 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
4042
4043 if (haveControl && withUndo)
4044 {
4045 newPara = new wxRichTextParagraph(*para);
4046 action->GetNewParagraphs().AppendChild(newPara);
4047
4048 // Also store the old ones for Undo
4049 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
4050 }
4051 else
4052 newPara = para;
4053
4054 if (def)
4055 {
4056 int thisIndent = newPara->GetAttributes().GetLeftIndent();
4057 int thisLevel = specifyLevel ? specifiedLevel : def->FindLevelForIndent(thisIndent);
4058
4059 // How is numbering going to work?
4060 // If we are renumbering, or numbering for the first time, we need to keep
4061 // track of the number for each level. But we might be simply applying a different
4062 // list style.
4063 // In Word, applying a style to several paragraphs, even if at different levels,
4064 // reverts the level back to the same one. So we could do the same here.
4065 // Renumbering will need to be done when we promote/demote a paragraph.
4066
4067 // Apply the overall list style, and item style for this level
4068 wxRichTextAttr listStyle(def->GetCombinedStyleForLevel(thisLevel, styleSheet));
4069 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
4070
4071 // Now we need to do numbering
4072 // Preserve the existing list item continuation bullet style, if any
4073 if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))
4074 newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
4075 else
4076 {
4077 if (renumber)
4078 {
4079 newPara->GetAttributes().SetBulletNumber(n);
4080 }
4081
4082 n ++;
4083 }
4084 }
4085 else if (!newPara->GetAttributes().GetListStyleName().IsEmpty())
4086 {
4087 // if def is NULL, remove list style, applying any associated paragraph style
4088 // to restore the attributes
4089
4090 newPara->GetAttributes().SetListStyleName(wxEmptyString);
4091 newPara->GetAttributes().SetLeftIndent(0, 0);
4092 newPara->GetAttributes().SetBulletText(wxEmptyString);
4093 newPara->GetAttributes().SetBulletStyle(0);
4094
4095 // Eliminate the main list-related attributes
4096 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);
4097
4098 if (styleSheet && !newPara->GetAttributes().GetParagraphStyleName().IsEmpty())
4099 {
4100 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(newPara->GetAttributes().GetParagraphStyleName());
4101 if (def)
4102 {
4103 newPara->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
4104 }
4105 }
4106 }
4107 }
4108 }
4109
4110 node = node->GetNext();
4111 }
4112
4113 // Do action, or delay it until end of batch.
4114 if (haveControl && withUndo)
4115 buffer->SubmitAction(action);
4116
4117 return true;
4118 }
4119
4120 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4121 {
4122 wxRichTextBuffer* buffer = GetBuffer();
4123 if (buffer && buffer->GetStyleSheet())
4124 {
4125 wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->FindListStyle(defName);
4126 if (def)
4127 return SetListStyle(range, def, flags, startFrom, specifiedLevel);
4128 }
4129 return false;
4130 }
4131
4132 /// Clear list for given range
4133 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange& range, int flags)
4134 {
4135 return SetListStyle(range, NULL, flags);
4136 }
4137
4138 /// Number/renumber any list elements in the given range
4139 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4140 {
4141 return DoNumberList(range, range, 0, def, flags, startFrom, specifiedLevel);
4142 }
4143
4144 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4145 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, const wxRichTextRange& promotionRange, int promoteBy,
4146 wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4147 {
4148 wxRichTextBuffer* buffer = GetBuffer();
4149 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
4150
4151 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
4152 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4153 #if wxDEBUG_LEVEL
4154 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
4155 #endif
4156
4157 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
4158
4159 // Max number of levels
4160 const int maxLevels = 10;
4161
4162 // The level we're looking at now
4163 int currentLevel = -1;
4164
4165 // The item number for each level
4166 int levels[maxLevels];
4167 int i;
4168
4169 // Reset all numbering
4170 for (i = 0; i < maxLevels; i++)
4171 {
4172 if (startFrom != -1)
4173 levels[i] = startFrom-1;
4174 else if (renumber) // start again
4175 levels[i] = 0;
4176 else
4177 levels[i] = -1; // start from the number we found, if any
4178 }
4179
4180 #if wxDEBUG_LEVEL
4181 wxASSERT(!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
4182 #endif
4183
4184 // If we are associated with a control, make undoable; otherwise, apply immediately
4185 // to the data.
4186
4187 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
4188
4189 wxRichTextAction* action = NULL;
4190
4191 if (haveControl && withUndo)
4192 {
4193 action = new wxRichTextAction(NULL, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
4194 action->SetRange(range);
4195 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
4196 }
4197
4198 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4199 while (node)
4200 {
4201 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
4202 // wxASSERT (para != NULL);
4203
4204 if (para && para->GetChildCount() > 0)
4205 {
4206 // Stop searching if we're beyond the range of interest
4207 if (para->GetRange().GetStart() > range.GetEnd())
4208 break;
4209
4210 if (!para->GetRange().IsOutside(range))
4211 {
4212 // We'll be using a copy of the paragraph to make style changes,
4213 // not updating the buffer directly.
4214 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
4215
4216 if (haveControl && withUndo)
4217 {
4218 newPara = new wxRichTextParagraph(*para);
4219 action->GetNewParagraphs().AppendChild(newPara);
4220
4221 // Also store the old ones for Undo
4222 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
4223 }
4224 else
4225 newPara = para;
4226
4227 wxRichTextListStyleDefinition* defToUse = def;
4228 if (!defToUse)
4229 {
4230 if (styleSheet && !newPara->GetAttributes().GetListStyleName().IsEmpty())
4231 defToUse = styleSheet->FindListStyle(newPara->GetAttributes().GetListStyleName());
4232 }
4233
4234 if (defToUse)
4235 {
4236 int thisIndent = newPara->GetAttributes().GetLeftIndent();
4237 int thisLevel = defToUse->FindLevelForIndent(thisIndent);
4238
4239 // If we've specified a level to apply to all, change the level.
4240 if (specifiedLevel != -1)
4241 thisLevel = specifiedLevel;
4242
4243 // Do promotion if specified
4244 if ((promoteBy != 0) && !para->GetRange().IsOutside(promotionRange))
4245 {
4246 thisLevel = thisLevel - promoteBy;
4247 if (thisLevel < 0)
4248 thisLevel = 0;
4249 if (thisLevel > 9)
4250 thisLevel = 9;
4251 }
4252
4253 // Apply the overall list style, and item style for this level
4254 wxRichTextAttr listStyle(defToUse->GetCombinedStyleForLevel(thisLevel, styleSheet));
4255 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
4256
4257 // Preserve the existing list item continuation bullet style, if any
4258 if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))
4259 newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
4260
4261 // OK, we've (re)applied the style, now let's get the numbering right.
4262
4263 if (currentLevel == -1)
4264 currentLevel = thisLevel;
4265
4266 // Same level as before, do nothing except increment level's number afterwards
4267 if (currentLevel == thisLevel)
4268 {
4269 }
4270 // A deeper level: start renumbering all levels after current level
4271 else if (thisLevel > currentLevel)
4272 {
4273 for (i = currentLevel+1; i <= thisLevel; i++)
4274 {
4275 levels[i] = 0;
4276 }
4277 currentLevel = thisLevel;
4278 }
4279 else if (thisLevel < currentLevel)
4280 {
4281 currentLevel = thisLevel;
4282 }
4283
4284 // Use the current numbering if -1 and we have a bullet number already
4285 if (levels[currentLevel] == -1)
4286 {
4287 if (newPara->GetAttributes().HasBulletNumber())
4288 levels[currentLevel] = newPara->GetAttributes().GetBulletNumber();
4289 else
4290 levels[currentLevel] = 1;
4291 }
4292 else
4293 {
4294 if (!(para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)))
4295 levels[currentLevel] ++;
4296 }
4297
4298 newPara->GetAttributes().SetBulletNumber(levels[currentLevel]);
4299
4300 // Create the bullet text if an outline list
4301 if (listStyle.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
4302 {
4303 wxString text;
4304 for (i = 0; i <= currentLevel; i++)
4305 {
4306 if (!text.IsEmpty())
4307 text += wxT(".");
4308 text += wxString::Format(wxT("%d"), levels[i]);
4309 }
4310 newPara->GetAttributes().SetBulletText(text);
4311 }
4312 }
4313 }
4314 }
4315
4316 node = node->GetNext();
4317 }
4318
4319 // Do action, or delay it until end of batch.
4320 if (haveControl && withUndo)
4321 buffer->SubmitAction(action);
4322
4323 return true;
4324 }
4325
4326 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4327 {
4328 wxRichTextBuffer* buffer = GetBuffer();
4329 if (buffer->GetStyleSheet())
4330 {
4331 wxRichTextListStyleDefinition* def = NULL;
4332 if (!defName.IsEmpty())
4333 def = buffer->GetStyleSheet()->FindListStyle(defName);
4334 return NumberList(range, def, flags, startFrom, specifiedLevel);
4335 }
4336 return false;
4337 }
4338
4339 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4340 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel)
4341 {
4342 // TODO
4343 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4344 // to NumberList with a flag indicating promotion is required within one of the ranges.
4345 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4346 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4347 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4348 // list position will start from 1.
4349 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4350 // We can end the renumbering at this point.
4351
4352 // For now, only renumber within the promotion range.
4353
4354 return DoNumberList(range, range, promoteBy, def, flags, 1, specifiedLevel);
4355 }
4356
4357 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel)
4358 {
4359 wxRichTextBuffer* buffer = GetBuffer();
4360 if (buffer->GetStyleSheet())
4361 {
4362 wxRichTextListStyleDefinition* def = NULL;
4363 if (!defName.IsEmpty())
4364 def = buffer->GetStyleSheet()->FindListStyle(defName);
4365 return PromoteList(promoteBy, range, def, flags, specifiedLevel);
4366 }
4367 return false;
4368 }
4369
4370 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4371 /// position of the paragraph that it had to start looking from.
4372 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr) const
4373 {
4374 // TODO: add GetNextChild/GetPreviousChild to composite
4375 // Search for a paragraph that isn't a continuation paragraph (no bullet)
4376 while (previousParagraph && previousParagraph->GetAttributes().HasBulletStyle() && previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)
4377 {
4378 wxRichTextObjectList::compatibility_iterator node = ((wxRichTextCompositeObject*) previousParagraph->GetParent())->GetChildren().Find(previousParagraph);
4379 if (node)
4380 {
4381 node = node->GetPrevious();
4382 if (node)
4383 previousParagraph = wxDynamicCast(node->GetData(), wxRichTextParagraph);
4384 else
4385 previousParagraph = NULL;
4386 }
4387 else
4388 previousParagraph = NULL;
4389 }
4390
4391 if (!previousParagraph || !previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE)
4392 return false;
4393
4394 wxRichTextBuffer* buffer = GetBuffer();
4395 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
4396 if (styleSheet && !previousParagraph->GetAttributes().GetListStyleName().IsEmpty())
4397 {
4398 wxRichTextListStyleDefinition* def = styleSheet->FindListStyle(previousParagraph->GetAttributes().GetListStyleName());
4399 if (def)
4400 {
4401 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4402 // int thisLevel = def->FindLevelForIndent(thisIndent);
4403
4404 bool isOutline = (previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE) != 0;
4405
4406 attr.SetFlags(previousParagraph->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER|wxTEXT_ATTR_BULLET_TEXT|wxTEXT_ATTR_BULLET_NAME));
4407 if (previousParagraph->GetAttributes().HasBulletName())
4408 attr.SetBulletName(previousParagraph->GetAttributes().GetBulletName());
4409 attr.SetBulletStyle(previousParagraph->GetAttributes().GetBulletStyle());
4410 attr.SetListStyleName(previousParagraph->GetAttributes().GetListStyleName());
4411
4412 int nextNumber = previousParagraph->GetAttributes().GetBulletNumber() + 1;
4413 attr.SetBulletNumber(nextNumber);
4414
4415 if (isOutline)
4416 {
4417 wxString text = previousParagraph->GetAttributes().GetBulletText();
4418 if (!text.IsEmpty())
4419 {
4420 int pos = text.Find(wxT('.'), true);
4421 if (pos != wxNOT_FOUND)
4422 {
4423 text = text.Mid(0, text.Length() - pos - 1);
4424 }
4425 else
4426 text = wxEmptyString;
4427 if (!text.IsEmpty())
4428 text += wxT(".");
4429 text += wxString::Format(wxT("%d"), nextNumber);
4430 attr.SetBulletText(text);
4431 }
4432 }
4433
4434 return true;
4435 }
4436 else
4437 return false;
4438 }
4439 else
4440 return false;
4441 }
4442
4443 /*!
4444 * wxRichTextParagraph
4445 * This object represents a single paragraph (or in a straight text editor, a line).
4446 */
4447
4448 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph, wxRichTextCompositeObject)
4449
4450 wxArrayInt wxRichTextParagraph::sm_defaultTabs;
4451
4452 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject* parent, wxRichTextAttr* style):
4453 wxRichTextCompositeObject(parent)
4454 {
4455 if (style)
4456 SetAttributes(*style);
4457 }
4458
4459 wxRichTextParagraph::wxRichTextParagraph(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* paraStyle, wxRichTextAttr* charStyle):
4460 wxRichTextCompositeObject(parent)
4461 {
4462 if (paraStyle)
4463 SetAttributes(*paraStyle);
4464
4465 AppendChild(new wxRichTextPlainText(text, this, charStyle));
4466 }
4467
4468 wxRichTextParagraph::~wxRichTextParagraph()
4469 {
4470 ClearLines();
4471 }
4472
4473 /// Draw the item
4474 bool wxRichTextParagraph::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int style)
4475 {
4476 if (!IsShown())
4477 return true;
4478
4479 // Currently we don't merge these attributes with the parent, but we
4480 // should consider whether we should (e.g. if we set a border colour
4481 // for all paragraphs). But generally box attributes are likely to be
4482 // different for different objects.
4483 wxRect paraRect = GetRect();
4484 wxRichTextAttr attr = GetCombinedAttributes();
4485 context.ApplyVirtualAttributes(attr, this);
4486
4487 DrawBoxAttributes(dc, GetBuffer(), attr, paraRect);
4488
4489 // Draw the bullet, if any
4490 if ((attr.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE) == 0 && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION) == 0)
4491 {
4492 if (attr.GetLeftSubIndent() != 0)
4493 {
4494 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
4495 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
4496
4497 wxRichTextAttr bulletAttr(attr);
4498
4499 // Combine with the font of the first piece of content, if one is specified
4500 if (GetChildren().GetCount() > 0)
4501 {
4502 wxRichTextObject* firstObj = (wxRichTextObject*) GetChildren().GetFirst()->GetData();
4503 if (!firstObj->IsFloatable() && firstObj->GetAttributes().HasFont())
4504 {
4505 wxRichTextApplyStyle(bulletAttr, firstObj->GetAttributes());
4506 }
4507 }
4508
4509 // Get line height from first line, if any
4510 wxRichTextLine* line = m_cachedLines.GetFirst() ? (wxRichTextLine* ) m_cachedLines.GetFirst()->GetData() : NULL;
4511
4512 wxPoint linePos;
4513 int lineHeight wxDUMMY_INITIALIZE(0);
4514 if (line)
4515 {
4516 lineHeight = line->GetSize().y;
4517 linePos = line->GetPosition() + GetPosition();
4518 }
4519 else
4520 {
4521 wxFont font;
4522 if (bulletAttr.HasFont() && GetBuffer())
4523 font = GetBuffer()->GetFontTable().FindFont(bulletAttr);
4524 else
4525 font = (*wxNORMAL_FONT);
4526
4527 wxCheckSetFont(dc, font);
4528
4529 lineHeight = dc.GetCharHeight();
4530 linePos = GetPosition();
4531 linePos.y += spaceBeforePara;
4532 }
4533
4534 wxRect bulletRect(GetPosition().x + leftIndent, linePos.y, linePos.x - (GetPosition().x + leftIndent), lineHeight);
4535
4536 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP)
4537 {
4538 if (wxRichTextBuffer::GetRenderer())
4539 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc, bulletAttr, bulletRect);
4540 }
4541 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD)
4542 {
4543 if (wxRichTextBuffer::GetRenderer())
4544 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc, bulletAttr, bulletRect);
4545 }
4546 else
4547 {
4548 wxString bulletText = GetBulletText();
4549
4550 if (!bulletText.empty() && wxRichTextBuffer::GetRenderer())
4551 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc, bulletAttr, bulletRect, bulletText);
4552 }
4553 }
4554 }
4555
4556 // Draw the range for each line, one object at a time.
4557
4558 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
4559 while (node)
4560 {
4561 wxRichTextLine* line = node->GetData();
4562 wxRichTextRange lineRange = line->GetAbsoluteRange();
4563
4564 // Lines are specified relative to the paragraph
4565
4566 wxPoint linePosition = line->GetPosition() + GetPosition();
4567
4568 // Don't draw if off the screen
4569 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) != 0) || ((linePosition.y + line->GetSize().y) >= rect.y && linePosition.y <= rect.y + rect.height))
4570 {
4571 wxPoint objectPosition = linePosition;
4572 int maxDescent = line->GetDescent();
4573
4574 // Loop through objects until we get to the one within range
4575 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
4576
4577 int i = 0;
4578 while (node2)
4579 {
4580 wxRichTextObject* child = node2->GetData();
4581
4582 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
4583 {
4584 // Draw this part of the line at the correct position
4585 wxRichTextRange objectRange(child->GetRange());
4586 objectRange.LimitTo(lineRange);
4587
4588 wxSize objectSize;
4589 if (child->IsTopLevel())
4590 {
4591 objectSize = child->GetCachedSize();
4592 objectRange = child->GetOwnRange();
4593 }
4594 else
4595 {
4596 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4597 if (i < (int) line->GetObjectSizes().GetCount())
4598 {
4599 objectSize.x = line->GetObjectSizes()[(size_t) i];
4600 }
4601 else
4602 #endif
4603 {
4604 int descent = 0;
4605 child->GetRangeSize(objectRange, objectSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, objectPosition);
4606 }
4607 }
4608
4609 // Use the child object's width, but the whole line's height
4610 wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
4611 child->Draw(dc, context, objectRange, selection, childRect, maxDescent, style);
4612
4613 objectPosition.x += objectSize.x;
4614 i ++;
4615 }
4616 else if (child->GetRange().GetStart() > lineRange.GetEnd())
4617 // Can break out of inner loop now since we've passed this line's range
4618 break;
4619
4620 node2 = node2->GetNext();
4621 }
4622 }
4623
4624 node = node->GetNext();
4625 }
4626
4627 return true;
4628 }
4629
4630 // Get the range width using partial extents calculated for the whole paragraph.
4631 static int wxRichTextGetRangeWidth(const wxRichTextParagraph& para, const wxRichTextRange& range, const wxArrayInt& partialExtents)
4632 {
4633 wxASSERT(partialExtents.GetCount() >= (size_t) range.GetLength());
4634
4635 if (partialExtents.GetCount() < (size_t) range.GetLength())
4636 return 0;
4637
4638 int leftMostPos = 0;
4639 if (range.GetStart() - para.GetRange().GetStart() > 0)
4640 leftMostPos = partialExtents[range.GetStart() - para.GetRange().GetStart() - 1];
4641
4642 int rightMostPos = partialExtents[range.GetEnd() - para.GetRange().GetStart()];
4643
4644 int w = rightMostPos - leftMostPos;
4645
4646 return w;
4647 }
4648
4649 /// Lay the item out
4650 bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
4651 {
4652 // Deal with floating objects firstly before the normal layout
4653 wxRichTextBuffer* buffer = GetBuffer();
4654 wxASSERT(buffer);
4655
4656 wxRichTextFloatCollector* collector = GetContainer()->GetFloatCollector();
4657
4658 if (wxRichTextBuffer::GetFloatingLayoutMode())
4659 {
4660 wxASSERT(collector != NULL);
4661 if (collector)
4662 LayoutFloat(dc, context, rect, parentRect, style, collector);
4663 }
4664
4665 wxRichTextAttr attr = GetCombinedAttributes();
4666 context.ApplyVirtualAttributes(attr, this);
4667
4668 // ClearLines();
4669
4670 // Increase the size of the paragraph due to spacing
4671 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
4672 int spaceAfterPara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingAfter());
4673 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
4674 int leftSubIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftSubIndent());
4675 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
4676
4677 int lineSpacing = 0;
4678
4679 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4680 if (attr.HasLineSpacing() && attr.GetLineSpacing() > 0 && attr.HasFont())
4681 {
4682 wxFont font(buffer->GetFontTable().FindFont(attr));
4683 if (font.IsOk())
4684 {
4685 wxCheckSetFont(dc, font);
4686 lineSpacing = (int) (double(dc.GetCharHeight()) * (double(attr.GetLineSpacing())/10.0 - 1.0));
4687 }
4688 }
4689
4690 // Start position for each line relative to the paragraph
4691 int startPositionFirstLine = leftIndent;
4692 int startPositionSubsequentLines = leftIndent + leftSubIndent;
4693
4694 // If we have a bullet in this paragraph, the start position for the first line's text
4695 // is actually leftIndent + leftSubIndent.
4696 if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
4697 startPositionFirstLine = startPositionSubsequentLines;
4698
4699 long lastEndPos = GetRange().GetStart()-1;
4700 long lastCompletedEndPos = lastEndPos;
4701
4702 int currentWidth = 0;
4703 SetPosition(rect.GetPosition());
4704
4705 wxPoint currentPosition(0, spaceBeforePara); // We will calculate lines relative to paragraph
4706 int lineHeight = 0;
4707 int maxWidth = 0;
4708 int maxHeight = currentPosition.y;
4709 int maxAscent = 0;
4710 int maxDescent = 0;
4711 int lineCount = 0;
4712 int lineAscent = 0;
4713 int lineDescent = 0;
4714
4715 wxRichTextObjectList::compatibility_iterator node;
4716
4717 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4718 wxUnusedVar(style);
4719 wxArrayInt partialExtents;
4720
4721 wxSize paraSize;
4722 int paraDescent = 0;
4723
4724 // This calculates the partial text extents
4725 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, rect.GetPosition(), parentRect.GetSize(), & partialExtents);
4726 #else
4727 node = m_children.GetFirst();
4728 while (node)
4729 {
4730 wxRichTextObject* child = node->GetData();
4731
4732 //child->SetCachedSize(wxDefaultSize);
4733 child->Layout(dc, context, rect, style);
4734
4735 node = node->GetNext();
4736 }
4737 #endif
4738
4739 // Split up lines
4740
4741 // We may need to go back to a previous child, in which case create the new line,
4742 // find the child corresponding to the start position of the string, and
4743 // continue.
4744
4745 wxRect availableRect;
4746
4747 node = m_children.GetFirst();
4748 while (node)
4749 {
4750 wxRichTextObject* child = node->GetData();
4751
4752 // If floating, ignore. We already laid out floats.
4753 // Also ignore if empty object, except if we haven't got any
4754 // size yet.
4755 if ((child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
4756 || !child->IsShown() || (child->GetRange().GetLength() == 0 && maxHeight > spaceBeforePara)
4757 )
4758 {
4759 node = node->GetNext();
4760 continue;
4761 }
4762
4763 // If this is e.g. a composite text box, it will need to be laid out itself.
4764 // But if just a text fragment or image, for example, this will
4765 // do nothing. NB: won't we need to set the position after layout?
4766 // since for example if position is dependent on vertical line size, we
4767 // can't tell the position until the size is determined. So possibly introduce
4768 // another layout phase.
4769
4770 // We may only be looking at part of a child, if we searched back for wrapping
4771 // and found a suitable point some way into the child. So get the size for the fragment
4772 // if necessary.
4773
4774 long nextBreakPos = GetFirstLineBreakPosition(lastEndPos+1);
4775 long lastPosToUse = child->GetRange().GetEnd();
4776 bool lineBreakInThisObject = (nextBreakPos > -1 && nextBreakPos <= child->GetRange().GetEnd());
4777
4778 if (lineBreakInThisObject)
4779 lastPosToUse = nextBreakPos;
4780
4781 wxSize childSize;
4782 int childDescent = 0;
4783
4784 int startOffset = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
4785 availableRect = wxRect(rect.x + startOffset, rect.y + currentPosition.y,
4786 rect.width - startOffset - rightIndent, rect.height);
4787
4788 if (child->IsTopLevel())
4789 {
4790 wxSize oldSize = child->GetCachedSize();
4791
4792 child->Invalidate(wxRICHTEXT_ALL);
4793 child->SetPosition(wxPoint(0, 0));
4794
4795 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4796 // lays out the object again using the minimum size
4797 // The position will be determined by its location in its line,
4798 // and not by the child's actual position.
4799 child->LayoutToBestSize(dc, context, buffer,
4800 attr, child->GetAttributes(), availableRect, parentRect, style);
4801
4802 if (oldSize != child->GetCachedSize())
4803 {
4804 partialExtents.Clear();
4805
4806 // Recalculate the partial text extents since the child object changed size
4807 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), parentRect.GetSize(), & partialExtents);
4808 }
4809 }
4810
4811 // Problem: we need to layout composites here for which we need the available width,
4812 // but we can't get the available width without using the float collector which
4813 // needs to know the object height.
4814
4815 if ((nextBreakPos == -1) && (lastEndPos == child->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4816 {
4817 childSize = child->GetCachedSize();
4818 childDescent = child->GetDescent();
4819 }
4820 else
4821 {
4822 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4823 // Get height only, then the width using the partial extents
4824 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY, wxPoint(0,0), parentRect.GetSize());
4825 childSize.x = wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos+1, lastPosToUse), partialExtents);
4826 #else
4827 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, rect.GetPosition(), parentRect.GetSize());
4828 #endif
4829 }
4830
4831 bool doLoop = true;
4832 int loopIterations = 0;
4833
4834 // If there are nested objects that need to lay themselves out, we have to do this in a
4835 // loop because the height of the object may well depend on the available width.
4836 // And because of floating object positioning, the available width depends on the
4837 // height of the object and whether it will clash with the floating objects.
4838 // So, we see whether the available width changes due to the presence of floating images.
4839 // If it does, then we'll use the new restricted width to find the object height again.
4840 // If this causes another restriction in the available width, we'll try again, until
4841 // either we lose patience or the available width settles down.
4842 do
4843 {
4844 loopIterations ++;
4845
4846 wxRect oldAvailableRect = availableRect;
4847
4848 // Available width depends on the floating objects and the line height.
4849 // Note: the floating objects may be placed vertically along the two sides of
4850 // buffer, so we may have different available line widths with different
4851 // [startY, endY]. So, we can't determine how wide the available
4852 // space is until we know the exact line height.
4853 if (childDescent == 0)
4854 {
4855 lineHeight = wxMax(lineHeight, childSize.y);
4856 lineDescent = maxDescent;
4857 lineAscent = maxAscent;
4858 }
4859 else
4860 {
4861 lineDescent = wxMax(childDescent, maxDescent);
4862 lineAscent = wxMax(childSize.y-childDescent, maxAscent);
4863 }
4864 lineHeight = wxMax(lineHeight, (lineDescent + lineAscent));
4865
4866 if (wxRichTextBuffer::GetFloatingLayoutMode() && collector)
4867 {
4868 wxRect floatAvailableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight);
4869
4870 // Adjust availableRect to the space that is available when taking floating objects into account.
4871
4872 if (floatAvailableRect.x + startOffset > availableRect.x)
4873 {
4874 int newX = floatAvailableRect.x + startOffset;
4875 int newW = availableRect.width - (newX - availableRect.x);
4876 availableRect.x = newX;
4877 availableRect.width = newW;
4878 }
4879
4880 if (floatAvailableRect.width < availableRect.width)
4881 availableRect.width = floatAvailableRect.width;
4882 }
4883
4884 currentPosition.x = availableRect.x - rect.x;
4885
4886 if (child->IsTopLevel() && loopIterations <= 20)
4887 {
4888 if (availableRect != oldAvailableRect)
4889 {
4890 wxSize oldSize = child->GetCachedSize();
4891
4892 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4893 // lays out the object again using the minimum size
4894 child->Invalidate(wxRICHTEXT_ALL);
4895 child->LayoutToBestSize(dc, context, buffer,
4896 attr, child->GetAttributes(), availableRect, parentRect.GetSize(), style);
4897 childSize = child->GetCachedSize();
4898 childDescent = child->GetDescent();
4899
4900 if (oldSize != child->GetCachedSize())
4901 {
4902 partialExtents.Clear();
4903
4904 // Recalculate the partial text extents since the child object changed size
4905 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), parentRect.GetSize(), & partialExtents);
4906 }
4907
4908 // Go around the loop finding the available rect for the given floating objects
4909 }
4910 else
4911 doLoop = false;
4912 }
4913 else
4914 doLoop = false;
4915 }
4916 while (doLoop);
4917
4918 if (child->IsTopLevel())
4919 {
4920 // We can move it to the correct position at this point
4921 // TODO: probably need to add margin
4922 child->Move(GetPosition() + wxPoint(currentWidth + (wxMax(leftIndent, leftIndent + leftSubIndent)), currentPosition.y));
4923 }
4924
4925 // Cases:
4926 // 1) There was a line break BEFORE the natural break
4927 // 2) There was a line break AFTER the natural break
4928 // 3) It's the last line
4929 // 4) The child still fits (carry on) - 'else' clause
4930
4931 if ((lineBreakInThisObject && (childSize.x + currentWidth <= availableRect.width))
4932 ||
4933 (childSize.x + currentWidth > availableRect.width)
4934 #if 0
4935 ||
4936 ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext())
4937 #endif
4938 )
4939 {
4940 long wrapPosition = 0;
4941 if ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext() && !lineBreakInThisObject)
4942 wrapPosition = child->GetRange().GetEnd();
4943 else
4944
4945 // Find a place to wrap. This may walk back to previous children,
4946 // for example if a word spans several objects.
4947 // Note: one object must contains only one wxTextAtrr, so the line height will not
4948 // change inside one object. Thus, we can pass the remain line width to the
4949 // FindWrapPosition function.
4950 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, context, availableRect.width, wrapPosition, & partialExtents))
4951 {
4952 // If the function failed, just cut it off at the end of this child.
4953 wrapPosition = child->GetRange().GetEnd();
4954 }
4955
4956 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4957 if (wrapPosition <= lastCompletedEndPos)
4958 wrapPosition = wxMax(lastCompletedEndPos+1,child->GetRange().GetEnd());
4959
4960 // Line end position shouldn't be the same as the end, or greater.
4961 if (wrapPosition >= GetRange().GetEnd())
4962 wrapPosition = wxMax(0, GetRange().GetEnd()-1);
4963
4964 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4965
4966 // Let's find the actual size of the current line now
4967 wxSize actualSize;
4968 wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
4969
4970 childDescent = 0;
4971
4972 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4973 if (!child->IsEmpty())
4974 {
4975 // Get height only, then the width using the partial extents
4976 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY, wxPoint(0,0), parentRect.GetSize());
4977 actualSize.x = wxRichTextGetRangeWidth(*this, actualRange, partialExtents);
4978 }
4979 else
4980 #endif
4981 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, wxPoint(0,0), parentRect.GetSize());
4982
4983 currentWidth = actualSize.x;
4984
4985 // The descent for the whole line at this point, is the correct max descent
4986 maxDescent = childDescent;
4987 // Maximum ascent
4988 maxAscent = actualSize.y-childDescent;
4989
4990 // lineHeight is given by the height for the whole line, since it will
4991 // take into account ascend/descend.
4992 lineHeight = actualSize.y;
4993
4994 if (lineHeight == 0 && buffer)
4995 {
4996 wxFont font(buffer->GetFontTable().FindFont(attr));
4997 wxCheckSetFont(dc, font);
4998 lineHeight = dc.GetCharHeight();
4999 }
5000
5001 if (maxDescent == 0)
5002 {
5003 int w, h;
5004 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
5005 }
5006
5007 // Add a new line
5008 wxRichTextLine* line = AllocateLine(lineCount);
5009
5010 // Set relative range so we won't have to change line ranges when paragraphs are moved
5011 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5012 line->SetPosition(currentPosition);
5013 line->SetSize(wxSize(currentWidth, lineHeight));
5014 line->SetDescent(maxDescent);
5015
5016 maxHeight = currentPosition.y + lineHeight;
5017
5018 // Now move down a line. TODO: add margins, spacing
5019 currentPosition.y += lineHeight;
5020 currentPosition.y += lineSpacing;
5021 maxDescent = 0;
5022 maxAscent = 0;
5023 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5024 currentWidth = 0;
5025
5026 lineCount ++;
5027
5028 // TODO: account for zero-length objects
5029 // wxASSERT(wrapPosition > lastCompletedEndPos);
5030
5031 lastEndPos = wrapPosition;
5032 lastCompletedEndPos = lastEndPos;
5033
5034 lineHeight = 0;
5035
5036 if (wrapPosition < GetRange().GetEnd()-1)
5037 {
5038 // May need to set the node back to a previous one, due to searching back in wrapping
5039 wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition(wrapPosition+1);
5040 if (childAfterWrapPosition)
5041 node = m_children.Find(childAfterWrapPosition);
5042 else
5043 node = node->GetNext();
5044 }
5045 else
5046 node = node->GetNext();
5047
5048 // Apply paragraph styles such as alignment to the wrapped line
5049 ApplyParagraphStyle(line, attr, availableRect, dc);
5050 }
5051 else
5052 {
5053 // We still fit, so don't add a line, and keep going
5054 currentWidth += childSize.x;
5055
5056 if (childDescent == 0)
5057 {
5058 // An object with a zero descend value wants to take up the whole
5059 // height regardless of baseline
5060 lineHeight = wxMax(lineHeight, childSize.y);
5061 }
5062 else
5063 {
5064 maxDescent = wxMax(childDescent, maxDescent);
5065 maxAscent = wxMax(childSize.y-childDescent, maxAscent);
5066 }
5067
5068 lineHeight = wxMax(lineHeight, (maxDescent + maxAscent));
5069
5070 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5071 lastEndPos = child->GetRange().GetEnd();
5072
5073 node = node->GetNext();
5074 }
5075 }
5076
5077 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5078
5079 // Add the last line - it's the current pos -> last para pos
5080 // Substract -1 because the last position is always the end-paragraph position.
5081 if (lastCompletedEndPos <= GetRange().GetEnd()-1)
5082 {
5083 currentPosition.x = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
5084
5085 wxRichTextLine* line = AllocateLine(lineCount);
5086
5087 wxRichTextRange actualRange(lastCompletedEndPos+1, GetRange().GetEnd()-1);
5088
5089 // Set relative range so we won't have to change line ranges when paragraphs are moved
5090 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5091
5092 line->SetPosition(currentPosition);
5093
5094 if (lineHeight == 0 && buffer)
5095 {
5096 wxFont font(buffer->GetFontTable().FindFont(attr));
5097 wxCheckSetFont(dc, font);
5098 lineHeight = dc.GetCharHeight();
5099 }
5100
5101 if (maxDescent == 0)
5102 {
5103 int w, h;
5104 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
5105 }
5106
5107 line->SetSize(wxSize(currentWidth, lineHeight));
5108 line->SetDescent(maxDescent);
5109 currentPosition.y += lineHeight;
5110 currentPosition.y += lineSpacing;
5111 lineCount ++;
5112
5113 // Apply paragraph styles such as alignment to the wrapped line
5114 ApplyParagraphStyle(line, attr, availableRect, dc);
5115 }
5116
5117 // Remove remaining unused line objects, if any
5118 ClearUnusedLines(lineCount);
5119
5120 // We need to add back the margins etc.
5121 {
5122 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5123 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, currentPosition.y + spaceAfterPara));
5124 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
5125 SetCachedSize(marginRect.GetSize());
5126 }
5127
5128 // The maximum size is the length of the paragraph stretched out into a line.
5129 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
5130 // this size. TODO: take into account line breaks.
5131 {
5132 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5133 contentRect = wxRect(wxPoint(0, 0), wxSize(paraSize.x + wxMax(leftIndent, leftIndent + leftSubIndent) + rightIndent, currentPosition.y + spaceAfterPara));
5134 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
5135 SetMaxSize(marginRect.GetSize());
5136 }
5137
5138 // Find the greatest minimum size. Currently we only look at non-text objects,
5139 // which isn't ideal but it would be slow to find the maximum word width to
5140 // use as the minimum.
5141 {
5142 int minWidth = 0;
5143 node = m_children.GetFirst();
5144 while (node)
5145 {
5146 wxRichTextObject* child = node->GetData();
5147
5148 // If floating, ignore. We already laid out floats.
5149 // Also ignore if empty object, except if we haven't got any
5150 // size yet.
5151 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() != 0 && !wxDynamicCast(child, wxRichTextPlainText))
5152 {
5153 if (child->GetCachedSize().x > minWidth)
5154 minWidth = child->GetMinSize().x;
5155 }
5156 node = node->GetNext();
5157 }
5158
5159 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5160 contentRect = wxRect(wxPoint(0, 0), wxSize(minWidth, currentPosition.y + spaceAfterPara));
5161 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
5162 SetMinSize(marginRect.GetSize());
5163 }
5164
5165 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5166 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5167 // Use the text extents to calculate the size of each fragment in each line
5168 wxRichTextLineList::compatibility_iterator lineNode = m_cachedLines.GetFirst();
5169 while (lineNode)
5170 {
5171 wxRichTextLine* line = lineNode->GetData();
5172 wxRichTextRange lineRange = line->GetAbsoluteRange();
5173
5174 // Loop through objects until we get to the one within range
5175 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5176
5177 while (node2)
5178 {
5179 wxRichTextObject* child = node2->GetData();
5180
5181 if (child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange))
5182 {
5183 wxRichTextRange rangeToUse = lineRange;
5184 rangeToUse.LimitTo(child->GetRange());
5185
5186 // Find the size of the child from the text extents, and store in an array
5187 // for drawing later
5188 int left = 0;
5189 if (rangeToUse.GetStart() > GetRange().GetStart())
5190 left = partialExtents[(rangeToUse.GetStart()-1) - GetRange().GetStart()];
5191 int right = partialExtents[rangeToUse.GetEnd() - GetRange().GetStart()];
5192 int sz = right - left;
5193 line->GetObjectSizes().Add(sz);
5194 }
5195 else if (child->GetRange().GetStart() > lineRange.GetEnd())
5196 // Can break out of inner loop now since we've passed this line's range
5197 break;
5198
5199 node2 = node2->GetNext();
5200 }
5201
5202 lineNode = lineNode->GetNext();
5203 }
5204 #endif
5205 #endif
5206
5207 return true;
5208 }
5209
5210 /// Apply paragraph styles, such as centering, to wrapped lines
5211 /// TODO: take into account box attributes, possibly
5212 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRichTextAttr& attr, const wxRect& rect, wxDC& dc)
5213 {
5214 if (!attr.HasAlignment())
5215 return;
5216
5217 wxPoint pos = line->GetPosition();
5218 wxPoint originalPos = pos;
5219 wxSize size = line->GetSize();
5220
5221 // centering, right-justification
5222 if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
5223 {
5224 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5225 pos.x = (rect.GetWidth() - rightIndent - size.x)/2 + pos.x;
5226 line->SetPosition(pos);
5227 }
5228 else if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
5229 {
5230 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5231 pos.x = pos.x + rect.GetWidth() - size.x - rightIndent;
5232 line->SetPosition(pos);
5233 }
5234
5235 if (pos != originalPos)
5236 {
5237 wxPoint inc = pos - originalPos;
5238
5239 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5240
5241 while (node)
5242 {
5243 wxRichTextObject* child = node->GetData();
5244 if (child->IsTopLevel() && !child->GetRange().IsOutside(line->GetAbsoluteRange()))
5245 child->Move(child->GetPosition() + inc);
5246
5247 node = node->GetNext();
5248 }
5249 }
5250 }
5251
5252 /// Insert text at the given position
5253 bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
5254 {
5255 wxRichTextObject* childToUse = NULL;
5256 wxRichTextObjectList::compatibility_iterator nodeToUse = wxRichTextObjectList::compatibility_iterator();
5257
5258 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5259 while (node)
5260 {
5261 wxRichTextObject* child = node->GetData();
5262 if (child->GetRange().Contains(pos) && child->GetRange().GetLength() > 0)
5263 {
5264 childToUse = child;
5265 nodeToUse = node;
5266 break;
5267 }
5268
5269 node = node->GetNext();
5270 }
5271
5272 if (childToUse)
5273 {
5274 wxRichTextPlainText* textObject = wxDynamicCast(childToUse, wxRichTextPlainText);
5275 if (textObject)
5276 {
5277 int posInString = pos - textObject->GetRange().GetStart();
5278
5279 wxString newText = textObject->GetText().Mid(0, posInString) +
5280 text + textObject->GetText().Mid(posInString);
5281 textObject->SetText(newText);
5282
5283 int textLength = text.length();
5284
5285 textObject->SetRange(wxRichTextRange(textObject->GetRange().GetStart(),
5286 textObject->GetRange().GetEnd() + textLength));
5287
5288 // Increment the end range of subsequent fragments in this paragraph.
5289 // We'll set the paragraph range itself at a higher level.
5290
5291 wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
5292 while (node)
5293 {
5294 wxRichTextObject* child = node->GetData();
5295 child->SetRange(wxRichTextRange(textObject->GetRange().GetStart() + textLength,
5296 textObject->GetRange().GetEnd() + textLength));
5297
5298 node = node->GetNext();
5299 }
5300
5301 return true;
5302 }
5303 else
5304 {
5305 // TODO: if not a text object, insert at closest position, e.g. in front of it
5306 }
5307 }
5308 else
5309 {
5310 // Add at end.
5311 // Don't pass parent initially to suppress auto-setting of parent range.
5312 // We'll do that at a higher level.
5313 wxRichTextPlainText* textObject = new wxRichTextPlainText(text, this);
5314
5315 AppendChild(textObject);
5316 return true;
5317 }
5318
5319 return false;
5320 }
5321
5322 void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
5323 {
5324 wxRichTextCompositeObject::Copy(obj);
5325 }
5326
5327 /// Clear the cached lines
5328 void wxRichTextParagraph::ClearLines()
5329 {
5330 WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
5331 }
5332
5333 /// Get/set the object size for the given range. Returns false if the range
5334 /// is invalid for this object.
5335 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
5336 {
5337 if (!range.IsWithin(GetRange()))
5338 return false;
5339
5340 if (flags & wxRICHTEXT_UNFORMATTED)
5341 {
5342 // Just use unformatted data, assume no line breaks
5343 wxSize sz;
5344
5345 wxArrayInt childExtents;
5346 wxArrayInt* p;
5347 if (partialExtents)
5348 p = & childExtents;
5349 else
5350 p = NULL;
5351
5352 int maxDescent = 0;
5353 int maxAscent = 0;
5354 int maxLineHeight = 0;
5355
5356 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5357 while (node)
5358 {
5359 wxRichTextObject* child = node->GetData();
5360 if (!child->GetRange().IsOutside(range))
5361 {
5362 // Floating objects have a zero size within the paragraph.
5363 if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
5364 {
5365 if (partialExtents)
5366 {
5367 int lastSize;
5368 if (partialExtents->GetCount() > 0)
5369 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5370 else
5371 lastSize = 0;
5372
5373 partialExtents->Add(0 /* zero size */ + lastSize);
5374 }
5375 }
5376 else
5377 {
5378 wxSize childSize;
5379
5380 wxRichTextRange rangeToUse = range;
5381 rangeToUse.LimitTo(child->GetRange());
5382 int childDescent = 0;
5383
5384 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
5385 // but it's only going to be used after caching has taken place.
5386 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
5387 {
5388 childDescent = child->GetDescent();
5389 childSize = child->GetCachedSize();
5390
5391 if (childDescent == 0)
5392 {
5393 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5394 }
5395 else
5396 {
5397 maxDescent = wxMax(maxDescent, childDescent);
5398 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5399 }
5400
5401 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5402
5403 sz.y = wxMax(sz.y, maxLineHeight);
5404 sz.x += childSize.x;
5405 descent = maxDescent;
5406 }
5407 else if (child->IsTopLevel())
5408 {
5409 childDescent = child->GetDescent();
5410 childSize = child->GetCachedSize();
5411
5412 if (childDescent == 0)
5413 {
5414 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5415 }
5416 else
5417 {
5418 maxDescent = wxMax(maxDescent, childDescent);
5419 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5420 }
5421
5422 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5423
5424 sz.y = wxMax(sz.y, maxLineHeight);
5425 sz.x += childSize.x;
5426 descent = maxDescent;
5427
5428 // FIXME: this won't change the original values.
5429 // Should we be calling GetRangeSize above instead of using cached values?
5430 #if 0
5431 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
5432 {
5433 child->SetCachedSize(childSize);
5434 child->SetDescent(childDescent);
5435 }
5436 #endif
5437
5438 if (partialExtents)
5439 {
5440 int lastSize;
5441 if (partialExtents->GetCount() > 0)
5442 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5443 else
5444 lastSize = 0;
5445
5446 partialExtents->Add(childSize.x + lastSize);
5447 }
5448 }
5449 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize, p))
5450 {
5451 if (childDescent == 0)
5452 {
5453 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5454 }
5455 else
5456 {
5457 maxDescent = wxMax(maxDescent, childDescent);
5458 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5459 }
5460
5461 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5462
5463 sz.y = wxMax(sz.y, maxLineHeight);
5464 sz.x += childSize.x;
5465 descent = maxDescent;
5466
5467 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
5468 {
5469 child->SetCachedSize(childSize);
5470 child->SetDescent(childDescent);
5471 }
5472
5473 if (partialExtents)
5474 {
5475 int lastSize;
5476 if (partialExtents->GetCount() > 0)
5477 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5478 else
5479 lastSize = 0;
5480
5481 size_t i;
5482 for (i = 0; i < childExtents.GetCount(); i++)
5483 {
5484 partialExtents->Add(childExtents[i] + lastSize);
5485 }
5486 }
5487 }
5488 }
5489
5490 if (p)
5491 p->Clear();
5492 }
5493
5494 node = node->GetNext();
5495 }
5496 size = sz;
5497 }
5498 else
5499 {
5500 // Use formatted data, with line breaks
5501 wxSize sz;
5502
5503 // We're going to loop through each line, and then for each line,
5504 // call GetRangeSize for the fragment that comprises that line.
5505 // Only we have to do that multiple times within the line, because
5506 // the line may be broken into pieces. For now ignore line break commands
5507 // (so we can assume that getting the unformatted size for a fragment
5508 // within a line is the actual size)
5509
5510 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5511 while (node)
5512 {
5513 wxRichTextLine* line = node->GetData();
5514 wxRichTextRange lineRange = line->GetAbsoluteRange();
5515 if (!lineRange.IsOutside(range))
5516 {
5517 int maxDescent = 0;
5518 int maxAscent = 0;
5519 int maxLineHeight = 0;
5520 int maxLineWidth = 0;
5521
5522 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5523 while (node2)
5524 {
5525 wxRichTextObject* child = node2->GetData();
5526
5527 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && !child->GetRange().IsOutside(lineRange))
5528 {
5529 wxRichTextRange rangeToUse = lineRange;
5530 rangeToUse.LimitTo(child->GetRange());
5531 if (child->IsTopLevel())
5532 rangeToUse = child->GetOwnRange();
5533
5534 wxSize childSize;
5535 int childDescent = 0;
5536 if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize))
5537 {
5538 if (childDescent == 0)
5539 {
5540 // Assume that if descent is zero, this child can occupy the full line height
5541 // and does not need space for the line's maximum descent. So we influence
5542 // the overall max line height only.
5543 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5544 }
5545 else
5546 {
5547 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5548 maxDescent = wxMax(maxAscent, childDescent);
5549 }
5550 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5551 maxLineWidth += childSize.x;
5552 }
5553 }
5554
5555 node2 = node2->GetNext();
5556 }
5557
5558 descent = wxMax(descent, maxDescent);
5559
5560 // Increase size by a line (TODO: paragraph spacing)
5561 sz.y += maxLineHeight;
5562 sz.x = wxMax(sz.x, maxLineWidth);
5563 }
5564 node = node->GetNext();
5565 }
5566 size = sz;
5567 }
5568 return true;
5569 }
5570
5571 /// Finds the absolute position and row height for the given character position
5572 bool wxRichTextParagraph::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
5573 {
5574 if (index == -1)
5575 {
5576 wxRichTextLine* line = ((wxRichTextParagraphLayoutBox*)GetParent())->GetLineAtPosition(0);
5577 if (line)
5578 *height = line->GetSize().y;
5579 else
5580 *height = dc.GetCharHeight();
5581
5582 // -1 means 'the start of the buffer'.
5583 pt = GetPosition();
5584 if (line)
5585 pt = pt + line->GetPosition();
5586
5587 return true;
5588 }
5589
5590 // The final position in a paragraph is taken to mean the position
5591 // at the start of the next paragraph.
5592 if (index == GetRange().GetEnd())
5593 {
5594 wxRichTextParagraphLayoutBox* parent = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
5595 wxASSERT( parent != NULL );
5596
5597 // Find the height at the next paragraph, if any
5598 wxRichTextLine* line = parent->GetLineAtPosition(index + 1);
5599 if (line)
5600 {
5601 *height = line->GetSize().y;
5602 pt = line->GetAbsolutePosition();
5603 }
5604 else
5605 {
5606 *height = dc.GetCharHeight();
5607 int indent = ConvertTenthsMMToPixels(dc, m_attributes.GetLeftIndent());
5608 pt = wxPoint(indent, GetCachedSize().y);
5609 }
5610
5611 return true;
5612 }
5613
5614 if (index < GetRange().GetStart() || index > GetRange().GetEnd())
5615 return false;
5616
5617 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5618 while (node)
5619 {
5620 wxRichTextLine* line = node->GetData();
5621 wxRichTextRange lineRange = line->GetAbsoluteRange();
5622 if (index >= lineRange.GetStart() && index <= lineRange.GetEnd())
5623 {
5624 // If this is the last point in the line, and we're forcing the
5625 // returned value to be the start of the next line, do the required
5626 // thing.
5627 if (index == lineRange.GetEnd() && forceLineStart)
5628 {
5629 if (node->GetNext())
5630 {
5631 wxRichTextLine* nextLine = node->GetNext()->GetData();
5632 *height = nextLine->GetSize().y;
5633 pt = nextLine->GetAbsolutePosition();
5634 return true;
5635 }
5636 }
5637
5638 pt.y = line->GetPosition().y + GetPosition().y;
5639
5640 wxRichTextRange r(lineRange.GetStart(), index);
5641 wxSize rangeSize;
5642 int descent = 0;
5643
5644 // We find the size of the line up to this point,
5645 // then we can add this size to the line start position and
5646 // paragraph start position to find the actual position.
5647
5648 if (GetRangeSize(r, rangeSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, line->GetPosition()+ GetPosition()))
5649 {
5650 pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
5651 *height = line->GetSize().y;
5652
5653 return true;
5654 }
5655
5656 }
5657
5658 node = node->GetNext();
5659 }
5660
5661 return false;
5662 }
5663
5664 /// Hit-testing: returns a flag indicating hit test details, plus
5665 /// information about position
5666 int wxRichTextParagraph::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5667 {
5668 if (!IsShown())
5669 return wxRICHTEXT_HITTEST_NONE;
5670
5671 // If we're in the top-level container, then we can return
5672 // a suitable hit test code even if the point is outside the container area,
5673 // so that we can position the caret sensibly even if we don't
5674 // click on valid content. If we're not at the top-level, and the point
5675 // is not within this paragraph object, then we don't want to stop more
5676 // precise hit-testing from working prematurely, so return immediately.
5677 // NEW STRATEGY: use the parent boundary to test whether we're in the
5678 // right region, not the paragraph, since the paragraph may be positioned
5679 // some way in from where the user clicks.
5680 {
5681 long tmpPos;
5682 wxRichTextObject* tempObj, *tempContextObj;
5683 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc, context, pt, tmpPos, & tempObj, & tempContextObj, flags) == wxRICHTEXT_HITTEST_NONE)
5684 return wxRICHTEXT_HITTEST_NONE;
5685 }
5686
5687 wxRichTextObjectList::compatibility_iterator objNode = m_children.GetFirst();
5688 while (objNode)
5689 {
5690 wxRichTextObject* child = objNode->GetData();
5691 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5692 // and also, if this seems composite but actually is marked as atomic,
5693 // don't recurse.
5694 if (child->IsTopLevel() && ((flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS) == 0) &&
5695 (! (((flags & wxRICHTEXT_HITTEST_HONOUR_ATOMIC) != 0) && child->IsAtomic())))
5696 {
5697 {
5698 int hitTest = child->HitTest(dc, context, pt, textPosition, obj, contextObj);
5699 if (hitTest != wxRICHTEXT_HITTEST_NONE)
5700 return hitTest;
5701 }
5702 }
5703
5704 objNode = objNode->GetNext();
5705 }
5706
5707 wxPoint paraPos = GetPosition();
5708
5709 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5710 while (node)
5711 {
5712 wxRichTextLine* line = node->GetData();
5713 wxPoint linePos = paraPos + line->GetPosition();
5714 wxSize lineSize = line->GetSize();
5715 wxRichTextRange lineRange = line->GetAbsoluteRange();
5716
5717 if (pt.y <= linePos.y + lineSize.y)
5718 {
5719 if (pt.x < linePos.x)
5720 {
5721 textPosition = lineRange.GetStart();
5722 *obj = FindObjectAtPosition(textPosition);
5723 *contextObj = GetContainer();
5724 return wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_OUTSIDE;
5725 }
5726 else if (pt.x >= (linePos.x + lineSize.x))
5727 {
5728 textPosition = lineRange.GetEnd();
5729 *obj = FindObjectAtPosition(textPosition);
5730 *contextObj = GetContainer();
5731 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
5732 }
5733 else
5734 {
5735 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5736 wxArrayInt partialExtents;
5737
5738 wxSize paraSize;
5739 int paraDescent;
5740
5741 // This calculates the partial text extents
5742 GetRangeSize(lineRange, paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED, linePos, wxDefaultSize, & partialExtents);
5743
5744 int lastX = linePos.x;
5745 size_t i;
5746 for (i = 0; i < partialExtents.GetCount(); i++)
5747 {
5748 int nextX = partialExtents[i] + linePos.x;
5749
5750 if (pt.x >= lastX && pt.x <= nextX)
5751 {
5752 textPosition = i + lineRange.GetStart(); // minus 1?
5753
5754 *obj = FindObjectAtPosition(textPosition);
5755 *contextObj = GetContainer();
5756
5757 // So now we know it's between i-1 and i.
5758 // Let's see if we can be more precise about
5759 // which side of the position it's on.
5760
5761 int midPoint = (nextX + lastX)/2;
5762 if (pt.x >= midPoint)
5763 return wxRICHTEXT_HITTEST_AFTER;
5764 else
5765 return wxRICHTEXT_HITTEST_BEFORE;
5766 }
5767
5768 lastX = nextX;
5769 }
5770 #else
5771 long i;
5772 int lastX = linePos.x;
5773 for (i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++)
5774 {
5775 wxSize childSize;
5776 int descent = 0;
5777
5778 wxRichTextRange rangeToUse(lineRange.GetStart(), i);
5779
5780 GetRangeSize(rangeToUse, childSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, linePos);
5781
5782 int nextX = childSize.x + linePos.x;
5783
5784 if (pt.x >= lastX && pt.x <= nextX)
5785 {
5786 textPosition = i;
5787
5788 *obj = FindObjectAtPosition(textPosition);
5789 *contextObj = GetContainer();
5790
5791 // So now we know it's between i-1 and i.
5792 // Let's see if we can be more precise about
5793 // which side of the position it's on.
5794
5795 int midPoint = (nextX + lastX)/2;
5796 if (pt.x >= midPoint)
5797 return wxRICHTEXT_HITTEST_AFTER;
5798 else
5799 return wxRICHTEXT_HITTEST_BEFORE;
5800 }
5801 else
5802 {
5803 lastX = nextX;
5804 }
5805 }
5806 #endif
5807 }
5808 }
5809
5810 node = node->GetNext();
5811 }
5812
5813 return wxRICHTEXT_HITTEST_NONE;
5814 }
5815
5816 /// Split an object at this position if necessary, and return
5817 /// the previous object, or NULL if inserting at beginning.
5818 wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** previousObject)
5819 {
5820 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5821 while (node)
5822 {
5823 wxRichTextObject* child = node->GetData();
5824
5825 if (pos == child->GetRange().GetStart())
5826 {
5827 if (previousObject)
5828 {
5829 if (node->GetPrevious())
5830 *previousObject = node->GetPrevious()->GetData();
5831 else
5832 *previousObject = NULL;
5833 }
5834
5835 return child;
5836 }
5837
5838 if (child->GetRange().Contains(pos))
5839 {
5840 // This should create a new object, transferring part of
5841 // the content to the old object and the rest to the new object.
5842 wxRichTextObject* newObject = child->DoSplit(pos);
5843
5844 // If we couldn't split this object, just insert in front of it.
5845 if (!newObject)
5846 {
5847 // Maybe this is an empty string, try the next one
5848 // return child;
5849 }
5850 else
5851 {
5852 // Insert the new object after 'child'
5853 if (node->GetNext())
5854 m_children.Insert(node->GetNext(), newObject);
5855 else
5856 m_children.Append(newObject);
5857 newObject->SetParent(this);
5858
5859 if (previousObject)
5860 *previousObject = child;
5861
5862 return newObject;
5863 }
5864 }
5865
5866 node = node->GetNext();
5867 }
5868 if (previousObject)
5869 *previousObject = NULL;
5870 return NULL;
5871 }
5872
5873 /// Move content to a list from obj on
5874 void wxRichTextParagraph::MoveToList(wxRichTextObject* obj, wxList& list)
5875 {
5876 wxRichTextObjectList::compatibility_iterator node = m_children.Find(obj);
5877 while (node)
5878 {
5879 wxRichTextObject* child = node->GetData();
5880 list.Append(child);
5881
5882 wxRichTextObjectList::compatibility_iterator oldNode = node;
5883
5884 node = node->GetNext();
5885
5886 m_children.DeleteNode(oldNode);
5887 }
5888 }
5889
5890 /// Add content back from list
5891 void wxRichTextParagraph::MoveFromList(wxList& list)
5892 {
5893 for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
5894 {
5895 AppendChild((wxRichTextObject*) node->GetData());
5896 }
5897 }
5898
5899 /// Calculate range
5900 void wxRichTextParagraph::CalculateRange(long start, long& end)
5901 {
5902 wxRichTextCompositeObject::CalculateRange(start, end);
5903
5904 // Add one for end of paragraph
5905 end ++;
5906
5907 m_range.SetRange(start, end);
5908 }
5909
5910 /// Find the object at the given position
5911 wxRichTextObject* wxRichTextParagraph::FindObjectAtPosition(long position)
5912 {
5913 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5914 while (node)
5915 {
5916 wxRichTextObject* obj = node->GetData();
5917 if (obj->GetRange().Contains(position) ||
5918 obj->GetRange().GetStart() == position ||
5919 obj->GetRange().GetEnd() == position)
5920 return obj;
5921
5922 node = node->GetNext();
5923 }
5924 return NULL;
5925 }
5926
5927 /// Get the plain text searching from the start or end of the range.
5928 /// The resulting string may be shorter than the range given.
5929 bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTextRange& range, bool fromStart)
5930 {
5931 text = wxEmptyString;
5932
5933 if (fromStart)
5934 {
5935 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5936 while (node)
5937 {
5938 wxRichTextObject* obj = node->GetData();
5939 if (!obj->GetRange().IsOutside(range))
5940 {
5941 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5942 if (textObj)
5943 {
5944 text += textObj->GetTextForRange(range);
5945 }
5946 else
5947 {
5948 text += wxT(" ");
5949 }
5950 }
5951
5952 node = node->GetNext();
5953 }
5954 }
5955 else
5956 {
5957 wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
5958 while (node)
5959 {
5960 wxRichTextObject* obj = node->GetData();
5961 if (!obj->GetRange().IsOutside(range))
5962 {
5963 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5964 if (textObj)
5965 {
5966 text = textObj->GetTextForRange(range) + text;
5967 }
5968 else
5969 {
5970 text = wxT(" ") + text;
5971 }
5972 }
5973
5974 node = node->GetPrevious();
5975 }
5976 }
5977
5978 return true;
5979 }
5980
5981 /// Find a suitable wrap position.
5982 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, wxRichTextDrawingContext& context, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents)
5983 {
5984 if (range.GetLength() <= 0)
5985 return false;
5986
5987 // Find the first position where the line exceeds the available space.
5988 wxSize sz;
5989 long breakPosition = range.GetEnd();
5990
5991 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5992 if (partialExtents && partialExtents->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5993 {
5994 int widthBefore;
5995
5996 if (range.GetStart() > GetRange().GetStart())
5997 widthBefore = (*partialExtents)[range.GetStart() - GetRange().GetStart() - 1];
5998 else
5999 widthBefore = 0;
6000
6001 size_t i;
6002 for (i = (size_t) range.GetStart(); i <= (size_t) range.GetEnd(); i++)
6003 {
6004 int widthFromStartOfThisRange = (*partialExtents)[i - GetRange().GetStart()] - widthBefore;
6005
6006 if (widthFromStartOfThisRange > availableSpace)
6007 {
6008 breakPosition = i-1;
6009 break;
6010 }
6011 }
6012 }
6013 else
6014 #endif
6015 {
6016 // Binary chop for speed
6017 long minPos = range.GetStart();
6018 long maxPos = range.GetEnd();
6019 while (true)
6020 {
6021 if (minPos == maxPos)
6022 {
6023 int descent = 0;
6024 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
6025
6026 if (sz.x > availableSpace)
6027 breakPosition = minPos - 1;
6028 break;
6029 }
6030 else if ((maxPos - minPos) == 1)
6031 {
6032 int descent = 0;
6033 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
6034
6035 if (sz.x > availableSpace)
6036 breakPosition = minPos - 1;
6037 else
6038 {
6039 GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
6040 if (sz.x > availableSpace)
6041 breakPosition = maxPos-1;
6042 }
6043 break;
6044 }
6045 else
6046 {
6047 long nextPos = minPos + ((maxPos - minPos) / 2);
6048
6049 int descent = 0;
6050 GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
6051
6052 if (sz.x > availableSpace)
6053 {
6054 maxPos = nextPos;
6055 }
6056 else
6057 {
6058 minPos = nextPos;
6059 }
6060 }
6061 }
6062 }
6063
6064 // Now we know the last position on the line.
6065 // Let's try to find a word break.
6066
6067 wxString plainText;
6068 if (GetContiguousPlainText(plainText, wxRichTextRange(range.GetStart(), breakPosition), false))
6069 {
6070 int newLinePos = plainText.Find(wxRichTextLineBreakChar);
6071 if (newLinePos != wxNOT_FOUND)
6072 {
6073 breakPosition = wxMax(0, range.GetStart() + newLinePos);
6074 }
6075 else
6076 {
6077 int spacePos = plainText.Find(wxT(' '), true);
6078 int tabPos = plainText.Find(wxT('\t'), true);
6079 int pos = wxMax(spacePos, tabPos);
6080 if (pos != wxNOT_FOUND)
6081 {
6082 int positionsFromEndOfString = plainText.length() - pos - 1;
6083 breakPosition = breakPosition - positionsFromEndOfString;
6084 }
6085 }
6086 }
6087
6088 wrapPosition = breakPosition;
6089
6090 return true;
6091 }
6092
6093 /// Get the bullet text for this paragraph.
6094 wxString wxRichTextParagraph::GetBulletText()
6095 {
6096 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ||
6097 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP))
6098 return wxEmptyString;
6099
6100 int number = GetAttributes().GetBulletNumber();
6101
6102 wxString text;
6103 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE))
6104 {
6105 text.Printf(wxT("%d"), number);
6106 }
6107 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER)
6108 {
6109 // TODO: Unicode, and also check if number > 26
6110 text.Printf(wxT("%c"), (wxChar) (number+64));
6111 }
6112 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER)
6113 {
6114 // TODO: Unicode, and also check if number > 26
6115 text.Printf(wxT("%c"), (wxChar) (number+96));
6116 }
6117 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
6118 {
6119 text = wxRichTextDecimalToRoman(number);
6120 }
6121 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
6122 {
6123 text = wxRichTextDecimalToRoman(number);
6124 text.MakeLower();
6125 }
6126 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
6127 {
6128 text = GetAttributes().GetBulletText();
6129 }
6130
6131 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
6132 {
6133 // The outline style relies on the text being computed statically,
6134 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
6135 // should be stored in the attributes; if not, just use the number for this
6136 // level, as previously computed.
6137 if (!GetAttributes().GetBulletText().IsEmpty())
6138 text = GetAttributes().GetBulletText();
6139 }
6140
6141 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES)
6142 {
6143 text = wxT("(") + text + wxT(")");
6144 }
6145 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS)
6146 {
6147 text = text + wxT(")");
6148 }
6149
6150 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD)
6151 {
6152 text += wxT(".");
6153 }
6154
6155 return text;
6156 }
6157
6158 /// Allocate or reuse a line object
6159 wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
6160 {
6161 if (pos < (int) m_cachedLines.GetCount())
6162 {
6163 wxRichTextLine* line = m_cachedLines.Item(pos)->GetData();
6164 line->Init(this);
6165 return line;
6166 }
6167 else
6168 {
6169 wxRichTextLine* line = new wxRichTextLine(this);
6170 m_cachedLines.Append(line);
6171 return line;
6172 }
6173 }
6174
6175 /// Clear remaining unused line objects, if any
6176 bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
6177 {
6178 int cachedLineCount = m_cachedLines.GetCount();
6179 if ((int) cachedLineCount > lineCount)
6180 {
6181 for (int i = 0; i < (int) (cachedLineCount - lineCount); i ++)
6182 {
6183 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetLast();
6184 wxRichTextLine* line = node->GetData();
6185 m_cachedLines.Erase(node);
6186 delete line;
6187 }
6188 }
6189 return true;
6190 }
6191
6192 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6193 /// retrieve the actual style.
6194 wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr& contentStyle, bool includingBoxAttr) const
6195 {
6196 wxRichTextAttr attr;
6197 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
6198 if (buf)
6199 {
6200 attr = buf->GetBasicStyle();
6201 if (!includingBoxAttr)
6202 {
6203 attr.GetTextBoxAttr().Reset();
6204 // The background colour will be painted by the container, and we don't
6205 // want to unnecessarily overwrite the background when we're drawing text
6206 // because this may erase the guideline (which appears just under the text
6207 // if there's no padding).
6208 attr.SetFlags(attr.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR);
6209 }
6210 wxRichTextApplyStyle(attr, GetAttributes());
6211 }
6212 else
6213 attr = GetAttributes();
6214
6215 wxRichTextApplyStyle(attr, contentStyle);
6216 return attr;
6217 }
6218
6219 /// Get combined attributes of the base style and paragraph style.
6220 wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr) const
6221 {
6222 wxRichTextAttr attr;
6223 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
6224 if (buf)
6225 {
6226 attr = buf->GetBasicStyle();
6227 if (!includingBoxAttr)
6228 attr.GetTextBoxAttr().Reset();
6229 wxRichTextApplyStyle(attr, GetAttributes());
6230 }
6231 else
6232 attr = GetAttributes();
6233
6234 return attr;
6235 }
6236
6237 // Create default tabstop array
6238 void wxRichTextParagraph::InitDefaultTabs()
6239 {
6240 // create a default tab list at 10 mm each.
6241 for (int i = 0; i < 20; ++i)
6242 {
6243 sm_defaultTabs.Add(i*100);
6244 }
6245 }
6246
6247 // Clear default tabstop array
6248 void wxRichTextParagraph::ClearDefaultTabs()
6249 {
6250 sm_defaultTabs.Clear();
6251 }
6252
6253 void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style, wxRichTextFloatCollector* floatCollector)
6254 {
6255 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
6256 while (node)
6257 {
6258 wxRichTextObject* anchored = node->GetData();
6259 if (anchored && anchored->IsFloating() && !floatCollector->HasFloat(anchored))
6260 {
6261 int x = 0;
6262 wxRichTextAttr parentAttr(GetAttributes());
6263 context.ApplyVirtualAttributes(parentAttr, this);
6264 #if 1
6265 // 27-09-2012
6266 wxRect availableSpace = GetParent()->GetAvailableContentArea(dc, context, rect);
6267
6268 anchored->LayoutToBestSize(dc, context, GetBuffer(),
6269 parentAttr, anchored->GetAttributes(),
6270 parentRect, availableSpace,
6271 style);
6272 wxSize size = anchored->GetCachedSize();
6273 #else
6274 wxSize size;
6275 int descent = 0;
6276 anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, context, style);
6277 #endif
6278
6279 int offsetY = 0;
6280 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6281 {
6282 offsetY = anchored->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6283 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6284 {
6285 offsetY = ConvertTenthsMMToPixels(dc, offsetY);
6286 }
6287 }
6288
6289 int pos = floatCollector->GetFitPosition(anchored->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect.y + offsetY, size.y);
6290
6291 /* Update the offset */
6292 int newOffsetY = pos - rect.y;
6293 if (newOffsetY != offsetY)
6294 {
6295 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6296 newOffsetY = ConvertPixelsToTenthsMM(dc, newOffsetY);
6297 anchored->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY);
6298 }
6299
6300 if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
6301 x = rect.x;
6302 else if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
6303 x = rect.x + rect.width - size.x;
6304
6305 //anchored->SetPosition(wxPoint(x, pos));
6306 anchored->Move(wxPoint(x, pos)); // should move children
6307 anchored->SetCachedSize(size);
6308 floatCollector->CollectFloat(this, anchored);
6309 }
6310
6311 node = node->GetNext();
6312 }
6313 }
6314
6315 // Get the first position from pos that has a line break character.
6316 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos)
6317 {
6318 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
6319 while (node)
6320 {
6321 wxRichTextObject* obj = node->GetData();
6322 if (pos >= obj->GetRange().GetStart() && pos <= obj->GetRange().GetEnd())
6323 {
6324 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
6325 if (textObj)
6326 {
6327 long breakPos = textObj->GetFirstLineBreakPosition(pos);
6328 if (breakPos > -1)
6329 return breakPos;
6330 }
6331 }
6332 node = node->GetNext();
6333 }
6334 return -1;
6335 }
6336
6337 /*!
6338 * wxRichTextLine
6339 * This object represents a line in a paragraph, and stores
6340 * offsets from the start of the paragraph representing the
6341 * start and end positions of the line.
6342 */
6343
6344 wxRichTextLine::wxRichTextLine(wxRichTextParagraph* parent)
6345 {
6346 Init(parent);
6347 }
6348
6349 /// Initialisation
6350 void wxRichTextLine::Init(wxRichTextParagraph* parent)
6351 {
6352 m_parent = parent;
6353 m_range.SetRange(-1, -1);
6354 m_pos = wxPoint(0, 0);
6355 m_size = wxSize(0, 0);
6356 m_descent = 0;
6357 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6358 m_objectSizes.Clear();
6359 #endif
6360 }
6361
6362 /// Copy
6363 void wxRichTextLine::Copy(const wxRichTextLine& obj)
6364 {
6365 m_range = obj.m_range;
6366 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6367 m_objectSizes = obj.m_objectSizes;
6368 #endif
6369 }
6370
6371 /// Get the absolute object position
6372 wxPoint wxRichTextLine::GetAbsolutePosition() const
6373 {
6374 return m_parent->GetPosition() + m_pos;
6375 }
6376
6377 /// Get the absolute range
6378 wxRichTextRange wxRichTextLine::GetAbsoluteRange() const
6379 {
6380 wxRichTextRange range(m_range.GetStart() + m_parent->GetRange().GetStart(), 0);
6381 range.SetEnd(range.GetStart() + m_range.GetLength()-1);
6382 return range;
6383 }
6384
6385 /*!
6386 * wxRichTextPlainText
6387 * This object represents a single piece of text.
6388 */
6389
6390 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText, wxRichTextObject)
6391
6392 wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* style):
6393 wxRichTextObject(parent)
6394 {
6395 if (style)
6396 SetAttributes(*style);
6397
6398 m_text = text;
6399 }
6400
6401 #define USE_KERNING_FIX 1
6402
6403 // If insufficient tabs are defined, this is the tab width used
6404 #define WIDTH_FOR_DEFAULT_TABS 50
6405
6406 /// Draw the item
6407 bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
6408 {
6409 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6410 wxASSERT (para != NULL);
6411
6412 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6413 context.ApplyVirtualAttributes(textAttr, this);
6414
6415 // Let's make the assumption for now that for content in a paragraph, including
6416 // text, we never have a discontinuous selection. So we only deal with a
6417 // single range.
6418 wxRichTextRange selectionRange;
6419 if (selection.IsValid())
6420 {
6421 wxRichTextRangeArray selectionRanges = selection.GetSelectionForObject(this);
6422 if (selectionRanges.GetCount() > 0)
6423 selectionRange = selectionRanges[0];
6424 else
6425 selectionRange = wxRICHTEXT_NO_SELECTION;
6426 }
6427 else
6428 selectionRange = wxRICHTEXT_NO_SELECTION;
6429
6430 int offset = GetRange().GetStart();
6431
6432 wxString str = m_text;
6433 if (context.HasVirtualText(this))
6434 {
6435 if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
6436 str = m_text;
6437 }
6438
6439 // Replace line break characters with spaces
6440 wxString toRemove = wxRichTextLineBreakChar;
6441 str.Replace(toRemove, wxT(" "));
6442 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
6443 str.MakeUpper();
6444
6445 long len = range.GetLength();
6446 wxString stringChunk = str.Mid(range.GetStart() - offset, (size_t) len);
6447
6448 // Test for the optimized situations where all is selected, or none
6449 // is selected.
6450
6451 wxFont textFont(GetBuffer()->GetFontTable().FindFont(textAttr));
6452 wxCheckSetFont(dc, textFont);
6453 int charHeight = dc.GetCharHeight();
6454
6455 int x, y;
6456 if ( textFont.IsOk() )
6457 {
6458 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
6459 {
6460 textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
6461 wxCheckSetFont(dc, textFont);
6462 charHeight = dc.GetCharHeight();
6463 }
6464
6465 if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT) )
6466 {
6467 if (textFont.IsUsingSizeInPixels())
6468 {
6469 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6470 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6471 x = rect.x;
6472 y = rect.y;
6473 }
6474 else
6475 {
6476 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6477 textFont.SetPointSize(static_cast<int>(size));
6478 x = rect.x;
6479 y = rect.y;
6480 }
6481 wxCheckSetFont(dc, textFont);
6482 }
6483 else if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) )
6484 {
6485 if (textFont.IsUsingSizeInPixels())
6486 {
6487 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6488 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6489 x = rect.x;
6490 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
6491 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6492 }
6493 else
6494 {
6495 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6496 textFont.SetPointSize(static_cast<int>(size));
6497 x = rect.x;
6498 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
6499 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6500 }
6501 wxCheckSetFont(dc, textFont);
6502 }
6503 else
6504 {
6505 x = rect.x;
6506 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6507 }
6508 }
6509 else
6510 {
6511 x = rect.x;
6512 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6513 }
6514
6515 // TODO: new selection code
6516
6517 // (a) All selected.
6518 if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd())
6519 {
6520 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, true);
6521 }
6522 // (b) None selected.
6523 else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd())
6524 {
6525 // Draw all unselected
6526 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, false);
6527 }
6528 else
6529 {
6530 // (c) Part selected, part not
6531 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6532
6533 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
6534
6535 // 1. Initial unselected chunk, if any, up until start of selection.
6536 if (selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd())
6537 {
6538 int r1 = range.GetStart();
6539 int s1 = selectionRange.GetStart()-1;
6540 int fragmentLen = s1 - r1 + 1;
6541 if (fragmentLen < 0)
6542 {
6543 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 - offset), (int)fragmentLen);
6544 }
6545 wxString stringFragment = str.Mid(r1 - offset, fragmentLen);
6546
6547 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
6548
6549 #if USE_KERNING_FIX
6550 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6551 {
6552 // Compensate for kerning difference
6553 wxString stringFragment2(str.Mid(r1 - offset, fragmentLen+1));
6554 wxString stringFragment3(str.Mid(r1 - offset + fragmentLen, 1));
6555
6556 wxCoord w1, h1, w2, h2, w3, h3;
6557 dc.GetTextExtent(stringFragment, & w1, & h1);
6558 dc.GetTextExtent(stringFragment2, & w2, & h2);
6559 dc.GetTextExtent(stringFragment3, & w3, & h3);
6560
6561 int kerningDiff = (w1 + w3) - w2;
6562 x = x - kerningDiff;
6563 }
6564 #endif
6565 }
6566
6567 // 2. Selected chunk, if any.
6568 if (selectionRange.GetEnd() >= range.GetStart())
6569 {
6570 int s1 = wxMax(selectionRange.GetStart(), range.GetStart());
6571 int s2 = wxMin(selectionRange.GetEnd(), range.GetEnd());
6572
6573 int fragmentLen = s2 - s1 + 1;
6574 if (fragmentLen < 0)
6575 {
6576 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 - offset), (int)fragmentLen);
6577 }
6578 wxString stringFragment = str.Mid(s1 - offset, fragmentLen);
6579
6580 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, true);
6581
6582 #if USE_KERNING_FIX
6583 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6584 {
6585 // Compensate for kerning difference
6586 wxString stringFragment2(str.Mid(s1 - offset, fragmentLen+1));
6587 wxString stringFragment3(str.Mid(s1 - offset + fragmentLen, 1));
6588
6589 wxCoord w1, h1, w2, h2, w3, h3;
6590 dc.GetTextExtent(stringFragment, & w1, & h1);
6591 dc.GetTextExtent(stringFragment2, & w2, & h2);
6592 dc.GetTextExtent(stringFragment3, & w3, & h3);
6593
6594 int kerningDiff = (w1 + w3) - w2;
6595 x = x - kerningDiff;
6596 }
6597 #endif
6598 }
6599
6600 // 3. Remaining unselected chunk, if any
6601 if (selectionRange.GetEnd() < range.GetEnd())
6602 {
6603 int s2 = wxMin(selectionRange.GetEnd()+1, range.GetEnd());
6604 int r2 = range.GetEnd();
6605
6606 int fragmentLen = r2 - s2 + 1;
6607 if (fragmentLen < 0)
6608 {
6609 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 - offset), (int)fragmentLen);
6610 }
6611 wxString stringFragment = str.Mid(s2 - offset, fragmentLen);
6612
6613 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
6614 }
6615 }
6616
6617 return true;
6618 }
6619
6620 bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect,wxString& str, wxCoord& x, wxCoord& y, bool selected)
6621 {
6622 bool hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6623
6624 wxArrayInt tabArray;
6625 int tabCount;
6626 if (hasTabs)
6627 {
6628 if (attr.GetTabs().IsEmpty())
6629 tabArray = wxRichTextParagraph::GetDefaultTabs();
6630 else
6631 tabArray = attr.GetTabs();
6632 tabCount = tabArray.GetCount();
6633
6634 for (int i = 0; i < tabCount; ++i)
6635 {
6636 int pos = tabArray[i];
6637 pos = ConvertTenthsMMToPixels(dc, pos);
6638 tabArray[i] = pos;
6639 }
6640 }
6641 else
6642 tabCount = 0;
6643
6644 int nextTabPos = -1;
6645 int tabPos = -1;
6646 wxCoord w, h;
6647
6648 if (selected)
6649 {
6650 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
6651 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
6652
6653 wxCheckSetBrush(dc, wxBrush(highlightColour));
6654 wxCheckSetPen(dc, wxPen(highlightColour));
6655 dc.SetTextForeground(highlightTextColour);
6656 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
6657 }
6658 else
6659 {
6660 dc.SetTextForeground(attr.GetTextColour());
6661
6662 if (attr.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR) && attr.GetBackgroundColour().IsOk())
6663 {
6664 dc.SetBackgroundMode(wxBRUSHSTYLE_SOLID);
6665 dc.SetTextBackground(attr.GetBackgroundColour());
6666 }
6667 else
6668 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
6669 }
6670
6671 wxCoord x_orig = GetParent()->GetPosition().x;
6672 while (hasTabs)
6673 {
6674 // the string has a tab
6675 // break up the string at the Tab
6676 wxString stringChunk = str.BeforeFirst(wxT('\t'));
6677 str = str.AfterFirst(wxT('\t'));
6678 dc.GetTextExtent(stringChunk, & w, & h);
6679 tabPos = x + w;
6680 bool not_found = true;
6681 for (int i = 0; i < tabCount && not_found; ++i)
6682 {
6683 nextTabPos = tabArray.Item(i) + x_orig;
6684
6685 // Find the next tab position.
6686 // Even if we're at the end of the tab array, we must still draw the chunk.
6687
6688 if (nextTabPos > tabPos || (i == (tabCount - 1)))
6689 {
6690 if (nextTabPos <= tabPos)
6691 {
6692 int defaultTabWidth = ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6693 nextTabPos = tabPos + defaultTabWidth;
6694 }
6695
6696 not_found = false;
6697 if (selected)
6698 {
6699 w = nextTabPos - x;
6700 wxRect selRect(x, rect.y, w, rect.GetHeight());
6701 dc.DrawRectangle(selRect);
6702 }
6703 dc.DrawText(stringChunk, x, y);
6704
6705 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6706 {
6707 wxPen oldPen = dc.GetPen();
6708 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
6709 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
6710 wxCheckSetPen(dc, oldPen);
6711 }
6712
6713 x = nextTabPos;
6714 }
6715 }
6716 hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6717 }
6718
6719 if (!str.IsEmpty())
6720 {
6721 dc.GetTextExtent(str, & w, & h);
6722 if (selected)
6723 {
6724 wxRect selRect(x, rect.y, w, rect.GetHeight());
6725 dc.DrawRectangle(selRect);
6726 }
6727 dc.DrawText(str, x, y);
6728
6729 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6730 {
6731 wxPen oldPen = dc.GetPen();
6732 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
6733 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
6734 wxCheckSetPen(dc, oldPen);
6735 }
6736
6737 x += w;
6738 }
6739
6740 return true;
6741 }
6742
6743 /// Lay the item out
6744 bool wxRichTextPlainText::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
6745 {
6746 // Only lay out if we haven't already cached the size
6747 if (m_size.x == -1)
6748 GetRangeSize(GetRange(), m_size, m_descent, dc, context, 0, wxPoint(0, 0));
6749 m_maxSize = m_size;
6750 // Eventually we want to have a reasonable estimate of minimum size.
6751 m_minSize = wxSize(0, 0);
6752 return true;
6753 }
6754
6755 /// Copy
6756 void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj)
6757 {
6758 wxRichTextObject::Copy(obj);
6759
6760 m_text = obj.m_text;
6761 }
6762
6763 /// Get/set the object size for the given range. Returns false if the range
6764 /// is invalid for this object.
6765 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
6766 {
6767 if (!range.IsWithin(GetRange()))
6768 return false;
6769
6770 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6771 wxASSERT (para != NULL);
6772
6773 int relativeX = position.x - GetParent()->GetPosition().x;
6774
6775 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6776 context.ApplyVirtualAttributes(textAttr, (wxRichTextObject*) this);
6777
6778 // Always assume unformatted text, since at this level we have no knowledge
6779 // of line breaks - and we don't need it, since we'll calculate size within
6780 // formatted text by doing it in chunks according to the line ranges
6781
6782 bool bScript(false);
6783 wxFont font(GetBuffer()->GetFontTable().FindFont(textAttr));
6784 if (font.IsOk())
6785 {
6786 if ( textAttr.HasTextEffects() && ( (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT)
6787 || (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) )
6788 {
6789 wxFont textFont = font;
6790 if (textFont.IsUsingSizeInPixels())
6791 {
6792 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6793 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6794 }
6795 else
6796 {
6797 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6798 textFont.SetPointSize(static_cast<int>(size));
6799 }
6800 wxCheckSetFont(dc, textFont);
6801 bScript = true;
6802 }
6803 else if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
6804 {
6805 wxFont textFont = font;
6806 textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
6807 wxCheckSetFont(dc, textFont);
6808 bScript = true;
6809 }
6810 else
6811 {
6812 wxCheckSetFont(dc, font);
6813 }
6814 }
6815
6816 bool haveDescent = false;
6817 int startPos = range.GetStart() - GetRange().GetStart();
6818 long len = range.GetLength();
6819
6820 wxString str(m_text);
6821 if (context.HasVirtualText(this))
6822 {
6823 if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
6824 str = m_text;
6825 }
6826
6827 wxString toReplace = wxRichTextLineBreakChar;
6828 str.Replace(toReplace, wxT(" "));
6829
6830 wxString stringChunk = str.Mid(startPos, (size_t) len);
6831
6832 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
6833 stringChunk.MakeUpper();
6834
6835 wxCoord w, h;
6836 int width = 0;
6837 if (stringChunk.Find(wxT('\t')) != wxNOT_FOUND)
6838 {
6839 // the string has a tab
6840 wxArrayInt tabArray;
6841 if (textAttr.GetTabs().IsEmpty())
6842 tabArray = wxRichTextParagraph::GetDefaultTabs();
6843 else
6844 tabArray = textAttr.GetTabs();
6845
6846 int tabCount = tabArray.GetCount();
6847
6848 for (int i = 0; i < tabCount; ++i)
6849 {
6850 int pos = tabArray[i];
6851 pos = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, pos);
6852 tabArray[i] = pos;
6853 }
6854
6855 int nextTabPos = -1;
6856
6857 while (stringChunk.Find(wxT('\t')) >= 0)
6858 {
6859 int absoluteWidth = 0;
6860
6861 // the string has a tab
6862 // break up the string at the Tab
6863 wxString stringFragment = stringChunk.BeforeFirst(wxT('\t'));
6864 stringChunk = stringChunk.AfterFirst(wxT('\t'));
6865
6866 if (partialExtents)
6867 {
6868 int oldWidth;
6869 if (partialExtents->GetCount() > 0)
6870 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6871 else
6872 oldWidth = 0;
6873
6874 // Add these partial extents
6875 wxArrayInt p;
6876 dc.GetPartialTextExtents(stringFragment, p);
6877 size_t j;
6878 for (j = 0; j < p.GetCount(); j++)
6879 partialExtents->Add(oldWidth + p[j]);
6880
6881 if (partialExtents->GetCount() > 0)
6882 absoluteWidth = (*partialExtents)[(*partialExtents).GetCount()-1] + relativeX;
6883 else
6884 absoluteWidth = relativeX;
6885 }
6886 else
6887 {
6888 dc.GetTextExtent(stringFragment, & w, & h);
6889 width += w;
6890 absoluteWidth = width + relativeX;
6891 haveDescent = true;
6892 }
6893
6894 bool notFound = true;
6895 for (int i = 0; i < tabCount && notFound; ++i)
6896 {
6897 nextTabPos = tabArray.Item(i);
6898
6899 // Find the next tab position.
6900 // Even if we're at the end of the tab array, we must still process the chunk.
6901
6902 if (nextTabPos > absoluteWidth || (i == (tabCount - 1)))
6903 {
6904 if (nextTabPos <= absoluteWidth)
6905 {
6906 int defaultTabWidth = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6907 nextTabPos = absoluteWidth + defaultTabWidth;
6908 }
6909
6910 notFound = false;
6911 width = nextTabPos - relativeX;
6912
6913 if (partialExtents)
6914 partialExtents->Add(width);
6915 }
6916 }
6917 }
6918 }
6919
6920 if (!stringChunk.IsEmpty())
6921 {
6922 if (partialExtents)
6923 {
6924 int oldWidth;
6925 if (partialExtents->GetCount() > 0)
6926 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6927 else
6928 oldWidth = 0;
6929
6930 // Add these partial extents
6931 wxArrayInt p;
6932 dc.GetPartialTextExtents(stringChunk, p);
6933 size_t j;
6934 for (j = 0; j < p.GetCount(); j++)
6935 partialExtents->Add(oldWidth + p[j]);
6936 }
6937 else
6938 {
6939 dc.GetTextExtent(stringChunk, & w, & h, & descent);
6940 width += w;
6941 haveDescent = true;
6942 }
6943 }
6944
6945 if (partialExtents)
6946 {
6947 int charHeight = dc.GetCharHeight();
6948 if ((*partialExtents).GetCount() > 0)
6949 w = (*partialExtents)[partialExtents->GetCount()-1];
6950 else
6951 w = 0;
6952 size = wxSize(w, charHeight);
6953 }
6954 else
6955 {
6956 size = wxSize(width, dc.GetCharHeight());
6957 }
6958
6959 if (!haveDescent)
6960 dc.GetTextExtent(wxT("X"), & w, & h, & descent);
6961
6962 if ( bScript )
6963 dc.SetFont(font);
6964
6965 return true;
6966 }
6967
6968 /// Do a split, returning an object containing the second part, and setting
6969 /// the first part in 'this'.
6970 wxRichTextObject* wxRichTextPlainText::DoSplit(long pos)
6971 {
6972 long index = pos - GetRange().GetStart();
6973
6974 if (index < 0 || index >= (int) m_text.length())
6975 return NULL;
6976
6977 wxString firstPart = m_text.Mid(0, index);
6978 wxString secondPart = m_text.Mid(index);
6979
6980 m_text = firstPart;
6981
6982 wxRichTextPlainText* newObject = new wxRichTextPlainText(secondPart);
6983 newObject->SetAttributes(GetAttributes());
6984 newObject->SetProperties(GetProperties());
6985
6986 newObject->SetRange(wxRichTextRange(pos, GetRange().GetEnd()));
6987 GetRange().SetEnd(pos-1);
6988
6989 return newObject;
6990 }
6991
6992 /// Calculate range
6993 void wxRichTextPlainText::CalculateRange(long start, long& end)
6994 {
6995 end = start + m_text.length() - 1;
6996 m_range.SetRange(start, end);
6997 }
6998
6999 /// Delete range
7000 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange& range)
7001 {
7002 wxRichTextRange r = range;
7003
7004 r.LimitTo(GetRange());
7005
7006 if (r.GetStart() == GetRange().GetStart() && r.GetEnd() == GetRange().GetEnd())
7007 {
7008 m_text.Empty();
7009 return true;
7010 }
7011
7012 long startIndex = r.GetStart() - GetRange().GetStart();
7013 long len = r.GetLength();
7014
7015 m_text = m_text.Mid(0, startIndex) + m_text.Mid(startIndex+len);
7016 return true;
7017 }
7018
7019 /// Get text for the given range.
7020 wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) const
7021 {
7022 wxRichTextRange r = range;
7023
7024 r.LimitTo(GetRange());
7025
7026 long startIndex = r.GetStart() - GetRange().GetStart();
7027 long len = r.GetLength();
7028
7029 return m_text.Mid(startIndex, len);
7030 }
7031
7032 /// Returns true if this object can merge itself with the given one.
7033 bool wxRichTextPlainText::CanMerge(wxRichTextObject* object, wxRichTextDrawingContext& context) const
7034 {
7035 // JACS 2013-01-27
7036 if (!context.GetVirtualAttributesEnabled())
7037 {
7038 return object->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText) &&
7039 (m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties()));
7040 }
7041 else
7042 {
7043 wxRichTextPlainText* otherObj = wxDynamicCast(object, wxRichTextPlainText);
7044 if (!otherObj || m_text.empty())
7045 return false;
7046
7047 if (!wxTextAttrEq(GetAttributes(), object->GetAttributes()) || !(m_properties == object->GetProperties()))
7048 return false;
7049
7050 // Check if differing virtual attributes makes it impossible to merge
7051 // these strings.
7052
7053 bool hasVirtualAttr1 = context.HasVirtualAttributes((wxRichTextObject*) this);
7054 bool hasVirtualAttr2 = context.HasVirtualAttributes((wxRichTextObject*) object);
7055 if (!hasVirtualAttr1 && !hasVirtualAttr2)
7056 return true;
7057 else if (hasVirtualAttr1 != hasVirtualAttr2)
7058 return false;
7059 else
7060 {
7061 wxRichTextAttr virtualAttr1 = context.GetVirtualAttributes((wxRichTextObject*) this);
7062 wxRichTextAttr virtualAttr2 = context.GetVirtualAttributes((wxRichTextObject*) object);
7063 return virtualAttr1 == virtualAttr2;
7064 }
7065 }
7066 }
7067
7068 /// Returns true if this object merged itself with the given one.
7069 /// The calling code will then delete the given object.
7070 bool wxRichTextPlainText::Merge(wxRichTextObject* object, wxRichTextDrawingContext& WXUNUSED(context))
7071 {
7072 wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText);
7073 wxASSERT( textObject != NULL );
7074
7075 if (textObject)
7076 {
7077 m_text += textObject->GetText();
7078 wxRichTextApplyStyle(m_attributes, textObject->GetAttributes());
7079 return true;
7080 }
7081 else
7082 return false;
7083 }
7084
7085 bool wxRichTextPlainText::CanSplit(wxRichTextDrawingContext& context) const
7086 {
7087 // If this object has any virtual attributes at all, whether for the whole object
7088 // or individual ones, we should try splitting it by calling Split.
7089 // Must be more than one character in order to be able to split.
7090 return m_text.Length() > 1 && context.HasVirtualAttributes((wxRichTextObject*) this);
7091 }
7092
7093 wxRichTextObject* wxRichTextPlainText::Split(wxRichTextDrawingContext& context)
7094 {
7095 int count = context.GetVirtualSubobjectAttributesCount(this);
7096 if (count > 0 && GetParent())
7097 {
7098 wxRichTextCompositeObject* parent = wxDynamicCast(GetParent(), wxRichTextCompositeObject);
7099 wxRichTextObjectList::compatibility_iterator node = parent->GetChildren().Find(this);
7100 if (node)
7101 {
7102 const wxRichTextAttr emptyAttr;
7103 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7104
7105 wxArrayInt positions;
7106 wxRichTextAttrArray attributes;
7107 if (context.GetVirtualSubobjectAttributes(this, positions, attributes) && positions.GetCount() > 0)
7108 {
7109 wxASSERT(positions.GetCount() == attributes.GetCount());
7110
7111 // We will gather up runs of text with the same virtual attributes
7112
7113 int len = m_text.Length();
7114 int i = 0;
7115
7116 // runStart and runEnd represent the accumulated run with a consistent attribute
7117 // that hasn't yet been appended
7118 int runStart = -1;
7119 int runEnd = -1;
7120 wxRichTextAttr currentAttr;
7121 wxString text = m_text;
7122 wxRichTextPlainText* lastPlainText = this;
7123
7124 for (i = 0; i < (int) positions.GetCount(); i++)
7125 {
7126 int pos = positions[i];
7127 wxASSERT(pos >= 0 && pos < len);
7128 if (pos >= 0 && pos < len)
7129 {
7130 const wxRichTextAttr& attr = attributes[i];
7131
7132 if (pos == 0)
7133 {
7134 runStart = 0;
7135 currentAttr = attr;
7136 }
7137 // Check if there was a gap from the last known attribute and this.
7138 // In that case, we need to do something with the span of non-attributed text.
7139 else if ((pos-1) > runEnd)
7140 {
7141 if (runEnd == -1)
7142 {
7143 // We hadn't processed anything previously, so the previous run is from the text start
7144 // to just before this position. The current attribute remains empty.
7145 runStart = 0;
7146 runEnd = pos-1;
7147 }
7148 else
7149 {
7150 // If the previous attribute matches the gap's attribute (i.e., no attributes)
7151 // then just extend the run.
7152 if (currentAttr.IsDefault())
7153 {
7154 runEnd = pos-1;
7155 }
7156 else
7157 {
7158 // We need to add an object, or reuse the existing one.
7159 if (runStart == 0)
7160 {
7161 lastPlainText = this;
7162 SetText(text.Mid(runStart, runEnd - runStart + 1));
7163 }
7164 else
7165 {
7166 wxRichTextPlainText* obj = new wxRichTextPlainText;
7167 lastPlainText = obj;
7168 obj->SetAttributes(GetAttributes());
7169 obj->SetProperties(GetProperties());
7170 obj->SetParent(parent);
7171
7172 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7173 if (next)
7174 parent->GetChildren().Insert(next, obj);
7175 else
7176 parent->GetChildren().Append(obj);
7177 }
7178
7179 runStart = runEnd+1;
7180 runEnd = pos-1;
7181
7182 currentAttr = emptyAttr;
7183 }
7184 }
7185 }
7186
7187 wxASSERT(runEnd == pos-1);
7188
7189 // Now we only have to deal with the previous run
7190 if (currentAttr == attr)
7191 {
7192 // If we still have the same attributes, then we
7193 // simply increase the run size.
7194 runEnd = pos;
7195 }
7196 else
7197 {
7198 if (runEnd >= 0)
7199 {
7200 // We need to add an object, or reuse the existing one.
7201 if (runStart == 0)
7202 {
7203 lastPlainText = this;
7204 SetText(text.Mid(runStart, runEnd - runStart + 1));
7205 }
7206 else
7207 {
7208 wxRichTextPlainText* obj = new wxRichTextPlainText;
7209 lastPlainText = obj;
7210 obj->SetAttributes(GetAttributes());
7211 obj->SetProperties(GetProperties());
7212 obj->SetParent(parent);
7213
7214 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7215 if (next)
7216 parent->GetChildren().Insert(next, obj);
7217 else
7218 parent->GetChildren().Append(obj);
7219 }
7220 }
7221
7222 runStart = pos;
7223 runEnd = pos;
7224
7225 currentAttr = attr;
7226 }
7227 }
7228 }
7229
7230 // We may still have a run to add, and possibly a no-attribute text fragment after that.
7231 // If the whole string was already a single attribute (the run covers the whole string), don't split.
7232 if ((runStart != -1) && !(runStart == 0 && runEnd == (len-1)))
7233 {
7234 // If the current attribute is empty, merge the run with the next fragment
7235 // which by definition (because it's not specified) has empty attributes.
7236 if (currentAttr.IsDefault())
7237 runEnd = (len-1);
7238
7239 if (runEnd < (len-1))
7240 {
7241 // We need to add an object, or reuse the existing one.
7242 if (runStart == 0)
7243 {
7244 lastPlainText = this;
7245 SetText(text.Mid(runStart, runEnd - runStart + 1));
7246 }
7247 else
7248 {
7249 wxRichTextPlainText* obj = new wxRichTextPlainText;
7250 lastPlainText = obj;
7251 obj->SetAttributes(GetAttributes());
7252 obj->SetProperties(GetProperties());
7253 obj->SetParent(parent);
7254
7255 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7256 if (next)
7257 parent->GetChildren().Insert(next, obj);
7258 else
7259 parent->GetChildren().Append(obj);
7260 }
7261
7262 runStart = runEnd+1;
7263 runEnd = (len-1);
7264 }
7265
7266 // Now the last, non-attributed fragment at the end, if any
7267 if ((runStart < len) && !(runStart == 0 && runEnd == (len-1)))
7268 {
7269 wxASSERT(runStart != 0);
7270
7271 wxRichTextPlainText* obj = new wxRichTextPlainText;
7272 obj->SetAttributes(GetAttributes());
7273 obj->SetProperties(GetProperties());
7274 obj->SetParent(parent);
7275
7276 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7277 if (next)
7278 parent->GetChildren().Insert(next, obj);
7279 else
7280 parent->GetChildren().Append(obj);
7281
7282 lastPlainText = obj;
7283 }
7284 }
7285
7286 return lastPlainText;
7287 }
7288 }
7289 }
7290 return this;
7291 }
7292
7293 /// Dump to output stream for debugging
7294 void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
7295 {
7296 wxRichTextObject::Dump(stream);
7297 stream << m_text << wxT("\n");
7298 }
7299
7300 /// Get the first position from pos that has a line break character.
7301 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos)
7302 {
7303 int i;
7304 int len = m_text.length();
7305 int startPos = pos - m_range.GetStart();
7306 for (i = startPos; i < len; i++)
7307 {
7308 wxChar ch = m_text[i];
7309 if (ch == wxRichTextLineBreakChar)
7310 {
7311 return i + m_range.GetStart();
7312 }
7313 }
7314 return -1;
7315 }
7316
7317 /*!
7318 * wxRichTextBuffer
7319 * This is a kind of box, used to represent the whole buffer
7320 */
7321
7322 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox)
7323
7324 wxList wxRichTextBuffer::sm_handlers;
7325 wxList wxRichTextBuffer::sm_drawingHandlers;
7326 wxRichTextFieldTypeHashMap wxRichTextBuffer::sm_fieldTypes;
7327 wxRichTextRenderer* wxRichTextBuffer::sm_renderer = NULL;
7328 int wxRichTextBuffer::sm_bulletRightMargin = 20;
7329 float wxRichTextBuffer::sm_bulletProportion = (float) 0.3;
7330 bool wxRichTextBuffer::sm_floatingLayoutMode = true;
7331
7332 /// Initialisation
7333 void wxRichTextBuffer::Init()
7334 {
7335 m_commandProcessor = new wxCommandProcessor;
7336 m_styleSheet = NULL;
7337 m_modified = false;
7338 m_batchedCommandDepth = 0;
7339 m_batchedCommand = NULL;
7340 m_suppressUndo = 0;
7341 m_handlerFlags = 0;
7342 m_scale = 1.0;
7343 m_dimensionScale = 1.0;
7344 m_fontScale = 1.0;
7345 SetMargins(4);
7346 }
7347
7348 /// Initialisation
7349 wxRichTextBuffer::~wxRichTextBuffer()
7350 {
7351 delete m_commandProcessor;
7352 delete m_batchedCommand;
7353
7354 ClearStyleStack();
7355 ClearEventHandlers();
7356 }
7357
7358 void wxRichTextBuffer::ResetAndClearCommands()
7359 {
7360 Reset();
7361
7362 GetCommandProcessor()->ClearCommands();
7363
7364 Modify(false);
7365 Invalidate(wxRICHTEXT_ALL);
7366 }
7367
7368 void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj)
7369 {
7370 wxRichTextParagraphLayoutBox::Copy(obj);
7371
7372 m_styleSheet = obj.m_styleSheet;
7373 m_modified = obj.m_modified;
7374 m_batchedCommandDepth = 0;
7375 if (m_batchedCommand)
7376 delete m_batchedCommand;
7377 m_batchedCommand = NULL;
7378 m_suppressUndo = obj.m_suppressUndo;
7379 m_invalidRange = obj.m_invalidRange;
7380 m_dimensionScale = obj.m_dimensionScale;
7381 m_fontScale = obj.m_fontScale;
7382 }
7383
7384 /// Push style sheet to top of stack
7385 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet* styleSheet)
7386 {
7387 if (m_styleSheet)
7388 styleSheet->InsertSheet(m_styleSheet);
7389
7390 SetStyleSheet(styleSheet);
7391
7392 return true;
7393 }
7394
7395 /// Pop style sheet from top of stack
7396 wxRichTextStyleSheet* wxRichTextBuffer::PopStyleSheet()
7397 {
7398 if (m_styleSheet)
7399 {
7400 wxRichTextStyleSheet* oldSheet = m_styleSheet;
7401 m_styleSheet = oldSheet->GetNextSheet();
7402 oldSheet->Unlink();
7403
7404 return oldSheet;
7405 }
7406 else
7407 return NULL;
7408 }
7409
7410 /// Submit command to insert paragraphs
7411 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int flags)
7412 {
7413 return ctrl->GetFocusObject()->InsertParagraphsWithUndo(this, pos, paragraphs, ctrl, flags);
7414 }
7415
7416 /// Submit command to insert paragraphs
7417 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int WXUNUSED(flags))
7418 {
7419 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7420
7421 action->GetNewParagraphs() = paragraphs;
7422
7423 action->SetPosition(pos);
7424
7425 wxRichTextRange range = wxRichTextRange(pos, pos + paragraphs.GetOwnRange().GetEnd() - 1);
7426 if (!paragraphs.GetPartialParagraph())
7427 range.SetEnd(range.GetEnd()+1);
7428
7429 // Set the range we'll need to delete in Undo
7430 action->SetRange(range);
7431
7432 buffer->SubmitAction(action);
7433
7434 return true;
7435 }
7436
7437 /// Submit command to insert the given text
7438 bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
7439 {
7440 return ctrl->GetFocusObject()->InsertTextWithUndo(this, pos, text, ctrl, flags);
7441 }
7442
7443 /// Submit command to insert the given text
7444 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
7445 {
7446 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7447
7448 wxRichTextAttr* p = NULL;
7449 wxRichTextAttr paraAttr;
7450 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7451 {
7452 // Get appropriate paragraph style
7453 paraAttr = GetStyleForNewParagraph(buffer, pos, false, false);
7454 if (!paraAttr.IsDefault())
7455 p = & paraAttr;
7456 }
7457
7458 action->GetNewParagraphs().AddParagraphs(text, p);
7459
7460 int length = action->GetNewParagraphs().GetOwnRange().GetLength();
7461
7462 if (!text.empty() && text.Last() != wxT('\n'))
7463 {
7464 // Don't count the newline when undoing
7465 length --;
7466 action->GetNewParagraphs().SetPartialParagraph(true);
7467 }
7468 else if (!text.empty() && text.Last() == wxT('\n'))
7469 length --;
7470
7471 action->SetPosition(pos);
7472
7473 // Set the range we'll need to delete in Undo
7474 action->SetRange(wxRichTextRange(pos, pos + length - 1));
7475
7476 buffer->SubmitAction(action);
7477
7478 return true;
7479 }
7480
7481 /// Submit command to insert the given text
7482 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl, int flags)
7483 {
7484 return ctrl->GetFocusObject()->InsertNewlineWithUndo(this, pos, ctrl, flags);
7485 }
7486
7487 /// Submit command to insert the given text
7488 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextCtrl* ctrl, int flags)
7489 {
7490 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7491
7492 wxRichTextAttr* p = NULL;
7493 wxRichTextAttr paraAttr;
7494 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7495 {
7496 paraAttr = GetStyleForNewParagraph(buffer, pos, false, true /* look for next paragraph style */);
7497 if (!paraAttr.IsDefault())
7498 p = & paraAttr;
7499 }
7500
7501 wxRichTextAttr attr(buffer->GetDefaultStyle());
7502 // Don't include box attributes such as margins
7503 attr.GetTextBoxAttr().Reset();
7504
7505 wxRichTextParagraph* newPara = new wxRichTextParagraph(wxEmptyString, this, & attr);
7506 action->GetNewParagraphs().AppendChild(newPara);
7507 action->GetNewParagraphs().UpdateRanges();
7508 action->GetNewParagraphs().SetPartialParagraph(false);
7509 wxRichTextParagraph* para = GetParagraphAtPosition(pos, false);
7510 long pos1 = pos;
7511
7512 if (p)
7513 newPara->SetAttributes(*p);
7514
7515 if (flags & wxRICHTEXT_INSERT_INTERACTIVE)
7516 {
7517 if (para && para->GetRange().GetEnd() == pos)
7518 pos1 ++;
7519
7520 // Now see if we need to number the paragraph.
7521 if (newPara->GetAttributes().HasBulletNumber())
7522 {
7523 wxRichTextAttr numberingAttr;
7524 if (FindNextParagraphNumber(para, numberingAttr))
7525 wxRichTextApplyStyle(newPara->GetAttributes(), (const wxRichTextAttr&) numberingAttr);
7526 }
7527 }
7528
7529 action->SetPosition(pos);
7530
7531 // Use the default character style
7532 if (!buffer->GetDefaultStyle().IsDefault() && newPara->GetChildren().GetFirst())
7533 {
7534 // Check whether the default style merely reflects the paragraph/basic style,
7535 // in which case don't apply it.
7536 wxRichTextAttr defaultStyle(buffer->GetDefaultStyle());
7537 defaultStyle.GetTextBoxAttr().Reset();
7538 wxRichTextAttr toApply;
7539 if (para)
7540 {
7541 wxRichTextAttr combinedAttr = para->GetCombinedAttributes(true /* include box attributes */);
7542 wxRichTextAttr newAttr;
7543 // This filters out attributes that are accounted for by the current
7544 // paragraph/basic style
7545 wxRichTextApplyStyle(toApply, defaultStyle, & combinedAttr);
7546 }
7547 else
7548 toApply = defaultStyle;
7549
7550 if (!toApply.IsDefault())
7551 newPara->GetChildren().GetFirst()->GetData()->SetAttributes(toApply);
7552 }
7553
7554 // Set the range we'll need to delete in Undo
7555 action->SetRange(wxRichTextRange(pos1, pos1));
7556
7557 buffer->SubmitAction(action);
7558
7559 return true;
7560 }
7561
7562 /// Submit command to insert the given image
7563 bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock, wxRichTextCtrl* ctrl, int flags,
7564 const wxRichTextAttr& textAttr)
7565 {
7566 return ctrl->GetFocusObject()->InsertImageWithUndo(this, pos, imageBlock, ctrl, flags, textAttr);
7567 }
7568
7569 /// Submit command to insert the given image
7570 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextImageBlock& imageBlock,
7571 wxRichTextCtrl* ctrl, int flags,
7572 const wxRichTextAttr& textAttr)
7573 {
7574 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7575
7576 wxRichTextAttr* p = NULL;
7577 wxRichTextAttr paraAttr;
7578 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7579 {
7580 paraAttr = GetStyleForNewParagraph(buffer, pos);
7581 if (!paraAttr.IsDefault())
7582 p = & paraAttr;
7583 }
7584
7585 wxRichTextAttr attr(buffer->GetDefaultStyle());
7586
7587 // Don't include box attributes such as margins
7588 attr.GetTextBoxAttr().Reset();
7589
7590 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7591 if (p)
7592 newPara->SetAttributes(*p);
7593
7594 wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
7595 newPara->AppendChild(imageObject);
7596 imageObject->SetAttributes(textAttr);
7597 action->GetNewParagraphs().AppendChild(newPara);
7598 action->GetNewParagraphs().UpdateRanges();
7599
7600 action->GetNewParagraphs().SetPartialParagraph(true);
7601
7602 action->SetPosition(pos);
7603
7604 // Set the range we'll need to delete in Undo
7605 action->SetRange(wxRichTextRange(pos, pos));
7606
7607 buffer->SubmitAction(action);
7608
7609 return true;
7610 }
7611
7612 // Insert an object with no change of it
7613 wxRichTextObject* wxRichTextBuffer::InsertObjectWithUndo(long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
7614 {
7615 return ctrl->GetFocusObject()->InsertObjectWithUndo(this, pos, object, ctrl, flags);
7616 }
7617
7618 // Insert an object with no change of it
7619 wxRichTextObject* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
7620 {
7621 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Object"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7622
7623 wxRichTextAttr* p = NULL;
7624 wxRichTextAttr paraAttr;
7625 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7626 {
7627 paraAttr = GetStyleForNewParagraph(buffer, pos);
7628 if (!paraAttr.IsDefault())
7629 p = & paraAttr;
7630 }
7631
7632 wxRichTextAttr attr(buffer->GetDefaultStyle());
7633
7634 // Don't include box attributes such as margins
7635 attr.GetTextBoxAttr().Reset();
7636
7637 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7638 if (p)
7639 newPara->SetAttributes(*p);
7640
7641 newPara->AppendChild(object);
7642 action->GetNewParagraphs().AppendChild(newPara);
7643 action->GetNewParagraphs().UpdateRanges();
7644
7645 action->GetNewParagraphs().SetPartialParagraph(true);
7646
7647 action->SetPosition(pos);
7648
7649 // Set the range we'll need to delete in Undo
7650 action->SetRange(wxRichTextRange(pos, pos));
7651
7652 buffer->SubmitAction(action);
7653
7654 wxRichTextObject* obj = GetLeafObjectAtPosition(pos);
7655 return obj;
7656 }
7657
7658 wxRichTextField* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& fieldType,
7659 const wxRichTextProperties& properties,
7660 wxRichTextCtrl* ctrl, int flags,
7661 const wxRichTextAttr& textAttr)
7662 {
7663 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Field"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7664
7665 wxRichTextAttr* p = NULL;
7666 wxRichTextAttr paraAttr;
7667 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7668 {
7669 paraAttr = GetStyleForNewParagraph(buffer, pos);
7670 if (!paraAttr.IsDefault())
7671 p = & paraAttr;
7672 }
7673
7674 wxRichTextAttr attr(buffer->GetDefaultStyle());
7675
7676 // Don't include box attributes such as margins
7677 attr.GetTextBoxAttr().Reset();
7678
7679 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7680 if (p)
7681 newPara->SetAttributes(*p);
7682
7683 wxRichTextField* fieldObject = new wxRichTextField();
7684 fieldObject->wxRichTextObject::SetProperties(properties);
7685 fieldObject->SetFieldType(fieldType);
7686 fieldObject->SetAttributes(textAttr);
7687 newPara->AppendChild(fieldObject);
7688 action->GetNewParagraphs().AppendChild(newPara);
7689 action->GetNewParagraphs().UpdateRanges();
7690 action->GetNewParagraphs().SetPartialParagraph(true);
7691 action->SetPosition(pos);
7692
7693 // Set the range we'll need to delete in Undo
7694 action->SetRange(wxRichTextRange(pos, pos));
7695
7696 buffer->SubmitAction(action);
7697
7698 wxRichTextField* obj = wxDynamicCast(GetLeafObjectAtPosition(pos), wxRichTextField);
7699 return obj;
7700 }
7701
7702 /// Get the style that is appropriate for a new paragraph at this position.
7703 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7704 /// style.
7705 wxRichTextAttr wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer* buffer, long pos, bool caretPosition, bool lookUpNewParaStyle) const
7706 {
7707 wxRichTextParagraph* para = GetParagraphAtPosition(pos, caretPosition);
7708 if (para)
7709 {
7710 wxRichTextAttr attr;
7711 bool foundAttributes = false;
7712
7713 // Look for a matching paragraph style
7714 if (lookUpNewParaStyle && !para->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer->GetStyleSheet())
7715 {
7716 wxRichTextParagraphStyleDefinition* paraDef = buffer->GetStyleSheet()->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
7717 if (paraDef)
7718 {
7719 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7720 if (para->GetRange().GetEnd() == pos && !paraDef->GetNextStyle().IsEmpty())
7721 {
7722 wxRichTextParagraphStyleDefinition* nextParaDef = buffer->GetStyleSheet()->FindParagraphStyle(paraDef->GetNextStyle());
7723 if (nextParaDef)
7724 {
7725 foundAttributes = true;
7726 attr = nextParaDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
7727 }
7728 }
7729
7730 // If we didn't find the 'next style', use this style instead.
7731 if (!foundAttributes)
7732 {
7733 foundAttributes = true;
7734 attr = paraDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
7735 }
7736 }
7737 }
7738
7739 // Also apply list style if present
7740 if (lookUpNewParaStyle && !para->GetAttributes().GetListStyleName().IsEmpty() && buffer->GetStyleSheet())
7741 {
7742 wxRichTextListStyleDefinition* listDef = buffer->GetStyleSheet()->FindListStyle(para->GetAttributes().GetListStyleName());
7743 if (listDef)
7744 {
7745 int thisIndent = para->GetAttributes().GetLeftIndent();
7746 int thisLevel = para->GetAttributes().HasOutlineLevel() ? para->GetAttributes().GetOutlineLevel() : listDef->FindLevelForIndent(thisIndent);
7747
7748 // Apply the overall list style, and item style for this level
7749 wxRichTextAttr listStyle(listDef->GetCombinedStyleForLevel(thisLevel, buffer->GetStyleSheet()));
7750 wxRichTextApplyStyle(attr, listStyle);
7751 attr.SetOutlineLevel(thisLevel);
7752 if (para->GetAttributes().HasBulletNumber())
7753 attr.SetBulletNumber(para->GetAttributes().GetBulletNumber());
7754 }
7755 }
7756
7757 if (!foundAttributes)
7758 {
7759 attr = para->GetAttributes();
7760 int flags = attr.GetFlags();
7761
7762 // Eliminate character styles
7763 flags &= ( (~ wxTEXT_ATTR_FONT) |
7764 (~ wxTEXT_ATTR_TEXT_COLOUR) |
7765 (~ wxTEXT_ATTR_BACKGROUND_COLOUR) );
7766 attr.SetFlags(flags);
7767 }
7768
7769 return attr;
7770 }
7771 else
7772 return wxRichTextAttr();
7773 }
7774
7775 /// Submit command to delete this range
7776 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl)
7777 {
7778 return ctrl->GetFocusObject()->DeleteRangeWithUndo(range, ctrl, this);
7779 }
7780
7781 /// Submit command to delete this range
7782 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer)
7783 {
7784 wxRichTextAction* action = new wxRichTextAction(NULL, _("Delete"), wxRICHTEXT_DELETE, buffer, this, ctrl);
7785
7786 action->SetPosition(ctrl->GetCaretPosition());
7787
7788 // Set the range to delete
7789 action->SetRange(range);
7790
7791 // Copy the fragment that we'll need to restore in Undo
7792 CopyFragment(range, action->GetOldParagraphs());
7793
7794 // See if we're deleting a paragraph marker, in which case we need to
7795 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7796 if (range.GetStart() == range.GetEnd())
7797 {
7798 wxRichTextParagraph* para = GetParagraphAtPosition(range.GetStart());
7799 if (para && para->GetRange().GetEnd() == range.GetEnd())
7800 {
7801 wxRichTextParagraph* nextPara = GetParagraphAtPosition(range.GetStart()+1);
7802 if (nextPara && nextPara != para)
7803 {
7804 action->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara->GetAttributes());
7805 action->GetOldParagraphs().GetAttributes().SetFlags(action->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE);
7806 }
7807 }
7808 }
7809
7810 buffer->SubmitAction(action);
7811
7812 return true;
7813 }
7814
7815 /// Collapse undo/redo commands
7816 bool wxRichTextBuffer::BeginBatchUndo(const wxString& cmdName)
7817 {
7818 if (m_batchedCommandDepth == 0)
7819 {
7820 wxASSERT(m_batchedCommand == NULL);
7821 if (m_batchedCommand)
7822 {
7823 GetCommandProcessor()->Store(m_batchedCommand);
7824 }
7825 m_batchedCommand = new wxRichTextCommand(cmdName);
7826 }
7827
7828 m_batchedCommandDepth ++;
7829
7830 return true;
7831 }
7832
7833 /// Collapse undo/redo commands
7834 bool wxRichTextBuffer::EndBatchUndo()
7835 {
7836 m_batchedCommandDepth --;
7837
7838 wxASSERT(m_batchedCommandDepth >= 0);
7839 wxASSERT(m_batchedCommand != NULL);
7840
7841 if (m_batchedCommandDepth == 0)
7842 {
7843 GetCommandProcessor()->Store(m_batchedCommand);
7844 m_batchedCommand = NULL;
7845 }
7846
7847 return true;
7848 }
7849
7850 /// Submit immediately, or delay according to whether collapsing is on
7851 bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action)
7852 {
7853 if (action && !action->GetNewParagraphs().IsEmpty())
7854 PrepareContent(action->GetNewParagraphs());
7855
7856 if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
7857 {
7858 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7859 cmd->AddAction(action);
7860 cmd->Do();
7861 cmd->GetActions().Clear();
7862 delete cmd;
7863
7864 m_batchedCommand->AddAction(action);
7865 }
7866 else
7867 {
7868 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7869 cmd->AddAction(action);
7870
7871 // Only store it if we're not suppressing undo.
7872 return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
7873 }
7874
7875 return true;
7876 }
7877
7878 /// Begin suppressing undo/redo commands.
7879 bool wxRichTextBuffer::BeginSuppressUndo()
7880 {
7881 m_suppressUndo ++;
7882
7883 return true;
7884 }
7885
7886 /// End suppressing undo/redo commands.
7887 bool wxRichTextBuffer::EndSuppressUndo()
7888 {
7889 m_suppressUndo --;
7890
7891 return true;
7892 }
7893
7894 /// Begin using a style
7895 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr& style)
7896 {
7897 wxRichTextAttr newStyle(GetDefaultStyle());
7898 newStyle.GetTextBoxAttr().Reset();
7899
7900 // Save the old default style
7901 m_attributeStack.Append((wxObject*) new wxRichTextAttr(newStyle));
7902
7903 wxRichTextApplyStyle(newStyle, style);
7904 newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags());
7905
7906 SetDefaultStyle(newStyle);
7907
7908 return true;
7909 }
7910
7911 /// End the style
7912 bool wxRichTextBuffer::EndStyle()
7913 {
7914 if (!m_attributeStack.GetFirst())
7915 {
7916 wxLogDebug(_("Too many EndStyle calls!"));
7917 return false;
7918 }
7919
7920 wxList::compatibility_iterator node = m_attributeStack.GetLast();
7921 wxRichTextAttr* attr = (wxRichTextAttr*)node->GetData();
7922 m_attributeStack.Erase(node);
7923
7924 SetDefaultStyle(*attr);
7925
7926 delete attr;
7927 return true;
7928 }
7929
7930 /// End all styles
7931 bool wxRichTextBuffer::EndAllStyles()
7932 {
7933 while (m_attributeStack.GetCount() != 0)
7934 EndStyle();
7935 return true;
7936 }
7937
7938 /// Clear the style stack
7939 void wxRichTextBuffer::ClearStyleStack()
7940 {
7941 for (wxList::compatibility_iterator node = m_attributeStack.GetFirst(); node; node = node->GetNext())
7942 delete (wxRichTextAttr*) node->GetData();
7943 m_attributeStack.Clear();
7944 }
7945
7946 /// Begin using bold
7947 bool wxRichTextBuffer::BeginBold()
7948 {
7949 wxRichTextAttr attr;
7950 attr.SetFontWeight(wxFONTWEIGHT_BOLD);
7951
7952 return BeginStyle(attr);
7953 }
7954
7955 /// Begin using italic
7956 bool wxRichTextBuffer::BeginItalic()
7957 {
7958 wxRichTextAttr attr;
7959 attr.SetFontStyle(wxFONTSTYLE_ITALIC);
7960
7961 return BeginStyle(attr);
7962 }
7963
7964 /// Begin using underline
7965 bool wxRichTextBuffer::BeginUnderline()
7966 {
7967 wxRichTextAttr attr;
7968 attr.SetFontUnderlined(true);
7969
7970 return BeginStyle(attr);
7971 }
7972
7973 /// Begin using point size
7974 bool wxRichTextBuffer::BeginFontSize(int pointSize)
7975 {
7976 wxRichTextAttr attr;
7977 attr.SetFontSize(pointSize);
7978
7979 return BeginStyle(attr);
7980 }
7981
7982 /// Begin using this font
7983 bool wxRichTextBuffer::BeginFont(const wxFont& font)
7984 {
7985 wxRichTextAttr attr;
7986 attr.SetFont(font);
7987
7988 return BeginStyle(attr);
7989 }
7990
7991 /// Begin using this colour
7992 bool wxRichTextBuffer::BeginTextColour(const wxColour& colour)
7993 {
7994 wxRichTextAttr attr;
7995 attr.SetFlags(wxTEXT_ATTR_TEXT_COLOUR);
7996 attr.SetTextColour(colour);
7997
7998 return BeginStyle(attr);
7999 }
8000
8001 /// Begin using alignment
8002 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment)
8003 {
8004 wxRichTextAttr attr;
8005 attr.SetFlags(wxTEXT_ATTR_ALIGNMENT);
8006 attr.SetAlignment(alignment);
8007
8008 return BeginStyle(attr);
8009 }
8010
8011 /// Begin left indent
8012 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent, int leftSubIndent)
8013 {
8014 wxRichTextAttr attr;
8015 attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
8016 attr.SetLeftIndent(leftIndent, leftSubIndent);
8017
8018 return BeginStyle(attr);
8019 }
8020
8021 /// Begin right indent
8022 bool wxRichTextBuffer::BeginRightIndent(int rightIndent)
8023 {
8024 wxRichTextAttr attr;
8025 attr.SetFlags(wxTEXT_ATTR_RIGHT_INDENT);
8026 attr.SetRightIndent(rightIndent);
8027
8028 return BeginStyle(attr);
8029 }
8030
8031 /// Begin paragraph spacing
8032 bool wxRichTextBuffer::BeginParagraphSpacing(int before, int after)
8033 {
8034 long flags = 0;
8035 if (before != 0)
8036 flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
8037 if (after != 0)
8038 flags |= wxTEXT_ATTR_PARA_SPACING_AFTER;
8039
8040 wxRichTextAttr attr;
8041 attr.SetFlags(flags);
8042 attr.SetParagraphSpacingBefore(before);
8043 attr.SetParagraphSpacingAfter(after);
8044
8045 return BeginStyle(attr);
8046 }
8047
8048 /// Begin line spacing
8049 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing)
8050 {
8051 wxRichTextAttr attr;
8052 attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
8053 attr.SetLineSpacing(lineSpacing);
8054
8055 return BeginStyle(attr);
8056 }
8057
8058 /// Begin numbered bullet
8059 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber, int leftIndent, int leftSubIndent, int bulletStyle)
8060 {
8061 wxRichTextAttr attr;
8062 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
8063 attr.SetBulletStyle(bulletStyle);
8064 attr.SetBulletNumber(bulletNumber);
8065 attr.SetLeftIndent(leftIndent, leftSubIndent);
8066
8067 return BeginStyle(attr);
8068 }
8069
8070 /// Begin symbol bullet
8071 bool wxRichTextBuffer::BeginSymbolBullet(const wxString& symbol, int leftIndent, int leftSubIndent, int bulletStyle)
8072 {
8073 wxRichTextAttr attr;
8074 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
8075 attr.SetBulletStyle(bulletStyle);
8076 attr.SetLeftIndent(leftIndent, leftSubIndent);
8077 attr.SetBulletText(symbol);
8078
8079 return BeginStyle(attr);
8080 }
8081
8082 /// Begin standard bullet
8083 bool wxRichTextBuffer::BeginStandardBullet(const wxString& bulletName, int leftIndent, int leftSubIndent, int bulletStyle)
8084 {
8085 wxRichTextAttr attr;
8086 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
8087 attr.SetBulletStyle(bulletStyle);
8088 attr.SetLeftIndent(leftIndent, leftSubIndent);
8089 attr.SetBulletName(bulletName);
8090
8091 return BeginStyle(attr);
8092 }
8093
8094 /// Begin named character style
8095 bool wxRichTextBuffer::BeginCharacterStyle(const wxString& characterStyle)
8096 {
8097 if (GetStyleSheet())
8098 {
8099 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
8100 if (def)
8101 {
8102 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
8103 return BeginStyle(attr);
8104 }
8105 }
8106 return false;
8107 }
8108
8109 /// Begin named paragraph style
8110 bool wxRichTextBuffer::BeginParagraphStyle(const wxString& paragraphStyle)
8111 {
8112 if (GetStyleSheet())
8113 {
8114 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(paragraphStyle);
8115 if (def)
8116 {
8117 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
8118 return BeginStyle(attr);
8119 }
8120 }
8121 return false;
8122 }
8123
8124 /// Begin named list style
8125 bool wxRichTextBuffer::BeginListStyle(const wxString& listStyle, int level, int number)
8126 {
8127 if (GetStyleSheet())
8128 {
8129 wxRichTextListStyleDefinition* def = GetStyleSheet()->FindListStyle(listStyle);
8130 if (def)
8131 {
8132 wxRichTextAttr attr(def->GetCombinedStyleForLevel(level));
8133
8134 attr.SetBulletNumber(number);
8135
8136 return BeginStyle(attr);
8137 }
8138 }
8139 return false;
8140 }
8141
8142 /// Begin URL
8143 bool wxRichTextBuffer::BeginURL(const wxString& url, const wxString& characterStyle)
8144 {
8145 wxRichTextAttr attr;
8146
8147 if (!characterStyle.IsEmpty() && GetStyleSheet())
8148 {
8149 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
8150 if (def)
8151 {
8152 attr = def->GetStyleMergedWithBase(GetStyleSheet());
8153 }
8154 }
8155 attr.SetURL(url);
8156
8157 return BeginStyle(attr);
8158 }
8159
8160 /// Adds a handler to the end
8161 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler *handler)
8162 {
8163 sm_handlers.Append(handler);
8164 }
8165
8166 /// Inserts a handler at the front
8167 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler *handler)
8168 {
8169 sm_handlers.Insert( handler );
8170 }
8171
8172 /// Removes a handler
8173 bool wxRichTextBuffer::RemoveHandler(const wxString& name)
8174 {
8175 wxRichTextFileHandler *handler = FindHandler(name);
8176 if (handler)
8177 {
8178 sm_handlers.DeleteObject(handler);
8179 delete handler;
8180 return true;
8181 }
8182 else
8183 return false;
8184 }
8185
8186 /// Finds a handler by filename or, if supplied, type
8187 wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename,
8188 wxRichTextFileType imageType)
8189 {
8190 if (imageType != wxRICHTEXT_TYPE_ANY)
8191 return FindHandler(imageType);
8192 else if (!filename.IsEmpty())
8193 {
8194 wxString path, file, ext;
8195 wxFileName::SplitPath(filename, & path, & file, & ext);
8196 return FindHandler(ext, imageType);
8197 }
8198 else
8199 return NULL;
8200 }
8201
8202
8203 /// Finds a handler by name
8204 wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& name)
8205 {
8206 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8207 while (node)
8208 {
8209 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
8210 if (handler->GetName().Lower() == name.Lower()) return handler;
8211
8212 node = node->GetNext();
8213 }
8214 return NULL;
8215 }
8216
8217 /// Finds a handler by extension and type
8218 wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, wxRichTextFileType type)
8219 {
8220 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8221 while (node)
8222 {
8223 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
8224 if ( handler->GetExtension().Lower() == extension.Lower() &&
8225 (type == wxRICHTEXT_TYPE_ANY || handler->GetType() == type) )
8226 return handler;
8227 node = node->GetNext();
8228 }
8229 return 0;
8230 }
8231
8232 /// Finds a handler by type
8233 wxRichTextFileHandler* wxRichTextBuffer::FindHandler(wxRichTextFileType type)
8234 {
8235 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8236 while (node)
8237 {
8238 wxRichTextFileHandler *handler = (wxRichTextFileHandler *)node->GetData();
8239 if (handler->GetType() == type) return handler;
8240 node = node->GetNext();
8241 }
8242 return NULL;
8243 }
8244
8245 void wxRichTextBuffer::InitStandardHandlers()
8246 {
8247 if (!FindHandler(wxRICHTEXT_TYPE_TEXT))
8248 AddHandler(new wxRichTextPlainTextHandler);
8249 }
8250
8251 void wxRichTextBuffer::CleanUpHandlers()
8252 {
8253 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8254 while (node)
8255 {
8256 wxRichTextFileHandler* handler = (wxRichTextFileHandler*)node->GetData();
8257 wxList::compatibility_iterator next = node->GetNext();
8258 delete handler;
8259 node = next;
8260 }
8261
8262 sm_handlers.Clear();
8263 }
8264
8265 wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* types)
8266 {
8267 if (types)
8268 types->Clear();
8269
8270 wxString wildcard;
8271
8272 wxList::compatibility_iterator node = GetHandlers().GetFirst();
8273 int count = 0;
8274 while (node)
8275 {
8276 wxRichTextFileHandler* handler = (wxRichTextFileHandler*) node->GetData();
8277 if (handler->IsVisible() && ((save && handler->CanSave()) || (!save && handler->CanLoad())))
8278 {
8279 if (combine)
8280 {
8281 if (count > 0)
8282 wildcard += wxT(";");
8283 wildcard += wxT("*.") + handler->GetExtension();
8284 }
8285 else
8286 {
8287 if (count > 0)
8288 wildcard += wxT("|");
8289 wildcard += handler->GetName();
8290 wildcard += wxT(" ");
8291 wildcard += _("files");
8292 wildcard += wxT(" (*.");
8293 wildcard += handler->GetExtension();
8294 wildcard += wxT(")|*.");
8295 wildcard += handler->GetExtension();
8296 if (types)
8297 types->Add(handler->GetType());
8298 }
8299 count ++;
8300 }
8301
8302 node = node->GetNext();
8303 }
8304
8305 if (combine)
8306 wildcard = wxT("(") + wildcard + wxT(")|") + wildcard;
8307 return wildcard;
8308 }
8309
8310 /// Load a file
8311 bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type)
8312 {
8313 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
8314 if (handler)
8315 {
8316 SetDefaultStyle(wxRichTextAttr());
8317 handler->SetFlags(GetHandlerFlags());
8318 bool success = handler->LoadFile(this, filename);
8319 Invalidate(wxRICHTEXT_ALL);
8320 return success;
8321 }
8322 else
8323 return false;
8324 }
8325
8326 /// Save a file
8327 bool wxRichTextBuffer::SaveFile(const wxString& filename, wxRichTextFileType type)
8328 {
8329 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
8330 if (handler)
8331 {
8332 handler->SetFlags(GetHandlerFlags());
8333 return handler->SaveFile(this, filename);
8334 }
8335 else
8336 return false;
8337 }
8338
8339 /// Load from a stream
8340 bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type)
8341 {
8342 wxRichTextFileHandler* handler = FindHandler(type);
8343 if (handler)
8344 {
8345 SetDefaultStyle(wxRichTextAttr());
8346 handler->SetFlags(GetHandlerFlags());
8347 bool success = handler->LoadFile(this, stream);
8348 Invalidate(wxRICHTEXT_ALL);
8349 return success;
8350 }
8351 else
8352 return false;
8353 }
8354
8355 /// Save to a stream
8356 bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, wxRichTextFileType type)
8357 {
8358 wxRichTextFileHandler* handler = FindHandler(type);
8359 if (handler)
8360 {
8361 handler->SetFlags(GetHandlerFlags());
8362 return handler->SaveFile(this, stream);
8363 }
8364 else
8365 return false;
8366 }
8367
8368 /// Copy the range to the clipboard
8369 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
8370 {
8371 bool success = false;
8372 wxRichTextParagraphLayoutBox* container = this;
8373 if (GetRichTextCtrl())
8374 container = GetRichTextCtrl()->GetFocusObject();
8375
8376 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8377
8378 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
8379 {
8380 wxTheClipboard->Clear();
8381
8382 // Add composite object
8383
8384 wxDataObjectComposite* compositeObject = new wxDataObjectComposite();
8385
8386 {
8387 wxString text = container->GetTextForRange(range);
8388
8389 #ifdef __WXMSW__
8390 text = wxTextFile::Translate(text, wxTextFileType_Dos);
8391 #endif
8392
8393 compositeObject->Add(new wxTextDataObject(text), false /* not preferred */);
8394 }
8395
8396 // Add rich text buffer data object. This needs the XML handler to be present.
8397
8398 if (FindHandler(wxRICHTEXT_TYPE_XML))
8399 {
8400 wxRichTextBuffer* richTextBuf = new wxRichTextBuffer;
8401 container->CopyFragment(range, *richTextBuf);
8402
8403 compositeObject->Add(new wxRichTextBufferDataObject(richTextBuf), true /* preferred */);
8404 }
8405
8406 if (wxTheClipboard->SetData(compositeObject))
8407 success = true;
8408
8409 wxTheClipboard->Close();
8410 }
8411
8412 #else
8413 wxUnusedVar(range);
8414 #endif
8415 return success;
8416 }
8417
8418 /// Paste the clipboard content to the buffer
8419 bool wxRichTextBuffer::PasteFromClipboard(long position)
8420 {
8421 bool success = false;
8422 wxRichTextParagraphLayoutBox* container = this;
8423 if (GetRichTextCtrl())
8424 container = GetRichTextCtrl()->GetFocusObject();
8425
8426 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8427 if (CanPasteFromClipboard())
8428 {
8429 if (wxTheClipboard->Open())
8430 {
8431 if (wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
8432 {
8433 wxRichTextBufferDataObject data;
8434 wxTheClipboard->GetData(data);
8435 wxRichTextBuffer* richTextBuffer = data.GetRichTextBuffer();
8436 if (richTextBuffer)
8437 {
8438 container->InsertParagraphsWithUndo(this, position+1, *richTextBuffer, GetRichTextCtrl(), 0);
8439 if (GetRichTextCtrl())
8440 GetRichTextCtrl()->ShowPosition(position + richTextBuffer->GetOwnRange().GetEnd());
8441 delete richTextBuffer;
8442 }
8443 }
8444 else if (wxTheClipboard->IsSupported(wxDF_TEXT)
8445 #if wxUSE_UNICODE
8446 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8447 #endif
8448 )
8449 {
8450 wxTextDataObject data;
8451 wxTheClipboard->GetData(data);
8452 wxString text(data.GetText());
8453 #ifdef __WXMSW__
8454 wxString text2;
8455 text2.Alloc(text.Length()+1);
8456 size_t i;
8457 for (i = 0; i < text.Length(); i++)
8458 {
8459 wxChar ch = text[i];
8460 if (ch != wxT('\r'))
8461 text2 += ch;
8462 }
8463 #else
8464 wxString text2 = text;
8465 #endif
8466 container->InsertTextWithUndo(this, position+1, text2, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
8467
8468 if (GetRichTextCtrl())
8469 GetRichTextCtrl()->ShowPosition(position + text2.Length());
8470
8471 success = true;
8472 }
8473 else if (wxTheClipboard->IsSupported(wxDF_BITMAP))
8474 {
8475 wxBitmapDataObject data;
8476 wxTheClipboard->GetData(data);
8477 wxBitmap bitmap(data.GetBitmap());
8478 wxImage image(bitmap.ConvertToImage());
8479
8480 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, container, GetRichTextCtrl(), false);
8481
8482 action->GetNewParagraphs().AddImage(image);
8483
8484 if (action->GetNewParagraphs().GetChildCount() == 1)
8485 action->GetNewParagraphs().SetPartialParagraph(true);
8486
8487 action->SetPosition(position+1);
8488
8489 // Set the range we'll need to delete in Undo
8490 action->SetRange(wxRichTextRange(position+1, position+1));
8491
8492 SubmitAction(action);
8493
8494 success = true;
8495 }
8496 wxTheClipboard->Close();
8497 }
8498 }
8499 #else
8500 wxUnusedVar(position);
8501 #endif
8502 return success;
8503 }
8504
8505 /// Can we paste from the clipboard?
8506 bool wxRichTextBuffer::CanPasteFromClipboard() const
8507 {
8508 bool canPaste = false;
8509 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8510 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
8511 {
8512 if (wxTheClipboard->IsSupported(wxDF_TEXT)
8513 #if wxUSE_UNICODE
8514 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8515 #endif
8516 || wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8517 wxTheClipboard->IsSupported(wxDF_BITMAP))
8518 {
8519 canPaste = true;
8520 }
8521 wxTheClipboard->Close();
8522 }
8523 #endif
8524 return canPaste;
8525 }
8526
8527 /// Dumps contents of buffer for debugging purposes
8528 void wxRichTextBuffer::Dump()
8529 {
8530 wxString text;
8531 {
8532 wxStringOutputStream stream(& text);
8533 wxTextOutputStream textStream(stream);
8534 Dump(textStream);
8535 }
8536
8537 wxLogDebug(text);
8538 }
8539
8540 /// Add an event handler
8541 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler* handler)
8542 {
8543 m_eventHandlers.Append(handler);
8544 return true;
8545 }
8546
8547 /// Remove an event handler
8548 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler* handler, bool deleteHandler)
8549 {
8550 wxList::compatibility_iterator node = m_eventHandlers.Find(handler);
8551 if (node)
8552 {
8553 m_eventHandlers.Erase(node);
8554 if (deleteHandler)
8555 delete handler;
8556
8557 return true;
8558 }
8559 else
8560 return false;
8561 }
8562
8563 /// Clear event handlers
8564 void wxRichTextBuffer::ClearEventHandlers()
8565 {
8566 m_eventHandlers.Clear();
8567 }
8568
8569 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8570 /// otherwise will stop at the first successful one.
8571 bool wxRichTextBuffer::SendEvent(wxEvent& event, bool sendToAll)
8572 {
8573 bool success = false;
8574 for (wxList::compatibility_iterator node = m_eventHandlers.GetFirst(); node; node = node->GetNext())
8575 {
8576 wxEvtHandler* handler = (wxEvtHandler*) node->GetData();
8577 if (handler->ProcessEvent(event))
8578 {
8579 success = true;
8580 if (!sendToAll)
8581 return true;
8582 }
8583 }
8584 return success;
8585 }
8586
8587 /// Set style sheet and notify of the change
8588 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet* sheet)
8589 {
8590 wxRichTextStyleSheet* oldSheet = GetStyleSheet();
8591
8592 wxWindowID winid = wxID_ANY;
8593 if (GetRichTextCtrl())
8594 winid = GetRichTextCtrl()->GetId();
8595
8596 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING, winid);
8597 event.SetEventObject(GetRichTextCtrl());
8598 event.SetContainer(GetRichTextCtrl()->GetFocusObject());
8599 event.SetOldStyleSheet(oldSheet);
8600 event.SetNewStyleSheet(sheet);
8601 event.Allow();
8602
8603 if (SendEvent(event) && !event.IsAllowed())
8604 {
8605 if (sheet != oldSheet)
8606 delete sheet;
8607
8608 return false;
8609 }
8610
8611 if (oldSheet && oldSheet != sheet)
8612 delete oldSheet;
8613
8614 SetStyleSheet(sheet);
8615
8616 event.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED);
8617 event.SetOldStyleSheet(NULL);
8618 event.Allow();
8619
8620 return SendEvent(event);
8621 }
8622
8623 /// Set renderer, deleting old one
8624 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer)
8625 {
8626 if (sm_renderer)
8627 delete sm_renderer;
8628 sm_renderer = renderer;
8629 }
8630
8631 /// Hit-testing: returns a flag indicating hit test details, plus
8632 /// information about position
8633 int wxRichTextBuffer::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
8634 {
8635 int ret = wxRichTextParagraphLayoutBox::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
8636 if (ret != wxRICHTEXT_HITTEST_NONE)
8637 {
8638 return ret;
8639 }
8640 else
8641 {
8642 textPosition = m_ownRange.GetEnd()-1;
8643 *obj = this;
8644 *contextObj = this;
8645 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
8646 }
8647 }
8648
8649 void wxRichTextBuffer::SetFontScale(double fontScale)
8650 {
8651 m_fontScale = fontScale;
8652 m_fontTable.SetFontScale(fontScale);
8653 }
8654
8655 void wxRichTextBuffer::SetDimensionScale(double dimScale)
8656 {
8657 m_dimensionScale = dimScale;
8658 }
8659
8660 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& bulletAttr, const wxRect& rect)
8661 {
8662 if (bulletAttr.GetTextColour().IsOk())
8663 {
8664 wxCheckSetPen(dc, wxPen(bulletAttr.GetTextColour()));
8665 wxCheckSetBrush(dc, wxBrush(bulletAttr.GetTextColour()));
8666 }
8667 else
8668 {
8669 wxCheckSetPen(dc, *wxBLACK_PEN);
8670 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
8671 }
8672
8673 wxFont font;
8674 if (bulletAttr.HasFont())
8675 {
8676 font = paragraph->GetBuffer()->GetFontTable().FindFont(bulletAttr);
8677 }
8678 else
8679 font = (*wxNORMAL_FONT);
8680
8681 wxCheckSetFont(dc, font);
8682
8683 int charHeight = dc.GetCharHeight();
8684
8685 int bulletWidth = (int) (((float) charHeight) * wxRichTextBuffer::GetBulletProportion());
8686 int bulletHeight = bulletWidth;
8687
8688 int x = rect.x;
8689
8690 // Calculate the top position of the character (as opposed to the whole line height)
8691 int y = rect.y + (rect.height - charHeight);
8692
8693 // Calculate where the bullet should be positioned
8694 y = y + (charHeight+1)/2 - (bulletHeight+1)/2;
8695
8696 // The margin between a bullet and text.
8697 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
8698
8699 if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8700 x = rect.x + rect.width - bulletWidth - margin;
8701 else if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8702 x = x + (rect.width)/2 - bulletWidth/2;
8703
8704 if (bulletAttr.GetBulletName() == wxT("standard/square"))
8705 {
8706 dc.DrawRectangle(x, y, bulletWidth, bulletHeight);
8707 }
8708 else if (bulletAttr.GetBulletName() == wxT("standard/diamond"))
8709 {
8710 wxPoint pts[5];
8711 pts[0].x = x; pts[0].y = y + bulletHeight/2;
8712 pts[1].x = x + bulletWidth/2; pts[1].y = y;
8713 pts[2].x = x + bulletWidth; pts[2].y = y + bulletHeight/2;
8714 pts[3].x = x + bulletWidth/2; pts[3].y = y + bulletHeight;
8715
8716 dc.DrawPolygon(4, pts);
8717 }
8718 else if (bulletAttr.GetBulletName() == wxT("standard/triangle"))
8719 {
8720 wxPoint pts[3];
8721 pts[0].x = x; pts[0].y = y;
8722 pts[1].x = x + bulletWidth; pts[1].y = y + bulletHeight/2;
8723 pts[2].x = x; pts[2].y = y + bulletHeight;
8724
8725 dc.DrawPolygon(3, pts);
8726 }
8727 else if (bulletAttr.GetBulletName() == wxT("standard/circle-outline"))
8728 {
8729 wxCheckSetBrush(dc, *wxTRANSPARENT_BRUSH);
8730 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
8731 }
8732 else // "standard/circle", and catch-all
8733 {
8734 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
8735 }
8736
8737 return true;
8738 }
8739
8740 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect, const wxString& text)
8741 {
8742 if (!text.empty())
8743 {
8744 wxFont font;
8745 if ((attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL) && !attr.GetBulletFont().IsEmpty() && attr.HasFont())
8746 {
8747 wxRichTextAttr fontAttr;
8748 if (attr.HasFontPixelSize())
8749 fontAttr.SetFontPixelSize(attr.GetFontSize());
8750 else
8751 fontAttr.SetFontPointSize(attr.GetFontSize());
8752 fontAttr.SetFontStyle(attr.GetFontStyle());
8753 fontAttr.SetFontWeight(attr.GetFontWeight());
8754 fontAttr.SetFontUnderlined(attr.GetFontUnderlined());
8755 fontAttr.SetFontFaceName(attr.GetBulletFont());
8756 font = paragraph->GetBuffer()->GetFontTable().FindFont(fontAttr);
8757 }
8758 else if (attr.HasFont())
8759 font = paragraph->GetBuffer()->GetFontTable().FindFont(attr);
8760 else
8761 font = (*wxNORMAL_FONT);
8762
8763 wxCheckSetFont(dc, font);
8764
8765 if (attr.GetTextColour().IsOk())
8766 dc.SetTextForeground(attr.GetTextColour());
8767
8768 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
8769
8770 int charHeight = dc.GetCharHeight();
8771 wxCoord tw, th;
8772 dc.GetTextExtent(text, & tw, & th);
8773
8774 int x = rect.x;
8775
8776 // Calculate the top position of the character (as opposed to the whole line height)
8777 int y = rect.y + (rect.height - charHeight);
8778
8779 // The margin between a bullet and text.
8780 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
8781
8782 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8783 x = (rect.x + rect.width) - tw - margin;
8784 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8785 x = x + (rect.width)/2 - tw/2;
8786
8787 dc.DrawText(text, x, y);
8788
8789 return true;
8790 }
8791 else
8792 return false;
8793 }
8794
8795 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& WXUNUSED(dc), const wxRichTextAttr& WXUNUSED(attr), const wxRect& WXUNUSED(rect))
8796 {
8797 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8798 // with the buffer. The store will allow retrieval from memory, disk or other means.
8799 return false;
8800 }
8801
8802 /// Enumerate the standard bullet names currently supported
8803 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString& bulletNames)
8804 {
8805 bulletNames.Add(wxTRANSLATE("standard/circle"));
8806 bulletNames.Add(wxTRANSLATE("standard/circle-outline"));
8807 bulletNames.Add(wxTRANSLATE("standard/square"));
8808 bulletNames.Add(wxTRANSLATE("standard/diamond"));
8809 bulletNames.Add(wxTRANSLATE("standard/triangle"));
8810
8811 return true;
8812 }
8813
8814 /*!
8815 * wxRichTextBox
8816 */
8817
8818 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextParagraphLayoutBox)
8819
8820 wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
8821 wxRichTextParagraphLayoutBox(parent)
8822 {
8823 }
8824
8825 /// Draw the item
8826 bool wxRichTextBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8827 {
8828 if (!IsShown())
8829 return true;
8830
8831 // TODO: if the active object in the control, draw an indication.
8832 // We need to add the concept of active object, and not just focus object,
8833 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8834 // Ultimately we would like to be able to interactively resize an active object
8835 // using drag handles.
8836 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
8837 }
8838
8839 /// Copy
8840 void wxRichTextBox::Copy(const wxRichTextBox& obj)
8841 {
8842 wxRichTextParagraphLayoutBox::Copy(obj);
8843 }
8844
8845 // Edit properties via a GUI
8846 bool wxRichTextBox::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8847 {
8848 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Box Properties"));
8849 boxDlg.SetAttributes(GetAttributes());
8850
8851 if (boxDlg.ShowModal() == wxID_OK)
8852 {
8853 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8854 // indeterminate in the object.
8855 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8856 return true;
8857 }
8858 else
8859 return false;
8860 }
8861
8862 /*!
8863 * wxRichTextField
8864 */
8865
8866 IMPLEMENT_DYNAMIC_CLASS(wxRichTextField, wxRichTextParagraphLayoutBox)
8867
8868 wxRichTextField::wxRichTextField(const wxString& fieldType, wxRichTextObject* parent):
8869 wxRichTextParagraphLayoutBox(parent)
8870 {
8871 SetFieldType(fieldType);
8872 }
8873
8874 /// Draw the item
8875 bool wxRichTextField::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8876 {
8877 if (!IsShown())
8878 return true;
8879
8880 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8881 if (fieldType && fieldType->Draw(this, dc, context, range, selection, rect, descent, style))
8882 return true;
8883
8884 // Fallback; but don't draw guidelines.
8885 style &= ~wxRICHTEXT_DRAW_GUIDELINES;
8886 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
8887 }
8888
8889 bool wxRichTextField::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
8890 {
8891 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8892 if (fieldType && fieldType->Layout(this, dc, context, rect, parentRect, style))
8893 return true;
8894
8895 // Fallback
8896 return wxRichTextParagraphLayoutBox::Layout(dc, context, rect, parentRect, style);
8897 }
8898
8899 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
8900 {
8901 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8902 if (fieldType)
8903 return fieldType->GetRangeSize((wxRichTextField*) this, range, size, descent, dc, context, flags, position, parentSize, partialExtents);
8904
8905 return wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize, partialExtents);
8906 }
8907
8908 /// Calculate range
8909 void wxRichTextField::CalculateRange(long start, long& end)
8910 {
8911 if (IsTopLevel())
8912 wxRichTextParagraphLayoutBox::CalculateRange(start, end);
8913 else
8914 wxRichTextObject::CalculateRange(start, end);
8915 }
8916
8917 /// Copy
8918 void wxRichTextField::Copy(const wxRichTextField& obj)
8919 {
8920 wxRichTextParagraphLayoutBox::Copy(obj);
8921
8922 UpdateField(GetBuffer());
8923 }
8924
8925 // Edit properties via a GUI
8926 bool wxRichTextField::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8927 {
8928 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8929 if (fieldType)
8930 return fieldType->EditProperties(this, parent, buffer);
8931
8932 return false;
8933 }
8934
8935 bool wxRichTextField::CanEditProperties() const
8936 {
8937 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8938 if (fieldType)
8939 return fieldType->CanEditProperties((wxRichTextField*) this);
8940
8941 return false;
8942 }
8943
8944 wxString wxRichTextField::GetPropertiesMenuLabel() const
8945 {
8946 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8947 if (fieldType)
8948 return fieldType->GetPropertiesMenuLabel((wxRichTextField*) this);
8949
8950 return wxEmptyString;
8951 }
8952
8953 bool wxRichTextField::UpdateField(wxRichTextBuffer* buffer)
8954 {
8955 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8956 if (fieldType)
8957 return fieldType->UpdateField(buffer, (wxRichTextField*) this);
8958
8959 return false;
8960 }
8961
8962 bool wxRichTextField::IsTopLevel() const
8963 {
8964 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8965 if (fieldType)
8966 return fieldType->IsTopLevel((wxRichTextField*) this);
8967
8968 return true;
8969 }
8970
8971 IMPLEMENT_CLASS(wxRichTextFieldType, wxObject)
8972
8973 IMPLEMENT_CLASS(wxRichTextFieldTypeStandard, wxRichTextFieldType)
8974
8975 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxString& label, int displayStyle)
8976 {
8977 Init();
8978
8979 SetName(name);
8980 SetLabel(label);
8981 SetDisplayStyle(displayStyle);
8982 }
8983
8984 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxBitmap& bitmap, int displayStyle)
8985 {
8986 Init();
8987
8988 SetName(name);
8989 SetBitmap(bitmap);
8990 SetDisplayStyle(displayStyle);
8991 }
8992
8993 void wxRichTextFieldTypeStandard::Init()
8994 {
8995 m_displayStyle = wxRICHTEXT_FIELD_STYLE_RECTANGLE;
8996 m_font = wxFont(6, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
8997 m_textColour = *wxWHITE;
8998 m_borderColour = *wxBLACK;
8999 m_backgroundColour = *wxBLACK;
9000 m_verticalPadding = 1;
9001 m_horizontalPadding = 3;
9002 m_horizontalMargin = 2;
9003 m_verticalMargin = 0;
9004 }
9005
9006 void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard& field)
9007 {
9008 wxRichTextFieldType::Copy(field);
9009
9010 m_label = field.m_label;
9011 m_displayStyle = field.m_displayStyle;
9012 m_font = field.m_font;
9013 m_textColour = field.m_textColour;
9014 m_borderColour = field.m_borderColour;
9015 m_backgroundColour = field.m_backgroundColour;
9016 m_verticalPadding = field.m_verticalPadding;
9017 m_horizontalPadding = field.m_horizontalPadding;
9018 m_horizontalMargin = field.m_horizontalMargin;
9019 m_bitmap = field.m_bitmap;
9020 }
9021
9022 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))
9023 {
9024 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
9025 return false; // USe default composite drawing
9026 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
9027 {
9028 int borderSize = 1;
9029
9030 wxPen borderPen(m_borderColour, 1, wxSOLID);
9031 wxBrush backgroundBrush(m_backgroundColour);
9032 wxColour textColour(m_textColour);
9033
9034 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
9035 {
9036 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
9037 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
9038
9039 borderPen = wxPen(highlightTextColour, 1, wxSOLID);
9040 backgroundBrush = wxBrush(highlightColour);
9041
9042 wxCheckSetBrush(dc, backgroundBrush);
9043 wxCheckSetPen(dc, wxPen(highlightColour, 1, wxSOLID));
9044 dc.DrawRectangle(rect);
9045 }
9046
9047 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
9048 borderSize = 0;
9049
9050 // objectRect is the area where the content is drawn, after margins around it have been taken into account
9051 wxRect objectRect = wxRect(wxPoint(rect.x + m_horizontalMargin, rect.y + wxMax(0, rect.height - descent - obj->GetCachedSize().y)),
9052 wxSize(obj->GetCachedSize().x - 2*m_horizontalMargin - borderSize, obj->GetCachedSize().y));
9053
9054 // clientArea is where the text is actually written
9055 wxRect clientArea = objectRect;
9056
9057 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE)
9058 {
9059 dc.SetPen(borderPen);
9060 dc.SetBrush(backgroundBrush);
9061 dc.DrawRoundedRectangle(objectRect, 4.0);
9062 }
9063 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG)
9064 {
9065 int arrowLength = objectRect.height/2;
9066 clientArea.width -= (arrowLength - m_horizontalPadding);
9067
9068 wxPoint pts[5];
9069 pts[0].x = objectRect.x; pts[0].y = objectRect.y;
9070 pts[1].x = objectRect.x + objectRect.width - arrowLength; pts[1].y = objectRect.y;
9071 pts[2].x = objectRect.x + objectRect.width; pts[2].y = objectRect.y + (objectRect.height/2);
9072 pts[3].x = objectRect.x + objectRect.width - arrowLength; pts[3].y = objectRect.y + objectRect.height;
9073 pts[4].x = objectRect.x; pts[4].y = objectRect.y + objectRect.height;
9074 dc.SetPen(borderPen);
9075 dc.SetBrush(backgroundBrush);
9076 dc.DrawPolygon(5, pts);
9077 }
9078 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
9079 {
9080 int arrowLength = objectRect.height/2;
9081 clientArea.width -= (arrowLength - m_horizontalPadding);
9082 clientArea.x += (arrowLength - m_horizontalPadding);
9083
9084 wxPoint pts[5];
9085 pts[0].x = objectRect.x + objectRect.width; pts[0].y = objectRect.y;
9086 pts[1].x = objectRect.x + arrowLength; pts[1].y = objectRect.y;
9087 pts[2].x = objectRect.x; pts[2].y = objectRect.y + (objectRect.height/2);
9088 pts[3].x = objectRect.x + arrowLength; pts[3].y = objectRect.y + objectRect.height;
9089 pts[4].x = objectRect.x + objectRect.width; pts[4].y = objectRect.y + objectRect.height;
9090 dc.SetPen(borderPen);
9091 dc.SetBrush(backgroundBrush);
9092 dc.DrawPolygon(5, pts);
9093 }
9094
9095 if (m_bitmap.IsOk())
9096 {
9097 int x = clientArea.x + (clientArea.width - m_bitmap.GetWidth())/2;
9098 int y = clientArea.y + m_verticalPadding;
9099 dc.DrawBitmap(m_bitmap, x, y, true);
9100
9101 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
9102 {
9103 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
9104 wxCheckSetPen(dc, *wxBLACK_PEN);
9105 dc.SetLogicalFunction(wxINVERT);
9106 dc.DrawRectangle(wxRect(x, y, m_bitmap.GetWidth(), m_bitmap.GetHeight()));
9107 dc.SetLogicalFunction(wxCOPY);
9108 }
9109 }
9110 else
9111 {
9112 wxString label(m_label);
9113 if (label.IsEmpty())
9114 label = wxT("??");
9115 int w, h, maxDescent;
9116 dc.SetFont(m_font);
9117 dc.GetTextExtent(m_label, & w, &h, & maxDescent);
9118 dc.SetTextForeground(textColour);
9119
9120 int x = clientArea.x + (clientArea.width - w)/2;
9121 int y = clientArea.y + (clientArea.height - (h - maxDescent))/2;
9122 dc.DrawText(m_label, x, y);
9123 }
9124 }
9125
9126 return true;
9127 }
9128
9129 bool wxRichTextFieldTypeStandard::Layout(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int style)
9130 {
9131 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
9132 return false; // USe default composite layout
9133
9134 wxSize size = GetSize(obj, dc, context, style);
9135 obj->SetCachedSize(size);
9136 obj->SetMinSize(size);
9137 obj->SetMaxSize(size);
9138 return true;
9139 }
9140
9141 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
9142 {
9143 if (IsTopLevel(obj))
9144 return obj->wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize);
9145 else
9146 {
9147 wxSize sz = GetSize(obj, dc, context, 0);
9148 if (partialExtents)
9149 {
9150 int lastSize;
9151 if (partialExtents->GetCount() > 0)
9152 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
9153 else
9154 lastSize = 0;
9155 partialExtents->Add(lastSize + sz.x);
9156 }
9157 size = sz;
9158 return true;
9159 }
9160 }
9161
9162 wxSize wxRichTextFieldTypeStandard::GetSize(wxRichTextField* WXUNUSED(obj), wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), int WXUNUSED(style)) const
9163 {
9164 int borderSize = 1;
9165 int w = 0, h = 0, maxDescent = 0;
9166
9167 wxSize sz;
9168 if (m_bitmap.IsOk())
9169 {
9170 w = m_bitmap.GetWidth();
9171 h = m_bitmap.GetHeight();
9172
9173 sz = wxSize(w + m_horizontalMargin*2, h + m_verticalMargin*2);
9174 }
9175 else
9176 {
9177 wxString label(m_label);
9178 if (label.IsEmpty())
9179 label = wxT("??");
9180 dc.SetFont(m_font);
9181 dc.GetTextExtent(label, & w, &h, & maxDescent);
9182
9183 sz = wxSize(w + m_horizontalPadding*2 + m_horizontalMargin*2, h + m_verticalPadding *2 + m_verticalMargin*2);
9184 }
9185
9186 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
9187 {
9188 sz.x += borderSize*2;
9189 sz.y += borderSize*2;
9190 }
9191
9192 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG || m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
9193 {
9194 // Add space for the arrow
9195 sz.x += (sz.y/2 - m_horizontalPadding);
9196 }
9197
9198 return sz;
9199 }
9200
9201 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell, wxRichTextBox)
9202
9203 wxRichTextCell::wxRichTextCell(wxRichTextObject* parent):
9204 wxRichTextBox(parent)
9205 {
9206 }
9207
9208 /// Draw the item
9209 bool wxRichTextCell::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
9210 {
9211 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
9212 }
9213
9214 /// Copy
9215 void wxRichTextCell::Copy(const wxRichTextCell& obj)
9216 {
9217 wxRichTextBox::Copy(obj);
9218 }
9219
9220 // Edit properties via a GUI
9221 bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
9222 {
9223 // We need to gather common attributes for all selected cells.
9224
9225 wxRichTextTable* table = wxDynamicCast(GetParent(), wxRichTextTable);
9226 bool multipleCells = false;
9227 wxRichTextAttr attr;
9228
9229 if (table && buffer && buffer->GetRichTextCtrl() && buffer->GetRichTextCtrl()->GetSelection().IsValid() &&
9230 buffer->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
9231 {
9232 wxRichTextAttr clashingAttr, absentAttr;
9233 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
9234 size_t i;
9235 int selectedCellCount = 0;
9236 for (i = 0; i < sel.GetCount(); i++)
9237 {
9238 const wxRichTextRange& range = sel[i];
9239 wxRichTextCell* cell = table->GetCell(range.GetStart());
9240 if (cell)
9241 {
9242 wxRichTextAttr cellStyle = cell->GetAttributes();
9243
9244 CollectStyle(attr, cellStyle, clashingAttr, absentAttr);
9245
9246 selectedCellCount ++;
9247 }
9248 }
9249 multipleCells = selectedCellCount > 1;
9250 }
9251 else
9252 {
9253 attr = GetAttributes();
9254 }
9255
9256 wxString caption;
9257 if (multipleCells)
9258 caption = _("Multiple Cell Properties");
9259 else
9260 caption = _("Cell Properties");
9261
9262 wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption);
9263 cellDlg.SetAttributes(attr);
9264
9265 wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(wxCLASSINFO(wxRichTextSizePage)), wxRichTextSizePage);
9266 if (sizePage)
9267 {
9268 // We don't want position and floating controls for a cell.
9269 sizePage->ShowPositionControls(false);
9270 sizePage->ShowFloatingControls(false);
9271 }
9272
9273 if (cellDlg.ShowModal() == wxID_OK)
9274 {
9275 if (multipleCells)
9276 {
9277 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
9278 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
9279 // since it may represent clashing attributes across multiple objects.
9280 table->SetCellStyle(sel, attr);
9281 }
9282 else
9283 // For a single object, indeterminate attributes set by the user should be reflected in the
9284 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
9285 // the style directly instead of applying (which ignores indeterminate attributes,
9286 // leaving them as they were).
9287 cellDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
9288 return true;
9289 }
9290 else
9291 return false;
9292 }
9293
9294 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray)
9295
9296 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable, wxRichTextBox)
9297
9298 wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent)
9299 {
9300 m_rowCount = 0;
9301 m_colCount = 0;
9302 }
9303
9304 // Draws the object.
9305 bool wxRichTextTable::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
9306 {
9307 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
9308 }
9309
9310 WX_DECLARE_OBJARRAY(wxRect, wxRichTextRectArray);
9311 WX_DEFINE_OBJARRAY(wxRichTextRectArray);
9312
9313 // Lays the object out. rect is the space available for layout. Often it will
9314 // be the specified overall space for this object, if trying to constrain
9315 // layout to a particular size, or it could be the total space available in the
9316 // parent. rect is the overall size, so we must subtract margins and padding.
9317 // to get the actual available space.
9318 bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int style)
9319 {
9320 SetPosition(rect.GetPosition());
9321
9322 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
9323 // minimum size if within alloted size, then divide up remaining size
9324 // between rows/cols.
9325
9326 double scale = 1.0;
9327 wxRichTextBuffer* buffer = GetBuffer();
9328 if (buffer) scale = buffer->GetScale();
9329
9330 wxRect availableSpace = GetAvailableContentArea(dc, context, rect);
9331 wxTextAttrDimensionConverter converter(dc, scale, availableSpace.GetSize());
9332
9333 wxRichTextAttr attr(GetAttributes());
9334 context.ApplyVirtualAttributes(attr, this);
9335
9336 // If we have no fixed table size, and assuming we're not pushed for
9337 // space, then we don't have to try to stretch the table to fit the contents.
9338 bool stretchToFitTableWidth = false;
9339
9340 int tableWidth = rect.width;
9341 if (attr.GetTextBoxAttr().GetWidth().IsValid())
9342 {
9343 tableWidth = converter.GetPixels(attr.GetTextBoxAttr().GetWidth());
9344
9345 // Fixed table width, so we do want to stretch columns out if necessary.
9346 stretchToFitTableWidth = true;
9347
9348 // Shouldn't be able to exceed the size passed to this function
9349 tableWidth = wxMin(rect.width, tableWidth);
9350 }
9351
9352 // Get internal padding
9353 int paddingLeft = 0, paddingTop = 0;
9354 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
9355 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
9356 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
9357 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
9358
9359 // Assume that left and top padding are also used for inter-cell padding.
9360 int paddingX = paddingLeft;
9361 int paddingY = paddingTop;
9362
9363 int totalLeftMargin = 0, totalRightMargin = 0, totalTopMargin = 0, totalBottomMargin = 0;
9364 GetTotalMargin(dc, buffer, attr, totalLeftMargin, totalRightMargin, totalTopMargin, totalBottomMargin);
9365
9366 // Internal table width - the area for content
9367 int internalTableWidth = tableWidth - totalLeftMargin - totalRightMargin;
9368
9369 int rowCount = m_cells.GetCount();
9370 if (m_colCount == 0 || rowCount == 0)
9371 {
9372 wxRect overallRect(rect.x, rect.y, totalLeftMargin + totalRightMargin, totalTopMargin + totalBottomMargin);
9373 SetCachedSize(overallRect.GetSize());
9374
9375 // Zero content size
9376 SetMinSize(overallRect.GetSize());
9377 SetMaxSize(GetMinSize());
9378 return true;
9379 }
9380
9381 // The final calculated widths
9382 wxArrayInt colWidths;
9383 colWidths.Add(0, m_colCount);
9384
9385 wxArrayInt absoluteColWidths;
9386 absoluteColWidths.Add(0, m_colCount);
9387
9388 wxArrayInt percentageColWidths;
9389 percentageColWidths.Add(0, m_colCount);
9390 // wxArrayInt percentageColWidthsSpanning(m_colCount);
9391 // These are only relevant when the first column contains spanning information.
9392 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
9393 wxArrayInt maxColWidths;
9394 maxColWidths.Add(0, m_colCount);
9395 wxArrayInt minColWidths;
9396 minColWidths.Add(0, m_colCount);
9397
9398 wxSize tableSize(tableWidth, 0);
9399
9400 int i, j, k;
9401
9402 for (i = 0; i < m_colCount; i++)
9403 {
9404 absoluteColWidths[i] = 0;
9405 // absoluteColWidthsSpanning[i] = 0;
9406 percentageColWidths[i] = -1;
9407 // percentageColWidthsSpanning[i] = -1;
9408 colWidths[i] = 0;
9409 maxColWidths[i] = 0;
9410 minColWidths[i] = 0;
9411 // columnSpans[i] = 1;
9412 }
9413
9414 // (0) Determine which cells are visible according to spans
9415 // 1 2 3 4 5
9416 // __________________
9417 // | | | | | 1
9418 // |------| |----|
9419 // |------| | | 2
9420 // |------| | | 3
9421 // |------------------|
9422 // |__________________| 4
9423
9424 // To calculate cell visibility:
9425 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9426 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9427 // that cell, hide the cell.
9428
9429 // We can also use this array to match the size of spanning cells to the grid. Or just do
9430 // this when we iterate through all cells.
9431
9432 // 0.1: add spanning cells to an array
9433 wxRichTextRectArray rectArray;
9434 for (j = 0; j < m_rowCount; j++)
9435 {
9436 for (i = 0; i < m_colCount; i++)
9437 {
9438 wxRichTextBox* cell = GetCell(j, i);
9439 int colSpan = 1, rowSpan = 1;
9440 if (cell->GetProperties().HasProperty(wxT("colspan")))
9441 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9442 if (cell->GetProperties().HasProperty(wxT("rowspan")))
9443 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
9444 if (colSpan > 1 || rowSpan > 1)
9445 {
9446 rectArray.Add(wxRect(i, j, colSpan, rowSpan));
9447 }
9448 }
9449 }
9450 // 0.2: find which cells are subsumed by a spanning cell
9451 for (j = 0; j < m_rowCount; j++)
9452 {
9453 for (i = 0; i < m_colCount; i++)
9454 {
9455 wxRichTextBox* cell = GetCell(j, i);
9456 if (rectArray.GetCount() == 0)
9457 {
9458 cell->Show(true);
9459 }
9460 else
9461 {
9462 int colSpan = 1, rowSpan = 1;
9463 if (cell->GetProperties().HasProperty(wxT("colspan")))
9464 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9465 if (cell->GetProperties().HasProperty(wxT("rowspan")))
9466 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
9467 if (colSpan > 1 || rowSpan > 1)
9468 {
9469 // Assume all spanning cells are shown
9470 cell->Show(true);
9471 }
9472 else
9473 {
9474 bool shown = true;
9475 for (k = 0; k < (int) rectArray.GetCount(); k++)
9476 {
9477 if (rectArray[k].Contains(wxPoint(i, j)))
9478 {
9479 shown = false;
9480 break;
9481 }
9482 }
9483 cell->Show(shown);
9484 }
9485 }
9486 }
9487 }
9488
9489 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
9490 // overlap with a spanned cell starting at a previous column position.
9491 // This means we need to keep an array of rects so we can check. However
9492 // it does also mean that some spans simply may not be taken into account
9493 // where there are different spans happening on different rows. In these cases,
9494 // they will simply be as wide as their constituent columns.
9495
9496 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9497 // the absolute or percentage width of each column.
9498
9499 for (j = 0; j < m_rowCount; j++)
9500 {
9501 // First get the overall margins so we can calculate percentage widths based on
9502 // the available content space for all cells on the row
9503
9504 int overallRowContentMargin = 0;
9505 int visibleCellCount = 0;
9506
9507 for (i = 0; i < m_colCount; i++)
9508 {
9509 wxRichTextBox* cell = GetCell(j, i);
9510 if (cell->IsShown())
9511 {
9512 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9513 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9514
9515 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9516 visibleCellCount ++;
9517 }
9518 }
9519
9520 // Add in inter-cell padding
9521 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9522
9523 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9524 wxSize rowTableSize(rowContentWidth, 0);
9525 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9526
9527 for (i = 0; i < m_colCount; i++)
9528 {
9529 wxRichTextBox* cell = GetCell(j, i);
9530 if (cell->IsShown())
9531 {
9532 int colSpan = 1;
9533 if (cell->GetProperties().HasProperty(wxT("colspan")))
9534 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9535
9536 // Lay out cell to find min/max widths
9537 cell->Invalidate(wxRICHTEXT_ALL);
9538 cell->Layout(dc, context, availableSpace, availableSpace, style);
9539
9540 if (colSpan == 1)
9541 {
9542 int absoluteCellWidth = -1;
9543 int percentageCellWidth = -1;
9544
9545 // I think we need to calculate percentages from the internal table size,
9546 // minus the padding between cells which we'll need to calculate from the
9547 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9548 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9549 // so if we want to conform to that we'll need to add in the overall cell margins.
9550 // However, this will make it difficult to specify percentages that add up to
9551 // 100% and still fit within the table width.
9552 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9553 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9554 // If we're using internal content size for the width, we would calculate the
9555 // the overall cell width for n cells as:
9556 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9557 // + thisOverallCellMargin
9558 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9559 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9560
9561 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9562 {
9563 int w = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9564 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
9565 {
9566 percentageCellWidth = w;
9567 }
9568 else
9569 {
9570 absoluteCellWidth = w;
9571 }
9572 // Override absolute width with minimum width if necessary
9573 if (cell->GetMinSize().x > 0 && absoluteCellWidth !=1 && cell->GetMinSize().x > absoluteCellWidth)
9574 absoluteCellWidth = cell->GetMinSize().x;
9575 }
9576
9577 if (absoluteCellWidth != -1)
9578 {
9579 if (absoluteCellWidth > absoluteColWidths[i])
9580 absoluteColWidths[i] = absoluteCellWidth;
9581 }
9582
9583 if (percentageCellWidth != -1)
9584 {
9585 if (percentageCellWidth > percentageColWidths[i])
9586 percentageColWidths[i] = percentageCellWidth;
9587 }
9588
9589 if (colSpan == 1 && cell->GetMinSize().x && cell->GetMinSize().x > minColWidths[i])
9590 minColWidths[i] = cell->GetMinSize().x;
9591 if (colSpan == 1 && cell->GetMaxSize().x && cell->GetMaxSize().x > maxColWidths[i])
9592 maxColWidths[i] = cell->GetMaxSize().x;
9593 }
9594 }
9595 }
9596 }
9597
9598 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9599 // TODO: simply merge this into (1).
9600 for (i = 0; i < m_colCount; i++)
9601 {
9602 if (absoluteColWidths[i] > 0)
9603 {
9604 colWidths[i] = absoluteColWidths[i];
9605 }
9606 else if (percentageColWidths[i] > 0)
9607 {
9608 colWidths[i] = percentageColWidths[i];
9609
9610 // This is rubbish - we calculated the absolute widths from percentages, so
9611 // we can't do it again here.
9612 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9613 }
9614 }
9615
9616 // (3) Process absolute or proportional widths of spanning columns,
9617 // now that we know what our fixed column widths are going to be.
9618 // Spanned cells will try to adjust columns so the span will fit.
9619 // Even existing fixed column widths can be expanded if necessary.
9620 // Actually, currently fixed columns widths aren't adjusted; instead,
9621 // the algorithm favours earlier rows and adjusts unspecified column widths
9622 // the first time only. After that, we can't know whether the column has been
9623 // specified explicitly or not. (We could make a note if necessary.)
9624 for (j = 0; j < m_rowCount; j++)
9625 {
9626 // First get the overall margins so we can calculate percentage widths based on
9627 // the available content space for all cells on the row
9628
9629 int overallRowContentMargin = 0;
9630 int visibleCellCount = 0;
9631
9632 for (i = 0; i < m_colCount; i++)
9633 {
9634 wxRichTextBox* cell = GetCell(j, i);
9635 if (cell->IsShown())
9636 {
9637 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9638 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9639
9640 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9641 visibleCellCount ++;
9642 }
9643 }
9644
9645 // Add in inter-cell padding
9646 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9647
9648 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9649 wxSize rowTableSize(rowContentWidth, 0);
9650 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9651
9652 for (i = 0; i < m_colCount; i++)
9653 {
9654 wxRichTextBox* cell = GetCell(j, i);
9655 if (cell->IsShown())
9656 {
9657 int colSpan = 1;
9658 if (cell->GetProperties().HasProperty(wxT("colspan")))
9659 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9660
9661 if (colSpan > 1)
9662 {
9663 int spans = wxMin(colSpan, m_colCount - i);
9664 int cellWidth = 0;
9665 if (spans > 0)
9666 {
9667 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9668 {
9669 cellWidth = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9670 // Override absolute width with minimum width if necessary
9671 if (cell->GetMinSize().x > 0 && cellWidth !=1 && cell->GetMinSize().x > cellWidth)
9672 cellWidth = cell->GetMinSize().x;
9673 }
9674 else
9675 {
9676 // Do we want to do this? It's the only chance we get to
9677 // use the cell's min/max sizes, so we need to work out
9678 // how we're going to balance the unspecified spanning cell
9679 // width with the possibility more-constrained constituent cell widths.
9680 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9681 // don't want to constraint all the spanned columns to fit into this cell.
9682 // OK, let's say that if any of the constituent columns don't fit,
9683 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9684 // cells to the columns later.
9685 cellWidth = cell->GetMinSize().x;
9686 if (cell->GetMaxSize().x > cellWidth)
9687 cellWidth = cell->GetMaxSize().x;
9688 }
9689
9690 // Subtract the padding between cells
9691 int spanningWidth = cellWidth;
9692 spanningWidth -= paddingX * (spans-1);
9693
9694 if (spanningWidth > 0)
9695 {
9696 // Now share the spanning width between columns within that span
9697 // TODO: take into account min widths of columns within the span
9698 int spanningWidthLeft = spanningWidth;
9699 int stretchColCount = 0;
9700 for (k = i; k < (i+spans); k++)
9701 {
9702 if (colWidths[k] > 0) // absolute or proportional width has been specified
9703 spanningWidthLeft -= colWidths[k];
9704 else
9705 stretchColCount ++;
9706 }
9707 // Now divide what's left between the remaining columns
9708 int colShare = 0;
9709 if (stretchColCount > 0)
9710 colShare = spanningWidthLeft / stretchColCount;
9711 int colShareRemainder = spanningWidthLeft - (colShare * stretchColCount);
9712
9713 // If fixed-width columns are currently too big, then we'll later
9714 // stretch the spanned cell to fit.
9715
9716 if (spanningWidthLeft > 0)
9717 {
9718 for (k = i; k < (i+spans); k++)
9719 {
9720 if (colWidths[k] <= 0) // absolute or proportional width has not been specified
9721 {
9722 int newWidth = colShare;
9723 if (k == (i+spans-1))
9724 newWidth += colShareRemainder; // ensure all pixels are filled
9725 colWidths[k] = newWidth;
9726 }
9727 }
9728 }
9729 }
9730 }
9731 }
9732 }
9733 }
9734 }
9735
9736 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9737 // TODO: take into account min widths of columns within the span
9738 int tableWidthMinusPadding = internalTableWidth - (m_colCount-1)*paddingX;
9739 int widthLeft = tableWidthMinusPadding;
9740 int stretchColCount = 0;
9741 for (i = 0; i < m_colCount; i++)
9742 {
9743 // TODO: we need to take into account min widths.
9744 // Subtract min width from width left, then
9745 // add the colShare to the min width
9746 if (colWidths[i] > 0) // absolute or proportional width has been specified
9747 widthLeft -= colWidths[i];
9748 else
9749 {
9750 if (minColWidths[i] > 0)
9751 widthLeft -= minColWidths[i];
9752
9753 stretchColCount ++;
9754 }
9755 }
9756
9757 // Now divide what's left between the remaining columns
9758 int colShare = 0;
9759 if (stretchColCount > 0)
9760 colShare = widthLeft / stretchColCount;
9761 int colShareRemainder = widthLeft - (colShare * stretchColCount);
9762
9763 // Check we don't have enough space, in which case shrink all columns, overriding
9764 // any absolute/proportional widths
9765 // TODO: actually we would like to divide up the shrinkage according to size.
9766 // How do we calculate the proportions that will achieve this?
9767 // Could first choose an arbitrary value for stretching cells, and then calculate
9768 // factors to multiply each width by.
9769 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9770 if (widthLeft < 0 || (stretchToFitTableWidth && (stretchColCount == 0)))
9771 {
9772 colShare = tableWidthMinusPadding / m_colCount;
9773 colShareRemainder = tableWidthMinusPadding - (colShare * m_colCount);
9774 for (i = 0; i < m_colCount; i++)
9775 {
9776 colWidths[i] = 0;
9777 minColWidths[i] = 0;
9778 }
9779 }
9780
9781 // We have to adjust the columns if either we need to shrink the
9782 // table to fit the parent/table width, or we explicitly set the
9783 // table width and need to stretch out the table.
9784 if (widthLeft < 0 || stretchToFitTableWidth)
9785 {
9786 for (i = 0; i < m_colCount; i++)
9787 {
9788 if (colWidths[i] <= 0) // absolute or proportional width has not been specified
9789 {
9790 if (minColWidths[i] > 0)
9791 colWidths[i] = minColWidths[i] + colShare;
9792 else
9793 colWidths[i] = colShare;
9794 if (i == (m_colCount-1))
9795 colWidths[i] += colShareRemainder; // ensure all pixels are filled
9796 }
9797 }
9798 }
9799
9800 // TODO: if spanned cells have no specified or max width, make them the
9801 // as big as the columns they span. Do this for all spanned cells in all
9802 // rows, of course. Size any spanned cells left over at the end - even if they
9803 // have width > 0, make sure they're limited to the appropriate column edge.
9804
9805
9806 /*
9807 Sort out confusion between content width
9808 and overall width later. For now, assume we specify overall width.
9809
9810 So, now we've laid out the table to fit into the given space
9811 and have used specified widths and minimum widths.
9812
9813 Now we need to consider how we will try to take maximum width into account.
9814
9815 */
9816
9817 // (??) TODO: take max width into account
9818
9819 // (6) Lay out all cells again with the current values
9820
9821 int maxRight = 0;
9822 int y = availableSpace.y;
9823 for (j = 0; j < m_rowCount; j++)
9824 {
9825 int x = availableSpace.x; // TODO: take into account centering etc.
9826 int maxCellHeight = 0;
9827 int maxSpecifiedCellHeight = 0;
9828
9829 wxArrayInt actualWidths;
9830 actualWidths.Add(0, m_colCount);
9831
9832 wxTextAttrDimensionConverter converter(dc, scale);
9833 for (i = 0; i < m_colCount; i++)
9834 {
9835 wxRichTextCell* cell = GetCell(j, i);
9836 if (cell->IsShown())
9837 {
9838 // Get max specified cell height
9839 // Don't handle percentages for height
9840 if (cell->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE)
9841 {
9842 int h = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetHeight());
9843 if (h > maxSpecifiedCellHeight)
9844 maxSpecifiedCellHeight = h;
9845 }
9846
9847 if (colWidths[i] > 0) // absolute or proportional width has been specified
9848 {
9849 int colSpan = 1;
9850 if (cell->GetProperties().HasProperty(wxT("colspan")))
9851 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9852
9853 wxRect availableCellSpace;
9854
9855 // TODO: take into acount spans
9856 if (colSpan > 1)
9857 {
9858 // Calculate the size of this spanning cell from its constituent columns
9859 int xx = x;
9860 int spans = wxMin(colSpan, m_colCount - i);
9861 for (k = i; k < spans; k++)
9862 {
9863 if (k != i)
9864 xx += paddingX;
9865 xx += colWidths[k];
9866 }
9867 availableCellSpace = wxRect(x, y, xx, -1);
9868 }
9869 else
9870 availableCellSpace = wxRect(x, y, colWidths[i], -1);
9871
9872 // Store actual width so we can force cell to be the appropriate width on the final loop
9873 actualWidths[i] = availableCellSpace.GetWidth();
9874
9875 // Lay out cell
9876 cell->Invalidate(wxRICHTEXT_ALL);
9877 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
9878
9879 // TODO: use GetCachedSize().x to compute 'natural' size
9880
9881 x += (availableCellSpace.GetWidth() + paddingX);
9882 if (cell->GetCachedSize().y > maxCellHeight)
9883 maxCellHeight = cell->GetCachedSize().y;
9884 }
9885 }
9886 }
9887
9888 maxCellHeight = wxMax(maxCellHeight, maxSpecifiedCellHeight);
9889
9890 for (i = 0; i < m_colCount; i++)
9891 {
9892 wxRichTextCell* cell = GetCell(j, i);
9893 if (cell->IsShown())
9894 {
9895 wxRect availableCellSpace = wxRect(cell->GetPosition(), wxSize(actualWidths[i], maxCellHeight));
9896 // Lay out cell with new height
9897 cell->Invalidate(wxRICHTEXT_ALL);
9898 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
9899
9900 // Make sure the cell size really is the appropriate size,
9901 // not the calculated box size
9902 cell->SetCachedSize(wxSize(actualWidths[i], maxCellHeight));
9903
9904 maxRight = wxMax(maxRight, cell->GetPosition().x + cell->GetCachedSize().x);
9905 }
9906 }
9907
9908 y += maxCellHeight;
9909 if (j < (m_rowCount-1))
9910 y += paddingY;
9911 }
9912
9913 // We need to add back the margins etc.
9914 {
9915 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
9916 contentRect = wxRect(wxPoint(0, 0), wxSize(maxRight - availableSpace.x, y - availableSpace.y));
9917 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
9918 SetCachedSize(marginRect.GetSize());
9919 }
9920
9921 // TODO: calculate max size
9922 {
9923 SetMaxSize(GetCachedSize());
9924 }
9925
9926 // TODO: calculate min size
9927 {
9928 SetMinSize(GetCachedSize());
9929 }
9930
9931 // TODO: currently we use either a fixed table width or the parent's size.
9932 // We also want to be able to calculate the table width from its content,
9933 // whether using fixed column widths or cell content min/max width.
9934 // Probably need a boolean flag to say whether we need to stretch cells
9935 // to fit the table width, or to simply use min/max cell widths. The
9936 // trouble with this is that if cell widths are not specified, they
9937 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9938 // Anyway, ignoring that problem, we probably need to factor layout into a function
9939 // that can can calculate the maximum unconstrained layout in case table size is
9940 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9941 // constrain Layout(), or the previously-calculated max size to constraint layout.
9942
9943 return true;
9944 }
9945
9946 // Finds the absolute position and row height for the given character position
9947 bool wxRichTextTable::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
9948 {
9949 wxRichTextCell* child = GetCell(index+1);
9950 if (child)
9951 {
9952 // Find the position at the start of the child cell, since the table doesn't
9953 // have any caret position of its own.
9954 return child->FindPosition(dc, context, -1, pt, height, forceLineStart);
9955 }
9956 else
9957 return false;
9958 }
9959
9960 // Get the cell at the given character position (in the range of the table).
9961 wxRichTextCell* wxRichTextTable::GetCell(long pos) const
9962 {
9963 int row = 0, col = 0;
9964 if (GetCellRowColumnPosition(pos, row, col))
9965 {
9966 return GetCell(row, col);
9967 }
9968 else
9969 return NULL;
9970 }
9971
9972 // Get the row/column for a given character position
9973 bool wxRichTextTable::GetCellRowColumnPosition(long pos, int& row, int& col) const
9974 {
9975 if (m_colCount == 0 || m_rowCount == 0)
9976 return false;
9977
9978 row = (int) (pos / m_colCount);
9979 col = pos - (row * m_colCount);
9980
9981 wxASSERT(row < m_rowCount && col < m_colCount);
9982
9983 if (row < m_rowCount && col < m_colCount)
9984 return true;
9985 else
9986 return false;
9987 }
9988
9989 // Calculate range, taking row/cell ordering into account instead of relying
9990 // on list ordering.
9991 void wxRichTextTable::CalculateRange(long start, long& end)
9992 {
9993 long current = start;
9994 long lastEnd = current;
9995
9996 if (IsTopLevel())
9997 {
9998 current = 0;
9999 lastEnd = 0;
10000 }
10001
10002 int i, j;
10003 for (i = 0; i < m_rowCount; i++)
10004 {
10005 for (j = 0; j < m_colCount; j++)
10006 {
10007 wxRichTextCell* child = GetCell(i, j);
10008 if (child)
10009 {
10010 long childEnd = 0;
10011
10012 child->CalculateRange(current, childEnd);
10013
10014 lastEnd = childEnd;
10015 current = childEnd + 1;
10016 }
10017 }
10018 }
10019
10020 // A top-level object always has a range of size 1,
10021 // because its children don't count at this level.
10022 end = start;
10023 m_range.SetRange(start, start);
10024
10025 // An object with no children has zero length
10026 if (m_children.GetCount() == 0)
10027 lastEnd --;
10028 m_ownRange.SetRange(0, lastEnd);
10029 }
10030
10031 // Gets the range size.
10032 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
10033 {
10034 return wxRichTextBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize, partialExtents);
10035 }
10036
10037 // Deletes content in the given range.
10038 bool wxRichTextTable::DeleteRange(const wxRichTextRange& WXUNUSED(range))
10039 {
10040 // TODO: implement deletion of cells
10041 return true;
10042 }
10043
10044 // Gets any text in this object for the given range.
10045 wxString wxRichTextTable::GetTextForRange(const wxRichTextRange& range) const
10046 {
10047 return wxRichTextBox::GetTextForRange(range);
10048 }
10049
10050 // Copies this object.
10051 void wxRichTextTable::Copy(const wxRichTextTable& obj)
10052 {
10053 wxRichTextBox::Copy(obj);
10054
10055 ClearTable();
10056
10057 m_rowCount = obj.m_rowCount;
10058 m_colCount = obj.m_colCount;
10059
10060 m_cells.Add(wxRichTextObjectPtrArray(), m_rowCount);
10061
10062 int i, j;
10063 for (i = 0; i < m_rowCount; i++)
10064 {
10065 wxRichTextObjectPtrArray& colArray = m_cells[i];
10066 for (j = 0; j < m_colCount; j++)
10067 {
10068 wxRichTextCell* cell = wxDynamicCast(obj.GetCell(i, j)->Clone(), wxRichTextCell);
10069 AppendChild(cell);
10070
10071 colArray.Add(cell);
10072 }
10073 }
10074 }
10075
10076 void wxRichTextTable::ClearTable()
10077 {
10078 m_cells.Clear();
10079 DeleteChildren();
10080 }
10081
10082 bool wxRichTextTable::CreateTable(int rows, int cols)
10083 {
10084 ClearTable();
10085
10086 m_rowCount = rows;
10087 m_colCount = cols;
10088
10089 m_cells.Add(wxRichTextObjectPtrArray(), rows);
10090
10091 int i, j;
10092 for (i = 0; i < rows; i++)
10093 {
10094 wxRichTextObjectPtrArray& colArray = m_cells[i];
10095 for (j = 0; j < cols; j++)
10096 {
10097 wxRichTextCell* cell = new wxRichTextCell;
10098 AppendChild(cell);
10099 cell->AddParagraph(wxEmptyString);
10100
10101 colArray.Add(cell);
10102 }
10103 }
10104
10105 return true;
10106 }
10107
10108 wxRichTextCell* wxRichTextTable::GetCell(int row, int col) const
10109 {
10110 wxASSERT(row < m_rowCount);
10111 wxASSERT(col < m_colCount);
10112
10113 if (row < m_rowCount && col < m_colCount)
10114 {
10115 wxRichTextObjectPtrArray& colArray = m_cells[row];
10116 wxRichTextObject* obj = colArray[col];
10117 return wxDynamicCast(obj, wxRichTextCell);
10118 }
10119 else
10120 return NULL;
10121 }
10122
10123 // Returns a selection object specifying the selections between start and end character positions.
10124 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
10125 wxRichTextSelection wxRichTextTable::GetSelection(long start, long end) const
10126 {
10127 wxRichTextSelection selection;
10128 selection.SetContainer((wxRichTextTable*) this);
10129
10130 if (start > end)
10131 {
10132 long tmp = end;
10133 end = start;
10134 start = tmp;
10135 }
10136
10137 wxASSERT( start >= 0 && end < (m_colCount * m_rowCount));
10138
10139 if (end >= (m_colCount * m_rowCount))
10140 return selection;
10141
10142 // We need to find the rectangle of cells that is described by the rectangle
10143 // with start, end as the diagonal. Make sure we don't add cells that are
10144 // not currenty visible because they are overlapped by spanning cells.
10145 /*
10146 --------------------------
10147 | 0 | 1 | 2 | 3 | 4 |
10148 --------------------------
10149 | 5 | 6 | 7 | 8 | 9 |
10150 --------------------------
10151 | 10 | 11 | 12 | 13 | 14 |
10152 --------------------------
10153 | 15 | 16 | 17 | 18 | 19 |
10154 --------------------------
10155
10156 Let's say we select 6 -> 18.
10157
10158 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
10159 which is left and which is right.
10160
10161 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
10162
10163 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
10164 and (b) shown.
10165
10166
10167 */
10168
10169 int leftCol = start - m_colCount * int(start/m_colCount);
10170 int rightCol = end - m_colCount * int(end/m_colCount);
10171
10172 int topRow = int(start/m_colCount);
10173 int bottomRow = int(end/m_colCount);
10174
10175 if (leftCol > rightCol)
10176 {
10177 int tmp = rightCol;
10178 rightCol = leftCol;
10179 leftCol = tmp;
10180 }
10181
10182 if (topRow > bottomRow)
10183 {
10184 int tmp = bottomRow;
10185 bottomRow = topRow;
10186 topRow = tmp;
10187 }
10188
10189 int i, j;
10190 for (i = topRow; i <= bottomRow; i++)
10191 {
10192 for (j = leftCol; j <= rightCol; j++)
10193 {
10194 wxRichTextCell* cell = GetCell(i, j);
10195 if (cell && cell->IsShown())
10196 selection.Add(cell->GetRange());
10197 }
10198 }
10199
10200 return selection;
10201 }
10202
10203 // Sets the attributes for the cells specified by the selection.
10204 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection& selection, const wxRichTextAttr& style, int flags)
10205 {
10206 if (selection.GetContainer() != this)
10207 return false;
10208
10209 wxRichTextBuffer* buffer = GetBuffer();
10210 bool haveControl = (buffer && buffer->GetRichTextCtrl() != NULL);
10211 bool withUndo = haveControl && ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
10212
10213 if (withUndo)
10214 buffer->BeginBatchUndo(_("Set Cell Style"));
10215
10216 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
10217 while (node)
10218 {
10219 wxRichTextCell* cell = wxDynamicCast(node->GetData(), wxRichTextCell);
10220 if (cell && selection.WithinSelection(cell->GetRange().GetStart()))
10221 SetStyle(cell, style, flags);
10222 node = node->GetNext();
10223 }
10224
10225 // Do action, or delay it until end of batch.
10226 if (withUndo)
10227 buffer->EndBatchUndo();
10228
10229 return true;
10230 }
10231
10232 bool wxRichTextTable::DeleteRows(int startRow, int noRows)
10233 {
10234 wxASSERT((startRow + noRows) < m_rowCount);
10235 if ((startRow + noRows) >= m_rowCount)
10236 return false;
10237
10238 int i, j;
10239 for (i = startRow; i < (startRow+noRows); i++)
10240 {
10241 wxRichTextObjectPtrArray& colArray = m_cells[startRow];
10242 for (j = 0; j < (int) colArray.GetCount(); j++)
10243 {
10244 wxRichTextObject* cell = colArray[j];
10245 RemoveChild(cell, true);
10246 }
10247
10248 // Keep deleting at the same position, since we move all
10249 // the others up
10250 m_cells.RemoveAt(startRow);
10251 }
10252
10253 m_rowCount = m_rowCount - noRows;
10254
10255 return true;
10256 }
10257
10258 bool wxRichTextTable::DeleteColumns(int startCol, int noCols)
10259 {
10260 wxASSERT((startCol + noCols) < m_colCount);
10261 if ((startCol + noCols) >= m_colCount)
10262 return false;
10263
10264 bool deleteRows = (noCols == m_colCount);
10265
10266 int i, j;
10267 for (i = 0; i < m_rowCount; i++)
10268 {
10269 wxRichTextObjectPtrArray& colArray = m_cells[deleteRows ? 0 : i];
10270 for (j = startCol; j < (startCol+noCols); j++)
10271 {
10272 wxRichTextObject* cell = colArray[j];
10273 RemoveChild(cell, true);
10274 }
10275
10276 if (deleteRows)
10277 m_cells.RemoveAt(0);
10278 }
10279
10280 if (deleteRows)
10281 m_rowCount = 0;
10282 m_colCount = m_colCount - noCols;
10283
10284 return true;
10285 }
10286
10287 bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& attr)
10288 {
10289 wxASSERT(startRow <= m_rowCount);
10290 if (startRow > m_rowCount)
10291 return false;
10292
10293 int i, j;
10294 for (i = 0; i < noRows; i++)
10295 {
10296 int idx;
10297 if (startRow == m_rowCount)
10298 {
10299 m_cells.Add(wxRichTextObjectPtrArray());
10300 idx = m_cells.GetCount() - 1;
10301 }
10302 else
10303 {
10304 m_cells.Insert(wxRichTextObjectPtrArray(), startRow+i);
10305 idx = startRow+i;
10306 }
10307
10308 wxRichTextObjectPtrArray& colArray = m_cells[idx];
10309 for (j = 0; j < m_colCount; j++)
10310 {
10311 wxRichTextCell* cell = new wxRichTextCell;
10312 cell->GetAttributes() = attr;
10313
10314 AppendChild(cell);
10315 colArray.Add(cell);
10316 }
10317 }
10318
10319 m_rowCount = m_rowCount + noRows;
10320 return true;
10321 }
10322
10323 bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& attr)
10324 {
10325 wxASSERT(startCol <= m_colCount);
10326 if (startCol > m_colCount)
10327 return false;
10328
10329 int i, j;
10330 for (i = 0; i < m_rowCount; i++)
10331 {
10332 wxRichTextObjectPtrArray& colArray = m_cells[i];
10333 for (j = 0; j < noCols; j++)
10334 {
10335 wxRichTextCell* cell = new wxRichTextCell;
10336 cell->GetAttributes() = attr;
10337
10338 AppendChild(cell);
10339
10340 if (startCol == m_colCount)
10341 colArray.Add(cell);
10342 else
10343 colArray.Insert(cell, startCol+j);
10344 }
10345 }
10346
10347 m_colCount = m_colCount + noCols;
10348
10349 return true;
10350 }
10351
10352 // Edit properties via a GUI
10353 bool wxRichTextTable::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
10354 {
10355 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Table Properties"));
10356 boxDlg.SetAttributes(GetAttributes());
10357
10358 if (boxDlg.ShowModal() == wxID_OK)
10359 {
10360 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
10361 return true;
10362 }
10363 else
10364 return false;
10365 }
10366
10367 /*
10368 * Module to initialise and clean up handlers
10369 */
10370
10371 class wxRichTextModule: public wxModule
10372 {
10373 DECLARE_DYNAMIC_CLASS(wxRichTextModule)
10374 public:
10375 wxRichTextModule() {}
10376 bool OnInit()
10377 {
10378 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer);
10379 wxRichTextBuffer::InitStandardHandlers();
10380 wxRichTextParagraph::InitDefaultTabs();
10381
10382 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
10383 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
10384 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
10385 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
10386 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
10387 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
10388 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
10389 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
10390 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
10391
10392 return true;
10393 }
10394 void OnExit()
10395 {
10396 wxRichTextBuffer::CleanUpHandlers();
10397 wxRichTextBuffer::CleanUpDrawingHandlers();
10398 wxRichTextBuffer::CleanUpFieldTypes();
10399 wxRichTextXMLHandler::ClearNodeToClassMap();
10400 wxRichTextDecimalToRoman(-1);
10401 wxRichTextParagraph::ClearDefaultTabs();
10402 wxRichTextCtrl::ClearAvailableFontNames();
10403 wxRichTextBuffer::SetRenderer(NULL);
10404 }
10405 };
10406
10407 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule)
10408
10409
10410 // If the richtext lib is dynamically loaded after the app has already started
10411 // (such as from wxPython) then the built-in module system will not init this
10412 // module. Provide this function to do it manually.
10413 void wxRichTextModuleInit()
10414 {
10415 wxModule* module = new wxRichTextModule;
10416 module->Init();
10417 wxModule::RegisterModule(module);
10418 }
10419
10420
10421 /*!
10422 * Commands for undo/redo
10423 *
10424 */
10425
10426 wxRichTextCommand::wxRichTextCommand(const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
10427 wxRichTextParagraphLayoutBox* container, wxRichTextCtrl* ctrl, bool ignoreFirstTime): wxCommand(true, name)
10428 {
10429 /* wxRichTextAction* action = */ new wxRichTextAction(this, name, id, buffer, container, ctrl, ignoreFirstTime);
10430 }
10431
10432 wxRichTextCommand::wxRichTextCommand(const wxString& name): wxCommand(true, name)
10433 {
10434 }
10435
10436 wxRichTextCommand::~wxRichTextCommand()
10437 {
10438 ClearActions();
10439 }
10440
10441 void wxRichTextCommand::AddAction(wxRichTextAction* action)
10442 {
10443 if (!m_actions.Member(action))
10444 m_actions.Append(action);
10445 }
10446
10447 bool wxRichTextCommand::Do()
10448 {
10449 for (wxList::compatibility_iterator node = m_actions.GetFirst(); node; node = node->GetNext())
10450 {
10451 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10452 action->Do();
10453 }
10454
10455 return true;
10456 }
10457
10458 bool wxRichTextCommand::Undo()
10459 {
10460 for (wxList::compatibility_iterator node = m_actions.GetLast(); node; node = node->GetPrevious())
10461 {
10462 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10463 action->Undo();
10464 }
10465
10466 return true;
10467 }
10468
10469 void wxRichTextCommand::ClearActions()
10470 {
10471 WX_CLEAR_LIST(wxList, m_actions);
10472 }
10473
10474 /*!
10475 * Individual action
10476 *
10477 */
10478
10479 wxRichTextAction::wxRichTextAction(wxRichTextCommand* cmd, const wxString& name, wxRichTextCommandId id,
10480 wxRichTextBuffer* buffer, wxRichTextParagraphLayoutBox* container,
10481 wxRichTextCtrl* ctrl, bool ignoreFirstTime)
10482 {
10483 m_buffer = buffer;
10484 m_object = NULL;
10485 m_containerAddress.Create(buffer, container);
10486 m_ignoreThis = ignoreFirstTime;
10487 m_cmdId = id;
10488 m_position = -1;
10489 m_ctrl = ctrl;
10490 m_name = name;
10491 m_newParagraphs.SetDefaultStyle(buffer->GetDefaultStyle());
10492 m_newParagraphs.SetBasicStyle(buffer->GetBasicStyle());
10493 if (cmd)
10494 cmd->AddAction(this);
10495 }
10496
10497 wxRichTextAction::~wxRichTextAction()
10498 {
10499 if (m_object)
10500 delete m_object;
10501 }
10502
10503 // Returns the container that this action refers to, using the container address and top-level buffer.
10504 wxRichTextParagraphLayoutBox* wxRichTextAction::GetContainer() const
10505 {
10506 wxRichTextParagraphLayoutBox* container = wxDynamicCast(GetContainerAddress().GetObject(m_buffer), wxRichTextParagraphLayoutBox);
10507 return container;
10508 }
10509
10510
10511 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions)
10512 {
10513 // Store a list of line start character and y positions so we can figure out which area
10514 // we need to refresh
10515
10516 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10517 wxRichTextParagraphLayoutBox* container = GetContainer();
10518 wxASSERT(container != NULL);
10519 if (!container)
10520 return;
10521
10522 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10523 // If we had several actions, which only invalidate and leave layout until the
10524 // paint handler is called, then this might not be true. So we may need to switch
10525 // optimisation on only when we're simply adding text and not simultaneously
10526 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10527 // first, but of course this means we'll be doing it twice.
10528 if (!m_buffer->IsDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
10529 {
10530 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
10531 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
10532 int lastY = firstVisiblePt.y + clientSize.y;
10533
10534 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetRange().GetStart());
10535 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
10536 while (node)
10537 {
10538 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10539 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10540 while (node2)
10541 {
10542 wxRichTextLine* line = node2->GetData();
10543 wxPoint pt = line->GetAbsolutePosition();
10544 wxRichTextRange range = line->GetAbsoluteRange();
10545
10546 if (pt.y > lastY)
10547 {
10548 node2 = wxRichTextLineList::compatibility_iterator();
10549 node = wxRichTextObjectList::compatibility_iterator();
10550 }
10551 else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
10552 {
10553 optimizationLineCharPositions.Add(range.GetStart());
10554 optimizationLineYPositions.Add(pt.y);
10555 }
10556
10557 if (node2)
10558 node2 = node2->GetNext();
10559 }
10560
10561 if (node)
10562 node = node->GetNext();
10563 }
10564 }
10565 #endif
10566 }
10567
10568 bool wxRichTextAction::Do()
10569 {
10570 m_buffer->Modify(true);
10571
10572 wxRichTextParagraphLayoutBox* container = GetContainer();
10573 wxASSERT(container != NULL);
10574 if (!container)
10575 return false;
10576
10577 switch (m_cmdId)
10578 {
10579 case wxRICHTEXT_INSERT:
10580 {
10581 // Store a list of line start character and y positions so we can figure out which area
10582 // we need to refresh
10583 wxArrayInt optimizationLineCharPositions;
10584 wxArrayInt optimizationLineYPositions;
10585
10586 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10587 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10588 #endif
10589
10590 container->InsertFragment(GetRange().GetStart(), m_newParagraphs);
10591 container->UpdateRanges();
10592
10593 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10594 // Layout() would stop prematurely at the top level.
10595 container->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
10596
10597 long newCaretPosition = GetPosition() + m_newParagraphs.GetOwnRange().GetLength();
10598
10599 // Character position to caret position
10600 newCaretPosition --;
10601
10602 // Don't take into account the last newline
10603 if (m_newParagraphs.GetPartialParagraph())
10604 newCaretPosition --;
10605 else
10606 if (m_newParagraphs.GetChildren().GetCount() > 1)
10607 {
10608 wxRichTextObject* p = (wxRichTextObject*) m_newParagraphs.GetChildren().GetLast()->GetData();
10609 if (p->GetRange().GetLength() == 1)
10610 newCaretPosition --;
10611 }
10612
10613 newCaretPosition = wxMin(newCaretPosition, (container->GetOwnRange().GetEnd()-1));
10614
10615 UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
10616
10617 wxRichTextEvent cmdEvent(
10618 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
10619 m_ctrl ? m_ctrl->GetId() : -1);
10620 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10621 cmdEvent.SetRange(GetRange());
10622 cmdEvent.SetPosition(GetRange().GetStart());
10623 cmdEvent.SetContainer(container);
10624
10625 m_buffer->SendEvent(cmdEvent);
10626
10627 break;
10628 }
10629 case wxRICHTEXT_DELETE:
10630 {
10631 wxArrayInt optimizationLineCharPositions;
10632 wxArrayInt optimizationLineYPositions;
10633
10634 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10635 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10636 #endif
10637
10638 container->DeleteRange(GetRange());
10639 container->UpdateRanges();
10640 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10641 // Layout() would stop prematurely at the top level.
10642 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10643
10644 long caretPos = GetRange().GetStart()-1;
10645 if (caretPos >= container->GetOwnRange().GetEnd())
10646 caretPos --;
10647
10648 UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
10649
10650 wxRichTextEvent cmdEvent(
10651 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
10652 m_ctrl ? m_ctrl->GetId() : -1);
10653 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10654 cmdEvent.SetRange(GetRange());
10655 cmdEvent.SetPosition(GetRange().GetStart());
10656 cmdEvent.SetContainer(container);
10657
10658 m_buffer->SendEvent(cmdEvent);
10659
10660 break;
10661 }
10662 case wxRICHTEXT_CHANGE_STYLE:
10663 case wxRICHTEXT_CHANGE_PROPERTIES:
10664 {
10665 ApplyParagraphs(GetNewParagraphs());
10666
10667 // Invalidate the whole buffer if there were floating objects
10668 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
10669 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
10670 else
10671 {
10672 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10673 // Layout() would stop prematurely at the top level.
10674 container->InvalidateHierarchy(GetRange());
10675 }
10676
10677 UpdateAppearance(GetPosition());
10678
10679 wxRichTextEvent cmdEvent(
10680 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
10681 m_ctrl ? m_ctrl->GetId() : -1);
10682 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10683 cmdEvent.SetRange(GetRange());
10684 cmdEvent.SetPosition(GetRange().GetStart());
10685 cmdEvent.SetContainer(container);
10686
10687 m_buffer->SendEvent(cmdEvent);
10688
10689 break;
10690 }
10691 case wxRICHTEXT_CHANGE_ATTRIBUTES:
10692 {
10693 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer); // container->GetChildAtPosition(GetRange().GetStart());
10694 if (obj)
10695 {
10696 wxRichTextAttr oldAttr = obj->GetAttributes();
10697 obj->GetAttributes() = m_attributes;
10698 m_attributes = oldAttr;
10699 }
10700
10701 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10702 // Layout() would stop prematurely at the top level.
10703 // Invalidate the whole buffer if there were floating objects
10704 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
10705 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
10706 else
10707 container->InvalidateHierarchy(GetRange());
10708
10709 UpdateAppearance(GetPosition());
10710
10711 wxRichTextEvent cmdEvent(
10712 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED,
10713 m_ctrl ? m_ctrl->GetId() : -1);
10714 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10715 cmdEvent.SetRange(GetRange());
10716 cmdEvent.SetPosition(GetRange().GetStart());
10717 cmdEvent.SetContainer(container);
10718
10719 m_buffer->SendEvent(cmdEvent);
10720
10721 break;
10722 }
10723 case wxRICHTEXT_CHANGE_OBJECT:
10724 {
10725 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer);
10726 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10727 if (obj && m_object)
10728 {
10729 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(obj);
10730 if (node)
10731 {
10732 wxRichTextObject* obj = node->GetData();
10733 node->SetData(m_object);
10734 m_object = obj;
10735 }
10736 }
10737
10738 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10739 // Layout() would stop prematurely at the top level.
10740 // Invalidate the whole buffer if there were floating objects
10741 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
10742 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
10743 else
10744 container->InvalidateHierarchy(GetRange());
10745
10746 UpdateAppearance(GetPosition());
10747
10748 // TODO: send new kind of modification event
10749
10750 break;
10751 }
10752 default:
10753 break;
10754 }
10755
10756 return true;
10757 }
10758
10759 bool wxRichTextAction::Undo()
10760 {
10761 m_buffer->Modify(true);
10762
10763 wxRichTextParagraphLayoutBox* container = GetContainer();
10764 wxASSERT(container != NULL);
10765 if (!container)
10766 return false;
10767
10768 switch (m_cmdId)
10769 {
10770 case wxRICHTEXT_INSERT:
10771 {
10772 wxArrayInt optimizationLineCharPositions;
10773 wxArrayInt optimizationLineYPositions;
10774
10775 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10776 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10777 #endif
10778
10779 container->DeleteRange(GetRange());
10780 container->UpdateRanges();
10781
10782 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10783 // Layout() would stop prematurely at the top level.
10784 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10785
10786 long newCaretPosition = GetPosition() - 1;
10787
10788 UpdateAppearance(newCaretPosition, true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
10789
10790 wxRichTextEvent cmdEvent(
10791 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
10792 m_ctrl ? m_ctrl->GetId() : -1);
10793 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10794 cmdEvent.SetRange(GetRange());
10795 cmdEvent.SetPosition(GetRange().GetStart());
10796 cmdEvent.SetContainer(container);
10797
10798 m_buffer->SendEvent(cmdEvent);
10799
10800 break;
10801 }
10802 case wxRICHTEXT_DELETE:
10803 {
10804 wxArrayInt optimizationLineCharPositions;
10805 wxArrayInt optimizationLineYPositions;
10806
10807 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10808 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10809 #endif
10810
10811 container->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
10812 container->UpdateRanges();
10813
10814 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10815 // Layout() would stop prematurely at the top level.
10816 container->InvalidateHierarchy(GetRange());
10817
10818 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
10819
10820 wxRichTextEvent cmdEvent(
10821 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
10822 m_ctrl ? m_ctrl->GetId() : -1);
10823 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10824 cmdEvent.SetRange(GetRange());
10825 cmdEvent.SetPosition(GetRange().GetStart());
10826 cmdEvent.SetContainer(container);
10827
10828 m_buffer->SendEvent(cmdEvent);
10829
10830 break;
10831 }
10832 case wxRICHTEXT_CHANGE_STYLE:
10833 case wxRICHTEXT_CHANGE_PROPERTIES:
10834 {
10835 ApplyParagraphs(GetOldParagraphs());
10836 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10837 // Layout() would stop prematurely at the top level.
10838 container->InvalidateHierarchy(GetRange());
10839
10840 UpdateAppearance(GetPosition());
10841
10842 wxRichTextEvent cmdEvent(
10843 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
10844 m_ctrl ? m_ctrl->GetId() : -1);
10845 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10846 cmdEvent.SetRange(GetRange());
10847 cmdEvent.SetPosition(GetRange().GetStart());
10848 cmdEvent.SetContainer(container);
10849
10850 m_buffer->SendEvent(cmdEvent);
10851
10852 break;
10853 }
10854 case wxRICHTEXT_CHANGE_ATTRIBUTES:
10855 case wxRICHTEXT_CHANGE_OBJECT:
10856 {
10857 return Do();
10858 }
10859 default:
10860 break;
10861 }
10862
10863 return true;
10864 }
10865
10866 /// Update the control appearance
10867 void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions, bool isDoCmd)
10868 {
10869 wxRichTextParagraphLayoutBox* container = GetContainer();
10870 wxASSERT(container != NULL);
10871 if (!container)
10872 return;
10873
10874 if (m_ctrl)
10875 {
10876 m_ctrl->SetFocusObject(container);
10877 m_ctrl->SetCaretPosition(caretPosition);
10878
10879 if (!m_ctrl->IsFrozen())
10880 {
10881 wxRect containerRect = container->GetRect();
10882
10883 m_ctrl->LayoutContent();
10884
10885 // Refresh everything if there were floating objects or the container changed size
10886 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
10887 if ((wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0) || (container->GetParent() && containerRect != container->GetRect()))
10888 {
10889 m_ctrl->Refresh(false);
10890 }
10891 else
10892
10893 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10894 // Find refresh rectangle if we are in a position to optimise refresh
10895 if ((m_cmdId == wxRICHTEXT_INSERT || m_cmdId == wxRICHTEXT_DELETE) && optimizationLineCharPositions)
10896 {
10897 size_t i;
10898
10899 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
10900 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
10901
10902 // Start/end positions
10903 int firstY = 0;
10904 int lastY = firstVisiblePt.y + clientSize.y;
10905
10906 bool foundEnd = false;
10907
10908 // position offset - how many characters were inserted
10909 int positionOffset = GetRange().GetLength();
10910
10911 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10912 if ((m_cmdId == wxRICHTEXT_DELETE && isDoCmd) || (m_cmdId == wxRICHTEXT_INSERT && !isDoCmd))
10913 positionOffset = - positionOffset;
10914
10915 // find the first line which is being drawn at the same position as it was
10916 // before. Since we're talking about a simple insertion, we can assume
10917 // that the rest of the window does not need to be redrawn.
10918
10919 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetPosition());
10920 // Since we support floating layout, we should redraw the whole para instead of just
10921 // the first line touching the invalid range.
10922 if (para)
10923 {
10924 firstY = para->GetPosition().y;
10925 }
10926
10927 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
10928 while (node)
10929 {
10930 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10931 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10932 while (node2)
10933 {
10934 wxRichTextLine* line = node2->GetData();
10935 wxPoint pt = line->GetAbsolutePosition();
10936 wxRichTextRange range = line->GetAbsoluteRange();
10937
10938 // we want to find the first line that is in the same position
10939 // as before. This will mean we're at the end of the changed text.
10940
10941 if (pt.y > lastY) // going past the end of the window, no more info
10942 {
10943 node2 = wxRichTextLineList::compatibility_iterator();
10944 node = wxRichTextObjectList::compatibility_iterator();
10945 }
10946 // Detect last line in the buffer
10947 else if (!node2->GetNext() && para->GetRange().Contains(container->GetOwnRange().GetEnd()))
10948 {
10949 // If deleting text, make sure we refresh below as well as above
10950 if (positionOffset >= 0)
10951 {
10952 foundEnd = true;
10953 lastY = pt.y + line->GetSize().y;
10954 }
10955
10956 node2 = wxRichTextLineList::compatibility_iterator();
10957 node = wxRichTextObjectList::compatibility_iterator();
10958
10959 break;
10960 }
10961 else
10962 {
10963 // search for this line being at the same position as before
10964 for (i = 0; i < optimizationLineCharPositions->GetCount(); i++)
10965 {
10966 if (((*optimizationLineCharPositions)[i] + positionOffset == range.GetStart()) &&
10967 ((*optimizationLineYPositions)[i] == pt.y))
10968 {
10969 // Stop, we're now the same as we were
10970 foundEnd = true;
10971
10972 lastY = pt.y;
10973
10974 node2 = wxRichTextLineList::compatibility_iterator();
10975 node = wxRichTextObjectList::compatibility_iterator();
10976
10977 break;
10978 }
10979 }
10980 }
10981
10982 if (node2)
10983 node2 = node2->GetNext();
10984 }
10985
10986 if (node)
10987 node = node->GetNext();
10988 }
10989
10990 firstY = wxMax(firstVisiblePt.y, firstY);
10991 if (!foundEnd)
10992 lastY = firstVisiblePt.y + clientSize.y;
10993
10994 // Convert to device coordinates
10995 wxRect rect(m_ctrl->GetPhysicalPoint(m_ctrl->GetScaledPoint(wxPoint(firstVisiblePt.x, firstY))), m_ctrl->GetScaledSize(wxSize(clientSize.x, lastY - firstY)));
10996 m_ctrl->RefreshRect(rect);
10997 }
10998 else
10999 #endif
11000 m_ctrl->Refresh(false);
11001
11002 m_ctrl->PositionCaret();
11003
11004 // This causes styles to persist when doing programmatic
11005 // content creation except when Freeze/Thaw is used, so
11006 // disable this and check for the consequences.
11007 // m_ctrl->SetDefaultStyleToCursorStyle();
11008
11009 if (sendUpdateEvent)
11010 wxTextCtrl::SendTextUpdatedEvent(m_ctrl);
11011 }
11012 }
11013 }
11014
11015 /// Replace the buffer paragraphs with the new ones.
11016 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment)
11017 {
11018 wxRichTextParagraphLayoutBox* container = GetContainer();
11019 wxASSERT(container != NULL);
11020 if (!container)
11021 return;
11022
11023 wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst();
11024 while (node)
11025 {
11026 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
11027 wxASSERT (para != NULL);
11028
11029 // We'll replace the existing paragraph by finding the paragraph at this position,
11030 // delete its node data, and setting a copy as the new node data.
11031 // TODO: make more efficient by simply swapping old and new paragraph objects.
11032
11033 wxRichTextParagraph* existingPara = container->GetParagraphAtPosition(para->GetRange().GetStart());
11034 if (existingPara)
11035 {
11036 wxRichTextObjectList::compatibility_iterator bufferParaNode = container->GetChildren().Find(existingPara);
11037 if (bufferParaNode)
11038 {
11039 wxRichTextParagraph* newPara = new wxRichTextParagraph(*para);
11040 newPara->SetParent(container);
11041
11042 bufferParaNode->SetData(newPara);
11043
11044 delete existingPara;
11045 }
11046 }
11047
11048 node = node->GetNext();
11049 }
11050 }
11051
11052
11053 /*!
11054 * wxRichTextRange
11055 * This stores beginning and end positions for a range of data.
11056 */
11057
11058 WX_DEFINE_OBJARRAY(wxRichTextRangeArray);
11059
11060 /// Limit this range to be within 'range'
11061 bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
11062 {
11063 if (m_start < range.m_start)
11064 m_start = range.m_start;
11065
11066 if (m_end > range.m_end)
11067 m_end = range.m_end;
11068
11069 return true;
11070 }
11071
11072 /*!
11073 * wxRichTextImage implementation
11074 * This object represents an image.
11075 */
11076
11077 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
11078
11079 wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle):
11080 wxRichTextObject(parent)
11081 {
11082 Init();
11083 m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG);
11084 if (charStyle)
11085 SetAttributes(*charStyle);
11086 }
11087
11088 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle):
11089 wxRichTextObject(parent)
11090 {
11091 Init();
11092 m_imageBlock = imageBlock;
11093 if (charStyle)
11094 SetAttributes(*charStyle);
11095 }
11096
11097 wxRichTextImage::~wxRichTextImage()
11098 {
11099 }
11100
11101 void wxRichTextImage::Init()
11102 {
11103 m_originalImageSize = wxSize(-1, -1);
11104 }
11105
11106 /// Create a cached image at the required size
11107 bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache, const wxSize& parentSize)
11108 {
11109 if (!m_imageBlock.IsOk())
11110 return false;
11111
11112 // If we have an original image size, use that to compute the cached bitmap size
11113 // instead of loading the image each time. This way we can avoid loading
11114 // the image so long as the new cached bitmap size hasn't changed.
11115
11116 wxImage image;
11117 if (resetCache || m_originalImageSize.GetWidth() <= 0 || m_originalImageSize.GetHeight() <= 0)
11118 {
11119 m_imageCache = wxNullBitmap;
11120
11121 m_imageBlock.Load(image);
11122 if (!image.IsOk())
11123 return false;
11124
11125 m_originalImageSize = wxSize(image.GetWidth(), image.GetHeight());
11126 }
11127
11128 int width = m_originalImageSize.GetWidth();
11129 int height = m_originalImageSize.GetHeight();
11130
11131 int parentWidth = 0;
11132 int parentHeight = 0;
11133
11134 int maxWidth = -1;
11135 int maxHeight = -1;
11136
11137 wxSize sz = parentSize;
11138 if (sz == wxDefaultSize)
11139 {
11140 if (GetParent() && GetParent()->GetParent())
11141 sz = GetParent()->GetParent()->GetCachedSize();
11142 }
11143
11144 if (sz != wxDefaultSize)
11145 {
11146 wxRichTextBuffer* buffer = GetBuffer();
11147 if (buffer)
11148 {
11149 // Find the actual space available when margin is taken into account
11150 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11151 marginRect = wxRect(0, 0, sz.x, sz.y);
11152 if (GetParent() && GetParent()->GetParent())
11153 {
11154 buffer->GetBoxRects(dc, buffer, GetParent()->GetParent()->GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
11155 sz = contentRect.GetSize();
11156 }
11157
11158 // Use a minimum size to stop images becoming very small
11159 parentWidth = wxMax(100, sz.GetWidth());
11160 parentHeight = wxMax(100, sz.GetHeight());
11161
11162 if (buffer->GetRichTextCtrl())
11163 // Start with a maximum width of the control size, even if not specified by the content,
11164 // to minimize the amount of picture overlapping the right-hand side
11165 maxWidth = parentWidth;
11166 }
11167 }
11168
11169 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
11170 {
11171 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11172 width = (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth)/100.0);
11173 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11174 width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
11175 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11176 width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
11177 }
11178
11179 // Limit to max width
11180
11181 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
11182 {
11183 int mw = -1;
11184
11185 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11186 mw = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth)/100.0);
11187 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11188 mw = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
11189 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11190 mw = GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
11191
11192 // If we already have a smaller max width due to the constraints of the control size,
11193 // don't use the larger max width.
11194 if (mw != -1 && ((maxWidth == -1) || (mw < maxWidth)))
11195 maxWidth = mw;
11196 }
11197
11198 if (maxWidth > 0 && width > maxWidth)
11199 width = maxWidth;
11200
11201 // Preserve the aspect ratio
11202 if (width != m_originalImageSize.GetWidth())
11203 height = (int) (float(m_originalImageSize.GetHeight()) * (float(width)/float(m_originalImageSize.GetWidth())));
11204
11205 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
11206 {
11207 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11208 height = (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight)/100.0);
11209 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11210 height = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
11211 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11212 height = GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
11213
11214 // Preserve the aspect ratio
11215 if (height != m_originalImageSize.GetHeight())
11216 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
11217 }
11218
11219 // Limit to max height
11220
11221 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
11222 {
11223 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11224 maxHeight = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight)/100.0);
11225 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11226 maxHeight = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
11227 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11228 maxHeight = GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
11229 }
11230
11231 if (maxHeight > 0 && height > maxHeight)
11232 {
11233 height = maxHeight;
11234
11235 // Preserve the aspect ratio
11236 if (height != m_originalImageSize.GetHeight())
11237 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
11238 }
11239
11240 // Prevent the use of zero size
11241 width = wxMax(1, width);
11242 height = wxMax(1, height);
11243
11244 if (m_imageCache.IsOk() && m_imageCache.GetWidth() == width && m_imageCache.GetHeight() == height)
11245 {
11246 // Do nothing, we didn't need to change the image cache
11247 }
11248 else
11249 {
11250 if (!image.IsOk())
11251 {
11252 m_imageBlock.Load(image);
11253 if (!image.IsOk())
11254 return false;
11255 }
11256
11257 if (image.GetWidth() == width && image.GetHeight() == height)
11258 m_imageCache = wxBitmap(image);
11259 else
11260 {
11261 // If the original width and height is small, e.g. 400 or below,
11262 // scale up and then down to improve image quality. This can make
11263 // a big difference, with not much performance hit.
11264 int upscaleThreshold = 400;
11265 wxImage img;
11266 if (image.GetWidth() <= upscaleThreshold || image.GetHeight() <= upscaleThreshold)
11267 {
11268 img = image.Scale(image.GetWidth()*2, image.GetHeight()*2);
11269 img.Rescale(width, height, wxIMAGE_QUALITY_HIGH);
11270 }
11271 else
11272 img = image.Scale(width, height, wxIMAGE_QUALITY_HIGH);
11273 m_imageCache = wxBitmap(img);
11274 }
11275 }
11276
11277 return m_imageCache.IsOk();
11278 }
11279
11280 /// Draw the item
11281 bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
11282 {
11283 if (!IsShown())
11284 return true;
11285
11286 // Don't need cached size AFAIK
11287 // wxSize size = GetCachedSize();
11288 if (!LoadImageCache(dc))
11289 return false;
11290
11291 wxRichTextAttr attr(GetAttributes());
11292 context.ApplyVirtualAttributes(attr, this);
11293
11294 DrawBoxAttributes(dc, GetBuffer(), attr, wxRect(rect.GetPosition(), GetCachedSize()));
11295
11296 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11297 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11298 marginRect = rect; // outer rectangle, will calculate contentRect
11299 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
11300
11301 dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true);
11302
11303 if (selection.WithinSelection(GetRange().GetStart(), this))
11304 {
11305 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
11306 wxCheckSetPen(dc, *wxBLACK_PEN);
11307 dc.SetLogicalFunction(wxINVERT);
11308 dc.DrawRectangle(contentRect);
11309 dc.SetLogicalFunction(wxCOPY);
11310 }
11311
11312 return true;
11313 }
11314
11315 /// Lay the item out
11316 bool wxRichTextImage::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
11317 {
11318 if (!LoadImageCache(dc))
11319 return false;
11320
11321 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11322 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11323 contentRect = wxRect(wxPoint(0,0), imageSize);
11324
11325 wxRichTextAttr attr(GetAttributes());
11326 context.ApplyVirtualAttributes(attr, this);
11327
11328 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
11329
11330 wxSize overallSize = marginRect.GetSize();
11331
11332 SetCachedSize(overallSize);
11333 SetMaxSize(overallSize);
11334 SetMinSize(overallSize);
11335 SetPosition(rect.GetPosition());
11336
11337 return true;
11338 }
11339
11340 /// Get/set the object size for the given range. Returns false if the range
11341 /// is invalid for this object.
11342 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
11343 {
11344 if (!range.IsWithin(GetRange()))
11345 return false;
11346
11347 if (!((wxRichTextImage*)this)->LoadImageCache(dc, false, parentSize))
11348 {
11349 size.x = 0; size.y = 0;
11350 if (partialExtents)
11351 partialExtents->Add(0);
11352 return false;
11353 }
11354
11355 wxRichTextAttr attr(GetAttributes());
11356 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
11357
11358 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11359 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11360 contentRect = wxRect(wxPoint(0,0), imageSize);
11361 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
11362
11363 wxSize overallSize = marginRect.GetSize();
11364
11365 if (partialExtents)
11366 partialExtents->Add(overallSize.x);
11367
11368 size = overallSize;
11369
11370 return true;
11371 }
11372
11373 // Get the 'natural' size for an object. For an image, it would be the
11374 // image size.
11375 wxTextAttrSize wxRichTextImage::GetNaturalSize() const
11376 {
11377 wxTextAttrSize size;
11378 if (GetImageCache().IsOk())
11379 {
11380 size.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
11381 size.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS);
11382 }
11383 return size;
11384 }
11385
11386
11387 /// Copy
11388 void wxRichTextImage::Copy(const wxRichTextImage& obj)
11389 {
11390 wxRichTextObject::Copy(obj);
11391
11392 m_imageBlock = obj.m_imageBlock;
11393 m_originalImageSize = obj.m_originalImageSize;
11394 }
11395
11396 /// Edit properties via a GUI
11397 bool wxRichTextImage::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
11398 {
11399 wxRichTextObjectPropertiesDialog imageDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Picture Properties"));
11400 imageDlg.SetAttributes(GetAttributes());
11401
11402 if (imageDlg.ShowModal() == wxID_OK)
11403 {
11404 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
11405 // indeterminate in the object.
11406 imageDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
11407 return true;
11408 }
11409 else
11410 return false;
11411 }
11412
11413 /*!
11414 * Utilities
11415 *
11416 */
11417
11418 /// Compare two attribute objects
11419 bool wxTextAttrEq(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
11420 {
11421 return (attr1 == attr2);
11422 }
11423
11424 /// Compare tabs
11425 bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2)
11426 {
11427 if (tabs1.GetCount() != tabs2.GetCount())
11428 return false;
11429
11430 size_t i;
11431 for (i = 0; i < tabs1.GetCount(); i++)
11432 {
11433 if (tabs1[i] != tabs2[i])
11434 return false;
11435 }
11436 return true;
11437 }
11438
11439 bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
11440 {
11441 return destStyle.Apply(style, compareWith);
11442 }
11443
11444 // Remove attributes
11445 bool wxRichTextRemoveStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style)
11446 {
11447 return destStyle.RemoveStyle(style);
11448 }
11449
11450 /// Combine two bitlists, specifying the bits of interest with separate flags.
11451 bool wxRichTextCombineBitlists(int& valueA, int valueB, int& flagsA, int flagsB)
11452 {
11453 return wxRichTextAttr::CombineBitlists(valueA, valueB, flagsA, flagsB);
11454 }
11455
11456 /// Compare two bitlists
11457 bool wxRichTextBitlistsEqPartial(int valueA, int valueB, int flags)
11458 {
11459 return wxRichTextAttr::BitlistsEqPartial(valueA, valueB, flags);
11460 }
11461
11462 /// Split into paragraph and character styles
11463 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr& style, wxRichTextAttr& parStyle, wxRichTextAttr& charStyle)
11464 {
11465 return wxRichTextAttr::SplitParaCharStyles(style, parStyle, charStyle);
11466 }
11467
11468 /// Convert a decimal to Roman numerals
11469 wxString wxRichTextDecimalToRoman(long n)
11470 {
11471 static wxArrayInt decimalNumbers;
11472 static wxArrayString romanNumbers;
11473
11474 // Clean up arrays
11475 if (n == -1)
11476 {
11477 decimalNumbers.Clear();
11478 romanNumbers.Clear();
11479 return wxEmptyString;
11480 }
11481
11482 if (decimalNumbers.GetCount() == 0)
11483 {
11484 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
11485
11486 wxRichTextAddDecRom(1000, wxT("M"));
11487 wxRichTextAddDecRom(900, wxT("CM"));
11488 wxRichTextAddDecRom(500, wxT("D"));
11489 wxRichTextAddDecRom(400, wxT("CD"));
11490 wxRichTextAddDecRom(100, wxT("C"));
11491 wxRichTextAddDecRom(90, wxT("XC"));
11492 wxRichTextAddDecRom(50, wxT("L"));
11493 wxRichTextAddDecRom(40, wxT("XL"));
11494 wxRichTextAddDecRom(10, wxT("X"));
11495 wxRichTextAddDecRom(9, wxT("IX"));
11496 wxRichTextAddDecRom(5, wxT("V"));
11497 wxRichTextAddDecRom(4, wxT("IV"));
11498 wxRichTextAddDecRom(1, wxT("I"));
11499 }
11500
11501 int i = 0;
11502 wxString roman;
11503
11504 while (n > 0 && i < 13)
11505 {
11506 if (n >= decimalNumbers[i])
11507 {
11508 n -= decimalNumbers[i];
11509 roman += romanNumbers[i];
11510 }
11511 else
11512 {
11513 i ++;
11514 }
11515 }
11516 if (roman.IsEmpty())
11517 roman = wxT("0");
11518 return roman;
11519 }
11520
11521 /*!
11522 * wxRichTextFileHandler
11523 * Base class for file handlers
11524 */
11525
11526 IMPLEMENT_CLASS(wxRichTextFileHandler, wxObject)
11527
11528 #if wxUSE_FFILE && wxUSE_STREAMS
11529 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer *buffer, const wxString& filename)
11530 {
11531 wxFFileInputStream stream(filename);
11532 if (stream.IsOk())
11533 return LoadFile(buffer, stream);
11534
11535 return false;
11536 }
11537
11538 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer *buffer, const wxString& filename)
11539 {
11540 wxFFileOutputStream stream(filename);
11541 if (stream.IsOk())
11542 return SaveFile(buffer, stream);
11543
11544 return false;
11545 }
11546 #endif // wxUSE_FFILE && wxUSE_STREAMS
11547
11548 /// Can we handle this filename (if using files)? By default, checks the extension.
11549 bool wxRichTextFileHandler::CanHandle(const wxString& filename) const
11550 {
11551 wxString path, file, ext;
11552 wxFileName::SplitPath(filename, & path, & file, & ext);
11553
11554 return (ext.Lower() == GetExtension());
11555 }
11556
11557 /*!
11558 * wxRichTextTextHandler
11559 * Plain text handler
11560 */
11561
11562 IMPLEMENT_CLASS(wxRichTextPlainTextHandler, wxRichTextFileHandler)
11563
11564 #if wxUSE_STREAMS
11565 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
11566 {
11567 if (!stream.IsOk())
11568 return false;
11569
11570 wxString str;
11571 int lastCh = 0;
11572
11573 while (!stream.Eof())
11574 {
11575 int ch = stream.GetC();
11576
11577 if (!stream.Eof())
11578 {
11579 if (ch == 10 && lastCh != 13)
11580 str += wxT('\n');
11581
11582 if (ch > 0 && ch != 10)
11583 str += wxChar(ch);
11584
11585 lastCh = ch;
11586 }
11587 }
11588
11589 buffer->ResetAndClearCommands();
11590 buffer->Clear();
11591 buffer->AddParagraphs(str);
11592 buffer->UpdateRanges();
11593
11594 return true;
11595 }
11596
11597 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
11598 {
11599 if (!stream.IsOk())
11600 return false;
11601
11602 wxString text = buffer->GetText();
11603
11604 wxString newLine = wxRichTextLineBreakChar;
11605 text.Replace(newLine, wxT("\n"));
11606
11607 wxCharBuffer buf = text.ToAscii();
11608
11609 stream.Write((const char*) buf, text.length());
11610 return true;
11611 }
11612 #endif // wxUSE_STREAMS
11613
11614 /*
11615 * Stores information about an image, in binary in-memory form
11616 */
11617
11618 wxRichTextImageBlock::wxRichTextImageBlock()
11619 {
11620 Init();
11621 }
11622
11623 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock& block):wxObject()
11624 {
11625 Init();
11626 Copy(block);
11627 }
11628
11629 wxRichTextImageBlock::~wxRichTextImageBlock()
11630 {
11631 wxDELETEA(m_data);
11632 }
11633
11634 void wxRichTextImageBlock::Init()
11635 {
11636 m_data = NULL;
11637 m_dataSize = 0;
11638 m_imageType = wxBITMAP_TYPE_INVALID;
11639 }
11640
11641 void wxRichTextImageBlock::Clear()
11642 {
11643 wxDELETEA(m_data);
11644 m_dataSize = 0;
11645 m_imageType = wxBITMAP_TYPE_INVALID;
11646 }
11647
11648
11649 // Load the original image into a memory block.
11650 // If the image is not a JPEG, we must convert it into a JPEG
11651 // to conserve space.
11652 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11653 // load the image a 2nd time.
11654
11655 bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, wxBitmapType imageType,
11656 wxImage& image, bool convertToJPEG)
11657 {
11658 m_imageType = imageType;
11659
11660 wxString filenameToRead(filename);
11661 bool removeFile = false;
11662
11663 if (imageType == wxBITMAP_TYPE_INVALID)
11664 return false; // Could not determine image type
11665
11666 if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
11667 {
11668 wxString tempFile =
11669 wxFileName::CreateTempFileName(_("image"));
11670
11671 wxASSERT(!tempFile.IsEmpty());
11672
11673 image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
11674 filenameToRead = tempFile;
11675 removeFile = true;
11676
11677 m_imageType = wxBITMAP_TYPE_JPEG;
11678 }
11679 wxFile file;
11680 if (!file.Open(filenameToRead))
11681 return false;
11682
11683 m_dataSize = (size_t) file.Length();
11684 file.Close();
11685
11686 if (m_data)
11687 delete[] m_data;
11688 m_data = ReadBlock(filenameToRead, m_dataSize);
11689
11690 if (removeFile)
11691 wxRemoveFile(filenameToRead);
11692
11693 return (m_data != NULL);
11694 }
11695
11696 // Make an image block from the wxImage in the given
11697 // format.
11698 bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, wxBitmapType imageType, int quality)
11699 {
11700 image.SetOption(wxT("quality"), quality);
11701
11702 if (imageType == wxBITMAP_TYPE_INVALID)
11703 return false; // Could not determine image type
11704
11705 return DoMakeImageBlock(image, imageType);
11706 }
11707
11708 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11709 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage& image, wxBitmapType imageType)
11710 {
11711 if (imageType == wxBITMAP_TYPE_INVALID)
11712 return false; // Could not determine image type
11713
11714 return DoMakeImageBlock(image, imageType);
11715 }
11716
11717 // Makes the image block
11718 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage& image, wxBitmapType imageType)
11719 {
11720 wxMemoryOutputStream memStream;
11721 if (!image.SaveFile(memStream, imageType))
11722 {
11723 return false;
11724 }
11725
11726 unsigned char* block = new unsigned char[memStream.GetSize()];
11727 if (!block)
11728 return false;
11729
11730 if (m_data)
11731 delete[] m_data;
11732 m_data = block;
11733
11734 m_imageType = imageType;
11735 m_dataSize = memStream.GetSize();
11736
11737 memStream.CopyTo(m_data, m_dataSize);
11738
11739 return (m_data != NULL);
11740 }
11741
11742 // Write to a file
11743 bool wxRichTextImageBlock::Write(const wxString& filename)
11744 {
11745 return WriteBlock(filename, m_data, m_dataSize);
11746 }
11747
11748 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock& block)
11749 {
11750 m_imageType = block.m_imageType;
11751 wxDELETEA(m_data);
11752 m_dataSize = block.m_dataSize;
11753 if (m_dataSize == 0)
11754 return;
11755
11756 m_data = new unsigned char[m_dataSize];
11757 unsigned int i;
11758 for (i = 0; i < m_dataSize; i++)
11759 m_data[i] = block.m_data[i];
11760 }
11761
11762 //// Operators
11763 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock& block)
11764 {
11765 Copy(block);
11766 }
11767
11768 // Load a wxImage from the block
11769 bool wxRichTextImageBlock::Load(wxImage& image)
11770 {
11771 if (!m_data)
11772 return false;
11773
11774 // Read in the image.
11775 #if wxUSE_STREAMS
11776 wxMemoryInputStream mstream(m_data, m_dataSize);
11777 bool success = image.LoadFile(mstream, GetImageType());
11778 #else
11779 wxString tempFile = wxFileName::CreateTempFileName(_("image"));
11780 wxASSERT(!tempFile.IsEmpty());
11781
11782 if (!WriteBlock(tempFile, m_data, m_dataSize))
11783 {
11784 return false;
11785 }
11786 success = image.LoadFile(tempFile, GetImageType());
11787 wxRemoveFile(tempFile);
11788 #endif
11789
11790 return success;
11791 }
11792
11793 // Write data in hex to a stream
11794 bool wxRichTextImageBlock::WriteHex(wxOutputStream& stream)
11795 {
11796 if (m_dataSize == 0)
11797 return true;
11798
11799 int bufSize = 100000;
11800 if (int(2*m_dataSize) < bufSize)
11801 bufSize = 2*m_dataSize;
11802 char* buf = new char[bufSize+1];
11803
11804 int left = m_dataSize;
11805 int n, i, j;
11806 j = 0;
11807 while (left > 0)
11808 {
11809 if (left*2 > bufSize)
11810 {
11811 n = bufSize; left -= (bufSize/2);
11812 }
11813 else
11814 {
11815 n = left*2; left = 0;
11816 }
11817
11818 char* b = buf;
11819 for (i = 0; i < (n/2); i++)
11820 {
11821 wxDecToHex(m_data[j], b, b+1);
11822 b += 2; j ++;
11823 }
11824
11825 buf[n] = 0;
11826 stream.Write((const char*) buf, n);
11827 }
11828 delete[] buf;
11829 return true;
11830 }
11831
11832 // Read data in hex from a stream
11833 bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, wxBitmapType imageType)
11834 {
11835 int dataSize = length/2;
11836
11837 if (m_data)
11838 delete[] m_data;
11839
11840 // create a null terminated temporary string:
11841 char str[3];
11842 str[2] = '\0';
11843
11844 m_data = new unsigned char[dataSize];
11845 int i;
11846 for (i = 0; i < dataSize; i ++)
11847 {
11848 str[0] = (char)stream.GetC();
11849 str[1] = (char)stream.GetC();
11850
11851 m_data[i] = (unsigned char)wxHexToDec(str);
11852 }
11853
11854 m_dataSize = dataSize;
11855 m_imageType = imageType;
11856
11857 return true;
11858 }
11859
11860 // Allocate and read from stream as a block of memory
11861 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream& stream, size_t size)
11862 {
11863 unsigned char* block = new unsigned char[size];
11864 if (!block)
11865 return NULL;
11866
11867 stream.Read(block, size);
11868
11869 return block;
11870 }
11871
11872 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString& filename, size_t size)
11873 {
11874 wxFileInputStream stream(filename);
11875 if (!stream.IsOk())
11876 return NULL;
11877
11878 return ReadBlock(stream, size);
11879 }
11880
11881 // Write memory block to stream
11882 bool wxRichTextImageBlock::WriteBlock(wxOutputStream& stream, unsigned char* block, size_t size)
11883 {
11884 stream.Write((void*) block, size);
11885 return stream.IsOk();
11886
11887 }
11888
11889 // Write memory block to file
11890 bool wxRichTextImageBlock::WriteBlock(const wxString& filename, unsigned char* block, size_t size)
11891 {
11892 wxFileOutputStream outStream(filename);
11893 if (!outStream.IsOk())
11894 return false;
11895
11896 return WriteBlock(outStream, block, size);
11897 }
11898
11899 // Gets the extension for the block's type
11900 wxString wxRichTextImageBlock::GetExtension() const
11901 {
11902 wxImageHandler* handler = wxImage::FindHandler(GetImageType());
11903 if (handler)
11904 return handler->GetExtension();
11905 else
11906 return wxEmptyString;
11907 }
11908
11909 #if wxUSE_DATAOBJ
11910
11911 /*!
11912 * The data object for a wxRichTextBuffer
11913 */
11914
11915 const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxRichText");
11916
11917 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer)
11918 {
11919 m_richTextBuffer = richTextBuffer;
11920
11921 // this string should uniquely identify our format, but is otherwise
11922 // arbitrary
11923 m_formatRichTextBuffer.SetId(GetRichTextBufferFormatId());
11924
11925 SetFormat(m_formatRichTextBuffer);
11926 }
11927
11928 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11929 {
11930 delete m_richTextBuffer;
11931 }
11932
11933 // after a call to this function, the richTextBuffer is owned by the caller and it
11934 // is responsible for deleting it!
11935 wxRichTextBuffer* wxRichTextBufferDataObject::GetRichTextBuffer()
11936 {
11937 wxRichTextBuffer* richTextBuffer = m_richTextBuffer;
11938 m_richTextBuffer = NULL;
11939
11940 return richTextBuffer;
11941 }
11942
11943 wxDataFormat wxRichTextBufferDataObject::GetPreferredFormat(Direction WXUNUSED(dir)) const
11944 {
11945 return m_formatRichTextBuffer;
11946 }
11947
11948 size_t wxRichTextBufferDataObject::GetDataSize() const
11949 {
11950 if (!m_richTextBuffer)
11951 return 0;
11952
11953 wxString bufXML;
11954
11955 {
11956 wxStringOutputStream stream(& bufXML);
11957 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
11958 {
11959 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11960 return 0;
11961 }
11962 }
11963
11964 #if wxUSE_UNICODE
11965 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
11966 return strlen(buffer) + 1;
11967 #else
11968 return bufXML.Length()+1;
11969 #endif
11970 }
11971
11972 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf) const
11973 {
11974 if (!pBuf || !m_richTextBuffer)
11975 return false;
11976
11977 wxString bufXML;
11978
11979 {
11980 wxStringOutputStream stream(& bufXML);
11981 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
11982 {
11983 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11984 return 0;
11985 }
11986 }
11987
11988 #if wxUSE_UNICODE
11989 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
11990 size_t len = strlen(buffer);
11991 memcpy((char*) pBuf, (const char*) buffer, len);
11992 ((char*) pBuf)[len] = 0;
11993 #else
11994 size_t len = bufXML.Length();
11995 memcpy((char*) pBuf, (const char*) bufXML.c_str(), len);
11996 ((char*) pBuf)[len] = 0;
11997 #endif
11998
11999 return true;
12000 }
12001
12002 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len), const void *buf)
12003 {
12004 wxDELETE(m_richTextBuffer);
12005
12006 wxString bufXML((const char*) buf, wxConvUTF8);
12007
12008 m_richTextBuffer = new wxRichTextBuffer;
12009
12010 wxStringInputStream stream(bufXML);
12011 if (!m_richTextBuffer->LoadFile(stream, wxRICHTEXT_TYPE_XML))
12012 {
12013 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
12014
12015 wxDELETE(m_richTextBuffer);
12016
12017 return false;
12018 }
12019 return true;
12020 }
12021
12022 #endif
12023 // wxUSE_DATAOBJ
12024
12025
12026 /*
12027 * wxRichTextFontTable
12028 * Manages quick access to a pool of fonts for rendering rich text
12029 */
12030
12031 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont, wxRichTextFontTableHashMap, class WXDLLIMPEXP_RICHTEXT);
12032
12033 class wxRichTextFontTableData: public wxObjectRefData
12034 {
12035 public:
12036 wxRichTextFontTableData() {}
12037
12038 wxFont FindFont(const wxRichTextAttr& fontSpec, double fontScale);
12039
12040 wxRichTextFontTableHashMap m_hashMap;
12041 };
12042
12043 wxFont wxRichTextFontTableData::FindFont(const wxRichTextAttr& fontSpec, double fontScale)
12044 {
12045 wxString facename(fontSpec.GetFontFaceName());
12046
12047 int fontSize = fontSpec.GetFontSize();
12048 if (fontScale != 1.0)
12049 fontSize = (int) ((double(fontSize) * fontScale) + 0.5);
12050
12051 wxString units;
12052 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
12053 units = wxT("px");
12054 else
12055 units = wxT("pt");
12056 wxString spec = wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
12057 fontSize, units.c_str(), fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), (int) fontSpec.GetFontUnderlined(), (int) fontSpec.GetFontStrikethrough(),
12058 facename.c_str(), (int) fontSpec.GetFontEncoding());
12059
12060 wxRichTextFontTableHashMap::iterator entry = m_hashMap.find(spec);
12061 if ( entry == m_hashMap.end() )
12062 {
12063 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
12064 {
12065 wxFont font(wxSize(0, fontSize), wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename);
12066 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
12067 font.SetStrikethrough(true);
12068 m_hashMap[spec] = font;
12069 return font;
12070 }
12071 else
12072 {
12073 wxFont font(fontSize, wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename.c_str());
12074 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
12075 font.SetStrikethrough(true);
12076
12077 m_hashMap[spec] = font;
12078 return font;
12079 }
12080 }
12081 else
12082 {
12083 return entry->second;
12084 }
12085 }
12086
12087 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable, wxObject)
12088
12089 wxRichTextFontTable::wxRichTextFontTable()
12090 {
12091 m_refData = new wxRichTextFontTableData;
12092 m_fontScale = 1.0;
12093 }
12094
12095 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table)
12096 : wxObject()
12097 {
12098 (*this) = table;
12099 }
12100
12101 wxRichTextFontTable::~wxRichTextFontTable()
12102 {
12103 UnRef();
12104 }
12105
12106 bool wxRichTextFontTable::operator == (const wxRichTextFontTable& table) const
12107 {
12108 return (m_refData == table.m_refData);
12109 }
12110
12111 void wxRichTextFontTable::operator= (const wxRichTextFontTable& table)
12112 {
12113 Ref(table);
12114 m_fontScale = table.m_fontScale;
12115 }
12116
12117 wxFont wxRichTextFontTable::FindFont(const wxRichTextAttr& fontSpec)
12118 {
12119 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
12120 if (data)
12121 return data->FindFont(fontSpec, m_fontScale);
12122 else
12123 return wxFont();
12124 }
12125
12126 void wxRichTextFontTable::Clear()
12127 {
12128 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
12129 if (data)
12130 data->m_hashMap.clear();
12131 }
12132
12133 void wxRichTextFontTable::SetFontScale(double fontScale)
12134 {
12135 if (fontScale != m_fontScale)
12136 Clear();
12137 m_fontScale = fontScale;
12138 }
12139
12140 // wxTextBoxAttr
12141
12142 void wxTextBoxAttr::Reset()
12143 {
12144 m_flags = 0;
12145 m_floatMode = wxTEXT_BOX_ATTR_FLOAT_NONE;
12146 m_clearMode = wxTEXT_BOX_ATTR_CLEAR_NONE;
12147 m_collapseMode = wxTEXT_BOX_ATTR_COLLAPSE_NONE;
12148 m_verticalAlignment = wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE;
12149 m_boxStyleName = wxEmptyString;
12150
12151 m_margins.Reset();
12152 m_padding.Reset();
12153 m_position.Reset();
12154
12155 m_size.Reset();
12156 m_minSize.Reset();
12157 m_maxSize.Reset();
12158
12159 m_border.Reset();
12160 m_outline.Reset();
12161 }
12162
12163 // Equality test
12164 bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const
12165 {
12166 return (
12167 m_flags == attr.m_flags &&
12168 m_floatMode == attr.m_floatMode &&
12169 m_clearMode == attr.m_clearMode &&
12170 m_collapseMode == attr.m_collapseMode &&
12171 m_verticalAlignment == attr.m_verticalAlignment &&
12172
12173 m_margins == attr.m_margins &&
12174 m_padding == attr.m_padding &&
12175 m_position == attr.m_position &&
12176
12177 m_size == attr.m_size &&
12178 m_minSize == attr.m_minSize &&
12179 m_maxSize == attr.m_maxSize &&
12180
12181 m_border == attr.m_border &&
12182 m_outline == attr.m_outline &&
12183
12184 m_boxStyleName == attr.m_boxStyleName
12185 );
12186 }
12187
12188 // Partial equality test
12189 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr, bool weakTest) const
12190 {
12191 if (!weakTest &&
12192 ((!HasFloatMode() && attr.HasFloatMode()) ||
12193 (!HasClearMode() && attr.HasClearMode()) ||
12194 (!HasCollapseBorders() && attr.HasCollapseBorders()) ||
12195 (!HasVerticalAlignment() && attr.HasVerticalAlignment()) ||
12196 (!HasBoxStyleName() && attr.HasBoxStyleName())))
12197 {
12198 return false;
12199 }
12200 if (attr.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr.GetFloatMode()))
12201 return false;
12202
12203 if (attr.HasClearMode() && HasClearMode() && (GetClearMode() != attr.GetClearMode()))
12204 return false;
12205
12206 if (attr.HasCollapseBorders() && HasCollapseBorders() && (attr.GetCollapseBorders() != GetCollapseBorders()))
12207 return false;
12208
12209 if (attr.HasVerticalAlignment() && HasVerticalAlignment() && (attr.GetVerticalAlignment() != GetVerticalAlignment()))
12210 return false;
12211
12212 if (attr.HasBoxStyleName() && HasBoxStyleName() && (attr.GetBoxStyleName() != GetBoxStyleName()))
12213 return false;
12214
12215 // Position
12216
12217 if (!m_position.EqPartial(attr.m_position, weakTest))
12218 return false;
12219
12220 // Size
12221
12222 if (!m_size.EqPartial(attr.m_size, weakTest))
12223 return false;
12224 if (!m_minSize.EqPartial(attr.m_minSize, weakTest))
12225 return false;
12226 if (!m_maxSize.EqPartial(attr.m_maxSize, weakTest))
12227 return false;
12228
12229 // Margins
12230
12231 if (!m_margins.EqPartial(attr.m_margins, weakTest))
12232 return false;
12233
12234 // Padding
12235
12236 if (!m_padding.EqPartial(attr.m_padding, weakTest))
12237 return false;
12238
12239 // Border
12240
12241 if (!GetBorder().EqPartial(attr.GetBorder(), weakTest))
12242 return false;
12243
12244 // Outline
12245
12246 if (!GetOutline().EqPartial(attr.GetOutline(), weakTest))
12247 return false;
12248
12249 return true;
12250 }
12251
12252 // Merges the given attributes. If compareWith
12253 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12254 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12255 bool wxTextBoxAttr::Apply(const wxTextBoxAttr& attr, const wxTextBoxAttr* compareWith)
12256 {
12257 if (attr.HasFloatMode())
12258 {
12259 if (!(compareWith && compareWith->HasFloatMode() && compareWith->GetFloatMode() == attr.GetFloatMode()))
12260 SetFloatMode(attr.GetFloatMode());
12261 }
12262
12263 if (attr.HasClearMode())
12264 {
12265 if (!(compareWith && compareWith->HasClearMode() && compareWith->GetClearMode() == attr.GetClearMode()))
12266 SetClearMode(attr.GetClearMode());
12267 }
12268
12269 if (attr.HasCollapseBorders())
12270 {
12271 if (!(compareWith && compareWith->HasCollapseBorders() && compareWith->GetCollapseBorders() == attr.GetCollapseBorders()))
12272 SetCollapseBorders(attr.GetCollapseBorders());
12273 }
12274
12275 if (attr.HasVerticalAlignment())
12276 {
12277 if (!(compareWith && compareWith->HasVerticalAlignment() && compareWith->GetVerticalAlignment() == attr.GetVerticalAlignment()))
12278 SetVerticalAlignment(attr.GetVerticalAlignment());
12279 }
12280
12281 if (attr.HasBoxStyleName())
12282 {
12283 if (!(compareWith && compareWith->HasBoxStyleName() && compareWith->GetBoxStyleName() == attr.GetBoxStyleName()))
12284 SetBoxStyleName(attr.GetBoxStyleName());
12285 }
12286
12287 m_margins.Apply(attr.m_margins, compareWith ? (& attr.m_margins) : (const wxTextAttrDimensions*) NULL);
12288 m_padding.Apply(attr.m_padding, compareWith ? (& attr.m_padding) : (const wxTextAttrDimensions*) NULL);
12289 m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextAttrDimensions*) NULL);
12290
12291 m_size.Apply(attr.m_size, compareWith ? (& attr.m_size) : (const wxTextAttrSize*) NULL);
12292 m_minSize.Apply(attr.m_minSize, compareWith ? (& attr.m_minSize) : (const wxTextAttrSize*) NULL);
12293 m_maxSize.Apply(attr.m_maxSize, compareWith ? (& attr.m_maxSize) : (const wxTextAttrSize*) NULL);
12294
12295 m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextAttrBorders*) NULL);
12296 m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextAttrBorders*) NULL);
12297
12298 return true;
12299 }
12300
12301 // Remove specified attributes from this object
12302 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr& attr)
12303 {
12304 if (attr.HasFloatMode())
12305 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
12306
12307 if (attr.HasClearMode())
12308 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
12309
12310 if (attr.HasCollapseBorders())
12311 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12312
12313 if (attr.HasVerticalAlignment())
12314 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12315
12316 if (attr.HasBoxStyleName())
12317 {
12318 SetBoxStyleName(wxEmptyString);
12319 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12320 }
12321
12322 m_margins.RemoveStyle(attr.m_margins);
12323 m_padding.RemoveStyle(attr.m_padding);
12324 m_position.RemoveStyle(attr.m_position);
12325
12326 m_size.RemoveStyle(attr.m_size);
12327 m_minSize.RemoveStyle(attr.m_minSize);
12328 m_maxSize.RemoveStyle(attr.m_maxSize);
12329
12330 m_border.RemoveStyle(attr.m_border);
12331 m_outline.RemoveStyle(attr.m_outline);
12332
12333 return true;
12334 }
12335
12336 // Collects the attributes that are common to a range of content, building up a note of
12337 // which attributes are absent in some objects and which clash in some objects.
12338 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBoxAttr& clashingAttr, wxTextBoxAttr& absentAttr)
12339 {
12340 if (attr.HasFloatMode())
12341 {
12342 if (!clashingAttr.HasFloatMode() && !absentAttr.HasFloatMode())
12343 {
12344 if (HasFloatMode())
12345 {
12346 if (GetFloatMode() != attr.GetFloatMode())
12347 {
12348 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
12349 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
12350 }
12351 }
12352 else
12353 SetFloatMode(attr.GetFloatMode());
12354 }
12355 }
12356 else
12357 absentAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
12358
12359 if (attr.HasClearMode())
12360 {
12361 if (!clashingAttr.HasClearMode() && !absentAttr.HasClearMode())
12362 {
12363 if (HasClearMode())
12364 {
12365 if (GetClearMode() != attr.GetClearMode())
12366 {
12367 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
12368 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
12369 }
12370 }
12371 else
12372 SetClearMode(attr.GetClearMode());
12373 }
12374 }
12375 else
12376 absentAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
12377
12378 if (attr.HasCollapseBorders())
12379 {
12380 if (!clashingAttr.HasCollapseBorders() && !absentAttr.HasCollapseBorders())
12381 {
12382 if (HasCollapseBorders())
12383 {
12384 if (GetCollapseBorders() != attr.GetCollapseBorders())
12385 {
12386 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12387 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12388 }
12389 }
12390 else
12391 SetCollapseBorders(attr.GetCollapseBorders());
12392 }
12393 }
12394 else
12395 absentAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12396
12397 if (attr.HasVerticalAlignment())
12398 {
12399 if (!clashingAttr.HasVerticalAlignment() && !absentAttr.HasVerticalAlignment())
12400 {
12401 if (HasVerticalAlignment())
12402 {
12403 if (GetVerticalAlignment() != attr.GetVerticalAlignment())
12404 {
12405 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12406 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12407 }
12408 }
12409 else
12410 SetVerticalAlignment(attr.GetVerticalAlignment());
12411 }
12412 }
12413 else
12414 absentAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12415
12416 if (attr.HasBoxStyleName())
12417 {
12418 if (!clashingAttr.HasBoxStyleName() && !absentAttr.HasBoxStyleName())
12419 {
12420 if (HasBoxStyleName())
12421 {
12422 if (GetBoxStyleName() != attr.GetBoxStyleName())
12423 {
12424 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12425 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12426 }
12427 }
12428 else
12429 SetBoxStyleName(attr.GetBoxStyleName());
12430 }
12431 }
12432 else
12433 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12434
12435 m_margins.CollectCommonAttributes(attr.m_margins, clashingAttr.m_margins, absentAttr.m_margins);
12436 m_padding.CollectCommonAttributes(attr.m_padding, clashingAttr.m_padding, absentAttr.m_padding);
12437 m_position.CollectCommonAttributes(attr.m_position, clashingAttr.m_position, absentAttr.m_position);
12438
12439 m_size.CollectCommonAttributes(attr.m_size, clashingAttr.m_size, absentAttr.m_size);
12440 m_minSize.CollectCommonAttributes(attr.m_minSize, clashingAttr.m_minSize, absentAttr.m_minSize);
12441 m_maxSize.CollectCommonAttributes(attr.m_maxSize, clashingAttr.m_maxSize, absentAttr.m_maxSize);
12442
12443 m_border.CollectCommonAttributes(attr.m_border, clashingAttr.m_border, absentAttr.m_border);
12444 m_outline.CollectCommonAttributes(attr.m_outline, clashingAttr.m_outline, absentAttr.m_outline);
12445 }
12446
12447 bool wxTextBoxAttr::IsDefault() const
12448 {
12449 return GetFlags() == 0 && !m_border.IsValid() && !m_outline.IsValid() &&
12450 !m_size.IsValid() && !m_minSize.IsValid() && !m_maxSize.IsValid() &&
12451 !m_position.IsValid() && !m_padding.IsValid() && !m_margins.IsValid();
12452 }
12453
12454 // wxRichTextAttr
12455
12456 void wxRichTextAttr::Copy(const wxRichTextAttr& attr)
12457 {
12458 wxTextAttr::Copy(attr);
12459
12460 m_textBoxAttr = attr.m_textBoxAttr;
12461 }
12462
12463 bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const
12464 {
12465 if (!(wxTextAttr::operator==(attr)))
12466 return false;
12467
12468 return (m_textBoxAttr == attr.m_textBoxAttr);
12469 }
12470
12471 // Partial equality test
12472 bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr, bool weakTest) const
12473 {
12474 if (!(wxTextAttr::EqPartial(attr, weakTest)))
12475 return false;
12476
12477 return m_textBoxAttr.EqPartial(attr.m_textBoxAttr, weakTest);
12478 }
12479
12480 // Merges the given attributes. If compareWith
12481 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12482 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12483 bool wxRichTextAttr::Apply(const wxRichTextAttr& style, const wxRichTextAttr* compareWith)
12484 {
12485 wxTextAttr::Apply(style, compareWith);
12486
12487 return m_textBoxAttr.Apply(style.m_textBoxAttr, compareWith ? (& compareWith->m_textBoxAttr) : (const wxTextBoxAttr*) NULL);
12488 }
12489
12490 // Remove specified attributes from this object
12491 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr& attr)
12492 {
12493 wxTextAttr::RemoveStyle(*this, attr);
12494
12495 return m_textBoxAttr.RemoveStyle(attr.m_textBoxAttr);
12496 }
12497
12498 // Collects the attributes that are common to a range of content, building up a note of
12499 // which attributes are absent in some objects and which clash in some objects.
12500 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr& attr, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
12501 {
12502 wxTextAttrCollectCommonAttributes(*this, attr, clashingAttr, absentAttr);
12503
12504 m_textBoxAttr.CollectCommonAttributes(attr.m_textBoxAttr, clashingAttr.m_textBoxAttr, absentAttr.m_textBoxAttr);
12505 }
12506
12507 // Partial equality test
12508 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border, bool weakTest) const
12509 {
12510 if (!weakTest &&
12511 ((!HasStyle() && border.HasStyle()) ||
12512 (!HasColour() && border.HasColour()) ||
12513 (!HasWidth() && border.HasWidth())))
12514 {
12515 return false;
12516 }
12517
12518 if (border.HasStyle() && HasStyle() && (border.GetStyle() != GetStyle()))
12519 return false;
12520
12521 if (border.HasColour() && HasColour() && (border.GetColourLong() != GetColourLong()))
12522 return false;
12523
12524 if (border.HasWidth() && HasWidth() && !(border.GetWidth() == GetWidth()))
12525 return false;
12526
12527 return true;
12528 }
12529
12530 // Apply border to 'this', but not if the same as compareWith
12531 bool wxTextAttrBorder::Apply(const wxTextAttrBorder& border, const wxTextAttrBorder* compareWith)
12532 {
12533 if (border.HasStyle())
12534 {
12535 if (!(compareWith && (border.GetStyle() == compareWith->GetStyle())))
12536 SetStyle(border.GetStyle());
12537 }
12538 if (border.HasColour())
12539 {
12540 if (!(compareWith && (border.GetColourLong() == compareWith->GetColourLong())))
12541 SetColour(border.GetColourLong());
12542 }
12543 if (border.HasWidth())
12544 {
12545 if (!(compareWith && (border.GetWidth() == compareWith->GetWidth())))
12546 SetWidth(border.GetWidth());
12547 }
12548
12549 return true;
12550 }
12551
12552 // Remove specified attributes from this object
12553 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder& attr)
12554 {
12555 if (attr.HasStyle() && HasStyle())
12556 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE);
12557 if (attr.HasColour() && HasColour())
12558 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR);
12559 if (attr.HasWidth() && HasWidth())
12560 m_borderWidth.Reset();
12561
12562 return true;
12563 }
12564
12565 // Collects the attributes that are common to a range of content, building up a note of
12566 // which attributes are absent in some objects and which clash in some objects.
12567 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder& attr, wxTextAttrBorder& clashingAttr, wxTextAttrBorder& absentAttr)
12568 {
12569 if (attr.HasStyle())
12570 {
12571 if (!clashingAttr.HasStyle() && !absentAttr.HasStyle())
12572 {
12573 if (HasStyle())
12574 {
12575 if (GetStyle() != attr.GetStyle())
12576 {
12577 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12578 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12579 }
12580 }
12581 else
12582 SetStyle(attr.GetStyle());
12583 }
12584 }
12585 else
12586 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12587
12588 if (attr.HasColour())
12589 {
12590 if (!clashingAttr.HasColour() && !absentAttr.HasColour())
12591 {
12592 if (HasColour())
12593 {
12594 if (GetColour() != attr.GetColour())
12595 {
12596 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12597 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12598 }
12599 }
12600 else
12601 SetColour(attr.GetColourLong());
12602 }
12603 }
12604 else
12605 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12606
12607 m_borderWidth.CollectCommonAttributes(attr.m_borderWidth, clashingAttr.m_borderWidth, absentAttr.m_borderWidth);
12608 }
12609
12610 // Partial equality test
12611 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders, bool weakTest) const
12612 {
12613 return m_left.EqPartial(borders.m_left, weakTest) && m_right.EqPartial(borders.m_right, weakTest) &&
12614 m_top.EqPartial(borders.m_top, weakTest) && m_bottom.EqPartial(borders.m_bottom, weakTest);
12615 }
12616
12617 // Apply border to 'this', but not if the same as compareWith
12618 bool wxTextAttrBorders::Apply(const wxTextAttrBorders& borders, const wxTextAttrBorders* compareWith)
12619 {
12620 m_left.Apply(borders.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrBorder*) NULL);
12621 m_right.Apply(borders.m_right, compareWith ? (& compareWith->m_right) : (const wxTextAttrBorder*) NULL);
12622 m_top.Apply(borders.m_top, compareWith ? (& compareWith->m_top) : (const wxTextAttrBorder*) NULL);
12623 m_bottom.Apply(borders.m_bottom, compareWith ? (& compareWith->m_bottom) : (const wxTextAttrBorder*) NULL);
12624 return true;
12625 }
12626
12627 // Remove specified attributes from this object
12628 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders& attr)
12629 {
12630 m_left.RemoveStyle(attr.m_left);
12631 m_right.RemoveStyle(attr.m_right);
12632 m_top.RemoveStyle(attr.m_top);
12633 m_bottom.RemoveStyle(attr.m_bottom);
12634 return true;
12635 }
12636
12637 // Collects the attributes that are common to a range of content, building up a note of
12638 // which attributes are absent in some objects and which clash in some objects.
12639 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders& attr, wxTextAttrBorders& clashingAttr, wxTextAttrBorders& absentAttr)
12640 {
12641 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12642 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12643 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12644 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12645 }
12646
12647 // Set style of all borders
12648 void wxTextAttrBorders::SetStyle(int style)
12649 {
12650 m_left.SetStyle(style);
12651 m_right.SetStyle(style);
12652 m_top.SetStyle(style);
12653 m_bottom.SetStyle(style);
12654 }
12655
12656 // Set colour of all borders
12657 void wxTextAttrBorders::SetColour(unsigned long colour)
12658 {
12659 m_left.SetColour(colour);
12660 m_right.SetColour(colour);
12661 m_top.SetColour(colour);
12662 m_bottom.SetColour(colour);
12663 }
12664
12665 void wxTextAttrBorders::SetColour(const wxColour& colour)
12666 {
12667 m_left.SetColour(colour);
12668 m_right.SetColour(colour);
12669 m_top.SetColour(colour);
12670 m_bottom.SetColour(colour);
12671 }
12672
12673 // Set width of all borders
12674 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension& width)
12675 {
12676 m_left.SetWidth(width);
12677 m_right.SetWidth(width);
12678 m_top.SetWidth(width);
12679 m_bottom.SetWidth(width);
12680 }
12681
12682 // Partial equality test
12683 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension& dim, bool weakTest) const
12684 {
12685 if (!weakTest && !IsValid() && dim.IsValid())
12686 return false;
12687
12688 if (dim.IsValid() && IsValid() && !((*this) == dim))
12689 return false;
12690 else
12691 return true;
12692 }
12693
12694 bool wxTextAttrDimension::Apply(const wxTextAttrDimension& dim, const wxTextAttrDimension* compareWith)
12695 {
12696 if (dim.IsValid())
12697 {
12698 if (!(compareWith && dim == (*compareWith)))
12699 (*this) = dim;
12700 }
12701
12702 return true;
12703 }
12704
12705 // Collects the attributes that are common to a range of content, building up a note of
12706 // which attributes are absent in some objects and which clash in some objects.
12707 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension& attr, wxTextAttrDimension& clashingAttr, wxTextAttrDimension& absentAttr)
12708 {
12709 if (attr.IsValid())
12710 {
12711 if (!clashingAttr.IsValid() && !absentAttr.IsValid())
12712 {
12713 if (IsValid())
12714 {
12715 if (!((*this) == attr))
12716 {
12717 clashingAttr.SetValid(true);
12718 SetValid(false);
12719 }
12720 }
12721 else
12722 (*this) = attr;
12723 }
12724 }
12725 else
12726 absentAttr.SetValid(true);
12727 }
12728
12729 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC& dc, double scale, const wxSize& parentSize)
12730 {
12731 m_ppi = dc.GetPPI().x; m_scale = scale; m_parentSize = parentSize;
12732 }
12733
12734 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi, double scale, const wxSize& parentSize)
12735 {
12736 m_ppi = ppi; m_scale = scale; m_parentSize = parentSize;
12737 }
12738
12739 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units) const
12740 {
12741 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi, units, m_scale);
12742 }
12743
12744 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels) const
12745 {
12746 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi, pixels, m_scale);
12747 }
12748
12749 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension& dim, int direction) const
12750 {
12751 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
12752 return ConvertTenthsMMToPixels(dim.GetValue());
12753 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
12754 return dim.GetValue();
12755 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
12756 {
12757 wxASSERT(m_parentSize != wxDefaultSize);
12758 if (direction == wxHORIZONTAL)
12759 return (int) (double(m_parentSize.x) * double(dim.GetValue()) / 100.0);
12760 else
12761 return (int) (double(m_parentSize.y) * double(dim.GetValue()) / 100.0);
12762 }
12763 else
12764 {
12765 wxASSERT(false);
12766 return 0;
12767 }
12768 }
12769
12770 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension& dim) const
12771 {
12772 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
12773 return dim.GetValue();
12774 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
12775 return ConvertPixelsToTenthsMM(dim.GetValue());
12776 else
12777 {
12778 wxASSERT(false);
12779 return 0;
12780 }
12781 }
12782
12783 // Partial equality test
12784 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims, bool weakTest) const
12785 {
12786 if (!m_left.EqPartial(dims.m_left, weakTest))
12787 return false;
12788
12789 if (!m_right.EqPartial(dims.m_right, weakTest))
12790 return false;
12791
12792 if (!m_top.EqPartial(dims.m_top, weakTest))
12793 return false;
12794
12795 if (!m_bottom.EqPartial(dims.m_bottom, weakTest))
12796 return false;
12797
12798 return true;
12799 }
12800
12801 // Apply border to 'this', but not if the same as compareWith
12802 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions& dims, const wxTextAttrDimensions* compareWith)
12803 {
12804 m_left.Apply(dims.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrDimension*) NULL);
12805 m_right.Apply(dims.m_right, compareWith ? (& compareWith->m_right): (const wxTextAttrDimension*) NULL);
12806 m_top.Apply(dims.m_top, compareWith ? (& compareWith->m_top): (const wxTextAttrDimension*) NULL);
12807 m_bottom.Apply(dims.m_bottom, compareWith ? (& compareWith->m_bottom): (const wxTextAttrDimension*) NULL);
12808
12809 return true;
12810 }
12811
12812 // Remove specified attributes from this object
12813 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions& attr)
12814 {
12815 if (attr.m_left.IsValid())
12816 m_left.Reset();
12817 if (attr.m_right.IsValid())
12818 m_right.Reset();
12819 if (attr.m_top.IsValid())
12820 m_top.Reset();
12821 if (attr.m_bottom.IsValid())
12822 m_bottom.Reset();
12823
12824 return true;
12825 }
12826
12827 // Collects the attributes that are common to a range of content, building up a note of
12828 // which attributes are absent in some objects and which clash in some objects.
12829 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions& attr, wxTextAttrDimensions& clashingAttr, wxTextAttrDimensions& absentAttr)
12830 {
12831 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12832 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12833 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12834 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12835 }
12836
12837 // Partial equality test
12838 bool wxTextAttrSize::EqPartial(const wxTextAttrSize& size, bool weakTest) const
12839 {
12840 if (!m_width.EqPartial(size.m_width, weakTest))
12841 return false;
12842
12843 if (!m_height.EqPartial(size.m_height, weakTest))
12844 return false;
12845
12846 return true;
12847 }
12848
12849 // Apply border to 'this', but not if the same as compareWith
12850 bool wxTextAttrSize::Apply(const wxTextAttrSize& size, const wxTextAttrSize* compareWith)
12851 {
12852 m_width.Apply(size.m_width, compareWith ? (& compareWith->m_width) : (const wxTextAttrDimension*) NULL);
12853 m_height.Apply(size.m_height, compareWith ? (& compareWith->m_height): (const wxTextAttrDimension*) NULL);
12854
12855 return true;
12856 }
12857
12858 // Remove specified attributes from this object
12859 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize& attr)
12860 {
12861 if (attr.m_width.IsValid())
12862 m_width.Reset();
12863 if (attr.m_height.IsValid())
12864 m_height.Reset();
12865
12866 return true;
12867 }
12868
12869 // Collects the attributes that are common to a range of content, building up a note of
12870 // which attributes are absent in some objects and which clash in some objects.
12871 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize& attr, wxTextAttrSize& clashingAttr, wxTextAttrSize& absentAttr)
12872 {
12873 m_width.CollectCommonAttributes(attr.m_width, clashingAttr.m_width, absentAttr.m_width);
12874 m_height.CollectCommonAttributes(attr.m_height, clashingAttr.m_height, absentAttr.m_height);
12875 }
12876
12877 // Collects the attributes that are common to a range of content, building up a note of
12878 // which attributes are absent in some objects and which clash in some objects.
12879 void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAttr& attr, wxTextAttr& clashingAttr, wxTextAttr& absentAttr)
12880 {
12881 absentAttr.SetFlags(absentAttr.GetFlags() | (~attr.GetFlags() & wxTEXT_ATTR_ALL));
12882 absentAttr.SetTextEffectFlags(absentAttr.GetTextEffectFlags() | (~attr.GetTextEffectFlags() & 0xFFFF));
12883
12884 long forbiddenFlags = clashingAttr.GetFlags()|absentAttr.GetFlags();
12885
12886 // If different font size units are being used, this is a clash.
12887 if (((attr.GetFlags() & wxTEXT_ATTR_FONT_SIZE) | (currentStyle.GetFlags() & wxTEXT_ATTR_FONT_SIZE)) == wxTEXT_ATTR_FONT_SIZE)
12888 {
12889 currentStyle.SetFontSize(0);
12890 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE);
12891 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE);
12892 }
12893 else
12894 {
12895 if (attr.HasFontPointSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_POINT_SIZE))
12896 {
12897 if (currentStyle.HasFontPointSize())
12898 {
12899 if (currentStyle.GetFontSize() != attr.GetFontSize())
12900 {
12901 // Clash of attr - mark as such
12902 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12903 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12904 }
12905 }
12906 else
12907 currentStyle.SetFontSize(attr.GetFontSize());
12908 }
12909 else if (!attr.HasFontPointSize() && currentStyle.HasFontPointSize())
12910 {
12911 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12912 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12913 }
12914
12915 if (attr.HasFontPixelSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_PIXEL_SIZE))
12916 {
12917 if (currentStyle.HasFontPixelSize())
12918 {
12919 if (currentStyle.GetFontSize() != attr.GetFontSize())
12920 {
12921 // Clash of attr - mark as such
12922 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12923 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12924 }
12925 }
12926 else
12927 currentStyle.SetFontPixelSize(attr.GetFontSize());
12928 }
12929 else if (!attr.HasFontPixelSize() && currentStyle.HasFontPixelSize())
12930 {
12931 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12932 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12933 }
12934 }
12935
12936 if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC))
12937 {
12938 if (currentStyle.HasFontItalic())
12939 {
12940 if (currentStyle.GetFontStyle() != attr.GetFontStyle())
12941 {
12942 // Clash of attr - mark as such
12943 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
12944 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
12945 }
12946 }
12947 else
12948 currentStyle.SetFontStyle(attr.GetFontStyle());
12949 }
12950 else if (!attr.HasFontItalic() && currentStyle.HasFontItalic())
12951 {
12952 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
12953 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
12954 }
12955
12956 if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY))
12957 {
12958 if (currentStyle.HasFontFamily())
12959 {
12960 if (currentStyle.GetFontFamily() != attr.GetFontFamily())
12961 {
12962 // Clash of attr - mark as such
12963 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
12964 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
12965 }
12966 }
12967 else
12968 currentStyle.SetFontFamily(attr.GetFontFamily());
12969 }
12970 else if (!attr.HasFontFamily() && currentStyle.HasFontFamily())
12971 {
12972 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
12973 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
12974 }
12975
12976 if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT))
12977 {
12978 if (currentStyle.HasFontWeight())
12979 {
12980 if (currentStyle.GetFontWeight() != attr.GetFontWeight())
12981 {
12982 // Clash of attr - mark as such
12983 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
12984 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
12985 }
12986 }
12987 else
12988 currentStyle.SetFontWeight(attr.GetFontWeight());
12989 }
12990 else if (!attr.HasFontWeight() && currentStyle.HasFontWeight())
12991 {
12992 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
12993 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
12994 }
12995
12996 if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE))
12997 {
12998 if (currentStyle.HasFontFaceName())
12999 {
13000 wxString faceName1(currentStyle.GetFontFaceName());
13001 wxString faceName2(attr.GetFontFaceName());
13002
13003 if (faceName1 != faceName2)
13004 {
13005 // Clash of attr - mark as such
13006 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
13007 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
13008 }
13009 }
13010 else
13011 currentStyle.SetFontFaceName(attr.GetFontFaceName());
13012 }
13013 else if (!attr.HasFontFaceName() && currentStyle.HasFontFaceName())
13014 {
13015 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
13016 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
13017 }
13018
13019 if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE))
13020 {
13021 if (currentStyle.HasFontUnderlined())
13022 {
13023 if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined())
13024 {
13025 // Clash of attr - mark as such
13026 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13027 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13028 }
13029 }
13030 else
13031 currentStyle.SetFontUnderlined(attr.GetFontUnderlined());
13032 }
13033 else if (!attr.HasFontUnderlined() && currentStyle.HasFontUnderlined())
13034 {
13035 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13036 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13037 }
13038
13039 if (attr.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_STRIKETHROUGH))
13040 {
13041 if (currentStyle.HasFontStrikethrough())
13042 {
13043 if (currentStyle.GetFontStrikethrough() != attr.GetFontStrikethrough())
13044 {
13045 // Clash of attr - mark as such
13046 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13047 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13048 }
13049 }
13050 else
13051 currentStyle.SetFontStrikethrough(attr.GetFontStrikethrough());
13052 }
13053 else if (!attr.HasFontStrikethrough() && currentStyle.HasFontStrikethrough())
13054 {
13055 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13056 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13057 }
13058
13059 if (attr.HasTextColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TEXT_COLOUR))
13060 {
13061 if (currentStyle.HasTextColour())
13062 {
13063 if (currentStyle.GetTextColour() != attr.GetTextColour())
13064 {
13065 // Clash of attr - mark as such
13066 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
13067 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
13068 }
13069 }
13070 else
13071 currentStyle.SetTextColour(attr.GetTextColour());
13072 }
13073 else if (!attr.HasTextColour() && currentStyle.HasTextColour())
13074 {
13075 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
13076 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
13077 }
13078
13079 if (attr.HasBackgroundColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BACKGROUND_COLOUR))
13080 {
13081 if (currentStyle.HasBackgroundColour())
13082 {
13083 if (currentStyle.GetBackgroundColour() != attr.GetBackgroundColour())
13084 {
13085 // Clash of attr - mark as such
13086 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13087 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13088 }
13089 }
13090 else
13091 currentStyle.SetBackgroundColour(attr.GetBackgroundColour());
13092 }
13093 else if (!attr.HasBackgroundColour() && currentStyle.HasBackgroundColour())
13094 {
13095 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13096 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13097 }
13098
13099 if (attr.HasAlignment() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_ALIGNMENT))
13100 {
13101 if (currentStyle.HasAlignment())
13102 {
13103 if (currentStyle.GetAlignment() != attr.GetAlignment())
13104 {
13105 // Clash of attr - mark as such
13106 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
13107 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
13108 }
13109 }
13110 else
13111 currentStyle.SetAlignment(attr.GetAlignment());
13112 }
13113 else if (!attr.HasAlignment() && currentStyle.HasAlignment())
13114 {
13115 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
13116 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
13117 }
13118
13119 if (attr.HasTabs() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TABS))
13120 {
13121 if (currentStyle.HasTabs())
13122 {
13123 if (!wxRichTextTabsEq(currentStyle.GetTabs(), attr.GetTabs()))
13124 {
13125 // Clash of attr - mark as such
13126 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
13127 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
13128 }
13129 }
13130 else
13131 currentStyle.SetTabs(attr.GetTabs());
13132 }
13133 else if (!attr.HasTabs() && currentStyle.HasTabs())
13134 {
13135 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
13136 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
13137 }
13138
13139 if (attr.HasLeftIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LEFT_INDENT))
13140 {
13141 if (currentStyle.HasLeftIndent())
13142 {
13143 if (currentStyle.GetLeftIndent() != attr.GetLeftIndent() || currentStyle.GetLeftSubIndent() != attr.GetLeftSubIndent())
13144 {
13145 // Clash of attr - mark as such
13146 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
13147 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
13148 }
13149 }
13150 else
13151 currentStyle.SetLeftIndent(attr.GetLeftIndent(), attr.GetLeftSubIndent());
13152 }
13153 else if (!attr.HasLeftIndent() && currentStyle.HasLeftIndent())
13154 {
13155 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
13156 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
13157 }
13158
13159 if (attr.HasRightIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_RIGHT_INDENT))
13160 {
13161 if (currentStyle.HasRightIndent())
13162 {
13163 if (currentStyle.GetRightIndent() != attr.GetRightIndent())
13164 {
13165 // Clash of attr - mark as such
13166 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
13167 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
13168 }
13169 }
13170 else
13171 currentStyle.SetRightIndent(attr.GetRightIndent());
13172 }
13173 else if (!attr.HasRightIndent() && currentStyle.HasRightIndent())
13174 {
13175 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
13176 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
13177 }
13178
13179 if (attr.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_AFTER))
13180 {
13181 if (currentStyle.HasParagraphSpacingAfter())
13182 {
13183 if (currentStyle.GetParagraphSpacingAfter() != attr.GetParagraphSpacingAfter())
13184 {
13185 // Clash of attr - mark as such
13186 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13187 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13188 }
13189 }
13190 else
13191 currentStyle.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter());
13192 }
13193 else if (!attr.HasParagraphSpacingAfter() && currentStyle.HasParagraphSpacingAfter())
13194 {
13195 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13196 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13197 }
13198
13199 if (attr.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_BEFORE))
13200 {
13201 if (currentStyle.HasParagraphSpacingBefore())
13202 {
13203 if (currentStyle.GetParagraphSpacingBefore() != attr.GetParagraphSpacingBefore())
13204 {
13205 // Clash of attr - mark as such
13206 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13207 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13208 }
13209 }
13210 else
13211 currentStyle.SetParagraphSpacingBefore(attr.GetParagraphSpacingBefore());
13212 }
13213 else if (!attr.HasParagraphSpacingBefore() && currentStyle.HasParagraphSpacingBefore())
13214 {
13215 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13216 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13217 }
13218
13219 if (attr.HasLineSpacing() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LINE_SPACING))
13220 {
13221 if (currentStyle.HasLineSpacing())
13222 {
13223 if (currentStyle.GetLineSpacing() != attr.GetLineSpacing())
13224 {
13225 // Clash of attr - mark as such
13226 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
13227 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
13228 }
13229 }
13230 else
13231 currentStyle.SetLineSpacing(attr.GetLineSpacing());
13232 }
13233 else if (!attr.HasLineSpacing() && currentStyle.HasLineSpacing())
13234 {
13235 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
13236 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
13237 }
13238
13239 if (attr.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
13240 {
13241 if (currentStyle.HasCharacterStyleName())
13242 {
13243 if (currentStyle.GetCharacterStyleName() != attr.GetCharacterStyleName())
13244 {
13245 // Clash of attr - mark as such
13246 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13247 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13248 }
13249 }
13250 else
13251 currentStyle.SetCharacterStyleName(attr.GetCharacterStyleName());
13252 }
13253 else if (!attr.HasCharacterStyleName() && currentStyle.HasCharacterStyleName())
13254 {
13255 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13256 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13257 }
13258
13259 if (attr.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
13260 {
13261 if (currentStyle.HasParagraphStyleName())
13262 {
13263 if (currentStyle.GetParagraphStyleName() != attr.GetParagraphStyleName())
13264 {
13265 // Clash of attr - mark as such
13266 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13267 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13268 }
13269 }
13270 else
13271 currentStyle.SetParagraphStyleName(attr.GetParagraphStyleName());
13272 }
13273 else if (!attr.HasParagraphStyleName() && currentStyle.HasParagraphStyleName())
13274 {
13275 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13276 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13277 }
13278
13279 if (attr.HasListStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LIST_STYLE_NAME))
13280 {
13281 if (currentStyle.HasListStyleName())
13282 {
13283 if (currentStyle.GetListStyleName() != attr.GetListStyleName())
13284 {
13285 // Clash of attr - mark as such
13286 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13287 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13288 }
13289 }
13290 else
13291 currentStyle.SetListStyleName(attr.GetListStyleName());
13292 }
13293 else if (!attr.HasListStyleName() && currentStyle.HasListStyleName())
13294 {
13295 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13296 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13297 }
13298
13299 if (attr.HasBulletStyle() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_STYLE))
13300 {
13301 if (currentStyle.HasBulletStyle())
13302 {
13303 if (currentStyle.GetBulletStyle() != attr.GetBulletStyle())
13304 {
13305 // Clash of attr - mark as such
13306 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
13307 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
13308 }
13309 }
13310 else
13311 currentStyle.SetBulletStyle(attr.GetBulletStyle());
13312 }
13313 else if (!attr.HasBulletStyle() && currentStyle.HasBulletStyle())
13314 {
13315 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
13316 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
13317 }
13318
13319 if (attr.HasBulletNumber() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NUMBER))
13320 {
13321 if (currentStyle.HasBulletNumber())
13322 {
13323 if (currentStyle.GetBulletNumber() != attr.GetBulletNumber())
13324 {
13325 // Clash of attr - mark as such
13326 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
13327 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
13328 }
13329 }
13330 else
13331 currentStyle.SetBulletNumber(attr.GetBulletNumber());
13332 }
13333 else if (!attr.HasBulletNumber() && currentStyle.HasBulletNumber())
13334 {
13335 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
13336 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
13337 }
13338
13339 if (attr.HasBulletText() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_TEXT))
13340 {
13341 if (currentStyle.HasBulletText())
13342 {
13343 if (currentStyle.GetBulletText() != attr.GetBulletText())
13344 {
13345 // Clash of attr - mark as such
13346 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
13347 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
13348 }
13349 }
13350 else
13351 {
13352 currentStyle.SetBulletText(attr.GetBulletText());
13353 currentStyle.SetBulletFont(attr.GetBulletFont());
13354 }
13355 }
13356 else if (!attr.HasBulletText() && currentStyle.HasBulletText())
13357 {
13358 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
13359 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
13360 }
13361
13362 if (attr.HasBulletName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NAME))
13363 {
13364 if (currentStyle.HasBulletName())
13365 {
13366 if (currentStyle.GetBulletName() != attr.GetBulletName())
13367 {
13368 // Clash of attr - mark as such
13369 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
13370 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
13371 }
13372 }
13373 else
13374 {
13375 currentStyle.SetBulletName(attr.GetBulletName());
13376 }
13377 }
13378 else if (!attr.HasBulletName() && currentStyle.HasBulletName())
13379 {
13380 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
13381 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
13382 }
13383
13384 if (attr.HasURL() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_URL))
13385 {
13386 if (currentStyle.HasURL())
13387 {
13388 if (currentStyle.GetURL() != attr.GetURL())
13389 {
13390 // Clash of attr - mark as such
13391 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
13392 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
13393 }
13394 }
13395 else
13396 {
13397 currentStyle.SetURL(attr.GetURL());
13398 }
13399 }
13400 else if (!attr.HasURL() && currentStyle.HasURL())
13401 {
13402 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
13403 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
13404 }
13405
13406 if (attr.HasTextEffects() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_EFFECTS))
13407 {
13408 if (currentStyle.HasTextEffects())
13409 {
13410 // We need to find the bits in the new attr that are different:
13411 // just look at those bits that are specified by the new attr.
13412
13413 // We need to remove the bits and flags that are not common between current attr
13414 // and new attr. In so doing we need to take account of the styles absent from one or more of the
13415 // previous styles.
13416
13417 int currentRelevantTextEffects = currentStyle.GetTextEffects() & attr.GetTextEffectFlags();
13418 int newRelevantTextEffects = attr.GetTextEffects() & attr.GetTextEffectFlags();
13419
13420 if (currentRelevantTextEffects != newRelevantTextEffects)
13421 {
13422 // Find the text effects that were different, using XOR
13423 int differentEffects = currentRelevantTextEffects ^ newRelevantTextEffects;
13424
13425 // Clash of attr - mark as such
13426 clashingAttr.SetTextEffectFlags(clashingAttr.GetTextEffectFlags() | differentEffects);
13427 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~differentEffects);
13428 }
13429 }
13430 else
13431 {
13432 currentStyle.SetTextEffects(attr.GetTextEffects());
13433 currentStyle.SetTextEffectFlags(attr.GetTextEffectFlags());
13434 }
13435
13436 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
13437 // that we've looked at so far
13438 currentStyle.SetTextEffects(currentStyle.GetTextEffects() & ~absentAttr.GetTextEffectFlags());
13439 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~absentAttr.GetTextEffectFlags());
13440
13441 if (currentStyle.GetTextEffectFlags() == 0)
13442 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
13443 }
13444 else if (!attr.HasTextEffects() && currentStyle.HasTextEffects())
13445 {
13446 clashingAttr.AddFlag(wxTEXT_ATTR_EFFECTS);
13447 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
13448 }
13449
13450 if (attr.HasOutlineLevel() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_OUTLINE_LEVEL))
13451 {
13452 if (currentStyle.HasOutlineLevel())
13453 {
13454 if (currentStyle.GetOutlineLevel() != attr.GetOutlineLevel())
13455 {
13456 // Clash of attr - mark as such
13457 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13458 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13459 }
13460 }
13461 else
13462 currentStyle.SetOutlineLevel(attr.GetOutlineLevel());
13463 }
13464 else if (!attr.HasOutlineLevel() && currentStyle.HasOutlineLevel())
13465 {
13466 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13467 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13468 }
13469 }
13470
13471 WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
13472
13473 // JACS 2013-01-27
13474 WX_DEFINE_OBJARRAY(wxRichTextAttrArray);
13475
13476 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
13477
13478 bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const
13479 {
13480 if (m_properties.GetCount() != props.GetCount())
13481 return false;
13482
13483 size_t i;
13484 for (i = 0; i < m_properties.GetCount(); i++)
13485 {
13486 const wxVariant& var1 = m_properties[i];
13487 int idx = props.Find(var1.GetName());
13488 if (idx == -1)
13489 return false;
13490 const wxVariant& var2 = props.m_properties[idx];
13491 if (!(var1 == var2))
13492 return false;
13493 }
13494
13495 return true;
13496 }
13497
13498 wxArrayString wxRichTextProperties::GetPropertyNames() const
13499 {
13500 wxArrayString arr;
13501 size_t i;
13502 for (i = 0; i < m_properties.GetCount(); i++)
13503 {
13504 arr.Add(m_properties[i].GetName());
13505 }
13506 return arr;
13507 }
13508
13509 int wxRichTextProperties::Find(const wxString& name) const
13510 {
13511 size_t i;
13512 for (i = 0; i < m_properties.GetCount(); i++)
13513 {
13514 if (m_properties[i].GetName() == name)
13515 return (int) i;
13516 }
13517 return -1;
13518 }
13519
13520 bool wxRichTextProperties::Remove(const wxString& name)
13521 {
13522 int idx = Find(name);
13523 if (idx != -1)
13524 {
13525 m_properties.RemoveAt(idx);
13526 return true;
13527 }
13528 else
13529 return false;
13530 }
13531
13532 wxVariant* wxRichTextProperties::FindOrCreateProperty(const wxString& name)
13533 {
13534 int idx = Find(name);
13535 if (idx == wxNOT_FOUND)
13536 SetProperty(name, wxString());
13537 idx = Find(name);
13538 if (idx != wxNOT_FOUND)
13539 {
13540 return & (*this)[idx];
13541 }
13542 else
13543 return NULL;
13544 }
13545
13546 const wxVariant& wxRichTextProperties::GetProperty(const wxString& name) const
13547 {
13548 static const wxVariant nullVariant;
13549 int idx = Find(name);
13550 if (idx != -1)
13551 return m_properties[idx];
13552 else
13553 return nullVariant;
13554 }
13555
13556 wxString wxRichTextProperties::GetPropertyString(const wxString& name) const
13557 {
13558 return GetProperty(name).GetString();
13559 }
13560
13561 long wxRichTextProperties::GetPropertyLong(const wxString& name) const
13562 {
13563 return GetProperty(name).GetLong();
13564 }
13565
13566 bool wxRichTextProperties::GetPropertyBool(const wxString& name) const
13567 {
13568 return GetProperty(name).GetBool();
13569 }
13570
13571 double wxRichTextProperties::GetPropertyDouble(const wxString& name) const
13572 {
13573 return GetProperty(name).GetDouble();
13574 }
13575
13576 void wxRichTextProperties::SetProperty(const wxVariant& variant)
13577 {
13578 wxASSERT(!variant.GetName().IsEmpty());
13579
13580 int idx = Find(variant.GetName());
13581
13582 if (idx == -1)
13583 m_properties.Add(variant);
13584 else
13585 m_properties[idx] = variant;
13586 }
13587
13588 void wxRichTextProperties::SetProperty(const wxString& name, const wxVariant& variant)
13589 {
13590 int idx = Find(name);
13591 wxVariant var(variant);
13592 var.SetName(name);
13593
13594 if (idx == -1)
13595 m_properties.Add(var);
13596 else
13597 m_properties[idx] = var;
13598 }
13599
13600 void wxRichTextProperties::SetProperty(const wxString& name, const wxString& value)
13601 {
13602 SetProperty(name, wxVariant(value, name));
13603 }
13604
13605 void wxRichTextProperties::SetProperty(const wxString& name, long value)
13606 {
13607 SetProperty(name, wxVariant(value, name));
13608 }
13609
13610 void wxRichTextProperties::SetProperty(const wxString& name, double value)
13611 {
13612 SetProperty(name, wxVariant(value, name));
13613 }
13614
13615 void wxRichTextProperties::SetProperty(const wxString& name, bool value)
13616 {
13617 SetProperty(name, wxVariant(value, name));
13618 }
13619
13620 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties& properties)
13621 {
13622 size_t i;
13623 for (i = 0; i < properties.GetCount(); i++)
13624 {
13625 wxString name = properties.GetProperties()[i].GetName();
13626 if (HasProperty(name))
13627 Remove(name);
13628 }
13629 }
13630
13631 void wxRichTextProperties::MergeProperties(const wxRichTextProperties& properties)
13632 {
13633 size_t i;
13634 for (i = 0; i < properties.GetCount(); i++)
13635 {
13636 SetProperty(properties.GetProperties()[i]);
13637 }
13638 }
13639
13640 wxRichTextObject* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox* topLevelContainer) const
13641 {
13642 if (m_address.GetCount() == 0)
13643 return topLevelContainer;
13644
13645 wxRichTextCompositeObject* p = topLevelContainer;
13646 size_t i = 0;
13647 while (p && i < m_address.GetCount())
13648 {
13649 int pos = m_address[i];
13650 wxASSERT(pos >= 0 && pos < (int) p->GetChildren().GetCount());
13651 if (pos < 0 || pos >= (int) p->GetChildren().GetCount())
13652 return NULL;
13653
13654 wxRichTextObject* p1 = p->GetChild(pos);
13655 if (i == (m_address.GetCount()-1))
13656 return p1;
13657
13658 p = wxDynamicCast(p1, wxRichTextCompositeObject);
13659 i ++;
13660 }
13661 return NULL;
13662 }
13663
13664 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox* topLevelContainer, wxRichTextObject* obj)
13665 {
13666 m_address.Clear();
13667
13668 if (topLevelContainer == obj)
13669 return true;
13670
13671 wxRichTextObject* o = obj;
13672 while (o)
13673 {
13674 wxRichTextCompositeObject* p = wxDynamicCast(o->GetParent(), wxRichTextCompositeObject);
13675 if (!p)
13676 return false;
13677
13678 int pos = p->GetChildren().IndexOf(o);
13679 if (pos == -1)
13680 return false;
13681
13682 m_address.Insert(pos, 0);
13683
13684 if (p == topLevelContainer)
13685 return true;
13686
13687 o = p;
13688 }
13689 return false;
13690 }
13691
13692 // Equality test
13693 bool wxRichTextSelection::operator==(const wxRichTextSelection& sel) const
13694 {
13695 if (m_container != sel.m_container)
13696 return false;
13697 if (m_ranges.GetCount() != sel.m_ranges.GetCount())
13698 return false;
13699 size_t i;
13700 for (i = 0; i < m_ranges.GetCount(); i++)
13701 if (!(m_ranges[i] == sel.m_ranges[i]))
13702 return false;
13703 return true;
13704 }
13705
13706 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
13707 // or none at the level of the object's container.
13708 wxRichTextRangeArray wxRichTextSelection::GetSelectionForObject(wxRichTextObject* obj) const
13709 {
13710 if (IsValid())
13711 {
13712 wxRichTextParagraphLayoutBox* container = obj->GetParentContainer();
13713
13714 if (container == m_container)
13715 return m_ranges;
13716
13717 container = obj->GetContainer();
13718 while (container)
13719 {
13720 if (container->GetParent())
13721 {
13722 // If we found that our object's container is within the range of
13723 // a selection higher up, then assume the whole original object
13724 // is also selected.
13725 wxRichTextParagraphLayoutBox* parentContainer = container->GetParentContainer();
13726 if (parentContainer == m_container)
13727 {
13728 if (WithinSelection(container->GetRange().GetStart(), m_ranges))
13729 {
13730 wxRichTextRangeArray ranges;
13731 ranges.Add(obj->GetRange());
13732 return ranges;
13733 }
13734 }
13735
13736 container = parentContainer;
13737 }
13738 else
13739 {
13740 container = NULL;
13741 break;
13742 }
13743 }
13744 }
13745 return wxRichTextRangeArray();
13746 }
13747
13748 // Is the given position within the selection?
13749 bool wxRichTextSelection::WithinSelection(long pos, wxRichTextObject* obj) const
13750 {
13751 if (!IsValid())
13752 return false;
13753 else
13754 {
13755 wxRichTextRangeArray selectionRanges = GetSelectionForObject(obj);
13756 return WithinSelection(pos, selectionRanges);
13757 }
13758 }
13759
13760 // Is the given position within the selection range?
13761 bool wxRichTextSelection::WithinSelection(long pos, const wxRichTextRangeArray& ranges)
13762 {
13763 size_t i;
13764 for (i = 0; i < ranges.GetCount(); i++)
13765 {
13766 const wxRichTextRange& range = ranges[i];
13767 if (pos >= range.GetStart() && pos <= range.GetEnd())
13768 return true;
13769 }
13770 return false;
13771 }
13772
13773 // Is the given range completely within the selection range?
13774 bool wxRichTextSelection::WithinSelection(const wxRichTextRange& range, const wxRichTextRangeArray& ranges)
13775 {
13776 size_t i;
13777 for (i = 0; i < ranges.GetCount(); i++)
13778 {
13779 const wxRichTextRange& eachRange = ranges[i];
13780 if (range.IsWithin(eachRange))
13781 return true;
13782 }
13783 return false;
13784 }
13785
13786 IMPLEMENT_CLASS(wxRichTextDrawingHandler, wxObject)
13787 IMPLEMENT_CLASS(wxRichTextDrawingContext, wxObject)
13788
13789 wxRichTextDrawingContext::wxRichTextDrawingContext(wxRichTextBuffer* buffer)
13790 {
13791 Init();
13792 m_buffer = buffer;
13793 if (m_buffer && m_buffer->GetRichTextCtrl())
13794 EnableVirtualAttributes(m_buffer->GetRichTextCtrl()->GetVirtualAttributesEnabled());
13795 }
13796
13797 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject* obj) const
13798 {
13799 if (!GetVirtualAttributesEnabled())
13800 return false;
13801
13802 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13803 while (node)
13804 {
13805 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13806 if (handler->HasVirtualAttributes(obj))
13807 return true;
13808
13809 node = node->GetNext();
13810 }
13811 return false;
13812 }
13813
13814 wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* obj) const
13815 {
13816 wxRichTextAttr attr;
13817 if (!GetVirtualAttributesEnabled())
13818 return attr;
13819
13820 // We apply all handlers, so we can may combine several different attributes
13821 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13822 while (node)
13823 {
13824 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13825 if (handler->HasVirtualAttributes(obj))
13826 {
13827 bool success = handler->GetVirtualAttributes(attr, obj);
13828 wxASSERT(success);
13829 wxUnusedVar(success);
13830 }
13831
13832 node = node->GetNext();
13833 }
13834 return attr;
13835 }
13836
13837 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const
13838 {
13839 if (!GetVirtualAttributesEnabled())
13840 return false;
13841
13842 if (HasVirtualAttributes(obj))
13843 {
13844 wxRichTextAttr a(GetVirtualAttributes(obj));
13845 attr.Apply(a);
13846 return true;
13847 }
13848 else
13849 return false;
13850 }
13851
13852 int wxRichTextDrawingContext::GetVirtualSubobjectAttributesCount(wxRichTextObject* obj) const
13853 {
13854 if (!GetVirtualAttributesEnabled())
13855 return 0;
13856
13857 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13858 while (node)
13859 {
13860 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13861 int count = handler->GetVirtualSubobjectAttributesCount(obj);
13862 if (count > 0)
13863 return count;
13864
13865 node = node->GetNext();
13866 }
13867 return 0;
13868 }
13869
13870 int wxRichTextDrawingContext::GetVirtualSubobjectAttributes(wxRichTextObject* obj, wxArrayInt& positions, wxRichTextAttrArray& attributes) const
13871 {
13872 if (!GetVirtualAttributesEnabled())
13873 return 0;
13874
13875 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13876 while (node)
13877 {
13878 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13879 if (handler->GetVirtualSubobjectAttributes(obj, positions, attributes))
13880 return positions.GetCount();
13881
13882 node = node->GetNext();
13883 }
13884 return 0;
13885 }
13886
13887 bool wxRichTextDrawingContext::HasVirtualText(const wxRichTextPlainText* obj) const
13888 {
13889 if (!GetVirtualAttributesEnabled())
13890 return false;
13891
13892 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13893 while (node)
13894 {
13895 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13896 if (handler->HasVirtualText(obj))
13897 return true;
13898
13899 node = node->GetNext();
13900 }
13901 return false;
13902 }
13903
13904 bool wxRichTextDrawingContext::GetVirtualText(const wxRichTextPlainText* obj, wxString& text) const
13905 {
13906 if (!GetVirtualAttributesEnabled())
13907 return false;
13908
13909 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13910 while (node)
13911 {
13912 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13913 if (handler->GetVirtualText(obj, text))
13914 return true;
13915
13916 node = node->GetNext();
13917 }
13918 return false;
13919 }
13920
13921 /// Adds a handler to the end
13922 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler *handler)
13923 {
13924 sm_drawingHandlers.Append(handler);
13925 }
13926
13927 /// Inserts a handler at the front
13928 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler *handler)
13929 {
13930 sm_drawingHandlers.Insert( handler );
13931 }
13932
13933 /// Removes a handler
13934 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString& name)
13935 {
13936 wxRichTextDrawingHandler *handler = FindDrawingHandler(name);
13937 if (handler)
13938 {
13939 sm_drawingHandlers.DeleteObject(handler);
13940 delete handler;
13941 return true;
13942 }
13943 else
13944 return false;
13945 }
13946
13947 wxRichTextDrawingHandler* wxRichTextBuffer::FindDrawingHandler(const wxString& name)
13948 {
13949 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
13950 while (node)
13951 {
13952 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13953 if (handler->GetName().Lower() == name.Lower()) return handler;
13954
13955 node = node->GetNext();
13956 }
13957 return NULL;
13958 }
13959
13960 void wxRichTextBuffer::CleanUpDrawingHandlers()
13961 {
13962 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
13963 while (node)
13964 {
13965 wxRichTextDrawingHandler* handler = (wxRichTextDrawingHandler*)node->GetData();
13966 wxList::compatibility_iterator next = node->GetNext();
13967 delete handler;
13968 node = next;
13969 }
13970
13971 sm_drawingHandlers.Clear();
13972 }
13973
13974 void wxRichTextBuffer::AddFieldType(wxRichTextFieldType *fieldType)
13975 {
13976 sm_fieldTypes[fieldType->GetName()] = fieldType;
13977 }
13978
13979 bool wxRichTextBuffer::RemoveFieldType(const wxString& name)
13980 {
13981 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
13982 if (it == sm_fieldTypes.end())
13983 return false;
13984 else
13985 {
13986 wxRichTextFieldType* fieldType = it->second;
13987 sm_fieldTypes.erase(it);
13988 delete fieldType;
13989 return true;
13990 }
13991 }
13992
13993 wxRichTextFieldType *wxRichTextBuffer::FindFieldType(const wxString& name)
13994 {
13995 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
13996 if (it == sm_fieldTypes.end())
13997 return NULL;
13998 else
13999 return it->second;
14000 }
14001
14002 void wxRichTextBuffer::CleanUpFieldTypes()
14003 {
14004 wxRichTextFieldTypeHashMap::iterator it;
14005 for( it = sm_fieldTypes.begin(); it != sm_fieldTypes.end(); ++it )
14006 {
14007 wxRichTextFieldType* fieldType = it->second;
14008 delete fieldType;
14009 }
14010
14011 sm_fieldTypes.clear();
14012 }
14013
14014 #endif
14015 // wxUSE_RICHTEXT