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