Corrected clipboard data id
[wxWidgets.git] / src / richtext / richtextbuffer.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/richtext/richtextbuffer.cpp
3 // Purpose: Buffer for wxRichTextCtrl
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 2005-09-30
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #if wxUSE_RICHTEXT
20
21 #include "wx/richtext/richtextbuffer.h"
22
23 #ifndef WX_PRECOMP
24 #include "wx/dc.h"
25 #include "wx/intl.h"
26 #include "wx/log.h"
27 #include "wx/dataobj.h"
28 #include "wx/module.h"
29 #endif
30
31 #include "wx/settings.h"
32 #include "wx/filename.h"
33 #include "wx/clipbrd.h"
34 #include "wx/wfstream.h"
35 #include "wx/mstream.h"
36 #include "wx/sstream.h"
37 #include "wx/textfile.h"
38 #include "wx/hashmap.h"
39 #include "wx/dynarray.h"
40
41 #include "wx/richtext/richtextctrl.h"
42 #include "wx/richtext/richtextstyles.h"
43 #include "wx/richtext/richtextimagedlg.h"
44 #include "wx/richtext/richtextsizepage.h"
45 #include "wx/richtext/richtextxml.h"
46
47 #include "wx/listimpl.cpp"
48 #include "wx/arrimpl.cpp"
49
50 WX_DEFINE_LIST(wxRichTextObjectList)
51 WX_DEFINE_LIST(wxRichTextLineList)
52
53 // Switch off if the platform doesn't like it for some reason
54 #define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
55
56 // Use GetPartialTextExtents for platforms that support it natively
57 #define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
58
59 const wxChar wxRichTextLineBreakChar = (wxChar) 29;
60
61 // Helper classes for floating layout
62 struct wxRichTextFloatRectMap
63 {
64 wxRichTextFloatRectMap(int sY, int eY, int w, wxRichTextObject* obj)
65 {
66 startY = sY;
67 endY = eY;
68 width = w;
69 anchor = obj;
70 }
71
72 int startY, endY;
73 int width;
74 wxRichTextObject* anchor;
75 };
76
77 WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap*, wxRichTextFloatRectMapArray);
78
79 int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap* r1, wxRichTextFloatRectMap* r2)
80 {
81 return r1->startY - r2->startY;
82 }
83
84 class wxRichTextFloatCollector
85 {
86 public:
87 wxRichTextFloatCollector(const wxRect& availableRect);
88 ~wxRichTextFloatCollector();
89
90 // Collect the floating objects info in the given paragraph
91 void CollectFloat(wxRichTextParagraph* para);
92 void CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating);
93
94 // Return the last paragraph we collected
95 wxRichTextParagraph* LastParagraph();
96
97 // Given the start y position and the height of the line,
98 // find out how wide the line can be
99 wxRect GetAvailableRect(int startY, int endY);
100
101 // Given a floating box, find its fit position
102 int GetFitPosition(int direction, int start, int height) const;
103 int GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const;
104
105 // Find the last y position
106 int GetLastRectBottom();
107
108 // Draw the floats inside a rect
109 void Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
110
111 // HitTest the floats
112 int HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
113
114 // Get floating object count
115 int GetFloatingObjectCount() const { return m_left.GetCount() + m_right.GetCount(); }
116
117 // Get floating objects
118 bool GetFloatingObjects(wxRichTextObjectList& objects) const;
119
120 // Delete a float
121 bool DeleteFloat(wxRichTextObject* obj);
122
123 // Do we have this float already?
124 bool HasFloat(wxRichTextObject* obj);
125
126 bool HasFloats() const { return m_left.GetCount() >0 || m_right.GetCount() > 0; }
127
128 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point);
129
130 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY);
131
132 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array);
133
134 static void DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
135
136 static int HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
137
138 private:
139 wxRichTextFloatRectMapArray m_left;
140 wxRichTextFloatRectMapArray m_right;
141 //int m_width;
142 wxRect m_availableRect;
143 wxRichTextParagraph* m_para;
144 };
145
146 // Delete a float
147 bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject* obj)
148 {
149 size_t i;
150 for (i = 0; i < m_left.GetCount(); i++)
151 {
152 if (m_left[i]->anchor == obj)
153 {
154 m_left.RemoveAt(i);
155 return true;
156 }
157 }
158 for (i = 0; i < m_right.GetCount(); i++)
159 {
160 if (m_right[i]->anchor == obj)
161 {
162 m_right.RemoveAt(i);
163 return true;
164 }
165 }
166 return false;
167 }
168
169 // Do we have this float already?
170 bool wxRichTextFloatCollector::HasFloat(wxRichTextObject* obj)
171 {
172 size_t i;
173 for (i = 0; i < m_left.GetCount(); i++)
174 {
175 if (m_left[i]->anchor == obj)
176 {
177 return true;
178 }
179 }
180 for (i = 0; i < m_right.GetCount(); i++)
181 {
182 if (m_right[i]->anchor == obj)
183 {
184 return true;
185 }
186 }
187 return false;
188 }
189
190 // Get floating objects
191 bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList& objects) const
192 {
193 size_t i;
194 for (i = 0; i < m_left.GetCount(); i++)
195 objects.Append(m_left[i]->anchor);
196 for (i = 0; i < m_right.GetCount(); i++)
197 objects.Append(m_right[i]->anchor);
198 return true;
199 }
200
201
202 /*
203 * Binary search helper function
204 * The argument point is the Y coordinate, and this fuction
205 * always return the floating rect that contain this coordinate
206 * or under this coordinate.
207 */
208 int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point)
209 {
210 int end = array.GetCount() - 1;
211 int start = 0;
212 int ret = 0;
213
214 wxASSERT(end >= 0);
215
216 while (true)
217 {
218 if (start > end)
219 {
220 break;
221 }
222
223 int mid = (start + end) / 2;
224 if (array[mid]->startY <= point && array[mid]->endY >= point)
225 return mid;
226 else if (array[mid]->startY > point)
227 {
228 end = mid - 1;
229 ret = mid;
230 }
231 else if (array[mid]->endY < point)
232 {
233 start = mid + 1;
234 ret = start;
235 }
236 }
237
238 return ret;
239 }
240
241 int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY)
242 {
243 int ret = 0;
244 int len = array.GetCount();
245
246 wxASSERT(index >= 0 && index < len);
247
248 if (array[index]->startY < startY && array[index]->endY > startY)
249 ret = ret < array[index]->width ? array[index]->width : ret;
250 while (index < len && array[index]->startY <= endY)
251 {
252 ret = ret < array[index]->width ? array[index]->width : ret;
253 index++;
254 }
255
256 return ret;
257 }
258
259 wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect& rect) : m_left(wxRichTextFloatRectMapCmp), m_right(wxRichTextFloatRectMapCmp)
260 {
261 m_availableRect = rect;
262 m_para = NULL;
263 }
264
265 void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array)
266 {
267 int len = array.GetCount();
268 for (int i = 0; i < len; i++)
269 delete array[i];
270 }
271
272 wxRichTextFloatCollector::~wxRichTextFloatCollector()
273 {
274 FreeFloatRectMapArray(m_left);
275 FreeFloatRectMapArray(m_right);
276 }
277
278 int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const
279 {
280 if (array.GetCount() == 0)
281 return start;
282
283 int i = SearchAdjacentRect(array, start);
284 int last = start;
285 while (i < (int) array.GetCount())
286 {
287 if (array[i]->startY - last >= height)
288 return last + 1;
289 last = array[i]->endY;
290 i++;
291 }
292
293 return last + 1;
294 }
295
296 int wxRichTextFloatCollector::GetFitPosition(int direction, int start, int height) const
297 {
298 if (direction == wxTEXT_BOX_ATTR_FLOAT_LEFT)
299 return GetFitPosition(m_left, start, height);
300 else if (direction == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
301 return GetFitPosition(m_right, start, height);
302 else
303 {
304 wxASSERT("Never should be here");
305 return start;
306 }
307 }
308
309 // Adds a floating image to the float collector.
310 // The actual positioning is done by wxRichTextParagraph::LayoutFloat.
311 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating)
312 {
313 int direction = floating->GetFloatDirection();
314
315 wxPoint pos = floating->GetPosition();
316 wxSize size = floating->GetCachedSize();
317 wxRichTextFloatRectMap *map = new wxRichTextFloatRectMap(pos.y, pos.y + size.y, size.x, floating);
318 switch (direction)
319 {
320 case wxTEXT_BOX_ATTR_FLOAT_NONE:
321 delete map;
322 break;
323 case wxTEXT_BOX_ATTR_FLOAT_LEFT:
324 // Just a not-enough simple assertion
325 wxASSERT (m_left.Index(map) == wxNOT_FOUND);
326 m_left.Add(map);
327 break;
328 case wxTEXT_BOX_ATTR_FLOAT_RIGHT:
329 wxASSERT (m_right.Index(map) == wxNOT_FOUND);
330 m_right.Add(map);
331 break;
332 default:
333 delete map;
334 wxASSERT("Unrecognised float attribute.");
335 }
336
337 m_para = para;
338 }
339
340 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para)
341 {
342 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
343 while (node)
344 {
345 wxRichTextObject* floating = node->GetData();
346
347 if (floating->IsFloating())
348 {
349 CollectFloat(para, floating);
350 }
351
352 node = node->GetNext();
353 }
354
355 m_para = para;
356 }
357
358 wxRichTextParagraph* wxRichTextFloatCollector::LastParagraph()
359 {
360 return m_para;
361 }
362
363 wxRect wxRichTextFloatCollector::GetAvailableRect(int startY, int endY)
364 {
365 int widthLeft = 0, widthRight = 0;
366 if (m_left.GetCount() != 0)
367 {
368 int i = SearchAdjacentRect(m_left, startY);
369 if (i < (int) m_left.GetCount())
370 widthLeft = GetWidthFromFloatRect(m_left, i, startY, endY);
371 }
372 if (m_right.GetCount() != 0)
373 {
374 int j = SearchAdjacentRect(m_right, startY);
375 if (j < (int) m_right.GetCount())
376 widthRight = GetWidthFromFloatRect(m_right, j, startY, endY);
377 }
378
379 // TODO: actually we want to use the actual image positions to find the
380 // available remaining space, since the image might not be right up against
381 // the left or right edge of the container.
382 return wxRect(widthLeft + m_availableRect.x, 0, m_availableRect.width - widthLeft - widthRight, 0);
383 }
384
385 int wxRichTextFloatCollector::GetLastRectBottom()
386 {
387 int ret = 0;
388 int len = m_left.GetCount();
389 if (len) {
390 ret = ret > m_left[len-1]->endY ? ret : m_left[len-1]->endY;
391 }
392 len = m_right.GetCount();
393 if (len) {
394 ret = ret > m_right[len-1]->endY ? ret : m_right[len-1]->endY;
395 }
396
397 return ret;
398 }
399
400 void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
401 {
402 int start = rect.y;
403 int end = rect.y + rect.height;
404 int i, j;
405 i = SearchAdjacentRect(array, start);
406 if (i < 0 || i >= (int) array.GetCount())
407 return;
408 j = SearchAdjacentRect(array, end);
409 if (j < 0 || j >= (int) array.GetCount())
410 j = array.GetCount() - 1;
411 while (i <= j)
412 {
413 wxRichTextObject* obj = array[i]->anchor;
414 wxRichTextRange r = obj->GetRange();
415 obj->Draw(dc, context, r, selection, wxRect(obj->GetPosition(), obj->GetCachedSize()), descent, style);
416 i++;
417 }
418 }
419
420 void wxRichTextFloatCollector::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
421 {
422 if (m_left.GetCount() > 0)
423 DrawFloat(m_left, dc, context, range, selection, rect, descent, style);
424 if (m_right.GetCount() > 0)
425 DrawFloat(m_right, dc, context, range, selection, rect, descent, style);
426 }
427
428 int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int WXUNUSED(flags))
429 {
430 int i;
431 if (array.GetCount() == 0)
432 return wxRICHTEXT_HITTEST_NONE;
433 i = SearchAdjacentRect(array, pt.y);
434 if (i < 0 || i >= (int) array.GetCount())
435 return wxRICHTEXT_HITTEST_NONE;
436 if (!array[i]->anchor->IsShown())
437 return wxRICHTEXT_HITTEST_NONE;
438
439 wxPoint point = array[i]->anchor->GetPosition();
440 wxSize size = array[i]->anchor->GetCachedSize();
441 if (point.x <= pt.x && point.x + size.x >= pt.x
442 && point.y <= pt.y && point.y + size.y >= pt.y)
443 {
444 textPosition = array[i]->anchor->GetRange().GetStart();
445 * obj = array[i]->anchor;
446 if (pt.x > (pt.x + pt.x + size.x) / 2)
447 return wxRICHTEXT_HITTEST_BEFORE;
448 else
449 return wxRICHTEXT_HITTEST_AFTER;
450 }
451
452 return wxRICHTEXT_HITTEST_NONE;
453 }
454
455 int wxRichTextFloatCollector::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags)
456 {
457 int ret = HitTestFloat(m_left, dc, context, pt, textPosition, obj, flags);
458 if (ret == wxRICHTEXT_HITTEST_NONE)
459 {
460 ret = HitTestFloat(m_right, dc, context, pt, textPosition, obj, flags);
461 }
462 return ret;
463 }
464
465 // Helpers for efficiency
466 inline void wxCheckSetFont(wxDC& dc, const wxFont& font)
467 {
468 dc.SetFont(font);
469 }
470
471 inline void wxCheckSetPen(wxDC& dc, const wxPen& pen)
472 {
473 const wxPen& pen1 = dc.GetPen();
474 if (pen1.IsOk() && pen.IsOk())
475 {
476 if (pen1.GetWidth() == pen.GetWidth() &&
477 pen1.GetStyle() == pen.GetStyle() &&
478 pen1.GetColour() == pen.GetColour())
479 return;
480 }
481 dc.SetPen(pen);
482 }
483
484 inline void wxCheckSetBrush(wxDC& dc, const wxBrush& brush)
485 {
486 const wxBrush& brush1 = dc.GetBrush();
487 if (brush1.IsOk() && brush.IsOk())
488 {
489 if (brush1.GetStyle() == brush.GetStyle() &&
490 brush1.GetColour() == brush.GetColour())
491 return;
492 }
493 dc.SetBrush(brush);
494 }
495
496 /*!
497 * wxRichTextObject
498 * This is the base for drawable objects.
499 */
500
501 IMPLEMENT_CLASS(wxRichTextObject, wxObject)
502
503 wxRichTextObject::wxRichTextObject(wxRichTextObject* parent)
504 {
505 m_refCount = 1;
506 m_parent = parent;
507 m_descent = 0;
508 m_show = true;
509 }
510
511 wxRichTextObject::~wxRichTextObject()
512 {
513 }
514
515 void wxRichTextObject::Dereference()
516 {
517 m_refCount --;
518 if (m_refCount <= 0)
519 delete this;
520 }
521
522 /// Copy
523 void wxRichTextObject::Copy(const wxRichTextObject& obj)
524 {
525 m_size = obj.m_size;
526 m_maxSize = obj.m_maxSize;
527 m_minSize = obj.m_minSize;
528 m_pos = obj.m_pos;
529 m_range = obj.m_range;
530 m_ownRange = obj.m_ownRange;
531 m_attributes = obj.m_attributes;
532 m_properties = obj.m_properties;
533 m_descent = obj.m_descent;
534 m_show = obj.m_show;
535 }
536
537 // Get/set the top-level container of this object.
538 wxRichTextParagraphLayoutBox* wxRichTextObject::GetContainer() const
539 {
540 const wxRichTextObject* p = this;
541 while (p)
542 {
543 if (p->IsTopLevel())
544 {
545 return wxDynamicCast(p, wxRichTextParagraphLayoutBox);
546 }
547 p = p->GetParent();
548 }
549 return NULL;
550 }
551
552 void wxRichTextObject::SetMargins(int margin)
553 {
554 SetMargins(margin, margin, margin, margin);
555 }
556
557 void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin, int bottomMargin)
558 {
559 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin, wxTEXT_ATTR_UNITS_PIXELS);
560 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin, wxTEXT_ATTR_UNITS_PIXELS);
561 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin, wxTEXT_ATTR_UNITS_PIXELS);
562 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin, wxTEXT_ATTR_UNITS_PIXELS);
563 }
564
565 int wxRichTextObject::GetLeftMargin() const
566 {
567 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
568 }
569
570 int wxRichTextObject::GetRightMargin() const
571 {
572 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
573 }
574
575 int wxRichTextObject::GetTopMargin() const
576 {
577 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
578 }
579
580 int wxRichTextObject::GetBottomMargin() const
581 {
582 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
583 }
584
585 // Calculate the available content space in the given rectangle, given the
586 // margins, border and padding specified in the object's attributes.
587 wxRect wxRichTextObject::GetAvailableContentArea(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& outerRect) const
588 {
589 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
590 marginRect = outerRect;
591 wxRichTextAttr attr(GetAttributes());
592 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
593 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
594 return contentRect;
595 }
596
597 // Invalidate the buffer. With no argument, invalidates whole buffer.
598 void wxRichTextObject::Invalidate(const wxRichTextRange& invalidRange)
599 {
600 if (invalidRange != wxRICHTEXT_NONE)
601 {
602 // If this is a floating object, size may not be recalculated
603 // after floats have been collected in an early stage of Layout.
604 // So avoid resetting the cache for floating objects during layout.
605 if (!IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode())
606 SetCachedSize(wxDefaultSize);
607 SetMaxSize(wxDefaultSize);
608 SetMinSize(wxDefaultSize);
609 }
610 }
611
612 // Convert units in tenths of a millimetre to device units
613 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units) const
614 {
615 // Unscale
616 double scale = 1.0;
617 if (GetBuffer())
618 scale = GetBuffer()->GetScale() / GetBuffer()->GetDimensionScale();
619 int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units, scale);
620
621 return p;
622 }
623
624 // Convert units in tenths of a millimetre to device units
625 int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units, double scale)
626 {
627 // There are ppi pixels in 254.1 "1/10 mm"
628
629 double pixels = ((double) units * (double)ppi) / 254.1;
630 if (scale != 1.0)
631 pixels /= scale;
632
633 // If the result is very small, make it at least one pixel in size.
634 if (pixels == 0 && units > 0)
635 pixels = 1;
636
637 return (int) pixels;
638 }
639
640 // Convert units in pixels to tenths of a millimetre
641 int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC& dc, int pixels) const
642 {
643 int p = pixels;
644 double scale = 1.0;
645 if (GetBuffer())
646 scale = GetBuffer()->GetScale();
647
648 return ConvertPixelsToTenthsMM(dc.GetPPI().x, p, scale);
649 }
650
651 int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi, int pixels, double scale)
652 {
653 // There are ppi pixels in 254.1 "1/10 mm"
654
655 double p = double(pixels);
656
657 if (scale != 1.0)
658 p *= scale;
659
660 int units = int( p * 254.1 / (double) ppi );
661 return units;
662 }
663
664 // Draw the borders and background for the given rectangle and attributes.
665 // Width and height are taken to be the outer margin size, not the content.
666 bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, const wxRect& boxRect, int flags)
667 {
668 // Assume boxRect is the area around the content
669 wxRect marginRect = boxRect;
670 wxRect contentRect, borderRect, paddingRect, outlineRect;
671
672 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
673
674 // Margin is transparent. Draw background from margin.
675 if (attr.HasBackgroundColour() || (flags & wxRICHTEXT_DRAW_SELECTED))
676 {
677 wxColour colour;
678 if (flags & wxRICHTEXT_DRAW_SELECTED)
679 {
680 // TODO: get selection colour from control?
681 colour = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
682 }
683 else
684 colour = attr.GetBackgroundColour();
685
686 wxPen pen(colour);
687 wxBrush brush(colour);
688
689 dc.SetPen(pen);
690 dc.SetBrush(brush);
691 dc.DrawRectangle(borderRect);
692 }
693
694 if (flags & wxRICHTEXT_DRAW_GUIDELINES)
695 {
696 wxRichTextAttr editBorderAttr = attr;
697 // TODO: make guideline colour configurable
698 editBorderAttr.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY);
699 editBorderAttr.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS);
700 editBorderAttr.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID);
701
702 DrawBorder(dc, buffer, editBorderAttr.GetTextBoxAttr().GetBorder(), borderRect, flags);
703 }
704
705 if (attr.GetTextBoxAttr().GetBorder().IsValid())
706 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetBorder(), borderRect);
707
708 if (attr.GetTextBoxAttr().GetOutline().IsValid())
709 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetOutline(), outlineRect);
710
711 return true;
712 }
713
714 // Draw a border
715 bool wxRichTextObject::DrawBorder(wxDC& dc, wxRichTextBuffer* buffer, const wxTextAttrBorders& attr, const wxRect& rect, int WXUNUSED(flags))
716 {
717 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
718 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
719
720 if (attr.GetLeft().IsValid() && attr.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
721 {
722 borderLeft = converter.GetPixels(attr.GetLeft().GetWidth());
723 wxColour col(attr.GetLeft().GetColour());
724
725 // If pen width is > 1, resorts to a solid rectangle.
726 if (borderLeft == 1)
727 {
728 int penStyle = wxSOLID;
729 if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
730 penStyle = wxDOT;
731 else if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
732 penStyle = wxLONG_DASH;
733 wxPen pen(col, 1, penStyle);
734 dc.SetPen(pen);
735 dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height);
736
737 }
738 else if (borderLeft > 1)
739 {
740 wxPen pen(col);
741 wxBrush brush(col);
742 dc.SetPen(pen);
743 dc.SetBrush(brush);
744 dc.DrawRectangle(rect.x, rect.y, borderLeft, rect.height);
745 }
746 }
747
748 if (attr.GetRight().IsValid() && attr.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
749 {
750 borderRight = converter.GetPixels(attr.GetRight().GetWidth());
751
752 wxColour col(attr.GetRight().GetColour());
753
754 // If pen width is > 1, resorts to a solid rectangle.
755 if (borderRight == 1)
756 {
757 int penStyle = wxSOLID;
758 if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
759 penStyle = wxDOT;
760 else if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
761 penStyle = wxLONG_DASH;
762 wxPen pen(col, 1, penStyle);
763 dc.SetPen(pen);
764 dc.DrawLine(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height + 1);
765
766 }
767 else if (borderRight > 1)
768 {
769 wxPen pen(col);
770 wxBrush brush(col);
771 dc.SetPen(pen);
772 dc.SetBrush(brush);
773 dc.DrawRectangle(rect.x + rect.width - borderRight, rect.y, borderRight, rect.height);
774 }
775 }
776
777 if (attr.GetTop().IsValid() && attr.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
778 {
779 borderTop = converter.GetPixels(attr.GetTop().GetWidth());
780
781 wxColour col(attr.GetTop().GetColour());
782
783 // If pen width is > 1, resorts to a solid rectangle.
784 if (borderTop == 1)
785 {
786 int penStyle = wxSOLID;
787 if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
788 penStyle = wxDOT;
789 else if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
790 penStyle = wxLONG_DASH;
791 wxPen pen(col, 1, penStyle);
792 dc.SetPen(pen);
793 dc.DrawLine(rect.x, rect.y, rect.x + rect.width, rect.y);
794
795 }
796 else if (borderTop > 1)
797 {
798 wxPen pen(col);
799 wxBrush brush(col);
800 dc.SetPen(pen);
801 dc.SetBrush(brush);
802 dc.DrawRectangle(rect.x, rect.y, rect.width, borderTop);
803 }
804 }
805
806 if (attr.GetBottom().IsValid() && attr.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
807 {
808 borderBottom = converter.GetPixels(attr.GetBottom().GetWidth());
809 wxColour col(attr.GetBottom().GetColour());
810
811 // If pen width is > 1, resorts to a solid rectangle.
812 if (borderBottom == 1)
813 {
814 int penStyle = wxSOLID;
815 if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
816 penStyle = wxDOT;
817 else if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
818 penStyle = wxLONG_DASH;
819 wxPen pen(col, 1, penStyle);
820 dc.SetPen(pen);
821 dc.DrawLine(rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height);
822
823 }
824 else if (borderBottom > 1)
825 {
826 wxPen pen(col);
827 wxBrush brush(col);
828 dc.SetPen(pen);
829 dc.SetBrush(brush);
830 dc.DrawRectangle(rect.x, rect.y + rect.height - borderBottom, rect.width, borderBottom);
831 }
832 }
833
834 return true;
835 }
836
837 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
838 // or marginRect (outer), and the other must be the default rectangle (no width or height).
839 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
840 // is available.
841 //
842 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
843
844 bool wxRichTextObject::GetBoxRects(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, wxRect& marginRect, wxRect& borderRect, wxRect& contentRect, wxRect& paddingRect, wxRect& outlineRect)
845 {
846 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
847 int outlineLeft = 0, outlineRight = 0, outlineTop = 0, outlineBottom = 0;
848 int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
849 int marginLeft = 0, marginRight = 0, marginTop = 0, marginBottom = 0;
850
851 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
852
853 if (attr.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
854 marginLeft = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetLeft());
855 if (attr.GetTextBoxAttr().GetMargins().GetRight().IsValid())
856 marginRight = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetRight());
857 if (attr.GetTextBoxAttr().GetMargins().GetTop().IsValid())
858 marginTop = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetTop());
859 if (attr.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
860 marginBottom = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetBottom());
861
862 if (attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
863 borderLeft = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
864 if (attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
865 borderRight = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
866 if (attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
867 borderTop = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
868 if (attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
869 borderBottom = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
870
871 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
872 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
873 if (attr.GetTextBoxAttr().GetPadding().GetRight().IsValid())
874 paddingRight = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetRight());
875 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
876 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
877 if (attr.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
878 paddingBottom = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetBottom());
879
880 if (attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
881 outlineLeft = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
882 if (attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
883 outlineRight = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
884 if (attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
885 outlineTop = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
886 if (attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
887 outlineBottom = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
888
889 int leftTotal = marginLeft + borderLeft + paddingLeft;
890 int rightTotal = marginRight + borderRight + paddingRight;
891 int topTotal = marginTop + borderTop + paddingTop;
892 int bottomTotal = marginBottom + borderBottom + paddingBottom;
893
894 if (marginRect != wxRect())
895 {
896 contentRect.x = marginRect.x + leftTotal;
897 contentRect.y = marginRect.y + topTotal;
898 contentRect.width = marginRect.width - (leftTotal + rightTotal);
899 contentRect.height = marginRect.height - (topTotal + bottomTotal);
900 }
901 else
902 {
903 marginRect.x = contentRect.x - leftTotal;
904 marginRect.y = contentRect.y - topTotal;
905 marginRect.width = contentRect.width + (leftTotal + rightTotal);
906 marginRect.height = contentRect.height + (topTotal + bottomTotal);
907 }
908
909 borderRect.x = marginRect.x + marginLeft;
910 borderRect.y = marginRect.y + marginTop;
911 borderRect.width = marginRect.width - (marginLeft + marginRight);
912 borderRect.height = marginRect.height - (marginTop + marginBottom);
913
914 paddingRect.x = marginRect.x + marginLeft + borderLeft;
915 paddingRect.y = marginRect.y + marginTop + borderTop;
916 paddingRect.width = marginRect.width - (marginLeft + marginRight + borderLeft + borderRight);
917 paddingRect.height = marginRect.height - (marginTop + marginBottom + borderTop + borderBottom);
918
919 // The outline is outside the margin and doesn't influence the overall box position or content size.
920 outlineRect.x = marginRect.x - outlineLeft;
921 outlineRect.y = marginRect.y - outlineTop;
922 outlineRect.width = marginRect.width + (outlineLeft + outlineRight);
923 outlineRect.height = marginRect.height + (outlineTop + outlineBottom);
924
925 return true;
926 }
927
928 // Get the total margin for the object in pixels, taking into account margin, padding and border size
929 bool wxRichTextObject::GetTotalMargin(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, int& leftMargin, int& rightMargin,
930 int& topMargin, int& bottomMargin)
931 {
932 // Assume boxRect is the area around the content
933 wxRect contentRect, marginRect, borderRect, paddingRect, outlineRect;
934 marginRect = wxRect(0, 0, 1000, 1000);
935
936 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
937
938 leftMargin = contentRect.GetLeft() - marginRect.GetLeft();
939 rightMargin = marginRect.GetRight() - contentRect.GetRight();
940 topMargin = contentRect.GetTop() - marginRect.GetTop();
941 bottomMargin = marginRect.GetBottom() - contentRect.GetBottom();
942
943 return true;
944 }
945
946 // Returns the rectangle which the child has available to it given restrictions specified in the
947 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
948 // availableContainerSpace might be a parent that the cell has to compute its width relative to.
949 // E.g. a cell that's 50% of its parent.
950 wxRect wxRichTextObject::AdjustAvailableSpace(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& WXUNUSED(parentAttr), const wxRichTextAttr& childAttr, const wxRect& availableParentSpace, const wxRect& availableContainerSpace)
951 {
952 wxRect rect = availableParentSpace;
953 double scale = 1.0;
954 if (buffer)
955 scale = buffer->GetScale();
956
957 wxTextAttrDimensionConverter converter(dc, scale, availableContainerSpace.GetSize());
958
959 if (childAttr.GetTextBoxAttr().GetWidth().IsValid())
960 rect.width = converter.GetPixels(childAttr.GetTextBoxAttr().GetWidth());
961
962 if (childAttr.GetTextBoxAttr().GetHeight().IsValid())
963 rect.height = converter.GetPixels(childAttr.GetTextBoxAttr().GetHeight());
964
965 // Can specify either left or right for the position (we're assuming we can't
966 // set the left and right edges to effectively set the size. Would we want to do that?)
967 if (childAttr.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
968 {
969 rect.x = rect.x + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetLeft());
970 }
971 else if (childAttr.GetTextBoxAttr().GetPosition().GetRight().IsValid())
972 {
973 int x = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetRight());
974 if (childAttr.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
975 rect.x = availableContainerSpace.x + availableContainerSpace.width - rect.width;
976 else
977 rect.x += x;
978 }
979
980 if (childAttr.GetTextBoxAttr().GetPosition().GetTop().IsValid())
981 {
982 rect.y = rect.y + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetTop());
983 }
984 else if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
985 {
986 int y = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetBottom());
987 if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
988 rect.y = availableContainerSpace.y + availableContainerSpace.height - rect.height;
989 else
990 rect.y += y;
991 }
992
993 if (rect.GetWidth() > availableParentSpace.GetWidth())
994 rect.SetWidth(availableParentSpace.GetWidth());
995
996 return rect;
997 }
998
999 // Dump to output stream for debugging
1000 void wxRichTextObject::Dump(wxTextOutputStream& stream)
1001 {
1002 stream << GetClassInfo()->GetClassName() << wxT("\n");
1003 stream << wxString::Format(wxT("Size: %d,%d. Position: %d,%d, Range: %ld,%ld"), m_size.x, m_size.y, m_pos.x, m_pos.y, m_range.GetStart(), m_range.GetEnd()) << wxT("\n");
1004 stream << wxString::Format(wxT("Text colour: %d,%d,%d."), (int) m_attributes.GetTextColour().Red(), (int) m_attributes.GetTextColour().Green(), (int) m_attributes.GetTextColour().Blue()) << wxT("\n");
1005 }
1006
1007 // Gets the containing buffer
1008 wxRichTextBuffer* wxRichTextObject::GetBuffer() const
1009 {
1010 const wxRichTextObject* obj = this;
1011 while (obj && !wxDynamicCast(obj, wxRichTextBuffer))
1012 obj = obj->GetParent();
1013 return wxDynamicCast(obj, wxRichTextBuffer);
1014 }
1015
1016 // Get the absolute object position, by traversing up the child/parent hierarchy
1017 wxPoint wxRichTextObject::GetAbsolutePosition() const
1018 {
1019 wxPoint pt = GetPosition();
1020
1021 wxRichTextObject* p = GetParent();
1022 while (p)
1023 {
1024 pt = pt + p->GetPosition();
1025 p = p->GetParent();
1026 }
1027
1028 return pt;
1029 }
1030
1031 // Hit-testing: returns a flag indicating hit test details, plus
1032 // information about position
1033 int wxRichTextObject::HitTest(wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int WXUNUSED(flags))
1034 {
1035 if (!IsShown())
1036 return wxRICHTEXT_HITTEST_NONE;
1037
1038 wxRect rect = GetRect();
1039 if (pt.x >= rect.x && pt.x < rect.x + rect.width &&
1040 pt.y >= rect.y && pt.y < rect.y + rect.height)
1041 {
1042 *obj = this;
1043 *contextObj = GetParentContainer();
1044 textPosition = GetRange().GetStart();
1045 return wxRICHTEXT_HITTEST_ON;
1046 }
1047 else
1048 return wxRICHTEXT_HITTEST_NONE;
1049 }
1050
1051 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1052 // lays out the object again using the maximum ('best') size
1053 bool wxRichTextObject::LayoutToBestSize(wxDC& dc, wxRichTextDrawingContext& context, wxRichTextBuffer* buffer,
1054 const wxRichTextAttr& parentAttr, const wxRichTextAttr& attr,
1055 const wxRect& availableParentSpace, const wxRect& availableContainerSpace,
1056 int style)
1057 {
1058 wxRect availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, attr, availableParentSpace, availableContainerSpace);
1059 wxRect originalAvailableRect = availableChildRect;
1060 Layout(dc, context, availableChildRect, availableContainerSpace, style);
1061
1062 wxSize maxSize = GetMaxSize();
1063
1064 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1065 // on this basis
1066 if (!attr.GetTextBoxAttr().GetWidth().IsValid() && maxSize.x < availableChildRect.width)
1067 {
1068 // Redo the layout with a fixed, minimum size this time.
1069 Invalidate(wxRICHTEXT_ALL);
1070 wxRichTextAttr newAttr(attr);
1071 newAttr.GetTextBoxAttr().GetWidth().SetValue(maxSize.x, wxTEXT_ATTR_UNITS_PIXELS);
1072 newAttr.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE);
1073
1074 availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, newAttr, availableParentSpace, availableContainerSpace);
1075
1076 // If a paragraph, align the whole paragraph.
1077 // Problem with this: if we're limited by a floating object, a line may be centered
1078 // w.r.t. the smaller resulting box rather than the actual available width.
1079 // FIXME: aligning whole paragraph not compatible with floating objects
1080 if (attr.HasAlignment() && (!wxRichTextBuffer::GetFloatingLayoutMode() || (GetContainer()->GetFloatCollector() && !GetContainer()->GetFloatCollector()->HasFloats())))
1081 {
1082 // centering, right-justification
1083 if (attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
1084 {
1085 availableChildRect.x = (originalAvailableRect.GetWidth() - availableChildRect.GetWidth())/2 + availableChildRect.x;
1086 }
1087 else if (attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
1088 {
1089 availableChildRect.x = availableChildRect.x + originalAvailableRect.GetWidth() - availableChildRect.GetWidth();
1090 }
1091 }
1092
1093 Layout(dc, context, availableChildRect, availableContainerSpace, style);
1094 }
1095
1096 /*
1097 __________________
1098 | ____________ |
1099 | | | |
1100
1101
1102 */
1103
1104 return true;
1105 }
1106
1107 // Move the object recursively, by adding the offset from old to new
1108 void wxRichTextObject::Move(const wxPoint& pt)
1109 {
1110 SetPosition(pt);
1111 }
1112
1113
1114 /*!
1115 * wxRichTextCompositeObject
1116 * This is the base for drawable objects.
1117 */
1118
1119 IMPLEMENT_CLASS(wxRichTextCompositeObject, wxRichTextObject)
1120
1121 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject* parent):
1122 wxRichTextObject(parent)
1123 {
1124 }
1125
1126 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1127 {
1128 DeleteChildren();
1129 }
1130
1131 /// Get the nth child
1132 wxRichTextObject* wxRichTextCompositeObject::GetChild(size_t n) const
1133 {
1134 wxASSERT ( n < m_children.GetCount() );
1135
1136 return m_children.Item(n)->GetData();
1137 }
1138
1139 /// Append a child, returning the position
1140 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject* child)
1141 {
1142 m_children.Append(child);
1143 child->SetParent(this);
1144 return m_children.GetCount() - 1;
1145 }
1146
1147 /// Insert the child in front of the given object, or at the beginning
1148 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject* child, wxRichTextObject* inFrontOf)
1149 {
1150 if (inFrontOf)
1151 {
1152 wxRichTextObjectList::compatibility_iterator node = m_children.Find(inFrontOf);
1153 m_children.Insert(node, child);
1154 }
1155 else
1156 m_children.Insert(child);
1157 child->SetParent(this);
1158
1159 return true;
1160 }
1161
1162 /// Delete the child
1163 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject* child, bool deleteChild)
1164 {
1165 wxRichTextObjectList::compatibility_iterator node = m_children.Find(child);
1166 if (node)
1167 {
1168 wxRichTextObject* obj = node->GetData();
1169 m_children.Erase(node);
1170 if (deleteChild)
1171 delete obj;
1172
1173 return true;
1174 }
1175 return false;
1176 }
1177
1178 /// Delete all children
1179 bool wxRichTextCompositeObject::DeleteChildren()
1180 {
1181 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1182 while (node)
1183 {
1184 wxRichTextObjectList::compatibility_iterator oldNode = node;
1185
1186 wxRichTextObject* child = node->GetData();
1187 child->Dereference(); // Only delete if reference count is zero
1188
1189 node = node->GetNext();
1190 m_children.Erase(oldNode);
1191 }
1192
1193 return true;
1194 }
1195
1196 /// Get the child count
1197 size_t wxRichTextCompositeObject::GetChildCount() const
1198 {
1199 return m_children.GetCount();
1200 }
1201
1202 /// Copy
1203 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject& obj)
1204 {
1205 wxRichTextObject::Copy(obj);
1206
1207 DeleteChildren();
1208
1209 wxRichTextObjectList::compatibility_iterator node = obj.m_children.GetFirst();
1210 while (node)
1211 {
1212 wxRichTextObject* child = node->GetData();
1213 wxRichTextObject* newChild = child->Clone();
1214 newChild->SetParent(this);
1215 m_children.Append(newChild);
1216
1217 node = node->GetNext();
1218 }
1219 }
1220
1221 /// Hit-testing: returns a flag indicating hit test details, plus
1222 /// information about position
1223 int wxRichTextCompositeObject::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
1224 {
1225 if (!IsShown())
1226 return wxRICHTEXT_HITTEST_NONE;
1227
1228 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1229 while (node)
1230 {
1231 wxRichTextObject* child = node->GetData();
1232
1233 if (child->IsShown() && child->IsTopLevel() && (flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS))
1234 {
1235 // Just check if we hit the overall object
1236 int ret = child->wxRichTextObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
1237 if (ret != wxRICHTEXT_HITTEST_NONE)
1238 return ret;
1239 }
1240 else if (child->IsShown())
1241 {
1242 int ret = child->HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
1243 if (ret != wxRICHTEXT_HITTEST_NONE)
1244 return ret;
1245 }
1246
1247 node = node->GetNext();
1248 }
1249
1250 return wxRICHTEXT_HITTEST_NONE;
1251 }
1252
1253 /// Finds the absolute position and row height for the given character position
1254 bool wxRichTextCompositeObject::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
1255 {
1256 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1257 while (node)
1258 {
1259 wxRichTextObject* child = node->GetData();
1260
1261 // Don't recurse if the child is a top-level object,
1262 // such as a text box, because the character position will no longer
1263 // apply. By definition, a top-level object has its own range of
1264 // character positions.
1265 if (!child->IsTopLevel() && child->FindPosition(dc, context, index, pt, height, forceLineStart))
1266 return true;
1267
1268 node = node->GetNext();
1269 }
1270
1271 return false;
1272 }
1273
1274 /// Calculate range
1275 void wxRichTextCompositeObject::CalculateRange(long start, long& end)
1276 {
1277 long current = start;
1278 long lastEnd = current;
1279
1280 if (IsTopLevel())
1281 {
1282 current = 0;
1283 lastEnd = 0;
1284 }
1285
1286 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1287 while (node)
1288 {
1289 wxRichTextObject* child = node->GetData();
1290 long childEnd = 0;
1291
1292 child->CalculateRange(current, childEnd);
1293 lastEnd = childEnd;
1294
1295 current = childEnd + 1;
1296
1297 node = node->GetNext();
1298 }
1299
1300 if (IsTopLevel())
1301 {
1302 // A top-level object always has a range of size 1,
1303 // because its children don't count at this level.
1304 end = start;
1305 m_range.SetRange(start, start);
1306
1307 // An object with no children has zero length
1308 if (m_children.GetCount() == 0)
1309 lastEnd --;
1310 m_ownRange.SetRange(0, lastEnd);
1311 }
1312 else
1313 {
1314 end = lastEnd;
1315
1316 // An object with no children has zero length
1317 if (m_children.GetCount() == 0)
1318 end --;
1319
1320 m_range.SetRange(start, end);
1321 }
1322 }
1323
1324 /// Delete range from layout.
1325 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange& range)
1326 {
1327 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1328
1329 while (node)
1330 {
1331 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
1332 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1333
1334 // Delete the range in each paragraph
1335
1336 // When a chunk has been deleted, internally the content does not
1337 // now match the ranges.
1338 // However, so long as deletion is not done on the same object twice this is OK.
1339 // If you may delete content from the same object twice, recalculate
1340 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1341 // adjust the range you're deleting accordingly.
1342
1343 if (!obj->GetRange().IsOutside(range))
1344 {
1345 // No need to delete within a top-level object; just removing this object will do fine
1346 if (!obj->IsTopLevel())
1347 obj->DeleteRange(range);
1348
1349 // Delete an empty object, or paragraph within this range.
1350 if (obj->IsEmpty() ||
1351 (range.GetStart() <= obj->GetRange().GetStart() && range.GetEnd() >= obj->GetRange().GetEnd()))
1352 {
1353 // An empty paragraph has length 1, so won't be deleted unless the
1354 // whole range is deleted.
1355 RemoveChild(obj, true);
1356 }
1357 }
1358
1359 node = next;
1360 }
1361
1362 return true;
1363 }
1364
1365 /// Get any text in this object for the given range
1366 wxString wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange& range) const
1367 {
1368 wxString text;
1369 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1370 while (node)
1371 {
1372 wxRichTextObject* child = node->GetData();
1373 wxRichTextRange childRange = range;
1374 if (!child->GetRange().IsOutside(range))
1375 {
1376 childRange.LimitTo(child->GetRange());
1377
1378 wxString childText = child->GetTextForRange(childRange);
1379
1380 text += childText;
1381 }
1382 node = node->GetNext();
1383 }
1384
1385 return text;
1386 }
1387
1388 /// Get the child object at the given character position
1389 wxRichTextObject* wxRichTextCompositeObject::GetChildAtPosition(long pos) const
1390 {
1391 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1392 while (node)
1393 {
1394 wxRichTextObject* child = node->GetData();
1395 if (child->GetRange().GetStart() == pos)
1396 return child;
1397 node = node->GetNext();
1398 }
1399 return NULL;
1400 }
1401
1402 /// Recursively merge all pieces that can be merged.
1403 bool wxRichTextCompositeObject::Defragment(wxRichTextDrawingContext& context, const wxRichTextRange& range)
1404 {
1405 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1406 while (node)
1407 {
1408 wxRichTextObject* child = node->GetData();
1409 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
1410 {
1411 wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject);
1412 if (composite)
1413 composite->Defragment(context);
1414
1415 // Optimization: if there are no virtual attributes, we won't need to
1416 // to split objects in order to paint individually attributed chunks.
1417 // So only merge in this case.
1418 if (!context.GetVirtualAttributesEnabled())
1419 {
1420 if (node->GetNext())
1421 {
1422 wxRichTextObject* nextChild = node->GetNext()->GetData();
1423 if (child->CanMerge(nextChild, context) && child->Merge(nextChild, context))
1424 {
1425 nextChild->Dereference();
1426 m_children.Erase(node->GetNext());
1427 }
1428 else
1429 node = node->GetNext();
1430 }
1431 else
1432 node = node->GetNext();
1433 }
1434 else
1435 {
1436 // If we might have virtual attributes, we first see if we have to split
1437 // objects so that they may be painted with potential virtual attributes,
1438 // since text objects can only draw or measure with a single attributes object
1439 // at a time.
1440 wxRichTextObject* childAfterSplit = child;
1441 if (child->CanSplit(context))
1442 {
1443 childAfterSplit = child->Split(context);
1444 node = m_children.Find(childAfterSplit);
1445 }
1446
1447 if (node->GetNext())
1448 {
1449 wxRichTextObject* nextChild = node->GetNext()->GetData();
1450 wxRichTextObjectList::compatibility_iterator nextNode = node->GetNext();
1451
1452 // First split child and nextChild so we have smaller fragments to merge.
1453 // Then Merge only has to test per-object virtual attributes
1454 // because for an object with all the same sub-object attributes,
1455 // then any general virtual attributes should be merged with sub-objects by
1456 // the implementation.
1457
1458 wxRichTextObject* nextChildAfterSplit = nextChild;
1459
1460 if (nextChildAfterSplit->CanSplit(context))
1461 nextChildAfterSplit = nextChild->Split(context);
1462
1463 bool splitNextChild = nextChild != nextChildAfterSplit;
1464
1465 // See if we can merge this new fragment with (perhaps the first part of) the next object.
1466 // Note that we use nextChild because if we had split nextChild, the first object always
1467 // remains (and further parts are appended). However we must use childAfterSplit since
1468 // it's the last part of a possibly split child.
1469
1470 if (childAfterSplit->CanMerge(nextChild, context) && childAfterSplit->Merge(nextChild, context))
1471 {
1472 nextChild->Dereference();
1473 m_children.Erase(node->GetNext());
1474
1475 // Don't set node -- we'll see if we can merge again with the next
1476 // child. UNLESS we split this or the next child, in which case we know we have to
1477 // move on to the end of the next child.
1478 if (splitNextChild)
1479 node = m_children.Find(nextChildAfterSplit);
1480 }
1481 else
1482 {
1483 if (splitNextChild)
1484 node = m_children.Find(nextChildAfterSplit); // start from the last object in the split
1485 else
1486 node = node->GetNext();
1487 }
1488 }
1489 else
1490 node = node->GetNext();
1491 }
1492 }
1493 else
1494 node = node->GetNext();
1495 }
1496
1497 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1498 if (GetChildCount() > 1)
1499 {
1500 node = m_children.GetFirst();
1501 while (node)
1502 {
1503 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1504 wxRichTextObject* child = node->GetData();
1505 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
1506 {
1507 if (child->IsEmpty())
1508 {
1509 child->Dereference();
1510 m_children.Erase(node);
1511 }
1512 node = next;
1513 }
1514 else
1515 node = node->GetNext();
1516 }
1517 }
1518
1519 return true;
1520 }
1521
1522 /// Dump to output stream for debugging
1523 void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
1524 {
1525 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1526 while (node)
1527 {
1528 wxRichTextObject* child = node->GetData();
1529 child->Dump(stream);
1530 node = node->GetNext();
1531 }
1532 }
1533
1534 /// Get/set the object size for the given range. Returns false if the range
1535 /// is invalid for this object.
1536 bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* partialExtents) const
1537 {
1538 if (!range.IsWithin(GetRange()))
1539 return false;
1540
1541 wxSize sz;
1542
1543 wxArrayInt childExtents;
1544 wxArrayInt* p;
1545 if (partialExtents)
1546 p = & childExtents;
1547 else
1548 p = NULL;
1549
1550 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1551 while (node)
1552 {
1553 wxRichTextObject* child = node->GetData();
1554 if (!child->GetRange().IsOutside(range))
1555 {
1556 // Floating objects have a zero size within the paragraph.
1557 if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
1558 {
1559 if (partialExtents)
1560 {
1561 int lastSize;
1562 if (partialExtents->GetCount() > 0)
1563 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1564 else
1565 lastSize = 0;
1566
1567 partialExtents->Add(0 /* zero size */ + lastSize);
1568 }
1569 }
1570 else
1571 {
1572 wxSize childSize;
1573
1574 wxRichTextRange rangeToUse = range;
1575 rangeToUse.LimitTo(child->GetRange());
1576 if (child->IsTopLevel())
1577 rangeToUse = child->GetOwnRange();
1578
1579 int childDescent = 0;
1580
1581 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1582 // but it's only going to be used after caching has taken place.
1583 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
1584 {
1585 childDescent = child->GetDescent();
1586 childSize = child->GetCachedSize();
1587
1588 sz.y = wxMax(sz.y, childSize.y);
1589 sz.x += childSize.x;
1590 descent = wxMax(descent, childDescent);
1591 }
1592 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize, p))
1593 {
1594 sz.y = wxMax(sz.y, childSize.y);
1595 sz.x += childSize.x;
1596 descent = wxMax(descent, childDescent);
1597
1598 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange() || child->IsTopLevel()))
1599 {
1600 child->SetCachedSize(childSize);
1601 child->SetDescent(childDescent);
1602 }
1603
1604 if (partialExtents)
1605 {
1606 int lastSize;
1607 if (partialExtents->GetCount() > 0)
1608 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1609 else
1610 lastSize = 0;
1611
1612 size_t i;
1613 for (i = 0; i < childExtents.GetCount(); i++)
1614 {
1615 partialExtents->Add(childExtents[i] + lastSize);
1616 }
1617 }
1618 }
1619 }
1620
1621 if (p)
1622 p->Clear();
1623 }
1624
1625 node = node->GetNext();
1626 }
1627 size = sz;
1628 return true;
1629 }
1630
1631 // Invalidate the buffer. With no argument, invalidates whole buffer.
1632 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange& invalidRange)
1633 {
1634 wxRichTextObject::Invalidate(invalidRange);
1635
1636 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1637 while (node)
1638 {
1639 wxRichTextObject* child = node->GetData();
1640 if (invalidRange != wxRICHTEXT_ALL && invalidRange != wxRICHTEXT_NONE && child->GetRange().IsOutside(invalidRange))
1641 {
1642 // Skip
1643 }
1644 else if (child->IsTopLevel())
1645 {
1646 if (wxRichTextBuffer::GetFloatingLayoutMode() && child->IsFloating() && GetBuffer()->GetFloatCollector() && GetBuffer()->GetFloatCollector()->HasFloat(child))
1647 {
1648 // Don't invalidate subhierarchy if we've already been laid out
1649 }
1650 else
1651 {
1652 if (invalidRange == wxRICHTEXT_NONE)
1653 child->Invalidate(wxRICHTEXT_NONE);
1654 else
1655 child->Invalidate(wxRICHTEXT_ALL); // All children must be invalidated if within parent range
1656 }
1657 }
1658 else
1659 child->Invalidate(invalidRange);
1660 node = node->GetNext();
1661 }
1662 }
1663
1664 // Move the object recursively, by adding the offset from old to new
1665 void wxRichTextCompositeObject::Move(const wxPoint& pt)
1666 {
1667 wxPoint oldPos = GetPosition();
1668 SetPosition(pt);
1669 wxPoint offset = pt - oldPos;
1670
1671 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1672 while (node)
1673 {
1674 wxRichTextObject* child = node->GetData();
1675 wxPoint childPos = child->GetPosition() + offset;
1676 child->Move(childPos);
1677 node = node->GetNext();
1678 }
1679 }
1680
1681
1682 /*!
1683 * wxRichTextParagraphLayoutBox
1684 * This box knows how to lay out paragraphs.
1685 */
1686
1687 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextCompositeObject)
1688
1689 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject* parent):
1690 wxRichTextCompositeObject(parent)
1691 {
1692 Init();
1693 }
1694
1695 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1696 {
1697 if (m_floatCollector)
1698 {
1699 delete m_floatCollector;
1700 m_floatCollector = NULL;
1701 }
1702 }
1703
1704 /// Initialize the object.
1705 void wxRichTextParagraphLayoutBox::Init()
1706 {
1707 m_ctrl = NULL;
1708
1709 // For now, assume is the only box and has no initial size.
1710 m_range = wxRichTextRange(0, -1);
1711 m_ownRange = wxRichTextRange(0, -1);
1712
1713 m_invalidRange = wxRICHTEXT_ALL;
1714
1715 m_partialParagraph = false;
1716 m_floatCollector = NULL;
1717 }
1718
1719 void wxRichTextParagraphLayoutBox::Clear()
1720 {
1721 DeleteChildren();
1722
1723 if (m_floatCollector)
1724 delete m_floatCollector;
1725 m_floatCollector = NULL;
1726 m_partialParagraph = false;
1727 }
1728
1729 /// Copy
1730 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
1731 {
1732 Clear();
1733
1734 wxRichTextCompositeObject::Copy(obj);
1735
1736 m_partialParagraph = obj.m_partialParagraph;
1737 m_defaultAttributes = obj.m_defaultAttributes;
1738 }
1739
1740 // Gather information about floating objects; only gather floats for those paragraphs that
1741 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1742 // during layout.
1743 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect& availableRect, wxRichTextObject* untilObj)
1744 {
1745 if (m_floatCollector != NULL)
1746 delete m_floatCollector;
1747 m_floatCollector = new wxRichTextFloatCollector(availableRect);
1748 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1749 // Only gather floats up to the point we'll start formatting paragraphs.
1750 while (untilObj && node && node->GetData() != untilObj)
1751 {
1752 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1753 wxASSERT (child != NULL);
1754 if (child)
1755 m_floatCollector->CollectFloat(child);
1756 node = node->GetNext();
1757 }
1758
1759 return true;
1760 }
1761
1762 // Returns the style sheet associated with the overall buffer.
1763 wxRichTextStyleSheet* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1764 {
1765 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet*) NULL;
1766 }
1767
1768 // Get the number of floating objects at this level
1769 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1770 {
1771 if (m_floatCollector)
1772 return m_floatCollector->GetFloatingObjectCount();
1773 else
1774 return 0;
1775 }
1776
1777 // Get a list of floating objects
1778 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList& objects) const
1779 {
1780 if (m_floatCollector)
1781 {
1782 return m_floatCollector->GetFloatingObjects(objects);
1783 }
1784 else
1785 return false;
1786 }
1787
1788 // Calculate ranges
1789 void wxRichTextParagraphLayoutBox::UpdateRanges()
1790 {
1791 long start = 0;
1792 if (GetParent())
1793 start = GetRange().GetStart();
1794 long end;
1795 CalculateRange(start, end);
1796 }
1797
1798 // HitTest
1799 int wxRichTextParagraphLayoutBox::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
1800 {
1801 if (!IsShown())
1802 return wxRICHTEXT_HITTEST_NONE;
1803
1804 int ret = wxRICHTEXT_HITTEST_NONE;
1805 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector && (flags & wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS) == 0)
1806 ret = m_floatCollector->HitTest(dc, context, pt, textPosition, obj, flags);
1807
1808 if (ret == wxRICHTEXT_HITTEST_NONE)
1809 return wxRichTextCompositeObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
1810 else
1811 {
1812 *contextObj = this;
1813 return ret;
1814 }
1815 }
1816
1817 /// Draw the floating objects
1818 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
1819 {
1820 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector)
1821 m_floatCollector->Draw(dc, context, range, selection, rect, descent, style);
1822 }
1823
1824 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextObject* obj)
1825 {
1826 if (from == to)
1827 return;
1828
1829 from->RemoveChild(obj);
1830 to->AppendChild(obj);
1831 }
1832
1833 /// Draw the item
1834 bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
1835 {
1836 if (!IsShown())
1837 return true;
1838
1839 wxRect thisRect(GetPosition(), GetCachedSize());
1840
1841 wxRichTextAttr attr(GetAttributes());
1842 context.ApplyVirtualAttributes(attr, this);
1843
1844 int flags = style;
1845 if (selection.IsValid() && GetParentContainer() != this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1846 flags |= wxRICHTEXT_DRAW_SELECTED;
1847
1848 // Don't draw guidelines if at top level
1849 int theseFlags = flags;
1850 if (!GetParent())
1851 theseFlags &= ~wxRICHTEXT_DRAW_GUIDELINES;
1852 DrawBoxAttributes(dc, GetBuffer(), attr, thisRect, theseFlags);
1853
1854 if (wxRichTextBuffer::GetFloatingLayoutMode())
1855 DrawFloats(dc, context, range, selection, rect, descent, style);
1856
1857 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1858 while (node)
1859 {
1860 wxRichTextObject* child = node->GetData();
1861
1862 if (child && !child->GetRange().IsOutside(range))
1863 {
1864 wxRect childRect(child->GetPosition(), child->GetCachedSize());
1865 wxRichTextRange childRange = range;
1866 if (child->IsTopLevel())
1867 {
1868 childRange = child->GetOwnRange();
1869 }
1870
1871 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetTop() > rect.GetBottom())
1872 {
1873 // Stop drawing
1874 break;
1875 }
1876 else if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetBottom() < rect.GetTop())
1877 {
1878 // Skip
1879 }
1880 else
1881 child->Draw(dc, context, childRange, selection, rect, descent, style);
1882 }
1883
1884 node = node->GetNext();
1885 }
1886 return true;
1887 }
1888
1889 /// Lay the item out
1890 bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
1891 {
1892 SetPosition(rect.GetPosition());
1893
1894 if (!IsShown())
1895 return true;
1896
1897 wxRect availableSpace;
1898 bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
1899
1900 wxRichTextAttr attr(GetAttributes());
1901 context.ApplyVirtualAttributes(attr, this);
1902
1903 // If only laying out a specific area, the passed rect has a different meaning:
1904 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1905 // so that during a size, only the visible part will be relaid out, or
1906 // it would take too long causing flicker. As an approximation, we assume that
1907 // everything up to the start of the visible area is laid out correctly.
1908 if (formatRect)
1909 {
1910 wxRect rect2(0, 0, rect.width, rect.height);
1911 availableSpace = GetAvailableContentArea(dc, context, rect2);
1912
1913 // Invalidate the part of the buffer from the first visible line
1914 // to the end. If other parts of the buffer are currently invalid,
1915 // then they too will be taken into account if they are above
1916 // the visible point.
1917 long startPos = 0;
1918 wxRichTextLine* line = GetLineAtYPosition(rect.y);
1919 if (line)
1920 startPos = line->GetAbsoluteRange().GetStart();
1921
1922 Invalidate(wxRichTextRange(startPos, GetOwnRange().GetEnd()));
1923 }
1924 else
1925 {
1926 availableSpace = GetAvailableContentArea(dc, context, rect);
1927 }
1928
1929 // Fix the width if we're at the top level
1930 if (!GetParent())
1931 attr.GetTextBoxAttr().GetWidth().SetValue(rect.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
1932
1933 int leftMargin, rightMargin, topMargin, bottomMargin;
1934 wxRichTextObject::GetTotalMargin(dc, GetBuffer(), attr, leftMargin, rightMargin,
1935 topMargin, bottomMargin);
1936
1937 int maxWidth = 0;
1938 int maxHeight = 0;
1939
1940 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1941 int maxMaxWidth = 0;
1942
1943 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1944 int maxMinWidth = 0;
1945
1946 // If we have vertical alignment, we must recalculate everything.
1947 bool hasVerticalAlignment = (attr.GetTextBoxAttr().HasVerticalAlignment() &&
1948 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP));
1949
1950 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1951
1952 bool layoutAll = true;
1953
1954 // Get invalid range, rounding to paragraph start/end.
1955 wxRichTextRange invalidRange = GetInvalidRange(true);
1956
1957 if (invalidRange == wxRICHTEXT_NONE && !formatRect)
1958 return true;
1959
1960 if (invalidRange == wxRICHTEXT_ALL || hasVerticalAlignment)
1961 layoutAll = true;
1962 else // If we know what range is affected, start laying out from that point on.
1963 if (invalidRange.GetStart() >= GetOwnRange().GetStart())
1964 {
1965 wxRichTextParagraph* firstParagraph = GetParagraphAtPosition(invalidRange.GetStart());
1966 if (firstParagraph)
1967 {
1968 wxRichTextObjectList::compatibility_iterator firstNode = m_children.Find(firstParagraph);
1969 wxRichTextObjectList::compatibility_iterator previousNode;
1970 if ( firstNode )
1971 previousNode = firstNode->GetPrevious();
1972 if (firstNode)
1973 {
1974 if (previousNode)
1975 {
1976 wxRichTextParagraph* previousParagraph = wxDynamicCast(previousNode->GetData(), wxRichTextParagraph);
1977 availableSpace.y = previousParagraph->GetPosition().y + previousParagraph->GetCachedSize().y;
1978 }
1979
1980 // Now we're going to start iterating from the first affected paragraph.
1981 node = firstNode;
1982
1983 layoutAll = false;
1984 }
1985 }
1986 }
1987
1988 // Gather information about only those floating objects that will not be formatted,
1989 // after which floats will be gathered per-paragraph during layout.
1990 if (wxRichTextBuffer::GetFloatingLayoutMode())
1991 UpdateFloatingObjects(availableSpace, node ? node->GetData() : (wxRichTextObject*) NULL);
1992
1993 // A way to force speedy rest-of-buffer layout (the 'else' below)
1994 bool forceQuickLayout = false;
1995
1996 // First get the size of the paragraphs we won't be laying out
1997 wxRichTextObjectList::compatibility_iterator n = m_children.GetFirst();
1998 while (n && n != node)
1999 {
2000 wxRichTextParagraph* child = wxDynamicCast(n->GetData(), wxRichTextParagraph);
2001 if (child)
2002 {
2003 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2004 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2005 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2006 }
2007 n = n->GetNext();
2008 }
2009
2010 while (node)
2011 {
2012 // Assume this box only contains paragraphs
2013
2014 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2015 // Unsure if this is needed
2016 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
2017
2018 if (child && child->IsShown())
2019 {
2020 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
2021 if ( !forceQuickLayout &&
2022 (layoutAll ||
2023 child->GetLines().IsEmpty() ||
2024 !child->GetRange().IsOutside(invalidRange)) )
2025 {
2026 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2027 // lays out the object again using the minimum size
2028 child->LayoutToBestSize(dc, context, GetBuffer(),
2029 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
2030
2031 // Layout must set the cached size
2032 availableSpace.y += child->GetCachedSize().y;
2033 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2034 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2035 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2036
2037 // If we're just formatting the visible part of the buffer,
2038 // and we're now past the bottom of the window, and we don't have any
2039 // floating objects (since they may cause wrapping to change for the rest of the
2040 // the buffer), start quick layout.
2041 if (!hasVerticalAlignment && formatRect && child->GetPosition().y > rect.GetBottom() && GetFloatingObjectCount() == 0)
2042 forceQuickLayout = true;
2043 }
2044 else
2045 {
2046 // We're outside the immediately affected range, so now let's just
2047 // move everything up or down. This assumes that all the children have previously
2048 // been laid out and have wrapped line lists associated with them.
2049 // TODO: check all paragraphs before the affected range.
2050
2051 int inc = availableSpace.y - child->GetPosition().y;
2052
2053 while (node)
2054 {
2055 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2056 if (child)
2057 {
2058 if (child->GetLines().GetCount() == 0)
2059 {
2060 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2061 // lays out the object again using the minimum size
2062 child->LayoutToBestSize(dc, context, GetBuffer(),
2063 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
2064
2065 //child->Layout(dc, availableChildRect, style);
2066 }
2067 else
2068 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + inc));
2069
2070 availableSpace.y += child->GetCachedSize().y;
2071 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2072 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2073 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2074 }
2075
2076 node = node->GetNext();
2077 }
2078 break;
2079 }
2080 }
2081
2082 node = node->GetNext();
2083 }
2084
2085 node = m_children.GetLast();
2086 if (node && node->GetData()->IsShown())
2087 {
2088 wxRichTextObject* child = node->GetData();
2089 maxHeight = child->GetPosition().y - (GetPosition().y + topMargin) + child->GetCachedSize().y;
2090 }
2091 else
2092 maxHeight = 0; // topMargin + bottomMargin;
2093
2094 // Check the bottom edge of any floating object
2095 if (wxRichTextBuffer::GetFloatingLayoutMode() && GetFloatCollector() && GetFloatCollector()->HasFloats())
2096 {
2097 int bottom = GetFloatCollector()->GetLastRectBottom();
2098 if (bottom > maxHeight)
2099 maxHeight = bottom;
2100 }
2101
2102 if (attr.GetTextBoxAttr().GetSize().GetWidth().IsValid())
2103 {
2104 wxRect r = AdjustAvailableSpace(dc, GetBuffer(), wxRichTextAttr() /* not used */, attr, parentRect, parentRect);
2105 int w = r.GetWidth();
2106
2107 // Convert external to content rect
2108 w = w - leftMargin - rightMargin;
2109 maxWidth = wxMax(maxWidth, w);
2110 maxMaxWidth = wxMax(maxMaxWidth, w);
2111 }
2112 else
2113 {
2114 // TODO: Make sure the layout box's position reflects
2115 // the position of the children, but without
2116 // breaking layout of a box within a paragraph.
2117 }
2118
2119 // TODO: (also in para layout) should set the
2120 // object's size to an absolute one if specified,
2121 // but if not specified, calculate it from content.
2122
2123 // We need to add back the margins etc.
2124 {
2125 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2126 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, maxHeight));
2127 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
2128 SetCachedSize(marginRect.GetSize());
2129 }
2130
2131 // The maximum size is the greatest of all maximum widths for all paragraphs.
2132 {
2133 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2134 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMaxWidth, maxHeight)); // Actually max height is a lie, we can't know it
2135 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
2136 SetMaxSize(marginRect.GetSize());
2137 }
2138
2139 // The minimum size is the greatest of all minimum widths for all paragraphs.
2140 {
2141 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2142 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMinWidth, maxHeight)); // Actually max height is a lie, we can't know it
2143 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
2144 SetMinSize(marginRect.GetSize());
2145 }
2146
2147 if (attr.GetTextBoxAttr().HasVerticalAlignment() &&
2148 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP))
2149 {
2150 int yOffset = 0;
2151 int leftOverSpace = availableSpace.height - topMargin - bottomMargin - maxHeight;
2152 if (leftOverSpace > 0)
2153 {
2154 if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE)
2155 {
2156 yOffset = (leftOverSpace/2);
2157 }
2158 else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM)
2159 {
2160 yOffset = leftOverSpace;
2161 }
2162 }
2163
2164 // Move all the children to vertically align the content
2165 // This doesn't take into account floating objects, unfortunately.
2166 if (yOffset != 0)
2167 {
2168 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2169 while (node)
2170 {
2171 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2172 if (child)
2173 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + yOffset));
2174
2175 node = node->GetNext();
2176 }
2177 }
2178 }
2179
2180 m_invalidRange = wxRICHTEXT_NONE;
2181
2182 return true;
2183 }
2184
2185 /// Get/set the size for the given range.
2186 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, const wxPoint& position, const wxSize& parentSize, wxArrayInt* WXUNUSED(partialExtents)) const
2187 {
2188 wxSize sz;
2189
2190 wxRichTextObjectList::compatibility_iterator startPara = wxRichTextObjectList::compatibility_iterator();
2191 wxRichTextObjectList::compatibility_iterator endPara = wxRichTextObjectList::compatibility_iterator();
2192
2193 // First find the first paragraph whose starting position is within the range.
2194 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2195 while (node)
2196 {
2197 // child is a paragraph
2198 wxRichTextObject* child = node->GetData();
2199 const wxRichTextRange& r = child->GetRange();
2200
2201 if (r.GetStart() <= range.GetStart() && r.GetEnd() >= range.GetStart())
2202 {
2203 startPara = node;
2204 break;
2205 }
2206
2207 node = node->GetNext();
2208 }
2209
2210 // Next find the last paragraph containing part of the range
2211 node = m_children.GetFirst();
2212 while (node)
2213 {
2214 // child is a paragraph
2215 wxRichTextObject* child = node->GetData();
2216 const wxRichTextRange& r = child->GetRange();
2217
2218 if (r.GetStart() <= range.GetEnd() && r.GetEnd() >= range.GetEnd())
2219 {
2220 endPara = node;
2221 break;
2222 }
2223
2224 node = node->GetNext();
2225 }
2226
2227 if (!startPara || !endPara)
2228 return false;
2229
2230 // Now we can add up the sizes
2231 for (node = startPara; node ; node = node->GetNext())
2232 {
2233 // child is a paragraph
2234 wxRichTextObject* child = node->GetData();
2235 const wxRichTextRange& childRange = child->GetRange();
2236 wxRichTextRange rangeToFind = range;
2237 rangeToFind.LimitTo(childRange);
2238
2239 if (child->IsTopLevel())
2240 rangeToFind = child->GetOwnRange();
2241
2242 wxSize childSize;
2243
2244 int childDescent = 0;
2245 child->GetRangeSize(rangeToFind, childSize, childDescent, dc, context, flags, position, parentSize);
2246
2247 descent = wxMax(childDescent, descent);
2248
2249 sz.x = wxMax(sz.x, childSize.x);
2250 sz.y += childSize.y;
2251
2252 if (node == endPara)
2253 break;
2254 }
2255
2256 size = sz;
2257
2258 return true;
2259 }
2260
2261 /// Get the paragraph at the given position
2262 wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos, bool caretPosition) const
2263 {
2264 if (caretPosition)
2265 pos ++;
2266
2267 // First find the first paragraph whose starting position is within the range.
2268 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2269 while (node)
2270 {
2271 // child is a paragraph
2272 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2273 // wxASSERT (child != NULL);
2274
2275 if (child)
2276 {
2277 // Return first child in buffer if position is -1
2278 // if (pos == -1)
2279 // return child;
2280
2281 if (child->GetRange().Contains(pos))
2282 return child;
2283 }
2284
2285 node = node->GetNext();
2286 }
2287 return NULL;
2288 }
2289
2290 /// Get the line at the given position
2291 wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos, bool caretPosition) const
2292 {
2293 if (caretPosition)
2294 pos ++;
2295
2296 // First find the first paragraph whose starting position is within the range.
2297 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2298 while (node)
2299 {
2300 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
2301 if (obj->GetRange().Contains(pos))
2302 {
2303 // child is a paragraph
2304 wxRichTextParagraph* child = wxDynamicCast(obj, wxRichTextParagraph);
2305 // wxASSERT (child != NULL);
2306
2307 if (child)
2308 {
2309 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2310 while (node2)
2311 {
2312 wxRichTextLine* line = node2->GetData();
2313
2314 wxRichTextRange range = line->GetAbsoluteRange();
2315
2316 if (range.Contains(pos) ||
2317
2318 // If the position is end-of-paragraph, then return the last line of
2319 // of the paragraph.
2320 ((range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd())))
2321 return line;
2322
2323 node2 = node2->GetNext();
2324 }
2325 }
2326 }
2327
2328 node = node->GetNext();
2329 }
2330
2331 int lineCount = GetLineCount();
2332 if (lineCount > 0)
2333 return GetLineForVisibleLineNumber(lineCount-1);
2334 else
2335 return NULL;
2336 }
2337
2338 /// Get the line at the given y pixel position, or the last line.
2339 wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y) const
2340 {
2341 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2342 while (node)
2343 {
2344 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2345 // wxASSERT (child != NULL);
2346
2347 if (child)
2348 {
2349 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2350 while (node2)
2351 {
2352 wxRichTextLine* line = node2->GetData();
2353
2354 wxRect rect(line->GetRect());
2355
2356 if (y <= rect.GetBottom())
2357 return line;
2358
2359 node2 = node2->GetNext();
2360 }
2361 }
2362
2363 node = node->GetNext();
2364 }
2365
2366 // Return last line
2367 int lineCount = GetLineCount();
2368 if (lineCount > 0)
2369 return GetLineForVisibleLineNumber(lineCount-1);
2370 else
2371 return NULL;
2372 }
2373
2374 /// Get the number of visible lines
2375 int wxRichTextParagraphLayoutBox::GetLineCount() const
2376 {
2377 int count = 0;
2378
2379 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2380 while (node)
2381 {
2382 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2383 // wxASSERT (child != NULL);
2384
2385 if (child)
2386 count += child->GetLines().GetCount();
2387
2388 node = node->GetNext();
2389 }
2390 return count;
2391 }
2392
2393
2394 /// Get the paragraph for a given line
2395 wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine* line) const
2396 {
2397 return GetParagraphAtPosition(line->GetAbsoluteRange().GetStart());
2398 }
2399
2400 /// Get the line size at the given position
2401 wxSize wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos, bool caretPosition) const
2402 {
2403 wxRichTextLine* line = GetLineAtPosition(pos, caretPosition);
2404 if (line)
2405 {
2406 return line->GetSize();
2407 }
2408 else
2409 return wxSize(0, 0);
2410 }
2411
2412
2413 /// Convenience function to add a paragraph of text
2414 wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph(const wxString& text, wxRichTextAttr* paraStyle)
2415 {
2416 // Don't use the base style, just the default style, and the base style will
2417 // be combined at display time.
2418 // Divide into paragraph and character styles.
2419
2420 wxRichTextAttr defaultCharStyle;
2421 wxRichTextAttr defaultParaStyle;
2422
2423 // If the default style is a named paragraph style, don't apply any character formatting
2424 // to the initial text string.
2425 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2426 {
2427 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2428 if (def)
2429 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2430 }
2431 else
2432 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2433
2434 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2435 wxRichTextAttr* cStyle = & defaultCharStyle;
2436
2437 wxRichTextParagraph* para = new wxRichTextParagraph(text, this, pStyle, cStyle);
2438 para->GetAttributes().GetTextBoxAttr().Reset();
2439
2440 AppendChild(para);
2441
2442 UpdateRanges();
2443
2444 return para->GetRange();
2445 }
2446
2447 /// Adds multiple paragraphs, based on newlines.
2448 wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text, wxRichTextAttr* paraStyle)
2449 {
2450 // Don't use the base style, just the default style, and the base style will
2451 // be combined at display time.
2452 // Divide into paragraph and character styles.
2453
2454 wxRichTextAttr defaultCharStyle;
2455 wxRichTextAttr defaultParaStyle;
2456
2457 // If the default style is a named paragraph style, don't apply any character formatting
2458 // to the initial text string.
2459 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2460 {
2461 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2462 if (def)
2463 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2464 }
2465 else
2466 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2467
2468 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2469 wxRichTextAttr* cStyle = & defaultCharStyle;
2470
2471 wxRichTextParagraph* firstPara = NULL;
2472 wxRichTextParagraph* lastPara = NULL;
2473
2474 wxRichTextRange range(-1, -1);
2475
2476 size_t i = 0;
2477 size_t len = text.length();
2478 wxString line;
2479 wxRichTextParagraph* para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
2480 para->GetAttributes().GetTextBoxAttr().Reset();
2481
2482 AppendChild(para);
2483
2484 firstPara = para;
2485 lastPara = para;
2486
2487 while (i < len)
2488 {
2489 wxChar ch = text[i];
2490 if (ch == wxT('\n') || ch == wxT('\r'))
2491 {
2492 if (i != (len-1))
2493 {
2494 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2495 plainText->SetText(line);
2496
2497 para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
2498 para->GetAttributes().GetTextBoxAttr().Reset();
2499
2500 AppendChild(para);
2501
2502 lastPara = para;
2503 line = wxEmptyString;
2504 }
2505 }
2506 else
2507 line += ch;
2508
2509 i ++;
2510 }
2511
2512 if (!line.empty())
2513 {
2514 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2515 plainText->SetText(line);
2516 }
2517
2518 UpdateRanges();
2519
2520 return wxRichTextRange(firstPara->GetRange().GetStart(), lastPara->GetRange().GetEnd());
2521 }
2522
2523 /// Convenience function to add an image
2524 wxRichTextRange wxRichTextParagraphLayoutBox::AddImage(const wxImage& image, wxRichTextAttr* paraStyle)
2525 {
2526 // Don't use the base style, just the default style, and the base style will
2527 // be combined at display time.
2528 // Divide into paragraph and character styles.
2529
2530 wxRichTextAttr defaultCharStyle;
2531 wxRichTextAttr defaultParaStyle;
2532
2533 // If the default style is a named paragraph style, don't apply any character formatting
2534 // to the initial text string.
2535 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2536 {
2537 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2538 if (def)
2539 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2540 }
2541 else
2542 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2543
2544 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2545 wxRichTextAttr* cStyle = & defaultCharStyle;
2546
2547 wxRichTextParagraph* para = new wxRichTextParagraph(this, pStyle);
2548 para->GetAttributes().GetTextBoxAttr().Reset();
2549 AppendChild(para);
2550 para->AppendChild(new wxRichTextImage(image, this, cStyle));
2551
2552 UpdateRanges();
2553
2554 return para->GetRange();
2555 }
2556
2557
2558 /// Insert fragment into this box at the given position. If partialParagraph is true,
2559 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2560 /// marker.
2561
2562 bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextParagraphLayoutBox& fragment)
2563 {
2564 // First, find the first paragraph whose starting position is within the range.
2565 wxRichTextParagraph* para = GetParagraphAtPosition(position);
2566 if (para)
2567 {
2568 wxRichTextAttr originalAttr = para->GetAttributes();
2569
2570 wxRichTextObjectList::compatibility_iterator node = m_children.Find(para);
2571
2572 // Now split at this position, returning the object to insert the new
2573 // ones in front of.
2574 wxRichTextObject* nextObject = para->SplitAt(position);
2575
2576 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2577 // text, for example, so let's optimize.
2578
2579 if (fragment.GetPartialParagraph() && fragment.GetChildren().GetCount() == 1)
2580 {
2581 // Add the first para to this para...
2582 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2583 if (!firstParaNode)
2584 return false;
2585
2586 // Iterate through the fragment paragraph inserting the content into this paragraph.
2587 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2588 wxASSERT (firstPara != NULL);
2589
2590 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
2591 while (objectNode)
2592 {
2593 wxRichTextObject* newObj = objectNode->GetData()->Clone();
2594
2595 if (!nextObject)
2596 {
2597 // Append
2598 para->AppendChild(newObj);
2599 }
2600 else
2601 {
2602 // Insert before nextObject
2603 para->InsertChild(newObj, nextObject);
2604 }
2605
2606 objectNode = objectNode->GetNext();
2607 }
2608
2609 return true;
2610 }
2611 else
2612 {
2613 // Procedure for inserting a fragment consisting of a number of
2614 // paragraphs:
2615 //
2616 // 1. Remove and save the content that's after the insertion point, for adding
2617 // back once we've added the fragment.
2618 // 2. Add the content from the first fragment paragraph to the current
2619 // paragraph.
2620 // 3. Add remaining fragment paragraphs after the current paragraph.
2621 // 4. Add back the saved content from the first paragraph. If partialParagraph
2622 // is true, add it to the last paragraph added and not a new one.
2623
2624 // 1. Remove and save objects after split point.
2625 wxList savedObjects;
2626 if (nextObject)
2627 para->MoveToList(nextObject, savedObjects);
2628
2629 // 2. Add the content from the 1st fragment paragraph.
2630 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2631 if (!firstParaNode)
2632 return false;
2633
2634 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2635 wxASSERT(firstPara != NULL);
2636
2637 if (!(fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE))
2638 para->SetAttributes(firstPara->GetAttributes());
2639
2640 // Save empty paragraph attributes for appending later
2641 // These are character attributes deliberately set for a new paragraph. Without this,
2642 // we couldn't pass default attributes when appending a new paragraph.
2643 wxRichTextAttr emptyParagraphAttributes;
2644
2645 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
2646
2647 if (objectNode && firstPara->GetChildren().GetCount() == 1 && objectNode->GetData()->IsEmpty())
2648 emptyParagraphAttributes = objectNode->GetData()->GetAttributes();
2649
2650 while (objectNode)
2651 {
2652 wxRichTextObject* newObj = objectNode->GetData()->Clone();
2653
2654 // Append
2655 para->AppendChild(newObj);
2656
2657 objectNode = objectNode->GetNext();
2658 }
2659
2660 // 3. Add remaining fragment paragraphs after the current paragraph.
2661 wxRichTextObjectList::compatibility_iterator nextParagraphNode = node->GetNext();
2662 wxRichTextObject* nextParagraph = NULL;
2663 if (nextParagraphNode)
2664 nextParagraph = nextParagraphNode->GetData();
2665
2666 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst()->GetNext();
2667 wxRichTextParagraph* finalPara = para;
2668
2669 bool needExtraPara = (!i || !fragment.GetPartialParagraph());
2670
2671 // If there was only one paragraph, we need to insert a new one.
2672 while (i)
2673 {
2674 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2675 wxASSERT( para != NULL );
2676
2677 finalPara = (wxRichTextParagraph*) para->Clone();
2678
2679 if (nextParagraph)
2680 InsertChild(finalPara, nextParagraph);
2681 else
2682 AppendChild(finalPara);
2683
2684 i = i->GetNext();
2685 }
2686
2687 // If there was only one paragraph, or we have full paragraphs in our fragment,
2688 // we need to insert a new one.
2689 if (needExtraPara)
2690 {
2691 finalPara = new wxRichTextParagraph;
2692
2693 if (nextParagraph)
2694 InsertChild(finalPara, nextParagraph);
2695 else
2696 AppendChild(finalPara);
2697 }
2698
2699 // 4. Add back the remaining content.
2700 if (finalPara)
2701 {
2702 if (nextObject)
2703 finalPara->MoveFromList(savedObjects);
2704
2705 // Ensure there's at least one object
2706 if (finalPara->GetChildCount() == 0)
2707 {
2708 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
2709 text->SetAttributes(emptyParagraphAttributes);
2710
2711 finalPara->AppendChild(text);
2712 }
2713 }
2714
2715 if ((fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE) && firstPara)
2716 finalPara->SetAttributes(firstPara->GetAttributes());
2717 else if (finalPara && finalPara != para)
2718 finalPara->SetAttributes(originalAttr);
2719
2720 return true;
2721 }
2722 }
2723 else
2724 {
2725 // Append
2726 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst();
2727 while (i)
2728 {
2729 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2730 wxASSERT( para != NULL );
2731
2732 AppendChild(para->Clone());
2733
2734 i = i->GetNext();
2735 }
2736
2737 return true;
2738 }
2739 }
2740
2741 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2742 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2743 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange& range, wxRichTextParagraphLayoutBox& fragment)
2744 {
2745 wxRichTextObjectList::compatibility_iterator i = GetChildren().GetFirst();
2746 while (i)
2747 {
2748 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2749 wxASSERT( para != NULL );
2750
2751 if (!para->GetRange().IsOutside(range))
2752 {
2753 fragment.AppendChild(para->Clone());
2754 }
2755 i = i->GetNext();
2756 }
2757
2758 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2759 if (!fragment.IsEmpty())
2760 {
2761 wxRichTextParagraph* firstPara = wxDynamicCast(fragment.GetChildren().GetFirst()->GetData(), wxRichTextParagraph);
2762 wxASSERT( firstPara != NULL );
2763
2764 wxRichTextParagraph* lastPara = wxDynamicCast(fragment.GetChildren().GetLast()->GetData(), wxRichTextParagraph);
2765 wxASSERT( lastPara != NULL );
2766
2767 if (!firstPara || !lastPara)
2768 return false;
2769
2770 bool isFragment = (range.GetEnd() < lastPara->GetRange().GetEnd());
2771
2772 long firstPos = firstPara->GetRange().GetStart();
2773
2774 // Adjust for renumbering from zero
2775 wxRichTextRange topTailRange(range.GetStart() - firstPos, range.GetEnd() - firstPos);
2776
2777 long end;
2778 fragment.CalculateRange(0, end);
2779
2780 // Chop off the start of the paragraph
2781 if (topTailRange.GetStart() > 0)
2782 {
2783 wxRichTextRange r(0, topTailRange.GetStart()-1);
2784 firstPara->DeleteRange(r);
2785
2786 // Make sure the numbering is correct
2787 fragment.CalculateRange(0, end);
2788
2789 // Now, we've deleted some positions, so adjust the range
2790 // accordingly.
2791 topTailRange.SetStart(range.GetLength());
2792 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
2793 }
2794 else
2795 {
2796 topTailRange.SetStart(range.GetLength());
2797 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
2798 }
2799
2800 if (topTailRange.GetStart() < lastPara->GetRange().GetEnd())
2801 {
2802 lastPara->DeleteRange(topTailRange);
2803
2804 // Make sure the numbering is correct
2805 long end;
2806 fragment.CalculateRange(0, end);
2807
2808 // We only have part of a paragraph at the end
2809 fragment.SetPartialParagraph(true);
2810 }
2811 else
2812 {
2813 // We have a partial paragraph (don't save last new paragraph marker)
2814 // or complete paragraph
2815 fragment.SetPartialParagraph(isFragment);
2816 }
2817 }
2818
2819 return true;
2820 }
2821
2822 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2823 /// starting from zero at the start of the buffer.
2824 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos, bool caretPosition, bool startOfLine) const
2825 {
2826 if (caretPosition)
2827 pos ++;
2828
2829 int lineCount = 0;
2830
2831 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2832 while (node)
2833 {
2834 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2835 // wxASSERT( child != NULL );
2836
2837 if (child)
2838 {
2839 if (child->GetRange().Contains(pos))
2840 {
2841 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2842 while (node2)
2843 {
2844 wxRichTextLine* line = node2->GetData();
2845 wxRichTextRange lineRange = line->GetAbsoluteRange();
2846
2847 if (lineRange.Contains(pos) || pos == lineRange.GetStart())
2848 {
2849 // If the caret is displayed at the end of the previous wrapped line,
2850 // we want to return the line it's _displayed_ at (not the actual line
2851 // containing the position).
2852 if (lineRange.GetStart() == pos && !startOfLine && child->GetRange().GetStart() != pos)
2853 return lineCount - 1;
2854 else
2855 return lineCount;
2856 }
2857
2858 lineCount ++;
2859
2860 node2 = node2->GetNext();
2861 }
2862 // If we didn't find it in the lines, it must be
2863 // the last position of the paragraph. So return the last line.
2864 return lineCount-1;
2865 }
2866 else
2867 lineCount += child->GetLines().GetCount();
2868 }
2869
2870 node = node->GetNext();
2871 }
2872
2873 // Not found
2874 return -1;
2875 }
2876
2877 /// Given a line number, get the corresponding wxRichTextLine object.
2878 wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber) const
2879 {
2880 int lineCount = 0;
2881
2882 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2883 while (node)
2884 {
2885 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2886 // wxASSERT(child != NULL);
2887
2888 if (child)
2889 {
2890 if (lineNumber < (int) (child->GetLines().GetCount() + lineCount))
2891 {
2892 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2893 while (node2)
2894 {
2895 wxRichTextLine* line = node2->GetData();
2896
2897 if (lineCount == lineNumber)
2898 return line;
2899
2900 lineCount ++;
2901
2902 node2 = node2->GetNext();
2903 }
2904 }
2905 else
2906 lineCount += child->GetLines().GetCount();
2907 }
2908
2909 node = node->GetNext();
2910 }
2911
2912 // Didn't find it
2913 return NULL;
2914 }
2915
2916 /// Delete range from layout.
2917 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange& range)
2918 {
2919 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2920
2921 wxRichTextParagraph* firstPara = NULL;
2922 while (node)
2923 {
2924 wxRichTextParagraph* obj = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2925 // wxASSERT (obj != NULL);
2926
2927 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
2928
2929 if (obj)
2930 {
2931 // Delete the range in each paragraph
2932
2933 if (!obj->GetRange().IsOutside(range))
2934 {
2935 // Deletes the content of this object within the given range
2936 obj->DeleteRange(range);
2937
2938 wxRichTextRange thisRange = obj->GetRange();
2939 wxRichTextAttr thisAttr = obj->GetAttributes();
2940
2941 // If the whole paragraph is within the range to delete,
2942 // delete the whole thing.
2943 if (range.GetStart() <= thisRange.GetStart() && range.GetEnd() >= thisRange.GetEnd())
2944 {
2945 // Delete the whole object
2946 RemoveChild(obj, true);
2947 obj = NULL;
2948 }
2949 else if (!firstPara)
2950 firstPara = obj;
2951
2952 // If the range includes the paragraph end, we need to join this
2953 // and the next paragraph.
2954 if (range.GetEnd() <= thisRange.GetEnd())
2955 {
2956 // We need to move the objects from the next paragraph
2957 // to this paragraph
2958
2959 wxRichTextParagraph* nextParagraph = NULL;
2960 if ((range.GetEnd() < thisRange.GetEnd()) && obj)
2961 nextParagraph = obj;
2962 else
2963 {
2964 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2965 if (next)
2966 nextParagraph = wxDynamicCast(next->GetData(), wxRichTextParagraph);
2967 }
2968
2969 bool applyFinalParagraphStyle = firstPara && nextParagraph && nextParagraph != firstPara;
2970
2971 wxRichTextAttr nextParaAttr;
2972 if (applyFinalParagraphStyle)
2973 {
2974 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2975 // not the next one.
2976 if (range.GetStart() == range.GetEnd() && range.GetStart() == thisRange.GetEnd())
2977 nextParaAttr = thisAttr;
2978 else
2979 nextParaAttr = nextParagraph->GetAttributes();
2980 }
2981
2982 if (firstPara && nextParagraph && firstPara != nextParagraph)
2983 {
2984 // Move the objects to the previous para
2985 wxRichTextObjectList::compatibility_iterator node1 = nextParagraph->GetChildren().GetFirst();
2986
2987 while (node1)
2988 {
2989 wxRichTextObject* obj1 = node1->GetData();
2990
2991 firstPara->AppendChild(obj1);
2992
2993 wxRichTextObjectList::compatibility_iterator next1 = node1->GetNext();
2994 nextParagraph->GetChildren().Erase(node1);
2995
2996 node1 = next1;
2997 }
2998
2999 // Delete the paragraph
3000 RemoveChild(nextParagraph, true);
3001 }
3002
3003 // Avoid empty paragraphs
3004 if (firstPara && firstPara->GetChildren().GetCount() == 0)
3005 {
3006 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
3007 firstPara->AppendChild(text);
3008 }
3009
3010 if (applyFinalParagraphStyle)
3011 firstPara->SetAttributes(nextParaAttr);
3012
3013 return true;
3014 }
3015 }
3016 }
3017
3018 node = next;
3019 }
3020
3021 return true;
3022 }
3023
3024 /// Get any text in this object for the given range
3025 wxString wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange& range) const
3026 {
3027 int lineCount = 0;
3028 wxString text;
3029 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3030 while (node)
3031 {
3032 wxRichTextObject* child = node->GetData();
3033 if (!child->GetRange().IsOutside(range))
3034 {
3035 wxRichTextRange childRange = range;
3036 childRange.LimitTo(child->GetRange());
3037
3038 wxString childText = child->GetTextForRange(childRange);
3039
3040 text += childText;
3041
3042 if ((childRange.GetEnd() == child->GetRange().GetEnd()) && node->GetNext())
3043 text += wxT("\n");
3044
3045 lineCount ++;
3046 }
3047 node = node->GetNext();
3048 }
3049
3050 return text;
3051 }
3052
3053 /// Get all the text
3054 wxString wxRichTextParagraphLayoutBox::GetText() const
3055 {
3056 return GetTextForRange(GetOwnRange());
3057 }
3058
3059 /// Get the paragraph by number
3060 wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber) const
3061 {
3062 if ((size_t) paragraphNumber >= GetChildCount())
3063 return NULL;
3064
3065 return (wxRichTextParagraph*) GetChild((size_t) paragraphNumber);
3066 }
3067
3068 /// Get the length of the paragraph
3069 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber) const
3070 {
3071 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3072 if (para)
3073 return para->GetRange().GetLength() - 1; // don't include newline
3074 else
3075 return 0;
3076 }
3077
3078 /// Get the text of the paragraph
3079 wxString wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber) const
3080 {
3081 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3082 if (para)
3083 return para->GetTextForRange(para->GetRange());
3084 else
3085 return wxEmptyString;
3086 }
3087
3088 /// Convert zero-based line column and paragraph number to a position.
3089 long wxRichTextParagraphLayoutBox::XYToPosition(long x, long y) const
3090 {
3091 wxRichTextParagraph* para = GetParagraphAtLine(y);
3092 if (para)
3093 {
3094 return para->GetRange().GetStart() + x;
3095 }
3096 else
3097 return -1;
3098 }
3099
3100 /// Convert zero-based position to line column and paragraph number
3101 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos, long* x, long* y) const
3102 {
3103 wxRichTextParagraph* para = GetParagraphAtPosition(pos);
3104 if (para)
3105 {
3106 int count = 0;
3107 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3108 while (node)
3109 {
3110 wxRichTextObject* child = node->GetData();
3111 if (child == para)
3112 break;
3113 count ++;
3114 node = node->GetNext();
3115 }
3116
3117 *y = count;
3118 *x = pos - para->GetRange().GetStart();
3119
3120 return true;
3121 }
3122 else
3123 return false;
3124 }
3125
3126 /// Get the leaf object in a paragraph at this position.
3127 /// Given a line number, get the corresponding wxRichTextLine object.
3128 wxRichTextObject* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position) const
3129 {
3130 wxRichTextParagraph* para = GetParagraphAtPosition(position);
3131 if (para)
3132 {
3133 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
3134
3135 while (node)
3136 {
3137 wxRichTextObject* child = node->GetData();
3138 if (child->GetRange().Contains(position))
3139 return child;
3140
3141 node = node->GetNext();
3142 }
3143 if (position == para->GetRange().GetEnd() && para->GetChildCount() > 0)
3144 return para->GetChildren().GetLast()->GetData();
3145 }
3146 return NULL;
3147 }
3148
3149 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3150 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, int flags)
3151 {
3152 bool characterStyle = false;
3153 bool paragraphStyle = false;
3154
3155 if (style.IsCharacterStyle())
3156 characterStyle = true;
3157 if (style.IsParagraphStyle())
3158 paragraphStyle = true;
3159
3160 wxRichTextBuffer* buffer = GetBuffer();
3161
3162 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3163 bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3164 bool parasOnly = ((flags & wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY) != 0);
3165 bool charactersOnly = ((flags & wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY) != 0);
3166 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
3167 bool removeStyle = ((flags & wxRICHTEXT_SETSTYLE_REMOVE) != 0);
3168
3169 // Apply paragraph style first, if any
3170 wxRichTextAttr wholeStyle(style);
3171
3172 if (!removeStyle && wholeStyle.HasParagraphStyleName() && buffer->GetStyleSheet())
3173 {
3174 wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->FindParagraphStyle(wholeStyle.GetParagraphStyleName());
3175 if (def)
3176 wxRichTextApplyStyle(wholeStyle, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
3177 }
3178
3179 // Limit the attributes to be set to the content to only character attributes.
3180 wxRichTextAttr characterAttributes(wholeStyle);
3181 characterAttributes.SetFlags(characterAttributes.GetFlags() & (wxTEXT_ATTR_CHARACTER));
3182
3183 if (!removeStyle && characterAttributes.HasCharacterStyleName() && buffer->GetStyleSheet())
3184 {
3185 wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->FindCharacterStyle(characterAttributes.GetCharacterStyleName());
3186 if (def)
3187 wxRichTextApplyStyle(characterAttributes, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
3188 }
3189
3190 // If we are associated with a control, make undoable; otherwise, apply immediately
3191 // to the data.
3192
3193 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3194
3195 wxRichTextAction* action = NULL;
3196
3197 if (haveControl && withUndo)
3198 {
3199 action = new wxRichTextAction(NULL, _("Change Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
3200 action->SetRange(range);
3201 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3202 }
3203
3204 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3205 while (node)
3206 {
3207 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3208 // wxASSERT (para != NULL);
3209
3210 if (para && para->GetChildCount() > 0)
3211 {
3212 // Stop searching if we're beyond the range of interest
3213 if (para->GetRange().GetStart() > range.GetEnd())
3214 break;
3215
3216 if (!para->GetRange().IsOutside(range))
3217 {
3218 // We'll be using a copy of the paragraph to make style changes,
3219 // not updating the buffer directly.
3220 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3221
3222 if (haveControl && withUndo)
3223 {
3224 newPara = new wxRichTextParagraph(*para);
3225 action->GetNewParagraphs().AppendChild(newPara);
3226
3227 // Also store the old ones for Undo
3228 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3229 }
3230 else
3231 newPara = para;
3232
3233 // If we're specifying paragraphs only, then we really mean character formatting
3234 // to be included in the paragraph style
3235 if ((paragraphStyle || parasOnly) && !charactersOnly)
3236 {
3237 if (removeStyle)
3238 {
3239 // Removes the given style from the paragraph
3240 wxRichTextRemoveStyle(newPara->GetAttributes(), style);
3241 }
3242 else if (resetExistingStyle)
3243 newPara->GetAttributes() = wholeStyle;
3244 else
3245 {
3246 if (applyMinimal)
3247 {
3248 // Only apply attributes that will make a difference to the combined
3249 // style as seen on the display
3250 wxRichTextAttr combinedAttr(para->GetCombinedAttributes(true));
3251 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle, & combinedAttr);
3252 }
3253 else
3254 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle);
3255 }
3256 }
3257
3258 // When applying paragraph styles dynamically, don't change the text objects' attributes
3259 // since they will computed as needed. Only apply the character styling if it's _only_
3260 // character styling. This policy is subject to change and might be put under user control.
3261
3262 // Hm. we might well be applying a mix of paragraph and character styles, in which
3263 // case we _do_ want to apply character styles regardless of what para styles are set.
3264 // But if we're applying a paragraph style, which has some character attributes, but
3265 // we only want the paragraphs to hold this character style, then we _don't_ want to
3266 // apply the character style. So we need to be able to choose.
3267
3268 if (!parasOnly && (characterStyle|charactersOnly) && range.GetStart() != newPara->GetRange().GetEnd())
3269 {
3270 wxRichTextRange childRange(range);
3271 childRange.LimitTo(newPara->GetRange());
3272
3273 // Find the starting position and if necessary split it so
3274 // we can start applying a different style.
3275 // TODO: check that the style actually changes or is different
3276 // from style outside of range
3277 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3278 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
3279
3280 if (childRange.GetStart() == newPara->GetRange().GetStart())
3281 firstObject = newPara->GetChildren().GetFirst()->GetData();
3282 else
3283 firstObject = newPara->SplitAt(range.GetStart());
3284
3285 // Increment by 1 because we're apply the style one _after_ the split point
3286 long splitPoint = childRange.GetEnd();
3287 if (splitPoint != newPara->GetRange().GetEnd())
3288 splitPoint ++;
3289
3290 // Find last object
3291 if (splitPoint == newPara->GetRange().GetEnd())
3292 lastObject = newPara->GetChildren().GetLast()->GetData();
3293 else
3294 // lastObject is set as a side-effect of splitting. It's
3295 // returned as the object before the new object.
3296 (void) newPara->SplitAt(splitPoint, & lastObject);
3297
3298 wxASSERT(firstObject != NULL);
3299 wxASSERT(lastObject != NULL);
3300
3301 if (!firstObject || !lastObject)
3302 continue;
3303
3304 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3305 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
3306
3307 wxASSERT(firstNode);
3308 wxASSERT(lastNode);
3309
3310 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
3311
3312 while (node2)
3313 {
3314 wxRichTextObject* child = node2->GetData();
3315
3316 if (removeStyle)
3317 {
3318 // Removes the given style from the paragraph
3319 wxRichTextRemoveStyle(child->GetAttributes(), style);
3320 }
3321 else if (resetExistingStyle)
3322 child->GetAttributes() = characterAttributes;
3323 else
3324 {
3325 if (applyMinimal)
3326 {
3327 // Only apply attributes that will make a difference to the combined
3328 // style as seen on the display
3329 wxRichTextAttr combinedAttr(newPara->GetCombinedAttributes(child->GetAttributes(), true));
3330 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes, & combinedAttr);
3331 }
3332 else
3333 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes);
3334 }
3335
3336 if (node2 == lastNode)
3337 break;
3338
3339 node2 = node2->GetNext();
3340 }
3341 }
3342 }
3343 }
3344
3345 node = node->GetNext();
3346 }
3347
3348 // Do action, or delay it until end of batch.
3349 if (haveControl && withUndo)
3350 buffer->SubmitAction(action);
3351
3352 return true;
3353 }
3354
3355 // Just change the attributes for this single object.
3356 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject* obj, const wxRichTextAttr& textAttr, int flags)
3357 {
3358 wxRichTextBuffer* buffer = GetBuffer();
3359 bool withUndo = flags & wxRICHTEXT_SETSTYLE_WITH_UNDO;
3360 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
3361 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3362
3363 wxRichTextAction *action = NULL;
3364 wxRichTextAttr newAttr = obj->GetAttributes();
3365 if (resetExistingStyle)
3366 newAttr = textAttr;
3367 else
3368 newAttr.Apply(textAttr);
3369
3370 if (haveControl && withUndo)
3371 {
3372 action = new wxRichTextAction(NULL, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES, buffer, obj->GetContainer(), buffer->GetRichTextCtrl());
3373 action->SetRange(obj->GetRange().FromInternal());
3374 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3375 action->MakeObject(obj);
3376
3377 action->GetAttributes() = newAttr;
3378 }
3379 else
3380 obj->GetAttributes() = newAttr;
3381
3382 if (haveControl && withUndo)
3383 buffer->SubmitAction(action);
3384 }
3385
3386 /// Get the text attributes for this position.
3387 bool wxRichTextParagraphLayoutBox::GetStyle(long position, wxRichTextAttr& style)
3388 {
3389 return DoGetStyle(position, style, true);
3390 }
3391
3392 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position, wxRichTextAttr& style)
3393 {
3394 return DoGetStyle(position, style, false);
3395 }
3396
3397 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3398 /// context attributes.
3399 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position, wxRichTextAttr& style, bool combineStyles)
3400 {
3401 wxRichTextObject* obj wxDUMMY_INITIALIZE(NULL);
3402
3403 if (style.IsParagraphStyle())
3404 {
3405 obj = GetParagraphAtPosition(position);
3406 if (obj)
3407 {
3408 if (combineStyles)
3409 {
3410 // Start with the base style
3411 style = GetAttributes();
3412 style.GetTextBoxAttr().Reset();
3413
3414 // Apply the paragraph style
3415 wxRichTextApplyStyle(style, obj->GetAttributes());
3416 }
3417 else
3418 style = obj->GetAttributes();
3419
3420 return true;
3421 }
3422 }
3423 else
3424 {
3425 obj = GetLeafObjectAtPosition(position);
3426 if (obj)
3427 {
3428 if (combineStyles)
3429 {
3430 wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph);
3431 style = para ? para->GetCombinedAttributes(obj->GetAttributes()) : obj->GetAttributes();
3432 }
3433 else
3434 style = obj->GetAttributes();
3435
3436 return true;
3437 }
3438 }
3439 return false;
3440 }
3441
3442 static bool wxHasStyle(long flags, long style)
3443 {
3444 return (flags & style) != 0;
3445 }
3446
3447 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3448 /// content.
3449 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr& currentStyle, const wxRichTextAttr& style, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
3450 {
3451 currentStyle.CollectCommonAttributes(style, clashingAttr, absentAttr);
3452
3453 return true;
3454 }
3455
3456 /// Get the combined style for a range - if any attribute is different within the range,
3457 /// that attribute is not present within the flags.
3458 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3459 /// nested.
3460 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange& range, wxRichTextAttr& style)
3461 {
3462 style = wxRichTextAttr();
3463
3464 wxRichTextAttr clashingAttrPara, clashingAttrChar;
3465 wxRichTextAttr absentAttrPara, absentAttrChar;
3466
3467 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
3468 while (node)
3469 {
3470 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3471 if (para && !(para->GetRange().GetStart() > range.GetEnd() || para->GetRange().GetEnd() < range.GetStart()))
3472 {
3473 if (para->GetChildren().GetCount() == 0)
3474 {
3475 wxRichTextAttr paraStyle = para->GetCombinedAttributes(true /* use box attributes */);
3476
3477 CollectStyle(style, paraStyle, clashingAttrPara, absentAttrPara);
3478 }
3479 else
3480 {
3481 wxRichTextRange paraRange(para->GetRange());
3482 paraRange.LimitTo(range);
3483
3484 // First collect paragraph attributes only
3485 wxRichTextAttr paraStyle = para->GetCombinedAttributes();
3486 paraStyle.SetFlags(paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH);
3487 CollectStyle(style, paraStyle, clashingAttrPara, absentAttrPara);
3488
3489 wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
3490
3491 while (childNode)
3492 {
3493 wxRichTextObject* child = childNode->GetData();
3494 if (!(child->GetRange().GetStart() > range.GetEnd() || child->GetRange().GetEnd() < range.GetStart()))
3495 {
3496 wxRichTextAttr childStyle = para->GetCombinedAttributes(child->GetAttributes(), true /* include box attributes */);
3497
3498 // Now collect character attributes only
3499 childStyle.SetFlags(childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER);
3500
3501 CollectStyle(style, childStyle, clashingAttrChar, absentAttrChar);
3502 }
3503
3504 childNode = childNode->GetNext();
3505 }
3506 }
3507 }
3508 node = node->GetNext();
3509 }
3510 return true;
3511 }
3512
3513 /// Set default style
3514 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr& style)
3515 {
3516 m_defaultAttributes = style;
3517 return true;
3518 }
3519
3520 /// Test if this whole range has character attributes of the specified kind. If any
3521 /// of the attributes are different within the range, the test fails. You
3522 /// can use this to implement, for example, bold button updating. style must have
3523 /// flags indicating which attributes are of interest.
3524 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3525 {
3526 int foundCount = 0;
3527 int matchingCount = 0;
3528
3529 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3530 while (node)
3531 {
3532 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3533 // wxASSERT (para != NULL);
3534
3535 if (para)
3536 {
3537 // Stop searching if we're beyond the range of interest
3538 if (para->GetRange().GetStart() > range.GetEnd())
3539 return foundCount == matchingCount && foundCount != 0;
3540
3541 if (!para->GetRange().IsOutside(range))
3542 {
3543 wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
3544
3545 while (node2)
3546 {
3547 wxRichTextObject* child = node2->GetData();
3548 // Allow for empty string if no buffer
3549 wxRichTextRange childRange = child->GetRange();
3550 if (childRange.GetLength() == 0 && GetRange().GetLength() == 1)
3551 childRange.SetEnd(childRange.GetEnd()+1);
3552
3553 if (!childRange.IsOutside(range) && wxDynamicCast(child, wxRichTextPlainText))
3554 {
3555 foundCount ++;
3556 wxRichTextAttr textAttr = para->GetCombinedAttributes(child->GetAttributes());
3557
3558 if (textAttr.EqPartial(style, false /* strong test - attributes must be valid in both objects */))
3559 matchingCount ++;
3560 }
3561
3562 node2 = node2->GetNext();
3563 }
3564 }
3565 }
3566
3567 node = node->GetNext();
3568 }
3569
3570 return foundCount == matchingCount && foundCount != 0;
3571 }
3572
3573 /// Test if this whole range has paragraph attributes of the specified kind. If any
3574 /// of the attributes are different within the range, the test fails. You
3575 /// can use this to implement, for example, centering button updating. style must have
3576 /// flags indicating which attributes are of interest.
3577 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3578 {
3579 int foundCount = 0;
3580 int matchingCount = 0;
3581
3582 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3583 while (node)
3584 {
3585 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3586 // wxASSERT (para != NULL);
3587
3588 if (para)
3589 {
3590 // Stop searching if we're beyond the range of interest
3591 if (para->GetRange().GetStart() > range.GetEnd())
3592 return foundCount == matchingCount && foundCount != 0;
3593
3594 if (!para->GetRange().IsOutside(range))
3595 {
3596 wxRichTextAttr textAttr = GetAttributes();
3597 // Apply the paragraph style
3598 wxRichTextApplyStyle(textAttr, para->GetAttributes());
3599
3600 foundCount ++;
3601 if (textAttr.EqPartial(style, false /* strong test */))
3602 matchingCount ++;
3603 }
3604 }
3605
3606 node = node->GetNext();
3607 }
3608 return foundCount == matchingCount && foundCount != 0;
3609 }
3610
3611 void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox& container)
3612 {
3613 wxRichTextBuffer* buffer = GetBuffer();
3614 if (buffer && buffer->GetRichTextCtrl())
3615 buffer->GetRichTextCtrl()->PrepareContent(container);
3616 }
3617
3618 /// Set character or paragraph properties
3619 bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange& range, const wxRichTextProperties& properties, int flags)
3620 {
3621 wxRichTextBuffer* buffer = GetBuffer();
3622
3623 bool withUndo = ((flags & wxRICHTEXT_SETPROPERTIES_WITH_UNDO) != 0);
3624 bool parasOnly = ((flags & wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY) != 0);
3625 bool charactersOnly = ((flags & wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY) != 0);
3626 bool resetExistingProperties = ((flags & wxRICHTEXT_SETPROPERTIES_RESET) != 0);
3627 bool removeProperties = ((flags & wxRICHTEXT_SETPROPERTIES_REMOVE) != 0);
3628
3629 // If we are associated with a control, make undoable; otherwise, apply immediately
3630 // to the data.
3631
3632 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3633
3634 wxRichTextAction* action = NULL;
3635
3636 if (haveControl && withUndo)
3637 {
3638 action = new wxRichTextAction(NULL, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES, buffer, this, buffer->GetRichTextCtrl());
3639 action->SetRange(range);
3640 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3641 }
3642
3643 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3644 while (node)
3645 {
3646 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3647 // wxASSERT (para != NULL);
3648
3649 if (para && para->GetChildCount() > 0)
3650 {
3651 // Stop searching if we're beyond the range of interest
3652 if (para->GetRange().GetStart() > range.GetEnd())
3653 break;
3654
3655 if (!para->GetRange().IsOutside(range))
3656 {
3657 // We'll be using a copy of the paragraph to make style changes,
3658 // not updating the buffer directly.
3659 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3660
3661 if (haveControl && withUndo)
3662 {
3663 newPara = new wxRichTextParagraph(*para);
3664 action->GetNewParagraphs().AppendChild(newPara);
3665
3666 // Also store the old ones for Undo
3667 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3668 }
3669 else
3670 newPara = para;
3671
3672 if (parasOnly)
3673 {
3674 if (removeProperties)
3675 {
3676 // Removes the given style from the paragraph
3677 // TODO
3678 newPara->GetProperties().RemoveProperties(properties);
3679 }
3680 else if (resetExistingProperties)
3681 newPara->GetProperties() = properties;
3682 else
3683 newPara->GetProperties().MergeProperties(properties);
3684 }
3685
3686 // When applying paragraph styles dynamically, don't change the text objects' attributes
3687 // since they will computed as needed. Only apply the character styling if it's _only_
3688 // character styling. This policy is subject to change and might be put under user control.
3689
3690 // Hm. we might well be applying a mix of paragraph and character styles, in which
3691 // case we _do_ want to apply character styles regardless of what para styles are set.
3692 // But if we're applying a paragraph style, which has some character attributes, but
3693 // we only want the paragraphs to hold this character style, then we _don't_ want to
3694 // apply the character style. So we need to be able to choose.
3695
3696 if (!parasOnly && charactersOnly && range.GetStart() != newPara->GetRange().GetEnd())
3697 {
3698 wxRichTextRange childRange(range);
3699 childRange.LimitTo(newPara->GetRange());
3700
3701 // Find the starting position and if necessary split it so
3702 // we can start applying different properties.
3703 // TODO: check that the properties actually change or are different
3704 // from properties outside of range
3705 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3706 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
3707
3708 if (childRange.GetStart() == newPara->GetRange().GetStart())
3709 firstObject = newPara->GetChildren().GetFirst()->GetData();
3710 else
3711 firstObject = newPara->SplitAt(range.GetStart());
3712
3713 // Increment by 1 because we're apply the style one _after_ the split point
3714 long splitPoint = childRange.GetEnd();
3715 if (splitPoint != newPara->GetRange().GetEnd())
3716 splitPoint ++;
3717
3718 // Find last object
3719 if (splitPoint == newPara->GetRange().GetEnd())
3720 lastObject = newPara->GetChildren().GetLast()->GetData();
3721 else
3722 // lastObject is set as a side-effect of splitting. It's
3723 // returned as the object before the new object.
3724 (void) newPara->SplitAt(splitPoint, & lastObject);
3725
3726 wxASSERT(firstObject != NULL);
3727 wxASSERT(lastObject != NULL);
3728
3729 if (!firstObject || !lastObject)
3730 continue;
3731
3732 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3733 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
3734
3735 wxASSERT(firstNode);
3736 wxASSERT(lastNode);
3737
3738 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
3739
3740 while (node2)
3741 {
3742 wxRichTextObject* child = node2->GetData();
3743
3744 if (removeProperties)
3745 {
3746 // Removes the given properties from the paragraph
3747 child->GetProperties().RemoveProperties(properties);
3748 }
3749 else if (resetExistingProperties)
3750 child->GetProperties() = properties;
3751 else
3752 {
3753 child->GetProperties().MergeProperties(properties);
3754 }
3755
3756 if (node2 == lastNode)
3757 break;
3758
3759 node2 = node2->GetNext();
3760 }
3761 }
3762 }
3763 }
3764
3765 node = node->GetNext();
3766 }
3767
3768 // Do action, or delay it until end of batch.
3769 if (haveControl && withUndo)
3770 buffer->SubmitAction(action);
3771
3772 return true;
3773 }
3774
3775 void wxRichTextParagraphLayoutBox::Reset()
3776 {
3777 Clear();
3778
3779 wxRichTextBuffer* buffer = GetBuffer();
3780 if (buffer && buffer->GetRichTextCtrl())
3781 {
3782 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET, buffer->GetRichTextCtrl()->GetId());
3783 event.SetEventObject(buffer->GetRichTextCtrl());
3784 event.SetContainer(this);
3785
3786 buffer->SendEvent(event, true);
3787 }
3788
3789 AddParagraph(wxEmptyString);
3790
3791 PrepareContent(*this);
3792
3793 InvalidateHierarchy(wxRICHTEXT_ALL);
3794 }
3795
3796 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3797 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange& invalidRange)
3798 {
3799 wxRichTextCompositeObject::Invalidate(invalidRange);
3800
3801 DoInvalidate(invalidRange);
3802 }
3803
3804 // Do the (in)validation for this object only
3805 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange& invalidRange)
3806 {
3807 if (invalidRange == wxRICHTEXT_ALL)
3808 {
3809 m_invalidRange = wxRICHTEXT_ALL;
3810 }
3811 // Already invalidating everything
3812 else if (m_invalidRange == wxRICHTEXT_ALL)
3813 {
3814 }
3815 else
3816 {
3817 if ((invalidRange.GetStart() < m_invalidRange.GetStart()) || m_invalidRange.GetStart() == -1)
3818 m_invalidRange.SetStart(invalidRange.GetStart());
3819 if (invalidRange.GetEnd() > m_invalidRange.GetEnd())
3820 m_invalidRange.SetEnd(invalidRange.GetEnd());
3821 }
3822 }
3823
3824 // Do the (in)validation both up and down the hierarchy
3825 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange& invalidRange)
3826 {
3827 Invalidate(invalidRange);
3828
3829 if (invalidRange != wxRICHTEXT_NONE)
3830 {
3831 // Now go up the hierarchy
3832 wxRichTextObject* thisObj = this;
3833 wxRichTextObject* p = GetParent();
3834 while (p)
3835 {
3836 wxRichTextParagraphLayoutBox* l = wxDynamicCast(p, wxRichTextParagraphLayoutBox);
3837 if (l)
3838 l->DoInvalidate(thisObj->GetRange());
3839
3840 thisObj = p;
3841 p = p->GetParent();
3842 }
3843 }
3844 }
3845
3846 /// Get invalid range, rounding to entire paragraphs if argument is true.
3847 wxRichTextRange wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs) const
3848 {
3849 if (m_invalidRange == wxRICHTEXT_ALL || m_invalidRange == wxRICHTEXT_NONE)
3850 return m_invalidRange;
3851
3852 wxRichTextRange range = m_invalidRange;
3853
3854 if (wholeParagraphs)
3855 {
3856 wxRichTextParagraph* para1 = GetParagraphAtPosition(range.GetStart());
3857 if (para1)
3858 range.SetStart(para1->GetRange().GetStart());
3859
3860 // FIXME: be more intelligent about this. Check if we have floating objects
3861 // before the end of the range. But it's not clear how we can in general
3862 // tell where it's safe to stop laying out.
3863 // Anyway, this code is central to efficiency when laying in floating mode.
3864 if (!wxRichTextBuffer::GetFloatingLayoutMode())
3865 {
3866 wxRichTextParagraph* para2 = GetParagraphAtPosition(range.GetEnd());
3867 if (para2)
3868 range.SetEnd(para2->GetRange().GetEnd());
3869 }
3870 else
3871 // Floating layout means that all children should be laid out,
3872 // because we can't tell how the whole buffer will be affected.
3873 range.SetEnd(GetOwnRange().GetEnd());
3874 }
3875 return range;
3876 }
3877
3878 /// Apply the style sheet to the buffer, for example if the styles have changed.
3879 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet* styleSheet)
3880 {
3881 wxASSERT(styleSheet != NULL);
3882 if (!styleSheet)
3883 return false;
3884
3885 int foundCount = 0;
3886
3887 wxRichTextAttr attr(GetBasicStyle());
3888 if (GetBasicStyle().HasParagraphStyleName())
3889 {
3890 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3891 if (paraDef)
3892 {
3893 attr.Apply(paraDef->GetStyleMergedWithBase(styleSheet));
3894 SetBasicStyle(attr);
3895 foundCount ++;
3896 }
3897 }
3898
3899 if (GetBasicStyle().HasCharacterStyleName())
3900 {
3901 wxRichTextCharacterStyleDefinition* charDef = styleSheet->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3902 if (charDef)
3903 {
3904 attr.Apply(charDef->GetStyleMergedWithBase(styleSheet));
3905 SetBasicStyle(attr);
3906 foundCount ++;
3907 }
3908 }
3909
3910 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3911 while (node)
3912 {
3913 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3914 // wxASSERT (para != NULL);
3915
3916 if (para)
3917 {
3918 // Combine paragraph and list styles. If there is a list style in the original attributes,
3919 // the current indentation overrides anything else and is used to find the item indentation.
3920 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3921 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3922 // exception as above).
3923 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3924 // So when changing a list style interactively, could retrieve level based on current style, then
3925 // set appropriate indent and apply new style.
3926
3927 int outline = -1;
3928 int num = -1;
3929 if (para->GetAttributes().HasOutlineLevel())
3930 outline = para->GetAttributes().GetOutlineLevel();
3931 if (para->GetAttributes().HasBulletNumber())
3932 num = para->GetAttributes().GetBulletNumber();
3933
3934 if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3935 {
3936 int currentIndent = para->GetAttributes().GetLeftIndent();
3937
3938 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3939 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3940 if (paraDef && !listDef)
3941 {
3942 para->GetAttributes() = paraDef->GetStyleMergedWithBase(styleSheet);
3943 foundCount ++;
3944 }
3945 else if (listDef && !paraDef)
3946 {
3947 // Set overall style defined for the list style definition
3948 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
3949
3950 // Apply the style for this level
3951 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3952 foundCount ++;
3953 }
3954 else if (listDef && paraDef)
3955 {
3956 // Combines overall list style, style for level, and paragraph style
3957 para->GetAttributes() = listDef->CombineWithParagraphStyle(currentIndent, paraDef->GetStyleMergedWithBase(styleSheet));
3958 foundCount ++;
3959 }
3960 }
3961 else if (para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3962 {
3963 int currentIndent = para->GetAttributes().GetLeftIndent();
3964
3965 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3966
3967 // Overall list definition style
3968 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
3969
3970 // Style for this level
3971 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3972
3973 foundCount ++;
3974 }
3975 else if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && para->GetAttributes().GetListStyleName().IsEmpty())
3976 {
3977 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3978 if (def)
3979 {
3980 para->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
3981 foundCount ++;
3982 }
3983 }
3984
3985 if (outline != -1)
3986 para->GetAttributes().SetOutlineLevel(outline);
3987 if (num != -1)
3988 para->GetAttributes().SetBulletNumber(num);
3989 }
3990
3991 node = node->GetNext();
3992 }
3993 return foundCount != 0;
3994 }
3995
3996 /// Set list style
3997 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
3998 {
3999 wxRichTextBuffer* buffer = GetBuffer();
4000 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
4001
4002 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
4003 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4004 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
4005 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
4006
4007 // Current number, if numbering
4008 int n = startFrom;
4009
4010 wxASSERT (!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
4011
4012 // If we are associated with a control, make undoable; otherwise, apply immediately
4013 // to the data.
4014
4015 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
4016
4017 wxRichTextAction* action = NULL;
4018
4019 if (haveControl && withUndo)
4020 {
4021 action = new wxRichTextAction(NULL, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
4022 action->SetRange(range);
4023 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
4024 }
4025
4026 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4027 while (node)
4028 {
4029 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
4030 // wxASSERT (para != NULL);
4031
4032 if (para && para->GetChildCount() > 0)
4033 {
4034 // Stop searching if we're beyond the range of interest
4035 if (para->GetRange().GetStart() > range.GetEnd())
4036 break;
4037
4038 if (!para->GetRange().IsOutside(range))
4039 {
4040 // We'll be using a copy of the paragraph to make style changes,
4041 // not updating the buffer directly.
4042 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
4043
4044 if (haveControl && withUndo)
4045 {
4046 newPara = new wxRichTextParagraph(*para);
4047 action->GetNewParagraphs().AppendChild(newPara);
4048
4049 // Also store the old ones for Undo
4050 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
4051 }
4052 else
4053 newPara = para;
4054
4055 if (def)
4056 {
4057 int thisIndent = newPara->GetAttributes().GetLeftIndent();
4058 int thisLevel = specifyLevel ? specifiedLevel : def->FindLevelForIndent(thisIndent);
4059
4060 // How is numbering going to work?
4061 // If we are renumbering, or numbering for the first time, we need to keep
4062 // track of the number for each level. But we might be simply applying a different
4063 // list style.
4064 // In Word, applying a style to several paragraphs, even if at different levels,
4065 // reverts the level back to the same one. So we could do the same here.
4066 // Renumbering will need to be done when we promote/demote a paragraph.
4067
4068 // Apply the overall list style, and item style for this level
4069 wxRichTextAttr listStyle(def->GetCombinedStyleForLevel(thisLevel, styleSheet));
4070 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
4071
4072 // Now we need to do numbering
4073 // Preserve the existing list item continuation bullet style, if any
4074 if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))
4075 newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
4076 else
4077 {
4078 if (renumber)
4079 {
4080 newPara->GetAttributes().SetBulletNumber(n);
4081 }
4082
4083 n ++;
4084 }
4085 }
4086 else if (!newPara->GetAttributes().GetListStyleName().IsEmpty())
4087 {
4088 // if def is NULL, remove list style, applying any associated paragraph style
4089 // to restore the attributes
4090
4091 newPara->GetAttributes().SetListStyleName(wxEmptyString);
4092 newPara->GetAttributes().SetLeftIndent(0, 0);
4093 newPara->GetAttributes().SetBulletText(wxEmptyString);
4094 newPara->GetAttributes().SetBulletStyle(0);
4095
4096 // Eliminate the main list-related attributes
4097 newPara->GetAttributes().SetFlags(newPara->GetAttributes().GetFlags() & ~wxTEXT_ATTR_LEFT_INDENT & ~wxTEXT_ATTR_BULLET_STYLE & ~wxTEXT_ATTR_BULLET_NUMBER & ~wxTEXT_ATTR_BULLET_TEXT & wxTEXT_ATTR_LIST_STYLE_NAME);
4098
4099 if (styleSheet && !newPara->GetAttributes().GetParagraphStyleName().IsEmpty())
4100 {
4101 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(newPara->GetAttributes().GetParagraphStyleName());
4102 if (def)
4103 {
4104 newPara->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
4105 }
4106 }
4107 }
4108 }
4109 }
4110
4111 node = node->GetNext();
4112 }
4113
4114 // Do action, or delay it until end of batch.
4115 if (haveControl && withUndo)
4116 buffer->SubmitAction(action);
4117
4118 return true;
4119 }
4120
4121 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4122 {
4123 wxRichTextBuffer* buffer = GetBuffer();
4124 if (buffer && buffer->GetStyleSheet())
4125 {
4126 wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->FindListStyle(defName);
4127 if (def)
4128 return SetListStyle(range, def, flags, startFrom, specifiedLevel);
4129 }
4130 return false;
4131 }
4132
4133 /// Clear list for given range
4134 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange& range, int flags)
4135 {
4136 return SetListStyle(range, NULL, flags);
4137 }
4138
4139 /// Number/renumber any list elements in the given range
4140 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4141 {
4142 return DoNumberList(range, range, 0, def, flags, startFrom, specifiedLevel);
4143 }
4144
4145 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4146 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, const wxRichTextRange& promotionRange, int promoteBy,
4147 wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4148 {
4149 wxRichTextBuffer* buffer = GetBuffer();
4150 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
4151
4152 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
4153 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4154 #if wxDEBUG_LEVEL
4155 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
4156 #endif
4157
4158 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
4159
4160 // Max number of levels
4161 const int maxLevels = 10;
4162
4163 // The level we're looking at now
4164 int currentLevel = -1;
4165
4166 // The item number for each level
4167 int levels[maxLevels];
4168 int i;
4169
4170 // Reset all numbering
4171 for (i = 0; i < maxLevels; i++)
4172 {
4173 if (startFrom != -1)
4174 levels[i] = startFrom-1;
4175 else if (renumber) // start again
4176 levels[i] = 0;
4177 else
4178 levels[i] = -1; // start from the number we found, if any
4179 }
4180
4181 #if wxDEBUG_LEVEL
4182 wxASSERT(!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
4183 #endif
4184
4185 // If we are associated with a control, make undoable; otherwise, apply immediately
4186 // to the data.
4187
4188 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
4189
4190 wxRichTextAction* action = NULL;
4191
4192 if (haveControl && withUndo)
4193 {
4194 action = new wxRichTextAction(NULL, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
4195 action->SetRange(range);
4196 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
4197 }
4198
4199 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4200 while (node)
4201 {
4202 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
4203 // wxASSERT (para != NULL);
4204
4205 if (para && para->GetChildCount() > 0)
4206 {
4207 // Stop searching if we're beyond the range of interest
4208 if (para->GetRange().GetStart() > range.GetEnd())
4209 break;
4210
4211 if (!para->GetRange().IsOutside(range))
4212 {
4213 // We'll be using a copy of the paragraph to make style changes,
4214 // not updating the buffer directly.
4215 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
4216
4217 if (haveControl && withUndo)
4218 {
4219 newPara = new wxRichTextParagraph(*para);
4220 action->GetNewParagraphs().AppendChild(newPara);
4221
4222 // Also store the old ones for Undo
4223 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
4224 }
4225 else
4226 newPara = para;
4227
4228 wxRichTextListStyleDefinition* defToUse = def;
4229 if (!defToUse)
4230 {
4231 if (styleSheet && !newPara->GetAttributes().GetListStyleName().IsEmpty())
4232 defToUse = styleSheet->FindListStyle(newPara->GetAttributes().GetListStyleName());
4233 }
4234
4235 if (defToUse)
4236 {
4237 int thisIndent = newPara->GetAttributes().GetLeftIndent();
4238 int thisLevel = defToUse->FindLevelForIndent(thisIndent);
4239
4240 // If we've specified a level to apply to all, change the level.
4241 if (specifiedLevel != -1)
4242 thisLevel = specifiedLevel;
4243
4244 // Do promotion if specified
4245 if ((promoteBy != 0) && !para->GetRange().IsOutside(promotionRange))
4246 {
4247 thisLevel = thisLevel - promoteBy;
4248 if (thisLevel < 0)
4249 thisLevel = 0;
4250 if (thisLevel > 9)
4251 thisLevel = 9;
4252 }
4253
4254 // Apply the overall list style, and item style for this level
4255 wxRichTextAttr listStyle(defToUse->GetCombinedStyleForLevel(thisLevel, styleSheet));
4256 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
4257
4258 // Preserve the existing list item continuation bullet style, if any
4259 if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))
4260 newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
4261
4262 // OK, we've (re)applied the style, now let's get the numbering right.
4263
4264 if (currentLevel == -1)
4265 currentLevel = thisLevel;
4266
4267 // Same level as before, do nothing except increment level's number afterwards
4268 if (currentLevel == thisLevel)
4269 {
4270 }
4271 // A deeper level: start renumbering all levels after current level
4272 else if (thisLevel > currentLevel)
4273 {
4274 for (i = currentLevel+1; i <= thisLevel; i++)
4275 {
4276 levels[i] = 0;
4277 }
4278 currentLevel = thisLevel;
4279 }
4280 else if (thisLevel < currentLevel)
4281 {
4282 currentLevel = thisLevel;
4283 }
4284
4285 // Use the current numbering if -1 and we have a bullet number already
4286 if (levels[currentLevel] == -1)
4287 {
4288 if (newPara->GetAttributes().HasBulletNumber())
4289 levels[currentLevel] = newPara->GetAttributes().GetBulletNumber();
4290 else
4291 levels[currentLevel] = 1;
4292 }
4293 else
4294 {
4295 if (!(para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)))
4296 levels[currentLevel] ++;
4297 }
4298
4299 newPara->GetAttributes().SetBulletNumber(levels[currentLevel]);
4300
4301 // Create the bullet text if an outline list
4302 if (listStyle.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
4303 {
4304 wxString text;
4305 for (i = 0; i <= currentLevel; i++)
4306 {
4307 if (!text.IsEmpty())
4308 text += wxT(".");
4309 text += wxString::Format(wxT("%d"), levels[i]);
4310 }
4311 newPara->GetAttributes().SetBulletText(text);
4312 }
4313 }
4314 }
4315 }
4316
4317 node = node->GetNext();
4318 }
4319
4320 // Do action, or delay it until end of batch.
4321 if (haveControl && withUndo)
4322 buffer->SubmitAction(action);
4323
4324 return true;
4325 }
4326
4327 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4328 {
4329 wxRichTextBuffer* buffer = GetBuffer();
4330 if (buffer->GetStyleSheet())
4331 {
4332 wxRichTextListStyleDefinition* def = NULL;
4333 if (!defName.IsEmpty())
4334 def = buffer->GetStyleSheet()->FindListStyle(defName);
4335 return NumberList(range, def, flags, startFrom, specifiedLevel);
4336 }
4337 return false;
4338 }
4339
4340 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4341 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel)
4342 {
4343 // TODO
4344 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4345 // to NumberList with a flag indicating promotion is required within one of the ranges.
4346 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4347 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4348 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4349 // list position will start from 1.
4350 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4351 // We can end the renumbering at this point.
4352
4353 // For now, only renumber within the promotion range.
4354
4355 return DoNumberList(range, range, promoteBy, def, flags, 1, specifiedLevel);
4356 }
4357
4358 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel)
4359 {
4360 wxRichTextBuffer* buffer = GetBuffer();
4361 if (buffer->GetStyleSheet())
4362 {
4363 wxRichTextListStyleDefinition* def = NULL;
4364 if (!defName.IsEmpty())
4365 def = buffer->GetStyleSheet()->FindListStyle(defName);
4366 return PromoteList(promoteBy, range, def, flags, specifiedLevel);
4367 }
4368 return false;
4369 }
4370
4371 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4372 /// position of the paragraph that it had to start looking from.
4373 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr) const
4374 {
4375 // TODO: add GetNextChild/GetPreviousChild to composite
4376 // Search for a paragraph that isn't a continuation paragraph (no bullet)
4377 while (previousParagraph && previousParagraph->GetAttributes().HasBulletStyle() && previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)
4378 {
4379 wxRichTextObjectList::compatibility_iterator node = ((wxRichTextCompositeObject*) previousParagraph->GetParent())->GetChildren().Find(previousParagraph);
4380 if (node)
4381 {
4382 node = node->GetPrevious();
4383 if (node)
4384 previousParagraph = wxDynamicCast(node->GetData(), wxRichTextParagraph);
4385 else
4386 previousParagraph = NULL;
4387 }
4388 else
4389 previousParagraph = NULL;
4390 }
4391
4392 if (!previousParagraph || !previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE)
4393 return false;
4394
4395 wxRichTextBuffer* buffer = GetBuffer();
4396 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
4397 if (styleSheet && !previousParagraph->GetAttributes().GetListStyleName().IsEmpty())
4398 {
4399 wxRichTextListStyleDefinition* def = styleSheet->FindListStyle(previousParagraph->GetAttributes().GetListStyleName());
4400 if (def)
4401 {
4402 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4403 // int thisLevel = def->FindLevelForIndent(thisIndent);
4404
4405 bool isOutline = (previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE) != 0;
4406
4407 attr.SetFlags(previousParagraph->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER|wxTEXT_ATTR_BULLET_TEXT|wxTEXT_ATTR_BULLET_NAME));
4408 if (previousParagraph->GetAttributes().HasBulletName())
4409 attr.SetBulletName(previousParagraph->GetAttributes().GetBulletName());
4410 attr.SetBulletStyle(previousParagraph->GetAttributes().GetBulletStyle());
4411 attr.SetListStyleName(previousParagraph->GetAttributes().GetListStyleName());
4412
4413 int nextNumber = previousParagraph->GetAttributes().GetBulletNumber() + 1;
4414 attr.SetBulletNumber(nextNumber);
4415
4416 if (isOutline)
4417 {
4418 wxString text = previousParagraph->GetAttributes().GetBulletText();
4419 if (!text.IsEmpty())
4420 {
4421 int pos = text.Find(wxT('.'), true);
4422 if (pos != wxNOT_FOUND)
4423 {
4424 text = text.Mid(0, text.Length() - pos - 1);
4425 }
4426 else
4427 text = wxEmptyString;
4428 if (!text.IsEmpty())
4429 text += wxT(".");
4430 text += wxString::Format(wxT("%d"), nextNumber);
4431 attr.SetBulletText(text);
4432 }
4433 }
4434
4435 return true;
4436 }
4437 else
4438 return false;
4439 }
4440 else
4441 return false;
4442 }
4443
4444 /*!
4445 * wxRichTextParagraph
4446 * This object represents a single paragraph (or in a straight text editor, a line).
4447 */
4448
4449 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph, wxRichTextCompositeObject)
4450
4451 wxArrayInt wxRichTextParagraph::sm_defaultTabs;
4452
4453 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject* parent, wxRichTextAttr* style):
4454 wxRichTextCompositeObject(parent)
4455 {
4456 if (style)
4457 SetAttributes(*style);
4458 }
4459
4460 wxRichTextParagraph::wxRichTextParagraph(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* paraStyle, wxRichTextAttr* charStyle):
4461 wxRichTextCompositeObject(parent)
4462 {
4463 if (paraStyle)
4464 SetAttributes(*paraStyle);
4465
4466 AppendChild(new wxRichTextPlainText(text, this, charStyle));
4467 }
4468
4469 wxRichTextParagraph::~wxRichTextParagraph()
4470 {
4471 ClearLines();
4472 }
4473
4474 /// Draw the item
4475 bool wxRichTextParagraph::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int style)
4476 {
4477 if (!IsShown())
4478 return true;
4479
4480 // Currently we don't merge these attributes with the parent, but we
4481 // should consider whether we should (e.g. if we set a border colour
4482 // for all paragraphs). But generally box attributes are likely to be
4483 // different for different objects.
4484 wxRect paraRect = GetRect();
4485 wxRichTextAttr attr = GetCombinedAttributes();
4486 context.ApplyVirtualAttributes(attr, this);
4487
4488 DrawBoxAttributes(dc, GetBuffer(), attr, paraRect);
4489
4490 // Draw the bullet, if any
4491 if ((attr.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE) == 0 && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION) == 0)
4492 {
4493 if (attr.GetLeftSubIndent() != 0)
4494 {
4495 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
4496 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
4497
4498 wxRichTextAttr bulletAttr(attr);
4499
4500 // Combine with the font of the first piece of content, if one is specified
4501 if (GetChildren().GetCount() > 0)
4502 {
4503 wxRichTextObject* firstObj = (wxRichTextObject*) GetChildren().GetFirst()->GetData();
4504 if (!firstObj->IsFloatable() && firstObj->GetAttributes().HasFont())
4505 {
4506 wxRichTextApplyStyle(bulletAttr, firstObj->GetAttributes());
4507 }
4508 }
4509
4510 // Get line height from first line, if any
4511 wxRichTextLine* line = m_cachedLines.GetFirst() ? (wxRichTextLine* ) m_cachedLines.GetFirst()->GetData() : NULL;
4512
4513 wxPoint linePos;
4514 int lineHeight wxDUMMY_INITIALIZE(0);
4515 if (line)
4516 {
4517 lineHeight = line->GetSize().y;
4518 linePos = line->GetPosition() + GetPosition();
4519 }
4520 else
4521 {
4522 wxFont font;
4523 if (bulletAttr.HasFont() && GetBuffer())
4524 font = GetBuffer()->GetFontTable().FindFont(bulletAttr);
4525 else
4526 font = (*wxNORMAL_FONT);
4527
4528 wxCheckSetFont(dc, font);
4529
4530 lineHeight = dc.GetCharHeight();
4531 linePos = GetPosition();
4532 linePos.y += spaceBeforePara;
4533 }
4534
4535 wxRect bulletRect(GetPosition().x + leftIndent, linePos.y, linePos.x - (GetPosition().x + leftIndent), lineHeight);
4536
4537 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP)
4538 {
4539 if (wxRichTextBuffer::GetRenderer())
4540 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc, bulletAttr, bulletRect);
4541 }
4542 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD)
4543 {
4544 if (wxRichTextBuffer::GetRenderer())
4545 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc, bulletAttr, bulletRect);
4546 }
4547 else
4548 {
4549 wxString bulletText = GetBulletText();
4550
4551 if (!bulletText.empty() && wxRichTextBuffer::GetRenderer())
4552 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc, bulletAttr, bulletRect, bulletText);
4553 }
4554 }
4555 }
4556
4557 // Draw the range for each line, one object at a time.
4558
4559 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
4560 while (node)
4561 {
4562 wxRichTextLine* line = node->GetData();
4563 wxRichTextRange lineRange = line->GetAbsoluteRange();
4564
4565 // Lines are specified relative to the paragraph
4566
4567 wxPoint linePosition = line->GetPosition() + GetPosition();
4568
4569 // Don't draw if off the screen
4570 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) != 0) || ((linePosition.y + line->GetSize().y) >= rect.y && linePosition.y <= rect.y + rect.height))
4571 {
4572 wxPoint objectPosition = linePosition;
4573 int maxDescent = line->GetDescent();
4574
4575 // Loop through objects until we get to the one within range
4576 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
4577
4578 int i = 0;
4579 while (node2)
4580 {
4581 wxRichTextObject* child = node2->GetData();
4582
4583 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
4584 {
4585 // Draw this part of the line at the correct position
4586 wxRichTextRange objectRange(child->GetRange());
4587 objectRange.LimitTo(lineRange);
4588
4589 wxSize objectSize;
4590 if (child->IsTopLevel())
4591 {
4592 objectSize = child->GetCachedSize();
4593 objectRange = child->GetOwnRange();
4594 }
4595 else
4596 {
4597 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4598 if (i < (int) line->GetObjectSizes().GetCount())
4599 {
4600 objectSize.x = line->GetObjectSizes()[(size_t) i];
4601 }
4602 else
4603 #endif
4604 {
4605 int descent = 0;
4606 child->GetRangeSize(objectRange, objectSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, objectPosition);
4607 }
4608 }
4609
4610 // Use the child object's width, but the whole line's height
4611 wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
4612 child->Draw(dc, context, objectRange, selection, childRect, maxDescent, style);
4613
4614 objectPosition.x += objectSize.x;
4615 i ++;
4616 }
4617 else if (child->GetRange().GetStart() > lineRange.GetEnd())
4618 // Can break out of inner loop now since we've passed this line's range
4619 break;
4620
4621 node2 = node2->GetNext();
4622 }
4623 }
4624
4625 node = node->GetNext();
4626 }
4627
4628 return true;
4629 }
4630
4631 // Get the range width using partial extents calculated for the whole paragraph.
4632 static int wxRichTextGetRangeWidth(const wxRichTextParagraph& para, const wxRichTextRange& range, const wxArrayInt& partialExtents)
4633 {
4634 wxASSERT(partialExtents.GetCount() >= (size_t) range.GetLength());
4635
4636 if (partialExtents.GetCount() < (size_t) range.GetLength())
4637 return 0;
4638
4639 int leftMostPos = 0;
4640 if (range.GetStart() - para.GetRange().GetStart() > 0)
4641 leftMostPos = partialExtents[range.GetStart() - para.GetRange().GetStart() - 1];
4642
4643 int rightMostPos = partialExtents[range.GetEnd() - para.GetRange().GetStart()];
4644
4645 int w = rightMostPos - leftMostPos;
4646
4647 return w;
4648 }
4649
4650 /// Lay the item out
4651 bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
4652 {
4653 // Deal with floating objects firstly before the normal layout
4654 wxRichTextBuffer* buffer = GetBuffer();
4655 wxASSERT(buffer);
4656
4657 wxRichTextFloatCollector* collector = GetContainer()->GetFloatCollector();
4658
4659 if (wxRichTextBuffer::GetFloatingLayoutMode())
4660 {
4661 wxASSERT(collector != NULL);
4662 if (collector)
4663 LayoutFloat(dc, context, rect, parentRect, style, collector);
4664 }
4665
4666 wxRichTextAttr attr = GetCombinedAttributes();
4667 context.ApplyVirtualAttributes(attr, this);
4668
4669 // ClearLines();
4670
4671 // Increase the size of the paragraph due to spacing
4672 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
4673 int spaceAfterPara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingAfter());
4674 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
4675 int leftSubIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftSubIndent());
4676 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
4677
4678 int lineSpacing = 0;
4679
4680 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4681 if (attr.HasLineSpacing() && attr.GetLineSpacing() > 0 && attr.HasFont())
4682 {
4683 wxFont font(buffer->GetFontTable().FindFont(attr));
4684 if (font.IsOk())
4685 {
4686 wxCheckSetFont(dc, font);
4687 lineSpacing = (int) (double(dc.GetCharHeight()) * (double(attr.GetLineSpacing())/10.0 - 1.0));
4688 }
4689 }
4690
4691 // Start position for each line relative to the paragraph
4692 int startPositionFirstLine = leftIndent;
4693 int startPositionSubsequentLines = leftIndent + leftSubIndent;
4694
4695 // If we have a bullet in this paragraph, the start position for the first line's text
4696 // is actually leftIndent + leftSubIndent.
4697 if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
4698 startPositionFirstLine = startPositionSubsequentLines;
4699
4700 long lastEndPos = GetRange().GetStart()-1;
4701 long lastCompletedEndPos = lastEndPos;
4702
4703 int currentWidth = 0;
4704 SetPosition(rect.GetPosition());
4705
4706 wxPoint currentPosition(0, spaceBeforePara); // We will calculate lines relative to paragraph
4707 int lineHeight = 0;
4708 int maxWidth = 0;
4709 int maxHeight = currentPosition.y;
4710 int maxAscent = 0;
4711 int maxDescent = 0;
4712 int lineCount = 0;
4713 int lineAscent = 0;
4714 int lineDescent = 0;
4715
4716 wxRichTextObjectList::compatibility_iterator node;
4717
4718 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4719 wxUnusedVar(style);
4720 wxArrayInt partialExtents;
4721
4722 wxSize paraSize;
4723 int paraDescent = 0;
4724
4725 // This calculates the partial text extents
4726 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, rect.GetPosition(), parentRect.GetSize(), & partialExtents);
4727 #else
4728 node = m_children.GetFirst();
4729 while (node)
4730 {
4731 wxRichTextObject* child = node->GetData();
4732
4733 //child->SetCachedSize(wxDefaultSize);
4734 child->Layout(dc, context, rect, style);
4735
4736 node = node->GetNext();
4737 }
4738 #endif
4739
4740 // Split up lines
4741
4742 // We may need to go back to a previous child, in which case create the new line,
4743 // find the child corresponding to the start position of the string, and
4744 // continue.
4745
4746 wxRect availableRect;
4747
4748 node = m_children.GetFirst();
4749 while (node)
4750 {
4751 wxRichTextObject* child = node->GetData();
4752
4753 // If floating, ignore. We already laid out floats.
4754 // Also ignore if empty object, except if we haven't got any
4755 // size yet.
4756 if ((child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
4757 || !child->IsShown() || (child->GetRange().GetLength() == 0 && maxHeight > spaceBeforePara)
4758 )
4759 {
4760 node = node->GetNext();
4761 continue;
4762 }
4763
4764 // If this is e.g. a composite text box, it will need to be laid out itself.
4765 // But if just a text fragment or image, for example, this will
4766 // do nothing. NB: won't we need to set the position after layout?
4767 // since for example if position is dependent on vertical line size, we
4768 // can't tell the position until the size is determined. So possibly introduce
4769 // another layout phase.
4770
4771 // We may only be looking at part of a child, if we searched back for wrapping
4772 // and found a suitable point some way into the child. So get the size for the fragment
4773 // if necessary.
4774
4775 long nextBreakPos = GetFirstLineBreakPosition(lastEndPos+1);
4776 long lastPosToUse = child->GetRange().GetEnd();
4777 bool lineBreakInThisObject = (nextBreakPos > -1 && nextBreakPos <= child->GetRange().GetEnd());
4778
4779 if (lineBreakInThisObject)
4780 lastPosToUse = nextBreakPos;
4781
4782 wxSize childSize;
4783 int childDescent = 0;
4784
4785 int startOffset = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
4786 availableRect = wxRect(rect.x + startOffset, rect.y + currentPosition.y,
4787 rect.width - startOffset - rightIndent, rect.height);
4788
4789 if (child->IsTopLevel())
4790 {
4791 wxSize oldSize = child->GetCachedSize();
4792
4793 child->Invalidate(wxRICHTEXT_ALL);
4794 child->SetPosition(wxPoint(0, 0));
4795
4796 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4797 // lays out the object again using the minimum size
4798 // The position will be determined by its location in its line,
4799 // and not by the child's actual position.
4800 child->LayoutToBestSize(dc, context, buffer,
4801 attr, child->GetAttributes(), availableRect, parentRect, style);
4802
4803 if (oldSize != child->GetCachedSize())
4804 {
4805 partialExtents.Clear();
4806
4807 // Recalculate the partial text extents since the child object changed size
4808 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), parentRect.GetSize(), & partialExtents);
4809 }
4810 }
4811
4812 // Problem: we need to layout composites here for which we need the available width,
4813 // but we can't get the available width without using the float collector which
4814 // needs to know the object height.
4815
4816 if ((nextBreakPos == -1) && (lastEndPos == child->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4817 {
4818 childSize = child->GetCachedSize();
4819 childDescent = child->GetDescent();
4820 }
4821 else
4822 {
4823 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4824 // Get height only, then the width using the partial extents
4825 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY, wxPoint(0,0), parentRect.GetSize());
4826 childSize.x = wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos+1, lastPosToUse), partialExtents);
4827 #else
4828 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, rect.GetPosition(), parentRect.GetSize());
4829 #endif
4830 }
4831
4832 bool doLoop = true;
4833 int loopIterations = 0;
4834
4835 // If there are nested objects that need to lay themselves out, we have to do this in a
4836 // loop because the height of the object may well depend on the available width.
4837 // And because of floating object positioning, the available width depends on the
4838 // height of the object and whether it will clash with the floating objects.
4839 // So, we see whether the available width changes due to the presence of floating images.
4840 // If it does, then we'll use the new restricted width to find the object height again.
4841 // If this causes another restriction in the available width, we'll try again, until
4842 // either we lose patience or the available width settles down.
4843 do
4844 {
4845 loopIterations ++;
4846
4847 wxRect oldAvailableRect = availableRect;
4848
4849 // Available width depends on the floating objects and the line height.
4850 // Note: the floating objects may be placed vertically along the two sides of
4851 // buffer, so we may have different available line widths with different
4852 // [startY, endY]. So, we can't determine how wide the available
4853 // space is until we know the exact line height.
4854 if (childDescent == 0)
4855 {
4856 lineHeight = wxMax(lineHeight, childSize.y);
4857 lineDescent = maxDescent;
4858 lineAscent = maxAscent;
4859 }
4860 else
4861 {
4862 lineDescent = wxMax(childDescent, maxDescent);
4863 lineAscent = wxMax(childSize.y-childDescent, maxAscent);
4864 }
4865 lineHeight = wxMax(lineHeight, (lineDescent + lineAscent));
4866
4867 if (wxRichTextBuffer::GetFloatingLayoutMode() && collector)
4868 {
4869 wxRect floatAvailableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight);
4870
4871 // Adjust availableRect to the space that is available when taking floating objects into account.
4872
4873 if (floatAvailableRect.x + startOffset > availableRect.x)
4874 {
4875 int newX = floatAvailableRect.x + startOffset;
4876 int newW = availableRect.width - (newX - availableRect.x);
4877 availableRect.x = newX;
4878 availableRect.width = newW;
4879 }
4880
4881 if (floatAvailableRect.width < availableRect.width)
4882 availableRect.width = floatAvailableRect.width;
4883 }
4884
4885 currentPosition.x = availableRect.x - rect.x;
4886
4887 if (child->IsTopLevel() && loopIterations <= 20)
4888 {
4889 if (availableRect != oldAvailableRect)
4890 {
4891 wxSize oldSize = child->GetCachedSize();
4892
4893 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4894 // lays out the object again using the minimum size
4895 child->Invalidate(wxRICHTEXT_ALL);
4896 child->LayoutToBestSize(dc, context, buffer,
4897 attr, child->GetAttributes(), availableRect, parentRect.GetSize(), style);
4898 childSize = child->GetCachedSize();
4899 childDescent = child->GetDescent();
4900
4901 if (oldSize != child->GetCachedSize())
4902 {
4903 partialExtents.Clear();
4904
4905 // Recalculate the partial text extents since the child object changed size
4906 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), parentRect.GetSize(), & partialExtents);
4907 }
4908
4909 // Go around the loop finding the available rect for the given floating objects
4910 }
4911 else
4912 doLoop = false;
4913 }
4914 else
4915 doLoop = false;
4916 }
4917 while (doLoop);
4918
4919 if (child->IsTopLevel())
4920 {
4921 // We can move it to the correct position at this point
4922 // TODO: probably need to add margin
4923 child->Move(GetPosition() + wxPoint(currentWidth + (wxMax(leftIndent, leftIndent + leftSubIndent)), currentPosition.y));
4924 }
4925
4926 // Cases:
4927 // 1) There was a line break BEFORE the natural break
4928 // 2) There was a line break AFTER the natural break
4929 // 3) It's the last line
4930 // 4) The child still fits (carry on) - 'else' clause
4931
4932 if ((lineBreakInThisObject && (childSize.x + currentWidth <= availableRect.width))
4933 ||
4934 (childSize.x + currentWidth > availableRect.width)
4935 #if 0
4936 ||
4937 ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext())
4938 #endif
4939 )
4940 {
4941 long wrapPosition = 0;
4942 if ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext() && !lineBreakInThisObject)
4943 wrapPosition = child->GetRange().GetEnd();
4944 else
4945
4946 // Find a place to wrap. This may walk back to previous children,
4947 // for example if a word spans several objects.
4948 // Note: one object must contains only one wxTextAtrr, so the line height will not
4949 // change inside one object. Thus, we can pass the remain line width to the
4950 // FindWrapPosition function.
4951 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, context, availableRect.width, wrapPosition, & partialExtents))
4952 {
4953 // If the function failed, just cut it off at the end of this child.
4954 wrapPosition = child->GetRange().GetEnd();
4955 }
4956
4957 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4958 if (wrapPosition <= lastCompletedEndPos)
4959 wrapPosition = wxMax(lastCompletedEndPos+1,child->GetRange().GetEnd());
4960
4961 // Line end position shouldn't be the same as the end, or greater.
4962 if (wrapPosition >= GetRange().GetEnd())
4963 wrapPosition = wxMax(0, GetRange().GetEnd()-1);
4964
4965 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4966
4967 // Let's find the actual size of the current line now
4968 wxSize actualSize;
4969 wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
4970
4971 childDescent = 0;
4972
4973 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4974 if (!child->IsEmpty())
4975 {
4976 // Get height only, then the width using the partial extents
4977 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY, wxPoint(0,0), parentRect.GetSize());
4978 actualSize.x = wxRichTextGetRangeWidth(*this, actualRange, partialExtents);
4979 }
4980 else
4981 #endif
4982 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, wxPoint(0,0), parentRect.GetSize());
4983
4984 currentWidth = actualSize.x;
4985
4986 // The descent for the whole line at this point, is the correct max descent
4987 maxDescent = childDescent;
4988 // Maximum ascent
4989 maxAscent = actualSize.y-childDescent;
4990
4991 // lineHeight is given by the height for the whole line, since it will
4992 // take into account ascend/descend.
4993 lineHeight = actualSize.y;
4994
4995 if (lineHeight == 0 && buffer)
4996 {
4997 wxFont font(buffer->GetFontTable().FindFont(attr));
4998 wxCheckSetFont(dc, font);
4999 lineHeight = dc.GetCharHeight();
5000 }
5001
5002 if (maxDescent == 0)
5003 {
5004 int w, h;
5005 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
5006 }
5007
5008 // Add a new line
5009 wxRichTextLine* line = AllocateLine(lineCount);
5010
5011 // Set relative range so we won't have to change line ranges when paragraphs are moved
5012 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5013 line->SetPosition(currentPosition);
5014 line->SetSize(wxSize(currentWidth, lineHeight));
5015 line->SetDescent(maxDescent);
5016
5017 maxHeight = currentPosition.y + lineHeight;
5018
5019 // Now move down a line. TODO: add margins, spacing
5020 currentPosition.y += lineHeight;
5021 currentPosition.y += lineSpacing;
5022 maxDescent = 0;
5023 maxAscent = 0;
5024 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5025 currentWidth = 0;
5026
5027 lineCount ++;
5028
5029 // TODO: account for zero-length objects
5030 // wxASSERT(wrapPosition > lastCompletedEndPos);
5031
5032 lastEndPos = wrapPosition;
5033 lastCompletedEndPos = lastEndPos;
5034
5035 lineHeight = 0;
5036
5037 if (wrapPosition < GetRange().GetEnd()-1)
5038 {
5039 // May need to set the node back to a previous one, due to searching back in wrapping
5040 wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition(wrapPosition+1);
5041 if (childAfterWrapPosition)
5042 node = m_children.Find(childAfterWrapPosition);
5043 else
5044 node = node->GetNext();
5045 }
5046 else
5047 node = node->GetNext();
5048
5049 // Apply paragraph styles such as alignment to the wrapped line
5050 ApplyParagraphStyle(line, attr, availableRect, dc);
5051 }
5052 else
5053 {
5054 // We still fit, so don't add a line, and keep going
5055 currentWidth += childSize.x;
5056
5057 if (childDescent == 0)
5058 {
5059 // An object with a zero descend value wants to take up the whole
5060 // height regardless of baseline
5061 lineHeight = wxMax(lineHeight, childSize.y);
5062 }
5063 else
5064 {
5065 maxDescent = wxMax(childDescent, maxDescent);
5066 maxAscent = wxMax(childSize.y-childDescent, maxAscent);
5067 }
5068
5069 lineHeight = wxMax(lineHeight, (maxDescent + maxAscent));
5070
5071 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5072 lastEndPos = child->GetRange().GetEnd();
5073
5074 node = node->GetNext();
5075 }
5076 }
5077
5078 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5079
5080 // Add the last line - it's the current pos -> last para pos
5081 // Substract -1 because the last position is always the end-paragraph position.
5082 if (lastCompletedEndPos <= GetRange().GetEnd()-1)
5083 {
5084 currentPosition.x = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
5085
5086 wxRichTextLine* line = AllocateLine(lineCount);
5087
5088 wxRichTextRange actualRange(lastCompletedEndPos+1, GetRange().GetEnd()-1);
5089
5090 // Set relative range so we won't have to change line ranges when paragraphs are moved
5091 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5092
5093 line->SetPosition(currentPosition);
5094
5095 if (lineHeight == 0 && buffer)
5096 {
5097 wxFont font(buffer->GetFontTable().FindFont(attr));
5098 wxCheckSetFont(dc, font);
5099 lineHeight = dc.GetCharHeight();
5100 }
5101
5102 if (maxDescent == 0)
5103 {
5104 int w, h;
5105 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
5106 }
5107
5108 line->SetSize(wxSize(currentWidth, lineHeight));
5109 line->SetDescent(maxDescent);
5110 currentPosition.y += lineHeight;
5111 currentPosition.y += lineSpacing;
5112 lineCount ++;
5113
5114 // Apply paragraph styles such as alignment to the wrapped line
5115 ApplyParagraphStyle(line, attr, availableRect, dc);
5116 }
5117
5118 // Remove remaining unused line objects, if any
5119 ClearUnusedLines(lineCount);
5120
5121 // We need to add back the margins etc.
5122 {
5123 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5124 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, currentPosition.y + spaceAfterPara));
5125 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
5126 SetCachedSize(marginRect.GetSize());
5127 }
5128
5129 // The maximum size is the length of the paragraph stretched out into a line.
5130 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
5131 // this size. TODO: take into account line breaks.
5132 {
5133 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5134 contentRect = wxRect(wxPoint(0, 0), wxSize(paraSize.x + wxMax(leftIndent, leftIndent + leftSubIndent) + rightIndent, currentPosition.y + spaceAfterPara));
5135 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
5136 SetMaxSize(marginRect.GetSize());
5137 }
5138
5139 // Find the greatest minimum size. Currently we only look at non-text objects,
5140 // which isn't ideal but it would be slow to find the maximum word width to
5141 // use as the minimum.
5142 {
5143 int minWidth = 0;
5144 node = m_children.GetFirst();
5145 while (node)
5146 {
5147 wxRichTextObject* child = node->GetData();
5148
5149 // If floating, ignore. We already laid out floats.
5150 // Also ignore if empty object, except if we haven't got any
5151 // size yet.
5152 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() != 0 && !wxDynamicCast(child, wxRichTextPlainText))
5153 {
5154 if (child->GetCachedSize().x > minWidth)
5155 minWidth = child->GetMinSize().x;
5156 }
5157 node = node->GetNext();
5158 }
5159
5160 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5161 contentRect = wxRect(wxPoint(0, 0), wxSize(minWidth, currentPosition.y + spaceAfterPara));
5162 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
5163 SetMinSize(marginRect.GetSize());
5164 }
5165
5166 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5167 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5168 // Use the text extents to calculate the size of each fragment in each line
5169 wxRichTextLineList::compatibility_iterator lineNode = m_cachedLines.GetFirst();
5170 while (lineNode)
5171 {
5172 wxRichTextLine* line = lineNode->GetData();
5173 wxRichTextRange lineRange = line->GetAbsoluteRange();
5174
5175 // Loop through objects until we get to the one within range
5176 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5177
5178 while (node2)
5179 {
5180 wxRichTextObject* child = node2->GetData();
5181
5182 if (child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange))
5183 {
5184 wxRichTextRange rangeToUse = lineRange;
5185 rangeToUse.LimitTo(child->GetRange());
5186
5187 // Find the size of the child from the text extents, and store in an array
5188 // for drawing later
5189 int left = 0;
5190 if (rangeToUse.GetStart() > GetRange().GetStart())
5191 left = partialExtents[(rangeToUse.GetStart()-1) - GetRange().GetStart()];
5192 int right = partialExtents[rangeToUse.GetEnd() - GetRange().GetStart()];
5193 int sz = right - left;
5194 line->GetObjectSizes().Add(sz);
5195 }
5196 else if (child->GetRange().GetStart() > lineRange.GetEnd())
5197 // Can break out of inner loop now since we've passed this line's range
5198 break;
5199
5200 node2 = node2->GetNext();
5201 }
5202
5203 lineNode = lineNode->GetNext();
5204 }
5205 #endif
5206 #endif
5207
5208 return true;
5209 }
5210
5211 /// Apply paragraph styles, such as centering, to wrapped lines
5212 /// TODO: take into account box attributes, possibly
5213 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRichTextAttr& attr, const wxRect& rect, wxDC& dc)
5214 {
5215 if (!attr.HasAlignment())
5216 return;
5217
5218 wxPoint pos = line->GetPosition();
5219 wxPoint originalPos = pos;
5220 wxSize size = line->GetSize();
5221
5222 // centering, right-justification
5223 if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
5224 {
5225 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5226 pos.x = (rect.GetWidth() - rightIndent - size.x)/2 + pos.x;
5227 line->SetPosition(pos);
5228 }
5229 else if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
5230 {
5231 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5232 pos.x = pos.x + rect.GetWidth() - size.x - rightIndent;
5233 line->SetPosition(pos);
5234 }
5235
5236 if (pos != originalPos)
5237 {
5238 wxPoint inc = pos - originalPos;
5239
5240 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5241
5242 while (node)
5243 {
5244 wxRichTextObject* child = node->GetData();
5245 if (child->IsTopLevel() && !child->GetRange().IsOutside(line->GetAbsoluteRange()))
5246 child->Move(child->GetPosition() + inc);
5247
5248 node = node->GetNext();
5249 }
5250 }
5251 }
5252
5253 /// Insert text at the given position
5254 bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
5255 {
5256 wxRichTextObject* childToUse = NULL;
5257 wxRichTextObjectList::compatibility_iterator nodeToUse = wxRichTextObjectList::compatibility_iterator();
5258
5259 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5260 while (node)
5261 {
5262 wxRichTextObject* child = node->GetData();
5263 if (child->GetRange().Contains(pos) && child->GetRange().GetLength() > 0)
5264 {
5265 childToUse = child;
5266 nodeToUse = node;
5267 break;
5268 }
5269
5270 node = node->GetNext();
5271 }
5272
5273 if (childToUse)
5274 {
5275 wxRichTextPlainText* textObject = wxDynamicCast(childToUse, wxRichTextPlainText);
5276 if (textObject)
5277 {
5278 int posInString = pos - textObject->GetRange().GetStart();
5279
5280 wxString newText = textObject->GetText().Mid(0, posInString) +
5281 text + textObject->GetText().Mid(posInString);
5282 textObject->SetText(newText);
5283
5284 int textLength = text.length();
5285
5286 textObject->SetRange(wxRichTextRange(textObject->GetRange().GetStart(),
5287 textObject->GetRange().GetEnd() + textLength));
5288
5289 // Increment the end range of subsequent fragments in this paragraph.
5290 // We'll set the paragraph range itself at a higher level.
5291
5292 wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
5293 while (node)
5294 {
5295 wxRichTextObject* child = node->GetData();
5296 child->SetRange(wxRichTextRange(textObject->GetRange().GetStart() + textLength,
5297 textObject->GetRange().GetEnd() + textLength));
5298
5299 node = node->GetNext();
5300 }
5301
5302 return true;
5303 }
5304 else
5305 {
5306 // TODO: if not a text object, insert at closest position, e.g. in front of it
5307 }
5308 }
5309 else
5310 {
5311 // Add at end.
5312 // Don't pass parent initially to suppress auto-setting of parent range.
5313 // We'll do that at a higher level.
5314 wxRichTextPlainText* textObject = new wxRichTextPlainText(text, this);
5315
5316 AppendChild(textObject);
5317 return true;
5318 }
5319
5320 return false;
5321 }
5322
5323 void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
5324 {
5325 wxRichTextCompositeObject::Copy(obj);
5326 }
5327
5328 /// Clear the cached lines
5329 void wxRichTextParagraph::ClearLines()
5330 {
5331 WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
5332 }
5333
5334 /// Get/set the object size for the given range. Returns false if the range
5335 /// is invalid for this object.
5336 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
5337 {
5338 if (!range.IsWithin(GetRange()))
5339 return false;
5340
5341 if (flags & wxRICHTEXT_UNFORMATTED)
5342 {
5343 // Just use unformatted data, assume no line breaks
5344 wxSize sz;
5345
5346 wxArrayInt childExtents;
5347 wxArrayInt* p;
5348 if (partialExtents)
5349 p = & childExtents;
5350 else
5351 p = NULL;
5352
5353 int maxDescent = 0;
5354 int maxAscent = 0;
5355 int maxLineHeight = 0;
5356
5357 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5358 while (node)
5359 {
5360 wxRichTextObject* child = node->GetData();
5361 if (!child->GetRange().IsOutside(range))
5362 {
5363 // Floating objects have a zero size within the paragraph.
5364 if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
5365 {
5366 if (partialExtents)
5367 {
5368 int lastSize;
5369 if (partialExtents->GetCount() > 0)
5370 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5371 else
5372 lastSize = 0;
5373
5374 partialExtents->Add(0 /* zero size */ + lastSize);
5375 }
5376 }
5377 else
5378 {
5379 wxSize childSize;
5380
5381 wxRichTextRange rangeToUse = range;
5382 rangeToUse.LimitTo(child->GetRange());
5383 int childDescent = 0;
5384
5385 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
5386 // but it's only going to be used after caching has taken place.
5387 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
5388 {
5389 childDescent = child->GetDescent();
5390 childSize = child->GetCachedSize();
5391
5392 if (childDescent == 0)
5393 {
5394 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5395 }
5396 else
5397 {
5398 maxDescent = wxMax(maxDescent, childDescent);
5399 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5400 }
5401
5402 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5403
5404 sz.y = wxMax(sz.y, maxLineHeight);
5405 sz.x += childSize.x;
5406 descent = maxDescent;
5407 }
5408 else if (child->IsTopLevel())
5409 {
5410 childDescent = child->GetDescent();
5411 childSize = child->GetCachedSize();
5412
5413 if (childDescent == 0)
5414 {
5415 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5416 }
5417 else
5418 {
5419 maxDescent = wxMax(maxDescent, childDescent);
5420 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5421 }
5422
5423 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5424
5425 sz.y = wxMax(sz.y, maxLineHeight);
5426 sz.x += childSize.x;
5427 descent = maxDescent;
5428
5429 // FIXME: this won't change the original values.
5430 // Should we be calling GetRangeSize above instead of using cached values?
5431 #if 0
5432 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
5433 {
5434 child->SetCachedSize(childSize);
5435 child->SetDescent(childDescent);
5436 }
5437 #endif
5438
5439 if (partialExtents)
5440 {
5441 int lastSize;
5442 if (partialExtents->GetCount() > 0)
5443 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5444 else
5445 lastSize = 0;
5446
5447 partialExtents->Add(childSize.x + lastSize);
5448 }
5449 }
5450 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize, p))
5451 {
5452 if (childDescent == 0)
5453 {
5454 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5455 }
5456 else
5457 {
5458 maxDescent = wxMax(maxDescent, childDescent);
5459 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5460 }
5461
5462 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5463
5464 sz.y = wxMax(sz.y, maxLineHeight);
5465 sz.x += childSize.x;
5466 descent = maxDescent;
5467
5468 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
5469 {
5470 child->SetCachedSize(childSize);
5471 child->SetDescent(childDescent);
5472 }
5473
5474 if (partialExtents)
5475 {
5476 int lastSize;
5477 if (partialExtents->GetCount() > 0)
5478 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5479 else
5480 lastSize = 0;
5481
5482 size_t i;
5483 for (i = 0; i < childExtents.GetCount(); i++)
5484 {
5485 partialExtents->Add(childExtents[i] + lastSize);
5486 }
5487 }
5488 }
5489 }
5490
5491 if (p)
5492 p->Clear();
5493 }
5494
5495 node = node->GetNext();
5496 }
5497 size = sz;
5498 }
5499 else
5500 {
5501 // Use formatted data, with line breaks
5502 wxSize sz;
5503
5504 // We're going to loop through each line, and then for each line,
5505 // call GetRangeSize for the fragment that comprises that line.
5506 // Only we have to do that multiple times within the line, because
5507 // the line may be broken into pieces. For now ignore line break commands
5508 // (so we can assume that getting the unformatted size for a fragment
5509 // within a line is the actual size)
5510
5511 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5512 while (node)
5513 {
5514 wxRichTextLine* line = node->GetData();
5515 wxRichTextRange lineRange = line->GetAbsoluteRange();
5516 if (!lineRange.IsOutside(range))
5517 {
5518 int maxDescent = 0;
5519 int maxAscent = 0;
5520 int maxLineHeight = 0;
5521 int maxLineWidth = 0;
5522
5523 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5524 while (node2)
5525 {
5526 wxRichTextObject* child = node2->GetData();
5527
5528 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && !child->GetRange().IsOutside(lineRange))
5529 {
5530 wxRichTextRange rangeToUse = lineRange;
5531 rangeToUse.LimitTo(child->GetRange());
5532 if (child->IsTopLevel())
5533 rangeToUse = child->GetOwnRange();
5534
5535 wxSize childSize;
5536 int childDescent = 0;
5537 if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize))
5538 {
5539 if (childDescent == 0)
5540 {
5541 // Assume that if descent is zero, this child can occupy the full line height
5542 // and does not need space for the line's maximum descent. So we influence
5543 // the overall max line height only.
5544 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5545 }
5546 else
5547 {
5548 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5549 maxDescent = wxMax(maxAscent, childDescent);
5550 }
5551 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5552 maxLineWidth += childSize.x;
5553 }
5554 }
5555
5556 node2 = node2->GetNext();
5557 }
5558
5559 descent = wxMax(descent, maxDescent);
5560
5561 // Increase size by a line (TODO: paragraph spacing)
5562 sz.y += maxLineHeight;
5563 sz.x = wxMax(sz.x, maxLineWidth);
5564 }
5565 node = node->GetNext();
5566 }
5567 size = sz;
5568 }
5569 return true;
5570 }
5571
5572 /// Finds the absolute position and row height for the given character position
5573 bool wxRichTextParagraph::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
5574 {
5575 if (index == -1)
5576 {
5577 wxRichTextLine* line = ((wxRichTextParagraphLayoutBox*)GetParent())->GetLineAtPosition(0);
5578 if (line)
5579 *height = line->GetSize().y;
5580 else
5581 *height = dc.GetCharHeight();
5582
5583 // -1 means 'the start of the buffer'.
5584 pt = GetPosition();
5585 if (line)
5586 pt = pt + line->GetPosition();
5587
5588 return true;
5589 }
5590
5591 // The final position in a paragraph is taken to mean the position
5592 // at the start of the next paragraph.
5593 if (index == GetRange().GetEnd())
5594 {
5595 wxRichTextParagraphLayoutBox* parent = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
5596 wxASSERT( parent != NULL );
5597
5598 // Find the height at the next paragraph, if any
5599 wxRichTextLine* line = parent->GetLineAtPosition(index + 1);
5600 if (line)
5601 {
5602 *height = line->GetSize().y;
5603 pt = line->GetAbsolutePosition();
5604 }
5605 else
5606 {
5607 *height = dc.GetCharHeight();
5608 int indent = ConvertTenthsMMToPixels(dc, m_attributes.GetLeftIndent());
5609 pt = wxPoint(indent, GetCachedSize().y);
5610 }
5611
5612 return true;
5613 }
5614
5615 if (index < GetRange().GetStart() || index > GetRange().GetEnd())
5616 return false;
5617
5618 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5619 while (node)
5620 {
5621 wxRichTextLine* line = node->GetData();
5622 wxRichTextRange lineRange = line->GetAbsoluteRange();
5623 if (index >= lineRange.GetStart() && index <= lineRange.GetEnd())
5624 {
5625 // If this is the last point in the line, and we're forcing the
5626 // returned value to be the start of the next line, do the required
5627 // thing.
5628 if (index == lineRange.GetEnd() && forceLineStart)
5629 {
5630 if (node->GetNext())
5631 {
5632 wxRichTextLine* nextLine = node->GetNext()->GetData();
5633 *height = nextLine->GetSize().y;
5634 pt = nextLine->GetAbsolutePosition();
5635 return true;
5636 }
5637 }
5638
5639 pt.y = line->GetPosition().y + GetPosition().y;
5640
5641 wxRichTextRange r(lineRange.GetStart(), index);
5642 wxSize rangeSize;
5643 int descent = 0;
5644
5645 // We find the size of the line up to this point,
5646 // then we can add this size to the line start position and
5647 // paragraph start position to find the actual position.
5648
5649 if (GetRangeSize(r, rangeSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, line->GetPosition()+ GetPosition()))
5650 {
5651 pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
5652 *height = line->GetSize().y;
5653
5654 return true;
5655 }
5656
5657 }
5658
5659 node = node->GetNext();
5660 }
5661
5662 return false;
5663 }
5664
5665 /// Hit-testing: returns a flag indicating hit test details, plus
5666 /// information about position
5667 int wxRichTextParagraph::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5668 {
5669 if (!IsShown())
5670 return wxRICHTEXT_HITTEST_NONE;
5671
5672 // If we're in the top-level container, then we can return
5673 // a suitable hit test code even if the point is outside the container area,
5674 // so that we can position the caret sensibly even if we don't
5675 // click on valid content. If we're not at the top-level, and the point
5676 // is not within this paragraph object, then we don't want to stop more
5677 // precise hit-testing from working prematurely, so return immediately.
5678 // NEW STRATEGY: use the parent boundary to test whether we're in the
5679 // right region, not the paragraph, since the paragraph may be positioned
5680 // some way in from where the user clicks.
5681 {
5682 long tmpPos;
5683 wxRichTextObject* tempObj, *tempContextObj;
5684 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc, context, pt, tmpPos, & tempObj, & tempContextObj, flags) == wxRICHTEXT_HITTEST_NONE)
5685 return wxRICHTEXT_HITTEST_NONE;
5686 }
5687
5688 wxRichTextObjectList::compatibility_iterator objNode = m_children.GetFirst();
5689 while (objNode)
5690 {
5691 wxRichTextObject* child = objNode->GetData();
5692 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5693 // and also, if this seems composite but actually is marked as atomic,
5694 // don't recurse.
5695 if (child->IsTopLevel() && ((flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS) == 0) &&
5696 (! (((flags & wxRICHTEXT_HITTEST_HONOUR_ATOMIC) != 0) && child->IsAtomic())))
5697 {
5698 {
5699 int hitTest = child->HitTest(dc, context, pt, textPosition, obj, contextObj);
5700 if (hitTest != wxRICHTEXT_HITTEST_NONE)
5701 return hitTest;
5702 }
5703 }
5704
5705 objNode = objNode->GetNext();
5706 }
5707
5708 wxPoint paraPos = GetPosition();
5709
5710 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5711 while (node)
5712 {
5713 wxRichTextLine* line = node->GetData();
5714 wxPoint linePos = paraPos + line->GetPosition();
5715 wxSize lineSize = line->GetSize();
5716 wxRichTextRange lineRange = line->GetAbsoluteRange();
5717
5718 if (pt.y <= linePos.y + lineSize.y)
5719 {
5720 if (pt.x < linePos.x)
5721 {
5722 textPosition = lineRange.GetStart();
5723 *obj = FindObjectAtPosition(textPosition);
5724 *contextObj = GetContainer();
5725 return wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_OUTSIDE;
5726 }
5727 else if (pt.x >= (linePos.x + lineSize.x))
5728 {
5729 textPosition = lineRange.GetEnd();
5730 *obj = FindObjectAtPosition(textPosition);
5731 *contextObj = GetContainer();
5732 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
5733 }
5734 else
5735 {
5736 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5737 wxArrayInt partialExtents;
5738
5739 wxSize paraSize;
5740 int paraDescent;
5741
5742 // This calculates the partial text extents
5743 GetRangeSize(lineRange, paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED, linePos, wxDefaultSize, & partialExtents);
5744
5745 int lastX = linePos.x;
5746 size_t i;
5747 for (i = 0; i < partialExtents.GetCount(); i++)
5748 {
5749 int nextX = partialExtents[i] + linePos.x;
5750
5751 if (pt.x >= lastX && pt.x <= nextX)
5752 {
5753 textPosition = i + lineRange.GetStart(); // minus 1?
5754
5755 *obj = FindObjectAtPosition(textPosition);
5756 *contextObj = GetContainer();
5757
5758 // So now we know it's between i-1 and i.
5759 // Let's see if we can be more precise about
5760 // which side of the position it's on.
5761
5762 int midPoint = (nextX + lastX)/2;
5763 if (pt.x >= midPoint)
5764 return wxRICHTEXT_HITTEST_AFTER;
5765 else
5766 return wxRICHTEXT_HITTEST_BEFORE;
5767 }
5768
5769 lastX = nextX;
5770 }
5771 #else
5772 long i;
5773 int lastX = linePos.x;
5774 for (i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++)
5775 {
5776 wxSize childSize;
5777 int descent = 0;
5778
5779 wxRichTextRange rangeToUse(lineRange.GetStart(), i);
5780
5781 GetRangeSize(rangeToUse, childSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, linePos);
5782
5783 int nextX = childSize.x + linePos.x;
5784
5785 if (pt.x >= lastX && pt.x <= nextX)
5786 {
5787 textPosition = i;
5788
5789 *obj = FindObjectAtPosition(textPosition);
5790 *contextObj = GetContainer();
5791
5792 // So now we know it's between i-1 and i.
5793 // Let's see if we can be more precise about
5794 // which side of the position it's on.
5795
5796 int midPoint = (nextX + lastX)/2;
5797 if (pt.x >= midPoint)
5798 return wxRICHTEXT_HITTEST_AFTER;
5799 else
5800 return wxRICHTEXT_HITTEST_BEFORE;
5801 }
5802 else
5803 {
5804 lastX = nextX;
5805 }
5806 }
5807 #endif
5808 }
5809 }
5810
5811 node = node->GetNext();
5812 }
5813
5814 return wxRICHTEXT_HITTEST_NONE;
5815 }
5816
5817 /// Split an object at this position if necessary, and return
5818 /// the previous object, or NULL if inserting at beginning.
5819 wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** previousObject)
5820 {
5821 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5822 while (node)
5823 {
5824 wxRichTextObject* child = node->GetData();
5825
5826 if (pos == child->GetRange().GetStart())
5827 {
5828 if (previousObject)
5829 {
5830 if (node->GetPrevious())
5831 *previousObject = node->GetPrevious()->GetData();
5832 else
5833 *previousObject = NULL;
5834 }
5835
5836 return child;
5837 }
5838
5839 if (child->GetRange().Contains(pos))
5840 {
5841 // This should create a new object, transferring part of
5842 // the content to the old object and the rest to the new object.
5843 wxRichTextObject* newObject = child->DoSplit(pos);
5844
5845 // If we couldn't split this object, just insert in front of it.
5846 if (!newObject)
5847 {
5848 // Maybe this is an empty string, try the next one
5849 // return child;
5850 }
5851 else
5852 {
5853 // Insert the new object after 'child'
5854 if (node->GetNext())
5855 m_children.Insert(node->GetNext(), newObject);
5856 else
5857 m_children.Append(newObject);
5858 newObject->SetParent(this);
5859
5860 if (previousObject)
5861 *previousObject = child;
5862
5863 return newObject;
5864 }
5865 }
5866
5867 node = node->GetNext();
5868 }
5869 if (previousObject)
5870 *previousObject = NULL;
5871 return NULL;
5872 }
5873
5874 /// Move content to a list from obj on
5875 void wxRichTextParagraph::MoveToList(wxRichTextObject* obj, wxList& list)
5876 {
5877 wxRichTextObjectList::compatibility_iterator node = m_children.Find(obj);
5878 while (node)
5879 {
5880 wxRichTextObject* child = node->GetData();
5881 list.Append(child);
5882
5883 wxRichTextObjectList::compatibility_iterator oldNode = node;
5884
5885 node = node->GetNext();
5886
5887 m_children.DeleteNode(oldNode);
5888 }
5889 }
5890
5891 /// Add content back from list
5892 void wxRichTextParagraph::MoveFromList(wxList& list)
5893 {
5894 for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
5895 {
5896 AppendChild((wxRichTextObject*) node->GetData());
5897 }
5898 }
5899
5900 /// Calculate range
5901 void wxRichTextParagraph::CalculateRange(long start, long& end)
5902 {
5903 wxRichTextCompositeObject::CalculateRange(start, end);
5904
5905 // Add one for end of paragraph
5906 end ++;
5907
5908 m_range.SetRange(start, end);
5909 }
5910
5911 /// Find the object at the given position
5912 wxRichTextObject* wxRichTextParagraph::FindObjectAtPosition(long position)
5913 {
5914 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5915 while (node)
5916 {
5917 wxRichTextObject* obj = node->GetData();
5918 if (obj->GetRange().Contains(position) ||
5919 obj->GetRange().GetStart() == position ||
5920 obj->GetRange().GetEnd() == position)
5921 return obj;
5922
5923 node = node->GetNext();
5924 }
5925 return NULL;
5926 }
5927
5928 /// Get the plain text searching from the start or end of the range.
5929 /// The resulting string may be shorter than the range given.
5930 bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTextRange& range, bool fromStart)
5931 {
5932 text = wxEmptyString;
5933
5934 if (fromStart)
5935 {
5936 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5937 while (node)
5938 {
5939 wxRichTextObject* obj = node->GetData();
5940 if (!obj->GetRange().IsOutside(range))
5941 {
5942 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5943 if (textObj)
5944 {
5945 text += textObj->GetTextForRange(range);
5946 }
5947 else
5948 {
5949 text += wxT(" ");
5950 }
5951 }
5952
5953 node = node->GetNext();
5954 }
5955 }
5956 else
5957 {
5958 wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
5959 while (node)
5960 {
5961 wxRichTextObject* obj = node->GetData();
5962 if (!obj->GetRange().IsOutside(range))
5963 {
5964 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5965 if (textObj)
5966 {
5967 text = textObj->GetTextForRange(range) + text;
5968 }
5969 else
5970 {
5971 text = wxT(" ") + text;
5972 }
5973 }
5974
5975 node = node->GetPrevious();
5976 }
5977 }
5978
5979 return true;
5980 }
5981
5982 /// Find a suitable wrap position.
5983 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, wxRichTextDrawingContext& context, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents)
5984 {
5985 if (range.GetLength() <= 0)
5986 return false;
5987
5988 // Find the first position where the line exceeds the available space.
5989 wxSize sz;
5990 long breakPosition = range.GetEnd();
5991
5992 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5993 if (partialExtents && partialExtents->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5994 {
5995 int widthBefore;
5996
5997 if (range.GetStart() > GetRange().GetStart())
5998 widthBefore = (*partialExtents)[range.GetStart() - GetRange().GetStart() - 1];
5999 else
6000 widthBefore = 0;
6001
6002 size_t i;
6003 for (i = (size_t) range.GetStart(); i <= (size_t) range.GetEnd(); i++)
6004 {
6005 int widthFromStartOfThisRange = (*partialExtents)[i - GetRange().GetStart()] - widthBefore;
6006
6007 if (widthFromStartOfThisRange > availableSpace)
6008 {
6009 breakPosition = i-1;
6010 break;
6011 }
6012 }
6013 }
6014 else
6015 #endif
6016 {
6017 // Binary chop for speed
6018 long minPos = range.GetStart();
6019 long maxPos = range.GetEnd();
6020 while (true)
6021 {
6022 if (minPos == maxPos)
6023 {
6024 int descent = 0;
6025 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
6026
6027 if (sz.x > availableSpace)
6028 breakPosition = minPos - 1;
6029 break;
6030 }
6031 else if ((maxPos - minPos) == 1)
6032 {
6033 int descent = 0;
6034 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
6035
6036 if (sz.x > availableSpace)
6037 breakPosition = minPos - 1;
6038 else
6039 {
6040 GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
6041 if (sz.x > availableSpace)
6042 breakPosition = maxPos-1;
6043 }
6044 break;
6045 }
6046 else
6047 {
6048 long nextPos = minPos + ((maxPos - minPos) / 2);
6049
6050 int descent = 0;
6051 GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
6052
6053 if (sz.x > availableSpace)
6054 {
6055 maxPos = nextPos;
6056 }
6057 else
6058 {
6059 minPos = nextPos;
6060 }
6061 }
6062 }
6063 }
6064
6065 // Now we know the last position on the line.
6066 // Let's try to find a word break.
6067
6068 wxString plainText;
6069 if (GetContiguousPlainText(plainText, wxRichTextRange(range.GetStart(), breakPosition), false))
6070 {
6071 int newLinePos = plainText.Find(wxRichTextLineBreakChar);
6072 if (newLinePos != wxNOT_FOUND)
6073 {
6074 breakPosition = wxMax(0, range.GetStart() + newLinePos);
6075 }
6076 else
6077 {
6078 int spacePos = plainText.Find(wxT(' '), true);
6079 int tabPos = plainText.Find(wxT('\t'), true);
6080 int pos = wxMax(spacePos, tabPos);
6081 if (pos != wxNOT_FOUND)
6082 {
6083 int positionsFromEndOfString = plainText.length() - pos - 1;
6084 breakPosition = breakPosition - positionsFromEndOfString;
6085 }
6086 }
6087 }
6088
6089 wrapPosition = breakPosition;
6090
6091 return true;
6092 }
6093
6094 /// Get the bullet text for this paragraph.
6095 wxString wxRichTextParagraph::GetBulletText()
6096 {
6097 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ||
6098 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP))
6099 return wxEmptyString;
6100
6101 int number = GetAttributes().GetBulletNumber();
6102
6103 wxString text;
6104 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE))
6105 {
6106 text.Printf(wxT("%d"), number);
6107 }
6108 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER)
6109 {
6110 // TODO: Unicode, and also check if number > 26
6111 text.Printf(wxT("%c"), (wxChar) (number+64));
6112 }
6113 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER)
6114 {
6115 // TODO: Unicode, and also check if number > 26
6116 text.Printf(wxT("%c"), (wxChar) (number+96));
6117 }
6118 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
6119 {
6120 text = wxRichTextDecimalToRoman(number);
6121 }
6122 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
6123 {
6124 text = wxRichTextDecimalToRoman(number);
6125 text.MakeLower();
6126 }
6127 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
6128 {
6129 text = GetAttributes().GetBulletText();
6130 }
6131
6132 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
6133 {
6134 // The outline style relies on the text being computed statically,
6135 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
6136 // should be stored in the attributes; if not, just use the number for this
6137 // level, as previously computed.
6138 if (!GetAttributes().GetBulletText().IsEmpty())
6139 text = GetAttributes().GetBulletText();
6140 }
6141
6142 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES)
6143 {
6144 text = wxT("(") + text + wxT(")");
6145 }
6146 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS)
6147 {
6148 text = text + wxT(")");
6149 }
6150
6151 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD)
6152 {
6153 text += wxT(".");
6154 }
6155
6156 return text;
6157 }
6158
6159 /// Allocate or reuse a line object
6160 wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
6161 {
6162 if (pos < (int) m_cachedLines.GetCount())
6163 {
6164 wxRichTextLine* line = m_cachedLines.Item(pos)->GetData();
6165 line->Init(this);
6166 return line;
6167 }
6168 else
6169 {
6170 wxRichTextLine* line = new wxRichTextLine(this);
6171 m_cachedLines.Append(line);
6172 return line;
6173 }
6174 }
6175
6176 /// Clear remaining unused line objects, if any
6177 bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
6178 {
6179 int cachedLineCount = m_cachedLines.GetCount();
6180 if ((int) cachedLineCount > lineCount)
6181 {
6182 for (int i = 0; i < (int) (cachedLineCount - lineCount); i ++)
6183 {
6184 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetLast();
6185 wxRichTextLine* line = node->GetData();
6186 m_cachedLines.Erase(node);
6187 delete line;
6188 }
6189 }
6190 return true;
6191 }
6192
6193 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6194 /// retrieve the actual style.
6195 wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr& contentStyle, bool includingBoxAttr) const
6196 {
6197 wxRichTextAttr attr;
6198 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
6199 if (buf)
6200 {
6201 attr = buf->GetBasicStyle();
6202 if (!includingBoxAttr)
6203 {
6204 attr.GetTextBoxAttr().Reset();
6205 // The background colour will be painted by the container, and we don't
6206 // want to unnecessarily overwrite the background when we're drawing text
6207 // because this may erase the guideline (which appears just under the text
6208 // if there's no padding).
6209 attr.SetFlags(attr.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR);
6210 }
6211 wxRichTextApplyStyle(attr, GetAttributes());
6212 }
6213 else
6214 attr = GetAttributes();
6215
6216 wxRichTextApplyStyle(attr, contentStyle);
6217 return attr;
6218 }
6219
6220 /// Get combined attributes of the base style and paragraph style.
6221 wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr) const
6222 {
6223 wxRichTextAttr attr;
6224 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
6225 if (buf)
6226 {
6227 attr = buf->GetBasicStyle();
6228 if (!includingBoxAttr)
6229 attr.GetTextBoxAttr().Reset();
6230 wxRichTextApplyStyle(attr, GetAttributes());
6231 }
6232 else
6233 attr = GetAttributes();
6234
6235 return attr;
6236 }
6237
6238 // Create default tabstop array
6239 void wxRichTextParagraph::InitDefaultTabs()
6240 {
6241 // create a default tab list at 10 mm each.
6242 for (int i = 0; i < 20; ++i)
6243 {
6244 sm_defaultTabs.Add(i*100);
6245 }
6246 }
6247
6248 // Clear default tabstop array
6249 void wxRichTextParagraph::ClearDefaultTabs()
6250 {
6251 sm_defaultTabs.Clear();
6252 }
6253
6254 void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style, wxRichTextFloatCollector* floatCollector)
6255 {
6256 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
6257 while (node)
6258 {
6259 wxRichTextObject* anchored = node->GetData();
6260 if (anchored && anchored->IsFloating() && !floatCollector->HasFloat(anchored))
6261 {
6262 int x = 0;
6263 wxRichTextAttr parentAttr(GetAttributes());
6264 context.ApplyVirtualAttributes(parentAttr, this);
6265 #if 1
6266 // 27-09-2012
6267 wxRect availableSpace = GetParent()->GetAvailableContentArea(dc, context, rect);
6268
6269 anchored->LayoutToBestSize(dc, context, GetBuffer(),
6270 parentAttr, anchored->GetAttributes(),
6271 parentRect, availableSpace,
6272 style);
6273 wxSize size = anchored->GetCachedSize();
6274 #else
6275 wxSize size;
6276 int descent = 0;
6277 anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, context, style);
6278 #endif
6279
6280 int offsetY = 0;
6281 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6282 {
6283 offsetY = anchored->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6284 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6285 {
6286 offsetY = ConvertTenthsMMToPixels(dc, offsetY);
6287 }
6288 }
6289
6290 int pos = floatCollector->GetFitPosition(anchored->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect.y + offsetY, size.y);
6291
6292 /* Update the offset */
6293 int newOffsetY = pos - rect.y;
6294 if (newOffsetY != offsetY)
6295 {
6296 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6297 newOffsetY = ConvertPixelsToTenthsMM(dc, newOffsetY);
6298 anchored->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY);
6299 }
6300
6301 if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
6302 x = rect.x;
6303 else if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
6304 x = rect.x + rect.width - size.x;
6305
6306 //anchored->SetPosition(wxPoint(x, pos));
6307 anchored->Move(wxPoint(x, pos)); // should move children
6308 anchored->SetCachedSize(size);
6309 floatCollector->CollectFloat(this, anchored);
6310 }
6311
6312 node = node->GetNext();
6313 }
6314 }
6315
6316 // Get the first position from pos that has a line break character.
6317 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos)
6318 {
6319 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
6320 while (node)
6321 {
6322 wxRichTextObject* obj = node->GetData();
6323 if (pos >= obj->GetRange().GetStart() && pos <= obj->GetRange().GetEnd())
6324 {
6325 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
6326 if (textObj)
6327 {
6328 long breakPos = textObj->GetFirstLineBreakPosition(pos);
6329 if (breakPos > -1)
6330 return breakPos;
6331 }
6332 }
6333 node = node->GetNext();
6334 }
6335 return -1;
6336 }
6337
6338 /*!
6339 * wxRichTextLine
6340 * This object represents a line in a paragraph, and stores
6341 * offsets from the start of the paragraph representing the
6342 * start and end positions of the line.
6343 */
6344
6345 wxRichTextLine::wxRichTextLine(wxRichTextParagraph* parent)
6346 {
6347 Init(parent);
6348 }
6349
6350 /// Initialisation
6351 void wxRichTextLine::Init(wxRichTextParagraph* parent)
6352 {
6353 m_parent = parent;
6354 m_range.SetRange(-1, -1);
6355 m_pos = wxPoint(0, 0);
6356 m_size = wxSize(0, 0);
6357 m_descent = 0;
6358 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6359 m_objectSizes.Clear();
6360 #endif
6361 }
6362
6363 /// Copy
6364 void wxRichTextLine::Copy(const wxRichTextLine& obj)
6365 {
6366 m_range = obj.m_range;
6367 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6368 m_objectSizes = obj.m_objectSizes;
6369 #endif
6370 }
6371
6372 /// Get the absolute object position
6373 wxPoint wxRichTextLine::GetAbsolutePosition() const
6374 {
6375 return m_parent->GetPosition() + m_pos;
6376 }
6377
6378 /// Get the absolute range
6379 wxRichTextRange wxRichTextLine::GetAbsoluteRange() const
6380 {
6381 wxRichTextRange range(m_range.GetStart() + m_parent->GetRange().GetStart(), 0);
6382 range.SetEnd(range.GetStart() + m_range.GetLength()-1);
6383 return range;
6384 }
6385
6386 /*!
6387 * wxRichTextPlainText
6388 * This object represents a single piece of text.
6389 */
6390
6391 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText, wxRichTextObject)
6392
6393 wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* style):
6394 wxRichTextObject(parent)
6395 {
6396 if (style)
6397 SetAttributes(*style);
6398
6399 m_text = text;
6400 }
6401
6402 #define USE_KERNING_FIX 1
6403
6404 // If insufficient tabs are defined, this is the tab width used
6405 #define WIDTH_FOR_DEFAULT_TABS 50
6406
6407 /// Draw the item
6408 bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
6409 {
6410 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6411 wxASSERT (para != NULL);
6412
6413 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6414 context.ApplyVirtualAttributes(textAttr, this);
6415
6416 // Let's make the assumption for now that for content in a paragraph, including
6417 // text, we never have a discontinuous selection. So we only deal with a
6418 // single range.
6419 wxRichTextRange selectionRange;
6420 if (selection.IsValid())
6421 {
6422 wxRichTextRangeArray selectionRanges = selection.GetSelectionForObject(this);
6423 if (selectionRanges.GetCount() > 0)
6424 selectionRange = selectionRanges[0];
6425 else
6426 selectionRange = wxRICHTEXT_NO_SELECTION;
6427 }
6428 else
6429 selectionRange = wxRICHTEXT_NO_SELECTION;
6430
6431 int offset = GetRange().GetStart();
6432
6433 wxString str = m_text;
6434 if (context.HasVirtualText(this))
6435 {
6436 if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
6437 str = m_text;
6438 }
6439
6440 // Replace line break characters with spaces
6441 wxString toRemove = wxRichTextLineBreakChar;
6442 str.Replace(toRemove, wxT(" "));
6443 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
6444 str.MakeUpper();
6445
6446 long len = range.GetLength();
6447 wxString stringChunk = str.Mid(range.GetStart() - offset, (size_t) len);
6448
6449 // Test for the optimized situations where all is selected, or none
6450 // is selected.
6451
6452 wxFont textFont(GetBuffer()->GetFontTable().FindFont(textAttr));
6453 wxCheckSetFont(dc, textFont);
6454 int charHeight = dc.GetCharHeight();
6455
6456 int x, y;
6457 if ( textFont.IsOk() )
6458 {
6459 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
6460 {
6461 textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
6462 wxCheckSetFont(dc, textFont);
6463 charHeight = dc.GetCharHeight();
6464 }
6465
6466 if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT) )
6467 {
6468 if (textFont.IsUsingSizeInPixels())
6469 {
6470 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6471 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6472 x = rect.x;
6473 y = rect.y;
6474 }
6475 else
6476 {
6477 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6478 textFont.SetPointSize(static_cast<int>(size));
6479 x = rect.x;
6480 y = rect.y;
6481 }
6482 wxCheckSetFont(dc, textFont);
6483 }
6484 else if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) )
6485 {
6486 if (textFont.IsUsingSizeInPixels())
6487 {
6488 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6489 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6490 x = rect.x;
6491 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
6492 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6493 }
6494 else
6495 {
6496 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6497 textFont.SetPointSize(static_cast<int>(size));
6498 x = rect.x;
6499 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
6500 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6501 }
6502 wxCheckSetFont(dc, textFont);
6503 }
6504 else
6505 {
6506 x = rect.x;
6507 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6508 }
6509 }
6510 else
6511 {
6512 x = rect.x;
6513 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6514 }
6515
6516 // TODO: new selection code
6517
6518 // (a) All selected.
6519 if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd())
6520 {
6521 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, true);
6522 }
6523 // (b) None selected.
6524 else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd())
6525 {
6526 // Draw all unselected
6527 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, false);
6528 }
6529 else
6530 {
6531 // (c) Part selected, part not
6532 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6533
6534 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
6535
6536 // 1. Initial unselected chunk, if any, up until start of selection.
6537 if (selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd())
6538 {
6539 int r1 = range.GetStart();
6540 int s1 = selectionRange.GetStart()-1;
6541 int fragmentLen = s1 - r1 + 1;
6542 if (fragmentLen < 0)
6543 {
6544 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 - offset), (int)fragmentLen);
6545 }
6546 wxString stringFragment = str.Mid(r1 - offset, fragmentLen);
6547
6548 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
6549
6550 #if USE_KERNING_FIX
6551 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6552 {
6553 // Compensate for kerning difference
6554 wxString stringFragment2(str.Mid(r1 - offset, fragmentLen+1));
6555 wxString stringFragment3(str.Mid(r1 - offset + fragmentLen, 1));
6556
6557 wxCoord w1, h1, w2, h2, w3, h3;
6558 dc.GetTextExtent(stringFragment, & w1, & h1);
6559 dc.GetTextExtent(stringFragment2, & w2, & h2);
6560 dc.GetTextExtent(stringFragment3, & w3, & h3);
6561
6562 int kerningDiff = (w1 + w3) - w2;
6563 x = x - kerningDiff;
6564 }
6565 #endif
6566 }
6567
6568 // 2. Selected chunk, if any.
6569 if (selectionRange.GetEnd() >= range.GetStart())
6570 {
6571 int s1 = wxMax(selectionRange.GetStart(), range.GetStart());
6572 int s2 = wxMin(selectionRange.GetEnd(), range.GetEnd());
6573
6574 int fragmentLen = s2 - s1 + 1;
6575 if (fragmentLen < 0)
6576 {
6577 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 - offset), (int)fragmentLen);
6578 }
6579 wxString stringFragment = str.Mid(s1 - offset, fragmentLen);
6580
6581 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, true);
6582
6583 #if USE_KERNING_FIX
6584 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6585 {
6586 // Compensate for kerning difference
6587 wxString stringFragment2(str.Mid(s1 - offset, fragmentLen+1));
6588 wxString stringFragment3(str.Mid(s1 - offset + fragmentLen, 1));
6589
6590 wxCoord w1, h1, w2, h2, w3, h3;
6591 dc.GetTextExtent(stringFragment, & w1, & h1);
6592 dc.GetTextExtent(stringFragment2, & w2, & h2);
6593 dc.GetTextExtent(stringFragment3, & w3, & h3);
6594
6595 int kerningDiff = (w1 + w3) - w2;
6596 x = x - kerningDiff;
6597 }
6598 #endif
6599 }
6600
6601 // 3. Remaining unselected chunk, if any
6602 if (selectionRange.GetEnd() < range.GetEnd())
6603 {
6604 int s2 = wxMin(selectionRange.GetEnd()+1, range.GetEnd());
6605 int r2 = range.GetEnd();
6606
6607 int fragmentLen = r2 - s2 + 1;
6608 if (fragmentLen < 0)
6609 {
6610 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 - offset), (int)fragmentLen);
6611 }
6612 wxString stringFragment = str.Mid(s2 - offset, fragmentLen);
6613
6614 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
6615 }
6616 }
6617
6618 return true;
6619 }
6620
6621 bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect,wxString& str, wxCoord& x, wxCoord& y, bool selected)
6622 {
6623 bool hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6624
6625 wxArrayInt tabArray;
6626 int tabCount;
6627 if (hasTabs)
6628 {
6629 if (attr.GetTabs().IsEmpty())
6630 tabArray = wxRichTextParagraph::GetDefaultTabs();
6631 else
6632 tabArray = attr.GetTabs();
6633 tabCount = tabArray.GetCount();
6634
6635 for (int i = 0; i < tabCount; ++i)
6636 {
6637 int pos = tabArray[i];
6638 pos = ConvertTenthsMMToPixels(dc, pos);
6639 tabArray[i] = pos;
6640 }
6641 }
6642 else
6643 tabCount = 0;
6644
6645 int nextTabPos = -1;
6646 int tabPos = -1;
6647 wxCoord w, h;
6648
6649 if (selected)
6650 {
6651 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
6652 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
6653
6654 wxCheckSetBrush(dc, wxBrush(highlightColour));
6655 wxCheckSetPen(dc, wxPen(highlightColour));
6656 dc.SetTextForeground(highlightTextColour);
6657 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
6658 }
6659 else
6660 {
6661 dc.SetTextForeground(attr.GetTextColour());
6662
6663 if (attr.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR) && attr.GetBackgroundColour().IsOk())
6664 {
6665 dc.SetBackgroundMode(wxBRUSHSTYLE_SOLID);
6666 dc.SetTextBackground(attr.GetBackgroundColour());
6667 }
6668 else
6669 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
6670 }
6671
6672 wxCoord x_orig = GetParent()->GetPosition().x;
6673 while (hasTabs)
6674 {
6675 // the string has a tab
6676 // break up the string at the Tab
6677 wxString stringChunk = str.BeforeFirst(wxT('\t'));
6678 str = str.AfterFirst(wxT('\t'));
6679 dc.GetTextExtent(stringChunk, & w, & h);
6680 tabPos = x + w;
6681 bool not_found = true;
6682 for (int i = 0; i < tabCount && not_found; ++i)
6683 {
6684 nextTabPos = tabArray.Item(i) + x_orig;
6685
6686 // Find the next tab position.
6687 // Even if we're at the end of the tab array, we must still draw the chunk.
6688
6689 if (nextTabPos > tabPos || (i == (tabCount - 1)))
6690 {
6691 if (nextTabPos <= tabPos)
6692 {
6693 int defaultTabWidth = ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6694 nextTabPos = tabPos + defaultTabWidth;
6695 }
6696
6697 not_found = false;
6698 if (selected)
6699 {
6700 w = nextTabPos - x;
6701 wxRect selRect(x, rect.y, w, rect.GetHeight());
6702 dc.DrawRectangle(selRect);
6703 }
6704 dc.DrawText(stringChunk, x, y);
6705
6706 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6707 {
6708 wxPen oldPen = dc.GetPen();
6709 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
6710 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
6711 wxCheckSetPen(dc, oldPen);
6712 }
6713
6714 x = nextTabPos;
6715 }
6716 }
6717 hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6718 }
6719
6720 if (!str.IsEmpty())
6721 {
6722 dc.GetTextExtent(str, & w, & h);
6723 if (selected)
6724 {
6725 wxRect selRect(x, rect.y, w, rect.GetHeight());
6726 dc.DrawRectangle(selRect);
6727 }
6728 dc.DrawText(str, x, y);
6729
6730 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6731 {
6732 wxPen oldPen = dc.GetPen();
6733 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
6734 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
6735 wxCheckSetPen(dc, oldPen);
6736 }
6737
6738 x += w;
6739 }
6740
6741 return true;
6742 }
6743
6744 /// Lay the item out
6745 bool wxRichTextPlainText::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
6746 {
6747 // Only lay out if we haven't already cached the size
6748 if (m_size.x == -1)
6749 GetRangeSize(GetRange(), m_size, m_descent, dc, context, 0, wxPoint(0, 0));
6750 m_maxSize = m_size;
6751 // Eventually we want to have a reasonable estimate of minimum size.
6752 m_minSize = wxSize(0, 0);
6753 return true;
6754 }
6755
6756 /// Copy
6757 void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj)
6758 {
6759 wxRichTextObject::Copy(obj);
6760
6761 m_text = obj.m_text;
6762 }
6763
6764 /// Get/set the object size for the given range. Returns false if the range
6765 /// is invalid for this object.
6766 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
6767 {
6768 if (!range.IsWithin(GetRange()))
6769 return false;
6770
6771 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6772 wxASSERT (para != NULL);
6773
6774 int relativeX = position.x - GetParent()->GetPosition().x;
6775
6776 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6777 context.ApplyVirtualAttributes(textAttr, (wxRichTextObject*) this);
6778
6779 // Always assume unformatted text, since at this level we have no knowledge
6780 // of line breaks - and we don't need it, since we'll calculate size within
6781 // formatted text by doing it in chunks according to the line ranges
6782
6783 bool bScript(false);
6784 wxFont font(GetBuffer()->GetFontTable().FindFont(textAttr));
6785 if (font.IsOk())
6786 {
6787 if ( textAttr.HasTextEffects() && ( (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT)
6788 || (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) )
6789 {
6790 wxFont textFont = font;
6791 if (textFont.IsUsingSizeInPixels())
6792 {
6793 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6794 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6795 }
6796 else
6797 {
6798 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6799 textFont.SetPointSize(static_cast<int>(size));
6800 }
6801 wxCheckSetFont(dc, textFont);
6802 bScript = true;
6803 }
6804 else if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
6805 {
6806 wxFont textFont = font;
6807 textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
6808 wxCheckSetFont(dc, textFont);
6809 bScript = true;
6810 }
6811 else
6812 {
6813 wxCheckSetFont(dc, font);
6814 }
6815 }
6816
6817 bool haveDescent = false;
6818 int startPos = range.GetStart() - GetRange().GetStart();
6819 long len = range.GetLength();
6820
6821 wxString str(m_text);
6822 if (context.HasVirtualText(this))
6823 {
6824 if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
6825 str = m_text;
6826 }
6827
6828 wxString toReplace = wxRichTextLineBreakChar;
6829 str.Replace(toReplace, wxT(" "));
6830
6831 wxString stringChunk = str.Mid(startPos, (size_t) len);
6832
6833 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
6834 stringChunk.MakeUpper();
6835
6836 wxCoord w, h;
6837 int width = 0;
6838 if (stringChunk.Find(wxT('\t')) != wxNOT_FOUND)
6839 {
6840 // the string has a tab
6841 wxArrayInt tabArray;
6842 if (textAttr.GetTabs().IsEmpty())
6843 tabArray = wxRichTextParagraph::GetDefaultTabs();
6844 else
6845 tabArray = textAttr.GetTabs();
6846
6847 int tabCount = tabArray.GetCount();
6848
6849 for (int i = 0; i < tabCount; ++i)
6850 {
6851 int pos = tabArray[i];
6852 pos = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, pos);
6853 tabArray[i] = pos;
6854 }
6855
6856 int nextTabPos = -1;
6857
6858 while (stringChunk.Find(wxT('\t')) >= 0)
6859 {
6860 int absoluteWidth = 0;
6861
6862 // the string has a tab
6863 // break up the string at the Tab
6864 wxString stringFragment = stringChunk.BeforeFirst(wxT('\t'));
6865 stringChunk = stringChunk.AfterFirst(wxT('\t'));
6866
6867 if (partialExtents)
6868 {
6869 int oldWidth;
6870 if (partialExtents->GetCount() > 0)
6871 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6872 else
6873 oldWidth = 0;
6874
6875 // Add these partial extents
6876 wxArrayInt p;
6877 dc.GetPartialTextExtents(stringFragment, p);
6878 size_t j;
6879 for (j = 0; j < p.GetCount(); j++)
6880 partialExtents->Add(oldWidth + p[j]);
6881
6882 if (partialExtents->GetCount() > 0)
6883 absoluteWidth = (*partialExtents)[(*partialExtents).GetCount()-1] + relativeX;
6884 else
6885 absoluteWidth = relativeX;
6886 }
6887 else
6888 {
6889 dc.GetTextExtent(stringFragment, & w, & h);
6890 width += w;
6891 absoluteWidth = width + relativeX;
6892 haveDescent = true;
6893 }
6894
6895 bool notFound = true;
6896 for (int i = 0; i < tabCount && notFound; ++i)
6897 {
6898 nextTabPos = tabArray.Item(i);
6899
6900 // Find the next tab position.
6901 // Even if we're at the end of the tab array, we must still process the chunk.
6902
6903 if (nextTabPos > absoluteWidth || (i == (tabCount - 1)))
6904 {
6905 if (nextTabPos <= absoluteWidth)
6906 {
6907 int defaultTabWidth = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6908 nextTabPos = absoluteWidth + defaultTabWidth;
6909 }
6910
6911 notFound = false;
6912 width = nextTabPos - relativeX;
6913
6914 if (partialExtents)
6915 partialExtents->Add(width);
6916 }
6917 }
6918 }
6919 }
6920
6921 if (!stringChunk.IsEmpty())
6922 {
6923 if (partialExtents)
6924 {
6925 int oldWidth;
6926 if (partialExtents->GetCount() > 0)
6927 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6928 else
6929 oldWidth = 0;
6930
6931 // Add these partial extents
6932 wxArrayInt p;
6933 dc.GetPartialTextExtents(stringChunk, p);
6934 size_t j;
6935 for (j = 0; j < p.GetCount(); j++)
6936 partialExtents->Add(oldWidth + p[j]);
6937 }
6938 else
6939 {
6940 dc.GetTextExtent(stringChunk, & w, & h, & descent);
6941 width += w;
6942 haveDescent = true;
6943 }
6944 }
6945
6946 if (partialExtents)
6947 {
6948 int charHeight = dc.GetCharHeight();
6949 if ((*partialExtents).GetCount() > 0)
6950 w = (*partialExtents)[partialExtents->GetCount()-1];
6951 else
6952 w = 0;
6953 size = wxSize(w, charHeight);
6954 }
6955 else
6956 {
6957 size = wxSize(width, dc.GetCharHeight());
6958 }
6959
6960 if (!haveDescent)
6961 dc.GetTextExtent(wxT("X"), & w, & h, & descent);
6962
6963 if ( bScript )
6964 dc.SetFont(font);
6965
6966 return true;
6967 }
6968
6969 /// Do a split, returning an object containing the second part, and setting
6970 /// the first part in 'this'.
6971 wxRichTextObject* wxRichTextPlainText::DoSplit(long pos)
6972 {
6973 long index = pos - GetRange().GetStart();
6974
6975 if (index < 0 || index >= (int) m_text.length())
6976 return NULL;
6977
6978 wxString firstPart = m_text.Mid(0, index);
6979 wxString secondPart = m_text.Mid(index);
6980
6981 m_text = firstPart;
6982
6983 wxRichTextPlainText* newObject = new wxRichTextPlainText(secondPart);
6984 newObject->SetAttributes(GetAttributes());
6985 newObject->SetProperties(GetProperties());
6986
6987 newObject->SetRange(wxRichTextRange(pos, GetRange().GetEnd()));
6988 GetRange().SetEnd(pos-1);
6989
6990 return newObject;
6991 }
6992
6993 /// Calculate range
6994 void wxRichTextPlainText::CalculateRange(long start, long& end)
6995 {
6996 end = start + m_text.length() - 1;
6997 m_range.SetRange(start, end);
6998 }
6999
7000 /// Delete range
7001 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange& range)
7002 {
7003 wxRichTextRange r = range;
7004
7005 r.LimitTo(GetRange());
7006
7007 if (r.GetStart() == GetRange().GetStart() && r.GetEnd() == GetRange().GetEnd())
7008 {
7009 m_text.Empty();
7010 return true;
7011 }
7012
7013 long startIndex = r.GetStart() - GetRange().GetStart();
7014 long len = r.GetLength();
7015
7016 m_text = m_text.Mid(0, startIndex) + m_text.Mid(startIndex+len);
7017 return true;
7018 }
7019
7020 /// Get text for the given range.
7021 wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) const
7022 {
7023 wxRichTextRange r = range;
7024
7025 r.LimitTo(GetRange());
7026
7027 long startIndex = r.GetStart() - GetRange().GetStart();
7028 long len = r.GetLength();
7029
7030 return m_text.Mid(startIndex, len);
7031 }
7032
7033 /// Returns true if this object can merge itself with the given one.
7034 bool wxRichTextPlainText::CanMerge(wxRichTextObject* object, wxRichTextDrawingContext& context) const
7035 {
7036 // JACS 2013-01-27
7037 if (!context.GetVirtualAttributesEnabled())
7038 {
7039 return object->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText) &&
7040 (m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties()));
7041 }
7042 else
7043 {
7044 wxRichTextPlainText* otherObj = wxDynamicCast(object, wxRichTextPlainText);
7045 if (!otherObj || m_text.empty())
7046 return false;
7047
7048 if (!wxTextAttrEq(GetAttributes(), object->GetAttributes()) || !(m_properties == object->GetProperties()))
7049 return false;
7050
7051 // Check if differing virtual attributes makes it impossible to merge
7052 // these strings.
7053
7054 bool hasVirtualAttr1 = context.HasVirtualAttributes((wxRichTextObject*) this);
7055 bool hasVirtualAttr2 = context.HasVirtualAttributes((wxRichTextObject*) object);
7056 if (!hasVirtualAttr1 && !hasVirtualAttr2)
7057 return true;
7058 else if (hasVirtualAttr1 != hasVirtualAttr2)
7059 return false;
7060 else
7061 {
7062 wxRichTextAttr virtualAttr1 = context.GetVirtualAttributes((wxRichTextObject*) this);
7063 wxRichTextAttr virtualAttr2 = context.GetVirtualAttributes((wxRichTextObject*) object);
7064 return virtualAttr1 == virtualAttr2;
7065 }
7066 }
7067 }
7068
7069 /// Returns true if this object merged itself with the given one.
7070 /// The calling code will then delete the given object.
7071 bool wxRichTextPlainText::Merge(wxRichTextObject* object, wxRichTextDrawingContext& WXUNUSED(context))
7072 {
7073 wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText);
7074 wxASSERT( textObject != NULL );
7075
7076 if (textObject)
7077 {
7078 m_text += textObject->GetText();
7079 wxRichTextApplyStyle(m_attributes, textObject->GetAttributes());
7080 return true;
7081 }
7082 else
7083 return false;
7084 }
7085
7086 bool wxRichTextPlainText::CanSplit(wxRichTextDrawingContext& context) const
7087 {
7088 // If this object has any virtual attributes at all, whether for the whole object
7089 // or individual ones, we should try splitting it by calling Split.
7090 // Must be more than one character in order to be able to split.
7091 return m_text.Length() > 1 && context.HasVirtualAttributes((wxRichTextObject*) this);
7092 }
7093
7094 wxRichTextObject* wxRichTextPlainText::Split(wxRichTextDrawingContext& context)
7095 {
7096 int count = context.GetVirtualSubobjectAttributesCount(this);
7097 if (count > 0 && GetParent())
7098 {
7099 wxRichTextCompositeObject* parent = wxDynamicCast(GetParent(), wxRichTextCompositeObject);
7100 wxRichTextObjectList::compatibility_iterator node = parent->GetChildren().Find(this);
7101 if (node)
7102 {
7103 const wxRichTextAttr emptyAttr;
7104 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7105
7106 wxArrayInt positions;
7107 wxRichTextAttrArray attributes;
7108 if (context.GetVirtualSubobjectAttributes(this, positions, attributes) && positions.GetCount() > 0)
7109 {
7110 wxASSERT(positions.GetCount() == attributes.GetCount());
7111
7112 // We will gather up runs of text with the same virtual attributes
7113
7114 int len = m_text.Length();
7115 int i = 0;
7116
7117 // runStart and runEnd represent the accumulated run with a consistent attribute
7118 // that hasn't yet been appended
7119 int runStart = -1;
7120 int runEnd = -1;
7121 wxRichTextAttr currentAttr;
7122 wxString text = m_text;
7123 wxRichTextPlainText* lastPlainText = this;
7124
7125 for (i = 0; i < (int) positions.GetCount(); i++)
7126 {
7127 int pos = positions[i];
7128 wxASSERT(pos >= 0 && pos < len);
7129 if (pos >= 0 && pos < len)
7130 {
7131 const wxRichTextAttr& attr = attributes[i];
7132
7133 if (pos == 0)
7134 {
7135 runStart = 0;
7136 currentAttr = attr;
7137 }
7138 // Check if there was a gap from the last known attribute and this.
7139 // In that case, we need to do something with the span of non-attributed text.
7140 else if ((pos-1) > runEnd)
7141 {
7142 if (runEnd == -1)
7143 {
7144 // We hadn't processed anything previously, so the previous run is from the text start
7145 // to just before this position. The current attribute remains empty.
7146 runStart = 0;
7147 runEnd = pos-1;
7148 }
7149 else
7150 {
7151 // If the previous attribute matches the gap's attribute (i.e., no attributes)
7152 // then just extend the run.
7153 if (currentAttr.IsDefault())
7154 {
7155 runEnd = pos-1;
7156 }
7157 else
7158 {
7159 // We need to add an object, or reuse the existing one.
7160 if (runStart == 0)
7161 {
7162 lastPlainText = this;
7163 SetText(text.Mid(runStart, runEnd - runStart + 1));
7164 }
7165 else
7166 {
7167 wxRichTextPlainText* obj = new wxRichTextPlainText;
7168 lastPlainText = obj;
7169 obj->SetAttributes(GetAttributes());
7170 obj->SetProperties(GetProperties());
7171 obj->SetParent(parent);
7172
7173 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7174 if (next)
7175 parent->GetChildren().Insert(next, obj);
7176 else
7177 parent->GetChildren().Append(obj);
7178 }
7179
7180 runStart = runEnd+1;
7181 runEnd = pos-1;
7182
7183 currentAttr = emptyAttr;
7184 }
7185 }
7186 }
7187
7188 wxASSERT(runEnd == pos-1);
7189
7190 // Now we only have to deal with the previous run
7191 if (currentAttr == attr)
7192 {
7193 // If we still have the same attributes, then we
7194 // simply increase the run size.
7195 runEnd = pos;
7196 }
7197 else
7198 {
7199 if (runEnd >= 0)
7200 {
7201 // We need to add an object, or reuse the existing one.
7202 if (runStart == 0)
7203 {
7204 lastPlainText = this;
7205 SetText(text.Mid(runStart, runEnd - runStart + 1));
7206 }
7207 else
7208 {
7209 wxRichTextPlainText* obj = new wxRichTextPlainText;
7210 lastPlainText = obj;
7211 obj->SetAttributes(GetAttributes());
7212 obj->SetProperties(GetProperties());
7213 obj->SetParent(parent);
7214
7215 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7216 if (next)
7217 parent->GetChildren().Insert(next, obj);
7218 else
7219 parent->GetChildren().Append(obj);
7220 }
7221 }
7222
7223 runStart = pos;
7224 runEnd = pos;
7225
7226 currentAttr = attr;
7227 }
7228 }
7229 }
7230
7231 // We may still have a run to add, and possibly a no-attribute text fragment after that.
7232 // If the whole string was already a single attribute (the run covers the whole string), don't split.
7233 if ((runStart != -1) && !(runStart == 0 && runEnd == (len-1)))
7234 {
7235 // If the current attribute is empty, merge the run with the next fragment
7236 // which by definition (because it's not specified) has empty attributes.
7237 if (currentAttr.IsDefault())
7238 runEnd = (len-1);
7239
7240 if (runEnd < (len-1))
7241 {
7242 // We need to add an object, or reuse the existing one.
7243 if (runStart == 0)
7244 {
7245 lastPlainText = this;
7246 SetText(text.Mid(runStart, runEnd - runStart + 1));
7247 }
7248 else
7249 {
7250 wxRichTextPlainText* obj = new wxRichTextPlainText;
7251 lastPlainText = obj;
7252 obj->SetAttributes(GetAttributes());
7253 obj->SetProperties(GetProperties());
7254 obj->SetParent(parent);
7255
7256 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7257 if (next)
7258 parent->GetChildren().Insert(next, obj);
7259 else
7260 parent->GetChildren().Append(obj);
7261 }
7262
7263 runStart = runEnd+1;
7264 runEnd = (len-1);
7265 }
7266
7267 // Now the last, non-attributed fragment at the end, if any
7268 if ((runStart < len) && !(runStart == 0 && runEnd == (len-1)))
7269 {
7270 wxASSERT(runStart != 0);
7271
7272 wxRichTextPlainText* obj = new wxRichTextPlainText;
7273 obj->SetAttributes(GetAttributes());
7274 obj->SetProperties(GetProperties());
7275 obj->SetParent(parent);
7276
7277 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7278 if (next)
7279 parent->GetChildren().Insert(next, obj);
7280 else
7281 parent->GetChildren().Append(obj);
7282
7283 lastPlainText = obj;
7284 }
7285 }
7286
7287 return lastPlainText;
7288 }
7289 }
7290 }
7291 return this;
7292 }
7293
7294 /// Dump to output stream for debugging
7295 void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
7296 {
7297 wxRichTextObject::Dump(stream);
7298 stream << m_text << wxT("\n");
7299 }
7300
7301 /// Get the first position from pos that has a line break character.
7302 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos)
7303 {
7304 int i;
7305 int len = m_text.length();
7306 int startPos = pos - m_range.GetStart();
7307 for (i = startPos; i < len; i++)
7308 {
7309 wxChar ch = m_text[i];
7310 if (ch == wxRichTextLineBreakChar)
7311 {
7312 return i + m_range.GetStart();
7313 }
7314 }
7315 return -1;
7316 }
7317
7318 /*!
7319 * wxRichTextBuffer
7320 * This is a kind of box, used to represent the whole buffer
7321 */
7322
7323 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox)
7324
7325 wxList wxRichTextBuffer::sm_handlers;
7326 wxList wxRichTextBuffer::sm_drawingHandlers;
7327 wxRichTextFieldTypeHashMap wxRichTextBuffer::sm_fieldTypes;
7328 wxRichTextRenderer* wxRichTextBuffer::sm_renderer = NULL;
7329 int wxRichTextBuffer::sm_bulletRightMargin = 20;
7330 float wxRichTextBuffer::sm_bulletProportion = (float) 0.3;
7331 bool wxRichTextBuffer::sm_floatingLayoutMode = true;
7332
7333 /// Initialisation
7334 void wxRichTextBuffer::Init()
7335 {
7336 m_commandProcessor = new wxCommandProcessor;
7337 m_styleSheet = NULL;
7338 m_modified = false;
7339 m_batchedCommandDepth = 0;
7340 m_batchedCommand = NULL;
7341 m_suppressUndo = 0;
7342 m_handlerFlags = 0;
7343 m_scale = 1.0;
7344 m_dimensionScale = 1.0;
7345 m_fontScale = 1.0;
7346 SetMargins(4);
7347 }
7348
7349 /// Initialisation
7350 wxRichTextBuffer::~wxRichTextBuffer()
7351 {
7352 delete m_commandProcessor;
7353 delete m_batchedCommand;
7354
7355 ClearStyleStack();
7356 ClearEventHandlers();
7357 }
7358
7359 void wxRichTextBuffer::ResetAndClearCommands()
7360 {
7361 Reset();
7362
7363 GetCommandProcessor()->ClearCommands();
7364
7365 Modify(false);
7366 Invalidate(wxRICHTEXT_ALL);
7367 }
7368
7369 void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj)
7370 {
7371 wxRichTextParagraphLayoutBox::Copy(obj);
7372
7373 m_styleSheet = obj.m_styleSheet;
7374 m_modified = obj.m_modified;
7375 m_batchedCommandDepth = 0;
7376 if (m_batchedCommand)
7377 delete m_batchedCommand;
7378 m_batchedCommand = NULL;
7379 m_suppressUndo = obj.m_suppressUndo;
7380 m_invalidRange = obj.m_invalidRange;
7381 m_dimensionScale = obj.m_dimensionScale;
7382 m_fontScale = obj.m_fontScale;
7383 }
7384
7385 /// Push style sheet to top of stack
7386 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet* styleSheet)
7387 {
7388 if (m_styleSheet)
7389 styleSheet->InsertSheet(m_styleSheet);
7390
7391 SetStyleSheet(styleSheet);
7392
7393 return true;
7394 }
7395
7396 /// Pop style sheet from top of stack
7397 wxRichTextStyleSheet* wxRichTextBuffer::PopStyleSheet()
7398 {
7399 if (m_styleSheet)
7400 {
7401 wxRichTextStyleSheet* oldSheet = m_styleSheet;
7402 m_styleSheet = oldSheet->GetNextSheet();
7403 oldSheet->Unlink();
7404
7405 return oldSheet;
7406 }
7407 else
7408 return NULL;
7409 }
7410
7411 /// Submit command to insert paragraphs
7412 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int flags)
7413 {
7414 return ctrl->GetFocusObject()->InsertParagraphsWithUndo(this, pos, paragraphs, ctrl, flags);
7415 }
7416
7417 /// Submit command to insert paragraphs
7418 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int WXUNUSED(flags))
7419 {
7420 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7421
7422 action->GetNewParagraphs() = paragraphs;
7423
7424 action->SetPosition(pos);
7425
7426 wxRichTextRange range = wxRichTextRange(pos, pos + paragraphs.GetOwnRange().GetEnd() - 1);
7427 if (!paragraphs.GetPartialParagraph())
7428 range.SetEnd(range.GetEnd()+1);
7429
7430 // Set the range we'll need to delete in Undo
7431 action->SetRange(range);
7432
7433 buffer->SubmitAction(action);
7434
7435 return true;
7436 }
7437
7438 /// Submit command to insert the given text
7439 bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
7440 {
7441 return ctrl->GetFocusObject()->InsertTextWithUndo(this, pos, text, ctrl, flags);
7442 }
7443
7444 /// Submit command to insert the given text
7445 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
7446 {
7447 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7448
7449 wxRichTextAttr* p = NULL;
7450 wxRichTextAttr paraAttr;
7451 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7452 {
7453 // Get appropriate paragraph style
7454 paraAttr = GetStyleForNewParagraph(buffer, pos, false, false);
7455 if (!paraAttr.IsDefault())
7456 p = & paraAttr;
7457 }
7458
7459 action->GetNewParagraphs().AddParagraphs(text, p);
7460
7461 int length = action->GetNewParagraphs().GetOwnRange().GetLength();
7462
7463 if (!text.empty() && text.Last() != wxT('\n'))
7464 {
7465 // Don't count the newline when undoing
7466 length --;
7467 action->GetNewParagraphs().SetPartialParagraph(true);
7468 }
7469 else if (!text.empty() && text.Last() == wxT('\n'))
7470 length --;
7471
7472 action->SetPosition(pos);
7473
7474 // Set the range we'll need to delete in Undo
7475 action->SetRange(wxRichTextRange(pos, pos + length - 1));
7476
7477 buffer->SubmitAction(action);
7478
7479 return true;
7480 }
7481
7482 /// Submit command to insert the given text
7483 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl, int flags)
7484 {
7485 return ctrl->GetFocusObject()->InsertNewlineWithUndo(this, pos, ctrl, flags);
7486 }
7487
7488 /// Submit command to insert the given text
7489 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextCtrl* ctrl, int flags)
7490 {
7491 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7492
7493 wxRichTextAttr* p = NULL;
7494 wxRichTextAttr paraAttr;
7495 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7496 {
7497 paraAttr = GetStyleForNewParagraph(buffer, pos, false, true /* look for next paragraph style */);
7498 if (!paraAttr.IsDefault())
7499 p = & paraAttr;
7500 }
7501
7502 wxRichTextAttr attr(buffer->GetDefaultStyle());
7503 // Don't include box attributes such as margins
7504 attr.GetTextBoxAttr().Reset();
7505
7506 wxRichTextParagraph* newPara = new wxRichTextParagraph(wxEmptyString, this, & attr);
7507 action->GetNewParagraphs().AppendChild(newPara);
7508 action->GetNewParagraphs().UpdateRanges();
7509 action->GetNewParagraphs().SetPartialParagraph(false);
7510 wxRichTextParagraph* para = GetParagraphAtPosition(pos, false);
7511 long pos1 = pos;
7512
7513 if (p)
7514 newPara->SetAttributes(*p);
7515
7516 if (flags & wxRICHTEXT_INSERT_INTERACTIVE)
7517 {
7518 if (para && para->GetRange().GetEnd() == pos)
7519 pos1 ++;
7520
7521 // Now see if we need to number the paragraph.
7522 if (newPara->GetAttributes().HasBulletNumber())
7523 {
7524 wxRichTextAttr numberingAttr;
7525 if (FindNextParagraphNumber(para, numberingAttr))
7526 wxRichTextApplyStyle(newPara->GetAttributes(), (const wxRichTextAttr&) numberingAttr);
7527 }
7528 }
7529
7530 action->SetPosition(pos);
7531
7532 // Use the default character style
7533 if (!buffer->GetDefaultStyle().IsDefault() && newPara->GetChildren().GetFirst())
7534 {
7535 // Check whether the default style merely reflects the paragraph/basic style,
7536 // in which case don't apply it.
7537 wxRichTextAttr defaultStyle(buffer->GetDefaultStyle());
7538 defaultStyle.GetTextBoxAttr().Reset();
7539 wxRichTextAttr toApply;
7540 if (para)
7541 {
7542 wxRichTextAttr combinedAttr = para->GetCombinedAttributes(true /* include box attributes */);
7543 wxRichTextAttr newAttr;
7544 // This filters out attributes that are accounted for by the current
7545 // paragraph/basic style
7546 wxRichTextApplyStyle(toApply, defaultStyle, & combinedAttr);
7547 }
7548 else
7549 toApply = defaultStyle;
7550
7551 if (!toApply.IsDefault())
7552 newPara->GetChildren().GetFirst()->GetData()->SetAttributes(toApply);
7553 }
7554
7555 // Set the range we'll need to delete in Undo
7556 action->SetRange(wxRichTextRange(pos1, pos1));
7557
7558 buffer->SubmitAction(action);
7559
7560 return true;
7561 }
7562
7563 /// Submit command to insert the given image
7564 bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock, wxRichTextCtrl* ctrl, int flags,
7565 const wxRichTextAttr& textAttr)
7566 {
7567 return ctrl->GetFocusObject()->InsertImageWithUndo(this, pos, imageBlock, ctrl, flags, textAttr);
7568 }
7569
7570 /// Submit command to insert the given image
7571 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextImageBlock& imageBlock,
7572 wxRichTextCtrl* ctrl, int flags,
7573 const wxRichTextAttr& textAttr)
7574 {
7575 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7576
7577 wxRichTextAttr* p = NULL;
7578 wxRichTextAttr paraAttr;
7579 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7580 {
7581 paraAttr = GetStyleForNewParagraph(buffer, pos);
7582 if (!paraAttr.IsDefault())
7583 p = & paraAttr;
7584 }
7585
7586 wxRichTextAttr attr(buffer->GetDefaultStyle());
7587
7588 // Don't include box attributes such as margins
7589 attr.GetTextBoxAttr().Reset();
7590
7591 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7592 if (p)
7593 newPara->SetAttributes(*p);
7594
7595 wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
7596 newPara->AppendChild(imageObject);
7597 imageObject->SetAttributes(textAttr);
7598 action->GetNewParagraphs().AppendChild(newPara);
7599 action->GetNewParagraphs().UpdateRanges();
7600
7601 action->GetNewParagraphs().SetPartialParagraph(true);
7602
7603 action->SetPosition(pos);
7604
7605 // Set the range we'll need to delete in Undo
7606 action->SetRange(wxRichTextRange(pos, pos));
7607
7608 buffer->SubmitAction(action);
7609
7610 return true;
7611 }
7612
7613 // Insert an object with no change of it
7614 wxRichTextObject* wxRichTextBuffer::InsertObjectWithUndo(long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
7615 {
7616 return ctrl->GetFocusObject()->InsertObjectWithUndo(this, pos, object, ctrl, flags);
7617 }
7618
7619 // Insert an object with no change of it
7620 wxRichTextObject* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
7621 {
7622 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Object"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7623
7624 wxRichTextAttr* p = NULL;
7625 wxRichTextAttr paraAttr;
7626 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7627 {
7628 paraAttr = GetStyleForNewParagraph(buffer, pos);
7629 if (!paraAttr.IsDefault())
7630 p = & paraAttr;
7631 }
7632
7633 wxRichTextAttr attr(buffer->GetDefaultStyle());
7634
7635 // Don't include box attributes such as margins
7636 attr.GetTextBoxAttr().Reset();
7637
7638 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7639 if (p)
7640 newPara->SetAttributes(*p);
7641
7642 newPara->AppendChild(object);
7643 action->GetNewParagraphs().AppendChild(newPara);
7644 action->GetNewParagraphs().UpdateRanges();
7645
7646 action->GetNewParagraphs().SetPartialParagraph(true);
7647
7648 action->SetPosition(pos);
7649
7650 // Set the range we'll need to delete in Undo
7651 action->SetRange(wxRichTextRange(pos, pos));
7652
7653 buffer->SubmitAction(action);
7654
7655 wxRichTextObject* obj = GetLeafObjectAtPosition(pos);
7656 return obj;
7657 }
7658
7659 wxRichTextField* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& fieldType,
7660 const wxRichTextProperties& properties,
7661 wxRichTextCtrl* ctrl, int flags,
7662 const wxRichTextAttr& textAttr)
7663 {
7664 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Field"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7665
7666 wxRichTextAttr* p = NULL;
7667 wxRichTextAttr paraAttr;
7668 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7669 {
7670 paraAttr = GetStyleForNewParagraph(buffer, pos);
7671 if (!paraAttr.IsDefault())
7672 p = & paraAttr;
7673 }
7674
7675 wxRichTextAttr attr(buffer->GetDefaultStyle());
7676
7677 // Don't include box attributes such as margins
7678 attr.GetTextBoxAttr().Reset();
7679
7680 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7681 if (p)
7682 newPara->SetAttributes(*p);
7683
7684 wxRichTextField* fieldObject = new wxRichTextField();
7685 fieldObject->wxRichTextObject::SetProperties(properties);
7686 fieldObject->SetFieldType(fieldType);
7687 fieldObject->SetAttributes(textAttr);
7688 newPara->AppendChild(fieldObject);
7689 action->GetNewParagraphs().AppendChild(newPara);
7690 action->GetNewParagraphs().UpdateRanges();
7691 action->GetNewParagraphs().SetPartialParagraph(true);
7692 action->SetPosition(pos);
7693
7694 // Set the range we'll need to delete in Undo
7695 action->SetRange(wxRichTextRange(pos, pos));
7696
7697 buffer->SubmitAction(action);
7698
7699 wxRichTextField* obj = wxDynamicCast(GetLeafObjectAtPosition(pos), wxRichTextField);
7700 return obj;
7701 }
7702
7703 /// Get the style that is appropriate for a new paragraph at this position.
7704 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7705 /// style.
7706 wxRichTextAttr wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer* buffer, long pos, bool caretPosition, bool lookUpNewParaStyle) const
7707 {
7708 wxRichTextParagraph* para = GetParagraphAtPosition(pos, caretPosition);
7709 if (para)
7710 {
7711 wxRichTextAttr attr;
7712 bool foundAttributes = false;
7713
7714 // Look for a matching paragraph style
7715 if (lookUpNewParaStyle && !para->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer->GetStyleSheet())
7716 {
7717 wxRichTextParagraphStyleDefinition* paraDef = buffer->GetStyleSheet()->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
7718 if (paraDef)
7719 {
7720 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7721 if (para->GetRange().GetEnd() == pos && !paraDef->GetNextStyle().IsEmpty())
7722 {
7723 wxRichTextParagraphStyleDefinition* nextParaDef = buffer->GetStyleSheet()->FindParagraphStyle(paraDef->GetNextStyle());
7724 if (nextParaDef)
7725 {
7726 foundAttributes = true;
7727 attr = nextParaDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
7728 }
7729 }
7730
7731 // If we didn't find the 'next style', use this style instead.
7732 if (!foundAttributes)
7733 {
7734 foundAttributes = true;
7735 attr = paraDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
7736 }
7737 }
7738 }
7739
7740 // Also apply list style if present
7741 if (lookUpNewParaStyle && !para->GetAttributes().GetListStyleName().IsEmpty() && buffer->GetStyleSheet())
7742 {
7743 wxRichTextListStyleDefinition* listDef = buffer->GetStyleSheet()->FindListStyle(para->GetAttributes().GetListStyleName());
7744 if (listDef)
7745 {
7746 int thisIndent = para->GetAttributes().GetLeftIndent();
7747 int thisLevel = para->GetAttributes().HasOutlineLevel() ? para->GetAttributes().GetOutlineLevel() : listDef->FindLevelForIndent(thisIndent);
7748
7749 // Apply the overall list style, and item style for this level
7750 wxRichTextAttr listStyle(listDef->GetCombinedStyleForLevel(thisLevel, buffer->GetStyleSheet()));
7751 wxRichTextApplyStyle(attr, listStyle);
7752 attr.SetOutlineLevel(thisLevel);
7753 if (para->GetAttributes().HasBulletNumber())
7754 attr.SetBulletNumber(para->GetAttributes().GetBulletNumber());
7755 }
7756 }
7757
7758 if (!foundAttributes)
7759 {
7760 attr = para->GetAttributes();
7761 int flags = attr.GetFlags();
7762
7763 // Eliminate character styles
7764 flags &= ( (~ wxTEXT_ATTR_FONT) |
7765 (~ wxTEXT_ATTR_TEXT_COLOUR) |
7766 (~ wxTEXT_ATTR_BACKGROUND_COLOUR) );
7767 attr.SetFlags(flags);
7768 }
7769
7770 return attr;
7771 }
7772 else
7773 return wxRichTextAttr();
7774 }
7775
7776 /// Submit command to delete this range
7777 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl)
7778 {
7779 return ctrl->GetFocusObject()->DeleteRangeWithUndo(range, ctrl, this);
7780 }
7781
7782 /// Submit command to delete this range
7783 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer)
7784 {
7785 wxRichTextAction* action = new wxRichTextAction(NULL, _("Delete"), wxRICHTEXT_DELETE, buffer, this, ctrl);
7786
7787 action->SetPosition(ctrl->GetCaretPosition());
7788
7789 // Set the range to delete
7790 action->SetRange(range);
7791
7792 // Copy the fragment that we'll need to restore in Undo
7793 CopyFragment(range, action->GetOldParagraphs());
7794
7795 // See if we're deleting a paragraph marker, in which case we need to
7796 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7797 if (range.GetStart() == range.GetEnd())
7798 {
7799 wxRichTextParagraph* para = GetParagraphAtPosition(range.GetStart());
7800 if (para && para->GetRange().GetEnd() == range.GetEnd())
7801 {
7802 wxRichTextParagraph* nextPara = GetParagraphAtPosition(range.GetStart()+1);
7803 if (nextPara && nextPara != para)
7804 {
7805 action->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara->GetAttributes());
7806 action->GetOldParagraphs().GetAttributes().SetFlags(action->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE);
7807 }
7808 }
7809 }
7810
7811 buffer->SubmitAction(action);
7812
7813 return true;
7814 }
7815
7816 /// Collapse undo/redo commands
7817 bool wxRichTextBuffer::BeginBatchUndo(const wxString& cmdName)
7818 {
7819 if (m_batchedCommandDepth == 0)
7820 {
7821 wxASSERT(m_batchedCommand == NULL);
7822 if (m_batchedCommand)
7823 {
7824 GetCommandProcessor()->Store(m_batchedCommand);
7825 }
7826 m_batchedCommand = new wxRichTextCommand(cmdName);
7827 }
7828
7829 m_batchedCommandDepth ++;
7830
7831 return true;
7832 }
7833
7834 /// Collapse undo/redo commands
7835 bool wxRichTextBuffer::EndBatchUndo()
7836 {
7837 m_batchedCommandDepth --;
7838
7839 wxASSERT(m_batchedCommandDepth >= 0);
7840 wxASSERT(m_batchedCommand != NULL);
7841
7842 if (m_batchedCommandDepth == 0)
7843 {
7844 GetCommandProcessor()->Store(m_batchedCommand);
7845 m_batchedCommand = NULL;
7846 }
7847
7848 return true;
7849 }
7850
7851 /// Submit immediately, or delay according to whether collapsing is on
7852 bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action)
7853 {
7854 if (action && !action->GetNewParagraphs().IsEmpty())
7855 PrepareContent(action->GetNewParagraphs());
7856
7857 if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
7858 {
7859 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7860 cmd->AddAction(action);
7861 cmd->Do();
7862 cmd->GetActions().Clear();
7863 delete cmd;
7864
7865 m_batchedCommand->AddAction(action);
7866 }
7867 else
7868 {
7869 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7870 cmd->AddAction(action);
7871
7872 // Only store it if we're not suppressing undo.
7873 return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
7874 }
7875
7876 return true;
7877 }
7878
7879 /// Begin suppressing undo/redo commands.
7880 bool wxRichTextBuffer::BeginSuppressUndo()
7881 {
7882 m_suppressUndo ++;
7883
7884 return true;
7885 }
7886
7887 /// End suppressing undo/redo commands.
7888 bool wxRichTextBuffer::EndSuppressUndo()
7889 {
7890 m_suppressUndo --;
7891
7892 return true;
7893 }
7894
7895 /// Begin using a style
7896 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr& style)
7897 {
7898 wxRichTextAttr newStyle(GetDefaultStyle());
7899 newStyle.GetTextBoxAttr().Reset();
7900
7901 // Save the old default style
7902 m_attributeStack.Append((wxObject*) new wxRichTextAttr(newStyle));
7903
7904 wxRichTextApplyStyle(newStyle, style);
7905 newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags());
7906
7907 SetDefaultStyle(newStyle);
7908
7909 return true;
7910 }
7911
7912 /// End the style
7913 bool wxRichTextBuffer::EndStyle()
7914 {
7915 if (!m_attributeStack.GetFirst())
7916 {
7917 wxLogDebug(_("Too many EndStyle calls!"));
7918 return false;
7919 }
7920
7921 wxList::compatibility_iterator node = m_attributeStack.GetLast();
7922 wxRichTextAttr* attr = (wxRichTextAttr*)node->GetData();
7923 m_attributeStack.Erase(node);
7924
7925 SetDefaultStyle(*attr);
7926
7927 delete attr;
7928 return true;
7929 }
7930
7931 /// End all styles
7932 bool wxRichTextBuffer::EndAllStyles()
7933 {
7934 while (m_attributeStack.GetCount() != 0)
7935 EndStyle();
7936 return true;
7937 }
7938
7939 /// Clear the style stack
7940 void wxRichTextBuffer::ClearStyleStack()
7941 {
7942 for (wxList::compatibility_iterator node = m_attributeStack.GetFirst(); node; node = node->GetNext())
7943 delete (wxRichTextAttr*) node->GetData();
7944 m_attributeStack.Clear();
7945 }
7946
7947 /// Begin using bold
7948 bool wxRichTextBuffer::BeginBold()
7949 {
7950 wxRichTextAttr attr;
7951 attr.SetFontWeight(wxFONTWEIGHT_BOLD);
7952
7953 return BeginStyle(attr);
7954 }
7955
7956 /// Begin using italic
7957 bool wxRichTextBuffer::BeginItalic()
7958 {
7959 wxRichTextAttr attr;
7960 attr.SetFontStyle(wxFONTSTYLE_ITALIC);
7961
7962 return BeginStyle(attr);
7963 }
7964
7965 /// Begin using underline
7966 bool wxRichTextBuffer::BeginUnderline()
7967 {
7968 wxRichTextAttr attr;
7969 attr.SetFontUnderlined(true);
7970
7971 return BeginStyle(attr);
7972 }
7973
7974 /// Begin using point size
7975 bool wxRichTextBuffer::BeginFontSize(int pointSize)
7976 {
7977 wxRichTextAttr attr;
7978 attr.SetFontSize(pointSize);
7979
7980 return BeginStyle(attr);
7981 }
7982
7983 /// Begin using this font
7984 bool wxRichTextBuffer::BeginFont(const wxFont& font)
7985 {
7986 wxRichTextAttr attr;
7987 attr.SetFont(font);
7988
7989 return BeginStyle(attr);
7990 }
7991
7992 /// Begin using this colour
7993 bool wxRichTextBuffer::BeginTextColour(const wxColour& colour)
7994 {
7995 wxRichTextAttr attr;
7996 attr.SetFlags(wxTEXT_ATTR_TEXT_COLOUR);
7997 attr.SetTextColour(colour);
7998
7999 return BeginStyle(attr);
8000 }
8001
8002 /// Begin using alignment
8003 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment)
8004 {
8005 wxRichTextAttr attr;
8006 attr.SetFlags(wxTEXT_ATTR_ALIGNMENT);
8007 attr.SetAlignment(alignment);
8008
8009 return BeginStyle(attr);
8010 }
8011
8012 /// Begin left indent
8013 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent, int leftSubIndent)
8014 {
8015 wxRichTextAttr attr;
8016 attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
8017 attr.SetLeftIndent(leftIndent, leftSubIndent);
8018
8019 return BeginStyle(attr);
8020 }
8021
8022 /// Begin right indent
8023 bool wxRichTextBuffer::BeginRightIndent(int rightIndent)
8024 {
8025 wxRichTextAttr attr;
8026 attr.SetFlags(wxTEXT_ATTR_RIGHT_INDENT);
8027 attr.SetRightIndent(rightIndent);
8028
8029 return BeginStyle(attr);
8030 }
8031
8032 /// Begin paragraph spacing
8033 bool wxRichTextBuffer::BeginParagraphSpacing(int before, int after)
8034 {
8035 long flags = 0;
8036 if (before != 0)
8037 flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
8038 if (after != 0)
8039 flags |= wxTEXT_ATTR_PARA_SPACING_AFTER;
8040
8041 wxRichTextAttr attr;
8042 attr.SetFlags(flags);
8043 attr.SetParagraphSpacingBefore(before);
8044 attr.SetParagraphSpacingAfter(after);
8045
8046 return BeginStyle(attr);
8047 }
8048
8049 /// Begin line spacing
8050 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing)
8051 {
8052 wxRichTextAttr attr;
8053 attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
8054 attr.SetLineSpacing(lineSpacing);
8055
8056 return BeginStyle(attr);
8057 }
8058
8059 /// Begin numbered bullet
8060 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber, int leftIndent, int leftSubIndent, int bulletStyle)
8061 {
8062 wxRichTextAttr attr;
8063 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
8064 attr.SetBulletStyle(bulletStyle);
8065 attr.SetBulletNumber(bulletNumber);
8066 attr.SetLeftIndent(leftIndent, leftSubIndent);
8067
8068 return BeginStyle(attr);
8069 }
8070
8071 /// Begin symbol bullet
8072 bool wxRichTextBuffer::BeginSymbolBullet(const wxString& symbol, int leftIndent, int leftSubIndent, int bulletStyle)
8073 {
8074 wxRichTextAttr attr;
8075 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
8076 attr.SetBulletStyle(bulletStyle);
8077 attr.SetLeftIndent(leftIndent, leftSubIndent);
8078 attr.SetBulletText(symbol);
8079
8080 return BeginStyle(attr);
8081 }
8082
8083 /// Begin standard bullet
8084 bool wxRichTextBuffer::BeginStandardBullet(const wxString& bulletName, int leftIndent, int leftSubIndent, int bulletStyle)
8085 {
8086 wxRichTextAttr attr;
8087 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
8088 attr.SetBulletStyle(bulletStyle);
8089 attr.SetLeftIndent(leftIndent, leftSubIndent);
8090 attr.SetBulletName(bulletName);
8091
8092 return BeginStyle(attr);
8093 }
8094
8095 /// Begin named character style
8096 bool wxRichTextBuffer::BeginCharacterStyle(const wxString& characterStyle)
8097 {
8098 if (GetStyleSheet())
8099 {
8100 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
8101 if (def)
8102 {
8103 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
8104 return BeginStyle(attr);
8105 }
8106 }
8107 return false;
8108 }
8109
8110 /// Begin named paragraph style
8111 bool wxRichTextBuffer::BeginParagraphStyle(const wxString& paragraphStyle)
8112 {
8113 if (GetStyleSheet())
8114 {
8115 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(paragraphStyle);
8116 if (def)
8117 {
8118 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
8119 return BeginStyle(attr);
8120 }
8121 }
8122 return false;
8123 }
8124
8125 /// Begin named list style
8126 bool wxRichTextBuffer::BeginListStyle(const wxString& listStyle, int level, int number)
8127 {
8128 if (GetStyleSheet())
8129 {
8130 wxRichTextListStyleDefinition* def = GetStyleSheet()->FindListStyle(listStyle);
8131 if (def)
8132 {
8133 wxRichTextAttr attr(def->GetCombinedStyleForLevel(level));
8134
8135 attr.SetBulletNumber(number);
8136
8137 return BeginStyle(attr);
8138 }
8139 }
8140 return false;
8141 }
8142
8143 /// Begin URL
8144 bool wxRichTextBuffer::BeginURL(const wxString& url, const wxString& characterStyle)
8145 {
8146 wxRichTextAttr attr;
8147
8148 if (!characterStyle.IsEmpty() && GetStyleSheet())
8149 {
8150 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
8151 if (def)
8152 {
8153 attr = def->GetStyleMergedWithBase(GetStyleSheet());
8154 }
8155 }
8156 attr.SetURL(url);
8157
8158 return BeginStyle(attr);
8159 }
8160
8161 /// Adds a handler to the end
8162 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler *handler)
8163 {
8164 sm_handlers.Append(handler);
8165 }
8166
8167 /// Inserts a handler at the front
8168 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler *handler)
8169 {
8170 sm_handlers.Insert( handler );
8171 }
8172
8173 /// Removes a handler
8174 bool wxRichTextBuffer::RemoveHandler(const wxString& name)
8175 {
8176 wxRichTextFileHandler *handler = FindHandler(name);
8177 if (handler)
8178 {
8179 sm_handlers.DeleteObject(handler);
8180 delete handler;
8181 return true;
8182 }
8183 else
8184 return false;
8185 }
8186
8187 /// Finds a handler by filename or, if supplied, type
8188 wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename,
8189 wxRichTextFileType imageType)
8190 {
8191 if (imageType != wxRICHTEXT_TYPE_ANY)
8192 return FindHandler(imageType);
8193 else if (!filename.IsEmpty())
8194 {
8195 wxString path, file, ext;
8196 wxFileName::SplitPath(filename, & path, & file, & ext);
8197 return FindHandler(ext, imageType);
8198 }
8199 else
8200 return NULL;
8201 }
8202
8203
8204 /// Finds a handler by name
8205 wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& name)
8206 {
8207 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8208 while (node)
8209 {
8210 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
8211 if (handler->GetName().Lower() == name.Lower()) return handler;
8212
8213 node = node->GetNext();
8214 }
8215 return NULL;
8216 }
8217
8218 /// Finds a handler by extension and type
8219 wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, wxRichTextFileType type)
8220 {
8221 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8222 while (node)
8223 {
8224 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
8225 if ( handler->GetExtension().Lower() == extension.Lower() &&
8226 (type == wxRICHTEXT_TYPE_ANY || handler->GetType() == type) )
8227 return handler;
8228 node = node->GetNext();
8229 }
8230 return 0;
8231 }
8232
8233 /// Finds a handler by type
8234 wxRichTextFileHandler* wxRichTextBuffer::FindHandler(wxRichTextFileType type)
8235 {
8236 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8237 while (node)
8238 {
8239 wxRichTextFileHandler *handler = (wxRichTextFileHandler *)node->GetData();
8240 if (handler->GetType() == type) return handler;
8241 node = node->GetNext();
8242 }
8243 return NULL;
8244 }
8245
8246 void wxRichTextBuffer::InitStandardHandlers()
8247 {
8248 if (!FindHandler(wxRICHTEXT_TYPE_TEXT))
8249 AddHandler(new wxRichTextPlainTextHandler);
8250 }
8251
8252 void wxRichTextBuffer::CleanUpHandlers()
8253 {
8254 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8255 while (node)
8256 {
8257 wxRichTextFileHandler* handler = (wxRichTextFileHandler*)node->GetData();
8258 wxList::compatibility_iterator next = node->GetNext();
8259 delete handler;
8260 node = next;
8261 }
8262
8263 sm_handlers.Clear();
8264 }
8265
8266 wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* types)
8267 {
8268 if (types)
8269 types->Clear();
8270
8271 wxString wildcard;
8272
8273 wxList::compatibility_iterator node = GetHandlers().GetFirst();
8274 int count = 0;
8275 while (node)
8276 {
8277 wxRichTextFileHandler* handler = (wxRichTextFileHandler*) node->GetData();
8278 if (handler->IsVisible() && ((save && handler->CanSave()) || (!save && handler->CanLoad())))
8279 {
8280 if (combine)
8281 {
8282 if (count > 0)
8283 wildcard += wxT(";");
8284 wildcard += wxT("*.") + handler->GetExtension();
8285 }
8286 else
8287 {
8288 if (count > 0)
8289 wildcard += wxT("|");
8290 wildcard += handler->GetName();
8291 wildcard += wxT(" ");
8292 wildcard += _("files");
8293 wildcard += wxT(" (*.");
8294 wildcard += handler->GetExtension();
8295 wildcard += wxT(")|*.");
8296 wildcard += handler->GetExtension();
8297 if (types)
8298 types->Add(handler->GetType());
8299 }
8300 count ++;
8301 }
8302
8303 node = node->GetNext();
8304 }
8305
8306 if (combine)
8307 wildcard = wxT("(") + wildcard + wxT(")|") + wildcard;
8308 return wildcard;
8309 }
8310
8311 /// Load a file
8312 bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type)
8313 {
8314 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
8315 if (handler)
8316 {
8317 SetDefaultStyle(wxRichTextAttr());
8318 handler->SetFlags(GetHandlerFlags());
8319 bool success = handler->LoadFile(this, filename);
8320 Invalidate(wxRICHTEXT_ALL);
8321 return success;
8322 }
8323 else
8324 return false;
8325 }
8326
8327 /// Save a file
8328 bool wxRichTextBuffer::SaveFile(const wxString& filename, wxRichTextFileType type)
8329 {
8330 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
8331 if (handler)
8332 {
8333 handler->SetFlags(GetHandlerFlags());
8334 return handler->SaveFile(this, filename);
8335 }
8336 else
8337 return false;
8338 }
8339
8340 /// Load from a stream
8341 bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type)
8342 {
8343 wxRichTextFileHandler* handler = FindHandler(type);
8344 if (handler)
8345 {
8346 SetDefaultStyle(wxRichTextAttr());
8347 handler->SetFlags(GetHandlerFlags());
8348 bool success = handler->LoadFile(this, stream);
8349 Invalidate(wxRICHTEXT_ALL);
8350 return success;
8351 }
8352 else
8353 return false;
8354 }
8355
8356 /// Save to a stream
8357 bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, wxRichTextFileType type)
8358 {
8359 wxRichTextFileHandler* handler = FindHandler(type);
8360 if (handler)
8361 {
8362 handler->SetFlags(GetHandlerFlags());
8363 return handler->SaveFile(this, stream);
8364 }
8365 else
8366 return false;
8367 }
8368
8369 /// Copy the range to the clipboard
8370 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
8371 {
8372 bool success = false;
8373 wxRichTextParagraphLayoutBox* container = this;
8374 if (GetRichTextCtrl())
8375 container = GetRichTextCtrl()->GetFocusObject();
8376
8377 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8378
8379 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
8380 {
8381 wxTheClipboard->Clear();
8382
8383 // Add composite object
8384
8385 wxDataObjectComposite* compositeObject = new wxDataObjectComposite();
8386
8387 {
8388 wxString text = container->GetTextForRange(range);
8389
8390 #ifdef __WXMSW__
8391 text = wxTextFile::Translate(text, wxTextFileType_Dos);
8392 #endif
8393
8394 compositeObject->Add(new wxTextDataObject(text), false /* not preferred */);
8395 }
8396
8397 // Add rich text buffer data object. This needs the XML handler to be present.
8398
8399 if (FindHandler(wxRICHTEXT_TYPE_XML))
8400 {
8401 wxRichTextBuffer* richTextBuf = new wxRichTextBuffer;
8402 container->CopyFragment(range, *richTextBuf);
8403
8404 compositeObject->Add(new wxRichTextBufferDataObject(richTextBuf), true /* preferred */);
8405 }
8406
8407 if (wxTheClipboard->SetData(compositeObject))
8408 success = true;
8409
8410 wxTheClipboard->Close();
8411 }
8412
8413 #else
8414 wxUnusedVar(range);
8415 #endif
8416 return success;
8417 }
8418
8419 /// Paste the clipboard content to the buffer
8420 bool wxRichTextBuffer::PasteFromClipboard(long position)
8421 {
8422 bool success = false;
8423 wxRichTextParagraphLayoutBox* container = this;
8424 if (GetRichTextCtrl())
8425 container = GetRichTextCtrl()->GetFocusObject();
8426
8427 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8428 if (CanPasteFromClipboard())
8429 {
8430 if (wxTheClipboard->Open())
8431 {
8432 if (wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
8433 {
8434 wxRichTextBufferDataObject data;
8435 wxTheClipboard->GetData(data);
8436 wxRichTextBuffer* richTextBuffer = data.GetRichTextBuffer();
8437 if (richTextBuffer)
8438 {
8439 container->InsertParagraphsWithUndo(this, position+1, *richTextBuffer, GetRichTextCtrl(), 0);
8440 if (GetRichTextCtrl())
8441 GetRichTextCtrl()->ShowPosition(position + richTextBuffer->GetOwnRange().GetEnd());
8442 delete richTextBuffer;
8443 }
8444 }
8445 else if (wxTheClipboard->IsSupported(wxDF_TEXT)
8446 #if wxUSE_UNICODE
8447 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8448 #endif
8449 )
8450 {
8451 wxTextDataObject data;
8452 wxTheClipboard->GetData(data);
8453 wxString text(data.GetText());
8454 #ifdef __WXMSW__
8455 wxString text2;
8456 text2.Alloc(text.Length()+1);
8457 size_t i;
8458 for (i = 0; i < text.Length(); i++)
8459 {
8460 wxChar ch = text[i];
8461 if (ch != wxT('\r'))
8462 text2 += ch;
8463 }
8464 #else
8465 wxString text2 = text;
8466 #endif
8467 container->InsertTextWithUndo(this, position+1, text2, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
8468
8469 if (GetRichTextCtrl())
8470 GetRichTextCtrl()->ShowPosition(position + text2.Length());
8471
8472 success = true;
8473 }
8474 else if (wxTheClipboard->IsSupported(wxDF_BITMAP))
8475 {
8476 wxBitmapDataObject data;
8477 wxTheClipboard->GetData(data);
8478 wxBitmap bitmap(data.GetBitmap());
8479 wxImage image(bitmap.ConvertToImage());
8480
8481 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, container, GetRichTextCtrl(), false);
8482
8483 action->GetNewParagraphs().AddImage(image);
8484
8485 if (action->GetNewParagraphs().GetChildCount() == 1)
8486 action->GetNewParagraphs().SetPartialParagraph(true);
8487
8488 action->SetPosition(position+1);
8489
8490 // Set the range we'll need to delete in Undo
8491 action->SetRange(wxRichTextRange(position+1, position+1));
8492
8493 SubmitAction(action);
8494
8495 success = true;
8496 }
8497 wxTheClipboard->Close();
8498 }
8499 }
8500 #else
8501 wxUnusedVar(position);
8502 #endif
8503 return success;
8504 }
8505
8506 /// Can we paste from the clipboard?
8507 bool wxRichTextBuffer::CanPasteFromClipboard() const
8508 {
8509 bool canPaste = false;
8510 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8511 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
8512 {
8513 if (wxTheClipboard->IsSupported(wxDF_TEXT)
8514 #if wxUSE_UNICODE
8515 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8516 #endif
8517 || wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8518 wxTheClipboard->IsSupported(wxDF_BITMAP))
8519 {
8520 canPaste = true;
8521 }
8522 wxTheClipboard->Close();
8523 }
8524 #endif
8525 return canPaste;
8526 }
8527
8528 /// Dumps contents of buffer for debugging purposes
8529 void wxRichTextBuffer::Dump()
8530 {
8531 wxString text;
8532 {
8533 wxStringOutputStream stream(& text);
8534 wxTextOutputStream textStream(stream);
8535 Dump(textStream);
8536 }
8537
8538 wxLogDebug(text);
8539 }
8540
8541 /// Add an event handler
8542 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler* handler)
8543 {
8544 m_eventHandlers.Append(handler);
8545 return true;
8546 }
8547
8548 /// Remove an event handler
8549 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler* handler, bool deleteHandler)
8550 {
8551 wxList::compatibility_iterator node = m_eventHandlers.Find(handler);
8552 if (node)
8553 {
8554 m_eventHandlers.Erase(node);
8555 if (deleteHandler)
8556 delete handler;
8557
8558 return true;
8559 }
8560 else
8561 return false;
8562 }
8563
8564 /// Clear event handlers
8565 void wxRichTextBuffer::ClearEventHandlers()
8566 {
8567 m_eventHandlers.Clear();
8568 }
8569
8570 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8571 /// otherwise will stop at the first successful one.
8572 bool wxRichTextBuffer::SendEvent(wxEvent& event, bool sendToAll)
8573 {
8574 bool success = false;
8575 for (wxList::compatibility_iterator node = m_eventHandlers.GetFirst(); node; node = node->GetNext())
8576 {
8577 wxEvtHandler* handler = (wxEvtHandler*) node->GetData();
8578 if (handler->ProcessEvent(event))
8579 {
8580 success = true;
8581 if (!sendToAll)
8582 return true;
8583 }
8584 }
8585 return success;
8586 }
8587
8588 /// Set style sheet and notify of the change
8589 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet* sheet)
8590 {
8591 wxRichTextStyleSheet* oldSheet = GetStyleSheet();
8592
8593 wxWindowID winid = wxID_ANY;
8594 if (GetRichTextCtrl())
8595 winid = GetRichTextCtrl()->GetId();
8596
8597 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING, winid);
8598 event.SetEventObject(GetRichTextCtrl());
8599 event.SetContainer(GetRichTextCtrl()->GetFocusObject());
8600 event.SetOldStyleSheet(oldSheet);
8601 event.SetNewStyleSheet(sheet);
8602 event.Allow();
8603
8604 if (SendEvent(event) && !event.IsAllowed())
8605 {
8606 if (sheet != oldSheet)
8607 delete sheet;
8608
8609 return false;
8610 }
8611
8612 if (oldSheet && oldSheet != sheet)
8613 delete oldSheet;
8614
8615 SetStyleSheet(sheet);
8616
8617 event.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED);
8618 event.SetOldStyleSheet(NULL);
8619 event.Allow();
8620
8621 return SendEvent(event);
8622 }
8623
8624 /// Set renderer, deleting old one
8625 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer)
8626 {
8627 if (sm_renderer)
8628 delete sm_renderer;
8629 sm_renderer = renderer;
8630 }
8631
8632 /// Hit-testing: returns a flag indicating hit test details, plus
8633 /// information about position
8634 int wxRichTextBuffer::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
8635 {
8636 int ret = wxRichTextParagraphLayoutBox::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
8637 if (ret != wxRICHTEXT_HITTEST_NONE)
8638 {
8639 return ret;
8640 }
8641 else
8642 {
8643 textPosition = m_ownRange.GetEnd()-1;
8644 *obj = this;
8645 *contextObj = this;
8646 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
8647 }
8648 }
8649
8650 void wxRichTextBuffer::SetFontScale(double fontScale)
8651 {
8652 m_fontScale = fontScale;
8653 m_fontTable.SetFontScale(fontScale);
8654 }
8655
8656 void wxRichTextBuffer::SetDimensionScale(double dimScale)
8657 {
8658 m_dimensionScale = dimScale;
8659 }
8660
8661 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& bulletAttr, const wxRect& rect)
8662 {
8663 if (bulletAttr.GetTextColour().IsOk())
8664 {
8665 wxCheckSetPen(dc, wxPen(bulletAttr.GetTextColour()));
8666 wxCheckSetBrush(dc, wxBrush(bulletAttr.GetTextColour()));
8667 }
8668 else
8669 {
8670 wxCheckSetPen(dc, *wxBLACK_PEN);
8671 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
8672 }
8673
8674 wxFont font;
8675 if (bulletAttr.HasFont())
8676 {
8677 font = paragraph->GetBuffer()->GetFontTable().FindFont(bulletAttr);
8678 }
8679 else
8680 font = (*wxNORMAL_FONT);
8681
8682 wxCheckSetFont(dc, font);
8683
8684 int charHeight = dc.GetCharHeight();
8685
8686 int bulletWidth = (int) (((float) charHeight) * wxRichTextBuffer::GetBulletProportion());
8687 int bulletHeight = bulletWidth;
8688
8689 int x = rect.x;
8690
8691 // Calculate the top position of the character (as opposed to the whole line height)
8692 int y = rect.y + (rect.height - charHeight);
8693
8694 // Calculate where the bullet should be positioned
8695 y = y + (charHeight+1)/2 - (bulletHeight+1)/2;
8696
8697 // The margin between a bullet and text.
8698 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
8699
8700 if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8701 x = rect.x + rect.width - bulletWidth - margin;
8702 else if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8703 x = x + (rect.width)/2 - bulletWidth/2;
8704
8705 if (bulletAttr.GetBulletName() == wxT("standard/square"))
8706 {
8707 dc.DrawRectangle(x, y, bulletWidth, bulletHeight);
8708 }
8709 else if (bulletAttr.GetBulletName() == wxT("standard/diamond"))
8710 {
8711 wxPoint pts[5];
8712 pts[0].x = x; pts[0].y = y + bulletHeight/2;
8713 pts[1].x = x + bulletWidth/2; pts[1].y = y;
8714 pts[2].x = x + bulletWidth; pts[2].y = y + bulletHeight/2;
8715 pts[3].x = x + bulletWidth/2; pts[3].y = y + bulletHeight;
8716
8717 dc.DrawPolygon(4, pts);
8718 }
8719 else if (bulletAttr.GetBulletName() == wxT("standard/triangle"))
8720 {
8721 wxPoint pts[3];
8722 pts[0].x = x; pts[0].y = y;
8723 pts[1].x = x + bulletWidth; pts[1].y = y + bulletHeight/2;
8724 pts[2].x = x; pts[2].y = y + bulletHeight;
8725
8726 dc.DrawPolygon(3, pts);
8727 }
8728 else if (bulletAttr.GetBulletName() == wxT("standard/circle-outline"))
8729 {
8730 wxCheckSetBrush(dc, *wxTRANSPARENT_BRUSH);
8731 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
8732 }
8733 else // "standard/circle", and catch-all
8734 {
8735 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
8736 }
8737
8738 return true;
8739 }
8740
8741 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect, const wxString& text)
8742 {
8743 if (!text.empty())
8744 {
8745 wxFont font;
8746 if ((attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL) && !attr.GetBulletFont().IsEmpty() && attr.HasFont())
8747 {
8748 wxRichTextAttr fontAttr;
8749 if (attr.HasFontPixelSize())
8750 fontAttr.SetFontPixelSize(attr.GetFontSize());
8751 else
8752 fontAttr.SetFontPointSize(attr.GetFontSize());
8753 fontAttr.SetFontStyle(attr.GetFontStyle());
8754 fontAttr.SetFontWeight(attr.GetFontWeight());
8755 fontAttr.SetFontUnderlined(attr.GetFontUnderlined());
8756 fontAttr.SetFontFaceName(attr.GetBulletFont());
8757 font = paragraph->GetBuffer()->GetFontTable().FindFont(fontAttr);
8758 }
8759 else if (attr.HasFont())
8760 font = paragraph->GetBuffer()->GetFontTable().FindFont(attr);
8761 else
8762 font = (*wxNORMAL_FONT);
8763
8764 wxCheckSetFont(dc, font);
8765
8766 if (attr.GetTextColour().IsOk())
8767 dc.SetTextForeground(attr.GetTextColour());
8768
8769 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
8770
8771 int charHeight = dc.GetCharHeight();
8772 wxCoord tw, th;
8773 dc.GetTextExtent(text, & tw, & th);
8774
8775 int x = rect.x;
8776
8777 // Calculate the top position of the character (as opposed to the whole line height)
8778 int y = rect.y + (rect.height - charHeight);
8779
8780 // The margin between a bullet and text.
8781 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
8782
8783 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8784 x = (rect.x + rect.width) - tw - margin;
8785 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8786 x = x + (rect.width)/2 - tw/2;
8787
8788 dc.DrawText(text, x, y);
8789
8790 return true;
8791 }
8792 else
8793 return false;
8794 }
8795
8796 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& WXUNUSED(dc), const wxRichTextAttr& WXUNUSED(attr), const wxRect& WXUNUSED(rect))
8797 {
8798 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8799 // with the buffer. The store will allow retrieval from memory, disk or other means.
8800 return false;
8801 }
8802
8803 /// Enumerate the standard bullet names currently supported
8804 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString& bulletNames)
8805 {
8806 bulletNames.Add(wxTRANSLATE("standard/circle"));
8807 bulletNames.Add(wxTRANSLATE("standard/circle-outline"));
8808 bulletNames.Add(wxTRANSLATE("standard/square"));
8809 bulletNames.Add(wxTRANSLATE("standard/diamond"));
8810 bulletNames.Add(wxTRANSLATE("standard/triangle"));
8811
8812 return true;
8813 }
8814
8815 /*!
8816 * wxRichTextBox
8817 */
8818
8819 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextParagraphLayoutBox)
8820
8821 wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
8822 wxRichTextParagraphLayoutBox(parent)
8823 {
8824 }
8825
8826 /// Draw the item
8827 bool wxRichTextBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8828 {
8829 if (!IsShown())
8830 return true;
8831
8832 // TODO: if the active object in the control, draw an indication.
8833 // We need to add the concept of active object, and not just focus object,
8834 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8835 // Ultimately we would like to be able to interactively resize an active object
8836 // using drag handles.
8837 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
8838 }
8839
8840 /// Copy
8841 void wxRichTextBox::Copy(const wxRichTextBox& obj)
8842 {
8843 wxRichTextParagraphLayoutBox::Copy(obj);
8844 }
8845
8846 // Edit properties via a GUI
8847 bool wxRichTextBox::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8848 {
8849 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Box Properties"));
8850 boxDlg.SetAttributes(GetAttributes());
8851
8852 if (boxDlg.ShowModal() == wxID_OK)
8853 {
8854 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8855 // indeterminate in the object.
8856 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8857 return true;
8858 }
8859 else
8860 return false;
8861 }
8862
8863 /*!
8864 * wxRichTextField
8865 */
8866
8867 IMPLEMENT_DYNAMIC_CLASS(wxRichTextField, wxRichTextParagraphLayoutBox)
8868
8869 wxRichTextField::wxRichTextField(const wxString& fieldType, wxRichTextObject* parent):
8870 wxRichTextParagraphLayoutBox(parent)
8871 {
8872 SetFieldType(fieldType);
8873 }
8874
8875 /// Draw the item
8876 bool wxRichTextField::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8877 {
8878 if (!IsShown())
8879 return true;
8880
8881 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8882 if (fieldType && fieldType->Draw(this, dc, context, range, selection, rect, descent, style))
8883 return true;
8884
8885 // Fallback; but don't draw guidelines.
8886 style &= ~wxRICHTEXT_DRAW_GUIDELINES;
8887 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
8888 }
8889
8890 bool wxRichTextField::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
8891 {
8892 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8893 if (fieldType && fieldType->Layout(this, dc, context, rect, parentRect, style))
8894 return true;
8895
8896 // Fallback
8897 return wxRichTextParagraphLayoutBox::Layout(dc, context, rect, parentRect, style);
8898 }
8899
8900 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
8901 {
8902 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8903 if (fieldType)
8904 return fieldType->GetRangeSize((wxRichTextField*) this, range, size, descent, dc, context, flags, position, parentSize, partialExtents);
8905
8906 return wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize, partialExtents);
8907 }
8908
8909 /// Calculate range
8910 void wxRichTextField::CalculateRange(long start, long& end)
8911 {
8912 if (IsTopLevel())
8913 wxRichTextParagraphLayoutBox::CalculateRange(start, end);
8914 else
8915 wxRichTextObject::CalculateRange(start, end);
8916 }
8917
8918 /// Copy
8919 void wxRichTextField::Copy(const wxRichTextField& obj)
8920 {
8921 wxRichTextParagraphLayoutBox::Copy(obj);
8922
8923 UpdateField(GetBuffer());
8924 }
8925
8926 // Edit properties via a GUI
8927 bool wxRichTextField::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8928 {
8929 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8930 if (fieldType)
8931 return fieldType->EditProperties(this, parent, buffer);
8932
8933 return false;
8934 }
8935
8936 bool wxRichTextField::CanEditProperties() const
8937 {
8938 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8939 if (fieldType)
8940 return fieldType->CanEditProperties((wxRichTextField*) this);
8941
8942 return false;
8943 }
8944
8945 wxString wxRichTextField::GetPropertiesMenuLabel() const
8946 {
8947 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8948 if (fieldType)
8949 return fieldType->GetPropertiesMenuLabel((wxRichTextField*) this);
8950
8951 return wxEmptyString;
8952 }
8953
8954 bool wxRichTextField::UpdateField(wxRichTextBuffer* buffer)
8955 {
8956 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8957 if (fieldType)
8958 return fieldType->UpdateField(buffer, (wxRichTextField*) this);
8959
8960 return false;
8961 }
8962
8963 bool wxRichTextField::IsTopLevel() const
8964 {
8965 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8966 if (fieldType)
8967 return fieldType->IsTopLevel((wxRichTextField*) this);
8968
8969 return true;
8970 }
8971
8972 IMPLEMENT_CLASS(wxRichTextFieldType, wxObject)
8973
8974 IMPLEMENT_CLASS(wxRichTextFieldTypeStandard, wxRichTextFieldType)
8975
8976 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxString& label, int displayStyle)
8977 {
8978 Init();
8979
8980 SetName(name);
8981 SetLabel(label);
8982 SetDisplayStyle(displayStyle);
8983 }
8984
8985 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxBitmap& bitmap, int displayStyle)
8986 {
8987 Init();
8988
8989 SetName(name);
8990 SetBitmap(bitmap);
8991 SetDisplayStyle(displayStyle);
8992 }
8993
8994 void wxRichTextFieldTypeStandard::Init()
8995 {
8996 m_displayStyle = wxRICHTEXT_FIELD_STYLE_RECTANGLE;
8997 m_font = wxFont(6, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
8998 m_textColour = *wxWHITE;
8999 m_borderColour = *wxBLACK;
9000 m_backgroundColour = *wxBLACK;
9001 m_verticalPadding = 1;
9002 m_horizontalPadding = 3;
9003 m_horizontalMargin = 2;
9004 m_verticalMargin = 0;
9005 }
9006
9007 void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard& field)
9008 {
9009 wxRichTextFieldType::Copy(field);
9010
9011 m_label = field.m_label;
9012 m_displayStyle = field.m_displayStyle;
9013 m_font = field.m_font;
9014 m_textColour = field.m_textColour;
9015 m_borderColour = field.m_borderColour;
9016 m_backgroundColour = field.m_backgroundColour;
9017 m_verticalPadding = field.m_verticalPadding;
9018 m_horizontalPadding = field.m_horizontalPadding;
9019 m_horizontalMargin = field.m_horizontalMargin;
9020 m_bitmap = field.m_bitmap;
9021 }
9022
9023 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))
9024 {
9025 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
9026 return false; // USe default composite drawing
9027 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
9028 {
9029 int borderSize = 1;
9030
9031 wxPen borderPen(m_borderColour, 1, wxSOLID);
9032 wxBrush backgroundBrush(m_backgroundColour);
9033 wxColour textColour(m_textColour);
9034
9035 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
9036 {
9037 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
9038 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
9039
9040 borderPen = wxPen(highlightTextColour, 1, wxSOLID);
9041 backgroundBrush = wxBrush(highlightColour);
9042
9043 wxCheckSetBrush(dc, backgroundBrush);
9044 wxCheckSetPen(dc, wxPen(highlightColour, 1, wxSOLID));
9045 dc.DrawRectangle(rect);
9046 }
9047
9048 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
9049 borderSize = 0;
9050
9051 // objectRect is the area where the content is drawn, after margins around it have been taken into account
9052 wxRect objectRect = wxRect(wxPoint(rect.x + m_horizontalMargin, rect.y + wxMax(0, rect.height - descent - obj->GetCachedSize().y)),
9053 wxSize(obj->GetCachedSize().x - 2*m_horizontalMargin - borderSize, obj->GetCachedSize().y));
9054
9055 // clientArea is where the text is actually written
9056 wxRect clientArea = objectRect;
9057
9058 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE)
9059 {
9060 dc.SetPen(borderPen);
9061 dc.SetBrush(backgroundBrush);
9062 dc.DrawRoundedRectangle(objectRect, 4.0);
9063 }
9064 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG)
9065 {
9066 int arrowLength = objectRect.height/2;
9067 clientArea.width -= (arrowLength - m_horizontalPadding);
9068
9069 wxPoint pts[5];
9070 pts[0].x = objectRect.x; pts[0].y = objectRect.y;
9071 pts[1].x = objectRect.x + objectRect.width - arrowLength; pts[1].y = objectRect.y;
9072 pts[2].x = objectRect.x + objectRect.width; pts[2].y = objectRect.y + (objectRect.height/2);
9073 pts[3].x = objectRect.x + objectRect.width - arrowLength; pts[3].y = objectRect.y + objectRect.height;
9074 pts[4].x = objectRect.x; pts[4].y = objectRect.y + objectRect.height;
9075 dc.SetPen(borderPen);
9076 dc.SetBrush(backgroundBrush);
9077 dc.DrawPolygon(5, pts);
9078 }
9079 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
9080 {
9081 int arrowLength = objectRect.height/2;
9082 clientArea.width -= (arrowLength - m_horizontalPadding);
9083 clientArea.x += (arrowLength - m_horizontalPadding);
9084
9085 wxPoint pts[5];
9086 pts[0].x = objectRect.x + objectRect.width; pts[0].y = objectRect.y;
9087 pts[1].x = objectRect.x + arrowLength; pts[1].y = objectRect.y;
9088 pts[2].x = objectRect.x; pts[2].y = objectRect.y + (objectRect.height/2);
9089 pts[3].x = objectRect.x + arrowLength; pts[3].y = objectRect.y + objectRect.height;
9090 pts[4].x = objectRect.x + objectRect.width; pts[4].y = objectRect.y + objectRect.height;
9091 dc.SetPen(borderPen);
9092 dc.SetBrush(backgroundBrush);
9093 dc.DrawPolygon(5, pts);
9094 }
9095
9096 if (m_bitmap.IsOk())
9097 {
9098 int x = clientArea.x + (clientArea.width - m_bitmap.GetWidth())/2;
9099 int y = clientArea.y + m_verticalPadding;
9100 dc.DrawBitmap(m_bitmap, x, y, true);
9101
9102 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
9103 {
9104 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
9105 wxCheckSetPen(dc, *wxBLACK_PEN);
9106 dc.SetLogicalFunction(wxINVERT);
9107 dc.DrawRectangle(wxRect(x, y, m_bitmap.GetWidth(), m_bitmap.GetHeight()));
9108 dc.SetLogicalFunction(wxCOPY);
9109 }
9110 }
9111 else
9112 {
9113 wxString label(m_label);
9114 if (label.IsEmpty())
9115 label = wxT("??");
9116 int w, h, maxDescent;
9117 dc.SetFont(m_font);
9118 dc.GetTextExtent(m_label, & w, &h, & maxDescent);
9119 dc.SetTextForeground(textColour);
9120
9121 int x = clientArea.x + (clientArea.width - w)/2;
9122 int y = clientArea.y + (clientArea.height - (h - maxDescent))/2;
9123 dc.DrawText(m_label, x, y);
9124 }
9125 }
9126
9127 return true;
9128 }
9129
9130 bool wxRichTextFieldTypeStandard::Layout(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int style)
9131 {
9132 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
9133 return false; // USe default composite layout
9134
9135 wxSize size = GetSize(obj, dc, context, style);
9136 obj->SetCachedSize(size);
9137 obj->SetMinSize(size);
9138 obj->SetMaxSize(size);
9139 return true;
9140 }
9141
9142 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
9143 {
9144 if (IsTopLevel(obj))
9145 return obj->wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize);
9146 else
9147 {
9148 wxSize sz = GetSize(obj, dc, context, 0);
9149 if (partialExtents)
9150 {
9151 int lastSize;
9152 if (partialExtents->GetCount() > 0)
9153 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
9154 else
9155 lastSize = 0;
9156 partialExtents->Add(lastSize + sz.x);
9157 }
9158 size = sz;
9159 return true;
9160 }
9161 }
9162
9163 wxSize wxRichTextFieldTypeStandard::GetSize(wxRichTextField* WXUNUSED(obj), wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), int WXUNUSED(style)) const
9164 {
9165 int borderSize = 1;
9166 int w = 0, h = 0, maxDescent = 0;
9167
9168 wxSize sz;
9169 if (m_bitmap.IsOk())
9170 {
9171 w = m_bitmap.GetWidth();
9172 h = m_bitmap.GetHeight();
9173
9174 sz = wxSize(w + m_horizontalMargin*2, h + m_verticalMargin*2);
9175 }
9176 else
9177 {
9178 wxString label(m_label);
9179 if (label.IsEmpty())
9180 label = wxT("??");
9181 dc.SetFont(m_font);
9182 dc.GetTextExtent(label, & w, &h, & maxDescent);
9183
9184 sz = wxSize(w + m_horizontalPadding*2 + m_horizontalMargin*2, h + m_verticalPadding *2 + m_verticalMargin*2);
9185 }
9186
9187 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
9188 {
9189 sz.x += borderSize*2;
9190 sz.y += borderSize*2;
9191 }
9192
9193 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG || m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
9194 {
9195 // Add space for the arrow
9196 sz.x += (sz.y/2 - m_horizontalPadding);
9197 }
9198
9199 return sz;
9200 }
9201
9202 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell, wxRichTextBox)
9203
9204 wxRichTextCell::wxRichTextCell(wxRichTextObject* parent):
9205 wxRichTextBox(parent)
9206 {
9207 }
9208
9209 /// Draw the item
9210 bool wxRichTextCell::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
9211 {
9212 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
9213 }
9214
9215 /// Copy
9216 void wxRichTextCell::Copy(const wxRichTextCell& obj)
9217 {
9218 wxRichTextBox::Copy(obj);
9219 }
9220
9221 // Edit properties via a GUI
9222 bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
9223 {
9224 // We need to gather common attributes for all selected cells.
9225
9226 wxRichTextTable* table = wxDynamicCast(GetParent(), wxRichTextTable);
9227 bool multipleCells = false;
9228 wxRichTextAttr attr;
9229
9230 if (table && buffer && buffer->GetRichTextCtrl() && buffer->GetRichTextCtrl()->GetSelection().IsValid() &&
9231 buffer->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
9232 {
9233 wxRichTextAttr clashingAttr, absentAttr;
9234 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
9235 size_t i;
9236 int selectedCellCount = 0;
9237 for (i = 0; i < sel.GetCount(); i++)
9238 {
9239 const wxRichTextRange& range = sel[i];
9240 wxRichTextCell* cell = table->GetCell(range.GetStart());
9241 if (cell)
9242 {
9243 wxRichTextAttr cellStyle = cell->GetAttributes();
9244
9245 CollectStyle(attr, cellStyle, clashingAttr, absentAttr);
9246
9247 selectedCellCount ++;
9248 }
9249 }
9250 multipleCells = selectedCellCount > 1;
9251 }
9252 else
9253 {
9254 attr = GetAttributes();
9255 }
9256
9257 wxString caption;
9258 if (multipleCells)
9259 caption = _("Multiple Cell Properties");
9260 else
9261 caption = _("Cell Properties");
9262
9263 wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption);
9264 cellDlg.SetAttributes(attr);
9265
9266 wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(wxCLASSINFO(wxRichTextSizePage)), wxRichTextSizePage);
9267 if (sizePage)
9268 {
9269 // We don't want position and floating controls for a cell.
9270 sizePage->ShowPositionControls(false);
9271 sizePage->ShowFloatingControls(false);
9272 }
9273
9274 if (cellDlg.ShowModal() == wxID_OK)
9275 {
9276 if (multipleCells)
9277 {
9278 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
9279 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
9280 // since it may represent clashing attributes across multiple objects.
9281 table->SetCellStyle(sel, attr);
9282 }
9283 else
9284 // For a single object, indeterminate attributes set by the user should be reflected in the
9285 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
9286 // the style directly instead of applying (which ignores indeterminate attributes,
9287 // leaving them as they were).
9288 cellDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
9289 return true;
9290 }
9291 else
9292 return false;
9293 }
9294
9295 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray)
9296
9297 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable, wxRichTextBox)
9298
9299 wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent)
9300 {
9301 m_rowCount = 0;
9302 m_colCount = 0;
9303 }
9304
9305 // Draws the object.
9306 bool wxRichTextTable::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
9307 {
9308 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
9309 }
9310
9311 WX_DECLARE_OBJARRAY(wxRect, wxRichTextRectArray);
9312 WX_DEFINE_OBJARRAY(wxRichTextRectArray);
9313
9314 // Lays the object out. rect is the space available for layout. Often it will
9315 // be the specified overall space for this object, if trying to constrain
9316 // layout to a particular size, or it could be the total space available in the
9317 // parent. rect is the overall size, so we must subtract margins and padding.
9318 // to get the actual available space.
9319 bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int style)
9320 {
9321 SetPosition(rect.GetPosition());
9322
9323 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
9324 // minimum size if within alloted size, then divide up remaining size
9325 // between rows/cols.
9326
9327 double scale = 1.0;
9328 wxRichTextBuffer* buffer = GetBuffer();
9329 if (buffer) scale = buffer->GetScale();
9330
9331 wxRect availableSpace = GetAvailableContentArea(dc, context, rect);
9332 wxTextAttrDimensionConverter converter(dc, scale, availableSpace.GetSize());
9333
9334 wxRichTextAttr attr(GetAttributes());
9335 context.ApplyVirtualAttributes(attr, this);
9336
9337 // If we have no fixed table size, and assuming we're not pushed for
9338 // space, then we don't have to try to stretch the table to fit the contents.
9339 bool stretchToFitTableWidth = false;
9340
9341 int tableWidth = rect.width;
9342 if (attr.GetTextBoxAttr().GetWidth().IsValid())
9343 {
9344 tableWidth = converter.GetPixels(attr.GetTextBoxAttr().GetWidth());
9345
9346 // Fixed table width, so we do want to stretch columns out if necessary.
9347 stretchToFitTableWidth = true;
9348
9349 // Shouldn't be able to exceed the size passed to this function
9350 tableWidth = wxMin(rect.width, tableWidth);
9351 }
9352
9353 // Get internal padding
9354 int paddingLeft = 0, paddingTop = 0;
9355 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
9356 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
9357 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
9358 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
9359
9360 // Assume that left and top padding are also used for inter-cell padding.
9361 int paddingX = paddingLeft;
9362 int paddingY = paddingTop;
9363
9364 int totalLeftMargin = 0, totalRightMargin = 0, totalTopMargin = 0, totalBottomMargin = 0;
9365 GetTotalMargin(dc, buffer, attr, totalLeftMargin, totalRightMargin, totalTopMargin, totalBottomMargin);
9366
9367 // Internal table width - the area for content
9368 int internalTableWidth = tableWidth - totalLeftMargin - totalRightMargin;
9369
9370 int rowCount = m_cells.GetCount();
9371 if (m_colCount == 0 || rowCount == 0)
9372 {
9373 wxRect overallRect(rect.x, rect.y, totalLeftMargin + totalRightMargin, totalTopMargin + totalBottomMargin);
9374 SetCachedSize(overallRect.GetSize());
9375
9376 // Zero content size
9377 SetMinSize(overallRect.GetSize());
9378 SetMaxSize(GetMinSize());
9379 return true;
9380 }
9381
9382 // The final calculated widths
9383 wxArrayInt colWidths;
9384 colWidths.Add(0, m_colCount);
9385
9386 wxArrayInt absoluteColWidths;
9387 absoluteColWidths.Add(0, m_colCount);
9388
9389 wxArrayInt percentageColWidths;
9390 percentageColWidths.Add(0, m_colCount);
9391 // wxArrayInt percentageColWidthsSpanning(m_colCount);
9392 // These are only relevant when the first column contains spanning information.
9393 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
9394 wxArrayInt maxColWidths;
9395 maxColWidths.Add(0, m_colCount);
9396 wxArrayInt minColWidths;
9397 minColWidths.Add(0, m_colCount);
9398
9399 wxSize tableSize(tableWidth, 0);
9400
9401 int i, j, k;
9402
9403 for (i = 0; i < m_colCount; i++)
9404 {
9405 absoluteColWidths[i] = 0;
9406 // absoluteColWidthsSpanning[i] = 0;
9407 percentageColWidths[i] = -1;
9408 // percentageColWidthsSpanning[i] = -1;
9409 colWidths[i] = 0;
9410 maxColWidths[i] = 0;
9411 minColWidths[i] = 0;
9412 // columnSpans[i] = 1;
9413 }
9414
9415 // (0) Determine which cells are visible according to spans
9416 // 1 2 3 4 5
9417 // __________________
9418 // | | | | | 1
9419 // |------| |----|
9420 // |------| | | 2
9421 // |------| | | 3
9422 // |------------------|
9423 // |__________________| 4
9424
9425 // To calculate cell visibility:
9426 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9427 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9428 // that cell, hide the cell.
9429
9430 // We can also use this array to match the size of spanning cells to the grid. Or just do
9431 // this when we iterate through all cells.
9432
9433 // 0.1: add spanning cells to an array
9434 wxRichTextRectArray rectArray;
9435 for (j = 0; j < m_rowCount; j++)
9436 {
9437 for (i = 0; i < m_colCount; i++)
9438 {
9439 wxRichTextBox* cell = GetCell(j, i);
9440 int colSpan = 1, rowSpan = 1;
9441 if (cell->GetProperties().HasProperty(wxT("colspan")))
9442 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9443 if (cell->GetProperties().HasProperty(wxT("rowspan")))
9444 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
9445 if (colSpan > 1 || rowSpan > 1)
9446 {
9447 rectArray.Add(wxRect(i, j, colSpan, rowSpan));
9448 }
9449 }
9450 }
9451 // 0.2: find which cells are subsumed by a spanning cell
9452 for (j = 0; j < m_rowCount; j++)
9453 {
9454 for (i = 0; i < m_colCount; i++)
9455 {
9456 wxRichTextBox* cell = GetCell(j, i);
9457 if (rectArray.GetCount() == 0)
9458 {
9459 cell->Show(true);
9460 }
9461 else
9462 {
9463 int colSpan = 1, rowSpan = 1;
9464 if (cell->GetProperties().HasProperty(wxT("colspan")))
9465 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9466 if (cell->GetProperties().HasProperty(wxT("rowspan")))
9467 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
9468 if (colSpan > 1 || rowSpan > 1)
9469 {
9470 // Assume all spanning cells are shown
9471 cell->Show(true);
9472 }
9473 else
9474 {
9475 bool shown = true;
9476 for (k = 0; k < (int) rectArray.GetCount(); k++)
9477 {
9478 if (rectArray[k].Contains(wxPoint(i, j)))
9479 {
9480 shown = false;
9481 break;
9482 }
9483 }
9484 cell->Show(shown);
9485 }
9486 }
9487 }
9488 }
9489
9490 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
9491 // overlap with a spanned cell starting at a previous column position.
9492 // This means we need to keep an array of rects so we can check. However
9493 // it does also mean that some spans simply may not be taken into account
9494 // where there are different spans happening on different rows. In these cases,
9495 // they will simply be as wide as their constituent columns.
9496
9497 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9498 // the absolute or percentage width of each column.
9499
9500 for (j = 0; j < m_rowCount; j++)
9501 {
9502 // First get the overall margins so we can calculate percentage widths based on
9503 // the available content space for all cells on the row
9504
9505 int overallRowContentMargin = 0;
9506 int visibleCellCount = 0;
9507
9508 for (i = 0; i < m_colCount; i++)
9509 {
9510 wxRichTextBox* cell = GetCell(j, i);
9511 if (cell->IsShown())
9512 {
9513 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9514 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9515
9516 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9517 visibleCellCount ++;
9518 }
9519 }
9520
9521 // Add in inter-cell padding
9522 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9523
9524 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9525 wxSize rowTableSize(rowContentWidth, 0);
9526 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9527
9528 for (i = 0; i < m_colCount; i++)
9529 {
9530 wxRichTextBox* cell = GetCell(j, i);
9531 if (cell->IsShown())
9532 {
9533 int colSpan = 1;
9534 if (cell->GetProperties().HasProperty(wxT("colspan")))
9535 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9536
9537 // Lay out cell to find min/max widths
9538 cell->Invalidate(wxRICHTEXT_ALL);
9539 cell->Layout(dc, context, availableSpace, availableSpace, style);
9540
9541 if (colSpan == 1)
9542 {
9543 int absoluteCellWidth = -1;
9544 int percentageCellWidth = -1;
9545
9546 // I think we need to calculate percentages from the internal table size,
9547 // minus the padding between cells which we'll need to calculate from the
9548 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9549 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9550 // so if we want to conform to that we'll need to add in the overall cell margins.
9551 // However, this will make it difficult to specify percentages that add up to
9552 // 100% and still fit within the table width.
9553 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9554 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9555 // If we're using internal content size for the width, we would calculate the
9556 // the overall cell width for n cells as:
9557 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9558 // + thisOverallCellMargin
9559 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9560 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9561
9562 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9563 {
9564 int w = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9565 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
9566 {
9567 percentageCellWidth = w;
9568 }
9569 else
9570 {
9571 absoluteCellWidth = w;
9572 }
9573 // Override absolute width with minimum width if necessary
9574 if (cell->GetMinSize().x > 0 && absoluteCellWidth !=1 && cell->GetMinSize().x > absoluteCellWidth)
9575 absoluteCellWidth = cell->GetMinSize().x;
9576 }
9577
9578 if (absoluteCellWidth != -1)
9579 {
9580 if (absoluteCellWidth > absoluteColWidths[i])
9581 absoluteColWidths[i] = absoluteCellWidth;
9582 }
9583
9584 if (percentageCellWidth != -1)
9585 {
9586 if (percentageCellWidth > percentageColWidths[i])
9587 percentageColWidths[i] = percentageCellWidth;
9588 }
9589
9590 if (colSpan == 1 && cell->GetMinSize().x && cell->GetMinSize().x > minColWidths[i])
9591 minColWidths[i] = cell->GetMinSize().x;
9592 if (colSpan == 1 && cell->GetMaxSize().x && cell->GetMaxSize().x > maxColWidths[i])
9593 maxColWidths[i] = cell->GetMaxSize().x;
9594 }
9595 }
9596 }
9597 }
9598
9599 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9600 // TODO: simply merge this into (1).
9601 for (i = 0; i < m_colCount; i++)
9602 {
9603 if (absoluteColWidths[i] > 0)
9604 {
9605 colWidths[i] = absoluteColWidths[i];
9606 }
9607 else if (percentageColWidths[i] > 0)
9608 {
9609 colWidths[i] = percentageColWidths[i];
9610
9611 // This is rubbish - we calculated the absolute widths from percentages, so
9612 // we can't do it again here.
9613 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9614 }
9615 }
9616
9617 // (3) Process absolute or proportional widths of spanning columns,
9618 // now that we know what our fixed column widths are going to be.
9619 // Spanned cells will try to adjust columns so the span will fit.
9620 // Even existing fixed column widths can be expanded if necessary.
9621 // Actually, currently fixed columns widths aren't adjusted; instead,
9622 // the algorithm favours earlier rows and adjusts unspecified column widths
9623 // the first time only. After that, we can't know whether the column has been
9624 // specified explicitly or not. (We could make a note if necessary.)
9625 for (j = 0; j < m_rowCount; j++)
9626 {
9627 // First get the overall margins so we can calculate percentage widths based on
9628 // the available content space for all cells on the row
9629
9630 int overallRowContentMargin = 0;
9631 int visibleCellCount = 0;
9632
9633 for (i = 0; i < m_colCount; i++)
9634 {
9635 wxRichTextBox* cell = GetCell(j, i);
9636 if (cell->IsShown())
9637 {
9638 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9639 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9640
9641 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9642 visibleCellCount ++;
9643 }
9644 }
9645
9646 // Add in inter-cell padding
9647 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9648
9649 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9650 wxSize rowTableSize(rowContentWidth, 0);
9651 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9652
9653 for (i = 0; i < m_colCount; i++)
9654 {
9655 wxRichTextBox* cell = GetCell(j, i);
9656 if (cell->IsShown())
9657 {
9658 int colSpan = 1;
9659 if (cell->GetProperties().HasProperty(wxT("colspan")))
9660 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9661
9662 if (colSpan > 1)
9663 {
9664 int spans = wxMin(colSpan, m_colCount - i);
9665 int cellWidth = 0;
9666 if (spans > 0)
9667 {
9668 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9669 {
9670 cellWidth = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9671 // Override absolute width with minimum width if necessary
9672 if (cell->GetMinSize().x > 0 && cellWidth !=1 && cell->GetMinSize().x > cellWidth)
9673 cellWidth = cell->GetMinSize().x;
9674 }
9675 else
9676 {
9677 // Do we want to do this? It's the only chance we get to
9678 // use the cell's min/max sizes, so we need to work out
9679 // how we're going to balance the unspecified spanning cell
9680 // width with the possibility more-constrained constituent cell widths.
9681 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9682 // don't want to constraint all the spanned columns to fit into this cell.
9683 // OK, let's say that if any of the constituent columns don't fit,
9684 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9685 // cells to the columns later.
9686 cellWidth = cell->GetMinSize().x;
9687 if (cell->GetMaxSize().x > cellWidth)
9688 cellWidth = cell->GetMaxSize().x;
9689 }
9690
9691 // Subtract the padding between cells
9692 int spanningWidth = cellWidth;
9693 spanningWidth -= paddingX * (spans-1);
9694
9695 if (spanningWidth > 0)
9696 {
9697 // Now share the spanning width between columns within that span
9698 // TODO: take into account min widths of columns within the span
9699 int spanningWidthLeft = spanningWidth;
9700 int stretchColCount = 0;
9701 for (k = i; k < (i+spans); k++)
9702 {
9703 if (colWidths[k] > 0) // absolute or proportional width has been specified
9704 spanningWidthLeft -= colWidths[k];
9705 else
9706 stretchColCount ++;
9707 }
9708 // Now divide what's left between the remaining columns
9709 int colShare = 0;
9710 if (stretchColCount > 0)
9711 colShare = spanningWidthLeft / stretchColCount;
9712 int colShareRemainder = spanningWidthLeft - (colShare * stretchColCount);
9713
9714 // If fixed-width columns are currently too big, then we'll later
9715 // stretch the spanned cell to fit.
9716
9717 if (spanningWidthLeft > 0)
9718 {
9719 for (k = i; k < (i+spans); k++)
9720 {
9721 if (colWidths[k] <= 0) // absolute or proportional width has not been specified
9722 {
9723 int newWidth = colShare;
9724 if (k == (i+spans-1))
9725 newWidth += colShareRemainder; // ensure all pixels are filled
9726 colWidths[k] = newWidth;
9727 }
9728 }
9729 }
9730 }
9731 }
9732 }
9733 }
9734 }
9735 }
9736
9737 // (4) Next, share any remaining space out between columns that have not yet been calculated.
9738 // TODO: take into account min widths of columns within the span
9739 int tableWidthMinusPadding = internalTableWidth - (m_colCount-1)*paddingX;
9740 int widthLeft = tableWidthMinusPadding;
9741 int stretchColCount = 0;
9742 for (i = 0; i < m_colCount; i++)
9743 {
9744 // TODO: we need to take into account min widths.
9745 // Subtract min width from width left, then
9746 // add the colShare to the min width
9747 if (colWidths[i] > 0) // absolute or proportional width has been specified
9748 widthLeft -= colWidths[i];
9749 else
9750 {
9751 if (minColWidths[i] > 0)
9752 widthLeft -= minColWidths[i];
9753
9754 stretchColCount ++;
9755 }
9756 }
9757
9758 // Now divide what's left between the remaining columns
9759 int colShare = 0;
9760 if (stretchColCount > 0)
9761 colShare = widthLeft / stretchColCount;
9762 int colShareRemainder = widthLeft - (colShare * stretchColCount);
9763
9764 // Check we don't have enough space, in which case shrink all columns, overriding
9765 // any absolute/proportional widths
9766 // TODO: actually we would like to divide up the shrinkage according to size.
9767 // How do we calculate the proportions that will achieve this?
9768 // Could first choose an arbitrary value for stretching cells, and then calculate
9769 // factors to multiply each width by.
9770 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
9771 if (widthLeft < 0 || (stretchToFitTableWidth && (stretchColCount == 0)))
9772 {
9773 colShare = tableWidthMinusPadding / m_colCount;
9774 colShareRemainder = tableWidthMinusPadding - (colShare * m_colCount);
9775 for (i = 0; i < m_colCount; i++)
9776 {
9777 colWidths[i] = 0;
9778 minColWidths[i] = 0;
9779 }
9780 }
9781
9782 // We have to adjust the columns if either we need to shrink the
9783 // table to fit the parent/table width, or we explicitly set the
9784 // table width and need to stretch out the table.
9785 if (widthLeft < 0 || stretchToFitTableWidth)
9786 {
9787 for (i = 0; i < m_colCount; i++)
9788 {
9789 if (colWidths[i] <= 0) // absolute or proportional width has not been specified
9790 {
9791 if (minColWidths[i] > 0)
9792 colWidths[i] = minColWidths[i] + colShare;
9793 else
9794 colWidths[i] = colShare;
9795 if (i == (m_colCount-1))
9796 colWidths[i] += colShareRemainder; // ensure all pixels are filled
9797 }
9798 }
9799 }
9800
9801 // TODO: if spanned cells have no specified or max width, make them the
9802 // as big as the columns they span. Do this for all spanned cells in all
9803 // rows, of course. Size any spanned cells left over at the end - even if they
9804 // have width > 0, make sure they're limited to the appropriate column edge.
9805
9806
9807 /*
9808 Sort out confusion between content width
9809 and overall width later. For now, assume we specify overall width.
9810
9811 So, now we've laid out the table to fit into the given space
9812 and have used specified widths and minimum widths.
9813
9814 Now we need to consider how we will try to take maximum width into account.
9815
9816 */
9817
9818 // (??) TODO: take max width into account
9819
9820 // (6) Lay out all cells again with the current values
9821
9822 int maxRight = 0;
9823 int y = availableSpace.y;
9824 for (j = 0; j < m_rowCount; j++)
9825 {
9826 int x = availableSpace.x; // TODO: take into account centering etc.
9827 int maxCellHeight = 0;
9828 int maxSpecifiedCellHeight = 0;
9829
9830 wxArrayInt actualWidths;
9831 actualWidths.Add(0, m_colCount);
9832
9833 wxTextAttrDimensionConverter converter(dc, scale);
9834 for (i = 0; i < m_colCount; i++)
9835 {
9836 wxRichTextCell* cell = GetCell(j, i);
9837 if (cell->IsShown())
9838 {
9839 // Get max specified cell height
9840 // Don't handle percentages for height
9841 if (cell->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE)
9842 {
9843 int h = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetHeight());
9844 if (h > maxSpecifiedCellHeight)
9845 maxSpecifiedCellHeight = h;
9846 }
9847
9848 if (colWidths[i] > 0) // absolute or proportional width has been specified
9849 {
9850 int colSpan = 1;
9851 if (cell->GetProperties().HasProperty(wxT("colspan")))
9852 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
9853
9854 wxRect availableCellSpace;
9855
9856 // TODO: take into acount spans
9857 if (colSpan > 1)
9858 {
9859 // Calculate the size of this spanning cell from its constituent columns
9860 int xx = x;
9861 int spans = wxMin(colSpan, m_colCount - i);
9862 for (k = i; k < spans; k++)
9863 {
9864 if (k != i)
9865 xx += paddingX;
9866 xx += colWidths[k];
9867 }
9868 availableCellSpace = wxRect(x, y, xx, -1);
9869 }
9870 else
9871 availableCellSpace = wxRect(x, y, colWidths[i], -1);
9872
9873 // Store actual width so we can force cell to be the appropriate width on the final loop
9874 actualWidths[i] = availableCellSpace.GetWidth();
9875
9876 // Lay out cell
9877 cell->Invalidate(wxRICHTEXT_ALL);
9878 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
9879
9880 // TODO: use GetCachedSize().x to compute 'natural' size
9881
9882 x += (availableCellSpace.GetWidth() + paddingX);
9883 if (cell->GetCachedSize().y > maxCellHeight)
9884 maxCellHeight = cell->GetCachedSize().y;
9885 }
9886 }
9887 }
9888
9889 maxCellHeight = wxMax(maxCellHeight, maxSpecifiedCellHeight);
9890
9891 for (i = 0; i < m_colCount; i++)
9892 {
9893 wxRichTextCell* cell = GetCell(j, i);
9894 if (cell->IsShown())
9895 {
9896 wxRect availableCellSpace = wxRect(cell->GetPosition(), wxSize(actualWidths[i], maxCellHeight));
9897 // Lay out cell with new height
9898 cell->Invalidate(wxRICHTEXT_ALL);
9899 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
9900
9901 // Make sure the cell size really is the appropriate size,
9902 // not the calculated box size
9903 cell->SetCachedSize(wxSize(actualWidths[i], maxCellHeight));
9904
9905 maxRight = wxMax(maxRight, cell->GetPosition().x + cell->GetCachedSize().x);
9906 }
9907 }
9908
9909 y += maxCellHeight;
9910 if (j < (m_rowCount-1))
9911 y += paddingY;
9912 }
9913
9914 // We need to add back the margins etc.
9915 {
9916 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
9917 contentRect = wxRect(wxPoint(0, 0), wxSize(maxRight - availableSpace.x, y - availableSpace.y));
9918 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
9919 SetCachedSize(marginRect.GetSize());
9920 }
9921
9922 // TODO: calculate max size
9923 {
9924 SetMaxSize(GetCachedSize());
9925 }
9926
9927 // TODO: calculate min size
9928 {
9929 SetMinSize(GetCachedSize());
9930 }
9931
9932 // TODO: currently we use either a fixed table width or the parent's size.
9933 // We also want to be able to calculate the table width from its content,
9934 // whether using fixed column widths or cell content min/max width.
9935 // Probably need a boolean flag to say whether we need to stretch cells
9936 // to fit the table width, or to simply use min/max cell widths. The
9937 // trouble with this is that if cell widths are not specified, they
9938 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
9939 // Anyway, ignoring that problem, we probably need to factor layout into a function
9940 // that can can calculate the maximum unconstrained layout in case table size is
9941 // not specified. Then LayoutToBestSize() can choose to use either parent size to
9942 // constrain Layout(), or the previously-calculated max size to constraint layout.
9943
9944 return true;
9945 }
9946
9947 // Finds the absolute position and row height for the given character position
9948 bool wxRichTextTable::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
9949 {
9950 wxRichTextCell* child = GetCell(index+1);
9951 if (child)
9952 {
9953 // Find the position at the start of the child cell, since the table doesn't
9954 // have any caret position of its own.
9955 return child->FindPosition(dc, context, -1, pt, height, forceLineStart);
9956 }
9957 else
9958 return false;
9959 }
9960
9961 // Get the cell at the given character position (in the range of the table).
9962 wxRichTextCell* wxRichTextTable::GetCell(long pos) const
9963 {
9964 int row = 0, col = 0;
9965 if (GetCellRowColumnPosition(pos, row, col))
9966 {
9967 return GetCell(row, col);
9968 }
9969 else
9970 return NULL;
9971 }
9972
9973 // Get the row/column for a given character position
9974 bool wxRichTextTable::GetCellRowColumnPosition(long pos, int& row, int& col) const
9975 {
9976 if (m_colCount == 0 || m_rowCount == 0)
9977 return false;
9978
9979 row = (int) (pos / m_colCount);
9980 col = pos - (row * m_colCount);
9981
9982 wxASSERT(row < m_rowCount && col < m_colCount);
9983
9984 if (row < m_rowCount && col < m_colCount)
9985 return true;
9986 else
9987 return false;
9988 }
9989
9990 // Calculate range, taking row/cell ordering into account instead of relying
9991 // on list ordering.
9992 void wxRichTextTable::CalculateRange(long start, long& end)
9993 {
9994 long current = start;
9995 long lastEnd = current;
9996
9997 if (IsTopLevel())
9998 {
9999 current = 0;
10000 lastEnd = 0;
10001 }
10002
10003 int i, j;
10004 for (i = 0; i < m_rowCount; i++)
10005 {
10006 for (j = 0; j < m_colCount; j++)
10007 {
10008 wxRichTextCell* child = GetCell(i, j);
10009 if (child)
10010 {
10011 long childEnd = 0;
10012
10013 child->CalculateRange(current, childEnd);
10014
10015 lastEnd = childEnd;
10016 current = childEnd + 1;
10017 }
10018 }
10019 }
10020
10021 // A top-level object always has a range of size 1,
10022 // because its children don't count at this level.
10023 end = start;
10024 m_range.SetRange(start, start);
10025
10026 // An object with no children has zero length
10027 if (m_children.GetCount() == 0)
10028 lastEnd --;
10029 m_ownRange.SetRange(0, lastEnd);
10030 }
10031
10032 // Gets the range size.
10033 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
10034 {
10035 return wxRichTextBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize, partialExtents);
10036 }
10037
10038 // Deletes content in the given range.
10039 bool wxRichTextTable::DeleteRange(const wxRichTextRange& WXUNUSED(range))
10040 {
10041 // TODO: implement deletion of cells
10042 return true;
10043 }
10044
10045 // Gets any text in this object for the given range.
10046 wxString wxRichTextTable::GetTextForRange(const wxRichTextRange& range) const
10047 {
10048 return wxRichTextBox::GetTextForRange(range);
10049 }
10050
10051 // Copies this object.
10052 void wxRichTextTable::Copy(const wxRichTextTable& obj)
10053 {
10054 wxRichTextBox::Copy(obj);
10055
10056 ClearTable();
10057
10058 m_rowCount = obj.m_rowCount;
10059 m_colCount = obj.m_colCount;
10060
10061 m_cells.Add(wxRichTextObjectPtrArray(), m_rowCount);
10062
10063 int i, j;
10064 for (i = 0; i < m_rowCount; i++)
10065 {
10066 wxRichTextObjectPtrArray& colArray = m_cells[i];
10067 for (j = 0; j < m_colCount; j++)
10068 {
10069 wxRichTextCell* cell = wxDynamicCast(obj.GetCell(i, j)->Clone(), wxRichTextCell);
10070 AppendChild(cell);
10071
10072 colArray.Add(cell);
10073 }
10074 }
10075 }
10076
10077 void wxRichTextTable::ClearTable()
10078 {
10079 m_cells.Clear();
10080 DeleteChildren();
10081 }
10082
10083 bool wxRichTextTable::CreateTable(int rows, int cols)
10084 {
10085 ClearTable();
10086
10087 m_rowCount = rows;
10088 m_colCount = cols;
10089
10090 m_cells.Add(wxRichTextObjectPtrArray(), rows);
10091
10092 int i, j;
10093 for (i = 0; i < rows; i++)
10094 {
10095 wxRichTextObjectPtrArray& colArray = m_cells[i];
10096 for (j = 0; j < cols; j++)
10097 {
10098 wxRichTextCell* cell = new wxRichTextCell;
10099 AppendChild(cell);
10100 cell->AddParagraph(wxEmptyString);
10101
10102 colArray.Add(cell);
10103 }
10104 }
10105
10106 return true;
10107 }
10108
10109 wxRichTextCell* wxRichTextTable::GetCell(int row, int col) const
10110 {
10111 wxASSERT(row < m_rowCount);
10112 wxASSERT(col < m_colCount);
10113
10114 if (row < m_rowCount && col < m_colCount)
10115 {
10116 wxRichTextObjectPtrArray& colArray = m_cells[row];
10117 wxRichTextObject* obj = colArray[col];
10118 return wxDynamicCast(obj, wxRichTextCell);
10119 }
10120 else
10121 return NULL;
10122 }
10123
10124 // Returns a selection object specifying the selections between start and end character positions.
10125 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
10126 wxRichTextSelection wxRichTextTable::GetSelection(long start, long end) const
10127 {
10128 wxRichTextSelection selection;
10129 selection.SetContainer((wxRichTextTable*) this);
10130
10131 if (start > end)
10132 {
10133 long tmp = end;
10134 end = start;
10135 start = tmp;
10136 }
10137
10138 wxASSERT( start >= 0 && end < (m_colCount * m_rowCount));
10139
10140 if (end >= (m_colCount * m_rowCount))
10141 return selection;
10142
10143 // We need to find the rectangle of cells that is described by the rectangle
10144 // with start, end as the diagonal. Make sure we don't add cells that are
10145 // not currenty visible because they are overlapped by spanning cells.
10146 /*
10147 --------------------------
10148 | 0 | 1 | 2 | 3 | 4 |
10149 --------------------------
10150 | 5 | 6 | 7 | 8 | 9 |
10151 --------------------------
10152 | 10 | 11 | 12 | 13 | 14 |
10153 --------------------------
10154 | 15 | 16 | 17 | 18 | 19 |
10155 --------------------------
10156
10157 Let's say we select 6 -> 18.
10158
10159 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
10160 which is left and which is right.
10161
10162 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
10163
10164 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
10165 and (b) shown.
10166
10167
10168 */
10169
10170 int leftCol = start - m_colCount * int(start/m_colCount);
10171 int rightCol = end - m_colCount * int(end/m_colCount);
10172
10173 int topRow = int(start/m_colCount);
10174 int bottomRow = int(end/m_colCount);
10175
10176 if (leftCol > rightCol)
10177 {
10178 int tmp = rightCol;
10179 rightCol = leftCol;
10180 leftCol = tmp;
10181 }
10182
10183 if (topRow > bottomRow)
10184 {
10185 int tmp = bottomRow;
10186 bottomRow = topRow;
10187 topRow = tmp;
10188 }
10189
10190 int i, j;
10191 for (i = topRow; i <= bottomRow; i++)
10192 {
10193 for (j = leftCol; j <= rightCol; j++)
10194 {
10195 wxRichTextCell* cell = GetCell(i, j);
10196 if (cell && cell->IsShown())
10197 selection.Add(cell->GetRange());
10198 }
10199 }
10200
10201 return selection;
10202 }
10203
10204 // Sets the attributes for the cells specified by the selection.
10205 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection& selection, const wxRichTextAttr& style, int flags)
10206 {
10207 if (selection.GetContainer() != this)
10208 return false;
10209
10210 wxRichTextBuffer* buffer = GetBuffer();
10211 bool haveControl = (buffer && buffer->GetRichTextCtrl() != NULL);
10212 bool withUndo = haveControl && ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
10213
10214 if (withUndo)
10215 buffer->BeginBatchUndo(_("Set Cell Style"));
10216
10217 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
10218 while (node)
10219 {
10220 wxRichTextCell* cell = wxDynamicCast(node->GetData(), wxRichTextCell);
10221 if (cell && selection.WithinSelection(cell->GetRange().GetStart()))
10222 SetStyle(cell, style, flags);
10223 node = node->GetNext();
10224 }
10225
10226 // Do action, or delay it until end of batch.
10227 if (withUndo)
10228 buffer->EndBatchUndo();
10229
10230 return true;
10231 }
10232
10233 bool wxRichTextTable::DeleteRows(int startRow, int noRows)
10234 {
10235 wxASSERT((startRow + noRows) < m_rowCount);
10236 if ((startRow + noRows) >= m_rowCount)
10237 return false;
10238
10239 int i, j;
10240 for (i = startRow; i < (startRow+noRows); i++)
10241 {
10242 wxRichTextObjectPtrArray& colArray = m_cells[startRow];
10243 for (j = 0; j < (int) colArray.GetCount(); j++)
10244 {
10245 wxRichTextObject* cell = colArray[j];
10246 RemoveChild(cell, true);
10247 }
10248
10249 // Keep deleting at the same position, since we move all
10250 // the others up
10251 m_cells.RemoveAt(startRow);
10252 }
10253
10254 m_rowCount = m_rowCount - noRows;
10255
10256 return true;
10257 }
10258
10259 bool wxRichTextTable::DeleteColumns(int startCol, int noCols)
10260 {
10261 wxASSERT((startCol + noCols) < m_colCount);
10262 if ((startCol + noCols) >= m_colCount)
10263 return false;
10264
10265 bool deleteRows = (noCols == m_colCount);
10266
10267 int i, j;
10268 for (i = 0; i < m_rowCount; i++)
10269 {
10270 wxRichTextObjectPtrArray& colArray = m_cells[deleteRows ? 0 : i];
10271 for (j = startCol; j < (startCol+noCols); j++)
10272 {
10273 wxRichTextObject* cell = colArray[j];
10274 RemoveChild(cell, true);
10275 }
10276
10277 if (deleteRows)
10278 m_cells.RemoveAt(0);
10279 }
10280
10281 if (deleteRows)
10282 m_rowCount = 0;
10283 m_colCount = m_colCount - noCols;
10284
10285 return true;
10286 }
10287
10288 bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& attr)
10289 {
10290 wxASSERT(startRow <= m_rowCount);
10291 if (startRow > m_rowCount)
10292 return false;
10293
10294 int i, j;
10295 for (i = 0; i < noRows; i++)
10296 {
10297 int idx;
10298 if (startRow == m_rowCount)
10299 {
10300 m_cells.Add(wxRichTextObjectPtrArray());
10301 idx = m_cells.GetCount() - 1;
10302 }
10303 else
10304 {
10305 m_cells.Insert(wxRichTextObjectPtrArray(), startRow+i);
10306 idx = startRow+i;
10307 }
10308
10309 wxRichTextObjectPtrArray& colArray = m_cells[idx];
10310 for (j = 0; j < m_colCount; j++)
10311 {
10312 wxRichTextCell* cell = new wxRichTextCell;
10313 cell->GetAttributes() = attr;
10314
10315 AppendChild(cell);
10316 colArray.Add(cell);
10317 }
10318 }
10319
10320 m_rowCount = m_rowCount + noRows;
10321 return true;
10322 }
10323
10324 bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& attr)
10325 {
10326 wxASSERT(startCol <= m_colCount);
10327 if (startCol > m_colCount)
10328 return false;
10329
10330 int i, j;
10331 for (i = 0; i < m_rowCount; i++)
10332 {
10333 wxRichTextObjectPtrArray& colArray = m_cells[i];
10334 for (j = 0; j < noCols; j++)
10335 {
10336 wxRichTextCell* cell = new wxRichTextCell;
10337 cell->GetAttributes() = attr;
10338
10339 AppendChild(cell);
10340
10341 if (startCol == m_colCount)
10342 colArray.Add(cell);
10343 else
10344 colArray.Insert(cell, startCol+j);
10345 }
10346 }
10347
10348 m_colCount = m_colCount + noCols;
10349
10350 return true;
10351 }
10352
10353 // Edit properties via a GUI
10354 bool wxRichTextTable::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
10355 {
10356 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Table Properties"));
10357 boxDlg.SetAttributes(GetAttributes());
10358
10359 if (boxDlg.ShowModal() == wxID_OK)
10360 {
10361 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
10362 return true;
10363 }
10364 else
10365 return false;
10366 }
10367
10368 /*
10369 * Module to initialise and clean up handlers
10370 */
10371
10372 class wxRichTextModule: public wxModule
10373 {
10374 DECLARE_DYNAMIC_CLASS(wxRichTextModule)
10375 public:
10376 wxRichTextModule() {}
10377 bool OnInit()
10378 {
10379 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer);
10380 wxRichTextBuffer::InitStandardHandlers();
10381 wxRichTextParagraph::InitDefaultTabs();
10382
10383 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
10384 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
10385 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
10386 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
10387 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
10388 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
10389 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
10390 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
10391 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
10392
10393 return true;
10394 }
10395 void OnExit()
10396 {
10397 wxRichTextBuffer::CleanUpHandlers();
10398 wxRichTextBuffer::CleanUpDrawingHandlers();
10399 wxRichTextBuffer::CleanUpFieldTypes();
10400 wxRichTextXMLHandler::ClearNodeToClassMap();
10401 wxRichTextDecimalToRoman(-1);
10402 wxRichTextParagraph::ClearDefaultTabs();
10403 wxRichTextCtrl::ClearAvailableFontNames();
10404 wxRichTextBuffer::SetRenderer(NULL);
10405 }
10406 };
10407
10408 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule)
10409
10410
10411 // If the richtext lib is dynamically loaded after the app has already started
10412 // (such as from wxPython) then the built-in module system will not init this
10413 // module. Provide this function to do it manually.
10414 void wxRichTextModuleInit()
10415 {
10416 wxModule* module = new wxRichTextModule;
10417 module->Init();
10418 wxModule::RegisterModule(module);
10419 }
10420
10421
10422 /*!
10423 * Commands for undo/redo
10424 *
10425 */
10426
10427 wxRichTextCommand::wxRichTextCommand(const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
10428 wxRichTextParagraphLayoutBox* container, wxRichTextCtrl* ctrl, bool ignoreFirstTime): wxCommand(true, name)
10429 {
10430 /* wxRichTextAction* action = */ new wxRichTextAction(this, name, id, buffer, container, ctrl, ignoreFirstTime);
10431 }
10432
10433 wxRichTextCommand::wxRichTextCommand(const wxString& name): wxCommand(true, name)
10434 {
10435 }
10436
10437 wxRichTextCommand::~wxRichTextCommand()
10438 {
10439 ClearActions();
10440 }
10441
10442 void wxRichTextCommand::AddAction(wxRichTextAction* action)
10443 {
10444 if (!m_actions.Member(action))
10445 m_actions.Append(action);
10446 }
10447
10448 bool wxRichTextCommand::Do()
10449 {
10450 for (wxList::compatibility_iterator node = m_actions.GetFirst(); node; node = node->GetNext())
10451 {
10452 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10453 action->Do();
10454 }
10455
10456 return true;
10457 }
10458
10459 bool wxRichTextCommand::Undo()
10460 {
10461 for (wxList::compatibility_iterator node = m_actions.GetLast(); node; node = node->GetPrevious())
10462 {
10463 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10464 action->Undo();
10465 }
10466
10467 return true;
10468 }
10469
10470 void wxRichTextCommand::ClearActions()
10471 {
10472 WX_CLEAR_LIST(wxList, m_actions);
10473 }
10474
10475 /*!
10476 * Individual action
10477 *
10478 */
10479
10480 wxRichTextAction::wxRichTextAction(wxRichTextCommand* cmd, const wxString& name, wxRichTextCommandId id,
10481 wxRichTextBuffer* buffer, wxRichTextParagraphLayoutBox* container,
10482 wxRichTextCtrl* ctrl, bool ignoreFirstTime)
10483 {
10484 m_buffer = buffer;
10485 m_object = NULL;
10486 m_containerAddress.Create(buffer, container);
10487 m_ignoreThis = ignoreFirstTime;
10488 m_cmdId = id;
10489 m_position = -1;
10490 m_ctrl = ctrl;
10491 m_name = name;
10492 m_newParagraphs.SetDefaultStyle(buffer->GetDefaultStyle());
10493 m_newParagraphs.SetBasicStyle(buffer->GetBasicStyle());
10494 if (cmd)
10495 cmd->AddAction(this);
10496 }
10497
10498 wxRichTextAction::~wxRichTextAction()
10499 {
10500 if (m_object)
10501 delete m_object;
10502 }
10503
10504 // Returns the container that this action refers to, using the container address and top-level buffer.
10505 wxRichTextParagraphLayoutBox* wxRichTextAction::GetContainer() const
10506 {
10507 wxRichTextParagraphLayoutBox* container = wxDynamicCast(GetContainerAddress().GetObject(m_buffer), wxRichTextParagraphLayoutBox);
10508 return container;
10509 }
10510
10511
10512 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions)
10513 {
10514 // Store a list of line start character and y positions so we can figure out which area
10515 // we need to refresh
10516
10517 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10518 wxRichTextParagraphLayoutBox* container = GetContainer();
10519 wxASSERT(container != NULL);
10520 if (!container)
10521 return;
10522
10523 // NOTE: we're assuming that the buffer is laid out correctly at this point.
10524 // If we had several actions, which only invalidate and leave layout until the
10525 // paint handler is called, then this might not be true. So we may need to switch
10526 // optimisation on only when we're simply adding text and not simultaneously
10527 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
10528 // first, but of course this means we'll be doing it twice.
10529 if (!m_buffer->IsDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
10530 {
10531 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
10532 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
10533 int lastY = firstVisiblePt.y + clientSize.y;
10534
10535 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetRange().GetStart());
10536 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
10537 while (node)
10538 {
10539 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10540 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10541 while (node2)
10542 {
10543 wxRichTextLine* line = node2->GetData();
10544 wxPoint pt = line->GetAbsolutePosition();
10545 wxRichTextRange range = line->GetAbsoluteRange();
10546
10547 if (pt.y > lastY)
10548 {
10549 node2 = wxRichTextLineList::compatibility_iterator();
10550 node = wxRichTextObjectList::compatibility_iterator();
10551 }
10552 else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
10553 {
10554 optimizationLineCharPositions.Add(range.GetStart());
10555 optimizationLineYPositions.Add(pt.y);
10556 }
10557
10558 if (node2)
10559 node2 = node2->GetNext();
10560 }
10561
10562 if (node)
10563 node = node->GetNext();
10564 }
10565 }
10566 #endif
10567 }
10568
10569 bool wxRichTextAction::Do()
10570 {
10571 m_buffer->Modify(true);
10572
10573 wxRichTextParagraphLayoutBox* container = GetContainer();
10574 wxASSERT(container != NULL);
10575 if (!container)
10576 return false;
10577
10578 switch (m_cmdId)
10579 {
10580 case wxRICHTEXT_INSERT:
10581 {
10582 // Store a list of line start character and y positions so we can figure out which area
10583 // we need to refresh
10584 wxArrayInt optimizationLineCharPositions;
10585 wxArrayInt optimizationLineYPositions;
10586
10587 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10588 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10589 #endif
10590
10591 container->InsertFragment(GetRange().GetStart(), m_newParagraphs);
10592 container->UpdateRanges();
10593
10594 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10595 // Layout() would stop prematurely at the top level.
10596 container->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
10597
10598 long newCaretPosition = GetPosition() + m_newParagraphs.GetOwnRange().GetLength();
10599
10600 // Character position to caret position
10601 newCaretPosition --;
10602
10603 // Don't take into account the last newline
10604 if (m_newParagraphs.GetPartialParagraph())
10605 newCaretPosition --;
10606 else
10607 if (m_newParagraphs.GetChildren().GetCount() > 1)
10608 {
10609 wxRichTextObject* p = (wxRichTextObject*) m_newParagraphs.GetChildren().GetLast()->GetData();
10610 if (p->GetRange().GetLength() == 1)
10611 newCaretPosition --;
10612 }
10613
10614 newCaretPosition = wxMin(newCaretPosition, (container->GetOwnRange().GetEnd()-1));
10615
10616 UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
10617
10618 wxRichTextEvent cmdEvent(
10619 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
10620 m_ctrl ? m_ctrl->GetId() : -1);
10621 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10622 cmdEvent.SetRange(GetRange());
10623 cmdEvent.SetPosition(GetRange().GetStart());
10624 cmdEvent.SetContainer(container);
10625
10626 m_buffer->SendEvent(cmdEvent);
10627
10628 break;
10629 }
10630 case wxRICHTEXT_DELETE:
10631 {
10632 wxArrayInt optimizationLineCharPositions;
10633 wxArrayInt optimizationLineYPositions;
10634
10635 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10636 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10637 #endif
10638
10639 container->DeleteRange(GetRange());
10640 container->UpdateRanges();
10641 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10642 // Layout() would stop prematurely at the top level.
10643 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10644
10645 long caretPos = GetRange().GetStart()-1;
10646 if (caretPos >= container->GetOwnRange().GetEnd())
10647 caretPos --;
10648
10649 UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
10650
10651 wxRichTextEvent cmdEvent(
10652 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
10653 m_ctrl ? m_ctrl->GetId() : -1);
10654 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10655 cmdEvent.SetRange(GetRange());
10656 cmdEvent.SetPosition(GetRange().GetStart());
10657 cmdEvent.SetContainer(container);
10658
10659 m_buffer->SendEvent(cmdEvent);
10660
10661 break;
10662 }
10663 case wxRICHTEXT_CHANGE_STYLE:
10664 case wxRICHTEXT_CHANGE_PROPERTIES:
10665 {
10666 ApplyParagraphs(GetNewParagraphs());
10667
10668 // Invalidate the whole buffer if there were floating objects
10669 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
10670 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
10671 else
10672 {
10673 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10674 // Layout() would stop prematurely at the top level.
10675 container->InvalidateHierarchy(GetRange());
10676 }
10677
10678 UpdateAppearance(GetPosition());
10679
10680 wxRichTextEvent cmdEvent(
10681 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
10682 m_ctrl ? m_ctrl->GetId() : -1);
10683 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10684 cmdEvent.SetRange(GetRange());
10685 cmdEvent.SetPosition(GetRange().GetStart());
10686 cmdEvent.SetContainer(container);
10687
10688 m_buffer->SendEvent(cmdEvent);
10689
10690 break;
10691 }
10692 case wxRICHTEXT_CHANGE_ATTRIBUTES:
10693 {
10694 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer); // container->GetChildAtPosition(GetRange().GetStart());
10695 if (obj)
10696 {
10697 wxRichTextAttr oldAttr = obj->GetAttributes();
10698 obj->GetAttributes() = m_attributes;
10699 m_attributes = oldAttr;
10700 }
10701
10702 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10703 // Layout() would stop prematurely at the top level.
10704 // Invalidate the whole buffer if there were floating objects
10705 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
10706 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
10707 else
10708 container->InvalidateHierarchy(GetRange());
10709
10710 UpdateAppearance(GetPosition());
10711
10712 wxRichTextEvent cmdEvent(
10713 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED,
10714 m_ctrl ? m_ctrl->GetId() : -1);
10715 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10716 cmdEvent.SetRange(GetRange());
10717 cmdEvent.SetPosition(GetRange().GetStart());
10718 cmdEvent.SetContainer(container);
10719
10720 m_buffer->SendEvent(cmdEvent);
10721
10722 break;
10723 }
10724 case wxRICHTEXT_CHANGE_OBJECT:
10725 {
10726 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer);
10727 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
10728 if (obj && m_object)
10729 {
10730 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(obj);
10731 if (node)
10732 {
10733 wxRichTextObject* obj = node->GetData();
10734 node->SetData(m_object);
10735 m_object = obj;
10736 }
10737 }
10738
10739 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10740 // Layout() would stop prematurely at the top level.
10741 // Invalidate the whole buffer if there were floating objects
10742 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
10743 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
10744 else
10745 container->InvalidateHierarchy(GetRange());
10746
10747 UpdateAppearance(GetPosition());
10748
10749 // TODO: send new kind of modification event
10750
10751 break;
10752 }
10753 default:
10754 break;
10755 }
10756
10757 return true;
10758 }
10759
10760 bool wxRichTextAction::Undo()
10761 {
10762 m_buffer->Modify(true);
10763
10764 wxRichTextParagraphLayoutBox* container = GetContainer();
10765 wxASSERT(container != NULL);
10766 if (!container)
10767 return false;
10768
10769 switch (m_cmdId)
10770 {
10771 case wxRICHTEXT_INSERT:
10772 {
10773 wxArrayInt optimizationLineCharPositions;
10774 wxArrayInt optimizationLineYPositions;
10775
10776 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10777 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10778 #endif
10779
10780 container->DeleteRange(GetRange());
10781 container->UpdateRanges();
10782
10783 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10784 // Layout() would stop prematurely at the top level.
10785 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
10786
10787 long newCaretPosition = GetPosition() - 1;
10788
10789 UpdateAppearance(newCaretPosition, true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
10790
10791 wxRichTextEvent cmdEvent(
10792 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
10793 m_ctrl ? m_ctrl->GetId() : -1);
10794 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10795 cmdEvent.SetRange(GetRange());
10796 cmdEvent.SetPosition(GetRange().GetStart());
10797 cmdEvent.SetContainer(container);
10798
10799 m_buffer->SendEvent(cmdEvent);
10800
10801 break;
10802 }
10803 case wxRICHTEXT_DELETE:
10804 {
10805 wxArrayInt optimizationLineCharPositions;
10806 wxArrayInt optimizationLineYPositions;
10807
10808 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10809 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
10810 #endif
10811
10812 container->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
10813 container->UpdateRanges();
10814
10815 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10816 // Layout() would stop prematurely at the top level.
10817 container->InvalidateHierarchy(GetRange());
10818
10819 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
10820
10821 wxRichTextEvent cmdEvent(
10822 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
10823 m_ctrl ? m_ctrl->GetId() : -1);
10824 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10825 cmdEvent.SetRange(GetRange());
10826 cmdEvent.SetPosition(GetRange().GetStart());
10827 cmdEvent.SetContainer(container);
10828
10829 m_buffer->SendEvent(cmdEvent);
10830
10831 break;
10832 }
10833 case wxRICHTEXT_CHANGE_STYLE:
10834 case wxRICHTEXT_CHANGE_PROPERTIES:
10835 {
10836 ApplyParagraphs(GetOldParagraphs());
10837 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
10838 // Layout() would stop prematurely at the top level.
10839 container->InvalidateHierarchy(GetRange());
10840
10841 UpdateAppearance(GetPosition());
10842
10843 wxRichTextEvent cmdEvent(
10844 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
10845 m_ctrl ? m_ctrl->GetId() : -1);
10846 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
10847 cmdEvent.SetRange(GetRange());
10848 cmdEvent.SetPosition(GetRange().GetStart());
10849 cmdEvent.SetContainer(container);
10850
10851 m_buffer->SendEvent(cmdEvent);
10852
10853 break;
10854 }
10855 case wxRICHTEXT_CHANGE_ATTRIBUTES:
10856 case wxRICHTEXT_CHANGE_OBJECT:
10857 {
10858 return Do();
10859 }
10860 default:
10861 break;
10862 }
10863
10864 return true;
10865 }
10866
10867 /// Update the control appearance
10868 void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions, bool isDoCmd)
10869 {
10870 wxRichTextParagraphLayoutBox* container = GetContainer();
10871 wxASSERT(container != NULL);
10872 if (!container)
10873 return;
10874
10875 if (m_ctrl)
10876 {
10877 m_ctrl->SetFocusObject(container);
10878 m_ctrl->SetCaretPosition(caretPosition);
10879
10880 if (!m_ctrl->IsFrozen())
10881 {
10882 wxRect containerRect = container->GetRect();
10883
10884 m_ctrl->LayoutContent();
10885
10886 // Refresh everything if there were floating objects or the container changed size
10887 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
10888 if ((wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0) || (container->GetParent() && containerRect != container->GetRect()))
10889 {
10890 m_ctrl->Refresh(false);
10891 }
10892 else
10893
10894 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
10895 // Find refresh rectangle if we are in a position to optimise refresh
10896 if ((m_cmdId == wxRICHTEXT_INSERT || m_cmdId == wxRICHTEXT_DELETE) && optimizationLineCharPositions)
10897 {
10898 size_t i;
10899
10900 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
10901 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
10902
10903 // Start/end positions
10904 int firstY = 0;
10905 int lastY = firstVisiblePt.y + clientSize.y;
10906
10907 bool foundEnd = false;
10908
10909 // position offset - how many characters were inserted
10910 int positionOffset = GetRange().GetLength();
10911
10912 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
10913 if ((m_cmdId == wxRICHTEXT_DELETE && isDoCmd) || (m_cmdId == wxRICHTEXT_INSERT && !isDoCmd))
10914 positionOffset = - positionOffset;
10915
10916 // find the first line which is being drawn at the same position as it was
10917 // before. Since we're talking about a simple insertion, we can assume
10918 // that the rest of the window does not need to be redrawn.
10919
10920 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetPosition());
10921 // Since we support floating layout, we should redraw the whole para instead of just
10922 // the first line touching the invalid range.
10923 if (para)
10924 {
10925 firstY = para->GetPosition().y;
10926 }
10927
10928 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
10929 while (node)
10930 {
10931 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
10932 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
10933 while (node2)
10934 {
10935 wxRichTextLine* line = node2->GetData();
10936 wxPoint pt = line->GetAbsolutePosition();
10937 wxRichTextRange range = line->GetAbsoluteRange();
10938
10939 // we want to find the first line that is in the same position
10940 // as before. This will mean we're at the end of the changed text.
10941
10942 if (pt.y > lastY) // going past the end of the window, no more info
10943 {
10944 node2 = wxRichTextLineList::compatibility_iterator();
10945 node = wxRichTextObjectList::compatibility_iterator();
10946 }
10947 // Detect last line in the buffer
10948 else if (!node2->GetNext() && para->GetRange().Contains(container->GetOwnRange().GetEnd()))
10949 {
10950 // If deleting text, make sure we refresh below as well as above
10951 if (positionOffset >= 0)
10952 {
10953 foundEnd = true;
10954 lastY = pt.y + line->GetSize().y;
10955 }
10956
10957 node2 = wxRichTextLineList::compatibility_iterator();
10958 node = wxRichTextObjectList::compatibility_iterator();
10959
10960 break;
10961 }
10962 else
10963 {
10964 // search for this line being at the same position as before
10965 for (i = 0; i < optimizationLineCharPositions->GetCount(); i++)
10966 {
10967 if (((*optimizationLineCharPositions)[i] + positionOffset == range.GetStart()) &&
10968 ((*optimizationLineYPositions)[i] == pt.y))
10969 {
10970 // Stop, we're now the same as we were
10971 foundEnd = true;
10972
10973 lastY = pt.y;
10974
10975 node2 = wxRichTextLineList::compatibility_iterator();
10976 node = wxRichTextObjectList::compatibility_iterator();
10977
10978 break;
10979 }
10980 }
10981 }
10982
10983 if (node2)
10984 node2 = node2->GetNext();
10985 }
10986
10987 if (node)
10988 node = node->GetNext();
10989 }
10990
10991 firstY = wxMax(firstVisiblePt.y, firstY);
10992 if (!foundEnd)
10993 lastY = firstVisiblePt.y + clientSize.y;
10994
10995 // Convert to device coordinates
10996 wxRect rect(m_ctrl->GetPhysicalPoint(m_ctrl->GetScaledPoint(wxPoint(firstVisiblePt.x, firstY))), m_ctrl->GetScaledSize(wxSize(clientSize.x, lastY - firstY)));
10997 m_ctrl->RefreshRect(rect);
10998 }
10999 else
11000 #endif
11001 m_ctrl->Refresh(false);
11002
11003 m_ctrl->PositionCaret();
11004
11005 // This causes styles to persist when doing programmatic
11006 // content creation except when Freeze/Thaw is used, so
11007 // disable this and check for the consequences.
11008 // m_ctrl->SetDefaultStyleToCursorStyle();
11009
11010 if (sendUpdateEvent)
11011 wxTextCtrl::SendTextUpdatedEvent(m_ctrl);
11012 }
11013 }
11014 }
11015
11016 /// Replace the buffer paragraphs with the new ones.
11017 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment)
11018 {
11019 wxRichTextParagraphLayoutBox* container = GetContainer();
11020 wxASSERT(container != NULL);
11021 if (!container)
11022 return;
11023
11024 wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst();
11025 while (node)
11026 {
11027 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
11028 wxASSERT (para != NULL);
11029
11030 // We'll replace the existing paragraph by finding the paragraph at this position,
11031 // delete its node data, and setting a copy as the new node data.
11032 // TODO: make more efficient by simply swapping old and new paragraph objects.
11033
11034 wxRichTextParagraph* existingPara = container->GetParagraphAtPosition(para->GetRange().GetStart());
11035 if (existingPara)
11036 {
11037 wxRichTextObjectList::compatibility_iterator bufferParaNode = container->GetChildren().Find(existingPara);
11038 if (bufferParaNode)
11039 {
11040 wxRichTextParagraph* newPara = new wxRichTextParagraph(*para);
11041 newPara->SetParent(container);
11042
11043 bufferParaNode->SetData(newPara);
11044
11045 delete existingPara;
11046 }
11047 }
11048
11049 node = node->GetNext();
11050 }
11051 }
11052
11053
11054 /*!
11055 * wxRichTextRange
11056 * This stores beginning and end positions for a range of data.
11057 */
11058
11059 WX_DEFINE_OBJARRAY(wxRichTextRangeArray);
11060
11061 /// Limit this range to be within 'range'
11062 bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
11063 {
11064 if (m_start < range.m_start)
11065 m_start = range.m_start;
11066
11067 if (m_end > range.m_end)
11068 m_end = range.m_end;
11069
11070 return true;
11071 }
11072
11073 /*!
11074 * wxRichTextImage implementation
11075 * This object represents an image.
11076 */
11077
11078 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
11079
11080 wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle):
11081 wxRichTextObject(parent)
11082 {
11083 Init();
11084 m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG);
11085 if (charStyle)
11086 SetAttributes(*charStyle);
11087 }
11088
11089 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle):
11090 wxRichTextObject(parent)
11091 {
11092 Init();
11093 m_imageBlock = imageBlock;
11094 if (charStyle)
11095 SetAttributes(*charStyle);
11096 }
11097
11098 wxRichTextImage::~wxRichTextImage()
11099 {
11100 }
11101
11102 void wxRichTextImage::Init()
11103 {
11104 m_originalImageSize = wxSize(-1, -1);
11105 }
11106
11107 /// Create a cached image at the required size
11108 bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache, const wxSize& parentSize)
11109 {
11110 if (!m_imageBlock.IsOk())
11111 return false;
11112
11113 // If we have an original image size, use that to compute the cached bitmap size
11114 // instead of loading the image each time. This way we can avoid loading
11115 // the image so long as the new cached bitmap size hasn't changed.
11116
11117 wxImage image;
11118 if (resetCache || m_originalImageSize.GetWidth() <= 0 || m_originalImageSize.GetHeight() <= 0)
11119 {
11120 m_imageCache = wxNullBitmap;
11121
11122 m_imageBlock.Load(image);
11123 if (!image.IsOk())
11124 return false;
11125
11126 m_originalImageSize = wxSize(image.GetWidth(), image.GetHeight());
11127 }
11128
11129 int width = m_originalImageSize.GetWidth();
11130 int height = m_originalImageSize.GetHeight();
11131
11132 int parentWidth = 0;
11133 int parentHeight = 0;
11134
11135 int maxWidth = -1;
11136 int maxHeight = -1;
11137
11138 wxSize sz = parentSize;
11139 if (sz == wxDefaultSize)
11140 {
11141 if (GetParent() && GetParent()->GetParent())
11142 sz = GetParent()->GetParent()->GetCachedSize();
11143 }
11144
11145 if (sz != wxDefaultSize)
11146 {
11147 wxRichTextBuffer* buffer = GetBuffer();
11148 if (buffer)
11149 {
11150 // Find the actual space available when margin is taken into account
11151 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11152 marginRect = wxRect(0, 0, sz.x, sz.y);
11153 if (GetParent() && GetParent()->GetParent())
11154 {
11155 buffer->GetBoxRects(dc, buffer, GetParent()->GetParent()->GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
11156 sz = contentRect.GetSize();
11157 }
11158
11159 // Use a minimum size to stop images becoming very small
11160 parentWidth = wxMax(100, sz.GetWidth());
11161 parentHeight = wxMax(100, sz.GetHeight());
11162
11163 if (buffer->GetRichTextCtrl())
11164 // Start with a maximum width of the control size, even if not specified by the content,
11165 // to minimize the amount of picture overlapping the right-hand side
11166 maxWidth = parentWidth;
11167 }
11168 }
11169
11170 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
11171 {
11172 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11173 width = (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth)/100.0);
11174 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11175 width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
11176 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11177 width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
11178 }
11179
11180 // Limit to max width
11181
11182 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
11183 {
11184 int mw = -1;
11185
11186 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11187 mw = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth)/100.0);
11188 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11189 mw = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
11190 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11191 mw = GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
11192
11193 // If we already have a smaller max width due to the constraints of the control size,
11194 // don't use the larger max width.
11195 if (mw != -1 && ((maxWidth == -1) || (mw < maxWidth)))
11196 maxWidth = mw;
11197 }
11198
11199 if (maxWidth > 0 && width > maxWidth)
11200 width = maxWidth;
11201
11202 // Preserve the aspect ratio
11203 if (width != m_originalImageSize.GetWidth())
11204 height = (int) (float(m_originalImageSize.GetHeight()) * (float(width)/float(m_originalImageSize.GetWidth())));
11205
11206 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
11207 {
11208 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11209 height = (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight)/100.0);
11210 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11211 height = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
11212 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11213 height = GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
11214
11215 // Preserve the aspect ratio
11216 if (height != m_originalImageSize.GetHeight())
11217 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
11218 }
11219
11220 // Limit to max height
11221
11222 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
11223 {
11224 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11225 maxHeight = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight)/100.0);
11226 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11227 maxHeight = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
11228 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11229 maxHeight = GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
11230 }
11231
11232 if (maxHeight > 0 && height > maxHeight)
11233 {
11234 height = maxHeight;
11235
11236 // Preserve the aspect ratio
11237 if (height != m_originalImageSize.GetHeight())
11238 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
11239 }
11240
11241 // Prevent the use of zero size
11242 width = wxMax(1, width);
11243 height = wxMax(1, height);
11244
11245 if (m_imageCache.IsOk() && m_imageCache.GetWidth() == width && m_imageCache.GetHeight() == height)
11246 {
11247 // Do nothing, we didn't need to change the image cache
11248 }
11249 else
11250 {
11251 if (!image.IsOk())
11252 {
11253 m_imageBlock.Load(image);
11254 if (!image.IsOk())
11255 return false;
11256 }
11257
11258 if (image.GetWidth() == width && image.GetHeight() == height)
11259 m_imageCache = wxBitmap(image);
11260 else
11261 {
11262 // If the original width and height is small, e.g. 400 or below,
11263 // scale up and then down to improve image quality. This can make
11264 // a big difference, with not much performance hit.
11265 int upscaleThreshold = 400;
11266 wxImage img;
11267 if (image.GetWidth() <= upscaleThreshold || image.GetHeight() <= upscaleThreshold)
11268 {
11269 img = image.Scale(image.GetWidth()*2, image.GetHeight()*2);
11270 img.Rescale(width, height, wxIMAGE_QUALITY_HIGH);
11271 }
11272 else
11273 img = image.Scale(width, height, wxIMAGE_QUALITY_HIGH);
11274 m_imageCache = wxBitmap(img);
11275 }
11276 }
11277
11278 return m_imageCache.IsOk();
11279 }
11280
11281 /// Draw the item
11282 bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
11283 {
11284 if (!IsShown())
11285 return true;
11286
11287 // Don't need cached size AFAIK
11288 // wxSize size = GetCachedSize();
11289 if (!LoadImageCache(dc))
11290 return false;
11291
11292 wxRichTextAttr attr(GetAttributes());
11293 context.ApplyVirtualAttributes(attr, this);
11294
11295 DrawBoxAttributes(dc, GetBuffer(), attr, wxRect(rect.GetPosition(), GetCachedSize()));
11296
11297 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11298 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11299 marginRect = rect; // outer rectangle, will calculate contentRect
11300 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
11301
11302 dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true);
11303
11304 if (selection.WithinSelection(GetRange().GetStart(), this))
11305 {
11306 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
11307 wxCheckSetPen(dc, *wxBLACK_PEN);
11308 dc.SetLogicalFunction(wxINVERT);
11309 dc.DrawRectangle(contentRect);
11310 dc.SetLogicalFunction(wxCOPY);
11311 }
11312
11313 return true;
11314 }
11315
11316 /// Lay the item out
11317 bool wxRichTextImage::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
11318 {
11319 if (!LoadImageCache(dc))
11320 return false;
11321
11322 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11323 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11324 contentRect = wxRect(wxPoint(0,0), imageSize);
11325
11326 wxRichTextAttr attr(GetAttributes());
11327 context.ApplyVirtualAttributes(attr, this);
11328
11329 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
11330
11331 wxSize overallSize = marginRect.GetSize();
11332
11333 SetCachedSize(overallSize);
11334 SetMaxSize(overallSize);
11335 SetMinSize(overallSize);
11336 SetPosition(rect.GetPosition());
11337
11338 return true;
11339 }
11340
11341 /// Get/set the object size for the given range. Returns false if the range
11342 /// is invalid for this object.
11343 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
11344 {
11345 if (!range.IsWithin(GetRange()))
11346 return false;
11347
11348 if (!((wxRichTextImage*)this)->LoadImageCache(dc, false, parentSize))
11349 {
11350 size.x = 0; size.y = 0;
11351 if (partialExtents)
11352 partialExtents->Add(0);
11353 return false;
11354 }
11355
11356 wxRichTextAttr attr(GetAttributes());
11357 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
11358
11359 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11360 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11361 contentRect = wxRect(wxPoint(0,0), imageSize);
11362 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
11363
11364 wxSize overallSize = marginRect.GetSize();
11365
11366 if (partialExtents)
11367 partialExtents->Add(overallSize.x);
11368
11369 size = overallSize;
11370
11371 return true;
11372 }
11373
11374 // Get the 'natural' size for an object. For an image, it would be the
11375 // image size.
11376 wxTextAttrSize wxRichTextImage::GetNaturalSize() const
11377 {
11378 wxTextAttrSize size;
11379 if (GetImageCache().IsOk())
11380 {
11381 size.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
11382 size.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS);
11383 }
11384 return size;
11385 }
11386
11387
11388 /// Copy
11389 void wxRichTextImage::Copy(const wxRichTextImage& obj)
11390 {
11391 wxRichTextObject::Copy(obj);
11392
11393 m_imageBlock = obj.m_imageBlock;
11394 m_originalImageSize = obj.m_originalImageSize;
11395 }
11396
11397 /// Edit properties via a GUI
11398 bool wxRichTextImage::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
11399 {
11400 wxRichTextObjectPropertiesDialog imageDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Picture Properties"));
11401 imageDlg.SetAttributes(GetAttributes());
11402
11403 if (imageDlg.ShowModal() == wxID_OK)
11404 {
11405 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
11406 // indeterminate in the object.
11407 imageDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
11408 return true;
11409 }
11410 else
11411 return false;
11412 }
11413
11414 /*!
11415 * Utilities
11416 *
11417 */
11418
11419 /// Compare two attribute objects
11420 bool wxTextAttrEq(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
11421 {
11422 return (attr1 == attr2);
11423 }
11424
11425 /// Compare tabs
11426 bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2)
11427 {
11428 if (tabs1.GetCount() != tabs2.GetCount())
11429 return false;
11430
11431 size_t i;
11432 for (i = 0; i < tabs1.GetCount(); i++)
11433 {
11434 if (tabs1[i] != tabs2[i])
11435 return false;
11436 }
11437 return true;
11438 }
11439
11440 bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
11441 {
11442 return destStyle.Apply(style, compareWith);
11443 }
11444
11445 // Remove attributes
11446 bool wxRichTextRemoveStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style)
11447 {
11448 return destStyle.RemoveStyle(style);
11449 }
11450
11451 /// Combine two bitlists, specifying the bits of interest with separate flags.
11452 bool wxRichTextCombineBitlists(int& valueA, int valueB, int& flagsA, int flagsB)
11453 {
11454 return wxRichTextAttr::CombineBitlists(valueA, valueB, flagsA, flagsB);
11455 }
11456
11457 /// Compare two bitlists
11458 bool wxRichTextBitlistsEqPartial(int valueA, int valueB, int flags)
11459 {
11460 return wxRichTextAttr::BitlistsEqPartial(valueA, valueB, flags);
11461 }
11462
11463 /// Split into paragraph and character styles
11464 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr& style, wxRichTextAttr& parStyle, wxRichTextAttr& charStyle)
11465 {
11466 return wxRichTextAttr::SplitParaCharStyles(style, parStyle, charStyle);
11467 }
11468
11469 /// Convert a decimal to Roman numerals
11470 wxString wxRichTextDecimalToRoman(long n)
11471 {
11472 static wxArrayInt decimalNumbers;
11473 static wxArrayString romanNumbers;
11474
11475 // Clean up arrays
11476 if (n == -1)
11477 {
11478 decimalNumbers.Clear();
11479 romanNumbers.Clear();
11480 return wxEmptyString;
11481 }
11482
11483 if (decimalNumbers.GetCount() == 0)
11484 {
11485 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
11486
11487 wxRichTextAddDecRom(1000, wxT("M"));
11488 wxRichTextAddDecRom(900, wxT("CM"));
11489 wxRichTextAddDecRom(500, wxT("D"));
11490 wxRichTextAddDecRom(400, wxT("CD"));
11491 wxRichTextAddDecRom(100, wxT("C"));
11492 wxRichTextAddDecRom(90, wxT("XC"));
11493 wxRichTextAddDecRom(50, wxT("L"));
11494 wxRichTextAddDecRom(40, wxT("XL"));
11495 wxRichTextAddDecRom(10, wxT("X"));
11496 wxRichTextAddDecRom(9, wxT("IX"));
11497 wxRichTextAddDecRom(5, wxT("V"));
11498 wxRichTextAddDecRom(4, wxT("IV"));
11499 wxRichTextAddDecRom(1, wxT("I"));
11500 }
11501
11502 int i = 0;
11503 wxString roman;
11504
11505 while (n > 0 && i < 13)
11506 {
11507 if (n >= decimalNumbers[i])
11508 {
11509 n -= decimalNumbers[i];
11510 roman += romanNumbers[i];
11511 }
11512 else
11513 {
11514 i ++;
11515 }
11516 }
11517 if (roman.IsEmpty())
11518 roman = wxT("0");
11519 return roman;
11520 }
11521
11522 /*!
11523 * wxRichTextFileHandler
11524 * Base class for file handlers
11525 */
11526
11527 IMPLEMENT_CLASS(wxRichTextFileHandler, wxObject)
11528
11529 #if wxUSE_FFILE && wxUSE_STREAMS
11530 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer *buffer, const wxString& filename)
11531 {
11532 wxFFileInputStream stream(filename);
11533 if (stream.IsOk())
11534 return LoadFile(buffer, stream);
11535
11536 return false;
11537 }
11538
11539 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer *buffer, const wxString& filename)
11540 {
11541 wxFFileOutputStream stream(filename);
11542 if (stream.IsOk())
11543 return SaveFile(buffer, stream);
11544
11545 return false;
11546 }
11547 #endif // wxUSE_FFILE && wxUSE_STREAMS
11548
11549 /// Can we handle this filename (if using files)? By default, checks the extension.
11550 bool wxRichTextFileHandler::CanHandle(const wxString& filename) const
11551 {
11552 wxString path, file, ext;
11553 wxFileName::SplitPath(filename, & path, & file, & ext);
11554
11555 return (ext.Lower() == GetExtension());
11556 }
11557
11558 /*!
11559 * wxRichTextTextHandler
11560 * Plain text handler
11561 */
11562
11563 IMPLEMENT_CLASS(wxRichTextPlainTextHandler, wxRichTextFileHandler)
11564
11565 #if wxUSE_STREAMS
11566 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
11567 {
11568 if (!stream.IsOk())
11569 return false;
11570
11571 wxString str;
11572 int lastCh = 0;
11573
11574 while (!stream.Eof())
11575 {
11576 int ch = stream.GetC();
11577
11578 if (!stream.Eof())
11579 {
11580 if (ch == 10 && lastCh != 13)
11581 str += wxT('\n');
11582
11583 if (ch > 0 && ch != 10)
11584 str += wxChar(ch);
11585
11586 lastCh = ch;
11587 }
11588 }
11589
11590 buffer->ResetAndClearCommands();
11591 buffer->Clear();
11592 buffer->AddParagraphs(str);
11593 buffer->UpdateRanges();
11594
11595 return true;
11596 }
11597
11598 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
11599 {
11600 if (!stream.IsOk())
11601 return false;
11602
11603 wxString text = buffer->GetText();
11604
11605 wxString newLine = wxRichTextLineBreakChar;
11606 text.Replace(newLine, wxT("\n"));
11607
11608 wxCharBuffer buf = text.ToAscii();
11609
11610 stream.Write((const char*) buf, text.length());
11611 return true;
11612 }
11613 #endif // wxUSE_STREAMS
11614
11615 /*
11616 * Stores information about an image, in binary in-memory form
11617 */
11618
11619 wxRichTextImageBlock::wxRichTextImageBlock()
11620 {
11621 Init();
11622 }
11623
11624 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock& block):wxObject()
11625 {
11626 Init();
11627 Copy(block);
11628 }
11629
11630 wxRichTextImageBlock::~wxRichTextImageBlock()
11631 {
11632 wxDELETEA(m_data);
11633 }
11634
11635 void wxRichTextImageBlock::Init()
11636 {
11637 m_data = NULL;
11638 m_dataSize = 0;
11639 m_imageType = wxBITMAP_TYPE_INVALID;
11640 }
11641
11642 void wxRichTextImageBlock::Clear()
11643 {
11644 wxDELETEA(m_data);
11645 m_dataSize = 0;
11646 m_imageType = wxBITMAP_TYPE_INVALID;
11647 }
11648
11649
11650 // Load the original image into a memory block.
11651 // If the image is not a JPEG, we must convert it into a JPEG
11652 // to conserve space.
11653 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
11654 // load the image a 2nd time.
11655
11656 bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, wxBitmapType imageType,
11657 wxImage& image, bool convertToJPEG)
11658 {
11659 m_imageType = imageType;
11660
11661 wxString filenameToRead(filename);
11662 bool removeFile = false;
11663
11664 if (imageType == wxBITMAP_TYPE_INVALID)
11665 return false; // Could not determine image type
11666
11667 if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
11668 {
11669 wxString tempFile =
11670 wxFileName::CreateTempFileName(_("image"));
11671
11672 wxASSERT(!tempFile.IsEmpty());
11673
11674 image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
11675 filenameToRead = tempFile;
11676 removeFile = true;
11677
11678 m_imageType = wxBITMAP_TYPE_JPEG;
11679 }
11680 wxFile file;
11681 if (!file.Open(filenameToRead))
11682 return false;
11683
11684 m_dataSize = (size_t) file.Length();
11685 file.Close();
11686
11687 if (m_data)
11688 delete[] m_data;
11689 m_data = ReadBlock(filenameToRead, m_dataSize);
11690
11691 if (removeFile)
11692 wxRemoveFile(filenameToRead);
11693
11694 return (m_data != NULL);
11695 }
11696
11697 // Make an image block from the wxImage in the given
11698 // format.
11699 bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, wxBitmapType imageType, int quality)
11700 {
11701 image.SetOption(wxT("quality"), quality);
11702
11703 if (imageType == wxBITMAP_TYPE_INVALID)
11704 return false; // Could not determine image type
11705
11706 return DoMakeImageBlock(image, imageType);
11707 }
11708
11709 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
11710 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage& image, wxBitmapType imageType)
11711 {
11712 if (imageType == wxBITMAP_TYPE_INVALID)
11713 return false; // Could not determine image type
11714
11715 return DoMakeImageBlock(image, imageType);
11716 }
11717
11718 // Makes the image block
11719 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage& image, wxBitmapType imageType)
11720 {
11721 wxMemoryOutputStream memStream;
11722 if (!image.SaveFile(memStream, imageType))
11723 {
11724 return false;
11725 }
11726
11727 unsigned char* block = new unsigned char[memStream.GetSize()];
11728 if (!block)
11729 return false;
11730
11731 if (m_data)
11732 delete[] m_data;
11733 m_data = block;
11734
11735 m_imageType = imageType;
11736 m_dataSize = memStream.GetSize();
11737
11738 memStream.CopyTo(m_data, m_dataSize);
11739
11740 return (m_data != NULL);
11741 }
11742
11743 // Write to a file
11744 bool wxRichTextImageBlock::Write(const wxString& filename)
11745 {
11746 return WriteBlock(filename, m_data, m_dataSize);
11747 }
11748
11749 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock& block)
11750 {
11751 m_imageType = block.m_imageType;
11752 wxDELETEA(m_data);
11753 m_dataSize = block.m_dataSize;
11754 if (m_dataSize == 0)
11755 return;
11756
11757 m_data = new unsigned char[m_dataSize];
11758 unsigned int i;
11759 for (i = 0; i < m_dataSize; i++)
11760 m_data[i] = block.m_data[i];
11761 }
11762
11763 //// Operators
11764 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock& block)
11765 {
11766 Copy(block);
11767 }
11768
11769 // Load a wxImage from the block
11770 bool wxRichTextImageBlock::Load(wxImage& image)
11771 {
11772 if (!m_data)
11773 return false;
11774
11775 // Read in the image.
11776 #if wxUSE_STREAMS
11777 wxMemoryInputStream mstream(m_data, m_dataSize);
11778 bool success = image.LoadFile(mstream, GetImageType());
11779 #else
11780 wxString tempFile = wxFileName::CreateTempFileName(_("image"));
11781 wxASSERT(!tempFile.IsEmpty());
11782
11783 if (!WriteBlock(tempFile, m_data, m_dataSize))
11784 {
11785 return false;
11786 }
11787 success = image.LoadFile(tempFile, GetImageType());
11788 wxRemoveFile(tempFile);
11789 #endif
11790
11791 return success;
11792 }
11793
11794 // Write data in hex to a stream
11795 bool wxRichTextImageBlock::WriteHex(wxOutputStream& stream)
11796 {
11797 if (m_dataSize == 0)
11798 return true;
11799
11800 int bufSize = 100000;
11801 if (int(2*m_dataSize) < bufSize)
11802 bufSize = 2*m_dataSize;
11803 char* buf = new char[bufSize+1];
11804
11805 int left = m_dataSize;
11806 int n, i, j;
11807 j = 0;
11808 while (left > 0)
11809 {
11810 if (left*2 > bufSize)
11811 {
11812 n = bufSize; left -= (bufSize/2);
11813 }
11814 else
11815 {
11816 n = left*2; left = 0;
11817 }
11818
11819 char* b = buf;
11820 for (i = 0; i < (n/2); i++)
11821 {
11822 wxDecToHex(m_data[j], b, b+1);
11823 b += 2; j ++;
11824 }
11825
11826 buf[n] = 0;
11827 stream.Write((const char*) buf, n);
11828 }
11829 delete[] buf;
11830 return true;
11831 }
11832
11833 // Read data in hex from a stream
11834 bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, wxBitmapType imageType)
11835 {
11836 int dataSize = length/2;
11837
11838 if (m_data)
11839 delete[] m_data;
11840
11841 // create a null terminated temporary string:
11842 char str[3];
11843 str[2] = '\0';
11844
11845 m_data = new unsigned char[dataSize];
11846 int i;
11847 for (i = 0; i < dataSize; i ++)
11848 {
11849 str[0] = (char)stream.GetC();
11850 str[1] = (char)stream.GetC();
11851
11852 m_data[i] = (unsigned char)wxHexToDec(str);
11853 }
11854
11855 m_dataSize = dataSize;
11856 m_imageType = imageType;
11857
11858 return true;
11859 }
11860
11861 // Allocate and read from stream as a block of memory
11862 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream& stream, size_t size)
11863 {
11864 unsigned char* block = new unsigned char[size];
11865 if (!block)
11866 return NULL;
11867
11868 stream.Read(block, size);
11869
11870 return block;
11871 }
11872
11873 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString& filename, size_t size)
11874 {
11875 wxFileInputStream stream(filename);
11876 if (!stream.IsOk())
11877 return NULL;
11878
11879 return ReadBlock(stream, size);
11880 }
11881
11882 // Write memory block to stream
11883 bool wxRichTextImageBlock::WriteBlock(wxOutputStream& stream, unsigned char* block, size_t size)
11884 {
11885 stream.Write((void*) block, size);
11886 return stream.IsOk();
11887
11888 }
11889
11890 // Write memory block to file
11891 bool wxRichTextImageBlock::WriteBlock(const wxString& filename, unsigned char* block, size_t size)
11892 {
11893 wxFileOutputStream outStream(filename);
11894 if (!outStream.IsOk())
11895 return false;
11896
11897 return WriteBlock(outStream, block, size);
11898 }
11899
11900 // Gets the extension for the block's type
11901 wxString wxRichTextImageBlock::GetExtension() const
11902 {
11903 wxImageHandler* handler = wxImage::FindHandler(GetImageType());
11904 if (handler)
11905 return handler->GetExtension();
11906 else
11907 return wxEmptyString;
11908 }
11909
11910 #if wxUSE_DATAOBJ
11911
11912 /*!
11913 * The data object for a wxRichTextBuffer
11914 */
11915
11916 const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxRichText");
11917
11918 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer)
11919 {
11920 m_richTextBuffer = richTextBuffer;
11921
11922 // this string should uniquely identify our format, but is otherwise
11923 // arbitrary
11924 m_formatRichTextBuffer.SetId(GetRichTextBufferFormatId());
11925
11926 SetFormat(m_formatRichTextBuffer);
11927 }
11928
11929 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
11930 {
11931 delete m_richTextBuffer;
11932 }
11933
11934 // after a call to this function, the richTextBuffer is owned by the caller and it
11935 // is responsible for deleting it!
11936 wxRichTextBuffer* wxRichTextBufferDataObject::GetRichTextBuffer()
11937 {
11938 wxRichTextBuffer* richTextBuffer = m_richTextBuffer;
11939 m_richTextBuffer = NULL;
11940
11941 return richTextBuffer;
11942 }
11943
11944 wxDataFormat wxRichTextBufferDataObject::GetPreferredFormat(Direction WXUNUSED(dir)) const
11945 {
11946 return m_formatRichTextBuffer;
11947 }
11948
11949 size_t wxRichTextBufferDataObject::GetDataSize() const
11950 {
11951 if (!m_richTextBuffer)
11952 return 0;
11953
11954 wxString bufXML;
11955
11956 {
11957 wxStringOutputStream stream(& bufXML);
11958 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
11959 {
11960 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11961 return 0;
11962 }
11963 }
11964
11965 #if wxUSE_UNICODE
11966 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
11967 return strlen(buffer) + 1;
11968 #else
11969 return bufXML.Length()+1;
11970 #endif
11971 }
11972
11973 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf) const
11974 {
11975 if (!pBuf || !m_richTextBuffer)
11976 return false;
11977
11978 wxString bufXML;
11979
11980 {
11981 wxStringOutputStream stream(& bufXML);
11982 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
11983 {
11984 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
11985 return 0;
11986 }
11987 }
11988
11989 #if wxUSE_UNICODE
11990 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
11991 size_t len = strlen(buffer);
11992 memcpy((char*) pBuf, (const char*) buffer, len);
11993 ((char*) pBuf)[len] = 0;
11994 #else
11995 size_t len = bufXML.Length();
11996 memcpy((char*) pBuf, (const char*) bufXML.c_str(), len);
11997 ((char*) pBuf)[len] = 0;
11998 #endif
11999
12000 return true;
12001 }
12002
12003 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len), const void *buf)
12004 {
12005 wxDELETE(m_richTextBuffer);
12006
12007 wxString bufXML((const char*) buf, wxConvUTF8);
12008
12009 m_richTextBuffer = new wxRichTextBuffer;
12010
12011 wxStringInputStream stream(bufXML);
12012 if (!m_richTextBuffer->LoadFile(stream, wxRICHTEXT_TYPE_XML))
12013 {
12014 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
12015
12016 wxDELETE(m_richTextBuffer);
12017
12018 return false;
12019 }
12020 return true;
12021 }
12022
12023 #endif
12024 // wxUSE_DATAOBJ
12025
12026
12027 /*
12028 * wxRichTextFontTable
12029 * Manages quick access to a pool of fonts for rendering rich text
12030 */
12031
12032 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont, wxRichTextFontTableHashMap, class WXDLLIMPEXP_RICHTEXT);
12033
12034 class wxRichTextFontTableData: public wxObjectRefData
12035 {
12036 public:
12037 wxRichTextFontTableData() {}
12038
12039 wxFont FindFont(const wxRichTextAttr& fontSpec, double fontScale);
12040
12041 wxRichTextFontTableHashMap m_hashMap;
12042 };
12043
12044 wxFont wxRichTextFontTableData::FindFont(const wxRichTextAttr& fontSpec, double fontScale)
12045 {
12046 wxString facename(fontSpec.GetFontFaceName());
12047
12048 int fontSize = fontSpec.GetFontSize();
12049 if (fontScale != 1.0)
12050 fontSize = (int) ((double(fontSize) * fontScale) + 0.5);
12051
12052 wxString units;
12053 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
12054 units = wxT("px");
12055 else
12056 units = wxT("pt");
12057 wxString spec = wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
12058 fontSize, units.c_str(), fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), (int) fontSpec.GetFontUnderlined(), (int) fontSpec.GetFontStrikethrough(),
12059 facename.c_str(), (int) fontSpec.GetFontEncoding());
12060
12061 wxRichTextFontTableHashMap::iterator entry = m_hashMap.find(spec);
12062 if ( entry == m_hashMap.end() )
12063 {
12064 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
12065 {
12066 wxFont font(wxSize(0, fontSize), wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename);
12067 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
12068 font.SetStrikethrough(true);
12069 m_hashMap[spec] = font;
12070 return font;
12071 }
12072 else
12073 {
12074 wxFont font(fontSize, wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename.c_str());
12075 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
12076 font.SetStrikethrough(true);
12077
12078 m_hashMap[spec] = font;
12079 return font;
12080 }
12081 }
12082 else
12083 {
12084 return entry->second;
12085 }
12086 }
12087
12088 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable, wxObject)
12089
12090 wxRichTextFontTable::wxRichTextFontTable()
12091 {
12092 m_refData = new wxRichTextFontTableData;
12093 m_fontScale = 1.0;
12094 }
12095
12096 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table)
12097 : wxObject()
12098 {
12099 (*this) = table;
12100 }
12101
12102 wxRichTextFontTable::~wxRichTextFontTable()
12103 {
12104 UnRef();
12105 }
12106
12107 bool wxRichTextFontTable::operator == (const wxRichTextFontTable& table) const
12108 {
12109 return (m_refData == table.m_refData);
12110 }
12111
12112 void wxRichTextFontTable::operator= (const wxRichTextFontTable& table)
12113 {
12114 Ref(table);
12115 m_fontScale = table.m_fontScale;
12116 }
12117
12118 wxFont wxRichTextFontTable::FindFont(const wxRichTextAttr& fontSpec)
12119 {
12120 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
12121 if (data)
12122 return data->FindFont(fontSpec, m_fontScale);
12123 else
12124 return wxFont();
12125 }
12126
12127 void wxRichTextFontTable::Clear()
12128 {
12129 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
12130 if (data)
12131 data->m_hashMap.clear();
12132 }
12133
12134 void wxRichTextFontTable::SetFontScale(double fontScale)
12135 {
12136 if (fontScale != m_fontScale)
12137 Clear();
12138 m_fontScale = fontScale;
12139 }
12140
12141 // wxTextBoxAttr
12142
12143 void wxTextBoxAttr::Reset()
12144 {
12145 m_flags = 0;
12146 m_floatMode = wxTEXT_BOX_ATTR_FLOAT_NONE;
12147 m_clearMode = wxTEXT_BOX_ATTR_CLEAR_NONE;
12148 m_collapseMode = wxTEXT_BOX_ATTR_COLLAPSE_NONE;
12149 m_verticalAlignment = wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE;
12150 m_boxStyleName = wxEmptyString;
12151
12152 m_margins.Reset();
12153 m_padding.Reset();
12154 m_position.Reset();
12155
12156 m_size.Reset();
12157 m_minSize.Reset();
12158 m_maxSize.Reset();
12159
12160 m_border.Reset();
12161 m_outline.Reset();
12162 }
12163
12164 // Equality test
12165 bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const
12166 {
12167 return (
12168 m_flags == attr.m_flags &&
12169 m_floatMode == attr.m_floatMode &&
12170 m_clearMode == attr.m_clearMode &&
12171 m_collapseMode == attr.m_collapseMode &&
12172 m_verticalAlignment == attr.m_verticalAlignment &&
12173
12174 m_margins == attr.m_margins &&
12175 m_padding == attr.m_padding &&
12176 m_position == attr.m_position &&
12177
12178 m_size == attr.m_size &&
12179 m_minSize == attr.m_minSize &&
12180 m_maxSize == attr.m_maxSize &&
12181
12182 m_border == attr.m_border &&
12183 m_outline == attr.m_outline &&
12184
12185 m_boxStyleName == attr.m_boxStyleName
12186 );
12187 }
12188
12189 // Partial equality test
12190 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr, bool weakTest) const
12191 {
12192 if (!weakTest &&
12193 ((!HasFloatMode() && attr.HasFloatMode()) ||
12194 (!HasClearMode() && attr.HasClearMode()) ||
12195 (!HasCollapseBorders() && attr.HasCollapseBorders()) ||
12196 (!HasVerticalAlignment() && attr.HasVerticalAlignment()) ||
12197 (!HasBoxStyleName() && attr.HasBoxStyleName())))
12198 {
12199 return false;
12200 }
12201 if (attr.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr.GetFloatMode()))
12202 return false;
12203
12204 if (attr.HasClearMode() && HasClearMode() && (GetClearMode() != attr.GetClearMode()))
12205 return false;
12206
12207 if (attr.HasCollapseBorders() && HasCollapseBorders() && (attr.GetCollapseBorders() != GetCollapseBorders()))
12208 return false;
12209
12210 if (attr.HasVerticalAlignment() && HasVerticalAlignment() && (attr.GetVerticalAlignment() != GetVerticalAlignment()))
12211 return false;
12212
12213 if (attr.HasBoxStyleName() && HasBoxStyleName() && (attr.GetBoxStyleName() != GetBoxStyleName()))
12214 return false;
12215
12216 // Position
12217
12218 if (!m_position.EqPartial(attr.m_position, weakTest))
12219 return false;
12220
12221 // Size
12222
12223 if (!m_size.EqPartial(attr.m_size, weakTest))
12224 return false;
12225 if (!m_minSize.EqPartial(attr.m_minSize, weakTest))
12226 return false;
12227 if (!m_maxSize.EqPartial(attr.m_maxSize, weakTest))
12228 return false;
12229
12230 // Margins
12231
12232 if (!m_margins.EqPartial(attr.m_margins, weakTest))
12233 return false;
12234
12235 // Padding
12236
12237 if (!m_padding.EqPartial(attr.m_padding, weakTest))
12238 return false;
12239
12240 // Border
12241
12242 if (!GetBorder().EqPartial(attr.GetBorder(), weakTest))
12243 return false;
12244
12245 // Outline
12246
12247 if (!GetOutline().EqPartial(attr.GetOutline(), weakTest))
12248 return false;
12249
12250 return true;
12251 }
12252
12253 // Merges the given attributes. If compareWith
12254 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12255 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12256 bool wxTextBoxAttr::Apply(const wxTextBoxAttr& attr, const wxTextBoxAttr* compareWith)
12257 {
12258 if (attr.HasFloatMode())
12259 {
12260 if (!(compareWith && compareWith->HasFloatMode() && compareWith->GetFloatMode() == attr.GetFloatMode()))
12261 SetFloatMode(attr.GetFloatMode());
12262 }
12263
12264 if (attr.HasClearMode())
12265 {
12266 if (!(compareWith && compareWith->HasClearMode() && compareWith->GetClearMode() == attr.GetClearMode()))
12267 SetClearMode(attr.GetClearMode());
12268 }
12269
12270 if (attr.HasCollapseBorders())
12271 {
12272 if (!(compareWith && compareWith->HasCollapseBorders() && compareWith->GetCollapseBorders() == attr.GetCollapseBorders()))
12273 SetCollapseBorders(attr.GetCollapseBorders());
12274 }
12275
12276 if (attr.HasVerticalAlignment())
12277 {
12278 if (!(compareWith && compareWith->HasVerticalAlignment() && compareWith->GetVerticalAlignment() == attr.GetVerticalAlignment()))
12279 SetVerticalAlignment(attr.GetVerticalAlignment());
12280 }
12281
12282 if (attr.HasBoxStyleName())
12283 {
12284 if (!(compareWith && compareWith->HasBoxStyleName() && compareWith->GetBoxStyleName() == attr.GetBoxStyleName()))
12285 SetBoxStyleName(attr.GetBoxStyleName());
12286 }
12287
12288 m_margins.Apply(attr.m_margins, compareWith ? (& attr.m_margins) : (const wxTextAttrDimensions*) NULL);
12289 m_padding.Apply(attr.m_padding, compareWith ? (& attr.m_padding) : (const wxTextAttrDimensions*) NULL);
12290 m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextAttrDimensions*) NULL);
12291
12292 m_size.Apply(attr.m_size, compareWith ? (& attr.m_size) : (const wxTextAttrSize*) NULL);
12293 m_minSize.Apply(attr.m_minSize, compareWith ? (& attr.m_minSize) : (const wxTextAttrSize*) NULL);
12294 m_maxSize.Apply(attr.m_maxSize, compareWith ? (& attr.m_maxSize) : (const wxTextAttrSize*) NULL);
12295
12296 m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextAttrBorders*) NULL);
12297 m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextAttrBorders*) NULL);
12298
12299 return true;
12300 }
12301
12302 // Remove specified attributes from this object
12303 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr& attr)
12304 {
12305 if (attr.HasFloatMode())
12306 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
12307
12308 if (attr.HasClearMode())
12309 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
12310
12311 if (attr.HasCollapseBorders())
12312 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12313
12314 if (attr.HasVerticalAlignment())
12315 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12316
12317 if (attr.HasBoxStyleName())
12318 {
12319 SetBoxStyleName(wxEmptyString);
12320 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12321 }
12322
12323 m_margins.RemoveStyle(attr.m_margins);
12324 m_padding.RemoveStyle(attr.m_padding);
12325 m_position.RemoveStyle(attr.m_position);
12326
12327 m_size.RemoveStyle(attr.m_size);
12328 m_minSize.RemoveStyle(attr.m_minSize);
12329 m_maxSize.RemoveStyle(attr.m_maxSize);
12330
12331 m_border.RemoveStyle(attr.m_border);
12332 m_outline.RemoveStyle(attr.m_outline);
12333
12334 return true;
12335 }
12336
12337 // Collects the attributes that are common to a range of content, building up a note of
12338 // which attributes are absent in some objects and which clash in some objects.
12339 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBoxAttr& clashingAttr, wxTextBoxAttr& absentAttr)
12340 {
12341 if (attr.HasFloatMode())
12342 {
12343 if (!clashingAttr.HasFloatMode() && !absentAttr.HasFloatMode())
12344 {
12345 if (HasFloatMode())
12346 {
12347 if (GetFloatMode() != attr.GetFloatMode())
12348 {
12349 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
12350 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
12351 }
12352 }
12353 else
12354 SetFloatMode(attr.GetFloatMode());
12355 }
12356 }
12357 else
12358 absentAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
12359
12360 if (attr.HasClearMode())
12361 {
12362 if (!clashingAttr.HasClearMode() && !absentAttr.HasClearMode())
12363 {
12364 if (HasClearMode())
12365 {
12366 if (GetClearMode() != attr.GetClearMode())
12367 {
12368 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
12369 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
12370 }
12371 }
12372 else
12373 SetClearMode(attr.GetClearMode());
12374 }
12375 }
12376 else
12377 absentAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
12378
12379 if (attr.HasCollapseBorders())
12380 {
12381 if (!clashingAttr.HasCollapseBorders() && !absentAttr.HasCollapseBorders())
12382 {
12383 if (HasCollapseBorders())
12384 {
12385 if (GetCollapseBorders() != attr.GetCollapseBorders())
12386 {
12387 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12388 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12389 }
12390 }
12391 else
12392 SetCollapseBorders(attr.GetCollapseBorders());
12393 }
12394 }
12395 else
12396 absentAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12397
12398 if (attr.HasVerticalAlignment())
12399 {
12400 if (!clashingAttr.HasVerticalAlignment() && !absentAttr.HasVerticalAlignment())
12401 {
12402 if (HasVerticalAlignment())
12403 {
12404 if (GetVerticalAlignment() != attr.GetVerticalAlignment())
12405 {
12406 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12407 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12408 }
12409 }
12410 else
12411 SetVerticalAlignment(attr.GetVerticalAlignment());
12412 }
12413 }
12414 else
12415 absentAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12416
12417 if (attr.HasBoxStyleName())
12418 {
12419 if (!clashingAttr.HasBoxStyleName() && !absentAttr.HasBoxStyleName())
12420 {
12421 if (HasBoxStyleName())
12422 {
12423 if (GetBoxStyleName() != attr.GetBoxStyleName())
12424 {
12425 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12426 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12427 }
12428 }
12429 else
12430 SetBoxStyleName(attr.GetBoxStyleName());
12431 }
12432 }
12433 else
12434 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12435
12436 m_margins.CollectCommonAttributes(attr.m_margins, clashingAttr.m_margins, absentAttr.m_margins);
12437 m_padding.CollectCommonAttributes(attr.m_padding, clashingAttr.m_padding, absentAttr.m_padding);
12438 m_position.CollectCommonAttributes(attr.m_position, clashingAttr.m_position, absentAttr.m_position);
12439
12440 m_size.CollectCommonAttributes(attr.m_size, clashingAttr.m_size, absentAttr.m_size);
12441 m_minSize.CollectCommonAttributes(attr.m_minSize, clashingAttr.m_minSize, absentAttr.m_minSize);
12442 m_maxSize.CollectCommonAttributes(attr.m_maxSize, clashingAttr.m_maxSize, absentAttr.m_maxSize);
12443
12444 m_border.CollectCommonAttributes(attr.m_border, clashingAttr.m_border, absentAttr.m_border);
12445 m_outline.CollectCommonAttributes(attr.m_outline, clashingAttr.m_outline, absentAttr.m_outline);
12446 }
12447
12448 bool wxTextBoxAttr::IsDefault() const
12449 {
12450 return GetFlags() == 0 && !m_border.IsValid() && !m_outline.IsValid() &&
12451 !m_size.IsValid() && !m_minSize.IsValid() && !m_maxSize.IsValid() &&
12452 !m_position.IsValid() && !m_padding.IsValid() && !m_margins.IsValid();
12453 }
12454
12455 // wxRichTextAttr
12456
12457 void wxRichTextAttr::Copy(const wxRichTextAttr& attr)
12458 {
12459 wxTextAttr::Copy(attr);
12460
12461 m_textBoxAttr = attr.m_textBoxAttr;
12462 }
12463
12464 bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const
12465 {
12466 if (!(wxTextAttr::operator==(attr)))
12467 return false;
12468
12469 return (m_textBoxAttr == attr.m_textBoxAttr);
12470 }
12471
12472 // Partial equality test
12473 bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr, bool weakTest) const
12474 {
12475 if (!(wxTextAttr::EqPartial(attr, weakTest)))
12476 return false;
12477
12478 return m_textBoxAttr.EqPartial(attr.m_textBoxAttr, weakTest);
12479 }
12480
12481 // Merges the given attributes. If compareWith
12482 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12483 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12484 bool wxRichTextAttr::Apply(const wxRichTextAttr& style, const wxRichTextAttr* compareWith)
12485 {
12486 wxTextAttr::Apply(style, compareWith);
12487
12488 return m_textBoxAttr.Apply(style.m_textBoxAttr, compareWith ? (& compareWith->m_textBoxAttr) : (const wxTextBoxAttr*) NULL);
12489 }
12490
12491 // Remove specified attributes from this object
12492 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr& attr)
12493 {
12494 wxTextAttr::RemoveStyle(*this, attr);
12495
12496 return m_textBoxAttr.RemoveStyle(attr.m_textBoxAttr);
12497 }
12498
12499 // Collects the attributes that are common to a range of content, building up a note of
12500 // which attributes are absent in some objects and which clash in some objects.
12501 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr& attr, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
12502 {
12503 wxTextAttrCollectCommonAttributes(*this, attr, clashingAttr, absentAttr);
12504
12505 m_textBoxAttr.CollectCommonAttributes(attr.m_textBoxAttr, clashingAttr.m_textBoxAttr, absentAttr.m_textBoxAttr);
12506 }
12507
12508 // Partial equality test
12509 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border, bool weakTest) const
12510 {
12511 if (!weakTest &&
12512 ((!HasStyle() && border.HasStyle()) ||
12513 (!HasColour() && border.HasColour()) ||
12514 (!HasWidth() && border.HasWidth())))
12515 {
12516 return false;
12517 }
12518
12519 if (border.HasStyle() && HasStyle() && (border.GetStyle() != GetStyle()))
12520 return false;
12521
12522 if (border.HasColour() && HasColour() && (border.GetColourLong() != GetColourLong()))
12523 return false;
12524
12525 if (border.HasWidth() && HasWidth() && !(border.GetWidth() == GetWidth()))
12526 return false;
12527
12528 return true;
12529 }
12530
12531 // Apply border to 'this', but not if the same as compareWith
12532 bool wxTextAttrBorder::Apply(const wxTextAttrBorder& border, const wxTextAttrBorder* compareWith)
12533 {
12534 if (border.HasStyle())
12535 {
12536 if (!(compareWith && (border.GetStyle() == compareWith->GetStyle())))
12537 SetStyle(border.GetStyle());
12538 }
12539 if (border.HasColour())
12540 {
12541 if (!(compareWith && (border.GetColourLong() == compareWith->GetColourLong())))
12542 SetColour(border.GetColourLong());
12543 }
12544 if (border.HasWidth())
12545 {
12546 if (!(compareWith && (border.GetWidth() == compareWith->GetWidth())))
12547 SetWidth(border.GetWidth());
12548 }
12549
12550 return true;
12551 }
12552
12553 // Remove specified attributes from this object
12554 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder& attr)
12555 {
12556 if (attr.HasStyle() && HasStyle())
12557 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE);
12558 if (attr.HasColour() && HasColour())
12559 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR);
12560 if (attr.HasWidth() && HasWidth())
12561 m_borderWidth.Reset();
12562
12563 return true;
12564 }
12565
12566 // Collects the attributes that are common to a range of content, building up a note of
12567 // which attributes are absent in some objects and which clash in some objects.
12568 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder& attr, wxTextAttrBorder& clashingAttr, wxTextAttrBorder& absentAttr)
12569 {
12570 if (attr.HasStyle())
12571 {
12572 if (!clashingAttr.HasStyle() && !absentAttr.HasStyle())
12573 {
12574 if (HasStyle())
12575 {
12576 if (GetStyle() != attr.GetStyle())
12577 {
12578 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12579 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12580 }
12581 }
12582 else
12583 SetStyle(attr.GetStyle());
12584 }
12585 }
12586 else
12587 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
12588
12589 if (attr.HasColour())
12590 {
12591 if (!clashingAttr.HasColour() && !absentAttr.HasColour())
12592 {
12593 if (HasColour())
12594 {
12595 if (GetColour() != attr.GetColour())
12596 {
12597 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12598 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12599 }
12600 }
12601 else
12602 SetColour(attr.GetColourLong());
12603 }
12604 }
12605 else
12606 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
12607
12608 m_borderWidth.CollectCommonAttributes(attr.m_borderWidth, clashingAttr.m_borderWidth, absentAttr.m_borderWidth);
12609 }
12610
12611 // Partial equality test
12612 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders, bool weakTest) const
12613 {
12614 return m_left.EqPartial(borders.m_left, weakTest) && m_right.EqPartial(borders.m_right, weakTest) &&
12615 m_top.EqPartial(borders.m_top, weakTest) && m_bottom.EqPartial(borders.m_bottom, weakTest);
12616 }
12617
12618 // Apply border to 'this', but not if the same as compareWith
12619 bool wxTextAttrBorders::Apply(const wxTextAttrBorders& borders, const wxTextAttrBorders* compareWith)
12620 {
12621 m_left.Apply(borders.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrBorder*) NULL);
12622 m_right.Apply(borders.m_right, compareWith ? (& compareWith->m_right) : (const wxTextAttrBorder*) NULL);
12623 m_top.Apply(borders.m_top, compareWith ? (& compareWith->m_top) : (const wxTextAttrBorder*) NULL);
12624 m_bottom.Apply(borders.m_bottom, compareWith ? (& compareWith->m_bottom) : (const wxTextAttrBorder*) NULL);
12625 return true;
12626 }
12627
12628 // Remove specified attributes from this object
12629 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders& attr)
12630 {
12631 m_left.RemoveStyle(attr.m_left);
12632 m_right.RemoveStyle(attr.m_right);
12633 m_top.RemoveStyle(attr.m_top);
12634 m_bottom.RemoveStyle(attr.m_bottom);
12635 return true;
12636 }
12637
12638 // Collects the attributes that are common to a range of content, building up a note of
12639 // which attributes are absent in some objects and which clash in some objects.
12640 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders& attr, wxTextAttrBorders& clashingAttr, wxTextAttrBorders& absentAttr)
12641 {
12642 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12643 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12644 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12645 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12646 }
12647
12648 // Set style of all borders
12649 void wxTextAttrBorders::SetStyle(int style)
12650 {
12651 m_left.SetStyle(style);
12652 m_right.SetStyle(style);
12653 m_top.SetStyle(style);
12654 m_bottom.SetStyle(style);
12655 }
12656
12657 // Set colour of all borders
12658 void wxTextAttrBorders::SetColour(unsigned long colour)
12659 {
12660 m_left.SetColour(colour);
12661 m_right.SetColour(colour);
12662 m_top.SetColour(colour);
12663 m_bottom.SetColour(colour);
12664 }
12665
12666 void wxTextAttrBorders::SetColour(const wxColour& colour)
12667 {
12668 m_left.SetColour(colour);
12669 m_right.SetColour(colour);
12670 m_top.SetColour(colour);
12671 m_bottom.SetColour(colour);
12672 }
12673
12674 // Set width of all borders
12675 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension& width)
12676 {
12677 m_left.SetWidth(width);
12678 m_right.SetWidth(width);
12679 m_top.SetWidth(width);
12680 m_bottom.SetWidth(width);
12681 }
12682
12683 // Partial equality test
12684 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension& dim, bool weakTest) const
12685 {
12686 if (!weakTest && !IsValid() && dim.IsValid())
12687 return false;
12688
12689 if (dim.IsValid() && IsValid() && !((*this) == dim))
12690 return false;
12691 else
12692 return true;
12693 }
12694
12695 bool wxTextAttrDimension::Apply(const wxTextAttrDimension& dim, const wxTextAttrDimension* compareWith)
12696 {
12697 if (dim.IsValid())
12698 {
12699 if (!(compareWith && dim == (*compareWith)))
12700 (*this) = dim;
12701 }
12702
12703 return true;
12704 }
12705
12706 // Collects the attributes that are common to a range of content, building up a note of
12707 // which attributes are absent in some objects and which clash in some objects.
12708 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension& attr, wxTextAttrDimension& clashingAttr, wxTextAttrDimension& absentAttr)
12709 {
12710 if (attr.IsValid())
12711 {
12712 if (!clashingAttr.IsValid() && !absentAttr.IsValid())
12713 {
12714 if (IsValid())
12715 {
12716 if (!((*this) == attr))
12717 {
12718 clashingAttr.SetValid(true);
12719 SetValid(false);
12720 }
12721 }
12722 else
12723 (*this) = attr;
12724 }
12725 }
12726 else
12727 absentAttr.SetValid(true);
12728 }
12729
12730 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC& dc, double scale, const wxSize& parentSize)
12731 {
12732 m_ppi = dc.GetPPI().x; m_scale = scale; m_parentSize = parentSize;
12733 }
12734
12735 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi, double scale, const wxSize& parentSize)
12736 {
12737 m_ppi = ppi; m_scale = scale; m_parentSize = parentSize;
12738 }
12739
12740 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units) const
12741 {
12742 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi, units, m_scale);
12743 }
12744
12745 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels) const
12746 {
12747 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi, pixels, m_scale);
12748 }
12749
12750 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension& dim, int direction) const
12751 {
12752 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
12753 return ConvertTenthsMMToPixels(dim.GetValue());
12754 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
12755 return dim.GetValue();
12756 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
12757 {
12758 wxASSERT(m_parentSize != wxDefaultSize);
12759 if (direction == wxHORIZONTAL)
12760 return (int) (double(m_parentSize.x) * double(dim.GetValue()) / 100.0);
12761 else
12762 return (int) (double(m_parentSize.y) * double(dim.GetValue()) / 100.0);
12763 }
12764 else
12765 {
12766 wxASSERT(false);
12767 return 0;
12768 }
12769 }
12770
12771 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension& dim) const
12772 {
12773 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
12774 return dim.GetValue();
12775 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
12776 return ConvertPixelsToTenthsMM(dim.GetValue());
12777 else
12778 {
12779 wxASSERT(false);
12780 return 0;
12781 }
12782 }
12783
12784 // Partial equality test
12785 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims, bool weakTest) const
12786 {
12787 if (!m_left.EqPartial(dims.m_left, weakTest))
12788 return false;
12789
12790 if (!m_right.EqPartial(dims.m_right, weakTest))
12791 return false;
12792
12793 if (!m_top.EqPartial(dims.m_top, weakTest))
12794 return false;
12795
12796 if (!m_bottom.EqPartial(dims.m_bottom, weakTest))
12797 return false;
12798
12799 return true;
12800 }
12801
12802 // Apply border to 'this', but not if the same as compareWith
12803 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions& dims, const wxTextAttrDimensions* compareWith)
12804 {
12805 m_left.Apply(dims.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrDimension*) NULL);
12806 m_right.Apply(dims.m_right, compareWith ? (& compareWith->m_right): (const wxTextAttrDimension*) NULL);
12807 m_top.Apply(dims.m_top, compareWith ? (& compareWith->m_top): (const wxTextAttrDimension*) NULL);
12808 m_bottom.Apply(dims.m_bottom, compareWith ? (& compareWith->m_bottom): (const wxTextAttrDimension*) NULL);
12809
12810 return true;
12811 }
12812
12813 // Remove specified attributes from this object
12814 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions& attr)
12815 {
12816 if (attr.m_left.IsValid())
12817 m_left.Reset();
12818 if (attr.m_right.IsValid())
12819 m_right.Reset();
12820 if (attr.m_top.IsValid())
12821 m_top.Reset();
12822 if (attr.m_bottom.IsValid())
12823 m_bottom.Reset();
12824
12825 return true;
12826 }
12827
12828 // Collects the attributes that are common to a range of content, building up a note of
12829 // which attributes are absent in some objects and which clash in some objects.
12830 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions& attr, wxTextAttrDimensions& clashingAttr, wxTextAttrDimensions& absentAttr)
12831 {
12832 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
12833 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
12834 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
12835 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
12836 }
12837
12838 // Partial equality test
12839 bool wxTextAttrSize::EqPartial(const wxTextAttrSize& size, bool weakTest) const
12840 {
12841 if (!m_width.EqPartial(size.m_width, weakTest))
12842 return false;
12843
12844 if (!m_height.EqPartial(size.m_height, weakTest))
12845 return false;
12846
12847 return true;
12848 }
12849
12850 // Apply border to 'this', but not if the same as compareWith
12851 bool wxTextAttrSize::Apply(const wxTextAttrSize& size, const wxTextAttrSize* compareWith)
12852 {
12853 m_width.Apply(size.m_width, compareWith ? (& compareWith->m_width) : (const wxTextAttrDimension*) NULL);
12854 m_height.Apply(size.m_height, compareWith ? (& compareWith->m_height): (const wxTextAttrDimension*) NULL);
12855
12856 return true;
12857 }
12858
12859 // Remove specified attributes from this object
12860 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize& attr)
12861 {
12862 if (attr.m_width.IsValid())
12863 m_width.Reset();
12864 if (attr.m_height.IsValid())
12865 m_height.Reset();
12866
12867 return true;
12868 }
12869
12870 // Collects the attributes that are common to a range of content, building up a note of
12871 // which attributes are absent in some objects and which clash in some objects.
12872 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize& attr, wxTextAttrSize& clashingAttr, wxTextAttrSize& absentAttr)
12873 {
12874 m_width.CollectCommonAttributes(attr.m_width, clashingAttr.m_width, absentAttr.m_width);
12875 m_height.CollectCommonAttributes(attr.m_height, clashingAttr.m_height, absentAttr.m_height);
12876 }
12877
12878 // Collects the attributes that are common to a range of content, building up a note of
12879 // which attributes are absent in some objects and which clash in some objects.
12880 void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAttr& attr, wxTextAttr& clashingAttr, wxTextAttr& absentAttr)
12881 {
12882 absentAttr.SetFlags(absentAttr.GetFlags() | (~attr.GetFlags() & wxTEXT_ATTR_ALL));
12883 absentAttr.SetTextEffectFlags(absentAttr.GetTextEffectFlags() | (~attr.GetTextEffectFlags() & 0xFFFF));
12884
12885 long forbiddenFlags = clashingAttr.GetFlags()|absentAttr.GetFlags();
12886
12887 // If different font size units are being used, this is a clash.
12888 if (((attr.GetFlags() & wxTEXT_ATTR_FONT_SIZE) | (currentStyle.GetFlags() & wxTEXT_ATTR_FONT_SIZE)) == wxTEXT_ATTR_FONT_SIZE)
12889 {
12890 currentStyle.SetFontSize(0);
12891 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE);
12892 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE);
12893 }
12894 else
12895 {
12896 if (attr.HasFontPointSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_POINT_SIZE))
12897 {
12898 if (currentStyle.HasFontPointSize())
12899 {
12900 if (currentStyle.GetFontSize() != attr.GetFontSize())
12901 {
12902 // Clash of attr - mark as such
12903 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12904 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12905 }
12906 }
12907 else
12908 currentStyle.SetFontSize(attr.GetFontSize());
12909 }
12910 else if (!attr.HasFontPointSize() && currentStyle.HasFontPointSize())
12911 {
12912 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12913 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
12914 }
12915
12916 if (attr.HasFontPixelSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_PIXEL_SIZE))
12917 {
12918 if (currentStyle.HasFontPixelSize())
12919 {
12920 if (currentStyle.GetFontSize() != attr.GetFontSize())
12921 {
12922 // Clash of attr - mark as such
12923 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12924 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12925 }
12926 }
12927 else
12928 currentStyle.SetFontPixelSize(attr.GetFontSize());
12929 }
12930 else if (!attr.HasFontPixelSize() && currentStyle.HasFontPixelSize())
12931 {
12932 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12933 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
12934 }
12935 }
12936
12937 if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC))
12938 {
12939 if (currentStyle.HasFontItalic())
12940 {
12941 if (currentStyle.GetFontStyle() != attr.GetFontStyle())
12942 {
12943 // Clash of attr - mark as such
12944 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
12945 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
12946 }
12947 }
12948 else
12949 currentStyle.SetFontStyle(attr.GetFontStyle());
12950 }
12951 else if (!attr.HasFontItalic() && currentStyle.HasFontItalic())
12952 {
12953 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
12954 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
12955 }
12956
12957 if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY))
12958 {
12959 if (currentStyle.HasFontFamily())
12960 {
12961 if (currentStyle.GetFontFamily() != attr.GetFontFamily())
12962 {
12963 // Clash of attr - mark as such
12964 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
12965 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
12966 }
12967 }
12968 else
12969 currentStyle.SetFontFamily(attr.GetFontFamily());
12970 }
12971 else if (!attr.HasFontFamily() && currentStyle.HasFontFamily())
12972 {
12973 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
12974 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
12975 }
12976
12977 if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT))
12978 {
12979 if (currentStyle.HasFontWeight())
12980 {
12981 if (currentStyle.GetFontWeight() != attr.GetFontWeight())
12982 {
12983 // Clash of attr - mark as such
12984 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
12985 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
12986 }
12987 }
12988 else
12989 currentStyle.SetFontWeight(attr.GetFontWeight());
12990 }
12991 else if (!attr.HasFontWeight() && currentStyle.HasFontWeight())
12992 {
12993 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
12994 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
12995 }
12996
12997 if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE))
12998 {
12999 if (currentStyle.HasFontFaceName())
13000 {
13001 wxString faceName1(currentStyle.GetFontFaceName());
13002 wxString faceName2(attr.GetFontFaceName());
13003
13004 if (faceName1 != faceName2)
13005 {
13006 // Clash of attr - mark as such
13007 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
13008 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
13009 }
13010 }
13011 else
13012 currentStyle.SetFontFaceName(attr.GetFontFaceName());
13013 }
13014 else if (!attr.HasFontFaceName() && currentStyle.HasFontFaceName())
13015 {
13016 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
13017 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
13018 }
13019
13020 if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE))
13021 {
13022 if (currentStyle.HasFontUnderlined())
13023 {
13024 if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined())
13025 {
13026 // Clash of attr - mark as such
13027 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13028 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13029 }
13030 }
13031 else
13032 currentStyle.SetFontUnderlined(attr.GetFontUnderlined());
13033 }
13034 else if (!attr.HasFontUnderlined() && currentStyle.HasFontUnderlined())
13035 {
13036 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13037 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13038 }
13039
13040 if (attr.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_STRIKETHROUGH))
13041 {
13042 if (currentStyle.HasFontStrikethrough())
13043 {
13044 if (currentStyle.GetFontStrikethrough() != attr.GetFontStrikethrough())
13045 {
13046 // Clash of attr - mark as such
13047 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13048 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13049 }
13050 }
13051 else
13052 currentStyle.SetFontStrikethrough(attr.GetFontStrikethrough());
13053 }
13054 else if (!attr.HasFontStrikethrough() && currentStyle.HasFontStrikethrough())
13055 {
13056 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13057 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13058 }
13059
13060 if (attr.HasTextColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TEXT_COLOUR))
13061 {
13062 if (currentStyle.HasTextColour())
13063 {
13064 if (currentStyle.GetTextColour() != attr.GetTextColour())
13065 {
13066 // Clash of attr - mark as such
13067 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
13068 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
13069 }
13070 }
13071 else
13072 currentStyle.SetTextColour(attr.GetTextColour());
13073 }
13074 else if (!attr.HasTextColour() && currentStyle.HasTextColour())
13075 {
13076 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
13077 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
13078 }
13079
13080 if (attr.HasBackgroundColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BACKGROUND_COLOUR))
13081 {
13082 if (currentStyle.HasBackgroundColour())
13083 {
13084 if (currentStyle.GetBackgroundColour() != attr.GetBackgroundColour())
13085 {
13086 // Clash of attr - mark as such
13087 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13088 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13089 }
13090 }
13091 else
13092 currentStyle.SetBackgroundColour(attr.GetBackgroundColour());
13093 }
13094 else if (!attr.HasBackgroundColour() && currentStyle.HasBackgroundColour())
13095 {
13096 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13097 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13098 }
13099
13100 if (attr.HasAlignment() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_ALIGNMENT))
13101 {
13102 if (currentStyle.HasAlignment())
13103 {
13104 if (currentStyle.GetAlignment() != attr.GetAlignment())
13105 {
13106 // Clash of attr - mark as such
13107 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
13108 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
13109 }
13110 }
13111 else
13112 currentStyle.SetAlignment(attr.GetAlignment());
13113 }
13114 else if (!attr.HasAlignment() && currentStyle.HasAlignment())
13115 {
13116 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
13117 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
13118 }
13119
13120 if (attr.HasTabs() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TABS))
13121 {
13122 if (currentStyle.HasTabs())
13123 {
13124 if (!wxRichTextTabsEq(currentStyle.GetTabs(), attr.GetTabs()))
13125 {
13126 // Clash of attr - mark as such
13127 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
13128 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
13129 }
13130 }
13131 else
13132 currentStyle.SetTabs(attr.GetTabs());
13133 }
13134 else if (!attr.HasTabs() && currentStyle.HasTabs())
13135 {
13136 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
13137 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
13138 }
13139
13140 if (attr.HasLeftIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LEFT_INDENT))
13141 {
13142 if (currentStyle.HasLeftIndent())
13143 {
13144 if (currentStyle.GetLeftIndent() != attr.GetLeftIndent() || currentStyle.GetLeftSubIndent() != attr.GetLeftSubIndent())
13145 {
13146 // Clash of attr - mark as such
13147 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
13148 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
13149 }
13150 }
13151 else
13152 currentStyle.SetLeftIndent(attr.GetLeftIndent(), attr.GetLeftSubIndent());
13153 }
13154 else if (!attr.HasLeftIndent() && currentStyle.HasLeftIndent())
13155 {
13156 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
13157 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
13158 }
13159
13160 if (attr.HasRightIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_RIGHT_INDENT))
13161 {
13162 if (currentStyle.HasRightIndent())
13163 {
13164 if (currentStyle.GetRightIndent() != attr.GetRightIndent())
13165 {
13166 // Clash of attr - mark as such
13167 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
13168 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
13169 }
13170 }
13171 else
13172 currentStyle.SetRightIndent(attr.GetRightIndent());
13173 }
13174 else if (!attr.HasRightIndent() && currentStyle.HasRightIndent())
13175 {
13176 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
13177 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
13178 }
13179
13180 if (attr.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_AFTER))
13181 {
13182 if (currentStyle.HasParagraphSpacingAfter())
13183 {
13184 if (currentStyle.GetParagraphSpacingAfter() != attr.GetParagraphSpacingAfter())
13185 {
13186 // Clash of attr - mark as such
13187 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13188 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13189 }
13190 }
13191 else
13192 currentStyle.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter());
13193 }
13194 else if (!attr.HasParagraphSpacingAfter() && currentStyle.HasParagraphSpacingAfter())
13195 {
13196 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13197 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13198 }
13199
13200 if (attr.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_BEFORE))
13201 {
13202 if (currentStyle.HasParagraphSpacingBefore())
13203 {
13204 if (currentStyle.GetParagraphSpacingBefore() != attr.GetParagraphSpacingBefore())
13205 {
13206 // Clash of attr - mark as such
13207 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13208 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13209 }
13210 }
13211 else
13212 currentStyle.SetParagraphSpacingBefore(attr.GetParagraphSpacingBefore());
13213 }
13214 else if (!attr.HasParagraphSpacingBefore() && currentStyle.HasParagraphSpacingBefore())
13215 {
13216 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13217 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13218 }
13219
13220 if (attr.HasLineSpacing() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LINE_SPACING))
13221 {
13222 if (currentStyle.HasLineSpacing())
13223 {
13224 if (currentStyle.GetLineSpacing() != attr.GetLineSpacing())
13225 {
13226 // Clash of attr - mark as such
13227 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
13228 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
13229 }
13230 }
13231 else
13232 currentStyle.SetLineSpacing(attr.GetLineSpacing());
13233 }
13234 else if (!attr.HasLineSpacing() && currentStyle.HasLineSpacing())
13235 {
13236 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
13237 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
13238 }
13239
13240 if (attr.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
13241 {
13242 if (currentStyle.HasCharacterStyleName())
13243 {
13244 if (currentStyle.GetCharacterStyleName() != attr.GetCharacterStyleName())
13245 {
13246 // Clash of attr - mark as such
13247 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13248 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13249 }
13250 }
13251 else
13252 currentStyle.SetCharacterStyleName(attr.GetCharacterStyleName());
13253 }
13254 else if (!attr.HasCharacterStyleName() && currentStyle.HasCharacterStyleName())
13255 {
13256 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13257 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13258 }
13259
13260 if (attr.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
13261 {
13262 if (currentStyle.HasParagraphStyleName())
13263 {
13264 if (currentStyle.GetParagraphStyleName() != attr.GetParagraphStyleName())
13265 {
13266 // Clash of attr - mark as such
13267 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13268 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13269 }
13270 }
13271 else
13272 currentStyle.SetParagraphStyleName(attr.GetParagraphStyleName());
13273 }
13274 else if (!attr.HasParagraphStyleName() && currentStyle.HasParagraphStyleName())
13275 {
13276 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13277 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13278 }
13279
13280 if (attr.HasListStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LIST_STYLE_NAME))
13281 {
13282 if (currentStyle.HasListStyleName())
13283 {
13284 if (currentStyle.GetListStyleName() != attr.GetListStyleName())
13285 {
13286 // Clash of attr - mark as such
13287 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13288 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13289 }
13290 }
13291 else
13292 currentStyle.SetListStyleName(attr.GetListStyleName());
13293 }
13294 else if (!attr.HasListStyleName() && currentStyle.HasListStyleName())
13295 {
13296 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13297 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13298 }
13299
13300 if (attr.HasBulletStyle() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_STYLE))
13301 {
13302 if (currentStyle.HasBulletStyle())
13303 {
13304 if (currentStyle.GetBulletStyle() != attr.GetBulletStyle())
13305 {
13306 // Clash of attr - mark as such
13307 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
13308 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
13309 }
13310 }
13311 else
13312 currentStyle.SetBulletStyle(attr.GetBulletStyle());
13313 }
13314 else if (!attr.HasBulletStyle() && currentStyle.HasBulletStyle())
13315 {
13316 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
13317 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
13318 }
13319
13320 if (attr.HasBulletNumber() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NUMBER))
13321 {
13322 if (currentStyle.HasBulletNumber())
13323 {
13324 if (currentStyle.GetBulletNumber() != attr.GetBulletNumber())
13325 {
13326 // Clash of attr - mark as such
13327 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
13328 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
13329 }
13330 }
13331 else
13332 currentStyle.SetBulletNumber(attr.GetBulletNumber());
13333 }
13334 else if (!attr.HasBulletNumber() && currentStyle.HasBulletNumber())
13335 {
13336 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
13337 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
13338 }
13339
13340 if (attr.HasBulletText() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_TEXT))
13341 {
13342 if (currentStyle.HasBulletText())
13343 {
13344 if (currentStyle.GetBulletText() != attr.GetBulletText())
13345 {
13346 // Clash of attr - mark as such
13347 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
13348 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
13349 }
13350 }
13351 else
13352 {
13353 currentStyle.SetBulletText(attr.GetBulletText());
13354 currentStyle.SetBulletFont(attr.GetBulletFont());
13355 }
13356 }
13357 else if (!attr.HasBulletText() && currentStyle.HasBulletText())
13358 {
13359 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
13360 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
13361 }
13362
13363 if (attr.HasBulletName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NAME))
13364 {
13365 if (currentStyle.HasBulletName())
13366 {
13367 if (currentStyle.GetBulletName() != attr.GetBulletName())
13368 {
13369 // Clash of attr - mark as such
13370 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
13371 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
13372 }
13373 }
13374 else
13375 {
13376 currentStyle.SetBulletName(attr.GetBulletName());
13377 }
13378 }
13379 else if (!attr.HasBulletName() && currentStyle.HasBulletName())
13380 {
13381 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
13382 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
13383 }
13384
13385 if (attr.HasURL() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_URL))
13386 {
13387 if (currentStyle.HasURL())
13388 {
13389 if (currentStyle.GetURL() != attr.GetURL())
13390 {
13391 // Clash of attr - mark as such
13392 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
13393 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
13394 }
13395 }
13396 else
13397 {
13398 currentStyle.SetURL(attr.GetURL());
13399 }
13400 }
13401 else if (!attr.HasURL() && currentStyle.HasURL())
13402 {
13403 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
13404 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
13405 }
13406
13407 if (attr.HasTextEffects() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_EFFECTS))
13408 {
13409 if (currentStyle.HasTextEffects())
13410 {
13411 // We need to find the bits in the new attr that are different:
13412 // just look at those bits that are specified by the new attr.
13413
13414 // We need to remove the bits and flags that are not common between current attr
13415 // and new attr. In so doing we need to take account of the styles absent from one or more of the
13416 // previous styles.
13417
13418 int currentRelevantTextEffects = currentStyle.GetTextEffects() & attr.GetTextEffectFlags();
13419 int newRelevantTextEffects = attr.GetTextEffects() & attr.GetTextEffectFlags();
13420
13421 if (currentRelevantTextEffects != newRelevantTextEffects)
13422 {
13423 // Find the text effects that were different, using XOR
13424 int differentEffects = currentRelevantTextEffects ^ newRelevantTextEffects;
13425
13426 // Clash of attr - mark as such
13427 clashingAttr.SetTextEffectFlags(clashingAttr.GetTextEffectFlags() | differentEffects);
13428 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~differentEffects);
13429 }
13430 }
13431 else
13432 {
13433 currentStyle.SetTextEffects(attr.GetTextEffects());
13434 currentStyle.SetTextEffectFlags(attr.GetTextEffectFlags());
13435 }
13436
13437 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
13438 // that we've looked at so far
13439 currentStyle.SetTextEffects(currentStyle.GetTextEffects() & ~absentAttr.GetTextEffectFlags());
13440 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~absentAttr.GetTextEffectFlags());
13441
13442 if (currentStyle.GetTextEffectFlags() == 0)
13443 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
13444 }
13445 else if (!attr.HasTextEffects() && currentStyle.HasTextEffects())
13446 {
13447 clashingAttr.AddFlag(wxTEXT_ATTR_EFFECTS);
13448 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
13449 }
13450
13451 if (attr.HasOutlineLevel() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_OUTLINE_LEVEL))
13452 {
13453 if (currentStyle.HasOutlineLevel())
13454 {
13455 if (currentStyle.GetOutlineLevel() != attr.GetOutlineLevel())
13456 {
13457 // Clash of attr - mark as such
13458 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13459 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13460 }
13461 }
13462 else
13463 currentStyle.SetOutlineLevel(attr.GetOutlineLevel());
13464 }
13465 else if (!attr.HasOutlineLevel() && currentStyle.HasOutlineLevel())
13466 {
13467 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13468 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
13469 }
13470 }
13471
13472 WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
13473
13474 // JACS 2013-01-27
13475 WX_DEFINE_OBJARRAY(wxRichTextAttrArray);
13476
13477 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
13478
13479 bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const
13480 {
13481 if (m_properties.GetCount() != props.GetCount())
13482 return false;
13483
13484 size_t i;
13485 for (i = 0; i < m_properties.GetCount(); i++)
13486 {
13487 const wxVariant& var1 = m_properties[i];
13488 int idx = props.Find(var1.GetName());
13489 if (idx == -1)
13490 return false;
13491 const wxVariant& var2 = props.m_properties[idx];
13492 if (!(var1 == var2))
13493 return false;
13494 }
13495
13496 return true;
13497 }
13498
13499 wxArrayString wxRichTextProperties::GetPropertyNames() const
13500 {
13501 wxArrayString arr;
13502 size_t i;
13503 for (i = 0; i < m_properties.GetCount(); i++)
13504 {
13505 arr.Add(m_properties[i].GetName());
13506 }
13507 return arr;
13508 }
13509
13510 int wxRichTextProperties::Find(const wxString& name) const
13511 {
13512 size_t i;
13513 for (i = 0; i < m_properties.GetCount(); i++)
13514 {
13515 if (m_properties[i].GetName() == name)
13516 return (int) i;
13517 }
13518 return -1;
13519 }
13520
13521 bool wxRichTextProperties::Remove(const wxString& name)
13522 {
13523 int idx = Find(name);
13524 if (idx != -1)
13525 {
13526 m_properties.RemoveAt(idx);
13527 return true;
13528 }
13529 else
13530 return false;
13531 }
13532
13533 wxVariant* wxRichTextProperties::FindOrCreateProperty(const wxString& name)
13534 {
13535 int idx = Find(name);
13536 if (idx == wxNOT_FOUND)
13537 SetProperty(name, wxString());
13538 idx = Find(name);
13539 if (idx != wxNOT_FOUND)
13540 {
13541 return & (*this)[idx];
13542 }
13543 else
13544 return NULL;
13545 }
13546
13547 const wxVariant& wxRichTextProperties::GetProperty(const wxString& name) const
13548 {
13549 static const wxVariant nullVariant;
13550 int idx = Find(name);
13551 if (idx != -1)
13552 return m_properties[idx];
13553 else
13554 return nullVariant;
13555 }
13556
13557 wxString wxRichTextProperties::GetPropertyString(const wxString& name) const
13558 {
13559 return GetProperty(name).GetString();
13560 }
13561
13562 long wxRichTextProperties::GetPropertyLong(const wxString& name) const
13563 {
13564 return GetProperty(name).GetLong();
13565 }
13566
13567 bool wxRichTextProperties::GetPropertyBool(const wxString& name) const
13568 {
13569 return GetProperty(name).GetBool();
13570 }
13571
13572 double wxRichTextProperties::GetPropertyDouble(const wxString& name) const
13573 {
13574 return GetProperty(name).GetDouble();
13575 }
13576
13577 void wxRichTextProperties::SetProperty(const wxVariant& variant)
13578 {
13579 wxASSERT(!variant.GetName().IsEmpty());
13580
13581 int idx = Find(variant.GetName());
13582
13583 if (idx == -1)
13584 m_properties.Add(variant);
13585 else
13586 m_properties[idx] = variant;
13587 }
13588
13589 void wxRichTextProperties::SetProperty(const wxString& name, const wxVariant& variant)
13590 {
13591 int idx = Find(name);
13592 wxVariant var(variant);
13593 var.SetName(name);
13594
13595 if (idx == -1)
13596 m_properties.Add(var);
13597 else
13598 m_properties[idx] = var;
13599 }
13600
13601 void wxRichTextProperties::SetProperty(const wxString& name, const wxString& value)
13602 {
13603 SetProperty(name, wxVariant(value, name));
13604 }
13605
13606 void wxRichTextProperties::SetProperty(const wxString& name, long value)
13607 {
13608 SetProperty(name, wxVariant(value, name));
13609 }
13610
13611 void wxRichTextProperties::SetProperty(const wxString& name, double value)
13612 {
13613 SetProperty(name, wxVariant(value, name));
13614 }
13615
13616 void wxRichTextProperties::SetProperty(const wxString& name, bool value)
13617 {
13618 SetProperty(name, wxVariant(value, name));
13619 }
13620
13621 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties& properties)
13622 {
13623 size_t i;
13624 for (i = 0; i < properties.GetCount(); i++)
13625 {
13626 wxString name = properties.GetProperties()[i].GetName();
13627 if (HasProperty(name))
13628 Remove(name);
13629 }
13630 }
13631
13632 void wxRichTextProperties::MergeProperties(const wxRichTextProperties& properties)
13633 {
13634 size_t i;
13635 for (i = 0; i < properties.GetCount(); i++)
13636 {
13637 SetProperty(properties.GetProperties()[i]);
13638 }
13639 }
13640
13641 wxRichTextObject* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox* topLevelContainer) const
13642 {
13643 if (m_address.GetCount() == 0)
13644 return topLevelContainer;
13645
13646 wxRichTextCompositeObject* p = topLevelContainer;
13647 size_t i = 0;
13648 while (p && i < m_address.GetCount())
13649 {
13650 int pos = m_address[i];
13651 wxASSERT(pos >= 0 && pos < (int) p->GetChildren().GetCount());
13652 if (pos < 0 || pos >= (int) p->GetChildren().GetCount())
13653 return NULL;
13654
13655 wxRichTextObject* p1 = p->GetChild(pos);
13656 if (i == (m_address.GetCount()-1))
13657 return p1;
13658
13659 p = wxDynamicCast(p1, wxRichTextCompositeObject);
13660 i ++;
13661 }
13662 return NULL;
13663 }
13664
13665 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox* topLevelContainer, wxRichTextObject* obj)
13666 {
13667 m_address.Clear();
13668
13669 if (topLevelContainer == obj)
13670 return true;
13671
13672 wxRichTextObject* o = obj;
13673 while (o)
13674 {
13675 wxRichTextCompositeObject* p = wxDynamicCast(o->GetParent(), wxRichTextCompositeObject);
13676 if (!p)
13677 return false;
13678
13679 int pos = p->GetChildren().IndexOf(o);
13680 if (pos == -1)
13681 return false;
13682
13683 m_address.Insert(pos, 0);
13684
13685 if (p == topLevelContainer)
13686 return true;
13687
13688 o = p;
13689 }
13690 return false;
13691 }
13692
13693 // Equality test
13694 bool wxRichTextSelection::operator==(const wxRichTextSelection& sel) const
13695 {
13696 if (m_container != sel.m_container)
13697 return false;
13698 if (m_ranges.GetCount() != sel.m_ranges.GetCount())
13699 return false;
13700 size_t i;
13701 for (i = 0; i < m_ranges.GetCount(); i++)
13702 if (!(m_ranges[i] == sel.m_ranges[i]))
13703 return false;
13704 return true;
13705 }
13706
13707 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
13708 // or none at the level of the object's container.
13709 wxRichTextRangeArray wxRichTextSelection::GetSelectionForObject(wxRichTextObject* obj) const
13710 {
13711 if (IsValid())
13712 {
13713 wxRichTextParagraphLayoutBox* container = obj->GetParentContainer();
13714
13715 if (container == m_container)
13716 return m_ranges;
13717
13718 container = obj->GetContainer();
13719 while (container)
13720 {
13721 if (container->GetParent())
13722 {
13723 // If we found that our object's container is within the range of
13724 // a selection higher up, then assume the whole original object
13725 // is also selected.
13726 wxRichTextParagraphLayoutBox* parentContainer = container->GetParentContainer();
13727 if (parentContainer == m_container)
13728 {
13729 if (WithinSelection(container->GetRange().GetStart(), m_ranges))
13730 {
13731 wxRichTextRangeArray ranges;
13732 ranges.Add(obj->GetRange());
13733 return ranges;
13734 }
13735 }
13736
13737 container = parentContainer;
13738 }
13739 else
13740 {
13741 container = NULL;
13742 break;
13743 }
13744 }
13745 }
13746 return wxRichTextRangeArray();
13747 }
13748
13749 // Is the given position within the selection?
13750 bool wxRichTextSelection::WithinSelection(long pos, wxRichTextObject* obj) const
13751 {
13752 if (!IsValid())
13753 return false;
13754 else
13755 {
13756 wxRichTextRangeArray selectionRanges = GetSelectionForObject(obj);
13757 return WithinSelection(pos, selectionRanges);
13758 }
13759 }
13760
13761 // Is the given position within the selection range?
13762 bool wxRichTextSelection::WithinSelection(long pos, const wxRichTextRangeArray& ranges)
13763 {
13764 size_t i;
13765 for (i = 0; i < ranges.GetCount(); i++)
13766 {
13767 const wxRichTextRange& range = ranges[i];
13768 if (pos >= range.GetStart() && pos <= range.GetEnd())
13769 return true;
13770 }
13771 return false;
13772 }
13773
13774 // Is the given range completely within the selection range?
13775 bool wxRichTextSelection::WithinSelection(const wxRichTextRange& range, const wxRichTextRangeArray& ranges)
13776 {
13777 size_t i;
13778 for (i = 0; i < ranges.GetCount(); i++)
13779 {
13780 const wxRichTextRange& eachRange = ranges[i];
13781 if (range.IsWithin(eachRange))
13782 return true;
13783 }
13784 return false;
13785 }
13786
13787 IMPLEMENT_CLASS(wxRichTextDrawingHandler, wxObject)
13788 IMPLEMENT_CLASS(wxRichTextDrawingContext, wxObject)
13789
13790 wxRichTextDrawingContext::wxRichTextDrawingContext(wxRichTextBuffer* buffer)
13791 {
13792 Init();
13793 m_buffer = buffer;
13794 if (m_buffer && m_buffer->GetRichTextCtrl())
13795 EnableVirtualAttributes(m_buffer->GetRichTextCtrl()->GetVirtualAttributesEnabled());
13796 }
13797
13798 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject* obj) const
13799 {
13800 if (!GetVirtualAttributesEnabled())
13801 return false;
13802
13803 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13804 while (node)
13805 {
13806 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13807 if (handler->HasVirtualAttributes(obj))
13808 return true;
13809
13810 node = node->GetNext();
13811 }
13812 return false;
13813 }
13814
13815 wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* obj) const
13816 {
13817 wxRichTextAttr attr;
13818 if (!GetVirtualAttributesEnabled())
13819 return attr;
13820
13821 // We apply all handlers, so we can may combine several different attributes
13822 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13823 while (node)
13824 {
13825 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13826 if (handler->HasVirtualAttributes(obj))
13827 {
13828 bool success = handler->GetVirtualAttributes(attr, obj);
13829 wxASSERT(success);
13830 wxUnusedVar(success);
13831 }
13832
13833 node = node->GetNext();
13834 }
13835 return attr;
13836 }
13837
13838 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const
13839 {
13840 if (!GetVirtualAttributesEnabled())
13841 return false;
13842
13843 if (HasVirtualAttributes(obj))
13844 {
13845 wxRichTextAttr a(GetVirtualAttributes(obj));
13846 attr.Apply(a);
13847 return true;
13848 }
13849 else
13850 return false;
13851 }
13852
13853 int wxRichTextDrawingContext::GetVirtualSubobjectAttributesCount(wxRichTextObject* obj) const
13854 {
13855 if (!GetVirtualAttributesEnabled())
13856 return 0;
13857
13858 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13859 while (node)
13860 {
13861 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13862 int count = handler->GetVirtualSubobjectAttributesCount(obj);
13863 if (count > 0)
13864 return count;
13865
13866 node = node->GetNext();
13867 }
13868 return 0;
13869 }
13870
13871 int wxRichTextDrawingContext::GetVirtualSubobjectAttributes(wxRichTextObject* obj, wxArrayInt& positions, wxRichTextAttrArray& attributes) const
13872 {
13873 if (!GetVirtualAttributesEnabled())
13874 return 0;
13875
13876 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13877 while (node)
13878 {
13879 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13880 if (handler->GetVirtualSubobjectAttributes(obj, positions, attributes))
13881 return positions.GetCount();
13882
13883 node = node->GetNext();
13884 }
13885 return 0;
13886 }
13887
13888 bool wxRichTextDrawingContext::HasVirtualText(const wxRichTextPlainText* obj) const
13889 {
13890 if (!GetVirtualAttributesEnabled())
13891 return false;
13892
13893 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13894 while (node)
13895 {
13896 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13897 if (handler->HasVirtualText(obj))
13898 return true;
13899
13900 node = node->GetNext();
13901 }
13902 return false;
13903 }
13904
13905 bool wxRichTextDrawingContext::GetVirtualText(const wxRichTextPlainText* obj, wxString& text) const
13906 {
13907 if (!GetVirtualAttributesEnabled())
13908 return false;
13909
13910 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
13911 while (node)
13912 {
13913 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13914 if (handler->GetVirtualText(obj, text))
13915 return true;
13916
13917 node = node->GetNext();
13918 }
13919 return false;
13920 }
13921
13922 /// Adds a handler to the end
13923 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler *handler)
13924 {
13925 sm_drawingHandlers.Append(handler);
13926 }
13927
13928 /// Inserts a handler at the front
13929 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler *handler)
13930 {
13931 sm_drawingHandlers.Insert( handler );
13932 }
13933
13934 /// Removes a handler
13935 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString& name)
13936 {
13937 wxRichTextDrawingHandler *handler = FindDrawingHandler(name);
13938 if (handler)
13939 {
13940 sm_drawingHandlers.DeleteObject(handler);
13941 delete handler;
13942 return true;
13943 }
13944 else
13945 return false;
13946 }
13947
13948 wxRichTextDrawingHandler* wxRichTextBuffer::FindDrawingHandler(const wxString& name)
13949 {
13950 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
13951 while (node)
13952 {
13953 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
13954 if (handler->GetName().Lower() == name.Lower()) return handler;
13955
13956 node = node->GetNext();
13957 }
13958 return NULL;
13959 }
13960
13961 void wxRichTextBuffer::CleanUpDrawingHandlers()
13962 {
13963 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
13964 while (node)
13965 {
13966 wxRichTextDrawingHandler* handler = (wxRichTextDrawingHandler*)node->GetData();
13967 wxList::compatibility_iterator next = node->GetNext();
13968 delete handler;
13969 node = next;
13970 }
13971
13972 sm_drawingHandlers.Clear();
13973 }
13974
13975 void wxRichTextBuffer::AddFieldType(wxRichTextFieldType *fieldType)
13976 {
13977 sm_fieldTypes[fieldType->GetName()] = fieldType;
13978 }
13979
13980 bool wxRichTextBuffer::RemoveFieldType(const wxString& name)
13981 {
13982 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
13983 if (it == sm_fieldTypes.end())
13984 return false;
13985 else
13986 {
13987 wxRichTextFieldType* fieldType = it->second;
13988 sm_fieldTypes.erase(it);
13989 delete fieldType;
13990 return true;
13991 }
13992 }
13993
13994 wxRichTextFieldType *wxRichTextBuffer::FindFieldType(const wxString& name)
13995 {
13996 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
13997 if (it == sm_fieldTypes.end())
13998 return NULL;
13999 else
14000 return it->second;
14001 }
14002
14003 void wxRichTextBuffer::CleanUpFieldTypes()
14004 {
14005 wxRichTextFieldTypeHashMap::iterator it;
14006 for( it = sm_fieldTypes.begin(); it != sm_fieldTypes.end(); ++it )
14007 {
14008 wxRichTextFieldType* fieldType = it->second;
14009 delete fieldType;
14010 }
14011
14012 sm_fieldTypes.clear();
14013 }
14014
14015 #endif
14016 // wxUSE_RICHTEXT