Upgrade bundled zlib to 1.2.8.
[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 // Copyright: (c) Julian Smart
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 #if wxUSE_RICHTEXT
19
20 #include "wx/richtext/richtextbuffer.h"
21
22 #ifndef WX_PRECOMP
23 #include "wx/dc.h"
24 #include "wx/intl.h"
25 #include "wx/log.h"
26 #include "wx/dataobj.h"
27 #include "wx/module.h"
28 #endif
29
30 #include "wx/settings.h"
31 #include "wx/filename.h"
32 #include "wx/clipbrd.h"
33 #include "wx/wfstream.h"
34 #include "wx/mstream.h"
35 #include "wx/sstream.h"
36 #include "wx/textfile.h"
37 #include "wx/hashmap.h"
38 #include "wx/dynarray.h"
39
40 #include "wx/richtext/richtextctrl.h"
41 #include "wx/richtext/richtextstyles.h"
42 #include "wx/richtext/richtextimagedlg.h"
43 #include "wx/richtext/richtextsizepage.h"
44 #include "wx/richtext/richtextxml.h"
45
46 #include "wx/listimpl.cpp"
47 #include "wx/arrimpl.cpp"
48
49 WX_DEFINE_LIST(wxRichTextObjectList)
50 WX_DEFINE_LIST(wxRichTextLineList)
51
52 // Switch off if the platform doesn't like it for some reason
53 #define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
54
55 // Use GetPartialTextExtents for platforms that support it natively
56 #define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
57
58 const wxChar wxRichTextLineBreakChar = (wxChar) 29;
59
60 // Helper classes for floating layout
61 struct wxRichTextFloatRectMap
62 {
63 wxRichTextFloatRectMap(int sY, int eY, int w, wxRichTextObject* obj)
64 {
65 startY = sY;
66 endY = eY;
67 width = w;
68 anchor = obj;
69 }
70
71 int startY, endY;
72 int width;
73 wxRichTextObject* anchor;
74 };
75
76 WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap*, wxRichTextFloatRectMapArray);
77
78 int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap* r1, wxRichTextFloatRectMap* r2)
79 {
80 return r1->startY - r2->startY;
81 }
82
83 class wxRichTextFloatCollector
84 {
85 public:
86 wxRichTextFloatCollector(const wxRect& availableRect);
87 ~wxRichTextFloatCollector();
88
89 // Collect the floating objects info in the given paragraph
90 void CollectFloat(wxRichTextParagraph* para);
91 void CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating);
92
93 // Return the last paragraph we collected
94 wxRichTextParagraph* LastParagraph();
95
96 // Given the start y position and the height of the line,
97 // find out how wide the line can be
98 wxRect GetAvailableRect(int startY, int endY);
99
100 // Given a floating box, find its fit position
101 int GetFitPosition(int direction, int start, int height) const;
102 int GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const;
103
104 // Find the last y position
105 int GetLastRectBottom();
106
107 // Draw the floats inside a rect
108 void Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
109
110 // HitTest the floats
111 int HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
112
113 // Get floating object count
114 int GetFloatingObjectCount() const { return m_left.GetCount() + m_right.GetCount(); }
115
116 // Get floating objects
117 bool GetFloatingObjects(wxRichTextObjectList& objects) const;
118
119 // Delete a float
120 bool DeleteFloat(wxRichTextObject* obj);
121
122 // Do we have this float already?
123 bool HasFloat(wxRichTextObject* obj);
124
125 bool HasFloats() const { return m_left.GetCount() >0 || m_right.GetCount() > 0; }
126
127 static int SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point);
128
129 static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY);
130
131 static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array);
132
133 static void DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
134
135 static int HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
136
137 private:
138 wxRichTextFloatRectMapArray m_left;
139 wxRichTextFloatRectMapArray m_right;
140 //int m_width;
141 wxRect m_availableRect;
142 wxRichTextParagraph* m_para;
143 };
144
145 // Delete a float
146 bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject* obj)
147 {
148 size_t i;
149 for (i = 0; i < m_left.GetCount(); i++)
150 {
151 if (m_left[i]->anchor == obj)
152 {
153 m_left.RemoveAt(i);
154 return true;
155 }
156 }
157 for (i = 0; i < m_right.GetCount(); i++)
158 {
159 if (m_right[i]->anchor == obj)
160 {
161 m_right.RemoveAt(i);
162 return true;
163 }
164 }
165 return false;
166 }
167
168 // Do we have this float already?
169 bool wxRichTextFloatCollector::HasFloat(wxRichTextObject* obj)
170 {
171 size_t i;
172 for (i = 0; i < m_left.GetCount(); i++)
173 {
174 if (m_left[i]->anchor == obj)
175 {
176 return true;
177 }
178 }
179 for (i = 0; i < m_right.GetCount(); i++)
180 {
181 if (m_right[i]->anchor == obj)
182 {
183 return true;
184 }
185 }
186 return false;
187 }
188
189 // Get floating objects
190 bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList& objects) const
191 {
192 size_t i;
193 for (i = 0; i < m_left.GetCount(); i++)
194 objects.Append(m_left[i]->anchor);
195 for (i = 0; i < m_right.GetCount(); i++)
196 objects.Append(m_right[i]->anchor);
197 return true;
198 }
199
200
201 /*
202 * Binary search helper function
203 * The argument point is the Y coordinate, and this fuction
204 * always return the floating rect that contain this coordinate
205 * or under this coordinate.
206 */
207 int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point)
208 {
209 int end = array.GetCount() - 1;
210 int start = 0;
211 int ret = 0;
212
213 wxASSERT(end >= 0);
214
215 while (true)
216 {
217 if (start > end)
218 {
219 break;
220 }
221
222 int mid = (start + end) / 2;
223 if (array[mid]->startY <= point && array[mid]->endY >= point)
224 return mid;
225 else if (array[mid]->startY > point)
226 {
227 end = mid - 1;
228 ret = mid;
229 }
230 else if (array[mid]->endY < point)
231 {
232 start = mid + 1;
233 ret = start;
234 }
235 }
236
237 return ret;
238 }
239
240 int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY)
241 {
242 int ret = 0;
243 int len = array.GetCount();
244
245 wxASSERT(index >= 0 && index < len);
246
247 if (array[index]->startY < startY && array[index]->endY > startY)
248 ret = ret < array[index]->width ? array[index]->width : ret;
249 while (index < len && array[index]->startY <= endY)
250 {
251 ret = ret < array[index]->width ? array[index]->width : ret;
252 index++;
253 }
254
255 return ret;
256 }
257
258 wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect& rect) : m_left(wxRichTextFloatRectMapCmp), m_right(wxRichTextFloatRectMapCmp)
259 {
260 m_availableRect = rect;
261 m_para = NULL;
262 }
263
264 void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array)
265 {
266 int len = array.GetCount();
267 for (int i = 0; i < len; i++)
268 delete array[i];
269 }
270
271 wxRichTextFloatCollector::~wxRichTextFloatCollector()
272 {
273 FreeFloatRectMapArray(m_left);
274 FreeFloatRectMapArray(m_right);
275 }
276
277 int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const
278 {
279 if (array.GetCount() == 0)
280 return start;
281
282 int i = SearchAdjacentRect(array, start);
283 int last = start;
284 while (i < (int) array.GetCount())
285 {
286 if (array[i]->startY - last >= height)
287 return last + 1;
288 last = array[i]->endY;
289 i++;
290 }
291
292 return last + 1;
293 }
294
295 int wxRichTextFloatCollector::GetFitPosition(int direction, int start, int height) const
296 {
297 if (direction == wxTEXT_BOX_ATTR_FLOAT_LEFT)
298 return GetFitPosition(m_left, start, height);
299 else if (direction == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
300 return GetFitPosition(m_right, start, height);
301 else
302 {
303 wxASSERT("Never should be here");
304 return start;
305 }
306 }
307
308 // Adds a floating image to the float collector.
309 // The actual positioning is done by wxRichTextParagraph::LayoutFloat.
310 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating)
311 {
312 int direction = floating->GetFloatDirection();
313
314 wxPoint pos = floating->GetPosition();
315 wxSize size = floating->GetCachedSize();
316 wxRichTextFloatRectMap *map = new wxRichTextFloatRectMap(pos.y, pos.y + size.y, size.x, floating);
317 switch (direction)
318 {
319 case wxTEXT_BOX_ATTR_FLOAT_NONE:
320 delete map;
321 break;
322 case wxTEXT_BOX_ATTR_FLOAT_LEFT:
323 // Just a not-enough simple assertion
324 wxASSERT (m_left.Index(map) == wxNOT_FOUND);
325 m_left.Add(map);
326 break;
327 case wxTEXT_BOX_ATTR_FLOAT_RIGHT:
328 wxASSERT (m_right.Index(map) == wxNOT_FOUND);
329 m_right.Add(map);
330 break;
331 default:
332 delete map;
333 wxASSERT("Unrecognised float attribute.");
334 }
335
336 m_para = para;
337 }
338
339 void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para)
340 {
341 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
342 while (node)
343 {
344 wxRichTextObject* floating = node->GetData();
345
346 if (floating->IsFloating())
347 {
348 CollectFloat(para, floating);
349 }
350
351 node = node->GetNext();
352 }
353
354 m_para = para;
355 }
356
357 wxRichTextParagraph* wxRichTextFloatCollector::LastParagraph()
358 {
359 return m_para;
360 }
361
362 wxRect wxRichTextFloatCollector::GetAvailableRect(int startY, int endY)
363 {
364 int widthLeft = 0, widthRight = 0;
365 if (m_left.GetCount() != 0)
366 {
367 int i = SearchAdjacentRect(m_left, startY);
368 if (i < (int) m_left.GetCount())
369 widthLeft = GetWidthFromFloatRect(m_left, i, startY, endY);
370 }
371 if (m_right.GetCount() != 0)
372 {
373 int j = SearchAdjacentRect(m_right, startY);
374 if (j < (int) m_right.GetCount())
375 widthRight = GetWidthFromFloatRect(m_right, j, startY, endY);
376 }
377
378 // TODO: actually we want to use the actual image positions to find the
379 // available remaining space, since the image might not be right up against
380 // the left or right edge of the container.
381 return wxRect(widthLeft + m_availableRect.x, 0, m_availableRect.width - widthLeft - widthRight, 0);
382 }
383
384 int wxRichTextFloatCollector::GetLastRectBottom()
385 {
386 int ret = 0;
387 int len = m_left.GetCount();
388 if (len) {
389 ret = ret > m_left[len-1]->endY ? ret : m_left[len-1]->endY;
390 }
391 len = m_right.GetCount();
392 if (len) {
393 ret = ret > m_right[len-1]->endY ? ret : m_right[len-1]->endY;
394 }
395
396 return ret;
397 }
398
399 void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
400 {
401 int start = rect.y;
402 int end = rect.y + rect.height;
403 int i, j;
404 i = SearchAdjacentRect(array, start);
405 if (i < 0 || i >= (int) array.GetCount())
406 return;
407 j = SearchAdjacentRect(array, end);
408 if (j < 0 || j >= (int) array.GetCount())
409 j = array.GetCount() - 1;
410 while (i <= j)
411 {
412 wxRichTextObject* obj = array[i]->anchor;
413 wxRichTextRange r = obj->GetRange();
414 obj->Draw(dc, context, r, selection, wxRect(obj->GetPosition(), obj->GetCachedSize()), descent, style);
415 i++;
416 }
417 }
418
419 void wxRichTextFloatCollector::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
420 {
421 if (m_left.GetCount() > 0)
422 DrawFloat(m_left, dc, context, range, selection, rect, descent, style);
423 if (m_right.GetCount() > 0)
424 DrawFloat(m_right, dc, context, range, selection, rect, descent, style);
425 }
426
427 int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int WXUNUSED(flags))
428 {
429 int i;
430 if (array.GetCount() == 0)
431 return wxRICHTEXT_HITTEST_NONE;
432 i = SearchAdjacentRect(array, pt.y);
433 if (i < 0 || i >= (int) array.GetCount())
434 return wxRICHTEXT_HITTEST_NONE;
435 if (!array[i]->anchor->IsShown())
436 return wxRICHTEXT_HITTEST_NONE;
437
438 wxPoint point = array[i]->anchor->GetPosition();
439 wxSize size = array[i]->anchor->GetCachedSize();
440 if (point.x <= pt.x && point.x + size.x >= pt.x
441 && point.y <= pt.y && point.y + size.y >= pt.y)
442 {
443 textPosition = array[i]->anchor->GetRange().GetStart();
444 * obj = array[i]->anchor;
445 if (pt.x > (pt.x + pt.x + size.x) / 2)
446 return wxRICHTEXT_HITTEST_BEFORE;
447 else
448 return wxRICHTEXT_HITTEST_AFTER;
449 }
450
451 return wxRICHTEXT_HITTEST_NONE;
452 }
453
454 int wxRichTextFloatCollector::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags)
455 {
456 int ret = HitTestFloat(m_left, dc, context, pt, textPosition, obj, flags);
457 if (ret == wxRICHTEXT_HITTEST_NONE)
458 {
459 ret = HitTestFloat(m_right, dc, context, pt, textPosition, obj, flags);
460 }
461 return ret;
462 }
463
464 // Helpers for efficiency
465 inline void wxCheckSetFont(wxDC& dc, const wxFont& font)
466 {
467 dc.SetFont(font);
468 }
469
470 inline void wxCheckSetPen(wxDC& dc, const wxPen& pen)
471 {
472 const wxPen& pen1 = dc.GetPen();
473 if (pen1.IsOk() && pen.IsOk())
474 {
475 if (pen1.GetWidth() == pen.GetWidth() &&
476 pen1.GetStyle() == pen.GetStyle() &&
477 pen1.GetColour() == pen.GetColour())
478 return;
479 }
480 dc.SetPen(pen);
481 }
482
483 inline void wxCheckSetBrush(wxDC& dc, const wxBrush& brush)
484 {
485 const wxBrush& brush1 = dc.GetBrush();
486 if (brush1.IsOk() && brush.IsOk())
487 {
488 if (brush1.GetStyle() == brush.GetStyle() &&
489 brush1.GetColour() == brush.GetColour())
490 return;
491 }
492 dc.SetBrush(brush);
493 }
494
495 /*!
496 * wxRichTextObject
497 * This is the base for drawable objects.
498 */
499
500 IMPLEMENT_CLASS(wxRichTextObject, wxObject)
501
502 wxRichTextObject::wxRichTextObject(wxRichTextObject* parent)
503 {
504 m_refCount = 1;
505 m_parent = parent;
506 m_descent = 0;
507 m_show = true;
508 }
509
510 wxRichTextObject::~wxRichTextObject()
511 {
512 }
513
514 void wxRichTextObject::Dereference()
515 {
516 m_refCount --;
517 if (m_refCount <= 0)
518 delete this;
519 }
520
521 /// Copy
522 void wxRichTextObject::Copy(const wxRichTextObject& obj)
523 {
524 m_size = obj.m_size;
525 m_maxSize = obj.m_maxSize;
526 m_minSize = obj.m_minSize;
527 m_pos = obj.m_pos;
528 m_range = obj.m_range;
529 m_ownRange = obj.m_ownRange;
530 m_attributes = obj.m_attributes;
531 m_properties = obj.m_properties;
532 m_descent = obj.m_descent;
533 m_show = obj.m_show;
534 }
535
536 // Get/set the top-level container of this object.
537 wxRichTextParagraphLayoutBox* wxRichTextObject::GetContainer() const
538 {
539 const wxRichTextObject* p = this;
540 while (p)
541 {
542 if (p->IsTopLevel())
543 {
544 return wxDynamicCast(p, wxRichTextParagraphLayoutBox);
545 }
546 p = p->GetParent();
547 }
548 return NULL;
549 }
550
551 void wxRichTextObject::SetMargins(int margin)
552 {
553 SetMargins(margin, margin, margin, margin);
554 }
555
556 void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin, int bottomMargin)
557 {
558 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin, wxTEXT_ATTR_UNITS_PIXELS);
559 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin, wxTEXT_ATTR_UNITS_PIXELS);
560 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin, wxTEXT_ATTR_UNITS_PIXELS);
561 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin, wxTEXT_ATTR_UNITS_PIXELS);
562 }
563
564 int wxRichTextObject::GetLeftMargin() const
565 {
566 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
567 }
568
569 int wxRichTextObject::GetRightMargin() const
570 {
571 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
572 }
573
574 int wxRichTextObject::GetTopMargin() const
575 {
576 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
577 }
578
579 int wxRichTextObject::GetBottomMargin() const
580 {
581 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
582 }
583
584 // Calculate the available content space in the given rectangle, given the
585 // margins, border and padding specified in the object's attributes.
586 wxRect wxRichTextObject::GetAvailableContentArea(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& outerRect) const
587 {
588 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
589 marginRect = outerRect;
590 wxRichTextAttr attr(GetAttributes());
591 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
592 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
593 return contentRect;
594 }
595
596 // Invalidate the buffer. With no argument, invalidates whole buffer.
597 void wxRichTextObject::Invalidate(const wxRichTextRange& invalidRange)
598 {
599 if (invalidRange != wxRICHTEXT_NONE)
600 {
601 // If this is a floating object, size may not be recalculated
602 // after floats have been collected in an early stage of Layout.
603 // So avoid resetting the cache for floating objects during layout.
604 if (!IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode())
605 SetCachedSize(wxDefaultSize);
606 SetMaxSize(wxDefaultSize);
607 SetMinSize(wxDefaultSize);
608 }
609 }
610
611 // Convert units in tenths of a millimetre to device units
612 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units) const
613 {
614 // Unscale
615 double scale = 1.0;
616 if (GetBuffer())
617 scale = GetBuffer()->GetScale() / GetBuffer()->GetDimensionScale();
618 int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units, scale);
619
620 return p;
621 }
622
623 // Convert units in tenths of a millimetre to device units
624 int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units, double scale)
625 {
626 // There are ppi pixels in 254.1 "1/10 mm"
627
628 double pixels = ((double) units * (double)ppi) / 254.1;
629 if (scale != 1.0)
630 pixels /= scale;
631
632 // If the result is very small, make it at least one pixel in size.
633 if (pixels == 0 && units > 0)
634 pixels = 1;
635
636 return (int) pixels;
637 }
638
639 // Convert units in pixels to tenths of a millimetre
640 int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC& dc, int pixels) const
641 {
642 int p = pixels;
643 double scale = 1.0;
644 if (GetBuffer())
645 scale = GetBuffer()->GetScale();
646
647 return ConvertPixelsToTenthsMM(dc.GetPPI().x, p, scale);
648 }
649
650 int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi, int pixels, double scale)
651 {
652 // There are ppi pixels in 254.1 "1/10 mm"
653
654 double p = double(pixels);
655
656 if (scale != 1.0)
657 p *= scale;
658
659 int units = int( p * 254.1 / (double) ppi );
660 return units;
661 }
662
663 // Draw the borders and background for the given rectangle and attributes.
664 // Width and height are taken to be the outer margin size, not the content.
665 bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, const wxRect& boxRect, int flags, wxRichTextObject* obj)
666 {
667 // Assume boxRect is the area around the content
668 wxRect marginRect = boxRect;
669 wxRect contentRect, borderRect, paddingRect, outlineRect;
670
671 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
672
673 // Margin is transparent. Draw background from margin.
674 if (attr.HasBackgroundColour() || (flags & wxRICHTEXT_DRAW_SELECTED))
675 {
676 wxColour colour;
677 if (flags & wxRICHTEXT_DRAW_SELECTED)
678 {
679 // TODO: get selection colour from control?
680 colour = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
681 }
682 else
683 colour = attr.GetBackgroundColour();
684
685 wxPen pen(colour);
686 wxBrush brush(colour);
687
688 dc.SetPen(pen);
689 dc.SetBrush(brush);
690 dc.DrawRectangle(borderRect);
691 }
692
693 if (flags & wxRICHTEXT_DRAW_GUIDELINES)
694 {
695 wxRichTextAttr editBorderAttr;
696 // TODO: make guideline colour configurable
697 editBorderAttr.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY);
698 editBorderAttr.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS);
699 editBorderAttr.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID);
700
701 if (obj)
702 {
703 wxRichTextCell* cell = wxDynamicCast(obj, wxRichTextCell);
704 if (cell)
705 {
706 // This ensures that thin lines drawn by adjacent cells (left and above)
707 // don't get overwritten by the guidelines.
708 editBorderAttr.GetTextBoxAttr().GetBorder().GetLeft().Reset();
709 editBorderAttr.GetTextBoxAttr().GetBorder().GetTop().Reset();
710 }
711 }
712
713 DrawBorder(dc, buffer, editBorderAttr.GetTextBoxAttr().GetBorder(), borderRect, flags);
714 }
715
716 if (attr.GetTextBoxAttr().GetBorder().IsValid())
717 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetBorder(), borderRect);
718
719 if (attr.GetTextBoxAttr().GetOutline().IsValid())
720 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetOutline(), outlineRect);
721
722 return true;
723 }
724
725 // Draw a border
726 bool wxRichTextObject::DrawBorder(wxDC& dc, wxRichTextBuffer* buffer, const wxTextAttrBorders& attr, const wxRect& rect, int WXUNUSED(flags))
727 {
728 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
729 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
730
731 if (attr.GetLeft().IsValid() && attr.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
732 {
733 borderLeft = converter.GetPixels(attr.GetLeft().GetWidth());
734 wxColour col(attr.GetLeft().GetColour());
735
736 // If pen width is > 1, resorts to a solid rectangle.
737 if (borderLeft == 1)
738 {
739 int penStyle = wxSOLID;
740 if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
741 penStyle = wxDOT;
742 else if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
743 penStyle = wxLONG_DASH;
744 wxPen pen(col, 1, penStyle);
745 dc.SetPen(pen);
746 dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height);
747
748 }
749 else if (borderLeft > 1)
750 {
751 wxPen pen(col);
752 wxBrush brush(col);
753 dc.SetPen(pen);
754 dc.SetBrush(brush);
755 dc.DrawRectangle(rect.x, rect.y, borderLeft, rect.height);
756 }
757 }
758
759 if (attr.GetRight().IsValid() && attr.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
760 {
761 borderRight = converter.GetPixels(attr.GetRight().GetWidth());
762
763 wxColour col(attr.GetRight().GetColour());
764
765 // If pen width is > 1, resorts to a solid rectangle.
766 if (borderRight == 1)
767 {
768 int penStyle = wxSOLID;
769 if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
770 penStyle = wxDOT;
771 else if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
772 penStyle = wxLONG_DASH;
773 wxPen pen(col, 1, penStyle);
774 dc.SetPen(pen);
775 dc.DrawLine(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height + 1);
776
777 }
778 else if (borderRight > 1)
779 {
780 wxPen pen(col);
781 wxBrush brush(col);
782 dc.SetPen(pen);
783 dc.SetBrush(brush);
784 dc.DrawRectangle(rect.x + rect.width - borderRight, rect.y, borderRight, rect.height + 1);
785 }
786 }
787
788 if (attr.GetTop().IsValid() && attr.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
789 {
790 borderTop = converter.GetPixels(attr.GetTop().GetWidth());
791
792 wxColour col(attr.GetTop().GetColour());
793
794 // If pen width is > 1, resorts to a solid rectangle.
795 if (borderTop == 1)
796 {
797 int penStyle = wxSOLID;
798 if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
799 penStyle = wxDOT;
800 else if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
801 penStyle = wxLONG_DASH;
802 wxPen pen(col, 1, penStyle);
803 dc.SetPen(pen);
804 dc.DrawLine(rect.x, rect.y, rect.x + rect.width, rect.y);
805
806 }
807 else if (borderTop > 1)
808 {
809 wxPen pen(col);
810 wxBrush brush(col);
811 dc.SetPen(pen);
812 dc.SetBrush(brush);
813 dc.DrawRectangle(rect.x, rect.y, rect.width, borderTop);
814 }
815 }
816
817 if (attr.GetBottom().IsValid() && attr.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
818 {
819 borderBottom = converter.GetPixels(attr.GetBottom().GetWidth());
820 wxColour col(attr.GetBottom().GetColour());
821
822 // If pen width is > 1, resorts to a solid rectangle.
823 if (borderBottom == 1)
824 {
825 int penStyle = wxSOLID;
826 if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
827 penStyle = wxDOT;
828 else if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
829 penStyle = wxLONG_DASH;
830 wxPen pen(col, 1, penStyle);
831 dc.SetPen(pen);
832 dc.DrawLine(rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height);
833
834 }
835 else if (borderBottom > 1)
836 {
837 wxPen pen(col);
838 wxBrush brush(col);
839 dc.SetPen(pen);
840 dc.SetBrush(brush);
841 dc.DrawRectangle(rect.x, rect.y + rect.height - borderBottom + 1, rect.width, borderBottom);
842 }
843 }
844
845 return true;
846 }
847
848 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
849 // or marginRect (outer), and the other must be the default rectangle (no width or height).
850 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
851 // is available.
852 //
853 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
854
855 bool wxRichTextObject::GetBoxRects(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, wxRect& marginRect, wxRect& borderRect, wxRect& contentRect, wxRect& paddingRect, wxRect& outlineRect)
856 {
857 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
858 int outlineLeft = 0, outlineRight = 0, outlineTop = 0, outlineBottom = 0;
859 int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
860 int marginLeft = 0, marginRight = 0, marginTop = 0, marginBottom = 0;
861
862 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
863
864 if (attr.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
865 marginLeft = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetLeft());
866 if (attr.GetTextBoxAttr().GetMargins().GetRight().IsValid())
867 marginRight = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetRight());
868 if (attr.GetTextBoxAttr().GetMargins().GetTop().IsValid())
869 marginTop = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetTop());
870 if (attr.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
871 marginBottom = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetBottom());
872
873 if (attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
874 borderLeft = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
875 if (attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
876 borderRight = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
877 if (attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
878 borderTop = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
879 if (attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
880 borderBottom = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
881
882 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
883 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
884 if (attr.GetTextBoxAttr().GetPadding().GetRight().IsValid())
885 paddingRight = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetRight());
886 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
887 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
888 if (attr.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
889 paddingBottom = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetBottom());
890
891 if (attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
892 outlineLeft = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
893 if (attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
894 outlineRight = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
895 if (attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
896 outlineTop = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
897 if (attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
898 outlineBottom = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
899
900 int leftTotal = marginLeft + borderLeft + paddingLeft;
901 int rightTotal = marginRight + borderRight + paddingRight;
902 int topTotal = marginTop + borderTop + paddingTop;
903 int bottomTotal = marginBottom + borderBottom + paddingBottom;
904
905 if (marginRect != wxRect())
906 {
907 contentRect.x = marginRect.x + leftTotal;
908 contentRect.y = marginRect.y + topTotal;
909 contentRect.width = marginRect.width - (leftTotal + rightTotal);
910 contentRect.height = marginRect.height - (topTotal + bottomTotal);
911 }
912 else
913 {
914 marginRect.x = contentRect.x - leftTotal;
915 marginRect.y = contentRect.y - topTotal;
916 marginRect.width = contentRect.width + (leftTotal + rightTotal);
917 marginRect.height = contentRect.height + (topTotal + bottomTotal);
918 }
919
920 borderRect.x = marginRect.x + marginLeft;
921 borderRect.y = marginRect.y + marginTop;
922 borderRect.width = marginRect.width - (marginLeft + marginRight);
923 borderRect.height = marginRect.height - (marginTop + marginBottom);
924
925 paddingRect.x = marginRect.x + marginLeft + borderLeft;
926 paddingRect.y = marginRect.y + marginTop + borderTop;
927 paddingRect.width = marginRect.width - (marginLeft + marginRight + borderLeft + borderRight);
928 paddingRect.height = marginRect.height - (marginTop + marginBottom + borderTop + borderBottom);
929
930 // The outline is outside the margin and doesn't influence the overall box position or content size.
931 outlineRect.x = marginRect.x - outlineLeft;
932 outlineRect.y = marginRect.y - outlineTop;
933 outlineRect.width = marginRect.width + (outlineLeft + outlineRight);
934 outlineRect.height = marginRect.height + (outlineTop + outlineBottom);
935
936 return true;
937 }
938
939 // Get the total margin for the object in pixels, taking into account margin, padding and border size
940 bool wxRichTextObject::GetTotalMargin(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, int& leftMargin, int& rightMargin,
941 int& topMargin, int& bottomMargin)
942 {
943 // Assume boxRect is the area around the content
944 wxRect contentRect, marginRect, borderRect, paddingRect, outlineRect;
945 marginRect = wxRect(0, 0, 1000, 1000);
946
947 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
948
949 leftMargin = contentRect.GetLeft() - marginRect.GetLeft();
950 rightMargin = marginRect.GetRight() - contentRect.GetRight();
951 topMargin = contentRect.GetTop() - marginRect.GetTop();
952 bottomMargin = marginRect.GetBottom() - contentRect.GetBottom();
953
954 return true;
955 }
956
957 // Returns the rectangle which the child has available to it given restrictions specified in the
958 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
959 // availableContainerSpace might be a parent that the cell has to compute its width relative to.
960 // E.g. a cell that's 50% of its parent.
961 wxRect wxRichTextObject::AdjustAvailableSpace(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& WXUNUSED(parentAttr), const wxRichTextAttr& childAttr, const wxRect& availableParentSpace, const wxRect& availableContainerSpace)
962 {
963 wxRect rect = availableParentSpace;
964 double scale = 1.0;
965 if (buffer)
966 scale = buffer->GetScale();
967
968 wxTextAttrDimensionConverter converter(dc, scale, availableContainerSpace.GetSize());
969
970 if (childAttr.GetTextBoxAttr().GetWidth().IsValid())
971 rect.width = converter.GetPixels(childAttr.GetTextBoxAttr().GetWidth());
972
973 if (childAttr.GetTextBoxAttr().GetHeight().IsValid())
974 rect.height = converter.GetPixels(childAttr.GetTextBoxAttr().GetHeight());
975
976 // Can specify either left or right for the position (we're assuming we can't
977 // set the left and right edges to effectively set the size. Would we want to do that?)
978 if (childAttr.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
979 {
980 rect.x = rect.x + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetLeft());
981 }
982 else if (childAttr.GetTextBoxAttr().GetPosition().GetRight().IsValid())
983 {
984 int x = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetRight());
985 if (childAttr.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
986 rect.x = availableContainerSpace.x + availableContainerSpace.width - rect.width;
987 else
988 rect.x += x;
989 }
990
991 if (childAttr.GetTextBoxAttr().GetPosition().GetTop().IsValid())
992 {
993 rect.y = rect.y + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetTop());
994 }
995 else if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
996 {
997 int y = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetBottom());
998 if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
999 rect.y = availableContainerSpace.y + availableContainerSpace.height - rect.height;
1000 else
1001 rect.y += y;
1002 }
1003
1004 if (rect.GetWidth() > availableParentSpace.GetWidth())
1005 rect.SetWidth(availableParentSpace.GetWidth());
1006
1007 return rect;
1008 }
1009
1010 // Dump to output stream for debugging
1011 void wxRichTextObject::Dump(wxTextOutputStream& stream)
1012 {
1013 stream << GetClassInfo()->GetClassName() << wxT("\n");
1014 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");
1015 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");
1016 }
1017
1018 // Gets the containing buffer
1019 wxRichTextBuffer* wxRichTextObject::GetBuffer() const
1020 {
1021 const wxRichTextObject* obj = this;
1022 while (obj && !wxDynamicCast(obj, wxRichTextBuffer))
1023 obj = obj->GetParent();
1024 return wxDynamicCast(obj, wxRichTextBuffer);
1025 }
1026
1027 // Get the absolute object position, by traversing up the child/parent hierarchy
1028 wxPoint wxRichTextObject::GetAbsolutePosition() const
1029 {
1030 wxPoint pt = GetPosition();
1031
1032 wxRichTextObject* p = GetParent();
1033 while (p)
1034 {
1035 pt = pt + p->GetPosition();
1036 p = p->GetParent();
1037 }
1038
1039 return pt;
1040 }
1041
1042 // Hit-testing: returns a flag indicating hit test details, plus
1043 // information about position
1044 int wxRichTextObject::HitTest(wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int WXUNUSED(flags))
1045 {
1046 if (!IsShown())
1047 return wxRICHTEXT_HITTEST_NONE;
1048
1049 wxRect rect = GetRect();
1050 if (pt.x >= rect.x && pt.x < rect.x + rect.width &&
1051 pt.y >= rect.y && pt.y < rect.y + rect.height)
1052 {
1053 *obj = this;
1054 *contextObj = GetParentContainer();
1055 textPosition = GetRange().GetStart();
1056 return wxRICHTEXT_HITTEST_ON;
1057 }
1058 else
1059 return wxRICHTEXT_HITTEST_NONE;
1060 }
1061
1062 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1063 // lays out the object again using the maximum ('best') size
1064 bool wxRichTextObject::LayoutToBestSize(wxDC& dc, wxRichTextDrawingContext& context, wxRichTextBuffer* buffer,
1065 const wxRichTextAttr& parentAttr, const wxRichTextAttr& attr,
1066 const wxRect& availableParentSpace, const wxRect& availableContainerSpace,
1067 int style)
1068 {
1069 wxRect availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, attr, availableParentSpace, availableContainerSpace);
1070 wxRect originalAvailableRect = availableChildRect;
1071 Layout(dc, context, availableChildRect, availableContainerSpace, style);
1072
1073 wxSize maxSize = GetMaxSize();
1074
1075 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1076 // on this basis
1077 if (!attr.GetTextBoxAttr().GetWidth().IsValid() && maxSize.x < availableChildRect.width)
1078 {
1079 // Redo the layout with a fixed, minimum size this time.
1080 Invalidate(wxRICHTEXT_ALL);
1081 wxRichTextAttr newAttr(attr);
1082 newAttr.GetTextBoxAttr().GetWidth().SetValue(maxSize.x, wxTEXT_ATTR_UNITS_PIXELS);
1083 newAttr.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE);
1084
1085 availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, newAttr, availableParentSpace, availableContainerSpace);
1086
1087 // If a paragraph, align the whole paragraph.
1088 // Problem with this: if we're limited by a floating object, a line may be centered
1089 // w.r.t. the smaller resulting box rather than the actual available width.
1090 // FIXME: aligning whole paragraph not compatible with floating objects
1091 if (attr.HasAlignment() && (!wxRichTextBuffer::GetFloatingLayoutMode() || (GetContainer()->GetFloatCollector() && !GetContainer()->GetFloatCollector()->HasFloats())))
1092 {
1093 // centering, right-justification
1094 if (attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
1095 {
1096 availableChildRect.x = (originalAvailableRect.GetWidth() - availableChildRect.GetWidth())/2 + availableChildRect.x;
1097 }
1098 else if (attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
1099 {
1100 availableChildRect.x = availableChildRect.x + originalAvailableRect.GetWidth() - availableChildRect.GetWidth();
1101 }
1102 }
1103
1104 Layout(dc, context, availableChildRect, availableContainerSpace, style);
1105 }
1106
1107 /*
1108 __________________
1109 | ____________ |
1110 | | | |
1111
1112
1113 */
1114
1115 return true;
1116 }
1117
1118 // Move the object recursively, by adding the offset from old to new
1119 void wxRichTextObject::Move(const wxPoint& pt)
1120 {
1121 SetPosition(pt);
1122 }
1123
1124
1125 /*!
1126 * wxRichTextCompositeObject
1127 * This is the base for drawable objects.
1128 */
1129
1130 IMPLEMENT_CLASS(wxRichTextCompositeObject, wxRichTextObject)
1131
1132 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject* parent):
1133 wxRichTextObject(parent)
1134 {
1135 }
1136
1137 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1138 {
1139 DeleteChildren();
1140 }
1141
1142 /// Get the nth child
1143 wxRichTextObject* wxRichTextCompositeObject::GetChild(size_t n) const
1144 {
1145 wxASSERT ( n < m_children.GetCount() );
1146
1147 return m_children.Item(n)->GetData();
1148 }
1149
1150 /// Append a child, returning the position
1151 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject* child)
1152 {
1153 m_children.Append(child);
1154 child->SetParent(this);
1155 return m_children.GetCount() - 1;
1156 }
1157
1158 /// Insert the child in front of the given object, or at the beginning
1159 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject* child, wxRichTextObject* inFrontOf)
1160 {
1161 if (inFrontOf)
1162 {
1163 wxRichTextObjectList::compatibility_iterator node = m_children.Find(inFrontOf);
1164 m_children.Insert(node, child);
1165 }
1166 else
1167 m_children.Insert(child);
1168 child->SetParent(this);
1169
1170 return true;
1171 }
1172
1173 /// Delete the child
1174 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject* child, bool deleteChild)
1175 {
1176 wxRichTextObjectList::compatibility_iterator node = m_children.Find(child);
1177 if (node)
1178 {
1179 wxRichTextObject* obj = node->GetData();
1180 m_children.Erase(node);
1181 if (deleteChild)
1182 delete obj;
1183
1184 return true;
1185 }
1186 return false;
1187 }
1188
1189 /// Delete all children
1190 bool wxRichTextCompositeObject::DeleteChildren()
1191 {
1192 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1193 while (node)
1194 {
1195 wxRichTextObjectList::compatibility_iterator oldNode = node;
1196
1197 wxRichTextObject* child = node->GetData();
1198 child->Dereference(); // Only delete if reference count is zero
1199
1200 node = node->GetNext();
1201 m_children.Erase(oldNode);
1202 }
1203
1204 return true;
1205 }
1206
1207 /// Get the child count
1208 size_t wxRichTextCompositeObject::GetChildCount() const
1209 {
1210 return m_children.GetCount();
1211 }
1212
1213 /// Copy
1214 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject& obj)
1215 {
1216 wxRichTextObject::Copy(obj);
1217
1218 DeleteChildren();
1219
1220 wxRichTextObjectList::compatibility_iterator node = obj.m_children.GetFirst();
1221 while (node)
1222 {
1223 wxRichTextObject* child = node->GetData();
1224 wxRichTextObject* newChild = child->Clone();
1225 newChild->SetParent(this);
1226 m_children.Append(newChild);
1227
1228 node = node->GetNext();
1229 }
1230 }
1231
1232 /// Hit-testing: returns a flag indicating hit test details, plus
1233 /// information about position
1234 int wxRichTextCompositeObject::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
1235 {
1236 if (!IsShown())
1237 return wxRICHTEXT_HITTEST_NONE;
1238
1239 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1240 while (node)
1241 {
1242 wxRichTextObject* child = node->GetData();
1243
1244 if (child->IsShown() && child->IsTopLevel() && (flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS))
1245 {
1246 // Just check if we hit the overall object
1247 int ret = child->wxRichTextObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
1248 if (ret != wxRICHTEXT_HITTEST_NONE)
1249 return ret;
1250 }
1251 else if (child->IsShown())
1252 {
1253 int ret = child->HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
1254 if (ret != wxRICHTEXT_HITTEST_NONE)
1255 return ret;
1256 }
1257
1258 node = node->GetNext();
1259 }
1260
1261 return wxRICHTEXT_HITTEST_NONE;
1262 }
1263
1264 /// Finds the absolute position and row height for the given character position
1265 bool wxRichTextCompositeObject::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
1266 {
1267 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1268 while (node)
1269 {
1270 wxRichTextObject* child = node->GetData();
1271
1272 // Don't recurse if the child is a top-level object,
1273 // such as a text box, because the character position will no longer
1274 // apply. By definition, a top-level object has its own range of
1275 // character positions.
1276 if (!child->IsTopLevel() && child->FindPosition(dc, context, index, pt, height, forceLineStart))
1277 return true;
1278
1279 node = node->GetNext();
1280 }
1281
1282 return false;
1283 }
1284
1285 /// Calculate range
1286 void wxRichTextCompositeObject::CalculateRange(long start, long& end)
1287 {
1288 long current = start;
1289 long lastEnd = current;
1290
1291 if (IsTopLevel())
1292 {
1293 current = 0;
1294 lastEnd = 0;
1295 }
1296
1297 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1298 while (node)
1299 {
1300 wxRichTextObject* child = node->GetData();
1301 long childEnd = 0;
1302
1303 child->CalculateRange(current, childEnd);
1304 lastEnd = childEnd;
1305
1306 current = childEnd + 1;
1307
1308 node = node->GetNext();
1309 }
1310
1311 if (IsTopLevel())
1312 {
1313 // A top-level object always has a range of size 1,
1314 // because its children don't count at this level.
1315 end = start;
1316 m_range.SetRange(start, start);
1317
1318 // An object with no children has zero length
1319 if (m_children.GetCount() == 0)
1320 lastEnd --;
1321 m_ownRange.SetRange(0, lastEnd);
1322 }
1323 else
1324 {
1325 end = lastEnd;
1326
1327 // An object with no children has zero length
1328 if (m_children.GetCount() == 0)
1329 end --;
1330
1331 m_range.SetRange(start, end);
1332 }
1333 }
1334
1335 /// Delete range from layout.
1336 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange& range)
1337 {
1338 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1339
1340 while (node)
1341 {
1342 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
1343 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1344
1345 // Delete the range in each paragraph
1346
1347 // When a chunk has been deleted, internally the content does not
1348 // now match the ranges.
1349 // However, so long as deletion is not done on the same object twice this is OK.
1350 // If you may delete content from the same object twice, recalculate
1351 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1352 // adjust the range you're deleting accordingly.
1353
1354 if (!obj->GetRange().IsOutside(range))
1355 {
1356 // No need to delete within a top-level object; just removing this object will do fine
1357 if (!obj->IsTopLevel())
1358 obj->DeleteRange(range);
1359
1360 // Delete an empty object, or paragraph within this range.
1361 if (obj->IsEmpty() ||
1362 (range.GetStart() <= obj->GetRange().GetStart() && range.GetEnd() >= obj->GetRange().GetEnd()))
1363 {
1364 // An empty paragraph has length 1, so won't be deleted unless the
1365 // whole range is deleted.
1366 RemoveChild(obj, true);
1367 }
1368 }
1369
1370 node = next;
1371 }
1372
1373 return true;
1374 }
1375
1376 /// Get any text in this object for the given range
1377 wxString wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange& range) const
1378 {
1379 wxString text;
1380 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1381 while (node)
1382 {
1383 wxRichTextObject* child = node->GetData();
1384 wxRichTextRange childRange = range;
1385 if (!child->GetRange().IsOutside(range))
1386 {
1387 childRange.LimitTo(child->GetRange());
1388
1389 wxString childText = child->GetTextForRange(childRange);
1390
1391 text += childText;
1392 }
1393 node = node->GetNext();
1394 }
1395
1396 return text;
1397 }
1398
1399 /// Get the child object at the given character position
1400 wxRichTextObject* wxRichTextCompositeObject::GetChildAtPosition(long pos) const
1401 {
1402 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1403 while (node)
1404 {
1405 wxRichTextObject* child = node->GetData();
1406 if (child->GetRange().GetStart() == pos)
1407 return child;
1408 node = node->GetNext();
1409 }
1410 return NULL;
1411 }
1412
1413 /// Recursively merge all pieces that can be merged.
1414 bool wxRichTextCompositeObject::Defragment(wxRichTextDrawingContext& context, const wxRichTextRange& range)
1415 {
1416 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1417 while (node)
1418 {
1419 wxRichTextObject* child = node->GetData();
1420 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
1421 {
1422 wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject);
1423 if (composite)
1424 composite->Defragment(context);
1425
1426 // Optimization: if there are no virtual attributes, we won't need to
1427 // to split objects in order to paint individually attributed chunks.
1428 // So only merge in this case.
1429 if (!context.GetVirtualAttributesEnabled())
1430 {
1431 if (node->GetNext())
1432 {
1433 wxRichTextObject* nextChild = node->GetNext()->GetData();
1434 if (child->CanMerge(nextChild, context) && child->Merge(nextChild, context))
1435 {
1436 nextChild->Dereference();
1437 m_children.Erase(node->GetNext());
1438 }
1439 else
1440 node = node->GetNext();
1441 }
1442 else
1443 node = node->GetNext();
1444 }
1445 else
1446 {
1447 // If we might have virtual attributes, we first see if we have to split
1448 // objects so that they may be painted with potential virtual attributes,
1449 // since text objects can only draw or measure with a single attributes object
1450 // at a time.
1451 wxRichTextObject* childAfterSplit = child;
1452 if (child->CanSplit(context))
1453 {
1454 childAfterSplit = child->Split(context);
1455 node = m_children.Find(childAfterSplit);
1456 }
1457
1458 if (node->GetNext())
1459 {
1460 wxRichTextObject* nextChild = node->GetNext()->GetData();
1461
1462 // First split child and nextChild so we have smaller fragments to merge.
1463 // Then Merge only has to test per-object virtual attributes
1464 // because for an object with all the same sub-object attributes,
1465 // then any general virtual attributes should be merged with sub-objects by
1466 // the implementation.
1467
1468 wxRichTextObject* nextChildAfterSplit = nextChild;
1469
1470 if (nextChildAfterSplit->CanSplit(context))
1471 nextChildAfterSplit = nextChild->Split(context);
1472
1473 bool splitNextChild = nextChild != nextChildAfterSplit;
1474
1475 // See if we can merge this new fragment with (perhaps the first part of) the next object.
1476 // Note that we use nextChild because if we had split nextChild, the first object always
1477 // remains (and further parts are appended). However we must use childAfterSplit since
1478 // it's the last part of a possibly split child.
1479
1480 if (childAfterSplit->CanMerge(nextChild, context) && childAfterSplit->Merge(nextChild, context))
1481 {
1482 nextChild->Dereference();
1483 m_children.Erase(node->GetNext());
1484
1485 // Don't set node -- we'll see if we can merge again with the next
1486 // child. UNLESS we split this or the next child, in which case we know we have to
1487 // move on to the end of the next child.
1488 if (splitNextChild)
1489 node = m_children.Find(nextChildAfterSplit);
1490 }
1491 else
1492 {
1493 if (splitNextChild)
1494 node = m_children.Find(nextChildAfterSplit); // start from the last object in the split
1495 else
1496 node = node->GetNext();
1497 }
1498 }
1499 else
1500 node = node->GetNext();
1501 }
1502 }
1503 else
1504 node = node->GetNext();
1505 }
1506
1507 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1508 if (GetChildCount() > 1)
1509 {
1510 node = m_children.GetFirst();
1511 while (node)
1512 {
1513 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1514 wxRichTextObject* child = node->GetData();
1515 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
1516 {
1517 if (child->IsEmpty())
1518 {
1519 child->Dereference();
1520 m_children.Erase(node);
1521 }
1522 node = next;
1523 }
1524 else
1525 node = node->GetNext();
1526 }
1527 }
1528
1529 return true;
1530 }
1531
1532 /// Dump to output stream for debugging
1533 void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
1534 {
1535 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1536 while (node)
1537 {
1538 wxRichTextObject* child = node->GetData();
1539 child->Dump(stream);
1540 node = node->GetNext();
1541 }
1542 }
1543
1544 /// Get/set the object size for the given range. Returns false if the range
1545 /// is invalid for this object.
1546 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
1547 {
1548 if (!range.IsWithin(GetRange()))
1549 return false;
1550
1551 wxSize sz;
1552
1553 wxArrayInt childExtents;
1554 wxArrayInt* p;
1555 if (partialExtents)
1556 p = & childExtents;
1557 else
1558 p = NULL;
1559
1560 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1561 while (node)
1562 {
1563 wxRichTextObject* child = node->GetData();
1564 if (!child->GetRange().IsOutside(range))
1565 {
1566 // Floating objects have a zero size within the paragraph.
1567 if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
1568 {
1569 if (partialExtents)
1570 {
1571 int lastSize;
1572 if (partialExtents->GetCount() > 0)
1573 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1574 else
1575 lastSize = 0;
1576
1577 partialExtents->Add(0 /* zero size */ + lastSize);
1578 }
1579 }
1580 else
1581 {
1582 wxSize childSize;
1583
1584 wxRichTextRange rangeToUse = range;
1585 rangeToUse.LimitTo(child->GetRange());
1586 if (child->IsTopLevel())
1587 rangeToUse = child->GetOwnRange();
1588
1589 int childDescent = 0;
1590
1591 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1592 // but it's only going to be used after caching has taken place.
1593 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
1594 {
1595 childDescent = child->GetDescent();
1596 childSize = child->GetCachedSize();
1597
1598 sz.y = wxMax(sz.y, childSize.y);
1599 sz.x += childSize.x;
1600 descent = wxMax(descent, childDescent);
1601 }
1602 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize, p))
1603 {
1604 sz.y = wxMax(sz.y, childSize.y);
1605 sz.x += childSize.x;
1606 descent = wxMax(descent, childDescent);
1607
1608 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange() || child->IsTopLevel()))
1609 {
1610 child->SetCachedSize(childSize);
1611 child->SetDescent(childDescent);
1612 }
1613
1614 if (partialExtents)
1615 {
1616 int lastSize;
1617 if (partialExtents->GetCount() > 0)
1618 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1619 else
1620 lastSize = 0;
1621
1622 size_t i;
1623 for (i = 0; i < childExtents.GetCount(); i++)
1624 {
1625 partialExtents->Add(childExtents[i] + lastSize);
1626 }
1627 }
1628 }
1629 }
1630
1631 if (p)
1632 p->Clear();
1633 }
1634
1635 node = node->GetNext();
1636 }
1637 size = sz;
1638 return true;
1639 }
1640
1641 // Invalidate the buffer. With no argument, invalidates whole buffer.
1642 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange& invalidRange)
1643 {
1644 wxRichTextObject::Invalidate(invalidRange);
1645
1646 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1647 while (node)
1648 {
1649 wxRichTextObject* child = node->GetData();
1650 if (invalidRange != wxRICHTEXT_ALL && invalidRange != wxRICHTEXT_NONE && child->GetRange().IsOutside(invalidRange))
1651 {
1652 // Skip
1653 }
1654 else if (child->IsTopLevel())
1655 {
1656 if (wxRichTextBuffer::GetFloatingLayoutMode() && child->IsFloating() && GetBuffer()->GetFloatCollector() && GetBuffer()->GetFloatCollector()->HasFloat(child))
1657 {
1658 // Don't invalidate subhierarchy if we've already been laid out
1659 }
1660 else
1661 {
1662 if (invalidRange == wxRICHTEXT_NONE)
1663 child->Invalidate(wxRICHTEXT_NONE);
1664 else
1665 child->Invalidate(wxRICHTEXT_ALL); // All children must be invalidated if within parent range
1666 }
1667 }
1668 else
1669 child->Invalidate(invalidRange);
1670 node = node->GetNext();
1671 }
1672 }
1673
1674 // Move the object recursively, by adding the offset from old to new
1675 void wxRichTextCompositeObject::Move(const wxPoint& pt)
1676 {
1677 wxPoint oldPos = GetPosition();
1678 SetPosition(pt);
1679 wxPoint offset = pt - oldPos;
1680
1681 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1682 while (node)
1683 {
1684 wxRichTextObject* child = node->GetData();
1685 wxPoint childPos = child->GetPosition() + offset;
1686 child->Move(childPos);
1687 node = node->GetNext();
1688 }
1689 }
1690
1691
1692 /*!
1693 * wxRichTextParagraphLayoutBox
1694 * This box knows how to lay out paragraphs.
1695 */
1696
1697 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextCompositeObject)
1698
1699 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject* parent):
1700 wxRichTextCompositeObject(parent)
1701 {
1702 Init();
1703 }
1704
1705 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1706 {
1707 if (m_floatCollector)
1708 {
1709 delete m_floatCollector;
1710 m_floatCollector = NULL;
1711 }
1712 }
1713
1714 /// Initialize the object.
1715 void wxRichTextParagraphLayoutBox::Init()
1716 {
1717 m_ctrl = NULL;
1718
1719 // For now, assume is the only box and has no initial size.
1720 m_range = wxRichTextRange(0, -1);
1721 m_ownRange = wxRichTextRange(0, -1);
1722
1723 m_invalidRange = wxRICHTEXT_ALL;
1724
1725 m_partialParagraph = false;
1726 m_floatCollector = NULL;
1727 }
1728
1729 void wxRichTextParagraphLayoutBox::Clear()
1730 {
1731 DeleteChildren();
1732
1733 if (m_floatCollector)
1734 delete m_floatCollector;
1735 m_floatCollector = NULL;
1736 m_partialParagraph = false;
1737 }
1738
1739 /// Copy
1740 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
1741 {
1742 Clear();
1743
1744 wxRichTextCompositeObject::Copy(obj);
1745
1746 m_partialParagraph = obj.m_partialParagraph;
1747 m_defaultAttributes = obj.m_defaultAttributes;
1748 }
1749
1750 // Gather information about floating objects; only gather floats for those paragraphs that
1751 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1752 // during layout.
1753 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect& availableRect, wxRichTextObject* untilObj)
1754 {
1755 if (m_floatCollector != NULL)
1756 delete m_floatCollector;
1757 m_floatCollector = new wxRichTextFloatCollector(availableRect);
1758 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1759 // Only gather floats up to the point we'll start formatting paragraphs.
1760 while (untilObj && node && node->GetData() != untilObj)
1761 {
1762 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1763 wxASSERT (child != NULL);
1764 if (child)
1765 m_floatCollector->CollectFloat(child);
1766 node = node->GetNext();
1767 }
1768
1769 return true;
1770 }
1771
1772 // Returns the style sheet associated with the overall buffer.
1773 wxRichTextStyleSheet* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1774 {
1775 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet*) NULL;
1776 }
1777
1778 // Get the number of floating objects at this level
1779 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1780 {
1781 if (m_floatCollector)
1782 return m_floatCollector->GetFloatingObjectCount();
1783 else
1784 return 0;
1785 }
1786
1787 // Get a list of floating objects
1788 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList& objects) const
1789 {
1790 if (m_floatCollector)
1791 {
1792 return m_floatCollector->GetFloatingObjects(objects);
1793 }
1794 else
1795 return false;
1796 }
1797
1798 // Calculate ranges
1799 void wxRichTextParagraphLayoutBox::UpdateRanges()
1800 {
1801 long start = 0;
1802 if (GetParent())
1803 start = GetRange().GetStart();
1804 long end;
1805 CalculateRange(start, end);
1806 }
1807
1808 // HitTest
1809 int wxRichTextParagraphLayoutBox::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
1810 {
1811 if (!IsShown())
1812 return wxRICHTEXT_HITTEST_NONE;
1813
1814 int ret = wxRICHTEXT_HITTEST_NONE;
1815 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector && (flags & wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS) == 0)
1816 ret = m_floatCollector->HitTest(dc, context, pt, textPosition, obj, flags);
1817
1818 if (ret == wxRICHTEXT_HITTEST_NONE)
1819 return wxRichTextCompositeObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
1820 else
1821 {
1822 *contextObj = this;
1823 return ret;
1824 }
1825 }
1826
1827 /// Draw the floating objects
1828 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
1829 {
1830 if (wxRichTextBuffer::GetFloatingLayoutMode() && m_floatCollector)
1831 m_floatCollector->Draw(dc, context, range, selection, rect, descent, style);
1832 }
1833
1834 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextObject* obj)
1835 {
1836 if (from == to)
1837 return;
1838
1839 from->RemoveChild(obj);
1840 to->AppendChild(obj);
1841 }
1842
1843 /// Draw the item
1844 bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
1845 {
1846 if (!IsShown())
1847 return true;
1848
1849 wxRect thisRect(GetPosition(), GetCachedSize());
1850
1851 wxRichTextAttr attr(GetAttributes());
1852 context.ApplyVirtualAttributes(attr, this);
1853
1854 int flags = style;
1855 if (selection.IsValid() && GetParentContainer() != this && selection.GetContainer() == this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1856 flags |= wxRICHTEXT_DRAW_SELECTED;
1857
1858 // Don't draw guidelines if at top level
1859 int theseFlags = flags;
1860 if (!GetParent())
1861 theseFlags &= ~wxRICHTEXT_DRAW_GUIDELINES;
1862 DrawBoxAttributes(dc, GetBuffer(), attr, thisRect, theseFlags, this);
1863
1864 if (wxRichTextBuffer::GetFloatingLayoutMode())
1865 DrawFloats(dc, context, range, selection, rect, descent, style);
1866
1867 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1868 while (node)
1869 {
1870 wxRichTextObject* child = node->GetData();
1871
1872 if (child && !child->GetRange().IsOutside(range))
1873 {
1874 wxRect childRect(child->GetPosition(), child->GetCachedSize());
1875 wxRichTextRange childRange = range;
1876 if (child->IsTopLevel())
1877 {
1878 childRange = child->GetOwnRange();
1879 }
1880
1881 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetTop() > rect.GetBottom())
1882 {
1883 // Stop drawing
1884 break;
1885 }
1886 else if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetBottom() < rect.GetTop())
1887 {
1888 // Skip
1889 }
1890 else
1891 child->Draw(dc, context, childRange, selection, rect, descent, style);
1892 }
1893
1894 node = node->GetNext();
1895 }
1896 return true;
1897 }
1898
1899 /// Lay the item out
1900 bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
1901 {
1902 SetPosition(rect.GetPosition());
1903
1904 if (!IsShown())
1905 return true;
1906
1907 wxRect availableSpace;
1908 bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
1909
1910 wxRichTextAttr attr(GetAttributes());
1911 context.ApplyVirtualAttributes(attr, this);
1912
1913 // If only laying out a specific area, the passed rect has a different meaning:
1914 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1915 // so that during a size, only the visible part will be relaid out, or
1916 // it would take too long causing flicker. As an approximation, we assume that
1917 // everything up to the start of the visible area is laid out correctly.
1918 if (formatRect)
1919 {
1920 wxRect rect2(0, 0, rect.width, rect.height);
1921 availableSpace = GetAvailableContentArea(dc, context, rect2);
1922
1923 // Invalidate the part of the buffer from the first visible line
1924 // to the end. If other parts of the buffer are currently invalid,
1925 // then they too will be taken into account if they are above
1926 // the visible point.
1927 long startPos = 0;
1928 wxRichTextLine* line = GetLineAtYPosition(rect.y);
1929 if (line)
1930 startPos = line->GetAbsoluteRange().GetStart();
1931
1932 Invalidate(wxRichTextRange(startPos, GetOwnRange().GetEnd()));
1933 }
1934 else
1935 {
1936 availableSpace = GetAvailableContentArea(dc, context, rect);
1937 }
1938
1939 // Fix the width if we're at the top level
1940 if (!GetParent())
1941 attr.GetTextBoxAttr().GetWidth().SetValue(rect.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
1942
1943 int leftMargin, rightMargin, topMargin, bottomMargin;
1944 wxRichTextObject::GetTotalMargin(dc, GetBuffer(), attr, leftMargin, rightMargin,
1945 topMargin, bottomMargin);
1946
1947 int maxWidth = 0;
1948 int maxHeight = 0;
1949
1950 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1951 int maxMaxWidth = 0;
1952
1953 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1954 int maxMinWidth = 0;
1955
1956 // If we have vertical alignment, we must recalculate everything.
1957 bool hasVerticalAlignment = (attr.GetTextBoxAttr().HasVerticalAlignment() &&
1958 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP));
1959
1960 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1961
1962 bool layoutAll = true;
1963
1964 // Get invalid range, rounding to paragraph start/end.
1965 wxRichTextRange invalidRange = GetInvalidRange(true);
1966
1967 if (invalidRange == wxRICHTEXT_NONE && !formatRect)
1968 return true;
1969
1970 if (invalidRange == wxRICHTEXT_ALL || hasVerticalAlignment)
1971 layoutAll = true;
1972 else // If we know what range is affected, start laying out from that point on.
1973 if (invalidRange.GetStart() >= GetOwnRange().GetStart())
1974 {
1975 wxRichTextParagraph* firstParagraph = GetParagraphAtPosition(invalidRange.GetStart());
1976 if (firstParagraph)
1977 {
1978 wxRichTextObjectList::compatibility_iterator firstNode = m_children.Find(firstParagraph);
1979 wxRichTextObjectList::compatibility_iterator previousNode;
1980 if ( firstNode )
1981 previousNode = firstNode->GetPrevious();
1982 if (firstNode)
1983 {
1984 if (previousNode)
1985 {
1986 wxRichTextParagraph* previousParagraph = wxDynamicCast(previousNode->GetData(), wxRichTextParagraph);
1987 availableSpace.y = previousParagraph->GetPosition().y + previousParagraph->GetCachedSize().y;
1988 }
1989
1990 // Now we're going to start iterating from the first affected paragraph.
1991 node = firstNode;
1992
1993 layoutAll = false;
1994 }
1995 }
1996 }
1997
1998 // Gather information about only those floating objects that will not be formatted,
1999 // after which floats will be gathered per-paragraph during layout.
2000 if (wxRichTextBuffer::GetFloatingLayoutMode())
2001 UpdateFloatingObjects(availableSpace, node ? node->GetData() : (wxRichTextObject*) NULL);
2002
2003 // A way to force speedy rest-of-buffer layout (the 'else' below)
2004 bool forceQuickLayout = false;
2005
2006 // First get the size of the paragraphs we won't be laying out
2007 wxRichTextObjectList::compatibility_iterator n = m_children.GetFirst();
2008 while (n && n != node)
2009 {
2010 wxRichTextParagraph* child = wxDynamicCast(n->GetData(), wxRichTextParagraph);
2011 if (child)
2012 {
2013 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2014 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2015 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2016 }
2017 n = n->GetNext();
2018 }
2019
2020 while (node)
2021 {
2022 // Assume this box only contains paragraphs
2023
2024 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2025 // Unsure if this is needed
2026 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
2027
2028 if (child && child->IsShown())
2029 {
2030 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
2031 if ( !forceQuickLayout &&
2032 (layoutAll ||
2033 child->GetLines().IsEmpty() ||
2034 !child->GetRange().IsOutside(invalidRange)) )
2035 {
2036 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2037 // lays out the object again using the minimum size
2038 child->LayoutToBestSize(dc, context, GetBuffer(),
2039 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
2040
2041 // Layout must set the cached size
2042 availableSpace.y += child->GetCachedSize().y;
2043 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2044 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2045 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2046
2047 // If we're just formatting the visible part of the buffer,
2048 // and we're now past the bottom of the window, and we don't have any
2049 // floating objects (since they may cause wrapping to change for the rest of the
2050 // the buffer), start quick layout.
2051 if (!hasVerticalAlignment && formatRect && child->GetPosition().y > rect.GetBottom() && GetFloatingObjectCount() == 0)
2052 forceQuickLayout = true;
2053 }
2054 else
2055 {
2056 // We're outside the immediately affected range, so now let's just
2057 // move everything up or down. This assumes that all the children have previously
2058 // been laid out and have wrapped line lists associated with them.
2059 // TODO: check all paragraphs before the affected range.
2060
2061 int inc = availableSpace.y - child->GetPosition().y;
2062
2063 while (node)
2064 {
2065 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2066 if (child)
2067 {
2068 if (child->GetLines().GetCount() == 0)
2069 {
2070 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
2071 // lays out the object again using the minimum size
2072 child->LayoutToBestSize(dc, context, GetBuffer(),
2073 attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
2074
2075 //child->Layout(dc, availableChildRect, style);
2076 }
2077 else
2078 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + inc));
2079
2080 availableSpace.y += child->GetCachedSize().y;
2081 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
2082 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
2083 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
2084 }
2085
2086 node = node->GetNext();
2087 }
2088 break;
2089 }
2090 }
2091
2092 node = node->GetNext();
2093 }
2094
2095 node = m_children.GetLast();
2096 if (node && node->GetData()->IsShown())
2097 {
2098 wxRichTextObject* child = node->GetData();
2099 maxHeight = child->GetPosition().y - (GetPosition().y + topMargin) + child->GetCachedSize().y;
2100 }
2101 else
2102 maxHeight = 0; // topMargin + bottomMargin;
2103
2104 // Check the bottom edge of any floating object
2105 if (wxRichTextBuffer::GetFloatingLayoutMode() && GetFloatCollector() && GetFloatCollector()->HasFloats())
2106 {
2107 int bottom = GetFloatCollector()->GetLastRectBottom();
2108 if (bottom > maxHeight)
2109 maxHeight = bottom;
2110 }
2111
2112 if (attr.GetTextBoxAttr().GetSize().GetWidth().IsValid())
2113 {
2114 wxRect r = AdjustAvailableSpace(dc, GetBuffer(), wxRichTextAttr() /* not used */, attr, parentRect, parentRect);
2115 int w = r.GetWidth();
2116
2117 // Convert external to content rect
2118 w = w - leftMargin - rightMargin;
2119 maxWidth = wxMax(maxWidth, w);
2120 maxMaxWidth = wxMax(maxMaxWidth, w);
2121 }
2122 else
2123 {
2124 // TODO: Make sure the layout box's position reflects
2125 // the position of the children, but without
2126 // breaking layout of a box within a paragraph.
2127 }
2128
2129 // TODO: (also in para layout) should set the
2130 // object's size to an absolute one if specified,
2131 // but if not specified, calculate it from content.
2132
2133 // We need to add back the margins etc.
2134 {
2135 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2136 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, maxHeight));
2137 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
2138 SetCachedSize(marginRect.GetSize());
2139 }
2140
2141 // The maximum size is the greatest of all maximum widths for all paragraphs.
2142 {
2143 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2144 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMaxWidth, maxHeight)); // Actually max height is a lie, we can't know it
2145 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
2146 SetMaxSize(marginRect.GetSize());
2147 }
2148
2149 // The minimum size is the greatest of all minimum widths for all paragraphs.
2150 {
2151 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2152 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMinWidth, maxHeight)); // Actually max height is a lie, we can't know it
2153 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
2154 SetMinSize(marginRect.GetSize());
2155 }
2156
2157 if (attr.GetTextBoxAttr().HasVerticalAlignment() &&
2158 (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP))
2159 {
2160 int yOffset = 0;
2161 int leftOverSpace = availableSpace.height - topMargin - bottomMargin - maxHeight;
2162 if (leftOverSpace > 0)
2163 {
2164 if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE)
2165 {
2166 yOffset = (leftOverSpace/2);
2167 }
2168 else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM)
2169 {
2170 yOffset = leftOverSpace;
2171 }
2172 }
2173
2174 // Move all the children to vertically align the content
2175 // This doesn't take into account floating objects, unfortunately.
2176 if (yOffset != 0)
2177 {
2178 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2179 while (node)
2180 {
2181 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2182 if (child)
2183 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + yOffset));
2184
2185 node = node->GetNext();
2186 }
2187 }
2188 }
2189
2190 m_invalidRange = wxRICHTEXT_NONE;
2191
2192 return true;
2193 }
2194
2195 /// Get/set the size for the given range.
2196 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
2197 {
2198 wxSize sz;
2199
2200 wxRichTextObjectList::compatibility_iterator startPara = wxRichTextObjectList::compatibility_iterator();
2201 wxRichTextObjectList::compatibility_iterator endPara = wxRichTextObjectList::compatibility_iterator();
2202
2203 // First find the first paragraph whose starting position is within the range.
2204 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2205 while (node)
2206 {
2207 // child is a paragraph
2208 wxRichTextObject* child = node->GetData();
2209 const wxRichTextRange& r = child->GetRange();
2210
2211 if (r.GetStart() <= range.GetStart() && r.GetEnd() >= range.GetStart())
2212 {
2213 startPara = node;
2214 break;
2215 }
2216
2217 node = node->GetNext();
2218 }
2219
2220 // Next find the last paragraph containing part of the range
2221 node = m_children.GetFirst();
2222 while (node)
2223 {
2224 // child is a paragraph
2225 wxRichTextObject* child = node->GetData();
2226 const wxRichTextRange& r = child->GetRange();
2227
2228 if (r.GetStart() <= range.GetEnd() && r.GetEnd() >= range.GetEnd())
2229 {
2230 endPara = node;
2231 break;
2232 }
2233
2234 node = node->GetNext();
2235 }
2236
2237 if (!startPara || !endPara)
2238 return false;
2239
2240 // Now we can add up the sizes
2241 for (node = startPara; node ; node = node->GetNext())
2242 {
2243 // child is a paragraph
2244 wxRichTextObject* child = node->GetData();
2245 const wxRichTextRange& childRange = child->GetRange();
2246 wxRichTextRange rangeToFind = range;
2247 rangeToFind.LimitTo(childRange);
2248
2249 if (child->IsTopLevel())
2250 rangeToFind = child->GetOwnRange();
2251
2252 wxSize childSize;
2253
2254 int childDescent = 0;
2255 child->GetRangeSize(rangeToFind, childSize, childDescent, dc, context, flags, position, parentSize);
2256
2257 descent = wxMax(childDescent, descent);
2258
2259 sz.x = wxMax(sz.x, childSize.x);
2260 sz.y += childSize.y;
2261
2262 if (node == endPara)
2263 break;
2264 }
2265
2266 size = sz;
2267
2268 return true;
2269 }
2270
2271 /// Get the paragraph at the given position
2272 wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos, bool caretPosition) const
2273 {
2274 if (caretPosition)
2275 pos ++;
2276
2277 // First find the first paragraph whose starting position is within the range.
2278 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2279 while (node)
2280 {
2281 // child is a paragraph
2282 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2283 // wxASSERT (child != NULL);
2284
2285 if (child)
2286 {
2287 // Return first child in buffer if position is -1
2288 // if (pos == -1)
2289 // return child;
2290
2291 if (child->GetRange().Contains(pos))
2292 return child;
2293 }
2294
2295 node = node->GetNext();
2296 }
2297 return NULL;
2298 }
2299
2300 /// Get the line at the given position
2301 wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos, bool caretPosition) const
2302 {
2303 if (caretPosition)
2304 pos ++;
2305
2306 // First find the first paragraph whose starting position is within the range.
2307 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2308 while (node)
2309 {
2310 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
2311 if (obj->GetRange().Contains(pos))
2312 {
2313 // child is a paragraph
2314 wxRichTextParagraph* child = wxDynamicCast(obj, wxRichTextParagraph);
2315 // wxASSERT (child != NULL);
2316
2317 if (child)
2318 {
2319 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2320 while (node2)
2321 {
2322 wxRichTextLine* line = node2->GetData();
2323
2324 wxRichTextRange range = line->GetAbsoluteRange();
2325
2326 if (range.Contains(pos) ||
2327
2328 // If the position is end-of-paragraph, then return the last line of
2329 // of the paragraph.
2330 ((range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd())))
2331 return line;
2332
2333 node2 = node2->GetNext();
2334 }
2335 }
2336 }
2337
2338 node = node->GetNext();
2339 }
2340
2341 int lineCount = GetLineCount();
2342 if (lineCount > 0)
2343 return GetLineForVisibleLineNumber(lineCount-1);
2344 else
2345 return NULL;
2346 }
2347
2348 /// Get the line at the given y pixel position, or the last line.
2349 wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y) const
2350 {
2351 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2352 while (node)
2353 {
2354 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2355 // wxASSERT (child != NULL);
2356
2357 if (child)
2358 {
2359 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2360 while (node2)
2361 {
2362 wxRichTextLine* line = node2->GetData();
2363
2364 wxRect rect(line->GetRect());
2365
2366 if (y <= rect.GetBottom())
2367 return line;
2368
2369 node2 = node2->GetNext();
2370 }
2371 }
2372
2373 node = node->GetNext();
2374 }
2375
2376 // Return last line
2377 int lineCount = GetLineCount();
2378 if (lineCount > 0)
2379 return GetLineForVisibleLineNumber(lineCount-1);
2380 else
2381 return NULL;
2382 }
2383
2384 /// Get the number of visible lines
2385 int wxRichTextParagraphLayoutBox::GetLineCount() const
2386 {
2387 int count = 0;
2388
2389 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2390 while (node)
2391 {
2392 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2393 // wxASSERT (child != NULL);
2394
2395 if (child)
2396 count += child->GetLines().GetCount();
2397
2398 node = node->GetNext();
2399 }
2400 return count;
2401 }
2402
2403
2404 /// Get the paragraph for a given line
2405 wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine* line) const
2406 {
2407 return GetParagraphAtPosition(line->GetAbsoluteRange().GetStart());
2408 }
2409
2410 /// Get the line size at the given position
2411 wxSize wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos, bool caretPosition) const
2412 {
2413 wxRichTextLine* line = GetLineAtPosition(pos, caretPosition);
2414 if (line)
2415 {
2416 return line->GetSize();
2417 }
2418 else
2419 return wxSize(0, 0);
2420 }
2421
2422
2423 /// Convenience function to add a paragraph of text
2424 wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph(const wxString& text, wxRichTextAttr* paraStyle)
2425 {
2426 // Don't use the base style, just the default style, and the base style will
2427 // be combined at display time.
2428 // Divide into paragraph and character styles.
2429
2430 wxRichTextAttr defaultCharStyle;
2431 wxRichTextAttr defaultParaStyle;
2432
2433 // If the default style is a named paragraph style, don't apply any character formatting
2434 // to the initial text string.
2435 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2436 {
2437 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2438 if (def)
2439 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2440 }
2441 else
2442 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2443
2444 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2445 wxRichTextAttr* cStyle = & defaultCharStyle;
2446
2447 wxRichTextParagraph* para = new wxRichTextParagraph(text, this, pStyle, cStyle);
2448 para->GetAttributes().GetTextBoxAttr().Reset();
2449
2450 AppendChild(para);
2451
2452 UpdateRanges();
2453
2454 return para->GetRange();
2455 }
2456
2457 /// Adds multiple paragraphs, based on newlines.
2458 wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text, wxRichTextAttr* paraStyle)
2459 {
2460 // Don't use the base style, just the default style, and the base style will
2461 // be combined at display time.
2462 // Divide into paragraph and character styles.
2463
2464 wxRichTextAttr defaultCharStyle;
2465 wxRichTextAttr defaultParaStyle;
2466
2467 // If the default style is a named paragraph style, don't apply any character formatting
2468 // to the initial text string.
2469 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2470 {
2471 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2472 if (def)
2473 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2474 }
2475 else
2476 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2477
2478 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2479 wxRichTextAttr* cStyle = & defaultCharStyle;
2480
2481 wxRichTextParagraph* firstPara = NULL;
2482 wxRichTextParagraph* lastPara = NULL;
2483
2484 wxRichTextRange range(-1, -1);
2485
2486 size_t i = 0;
2487 size_t len = text.length();
2488 wxString line;
2489 wxRichTextParagraph* para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
2490 para->GetAttributes().GetTextBoxAttr().Reset();
2491
2492 AppendChild(para);
2493
2494 firstPara = para;
2495 lastPara = para;
2496
2497 while (i < len)
2498 {
2499 wxChar ch = text[i];
2500 if (ch == wxT('\n') || ch == wxT('\r'))
2501 {
2502 if (i != (len-1))
2503 {
2504 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2505 plainText->SetText(line);
2506
2507 para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
2508 para->GetAttributes().GetTextBoxAttr().Reset();
2509
2510 AppendChild(para);
2511
2512 lastPara = para;
2513 line = wxEmptyString;
2514 }
2515 }
2516 else
2517 line += ch;
2518
2519 i ++;
2520 }
2521
2522 if (!line.empty())
2523 {
2524 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2525 plainText->SetText(line);
2526 }
2527
2528 UpdateRanges();
2529
2530 return wxRichTextRange(firstPara->GetRange().GetStart(), lastPara->GetRange().GetEnd());
2531 }
2532
2533 /// Convenience function to add an image
2534 wxRichTextRange wxRichTextParagraphLayoutBox::AddImage(const wxImage& image, wxRichTextAttr* paraStyle)
2535 {
2536 // Don't use the base style, just the default style, and the base style will
2537 // be combined at display time.
2538 // Divide into paragraph and character styles.
2539
2540 wxRichTextAttr defaultCharStyle;
2541 wxRichTextAttr defaultParaStyle;
2542
2543 // If the default style is a named paragraph style, don't apply any character formatting
2544 // to the initial text string.
2545 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2546 {
2547 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2548 if (def)
2549 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2550 }
2551 else
2552 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2553
2554 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2555 wxRichTextAttr* cStyle = & defaultCharStyle;
2556
2557 wxRichTextParagraph* para = new wxRichTextParagraph(this, pStyle);
2558 para->GetAttributes().GetTextBoxAttr().Reset();
2559 AppendChild(para);
2560 para->AppendChild(new wxRichTextImage(image, this, cStyle));
2561
2562 UpdateRanges();
2563
2564 return para->GetRange();
2565 }
2566
2567
2568 /// Insert fragment into this box at the given position. If partialParagraph is true,
2569 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2570 /// marker.
2571
2572 bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextParagraphLayoutBox& fragment)
2573 {
2574 // First, find the first paragraph whose starting position is within the range.
2575 wxRichTextParagraph* para = GetParagraphAtPosition(position);
2576 if (para)
2577 {
2578 wxRichTextAttr originalAttr = para->GetAttributes();
2579
2580 wxRichTextObjectList::compatibility_iterator node = m_children.Find(para);
2581
2582 // Now split at this position, returning the object to insert the new
2583 // ones in front of.
2584 wxRichTextObject* nextObject = para->SplitAt(position);
2585
2586 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2587 // text, for example, so let's optimize.
2588
2589 if (fragment.GetPartialParagraph() && fragment.GetChildren().GetCount() == 1)
2590 {
2591 // Add the first para to this para...
2592 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2593 if (!firstParaNode)
2594 return false;
2595
2596 // Iterate through the fragment paragraph inserting the content into this paragraph.
2597 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2598 wxASSERT (firstPara != NULL);
2599
2600 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
2601 while (objectNode)
2602 {
2603 wxRichTextObject* newObj = objectNode->GetData()->Clone();
2604
2605 if (!nextObject)
2606 {
2607 // Append
2608 para->AppendChild(newObj);
2609 }
2610 else
2611 {
2612 // Insert before nextObject
2613 para->InsertChild(newObj, nextObject);
2614 }
2615
2616 objectNode = objectNode->GetNext();
2617 }
2618
2619 return true;
2620 }
2621 else
2622 {
2623 // Procedure for inserting a fragment consisting of a number of
2624 // paragraphs:
2625 //
2626 // 1. Remove and save the content that's after the insertion point, for adding
2627 // back once we've added the fragment.
2628 // 2. Add the content from the first fragment paragraph to the current
2629 // paragraph.
2630 // 3. Add remaining fragment paragraphs after the current paragraph.
2631 // 4. Add back the saved content from the first paragraph. If partialParagraph
2632 // is true, add it to the last paragraph added and not a new one.
2633
2634 // 1. Remove and save objects after split point.
2635 wxList savedObjects;
2636 if (nextObject)
2637 para->MoveToList(nextObject, savedObjects);
2638
2639 // 2. Add the content from the 1st fragment paragraph.
2640 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2641 if (!firstParaNode)
2642 return false;
2643
2644 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2645 wxASSERT(firstPara != NULL);
2646
2647 if (!(fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE))
2648 para->SetAttributes(firstPara->GetAttributes());
2649
2650 // Save empty paragraph attributes for appending later
2651 // These are character attributes deliberately set for a new paragraph. Without this,
2652 // we couldn't pass default attributes when appending a new paragraph.
2653 wxRichTextAttr emptyParagraphAttributes;
2654
2655 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
2656
2657 if (objectNode && firstPara->GetChildren().GetCount() == 1 && objectNode->GetData()->IsEmpty())
2658 emptyParagraphAttributes = objectNode->GetData()->GetAttributes();
2659
2660 while (objectNode)
2661 {
2662 wxRichTextObject* newObj = objectNode->GetData()->Clone();
2663
2664 // Append
2665 para->AppendChild(newObj);
2666
2667 objectNode = objectNode->GetNext();
2668 }
2669
2670 // 3. Add remaining fragment paragraphs after the current paragraph.
2671 wxRichTextObjectList::compatibility_iterator nextParagraphNode = node->GetNext();
2672 wxRichTextObject* nextParagraph = NULL;
2673 if (nextParagraphNode)
2674 nextParagraph = nextParagraphNode->GetData();
2675
2676 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst()->GetNext();
2677 wxRichTextParagraph* finalPara = para;
2678
2679 bool needExtraPara = (!i || !fragment.GetPartialParagraph());
2680
2681 // If there was only one paragraph, we need to insert a new one.
2682 while (i)
2683 {
2684 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2685 wxASSERT( para != NULL );
2686
2687 finalPara = (wxRichTextParagraph*) para->Clone();
2688
2689 if (nextParagraph)
2690 InsertChild(finalPara, nextParagraph);
2691 else
2692 AppendChild(finalPara);
2693
2694 i = i->GetNext();
2695 }
2696
2697 // If there was only one paragraph, or we have full paragraphs in our fragment,
2698 // we need to insert a new one.
2699 if (needExtraPara)
2700 {
2701 finalPara = new wxRichTextParagraph;
2702
2703 if (nextParagraph)
2704 InsertChild(finalPara, nextParagraph);
2705 else
2706 AppendChild(finalPara);
2707 }
2708
2709 // 4. Add back the remaining content.
2710 if (finalPara)
2711 {
2712 if (nextObject)
2713 finalPara->MoveFromList(savedObjects);
2714
2715 // Ensure there's at least one object
2716 if (finalPara->GetChildCount() == 0)
2717 {
2718 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
2719 text->SetAttributes(emptyParagraphAttributes);
2720
2721 finalPara->AppendChild(text);
2722 }
2723 }
2724
2725 if ((fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE) && firstPara)
2726 finalPara->SetAttributes(firstPara->GetAttributes());
2727 else if (finalPara && finalPara != para)
2728 finalPara->SetAttributes(originalAttr);
2729
2730 return true;
2731 }
2732 }
2733 else
2734 {
2735 // Append
2736 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst();
2737 while (i)
2738 {
2739 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2740 wxASSERT( para != NULL );
2741
2742 AppendChild(para->Clone());
2743
2744 i = i->GetNext();
2745 }
2746
2747 return true;
2748 }
2749 }
2750
2751 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2752 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2753 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange& range, wxRichTextParagraphLayoutBox& fragment)
2754 {
2755 wxRichTextObjectList::compatibility_iterator i = GetChildren().GetFirst();
2756 while (i)
2757 {
2758 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2759 wxASSERT( para != NULL );
2760
2761 if (!para->GetRange().IsOutside(range))
2762 {
2763 fragment.AppendChild(para->Clone());
2764 }
2765 i = i->GetNext();
2766 }
2767
2768 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2769 if (!fragment.IsEmpty())
2770 {
2771 wxRichTextParagraph* firstPara = wxDynamicCast(fragment.GetChildren().GetFirst()->GetData(), wxRichTextParagraph);
2772 wxASSERT( firstPara != NULL );
2773
2774 wxRichTextParagraph* lastPara = wxDynamicCast(fragment.GetChildren().GetLast()->GetData(), wxRichTextParagraph);
2775 wxASSERT( lastPara != NULL );
2776
2777 if (!firstPara || !lastPara)
2778 return false;
2779
2780 bool isFragment = (range.GetEnd() < lastPara->GetRange().GetEnd());
2781
2782 long firstPos = firstPara->GetRange().GetStart();
2783
2784 // Adjust for renumbering from zero
2785 wxRichTextRange topTailRange(range.GetStart() - firstPos, range.GetEnd() - firstPos);
2786
2787 long end;
2788 fragment.CalculateRange(0, end);
2789
2790 // Chop off the start of the paragraph
2791 if (topTailRange.GetStart() > 0)
2792 {
2793 wxRichTextRange r(0, topTailRange.GetStart()-1);
2794 firstPara->DeleteRange(r);
2795
2796 // Make sure the numbering is correct
2797 fragment.CalculateRange(0, end);
2798
2799 // Now, we've deleted some positions, so adjust the range
2800 // accordingly.
2801 topTailRange.SetStart(range.GetLength());
2802 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
2803 }
2804 else
2805 {
2806 topTailRange.SetStart(range.GetLength());
2807 topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
2808 }
2809
2810 if (topTailRange.GetStart() < lastPara->GetRange().GetEnd())
2811 {
2812 lastPara->DeleteRange(topTailRange);
2813
2814 // Make sure the numbering is correct
2815 long end;
2816 fragment.CalculateRange(0, end);
2817
2818 // We only have part of a paragraph at the end
2819 fragment.SetPartialParagraph(true);
2820 }
2821 else
2822 {
2823 // We have a partial paragraph (don't save last new paragraph marker)
2824 // or complete paragraph
2825 fragment.SetPartialParagraph(isFragment);
2826 }
2827 }
2828
2829 return true;
2830 }
2831
2832 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2833 /// starting from zero at the start of the buffer.
2834 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos, bool caretPosition, bool startOfLine) const
2835 {
2836 if (caretPosition)
2837 pos ++;
2838
2839 int lineCount = 0;
2840
2841 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2842 while (node)
2843 {
2844 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2845 // wxASSERT( child != NULL );
2846
2847 if (child)
2848 {
2849 if (child->GetRange().Contains(pos))
2850 {
2851 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2852 while (node2)
2853 {
2854 wxRichTextLine* line = node2->GetData();
2855 wxRichTextRange lineRange = line->GetAbsoluteRange();
2856
2857 if (lineRange.Contains(pos) || pos == lineRange.GetStart())
2858 {
2859 // If the caret is displayed at the end of the previous wrapped line,
2860 // we want to return the line it's _displayed_ at (not the actual line
2861 // containing the position).
2862 if (lineRange.GetStart() == pos && !startOfLine && child->GetRange().GetStart() != pos)
2863 return lineCount - 1;
2864 else
2865 return lineCount;
2866 }
2867
2868 lineCount ++;
2869
2870 node2 = node2->GetNext();
2871 }
2872 // If we didn't find it in the lines, it must be
2873 // the last position of the paragraph. So return the last line.
2874 return lineCount-1;
2875 }
2876 else
2877 lineCount += child->GetLines().GetCount();
2878 }
2879
2880 node = node->GetNext();
2881 }
2882
2883 // Not found
2884 return -1;
2885 }
2886
2887 /// Given a line number, get the corresponding wxRichTextLine object.
2888 wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber) const
2889 {
2890 int lineCount = 0;
2891
2892 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2893 while (node)
2894 {
2895 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2896 // wxASSERT(child != NULL);
2897
2898 if (child)
2899 {
2900 if (lineNumber < (int) (child->GetLines().GetCount() + lineCount))
2901 {
2902 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2903 while (node2)
2904 {
2905 wxRichTextLine* line = node2->GetData();
2906
2907 if (lineCount == lineNumber)
2908 return line;
2909
2910 lineCount ++;
2911
2912 node2 = node2->GetNext();
2913 }
2914 }
2915 else
2916 lineCount += child->GetLines().GetCount();
2917 }
2918
2919 node = node->GetNext();
2920 }
2921
2922 // Didn't find it
2923 return NULL;
2924 }
2925
2926 /// Delete range from layout.
2927 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange& range)
2928 {
2929 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2930
2931 wxRichTextParagraph* firstPara = NULL;
2932 while (node)
2933 {
2934 wxRichTextParagraph* obj = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2935 // wxASSERT (obj != NULL);
2936
2937 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
2938
2939 if (obj)
2940 {
2941 // Delete the range in each paragraph
2942
2943 if (!obj->GetRange().IsOutside(range))
2944 {
2945 // Deletes the content of this object within the given range
2946 obj->DeleteRange(range);
2947
2948 wxRichTextRange thisRange = obj->GetRange();
2949 wxRichTextAttr thisAttr = obj->GetAttributes();
2950
2951 // If the whole paragraph is within the range to delete,
2952 // delete the whole thing.
2953 if (range.GetStart() <= thisRange.GetStart() && range.GetEnd() >= thisRange.GetEnd())
2954 {
2955 // Delete the whole object
2956 RemoveChild(obj, true);
2957 obj = NULL;
2958 }
2959 else if (!firstPara)
2960 firstPara = obj;
2961
2962 // If the range includes the paragraph end, we need to join this
2963 // and the next paragraph.
2964 if (range.GetEnd() <= thisRange.GetEnd())
2965 {
2966 // We need to move the objects from the next paragraph
2967 // to this paragraph
2968
2969 wxRichTextParagraph* nextParagraph = NULL;
2970 if ((range.GetEnd() < thisRange.GetEnd()) && obj)
2971 nextParagraph = obj;
2972 else
2973 {
2974 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2975 if (next)
2976 nextParagraph = wxDynamicCast(next->GetData(), wxRichTextParagraph);
2977 }
2978
2979 bool applyFinalParagraphStyle = firstPara && nextParagraph && nextParagraph != firstPara;
2980
2981 wxRichTextAttr nextParaAttr;
2982 if (applyFinalParagraphStyle)
2983 {
2984 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2985 // not the next one.
2986 if (range.GetStart() == range.GetEnd() && range.GetStart() == thisRange.GetEnd())
2987 nextParaAttr = thisAttr;
2988 else
2989 nextParaAttr = nextParagraph->GetAttributes();
2990 }
2991
2992 if (firstPara && nextParagraph && firstPara != nextParagraph)
2993 {
2994 // Move the objects to the previous para
2995 wxRichTextObjectList::compatibility_iterator node1 = nextParagraph->GetChildren().GetFirst();
2996
2997 while (node1)
2998 {
2999 wxRichTextObject* obj1 = node1->GetData();
3000
3001 firstPara->AppendChild(obj1);
3002
3003 wxRichTextObjectList::compatibility_iterator next1 = node1->GetNext();
3004 nextParagraph->GetChildren().Erase(node1);
3005
3006 node1 = next1;
3007 }
3008
3009 // Delete the paragraph
3010 RemoveChild(nextParagraph, true);
3011 }
3012
3013 // Avoid empty paragraphs
3014 if (firstPara && firstPara->GetChildren().GetCount() == 0)
3015 {
3016 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
3017 firstPara->AppendChild(text);
3018 }
3019
3020 if (applyFinalParagraphStyle)
3021 firstPara->SetAttributes(nextParaAttr);
3022
3023 return true;
3024 }
3025 }
3026 }
3027
3028 node = next;
3029 }
3030
3031 return true;
3032 }
3033
3034 /// Get any text in this object for the given range
3035 wxString wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange& range) const
3036 {
3037 int lineCount = 0;
3038 wxString text;
3039 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3040 while (node)
3041 {
3042 wxRichTextObject* child = node->GetData();
3043 if (!child->GetRange().IsOutside(range))
3044 {
3045 wxRichTextRange childRange = range;
3046 childRange.LimitTo(child->GetRange());
3047
3048 wxString childText = child->GetTextForRange(childRange);
3049
3050 text += childText;
3051
3052 if ((childRange.GetEnd() == child->GetRange().GetEnd()) && node->GetNext())
3053 text += wxT("\n");
3054
3055 lineCount ++;
3056 }
3057 node = node->GetNext();
3058 }
3059
3060 return text;
3061 }
3062
3063 /// Get all the text
3064 wxString wxRichTextParagraphLayoutBox::GetText() const
3065 {
3066 return GetTextForRange(GetOwnRange());
3067 }
3068
3069 /// Get the paragraph by number
3070 wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber) const
3071 {
3072 if ((size_t) paragraphNumber >= GetChildCount())
3073 return NULL;
3074
3075 return (wxRichTextParagraph*) GetChild((size_t) paragraphNumber);
3076 }
3077
3078 /// Get the length of the paragraph
3079 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber) const
3080 {
3081 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3082 if (para)
3083 return para->GetRange().GetLength() - 1; // don't include newline
3084 else
3085 return 0;
3086 }
3087
3088 /// Get the text of the paragraph
3089 wxString wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber) const
3090 {
3091 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
3092 if (para)
3093 return para->GetTextForRange(para->GetRange());
3094 else
3095 return wxEmptyString;
3096 }
3097
3098 /// Convert zero-based line column and paragraph number to a position.
3099 long wxRichTextParagraphLayoutBox::XYToPosition(long x, long y) const
3100 {
3101 wxRichTextParagraph* para = GetParagraphAtLine(y);
3102 if (para)
3103 {
3104 return para->GetRange().GetStart() + x;
3105 }
3106 else
3107 return -1;
3108 }
3109
3110 /// Convert zero-based position to line column and paragraph number
3111 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos, long* x, long* y) const
3112 {
3113 wxRichTextParagraph* para = GetParagraphAtPosition(pos);
3114 if (para)
3115 {
3116 int count = 0;
3117 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3118 while (node)
3119 {
3120 wxRichTextObject* child = node->GetData();
3121 if (child == para)
3122 break;
3123 count ++;
3124 node = node->GetNext();
3125 }
3126
3127 *y = count;
3128 *x = pos - para->GetRange().GetStart();
3129
3130 return true;
3131 }
3132 else
3133 return false;
3134 }
3135
3136 /// Get the leaf object in a paragraph at this position.
3137 /// Given a line number, get the corresponding wxRichTextLine object.
3138 wxRichTextObject* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position) const
3139 {
3140 wxRichTextParagraph* para = GetParagraphAtPosition(position);
3141 if (para)
3142 {
3143 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
3144
3145 while (node)
3146 {
3147 wxRichTextObject* child = node->GetData();
3148 if (child->GetRange().Contains(position))
3149 return child;
3150
3151 node = node->GetNext();
3152 }
3153 if (position == para->GetRange().GetEnd() && para->GetChildCount() > 0)
3154 return para->GetChildren().GetLast()->GetData();
3155 }
3156 return NULL;
3157 }
3158
3159 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3160 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, int flags)
3161 {
3162 bool characterStyle = false;
3163 bool paragraphStyle = false;
3164
3165 if (style.IsCharacterStyle())
3166 characterStyle = true;
3167 if (style.IsParagraphStyle())
3168 paragraphStyle = true;
3169
3170 wxRichTextBuffer* buffer = GetBuffer();
3171
3172 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3173 bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3174 bool parasOnly = ((flags & wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY) != 0);
3175 bool charactersOnly = ((flags & wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY) != 0);
3176 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
3177 bool removeStyle = ((flags & wxRICHTEXT_SETSTYLE_REMOVE) != 0);
3178
3179 // Apply paragraph style first, if any
3180 wxRichTextAttr wholeStyle(style);
3181
3182 if (!removeStyle && wholeStyle.HasParagraphStyleName() && buffer->GetStyleSheet())
3183 {
3184 wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->FindParagraphStyle(wholeStyle.GetParagraphStyleName());
3185 if (def)
3186 wxRichTextApplyStyle(wholeStyle, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
3187 }
3188
3189 // Limit the attributes to be set to the content to only character attributes.
3190 wxRichTextAttr characterAttributes(wholeStyle);
3191 characterAttributes.SetFlags(characterAttributes.GetFlags() & (wxTEXT_ATTR_CHARACTER));
3192
3193 if (!removeStyle && characterAttributes.HasCharacterStyleName() && buffer->GetStyleSheet())
3194 {
3195 wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->FindCharacterStyle(characterAttributes.GetCharacterStyleName());
3196 if (def)
3197 wxRichTextApplyStyle(characterAttributes, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
3198 }
3199
3200 // If we are associated with a control, make undoable; otherwise, apply immediately
3201 // to the data.
3202
3203 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3204
3205 wxRichTextAction* action = NULL;
3206
3207 if (haveControl && withUndo)
3208 {
3209 action = new wxRichTextAction(NULL, _("Change Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
3210 action->SetRange(range);
3211 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3212 }
3213
3214 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3215 while (node)
3216 {
3217 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3218 // wxASSERT (para != NULL);
3219
3220 if (para && para->GetChildCount() > 0)
3221 {
3222 // Stop searching if we're beyond the range of interest
3223 if (para->GetRange().GetStart() > range.GetEnd())
3224 break;
3225
3226 if (!para->GetRange().IsOutside(range))
3227 {
3228 // We'll be using a copy of the paragraph to make style changes,
3229 // not updating the buffer directly.
3230 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3231
3232 if (haveControl && withUndo)
3233 {
3234 newPara = new wxRichTextParagraph(*para);
3235 action->GetNewParagraphs().AppendChild(newPara);
3236
3237 // Also store the old ones for Undo
3238 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3239 }
3240 else
3241 newPara = para;
3242
3243 // If we're specifying paragraphs only, then we really mean character formatting
3244 // to be included in the paragraph style
3245 if ((paragraphStyle || parasOnly) && !charactersOnly)
3246 {
3247 if (removeStyle)
3248 {
3249 // Removes the given style from the paragraph
3250 wxRichTextRemoveStyle(newPara->GetAttributes(), style);
3251 }
3252 else if (resetExistingStyle)
3253 newPara->GetAttributes() = wholeStyle;
3254 else
3255 {
3256 if (applyMinimal)
3257 {
3258 // Only apply attributes that will make a difference to the combined
3259 // style as seen on the display
3260 wxRichTextAttr combinedAttr(para->GetCombinedAttributes(true));
3261 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle, & combinedAttr);
3262 }
3263 else
3264 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle);
3265 }
3266 }
3267
3268 // When applying paragraph styles dynamically, don't change the text objects' attributes
3269 // since they will computed as needed. Only apply the character styling if it's _only_
3270 // character styling. This policy is subject to change and might be put under user control.
3271
3272 // Hm. we might well be applying a mix of paragraph and character styles, in which
3273 // case we _do_ want to apply character styles regardless of what para styles are set.
3274 // But if we're applying a paragraph style, which has some character attributes, but
3275 // we only want the paragraphs to hold this character style, then we _don't_ want to
3276 // apply the character style. So we need to be able to choose.
3277
3278 if (!parasOnly && (characterStyle|charactersOnly) && range.GetStart() != newPara->GetRange().GetEnd())
3279 {
3280 wxRichTextRange childRange(range);
3281 childRange.LimitTo(newPara->GetRange());
3282
3283 // Find the starting position and if necessary split it so
3284 // we can start applying a different style.
3285 // TODO: check that the style actually changes or is different
3286 // from style outside of range
3287 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3288 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
3289
3290 if (childRange.GetStart() == newPara->GetRange().GetStart())
3291 firstObject = newPara->GetChildren().GetFirst()->GetData();
3292 else
3293 firstObject = newPara->SplitAt(range.GetStart());
3294
3295 // Increment by 1 because we're apply the style one _after_ the split point
3296 long splitPoint = childRange.GetEnd();
3297 if (splitPoint != newPara->GetRange().GetEnd())
3298 splitPoint ++;
3299
3300 // Find last object
3301 if (splitPoint == newPara->GetRange().GetEnd())
3302 lastObject = newPara->GetChildren().GetLast()->GetData();
3303 else
3304 // lastObject is set as a side-effect of splitting. It's
3305 // returned as the object before the new object.
3306 (void) newPara->SplitAt(splitPoint, & lastObject);
3307
3308 wxASSERT(firstObject != NULL);
3309 wxASSERT(lastObject != NULL);
3310
3311 if (!firstObject || !lastObject)
3312 continue;
3313
3314 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3315 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
3316
3317 wxASSERT(firstNode);
3318 wxASSERT(lastNode);
3319
3320 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
3321
3322 while (node2)
3323 {
3324 wxRichTextObject* child = node2->GetData();
3325
3326 if (removeStyle)
3327 {
3328 // Removes the given style from the paragraph
3329 wxRichTextRemoveStyle(child->GetAttributes(), style);
3330 }
3331 else if (resetExistingStyle)
3332 {
3333 // Preserve the URL as it's not really a formatting style but a property of the object
3334 wxString url;
3335 if (child->GetAttributes().HasURL() && !characterAttributes.HasURL())
3336 url = child->GetAttributes().GetURL();
3337
3338 child->GetAttributes() = characterAttributes;
3339
3340 if (!url.IsEmpty())
3341 child->GetAttributes().SetURL(url);
3342 }
3343 else
3344 {
3345 if (applyMinimal)
3346 {
3347 // Only apply attributes that will make a difference to the combined
3348 // style as seen on the display
3349 wxRichTextAttr combinedAttr(newPara->GetCombinedAttributes(child->GetAttributes(), true));
3350 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes, & combinedAttr);
3351 }
3352 else
3353 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes);
3354 }
3355
3356 if (node2 == lastNode)
3357 break;
3358
3359 node2 = node2->GetNext();
3360 }
3361 }
3362 }
3363 }
3364
3365 node = node->GetNext();
3366 }
3367
3368 // Do action, or delay it until end of batch.
3369 if (haveControl && withUndo)
3370 buffer->SubmitAction(action);
3371
3372 return true;
3373 }
3374
3375 // Just change the attributes for this single object.
3376 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject* obj, const wxRichTextAttr& textAttr, int flags)
3377 {
3378 wxRichTextBuffer* buffer = GetBuffer();
3379 bool withUndo = flags & wxRICHTEXT_SETSTYLE_WITH_UNDO;
3380 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
3381 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3382
3383 wxRichTextAction *action = NULL;
3384 wxRichTextAttr newAttr = obj->GetAttributes();
3385 if (resetExistingStyle)
3386 newAttr = textAttr;
3387 else
3388 newAttr.Apply(textAttr);
3389
3390 if (haveControl && withUndo)
3391 {
3392 action = new wxRichTextAction(NULL, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES, buffer, obj->GetContainer(), buffer->GetRichTextCtrl());
3393 action->SetRange(obj->GetRange().FromInternal());
3394 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3395 action->MakeObject(obj);
3396
3397 action->GetAttributes() = newAttr;
3398 }
3399 else
3400 obj->GetAttributes() = newAttr;
3401
3402 if (haveControl && withUndo)
3403 buffer->SubmitAction(action);
3404 }
3405
3406 /// Get the text attributes for this position.
3407 bool wxRichTextParagraphLayoutBox::GetStyle(long position, wxRichTextAttr& style)
3408 {
3409 return DoGetStyle(position, style, true);
3410 }
3411
3412 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position, wxRichTextAttr& style)
3413 {
3414 return DoGetStyle(position, style, false);
3415 }
3416
3417 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3418 /// context attributes.
3419 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position, wxRichTextAttr& style, bool combineStyles)
3420 {
3421 wxRichTextObject* obj wxDUMMY_INITIALIZE(NULL);
3422
3423 if (style.IsParagraphStyle())
3424 {
3425 obj = GetParagraphAtPosition(position);
3426 if (obj)
3427 {
3428 if (combineStyles)
3429 {
3430 // Start with the base style
3431 style = GetAttributes();
3432 style.GetTextBoxAttr().Reset();
3433
3434 // Apply the paragraph style
3435 wxRichTextApplyStyle(style, obj->GetAttributes());
3436 }
3437 else
3438 style = obj->GetAttributes();
3439
3440 return true;
3441 }
3442 }
3443 else
3444 {
3445 obj = GetLeafObjectAtPosition(position);
3446 if (obj)
3447 {
3448 if (combineStyles)
3449 {
3450 wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph);
3451 style = para ? para->GetCombinedAttributes(obj->GetAttributes()) : obj->GetAttributes();
3452 }
3453 else
3454 style = obj->GetAttributes();
3455
3456 return true;
3457 }
3458 }
3459 return false;
3460 }
3461
3462 static bool wxHasStyle(long flags, long style)
3463 {
3464 return (flags & style) != 0;
3465 }
3466
3467 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3468 /// content.
3469 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr& currentStyle, const wxRichTextAttr& style, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
3470 {
3471 currentStyle.CollectCommonAttributes(style, clashingAttr, absentAttr);
3472
3473 return true;
3474 }
3475
3476 /// Get the combined style for a range - if any attribute is different within the range,
3477 /// that attribute is not present within the flags.
3478 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3479 /// nested.
3480 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange& range, wxRichTextAttr& style)
3481 {
3482 style = wxRichTextAttr();
3483
3484 wxRichTextAttr clashingAttrPara, clashingAttrChar;
3485 wxRichTextAttr absentAttrPara, absentAttrChar;
3486
3487 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
3488 while (node)
3489 {
3490 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3491 if (para && !(para->GetRange().GetStart() > range.GetEnd() || para->GetRange().GetEnd() < range.GetStart()))
3492 {
3493 if (para->GetChildren().GetCount() == 0)
3494 {
3495 wxRichTextAttr paraStyle = para->GetCombinedAttributes(true /* use box attributes */);
3496
3497 CollectStyle(style, paraStyle, clashingAttrPara, absentAttrPara);
3498 }
3499 else
3500 {
3501 wxRichTextRange paraRange(para->GetRange());
3502 paraRange.LimitTo(range);
3503
3504 // First collect paragraph attributes only
3505 wxRichTextAttr paraStyle = para->GetCombinedAttributes();
3506 paraStyle.SetFlags(paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH);
3507 CollectStyle(style, paraStyle, clashingAttrPara, absentAttrPara);
3508
3509 wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
3510
3511 while (childNode)
3512 {
3513 wxRichTextObject* child = childNode->GetData();
3514 if (!(child->GetRange().GetStart() > range.GetEnd() || child->GetRange().GetEnd() < range.GetStart()))
3515 {
3516 wxRichTextAttr childStyle = para->GetCombinedAttributes(child->GetAttributes(), true /* include box attributes */);
3517
3518 // Now collect character attributes only
3519 childStyle.SetFlags(childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER);
3520
3521 CollectStyle(style, childStyle, clashingAttrChar, absentAttrChar);
3522 }
3523
3524 childNode = childNode->GetNext();
3525 }
3526 }
3527 }
3528 node = node->GetNext();
3529 }
3530 return true;
3531 }
3532
3533 /// Set default style
3534 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr& style)
3535 {
3536 m_defaultAttributes = style;
3537 return true;
3538 }
3539
3540 /// Test if this whole range has character attributes of the specified kind. If any
3541 /// of the attributes are different within the range, the test fails. You
3542 /// can use this to implement, for example, bold button updating. style must have
3543 /// flags indicating which attributes are of interest.
3544 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3545 {
3546 int foundCount = 0;
3547 int matchingCount = 0;
3548
3549 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3550 while (node)
3551 {
3552 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3553 // wxASSERT (para != NULL);
3554
3555 if (para)
3556 {
3557 // Stop searching if we're beyond the range of interest
3558 if (para->GetRange().GetStart() > range.GetEnd())
3559 return foundCount == matchingCount && foundCount != 0;
3560
3561 if (!para->GetRange().IsOutside(range))
3562 {
3563 wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
3564
3565 while (node2)
3566 {
3567 wxRichTextObject* child = node2->GetData();
3568 // Allow for empty string if no buffer
3569 wxRichTextRange childRange = child->GetRange();
3570 if (childRange.GetLength() == 0 && GetRange().GetLength() == 1)
3571 childRange.SetEnd(childRange.GetEnd()+1);
3572
3573 if (!childRange.IsOutside(range) && wxDynamicCast(child, wxRichTextPlainText))
3574 {
3575 foundCount ++;
3576 wxRichTextAttr textAttr = para->GetCombinedAttributes(child->GetAttributes());
3577
3578 if (textAttr.EqPartial(style, false /* strong test - attributes must be valid in both objects */))
3579 matchingCount ++;
3580 }
3581
3582 node2 = node2->GetNext();
3583 }
3584 }
3585 }
3586
3587 node = node->GetNext();
3588 }
3589
3590 return foundCount == matchingCount && foundCount != 0;
3591 }
3592
3593 /// Test if this whole range has paragraph attributes of the specified kind. If any
3594 /// of the attributes are different within the range, the test fails. You
3595 /// can use this to implement, for example, centering button updating. style must have
3596 /// flags indicating which attributes are of interest.
3597 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3598 {
3599 int foundCount = 0;
3600 int matchingCount = 0;
3601
3602 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3603 while (node)
3604 {
3605 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3606 // wxASSERT (para != NULL);
3607
3608 if (para)
3609 {
3610 // Stop searching if we're beyond the range of interest
3611 if (para->GetRange().GetStart() > range.GetEnd())
3612 return foundCount == matchingCount && foundCount != 0;
3613
3614 if (!para->GetRange().IsOutside(range))
3615 {
3616 wxRichTextAttr textAttr = GetAttributes();
3617 // Apply the paragraph style
3618 wxRichTextApplyStyle(textAttr, para->GetAttributes());
3619
3620 foundCount ++;
3621 if (textAttr.EqPartial(style, false /* strong test */))
3622 matchingCount ++;
3623 }
3624 }
3625
3626 node = node->GetNext();
3627 }
3628 return foundCount == matchingCount && foundCount != 0;
3629 }
3630
3631 void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox& container)
3632 {
3633 wxRichTextBuffer* buffer = GetBuffer();
3634 if (buffer && buffer->GetRichTextCtrl())
3635 buffer->GetRichTextCtrl()->PrepareContent(container);
3636 }
3637
3638 /// Set character or paragraph properties
3639 bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange& range, const wxRichTextProperties& properties, int flags)
3640 {
3641 wxRichTextBuffer* buffer = GetBuffer();
3642
3643 bool withUndo = ((flags & wxRICHTEXT_SETPROPERTIES_WITH_UNDO) != 0);
3644 bool parasOnly = ((flags & wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY) != 0);
3645 bool charactersOnly = ((flags & wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY) != 0);
3646 bool resetExistingProperties = ((flags & wxRICHTEXT_SETPROPERTIES_RESET) != 0);
3647 bool removeProperties = ((flags & wxRICHTEXT_SETPROPERTIES_REMOVE) != 0);
3648
3649 // If we are associated with a control, make undoable; otherwise, apply immediately
3650 // to the data.
3651
3652 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3653
3654 wxRichTextAction* action = NULL;
3655
3656 if (haveControl && withUndo)
3657 {
3658 action = new wxRichTextAction(NULL, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES, buffer, this, buffer->GetRichTextCtrl());
3659 action->SetRange(range);
3660 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3661 }
3662
3663 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3664 while (node)
3665 {
3666 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3667 // wxASSERT (para != NULL);
3668
3669 if (para && para->GetChildCount() > 0)
3670 {
3671 // Stop searching if we're beyond the range of interest
3672 if (para->GetRange().GetStart() > range.GetEnd())
3673 break;
3674
3675 if (!para->GetRange().IsOutside(range))
3676 {
3677 // We'll be using a copy of the paragraph to make style changes,
3678 // not updating the buffer directly.
3679 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3680
3681 if (haveControl && withUndo)
3682 {
3683 newPara = new wxRichTextParagraph(*para);
3684 action->GetNewParagraphs().AppendChild(newPara);
3685
3686 // Also store the old ones for Undo
3687 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3688 }
3689 else
3690 newPara = para;
3691
3692 if (parasOnly)
3693 {
3694 if (removeProperties)
3695 {
3696 // Removes the given style from the paragraph
3697 // TODO
3698 newPara->GetProperties().RemoveProperties(properties);
3699 }
3700 else if (resetExistingProperties)
3701 newPara->GetProperties() = properties;
3702 else
3703 newPara->GetProperties().MergeProperties(properties);
3704 }
3705
3706 // When applying paragraph styles dynamically, don't change the text objects' attributes
3707 // since they will computed as needed. Only apply the character styling if it's _only_
3708 // character styling. This policy is subject to change and might be put under user control.
3709
3710 // Hm. we might well be applying a mix of paragraph and character styles, in which
3711 // case we _do_ want to apply character styles regardless of what para styles are set.
3712 // But if we're applying a paragraph style, which has some character attributes, but
3713 // we only want the paragraphs to hold this character style, then we _don't_ want to
3714 // apply the character style. So we need to be able to choose.
3715
3716 if (!parasOnly && charactersOnly && range.GetStart() != newPara->GetRange().GetEnd())
3717 {
3718 wxRichTextRange childRange(range);
3719 childRange.LimitTo(newPara->GetRange());
3720
3721 // Find the starting position and if necessary split it so
3722 // we can start applying different properties.
3723 // TODO: check that the properties actually change or are different
3724 // from properties outside of range
3725 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3726 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
3727
3728 if (childRange.GetStart() == newPara->GetRange().GetStart())
3729 firstObject = newPara->GetChildren().GetFirst()->GetData();
3730 else
3731 firstObject = newPara->SplitAt(range.GetStart());
3732
3733 // Increment by 1 because we're apply the style one _after_ the split point
3734 long splitPoint = childRange.GetEnd();
3735 if (splitPoint != newPara->GetRange().GetEnd())
3736 splitPoint ++;
3737
3738 // Find last object
3739 if (splitPoint == newPara->GetRange().GetEnd())
3740 lastObject = newPara->GetChildren().GetLast()->GetData();
3741 else
3742 // lastObject is set as a side-effect of splitting. It's
3743 // returned as the object before the new object.
3744 (void) newPara->SplitAt(splitPoint, & lastObject);
3745
3746 wxASSERT(firstObject != NULL);
3747 wxASSERT(lastObject != NULL);
3748
3749 if (!firstObject || !lastObject)
3750 continue;
3751
3752 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3753 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
3754
3755 wxASSERT(firstNode);
3756 wxASSERT(lastNode);
3757
3758 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
3759
3760 while (node2)
3761 {
3762 wxRichTextObject* child = node2->GetData();
3763
3764 if (removeProperties)
3765 {
3766 // Removes the given properties from the paragraph
3767 child->GetProperties().RemoveProperties(properties);
3768 }
3769 else if (resetExistingProperties)
3770 child->GetProperties() = properties;
3771 else
3772 {
3773 child->GetProperties().MergeProperties(properties);
3774 }
3775
3776 if (node2 == lastNode)
3777 break;
3778
3779 node2 = node2->GetNext();
3780 }
3781 }
3782 }
3783 }
3784
3785 node = node->GetNext();
3786 }
3787
3788 // Do action, or delay it until end of batch.
3789 if (haveControl && withUndo)
3790 buffer->SubmitAction(action);
3791
3792 return true;
3793 }
3794
3795 void wxRichTextParagraphLayoutBox::Reset()
3796 {
3797 Clear();
3798
3799 wxRichTextBuffer* buffer = GetBuffer();
3800 if (buffer && buffer->GetRichTextCtrl())
3801 {
3802 wxRichTextEvent event(wxEVT_RICHTEXT_BUFFER_RESET, buffer->GetRichTextCtrl()->GetId());
3803 event.SetEventObject(buffer->GetRichTextCtrl());
3804 event.SetContainer(this);
3805
3806 buffer->SendEvent(event, true);
3807 }
3808
3809 AddParagraph(wxEmptyString);
3810
3811 PrepareContent(*this);
3812
3813 InvalidateHierarchy(wxRICHTEXT_ALL);
3814 }
3815
3816 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3817 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange& invalidRange)
3818 {
3819 wxRichTextCompositeObject::Invalidate(invalidRange);
3820
3821 DoInvalidate(invalidRange);
3822 }
3823
3824 // Do the (in)validation for this object only
3825 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange& invalidRange)
3826 {
3827 if (invalidRange == wxRICHTEXT_ALL)
3828 {
3829 m_invalidRange = wxRICHTEXT_ALL;
3830 }
3831 // Already invalidating everything
3832 else if (m_invalidRange == wxRICHTEXT_ALL)
3833 {
3834 }
3835 else
3836 {
3837 if ((invalidRange.GetStart() < m_invalidRange.GetStart()) || m_invalidRange.GetStart() == -1)
3838 m_invalidRange.SetStart(invalidRange.GetStart());
3839 if (invalidRange.GetEnd() > m_invalidRange.GetEnd())
3840 m_invalidRange.SetEnd(invalidRange.GetEnd());
3841 }
3842 }
3843
3844 // Do the (in)validation both up and down the hierarchy
3845 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange& invalidRange)
3846 {
3847 Invalidate(invalidRange);
3848
3849 if (invalidRange != wxRICHTEXT_NONE)
3850 {
3851 // Now go up the hierarchy
3852 wxRichTextObject* thisObj = this;
3853 wxRichTextObject* p = GetParent();
3854 while (p)
3855 {
3856 wxRichTextParagraphLayoutBox* l = wxDynamicCast(p, wxRichTextParagraphLayoutBox);
3857 if (l)
3858 l->DoInvalidate(thisObj->GetRange());
3859
3860 thisObj = p;
3861 p = p->GetParent();
3862 }
3863 }
3864 }
3865
3866 /// Get invalid range, rounding to entire paragraphs if argument is true.
3867 wxRichTextRange wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs) const
3868 {
3869 if (m_invalidRange == wxRICHTEXT_ALL || m_invalidRange == wxRICHTEXT_NONE)
3870 return m_invalidRange;
3871
3872 wxRichTextRange range = m_invalidRange;
3873
3874 if (wholeParagraphs)
3875 {
3876 wxRichTextParagraph* para1 = GetParagraphAtPosition(range.GetStart());
3877 if (para1)
3878 range.SetStart(para1->GetRange().GetStart());
3879
3880 // FIXME: be more intelligent about this. Check if we have floating objects
3881 // before the end of the range. But it's not clear how we can in general
3882 // tell where it's safe to stop laying out.
3883 // Anyway, this code is central to efficiency when laying in floating mode.
3884 if (!wxRichTextBuffer::GetFloatingLayoutMode())
3885 {
3886 wxRichTextParagraph* para2 = GetParagraphAtPosition(range.GetEnd());
3887 if (para2)
3888 range.SetEnd(para2->GetRange().GetEnd());
3889 }
3890 else
3891 // Floating layout means that all children should be laid out,
3892 // because we can't tell how the whole buffer will be affected.
3893 range.SetEnd(GetOwnRange().GetEnd());
3894 }
3895 return range;
3896 }
3897
3898 /// Apply the style sheet to the buffer, for example if the styles have changed.
3899 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet* styleSheet)
3900 {
3901 wxASSERT(styleSheet != NULL);
3902 if (!styleSheet)
3903 return false;
3904
3905 int foundCount = 0;
3906
3907 wxRichTextAttr attr(GetBasicStyle());
3908 if (GetBasicStyle().HasParagraphStyleName())
3909 {
3910 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3911 if (paraDef)
3912 {
3913 attr.Apply(paraDef->GetStyleMergedWithBase(styleSheet));
3914 SetBasicStyle(attr);
3915 foundCount ++;
3916 }
3917 }
3918
3919 if (GetBasicStyle().HasCharacterStyleName())
3920 {
3921 wxRichTextCharacterStyleDefinition* charDef = styleSheet->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3922 if (charDef)
3923 {
3924 attr.Apply(charDef->GetStyleMergedWithBase(styleSheet));
3925 SetBasicStyle(attr);
3926 foundCount ++;
3927 }
3928 }
3929
3930 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3931 while (node)
3932 {
3933 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3934 // wxASSERT (para != NULL);
3935
3936 if (para)
3937 {
3938 // Combine paragraph and list styles. If there is a list style in the original attributes,
3939 // the current indentation overrides anything else and is used to find the item indentation.
3940 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3941 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3942 // exception as above).
3943 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3944 // So when changing a list style interactively, could retrieve level based on current style, then
3945 // set appropriate indent and apply new style.
3946
3947 int outline = -1;
3948 int num = -1;
3949 if (para->GetAttributes().HasOutlineLevel())
3950 outline = para->GetAttributes().GetOutlineLevel();
3951 if (para->GetAttributes().HasBulletNumber())
3952 num = para->GetAttributes().GetBulletNumber();
3953
3954 if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3955 {
3956 int currentIndent = para->GetAttributes().GetLeftIndent();
3957
3958 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3959 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3960 if (paraDef && !listDef)
3961 {
3962 para->GetAttributes() = paraDef->GetStyleMergedWithBase(styleSheet);
3963 foundCount ++;
3964 }
3965 else if (listDef && !paraDef)
3966 {
3967 // Set overall style defined for the list style definition
3968 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
3969
3970 // Apply the style for this level
3971 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3972 foundCount ++;
3973 }
3974 else if (listDef && paraDef)
3975 {
3976 // Combines overall list style, style for level, and paragraph style
3977 para->GetAttributes() = listDef->CombineWithParagraphStyle(currentIndent, paraDef->GetStyleMergedWithBase(styleSheet));
3978 foundCount ++;
3979 }
3980 }
3981 else if (para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3982 {
3983 int currentIndent = para->GetAttributes().GetLeftIndent();
3984
3985 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3986
3987 // Overall list definition style
3988 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
3989
3990 // Style for this level
3991 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3992
3993 foundCount ++;
3994 }
3995 else if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && para->GetAttributes().GetListStyleName().IsEmpty())
3996 {
3997 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3998 if (def)
3999 {
4000 para->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
4001 foundCount ++;
4002 }
4003 }
4004
4005 if (outline != -1)
4006 para->GetAttributes().SetOutlineLevel(outline);
4007 if (num != -1)
4008 para->GetAttributes().SetBulletNumber(num);
4009 }
4010
4011 node = node->GetNext();
4012 }
4013 return foundCount != 0;
4014 }
4015
4016 /// Set list style
4017 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4018 {
4019 wxRichTextBuffer* buffer = GetBuffer();
4020 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
4021
4022 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
4023 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4024 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
4025 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
4026
4027 // Current number, if numbering
4028 int n = startFrom;
4029
4030 wxASSERT (!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
4031
4032 // If we are associated with a control, make undoable; otherwise, apply immediately
4033 // to the data.
4034
4035 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
4036
4037 wxRichTextAction* action = NULL;
4038
4039 if (haveControl && withUndo)
4040 {
4041 action = new wxRichTextAction(NULL, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
4042 action->SetRange(range);
4043 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
4044 }
4045
4046 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4047 while (node)
4048 {
4049 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
4050 // wxASSERT (para != NULL);
4051
4052 if (para && para->GetChildCount() > 0)
4053 {
4054 // Stop searching if we're beyond the range of interest
4055 if (para->GetRange().GetStart() > range.GetEnd())
4056 break;
4057
4058 if (!para->GetRange().IsOutside(range))
4059 {
4060 // We'll be using a copy of the paragraph to make style changes,
4061 // not updating the buffer directly.
4062 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
4063
4064 if (haveControl && withUndo)
4065 {
4066 newPara = new wxRichTextParagraph(*para);
4067 action->GetNewParagraphs().AppendChild(newPara);
4068
4069 // Also store the old ones for Undo
4070 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
4071 }
4072 else
4073 newPara = para;
4074
4075 if (def)
4076 {
4077 int thisIndent = newPara->GetAttributes().GetLeftIndent();
4078 int thisLevel = specifyLevel ? specifiedLevel : def->FindLevelForIndent(thisIndent);
4079
4080 // How is numbering going to work?
4081 // If we are renumbering, or numbering for the first time, we need to keep
4082 // track of the number for each level. But we might be simply applying a different
4083 // list style.
4084 // In Word, applying a style to several paragraphs, even if at different levels,
4085 // reverts the level back to the same one. So we could do the same here.
4086 // Renumbering will need to be done when we promote/demote a paragraph.
4087
4088 // Apply the overall list style, and item style for this level
4089 wxRichTextAttr listStyle(def->GetCombinedStyleForLevel(thisLevel, styleSheet));
4090 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
4091
4092 // Now we need to do numbering
4093 // Preserve the existing list item continuation bullet style, if any
4094 if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))
4095 newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
4096 else
4097 {
4098 if (renumber)
4099 {
4100 newPara->GetAttributes().SetBulletNumber(n);
4101 }
4102
4103 n ++;
4104 }
4105 }
4106 else if (!newPara->GetAttributes().GetListStyleName().IsEmpty())
4107 {
4108 // if def is NULL, remove list style, applying any associated paragraph style
4109 // to restore the attributes
4110
4111 newPara->GetAttributes().SetListStyleName(wxEmptyString);
4112 newPara->GetAttributes().SetLeftIndent(0, 0);
4113 newPara->GetAttributes().SetBulletText(wxEmptyString);
4114 newPara->GetAttributes().SetBulletStyle(0);
4115
4116 // Eliminate the main list-related attributes
4117 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);
4118
4119 if (styleSheet && !newPara->GetAttributes().GetParagraphStyleName().IsEmpty())
4120 {
4121 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(newPara->GetAttributes().GetParagraphStyleName());
4122 if (def)
4123 {
4124 newPara->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
4125 }
4126 }
4127 }
4128 }
4129 }
4130
4131 node = node->GetNext();
4132 }
4133
4134 // Do action, or delay it until end of batch.
4135 if (haveControl && withUndo)
4136 buffer->SubmitAction(action);
4137
4138 return true;
4139 }
4140
4141 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4142 {
4143 wxRichTextBuffer* buffer = GetBuffer();
4144 if (buffer && buffer->GetStyleSheet())
4145 {
4146 wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->FindListStyle(defName);
4147 if (def)
4148 return SetListStyle(range, def, flags, startFrom, specifiedLevel);
4149 }
4150 return false;
4151 }
4152
4153 /// Clear list for given range
4154 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange& range, int flags)
4155 {
4156 return SetListStyle(range, NULL, flags);
4157 }
4158
4159 /// Number/renumber any list elements in the given range
4160 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4161 {
4162 return DoNumberList(range, range, 0, def, flags, startFrom, specifiedLevel);
4163 }
4164
4165 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
4166 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, const wxRichTextRange& promotionRange, int promoteBy,
4167 wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
4168 {
4169 wxRichTextBuffer* buffer = GetBuffer();
4170 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
4171
4172 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
4173 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
4174 #if wxDEBUG_LEVEL
4175 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
4176 #endif
4177
4178 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
4179
4180 // Max number of levels
4181 const int maxLevels = 10;
4182
4183 // The level we're looking at now
4184 int currentLevel = -1;
4185
4186 // The item number for each level
4187 int levels[maxLevels];
4188 int i;
4189
4190 // Reset all numbering
4191 for (i = 0; i < maxLevels; i++)
4192 {
4193 if (startFrom != -1)
4194 levels[i] = startFrom-1;
4195 else if (renumber) // start again
4196 levels[i] = 0;
4197 else
4198 levels[i] = -1; // start from the number we found, if any
4199 }
4200
4201 #if wxDEBUG_LEVEL
4202 wxASSERT(!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
4203 #endif
4204
4205 // If we are associated with a control, make undoable; otherwise, apply immediately
4206 // to the data.
4207
4208 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
4209
4210 wxRichTextAction* action = NULL;
4211
4212 if (haveControl && withUndo)
4213 {
4214 action = new wxRichTextAction(NULL, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
4215 action->SetRange(range);
4216 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
4217 }
4218
4219 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4220 while (node)
4221 {
4222 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
4223 // wxASSERT (para != NULL);
4224
4225 if (para && para->GetChildCount() > 0)
4226 {
4227 // Stop searching if we're beyond the range of interest
4228 if (para->GetRange().GetStart() > range.GetEnd())
4229 break;
4230
4231 if (!para->GetRange().IsOutside(range))
4232 {
4233 // We'll be using a copy of the paragraph to make style changes,
4234 // not updating the buffer directly.
4235 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
4236
4237 if (haveControl && withUndo)
4238 {
4239 newPara = new wxRichTextParagraph(*para);
4240 action->GetNewParagraphs().AppendChild(newPara);
4241
4242 // Also store the old ones for Undo
4243 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
4244 }
4245 else
4246 newPara = para;
4247
4248 wxRichTextListStyleDefinition* defToUse = def;
4249 if (!defToUse)
4250 {
4251 if (styleSheet && !newPara->GetAttributes().GetListStyleName().IsEmpty())
4252 defToUse = styleSheet->FindListStyle(newPara->GetAttributes().GetListStyleName());
4253 }
4254
4255 if (defToUse)
4256 {
4257 int thisIndent = newPara->GetAttributes().GetLeftIndent();
4258 int thisLevel = defToUse->FindLevelForIndent(thisIndent);
4259
4260 // If we've specified a level to apply to all, change the level.
4261 if (specifiedLevel != -1)
4262 thisLevel = specifiedLevel;
4263
4264 // Do promotion if specified
4265 if ((promoteBy != 0) && !para->GetRange().IsOutside(promotionRange))
4266 {
4267 thisLevel = thisLevel - promoteBy;
4268 if (thisLevel < 0)
4269 thisLevel = 0;
4270 if (thisLevel > 9)
4271 thisLevel = 9;
4272 }
4273
4274 // Apply the overall list style, and item style for this level
4275 wxRichTextAttr listStyle(defToUse->GetCombinedStyleForLevel(thisLevel, styleSheet));
4276 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
4277
4278 // Preserve the existing list item continuation bullet style, if any
4279 if (para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION))
4280 newPara->GetAttributes().SetBulletStyle(newPara->GetAttributes().GetBulletStyle()|wxTEXT_ATTR_BULLET_STYLE_CONTINUATION);
4281
4282 // OK, we've (re)applied the style, now let's get the numbering right.
4283
4284 if (currentLevel == -1)
4285 currentLevel = thisLevel;
4286
4287 // Same level as before, do nothing except increment level's number afterwards
4288 if (currentLevel == thisLevel)
4289 {
4290 }
4291 // A deeper level: start renumbering all levels after current level
4292 else if (thisLevel > currentLevel)
4293 {
4294 for (i = currentLevel+1; i <= thisLevel; i++)
4295 {
4296 levels[i] = 0;
4297 }
4298 currentLevel = thisLevel;
4299 }
4300 else if (thisLevel < currentLevel)
4301 {
4302 currentLevel = thisLevel;
4303 }
4304
4305 // Use the current numbering if -1 and we have a bullet number already
4306 if (levels[currentLevel] == -1)
4307 {
4308 if (newPara->GetAttributes().HasBulletNumber())
4309 levels[currentLevel] = newPara->GetAttributes().GetBulletNumber();
4310 else
4311 levels[currentLevel] = 1;
4312 }
4313 else
4314 {
4315 if (!(para->GetAttributes().HasBulletStyle() && (para->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)))
4316 levels[currentLevel] ++;
4317 }
4318
4319 newPara->GetAttributes().SetBulletNumber(levels[currentLevel]);
4320
4321 // Create the bullet text if an outline list
4322 if (listStyle.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
4323 {
4324 wxString text;
4325 for (i = 0; i <= currentLevel; i++)
4326 {
4327 if (!text.IsEmpty())
4328 text += wxT(".");
4329 text += wxString::Format(wxT("%d"), levels[i]);
4330 }
4331 newPara->GetAttributes().SetBulletText(text);
4332 }
4333 }
4334 }
4335 }
4336
4337 node = node->GetNext();
4338 }
4339
4340 // Do action, or delay it until end of batch.
4341 if (haveControl && withUndo)
4342 buffer->SubmitAction(action);
4343
4344 return true;
4345 }
4346
4347 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4348 {
4349 wxRichTextBuffer* buffer = GetBuffer();
4350 if (buffer->GetStyleSheet())
4351 {
4352 wxRichTextListStyleDefinition* def = NULL;
4353 if (!defName.IsEmpty())
4354 def = buffer->GetStyleSheet()->FindListStyle(defName);
4355 return NumberList(range, def, flags, startFrom, specifiedLevel);
4356 }
4357 return false;
4358 }
4359
4360 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4361 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel)
4362 {
4363 // TODO
4364 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4365 // to NumberList with a flag indicating promotion is required within one of the ranges.
4366 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4367 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4368 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4369 // list position will start from 1.
4370 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4371 // We can end the renumbering at this point.
4372
4373 // For now, only renumber within the promotion range.
4374
4375 return DoNumberList(range, range, promoteBy, def, flags, 1, specifiedLevel);
4376 }
4377
4378 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel)
4379 {
4380 wxRichTextBuffer* buffer = GetBuffer();
4381 if (buffer->GetStyleSheet())
4382 {
4383 wxRichTextListStyleDefinition* def = NULL;
4384 if (!defName.IsEmpty())
4385 def = buffer->GetStyleSheet()->FindListStyle(defName);
4386 return PromoteList(promoteBy, range, def, flags, specifiedLevel);
4387 }
4388 return false;
4389 }
4390
4391 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4392 /// position of the paragraph that it had to start looking from.
4393 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr) const
4394 {
4395 // TODO: add GetNextChild/GetPreviousChild to composite
4396 // Search for a paragraph that isn't a continuation paragraph (no bullet)
4397 while (previousParagraph && previousParagraph->GetAttributes().HasBulletStyle() && previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION)
4398 {
4399 wxRichTextObjectList::compatibility_iterator node = ((wxRichTextCompositeObject*) previousParagraph->GetParent())->GetChildren().Find(previousParagraph);
4400 if (node)
4401 {
4402 node = node->GetPrevious();
4403 if (node)
4404 previousParagraph = wxDynamicCast(node->GetData(), wxRichTextParagraph);
4405 else
4406 previousParagraph = NULL;
4407 }
4408 else
4409 previousParagraph = NULL;
4410 }
4411
4412 if (!previousParagraph || !previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE)
4413 return false;
4414
4415 wxRichTextBuffer* buffer = GetBuffer();
4416 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
4417 if (styleSheet && !previousParagraph->GetAttributes().GetListStyleName().IsEmpty())
4418 {
4419 wxRichTextListStyleDefinition* def = styleSheet->FindListStyle(previousParagraph->GetAttributes().GetListStyleName());
4420 if (def)
4421 {
4422 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4423 // int thisLevel = def->FindLevelForIndent(thisIndent);
4424
4425 bool isOutline = (previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE) != 0;
4426
4427 attr.SetFlags(previousParagraph->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER|wxTEXT_ATTR_BULLET_TEXT|wxTEXT_ATTR_BULLET_NAME));
4428 if (previousParagraph->GetAttributes().HasBulletName())
4429 attr.SetBulletName(previousParagraph->GetAttributes().GetBulletName());
4430 attr.SetBulletStyle(previousParagraph->GetAttributes().GetBulletStyle());
4431 attr.SetListStyleName(previousParagraph->GetAttributes().GetListStyleName());
4432
4433 int nextNumber = previousParagraph->GetAttributes().GetBulletNumber() + 1;
4434 attr.SetBulletNumber(nextNumber);
4435
4436 if (isOutline)
4437 {
4438 wxString text = previousParagraph->GetAttributes().GetBulletText();
4439 if (!text.IsEmpty())
4440 {
4441 int pos = text.Find(wxT('.'), true);
4442 if (pos != wxNOT_FOUND)
4443 {
4444 text = text.Mid(0, text.Length() - pos - 1);
4445 }
4446 else
4447 text = wxEmptyString;
4448 if (!text.IsEmpty())
4449 text += wxT(".");
4450 text += wxString::Format(wxT("%d"), nextNumber);
4451 attr.SetBulletText(text);
4452 }
4453 }
4454
4455 return true;
4456 }
4457 else
4458 return false;
4459 }
4460 else
4461 return false;
4462 }
4463
4464 /*!
4465 * wxRichTextParagraph
4466 * This object represents a single paragraph (or in a straight text editor, a line).
4467 */
4468
4469 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph, wxRichTextCompositeObject)
4470
4471 wxArrayInt wxRichTextParagraph::sm_defaultTabs;
4472
4473 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject* parent, wxRichTextAttr* style):
4474 wxRichTextCompositeObject(parent)
4475 {
4476 if (style)
4477 SetAttributes(*style);
4478 }
4479
4480 wxRichTextParagraph::wxRichTextParagraph(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* paraStyle, wxRichTextAttr* charStyle):
4481 wxRichTextCompositeObject(parent)
4482 {
4483 if (paraStyle)
4484 SetAttributes(*paraStyle);
4485
4486 AppendChild(new wxRichTextPlainText(text, this, charStyle));
4487 }
4488
4489 wxRichTextParagraph::~wxRichTextParagraph()
4490 {
4491 ClearLines();
4492 }
4493
4494 /// Draw the item
4495 bool wxRichTextParagraph::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int style)
4496 {
4497 if (!IsShown())
4498 return true;
4499
4500 // Currently we don't merge these attributes with the parent, but we
4501 // should consider whether we should (e.g. if we set a border colour
4502 // for all paragraphs). But generally box attributes are likely to be
4503 // different for different objects.
4504 wxRect paraRect = GetRect();
4505 wxRichTextAttr attr = GetCombinedAttributes();
4506 context.ApplyVirtualAttributes(attr, this);
4507
4508 DrawBoxAttributes(dc, GetBuffer(), attr, paraRect);
4509
4510 // Draw the bullet, if any
4511 if ((attr.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE) == 0 && (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_CONTINUATION) == 0)
4512 {
4513 if (attr.GetLeftSubIndent() != 0)
4514 {
4515 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
4516 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
4517
4518 wxRichTextAttr bulletAttr(attr);
4519
4520 // Combine with the font of the first piece of content, if one is specified
4521 if (GetChildren().GetCount() > 0)
4522 {
4523 wxRichTextObject* firstObj = (wxRichTextObject*) GetChildren().GetFirst()->GetData();
4524 if (!firstObj->IsFloatable() && firstObj->GetAttributes().HasFont())
4525 {
4526 wxRichTextApplyStyle(bulletAttr, firstObj->GetAttributes());
4527 }
4528 }
4529
4530 // Get line height from first line, if any
4531 wxRichTextLine* line = m_cachedLines.GetFirst() ? (wxRichTextLine* ) m_cachedLines.GetFirst()->GetData() : NULL;
4532
4533 wxPoint linePos;
4534 int lineHeight wxDUMMY_INITIALIZE(0);
4535 if (line)
4536 {
4537 lineHeight = line->GetSize().y;
4538 linePos = line->GetPosition() + GetPosition();
4539 }
4540 else
4541 {
4542 wxFont font;
4543 if (bulletAttr.HasFont() && GetBuffer())
4544 font = GetBuffer()->GetFontTable().FindFont(bulletAttr);
4545 else
4546 font = (*wxNORMAL_FONT);
4547
4548 wxCheckSetFont(dc, font);
4549
4550 lineHeight = dc.GetCharHeight();
4551 linePos = GetPosition();
4552 linePos.y += spaceBeforePara;
4553 }
4554
4555 wxRect bulletRect(GetPosition().x + leftIndent, linePos.y, linePos.x - (GetPosition().x + leftIndent), lineHeight);
4556
4557 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP)
4558 {
4559 if (wxRichTextBuffer::GetRenderer())
4560 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc, bulletAttr, bulletRect);
4561 }
4562 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD)
4563 {
4564 if (wxRichTextBuffer::GetRenderer())
4565 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc, bulletAttr, bulletRect);
4566 }
4567 else
4568 {
4569 wxString bulletText = GetBulletText();
4570
4571 if (!bulletText.empty() && wxRichTextBuffer::GetRenderer())
4572 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc, bulletAttr, bulletRect, bulletText);
4573 }
4574 }
4575 }
4576
4577 // Draw the range for each line, one object at a time.
4578
4579 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
4580 while (node)
4581 {
4582 wxRichTextLine* line = node->GetData();
4583 wxRichTextRange lineRange = line->GetAbsoluteRange();
4584
4585 // Lines are specified relative to the paragraph
4586
4587 wxPoint linePosition = line->GetPosition() + GetPosition();
4588
4589 // Don't draw if off the screen
4590 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) != 0) || ((linePosition.y + line->GetSize().y) >= rect.y && linePosition.y <= rect.y + rect.height))
4591 {
4592 wxPoint objectPosition = linePosition;
4593 int maxDescent = line->GetDescent();
4594
4595 // Loop through objects until we get to the one within range
4596 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
4597
4598 int i = 0;
4599 while (node2)
4600 {
4601 wxRichTextObject* child = node2->GetData();
4602
4603 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
4604 {
4605 // Draw this part of the line at the correct position
4606 wxRichTextRange objectRange(child->GetRange());
4607 objectRange.LimitTo(lineRange);
4608
4609 wxSize objectSize;
4610 if (child->IsTopLevel())
4611 {
4612 objectSize = child->GetCachedSize();
4613 objectRange = child->GetOwnRange();
4614 }
4615 else
4616 {
4617 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4618 if (i < (int) line->GetObjectSizes().GetCount())
4619 {
4620 objectSize.x = line->GetObjectSizes()[(size_t) i];
4621 }
4622 else
4623 #endif
4624 {
4625 int descent = 0;
4626 child->GetRangeSize(objectRange, objectSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, objectPosition);
4627 }
4628 }
4629
4630 // Use the child object's width, but the whole line's height
4631 wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
4632 child->Draw(dc, context, objectRange, selection, childRect, maxDescent, style);
4633
4634 objectPosition.x += objectSize.x;
4635 i ++;
4636 }
4637 else if (child->GetRange().GetStart() > lineRange.GetEnd())
4638 // Can break out of inner loop now since we've passed this line's range
4639 break;
4640
4641 node2 = node2->GetNext();
4642 }
4643 }
4644
4645 node = node->GetNext();
4646 }
4647
4648 return true;
4649 }
4650
4651 // Get the range width using partial extents calculated for the whole paragraph.
4652 static int wxRichTextGetRangeWidth(const wxRichTextParagraph& para, const wxRichTextRange& range, const wxArrayInt& partialExtents)
4653 {
4654 wxASSERT(partialExtents.GetCount() >= (size_t) range.GetLength());
4655
4656 if (partialExtents.GetCount() < (size_t) range.GetLength())
4657 return 0;
4658
4659 int leftMostPos = 0;
4660 if (range.GetStart() - para.GetRange().GetStart() > 0)
4661 leftMostPos = partialExtents[range.GetStart() - para.GetRange().GetStart() - 1];
4662
4663 int rightMostPos = partialExtents[range.GetEnd() - para.GetRange().GetStart()];
4664
4665 int w = rightMostPos - leftMostPos;
4666
4667 return w;
4668 }
4669
4670 /// Lay the item out
4671 bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
4672 {
4673 // Deal with floating objects firstly before the normal layout
4674 wxRichTextBuffer* buffer = GetBuffer();
4675 wxASSERT(buffer);
4676
4677 wxRichTextFloatCollector* collector = GetContainer()->GetFloatCollector();
4678
4679 if (wxRichTextBuffer::GetFloatingLayoutMode())
4680 {
4681 wxASSERT(collector != NULL);
4682 if (collector)
4683 LayoutFloat(dc, context, rect, parentRect, style, collector);
4684 }
4685
4686 wxRichTextAttr attr = GetCombinedAttributes();
4687 context.ApplyVirtualAttributes(attr, this);
4688
4689 // ClearLines();
4690
4691 // Increase the size of the paragraph due to spacing
4692 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
4693 int spaceAfterPara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingAfter());
4694 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
4695 int leftSubIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftSubIndent());
4696 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
4697
4698 int lineSpacing = 0;
4699
4700 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4701 if (attr.HasLineSpacing() && attr.GetLineSpacing() > 0 && attr.HasFont())
4702 {
4703 wxFont font(buffer->GetFontTable().FindFont(attr));
4704 if (font.IsOk())
4705 {
4706 wxCheckSetFont(dc, font);
4707 lineSpacing = (int) (double(dc.GetCharHeight()) * (double(attr.GetLineSpacing())/10.0 - 1.0));
4708 }
4709 }
4710
4711 // Start position for each line relative to the paragraph
4712 int startPositionFirstLine = leftIndent;
4713 int startPositionSubsequentLines = leftIndent + leftSubIndent;
4714
4715 // If we have a bullet in this paragraph, the start position for the first line's text
4716 // is actually leftIndent + leftSubIndent.
4717 if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
4718 startPositionFirstLine = startPositionSubsequentLines;
4719
4720 long lastEndPos = GetRange().GetStart()-1;
4721 long lastCompletedEndPos = lastEndPos;
4722
4723 int currentWidth = 0;
4724 SetPosition(rect.GetPosition());
4725
4726 wxPoint currentPosition(0, spaceBeforePara); // We will calculate lines relative to paragraph
4727 int lineHeight = 0;
4728 int maxWidth = 0;
4729 int maxHeight = currentPosition.y;
4730 int maxAscent = 0;
4731 int maxDescent = 0;
4732 int lineCount = 0;
4733 int lineAscent = 0;
4734 int lineDescent = 0;
4735
4736 wxRichTextObjectList::compatibility_iterator node;
4737
4738 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4739 wxUnusedVar(style);
4740 wxArrayInt partialExtents;
4741
4742 wxSize paraSize;
4743 int paraDescent = 0;
4744
4745 // This calculates the partial text extents
4746 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, rect.GetPosition(), parentRect.GetSize(), & partialExtents);
4747 #else
4748 node = m_children.GetFirst();
4749 while (node)
4750 {
4751 wxRichTextObject* child = node->GetData();
4752
4753 //child->SetCachedSize(wxDefaultSize);
4754 child->Layout(dc, context, rect, style);
4755
4756 node = node->GetNext();
4757 }
4758 #endif
4759
4760 // Split up lines
4761
4762 // We may need to go back to a previous child, in which case create the new line,
4763 // find the child corresponding to the start position of the string, and
4764 // continue.
4765
4766 wxRect availableRect;
4767
4768 node = m_children.GetFirst();
4769 while (node)
4770 {
4771 wxRichTextObject* child = node->GetData();
4772
4773 // If floating, ignore. We already laid out floats.
4774 // Also ignore if empty object, except if we haven't got any
4775 // size yet.
4776 if ((child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
4777 || !child->IsShown() || (child->GetRange().GetLength() == 0 && maxHeight > spaceBeforePara)
4778 )
4779 {
4780 node = node->GetNext();
4781 continue;
4782 }
4783
4784 // If this is e.g. a composite text box, it will need to be laid out itself.
4785 // But if just a text fragment or image, for example, this will
4786 // do nothing. NB: won't we need to set the position after layout?
4787 // since for example if position is dependent on vertical line size, we
4788 // can't tell the position until the size is determined. So possibly introduce
4789 // another layout phase.
4790
4791 // We may only be looking at part of a child, if we searched back for wrapping
4792 // and found a suitable point some way into the child. So get the size for the fragment
4793 // if necessary.
4794
4795 long nextBreakPos = GetFirstLineBreakPosition(lastEndPos+1);
4796 long lastPosToUse = child->GetRange().GetEnd();
4797 bool lineBreakInThisObject = (nextBreakPos > -1 && nextBreakPos <= child->GetRange().GetEnd());
4798
4799 if (lineBreakInThisObject)
4800 lastPosToUse = nextBreakPos;
4801
4802 wxSize childSize;
4803 int childDescent = 0;
4804
4805 int startOffset = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
4806 availableRect = wxRect(rect.x + startOffset, rect.y + currentPosition.y,
4807 rect.width - startOffset - rightIndent, rect.height);
4808
4809 if (child->IsTopLevel())
4810 {
4811 wxSize oldSize = child->GetCachedSize();
4812
4813 child->Invalidate(wxRICHTEXT_ALL);
4814 child->SetPosition(wxPoint(0, 0));
4815
4816 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4817 // lays out the object again using the minimum size
4818 // The position will be determined by its location in its line,
4819 // and not by the child's actual position.
4820 child->LayoutToBestSize(dc, context, buffer,
4821 attr, child->GetAttributes(), availableRect, parentRect, style);
4822
4823 if (oldSize != child->GetCachedSize())
4824 {
4825 partialExtents.Clear();
4826
4827 // Recalculate the partial text extents since the child object changed size
4828 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), parentRect.GetSize(), & partialExtents);
4829 }
4830 }
4831
4832 // Problem: we need to layout composites here for which we need the available width,
4833 // but we can't get the available width without using the float collector which
4834 // needs to know the object height.
4835
4836 if ((nextBreakPos == -1) && (lastEndPos == child->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4837 {
4838 childSize = child->GetCachedSize();
4839 childDescent = child->GetDescent();
4840 }
4841 else
4842 {
4843 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4844 // Get height only, then the width using the partial extents
4845 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY, wxPoint(0,0), parentRect.GetSize());
4846 childSize.x = wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos+1, lastPosToUse), partialExtents);
4847 #else
4848 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, rect.GetPosition(), parentRect.GetSize());
4849 #endif
4850 }
4851
4852 bool doLoop = true;
4853 int loopIterations = 0;
4854
4855 // If there are nested objects that need to lay themselves out, we have to do this in a
4856 // loop because the height of the object may well depend on the available width.
4857 // And because of floating object positioning, the available width depends on the
4858 // height of the object and whether it will clash with the floating objects.
4859 // So, we see whether the available width changes due to the presence of floating images.
4860 // If it does, then we'll use the new restricted width to find the object height again.
4861 // If this causes another restriction in the available width, we'll try again, until
4862 // either we lose patience or the available width settles down.
4863 do
4864 {
4865 loopIterations ++;
4866
4867 wxRect oldAvailableRect = availableRect;
4868
4869 // Available width depends on the floating objects and the line height.
4870 // Note: the floating objects may be placed vertically along the two sides of
4871 // buffer, so we may have different available line widths with different
4872 // [startY, endY]. So, we can't determine how wide the available
4873 // space is until we know the exact line height.
4874 if (childDescent == 0)
4875 {
4876 lineHeight = wxMax(lineHeight, childSize.y);
4877 lineDescent = maxDescent;
4878 lineAscent = maxAscent;
4879 }
4880 else
4881 {
4882 lineDescent = wxMax(childDescent, maxDescent);
4883 lineAscent = wxMax(childSize.y-childDescent, maxAscent);
4884 }
4885 lineHeight = wxMax(lineHeight, (lineDescent + lineAscent));
4886
4887 if (wxRichTextBuffer::GetFloatingLayoutMode() && collector)
4888 {
4889 wxRect floatAvailableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight);
4890
4891 // Adjust availableRect to the space that is available when taking floating objects into account.
4892
4893 if (floatAvailableRect.x + startOffset > availableRect.x)
4894 {
4895 int newX = floatAvailableRect.x + startOffset;
4896 int newW = availableRect.width - (newX - availableRect.x);
4897 availableRect.x = newX;
4898 availableRect.width = newW;
4899 }
4900
4901 if (floatAvailableRect.width < availableRect.width)
4902 availableRect.width = floatAvailableRect.width;
4903 }
4904
4905 currentPosition.x = availableRect.x - rect.x;
4906
4907 if (child->IsTopLevel() && loopIterations <= 20)
4908 {
4909 if (availableRect != oldAvailableRect)
4910 {
4911 wxSize oldSize = child->GetCachedSize();
4912
4913 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4914 // lays out the object again using the minimum size
4915 child->Invalidate(wxRICHTEXT_ALL);
4916 child->LayoutToBestSize(dc, context, buffer,
4917 attr, child->GetAttributes(), availableRect, parentRect.GetSize(), style);
4918 childSize = child->GetCachedSize();
4919 childDescent = child->GetDescent();
4920
4921 if (oldSize != child->GetCachedSize())
4922 {
4923 partialExtents.Clear();
4924
4925 // Recalculate the partial text extents since the child object changed size
4926 GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), parentRect.GetSize(), & partialExtents);
4927 }
4928
4929 // Go around the loop finding the available rect for the given floating objects
4930 }
4931 else
4932 doLoop = false;
4933 }
4934 else
4935 doLoop = false;
4936 }
4937 while (doLoop);
4938
4939 if (child->IsTopLevel())
4940 {
4941 // We can move it to the correct position at this point
4942 // TODO: probably need to add margin
4943 child->Move(GetPosition() + wxPoint(currentWidth + (wxMax(leftIndent, leftIndent + leftSubIndent)), currentPosition.y));
4944 }
4945
4946 // Cases:
4947 // 1) There was a line break BEFORE the natural break
4948 // 2) There was a line break AFTER the natural break
4949 // 3) It's the last line
4950 // 4) The child still fits (carry on) - 'else' clause
4951
4952 if ((lineBreakInThisObject && (childSize.x + currentWidth <= availableRect.width))
4953 ||
4954 (childSize.x + currentWidth > availableRect.width)
4955 #if 0
4956 ||
4957 ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext())
4958 #endif
4959 )
4960 {
4961 long wrapPosition = 0;
4962 if ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext() && !lineBreakInThisObject)
4963 wrapPosition = child->GetRange().GetEnd();
4964 else
4965
4966 // Find a place to wrap. This may walk back to previous children,
4967 // for example if a word spans several objects.
4968 // Note: one object must contains only one wxTextAtrr, so the line height will not
4969 // change inside one object. Thus, we can pass the remain line width to the
4970 // FindWrapPosition function.
4971 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, context, availableRect.width, wrapPosition, & partialExtents))
4972 {
4973 // If the function failed, just cut it off at the end of this child.
4974 wrapPosition = child->GetRange().GetEnd();
4975 }
4976
4977 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4978 if (wrapPosition <= lastCompletedEndPos)
4979 wrapPosition = wxMax(lastCompletedEndPos+1,child->GetRange().GetEnd());
4980
4981 // Line end position shouldn't be the same as the end, or greater.
4982 if (wrapPosition >= GetRange().GetEnd())
4983 wrapPosition = wxMax(0, GetRange().GetEnd()-1);
4984
4985 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4986
4987 // Let's find the actual size of the current line now
4988 wxSize actualSize;
4989 wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
4990
4991 childDescent = 0;
4992
4993 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4994 if (!child->IsEmpty())
4995 {
4996 // Get height only, then the width using the partial extents
4997 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY, wxPoint(0,0), parentRect.GetSize());
4998 actualSize.x = wxRichTextGetRangeWidth(*this, actualRange, partialExtents);
4999 }
5000 else
5001 #endif
5002 GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, wxPoint(0,0), parentRect.GetSize());
5003
5004 currentWidth = actualSize.x;
5005
5006 // The descent for the whole line at this point, is the correct max descent
5007 maxDescent = childDescent;
5008 // Maximum ascent
5009 maxAscent = actualSize.y-childDescent;
5010
5011 // lineHeight is given by the height for the whole line, since it will
5012 // take into account ascend/descend.
5013 lineHeight = actualSize.y;
5014
5015 if (lineHeight == 0 && buffer)
5016 {
5017 wxFont font(buffer->GetFontTable().FindFont(attr));
5018 wxCheckSetFont(dc, font);
5019 lineHeight = dc.GetCharHeight();
5020 }
5021
5022 if (maxDescent == 0)
5023 {
5024 int w, h;
5025 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
5026 }
5027
5028 // Add a new line
5029 wxRichTextLine* line = AllocateLine(lineCount);
5030
5031 // Set relative range so we won't have to change line ranges when paragraphs are moved
5032 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5033 line->SetPosition(currentPosition);
5034 line->SetSize(wxSize(currentWidth, lineHeight));
5035 line->SetDescent(maxDescent);
5036
5037 maxHeight = currentPosition.y + lineHeight;
5038
5039 // Now move down a line. TODO: add margins, spacing
5040 currentPosition.y += lineHeight;
5041 currentPosition.y += lineSpacing;
5042 maxDescent = 0;
5043 maxAscent = 0;
5044 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5045 currentWidth = 0;
5046
5047 lineCount ++;
5048
5049 // TODO: account for zero-length objects
5050 // wxASSERT(wrapPosition > lastCompletedEndPos);
5051
5052 lastEndPos = wrapPosition;
5053 lastCompletedEndPos = lastEndPos;
5054
5055 lineHeight = 0;
5056
5057 if (wrapPosition < GetRange().GetEnd()-1)
5058 {
5059 // May need to set the node back to a previous one, due to searching back in wrapping
5060 wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition(wrapPosition+1);
5061 if (childAfterWrapPosition)
5062 node = m_children.Find(childAfterWrapPosition);
5063 else
5064 node = node->GetNext();
5065 }
5066 else
5067 node = node->GetNext();
5068
5069 // Apply paragraph styles such as alignment to the wrapped line
5070 ApplyParagraphStyle(line, attr, availableRect, dc);
5071 }
5072 else
5073 {
5074 // We still fit, so don't add a line, and keep going
5075 currentWidth += childSize.x;
5076
5077 if (childDescent == 0)
5078 {
5079 // An object with a zero descend value wants to take up the whole
5080 // height regardless of baseline
5081 lineHeight = wxMax(lineHeight, childSize.y);
5082 }
5083 else
5084 {
5085 maxDescent = wxMax(childDescent, maxDescent);
5086 maxAscent = wxMax(childSize.y-childDescent, maxAscent);
5087 }
5088
5089 lineHeight = wxMax(lineHeight, (maxDescent + maxAscent));
5090
5091 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
5092 lastEndPos = child->GetRange().GetEnd();
5093
5094 node = node->GetNext();
5095 }
5096 }
5097
5098 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
5099
5100 // Add the last line - it's the current pos -> last para pos
5101 // Substract -1 because the last position is always the end-paragraph position.
5102 if (lastCompletedEndPos <= GetRange().GetEnd()-1)
5103 {
5104 currentPosition.x = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
5105
5106 wxRichTextLine* line = AllocateLine(lineCount);
5107
5108 wxRichTextRange actualRange(lastCompletedEndPos+1, GetRange().GetEnd()-1);
5109
5110 // Set relative range so we won't have to change line ranges when paragraphs are moved
5111 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5112
5113 line->SetPosition(currentPosition);
5114
5115 if (lineHeight == 0 && buffer)
5116 {
5117 wxFont font(buffer->GetFontTable().FindFont(attr));
5118 wxCheckSetFont(dc, font);
5119 lineHeight = dc.GetCharHeight();
5120 }
5121
5122 if (maxDescent == 0)
5123 {
5124 int w, h;
5125 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
5126 }
5127
5128 line->SetSize(wxSize(currentWidth, lineHeight));
5129 line->SetDescent(maxDescent);
5130 currentPosition.y += lineHeight;
5131 currentPosition.y += lineSpacing;
5132 lineCount ++;
5133
5134 // Apply paragraph styles such as alignment to the wrapped line
5135 ApplyParagraphStyle(line, attr, availableRect, dc);
5136 }
5137
5138 // Remove remaining unused line objects, if any
5139 ClearUnusedLines(lineCount);
5140
5141 // We need to add back the margins etc.
5142 {
5143 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5144 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, currentPosition.y + spaceAfterPara));
5145 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
5146 SetCachedSize(marginRect.GetSize());
5147 }
5148
5149 // The maximum size is the length of the paragraph stretched out into a line.
5150 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
5151 // this size. TODO: take into account line breaks.
5152 {
5153 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5154 contentRect = wxRect(wxPoint(0, 0), wxSize(paraSize.x + wxMax(leftIndent, leftIndent + leftSubIndent) + rightIndent, currentPosition.y + spaceAfterPara));
5155 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
5156 SetMaxSize(marginRect.GetSize());
5157 }
5158
5159 // Find the greatest minimum size. Currently we only look at non-text objects,
5160 // which isn't ideal but it would be slow to find the maximum word width to
5161 // use as the minimum.
5162 {
5163 int minWidth = 0;
5164 node = m_children.GetFirst();
5165 while (node)
5166 {
5167 wxRichTextObject* child = node->GetData();
5168
5169 // If floating, ignore. We already laid out floats.
5170 // Also ignore if empty object, except if we haven't got any
5171 // size yet.
5172 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && child->GetRange().GetLength() != 0 && !wxDynamicCast(child, wxRichTextPlainText))
5173 {
5174 if (child->GetCachedSize().x > minWidth)
5175 minWidth = child->GetMinSize().x;
5176 }
5177 node = node->GetNext();
5178 }
5179
5180 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
5181 contentRect = wxRect(wxPoint(0, 0), wxSize(minWidth, currentPosition.y + spaceAfterPara));
5182 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
5183 SetMinSize(marginRect.GetSize());
5184 }
5185
5186 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5187 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5188 // Use the text extents to calculate the size of each fragment in each line
5189 wxRichTextLineList::compatibility_iterator lineNode = m_cachedLines.GetFirst();
5190 while (lineNode)
5191 {
5192 wxRichTextLine* line = lineNode->GetData();
5193 wxRichTextRange lineRange = line->GetAbsoluteRange();
5194
5195 // Loop through objects until we get to the one within range
5196 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5197
5198 while (node2)
5199 {
5200 wxRichTextObject* child = node2->GetData();
5201
5202 if (child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange))
5203 {
5204 wxRichTextRange rangeToUse = lineRange;
5205 rangeToUse.LimitTo(child->GetRange());
5206
5207 // Find the size of the child from the text extents, and store in an array
5208 // for drawing later
5209 int left = 0;
5210 if (rangeToUse.GetStart() > GetRange().GetStart())
5211 left = partialExtents[(rangeToUse.GetStart()-1) - GetRange().GetStart()];
5212 int right = partialExtents[rangeToUse.GetEnd() - GetRange().GetStart()];
5213 int sz = right - left;
5214 line->GetObjectSizes().Add(sz);
5215 }
5216 else if (child->GetRange().GetStart() > lineRange.GetEnd())
5217 // Can break out of inner loop now since we've passed this line's range
5218 break;
5219
5220 node2 = node2->GetNext();
5221 }
5222
5223 lineNode = lineNode->GetNext();
5224 }
5225 #endif
5226 #endif
5227
5228 return true;
5229 }
5230
5231 /// Apply paragraph styles, such as centering, to wrapped lines
5232 /// TODO: take into account box attributes, possibly
5233 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRichTextAttr& attr, const wxRect& rect, wxDC& dc)
5234 {
5235 if (!attr.HasAlignment())
5236 return;
5237
5238 wxPoint pos = line->GetPosition();
5239 wxPoint originalPos = pos;
5240 wxSize size = line->GetSize();
5241
5242 // centering, right-justification
5243 if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
5244 {
5245 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5246 pos.x = (rect.GetWidth() - rightIndent - size.x)/2 + pos.x;
5247 line->SetPosition(pos);
5248 }
5249 else if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
5250 {
5251 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5252 pos.x = pos.x + rect.GetWidth() - size.x - rightIndent;
5253 line->SetPosition(pos);
5254 }
5255
5256 if (pos != originalPos)
5257 {
5258 wxPoint inc = pos - originalPos;
5259
5260 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5261
5262 while (node)
5263 {
5264 wxRichTextObject* child = node->GetData();
5265 if (child->IsTopLevel() && !child->GetRange().IsOutside(line->GetAbsoluteRange()))
5266 child->Move(child->GetPosition() + inc);
5267
5268 node = node->GetNext();
5269 }
5270 }
5271 }
5272
5273 /// Insert text at the given position
5274 bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
5275 {
5276 wxRichTextObject* childToUse = NULL;
5277 wxRichTextObjectList::compatibility_iterator nodeToUse = wxRichTextObjectList::compatibility_iterator();
5278
5279 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5280 while (node)
5281 {
5282 wxRichTextObject* child = node->GetData();
5283 if (child->GetRange().Contains(pos) && child->GetRange().GetLength() > 0)
5284 {
5285 childToUse = child;
5286 nodeToUse = node;
5287 break;
5288 }
5289
5290 node = node->GetNext();
5291 }
5292
5293 if (childToUse)
5294 {
5295 wxRichTextPlainText* textObject = wxDynamicCast(childToUse, wxRichTextPlainText);
5296 if (textObject)
5297 {
5298 int posInString = pos - textObject->GetRange().GetStart();
5299
5300 wxString newText = textObject->GetText().Mid(0, posInString) +
5301 text + textObject->GetText().Mid(posInString);
5302 textObject->SetText(newText);
5303
5304 int textLength = text.length();
5305
5306 textObject->SetRange(wxRichTextRange(textObject->GetRange().GetStart(),
5307 textObject->GetRange().GetEnd() + textLength));
5308
5309 // Increment the end range of subsequent fragments in this paragraph.
5310 // We'll set the paragraph range itself at a higher level.
5311
5312 wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
5313 while (node)
5314 {
5315 wxRichTextObject* child = node->GetData();
5316 child->SetRange(wxRichTextRange(textObject->GetRange().GetStart() + textLength,
5317 textObject->GetRange().GetEnd() + textLength));
5318
5319 node = node->GetNext();
5320 }
5321
5322 return true;
5323 }
5324 else
5325 {
5326 // TODO: if not a text object, insert at closest position, e.g. in front of it
5327 }
5328 }
5329 else
5330 {
5331 // Add at end.
5332 // Don't pass parent initially to suppress auto-setting of parent range.
5333 // We'll do that at a higher level.
5334 wxRichTextPlainText* textObject = new wxRichTextPlainText(text, this);
5335
5336 AppendChild(textObject);
5337 return true;
5338 }
5339
5340 return false;
5341 }
5342
5343 void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
5344 {
5345 wxRichTextCompositeObject::Copy(obj);
5346 }
5347
5348 /// Clear the cached lines
5349 void wxRichTextParagraph::ClearLines()
5350 {
5351 WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
5352 }
5353
5354 /// Get/set the object size for the given range. Returns false if the range
5355 /// is invalid for this object.
5356 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
5357 {
5358 if (!range.IsWithin(GetRange()))
5359 return false;
5360
5361 if (flags & wxRICHTEXT_UNFORMATTED)
5362 {
5363 // Just use unformatted data, assume no line breaks
5364 wxSize sz;
5365
5366 wxArrayInt childExtents;
5367 wxArrayInt* p;
5368 if (partialExtents)
5369 p = & childExtents;
5370 else
5371 p = NULL;
5372
5373 int maxDescent = 0;
5374 int maxAscent = 0;
5375 int maxLineHeight = 0;
5376
5377 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5378 while (node)
5379 {
5380 wxRichTextObject* child = node->GetData();
5381 if (!child->GetRange().IsOutside(range))
5382 {
5383 // Floating objects have a zero size within the paragraph.
5384 if (child->IsFloating() && wxRichTextBuffer::GetFloatingLayoutMode())
5385 {
5386 if (partialExtents)
5387 {
5388 int lastSize;
5389 if (partialExtents->GetCount() > 0)
5390 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5391 else
5392 lastSize = 0;
5393
5394 partialExtents->Add(0 /* zero size */ + lastSize);
5395 }
5396 }
5397 else
5398 {
5399 wxSize childSize;
5400
5401 wxRichTextRange rangeToUse = range;
5402 rangeToUse.LimitTo(child->GetRange());
5403 int childDescent = 0;
5404
5405 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
5406 // but it's only going to be used after caching has taken place.
5407 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
5408 {
5409 childDescent = child->GetDescent();
5410 childSize = child->GetCachedSize();
5411
5412 if (childDescent == 0)
5413 {
5414 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5415 }
5416 else
5417 {
5418 maxDescent = wxMax(maxDescent, childDescent);
5419 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5420 }
5421
5422 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5423
5424 sz.y = wxMax(sz.y, maxLineHeight);
5425 sz.x += childSize.x;
5426 descent = maxDescent;
5427 }
5428 else if (child->IsTopLevel())
5429 {
5430 childDescent = child->GetDescent();
5431 childSize = child->GetCachedSize();
5432
5433 if (childDescent == 0)
5434 {
5435 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5436 }
5437 else
5438 {
5439 maxDescent = wxMax(maxDescent, childDescent);
5440 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5441 }
5442
5443 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5444
5445 sz.y = wxMax(sz.y, maxLineHeight);
5446 sz.x += childSize.x;
5447 descent = maxDescent;
5448
5449 // FIXME: this won't change the original values.
5450 // Should we be calling GetRangeSize above instead of using cached values?
5451 #if 0
5452 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
5453 {
5454 child->SetCachedSize(childSize);
5455 child->SetDescent(childDescent);
5456 }
5457 #endif
5458
5459 if (partialExtents)
5460 {
5461 int lastSize;
5462 if (partialExtents->GetCount() > 0)
5463 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5464 else
5465 lastSize = 0;
5466
5467 partialExtents->Add(childSize.x + lastSize);
5468 }
5469 }
5470 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize, p))
5471 {
5472 if (childDescent == 0)
5473 {
5474 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5475 }
5476 else
5477 {
5478 maxDescent = wxMax(maxDescent, childDescent);
5479 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5480 }
5481
5482 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5483
5484 sz.y = wxMax(sz.y, maxLineHeight);
5485 sz.x += childSize.x;
5486 descent = maxDescent;
5487
5488 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
5489 {
5490 child->SetCachedSize(childSize);
5491 child->SetDescent(childDescent);
5492 }
5493
5494 if (partialExtents)
5495 {
5496 int lastSize;
5497 if (partialExtents->GetCount() > 0)
5498 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5499 else
5500 lastSize = 0;
5501
5502 size_t i;
5503 for (i = 0; i < childExtents.GetCount(); i++)
5504 {
5505 partialExtents->Add(childExtents[i] + lastSize);
5506 }
5507 }
5508 }
5509 }
5510
5511 if (p)
5512 p->Clear();
5513 }
5514
5515 node = node->GetNext();
5516 }
5517 size = sz;
5518 }
5519 else
5520 {
5521 // Use formatted data, with line breaks
5522 wxSize sz;
5523
5524 // We're going to loop through each line, and then for each line,
5525 // call GetRangeSize for the fragment that comprises that line.
5526 // Only we have to do that multiple times within the line, because
5527 // the line may be broken into pieces. For now ignore line break commands
5528 // (so we can assume that getting the unformatted size for a fragment
5529 // within a line is the actual size)
5530
5531 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5532 while (node)
5533 {
5534 wxRichTextLine* line = node->GetData();
5535 wxRichTextRange lineRange = line->GetAbsoluteRange();
5536 if (!lineRange.IsOutside(range))
5537 {
5538 int maxDescent = 0;
5539 int maxAscent = 0;
5540 int maxLineHeight = 0;
5541 int maxLineWidth = 0;
5542
5543 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5544 while (node2)
5545 {
5546 wxRichTextObject* child = node2->GetData();
5547
5548 if ((!child->IsFloating() || !wxRichTextBuffer::GetFloatingLayoutMode()) && !child->GetRange().IsOutside(lineRange))
5549 {
5550 wxRichTextRange rangeToUse = lineRange;
5551 rangeToUse.LimitTo(child->GetRange());
5552 if (child->IsTopLevel())
5553 rangeToUse = child->GetOwnRange();
5554
5555 wxSize childSize;
5556 int childDescent = 0;
5557 if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), parentSize))
5558 {
5559 if (childDescent == 0)
5560 {
5561 // Assume that if descent is zero, this child can occupy the full line height
5562 // and does not need space for the line's maximum descent. So we influence
5563 // the overall max line height only.
5564 maxLineHeight = wxMax(maxLineHeight, childSize.y);
5565 }
5566 else
5567 {
5568 maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
5569 maxDescent = wxMax(maxAscent, childDescent);
5570 }
5571 maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
5572 maxLineWidth += childSize.x;
5573 }
5574 }
5575
5576 node2 = node2->GetNext();
5577 }
5578
5579 descent = wxMax(descent, maxDescent);
5580
5581 // Increase size by a line (TODO: paragraph spacing)
5582 sz.y += maxLineHeight;
5583 sz.x = wxMax(sz.x, maxLineWidth);
5584 }
5585 node = node->GetNext();
5586 }
5587 size = sz;
5588 }
5589 return true;
5590 }
5591
5592 /// Finds the absolute position and row height for the given character position
5593 bool wxRichTextParagraph::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
5594 {
5595 if (index == -1)
5596 {
5597 wxRichTextLine* line = ((wxRichTextParagraphLayoutBox*)GetParent())->GetLineAtPosition(0);
5598 if (line)
5599 *height = line->GetSize().y;
5600 else
5601 *height = dc.GetCharHeight();
5602
5603 // -1 means 'the start of the buffer'.
5604 pt = GetPosition();
5605 if (line)
5606 pt = pt + line->GetPosition();
5607
5608 return true;
5609 }
5610
5611 // The final position in a paragraph is taken to mean the position
5612 // at the start of the next paragraph.
5613 if (index == GetRange().GetEnd())
5614 {
5615 wxRichTextParagraphLayoutBox* parent = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
5616 wxASSERT( parent != NULL );
5617
5618 // Find the height at the next paragraph, if any
5619 wxRichTextLine* line = parent->GetLineAtPosition(index + 1);
5620 if (line)
5621 {
5622 *height = line->GetSize().y;
5623 pt = line->GetAbsolutePosition();
5624 }
5625 else
5626 {
5627 *height = dc.GetCharHeight();
5628 int indent = ConvertTenthsMMToPixels(dc, m_attributes.GetLeftIndent());
5629 pt = wxPoint(indent, GetCachedSize().y);
5630 }
5631
5632 return true;
5633 }
5634
5635 if (index < GetRange().GetStart() || index > GetRange().GetEnd())
5636 return false;
5637
5638 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5639 while (node)
5640 {
5641 wxRichTextLine* line = node->GetData();
5642 wxRichTextRange lineRange = line->GetAbsoluteRange();
5643 if (index >= lineRange.GetStart() && index <= lineRange.GetEnd())
5644 {
5645 // If this is the last point in the line, and we're forcing the
5646 // returned value to be the start of the next line, do the required
5647 // thing.
5648 if (index == lineRange.GetEnd() && forceLineStart)
5649 {
5650 if (node->GetNext())
5651 {
5652 wxRichTextLine* nextLine = node->GetNext()->GetData();
5653 *height = nextLine->GetSize().y;
5654 pt = nextLine->GetAbsolutePosition();
5655 return true;
5656 }
5657 }
5658
5659 pt.y = line->GetPosition().y + GetPosition().y;
5660
5661 wxRichTextRange r(lineRange.GetStart(), index);
5662 wxSize rangeSize;
5663 int descent = 0;
5664
5665 // We find the size of the line up to this point,
5666 // then we can add this size to the line start position and
5667 // paragraph start position to find the actual position.
5668
5669 if (GetRangeSize(r, rangeSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, line->GetPosition()+ GetPosition()))
5670 {
5671 pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
5672 *height = line->GetSize().y;
5673
5674 return true;
5675 }
5676
5677 }
5678
5679 node = node->GetNext();
5680 }
5681
5682 return false;
5683 }
5684
5685 /// Hit-testing: returns a flag indicating hit test details, plus
5686 /// information about position
5687 int wxRichTextParagraph::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5688 {
5689 if (!IsShown())
5690 return wxRICHTEXT_HITTEST_NONE;
5691
5692 // If we're in the top-level container, then we can return
5693 // a suitable hit test code even if the point is outside the container area,
5694 // so that we can position the caret sensibly even if we don't
5695 // click on valid content. If we're not at the top-level, and the point
5696 // is not within this paragraph object, then we don't want to stop more
5697 // precise hit-testing from working prematurely, so return immediately.
5698 // NEW STRATEGY: use the parent boundary to test whether we're in the
5699 // right region, not the paragraph, since the paragraph may be positioned
5700 // some way in from where the user clicks.
5701 {
5702 long tmpPos;
5703 wxRichTextObject* tempObj, *tempContextObj;
5704 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc, context, pt, tmpPos, & tempObj, & tempContextObj, flags) == wxRICHTEXT_HITTEST_NONE)
5705 return wxRICHTEXT_HITTEST_NONE;
5706 }
5707
5708 wxRichTextObjectList::compatibility_iterator objNode = m_children.GetFirst();
5709 while (objNode)
5710 {
5711 wxRichTextObject* child = objNode->GetData();
5712 // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
5713 // and also, if this seems composite but actually is marked as atomic,
5714 // don't recurse.
5715 if (child->IsTopLevel() && ((flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS) == 0) &&
5716 (! (((flags & wxRICHTEXT_HITTEST_HONOUR_ATOMIC) != 0) && child->IsAtomic())))
5717 {
5718 {
5719 int hitTest = child->HitTest(dc, context, pt, textPosition, obj, contextObj);
5720 if (hitTest != wxRICHTEXT_HITTEST_NONE)
5721 return hitTest;
5722 }
5723 }
5724
5725 objNode = objNode->GetNext();
5726 }
5727
5728 wxPoint paraPos = GetPosition();
5729
5730 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5731 while (node)
5732 {
5733 wxRichTextLine* line = node->GetData();
5734 wxPoint linePos = paraPos + line->GetPosition();
5735 wxSize lineSize = line->GetSize();
5736 wxRichTextRange lineRange = line->GetAbsoluteRange();
5737
5738 if (pt.y <= linePos.y + lineSize.y)
5739 {
5740 if (pt.x < linePos.x)
5741 {
5742 textPosition = lineRange.GetStart();
5743 *obj = FindObjectAtPosition(textPosition);
5744 *contextObj = GetContainer();
5745 return wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_OUTSIDE;
5746 }
5747 else if (pt.x >= (linePos.x + lineSize.x))
5748 {
5749 textPosition = lineRange.GetEnd();
5750 *obj = FindObjectAtPosition(textPosition);
5751 *contextObj = GetContainer();
5752 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
5753 }
5754 else
5755 {
5756 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5757 wxArrayInt partialExtents;
5758
5759 wxSize paraSize;
5760 int paraDescent;
5761
5762 // This calculates the partial text extents
5763 GetRangeSize(lineRange, paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED, linePos, wxDefaultSize, & partialExtents);
5764
5765 int lastX = linePos.x;
5766 size_t i;
5767 for (i = 0; i < partialExtents.GetCount(); i++)
5768 {
5769 int nextX = partialExtents[i] + linePos.x;
5770
5771 if (pt.x >= lastX && pt.x <= nextX)
5772 {
5773 textPosition = i + lineRange.GetStart(); // minus 1?
5774
5775 *obj = FindObjectAtPosition(textPosition);
5776 *contextObj = GetContainer();
5777
5778 // So now we know it's between i-1 and i.
5779 // Let's see if we can be more precise about
5780 // which side of the position it's on.
5781
5782 int midPoint = (nextX + lastX)/2;
5783 if (pt.x >= midPoint)
5784 return wxRICHTEXT_HITTEST_AFTER;
5785 else
5786 return wxRICHTEXT_HITTEST_BEFORE;
5787 }
5788
5789 lastX = nextX;
5790 }
5791 #else
5792 long i;
5793 int lastX = linePos.x;
5794 for (i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++)
5795 {
5796 wxSize childSize;
5797 int descent = 0;
5798
5799 wxRichTextRange rangeToUse(lineRange.GetStart(), i);
5800
5801 GetRangeSize(rangeToUse, childSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, linePos);
5802
5803 int nextX = childSize.x + linePos.x;
5804
5805 if (pt.x >= lastX && pt.x <= nextX)
5806 {
5807 textPosition = i;
5808
5809 *obj = FindObjectAtPosition(textPosition);
5810 *contextObj = GetContainer();
5811
5812 // So now we know it's between i-1 and i.
5813 // Let's see if we can be more precise about
5814 // which side of the position it's on.
5815
5816 int midPoint = (nextX + lastX)/2;
5817 if (pt.x >= midPoint)
5818 return wxRICHTEXT_HITTEST_AFTER;
5819 else
5820 return wxRICHTEXT_HITTEST_BEFORE;
5821 }
5822 else
5823 {
5824 lastX = nextX;
5825 }
5826 }
5827 #endif
5828 }
5829 }
5830
5831 node = node->GetNext();
5832 }
5833
5834 return wxRICHTEXT_HITTEST_NONE;
5835 }
5836
5837 /// Split an object at this position if necessary, and return
5838 /// the previous object, or NULL if inserting at beginning.
5839 wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** previousObject)
5840 {
5841 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5842 while (node)
5843 {
5844 wxRichTextObject* child = node->GetData();
5845
5846 if (pos == child->GetRange().GetStart())
5847 {
5848 if (previousObject)
5849 {
5850 if (node->GetPrevious())
5851 *previousObject = node->GetPrevious()->GetData();
5852 else
5853 *previousObject = NULL;
5854 }
5855
5856 return child;
5857 }
5858
5859 if (child->GetRange().Contains(pos))
5860 {
5861 // This should create a new object, transferring part of
5862 // the content to the old object and the rest to the new object.
5863 wxRichTextObject* newObject = child->DoSplit(pos);
5864
5865 // If we couldn't split this object, just insert in front of it.
5866 if (!newObject)
5867 {
5868 // Maybe this is an empty string, try the next one
5869 // return child;
5870 }
5871 else
5872 {
5873 // Insert the new object after 'child'
5874 if (node->GetNext())
5875 m_children.Insert(node->GetNext(), newObject);
5876 else
5877 m_children.Append(newObject);
5878 newObject->SetParent(this);
5879
5880 if (previousObject)
5881 *previousObject = child;
5882
5883 return newObject;
5884 }
5885 }
5886
5887 node = node->GetNext();
5888 }
5889 if (previousObject)
5890 *previousObject = NULL;
5891 return NULL;
5892 }
5893
5894 /// Move content to a list from obj on
5895 void wxRichTextParagraph::MoveToList(wxRichTextObject* obj, wxList& list)
5896 {
5897 wxRichTextObjectList::compatibility_iterator node = m_children.Find(obj);
5898 while (node)
5899 {
5900 wxRichTextObject* child = node->GetData();
5901 list.Append(child);
5902
5903 wxRichTextObjectList::compatibility_iterator oldNode = node;
5904
5905 node = node->GetNext();
5906
5907 m_children.DeleteNode(oldNode);
5908 }
5909 }
5910
5911 /// Add content back from list
5912 void wxRichTextParagraph::MoveFromList(wxList& list)
5913 {
5914 for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
5915 {
5916 AppendChild((wxRichTextObject*) node->GetData());
5917 }
5918 }
5919
5920 /// Calculate range
5921 void wxRichTextParagraph::CalculateRange(long start, long& end)
5922 {
5923 wxRichTextCompositeObject::CalculateRange(start, end);
5924
5925 // Add one for end of paragraph
5926 end ++;
5927
5928 m_range.SetRange(start, end);
5929 }
5930
5931 /// Find the object at the given position
5932 wxRichTextObject* wxRichTextParagraph::FindObjectAtPosition(long position)
5933 {
5934 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5935 while (node)
5936 {
5937 wxRichTextObject* obj = node->GetData();
5938 if (obj->GetRange().Contains(position) ||
5939 obj->GetRange().GetStart() == position ||
5940 obj->GetRange().GetEnd() == position)
5941 return obj;
5942
5943 node = node->GetNext();
5944 }
5945 return NULL;
5946 }
5947
5948 /// Get the plain text searching from the start or end of the range.
5949 /// The resulting string may be shorter than the range given.
5950 bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTextRange& range, bool fromStart)
5951 {
5952 text = wxEmptyString;
5953
5954 if (fromStart)
5955 {
5956 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5957 while (node)
5958 {
5959 wxRichTextObject* obj = node->GetData();
5960 if (!obj->GetRange().IsOutside(range))
5961 {
5962 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5963 if (textObj)
5964 {
5965 text += textObj->GetTextForRange(range);
5966 }
5967 else
5968 {
5969 text += wxT(" ");
5970 }
5971 }
5972
5973 node = node->GetNext();
5974 }
5975 }
5976 else
5977 {
5978 wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
5979 while (node)
5980 {
5981 wxRichTextObject* obj = node->GetData();
5982 if (!obj->GetRange().IsOutside(range))
5983 {
5984 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5985 if (textObj)
5986 {
5987 text = textObj->GetTextForRange(range) + text;
5988 }
5989 else
5990 {
5991 text = wxT(" ") + text;
5992 }
5993 }
5994
5995 node = node->GetPrevious();
5996 }
5997 }
5998
5999 return true;
6000 }
6001
6002 /// Find a suitable wrap position.
6003 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, wxRichTextDrawingContext& context, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents)
6004 {
6005 if (range.GetLength() <= 0)
6006 return false;
6007
6008 // Find the first position where the line exceeds the available space.
6009 wxSize sz;
6010 long breakPosition = range.GetEnd();
6011
6012 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
6013 if (partialExtents && partialExtents->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
6014 {
6015 int widthBefore;
6016
6017 if (range.GetStart() > GetRange().GetStart())
6018 widthBefore = (*partialExtents)[range.GetStart() - GetRange().GetStart() - 1];
6019 else
6020 widthBefore = 0;
6021
6022 size_t i;
6023 for (i = (size_t) range.GetStart(); i <= (size_t) range.GetEnd(); i++)
6024 {
6025 int widthFromStartOfThisRange = (*partialExtents)[i - GetRange().GetStart()] - widthBefore;
6026
6027 if (widthFromStartOfThisRange > availableSpace)
6028 {
6029 breakPosition = i-1;
6030 break;
6031 }
6032 }
6033 }
6034 else
6035 #endif
6036 {
6037 // Binary chop for speed
6038 long minPos = range.GetStart();
6039 long maxPos = range.GetEnd();
6040 while (true)
6041 {
6042 if (minPos == maxPos)
6043 {
6044 int descent = 0;
6045 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
6046
6047 if (sz.x > availableSpace)
6048 breakPosition = minPos - 1;
6049 break;
6050 }
6051 else if ((maxPos - minPos) == 1)
6052 {
6053 int descent = 0;
6054 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
6055
6056 if (sz.x > availableSpace)
6057 breakPosition = minPos - 1;
6058 else
6059 {
6060 GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
6061 if (sz.x > availableSpace)
6062 breakPosition = maxPos-1;
6063 }
6064 break;
6065 }
6066 else
6067 {
6068 long nextPos = minPos + ((maxPos - minPos) / 2);
6069
6070 int descent = 0;
6071 GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
6072
6073 if (sz.x > availableSpace)
6074 {
6075 maxPos = nextPos;
6076 }
6077 else
6078 {
6079 minPos = nextPos;
6080 }
6081 }
6082 }
6083 }
6084
6085 // Now we know the last position on the line.
6086 // Let's try to find a word break.
6087
6088 wxString plainText;
6089 if (GetContiguousPlainText(plainText, wxRichTextRange(range.GetStart(), breakPosition), false))
6090 {
6091 int newLinePos = plainText.Find(wxRichTextLineBreakChar);
6092 if (newLinePos != wxNOT_FOUND)
6093 {
6094 breakPosition = wxMax(0, range.GetStart() + newLinePos);
6095 }
6096 else
6097 {
6098 int spacePos = plainText.Find(wxT(' '), true);
6099 int tabPos = plainText.Find(wxT('\t'), true);
6100 int pos = wxMax(spacePos, tabPos);
6101 if (pos != wxNOT_FOUND)
6102 {
6103 int positionsFromEndOfString = plainText.length() - pos - 1;
6104 breakPosition = breakPosition - positionsFromEndOfString;
6105 }
6106 }
6107 }
6108
6109 wrapPosition = breakPosition;
6110
6111 return true;
6112 }
6113
6114 /// Get the bullet text for this paragraph.
6115 wxString wxRichTextParagraph::GetBulletText()
6116 {
6117 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ||
6118 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP))
6119 return wxEmptyString;
6120
6121 int number = GetAttributes().GetBulletNumber();
6122
6123 wxString text;
6124 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE))
6125 {
6126 text.Printf(wxT("%d"), number);
6127 }
6128 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER)
6129 {
6130 // TODO: Unicode, and also check if number > 26
6131 text.Printf(wxT("%c"), (wxChar) (number+64));
6132 }
6133 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER)
6134 {
6135 // TODO: Unicode, and also check if number > 26
6136 text.Printf(wxT("%c"), (wxChar) (number+96));
6137 }
6138 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
6139 {
6140 text = wxRichTextDecimalToRoman(number);
6141 }
6142 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
6143 {
6144 text = wxRichTextDecimalToRoman(number);
6145 text.MakeLower();
6146 }
6147 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
6148 {
6149 text = GetAttributes().GetBulletText();
6150 }
6151
6152 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
6153 {
6154 // The outline style relies on the text being computed statically,
6155 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
6156 // should be stored in the attributes; if not, just use the number for this
6157 // level, as previously computed.
6158 if (!GetAttributes().GetBulletText().IsEmpty())
6159 text = GetAttributes().GetBulletText();
6160 }
6161
6162 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES)
6163 {
6164 text = wxT("(") + text + wxT(")");
6165 }
6166 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS)
6167 {
6168 text = text + wxT(")");
6169 }
6170
6171 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD)
6172 {
6173 text += wxT(".");
6174 }
6175
6176 return text;
6177 }
6178
6179 /// Allocate or reuse a line object
6180 wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
6181 {
6182 if (pos < (int) m_cachedLines.GetCount())
6183 {
6184 wxRichTextLine* line = m_cachedLines.Item(pos)->GetData();
6185 line->Init(this);
6186 return line;
6187 }
6188 else
6189 {
6190 wxRichTextLine* line = new wxRichTextLine(this);
6191 m_cachedLines.Append(line);
6192 return line;
6193 }
6194 }
6195
6196 /// Clear remaining unused line objects, if any
6197 bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
6198 {
6199 int cachedLineCount = m_cachedLines.GetCount();
6200 if ((int) cachedLineCount > lineCount)
6201 {
6202 for (int i = 0; i < (int) (cachedLineCount - lineCount); i ++)
6203 {
6204 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetLast();
6205 wxRichTextLine* line = node->GetData();
6206 m_cachedLines.Erase(node);
6207 delete line;
6208 }
6209 }
6210 return true;
6211 }
6212
6213 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
6214 /// retrieve the actual style.
6215 wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr& contentStyle, bool includingBoxAttr) const
6216 {
6217 wxRichTextAttr attr;
6218 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
6219 if (buf)
6220 {
6221 attr = buf->GetBasicStyle();
6222 if (!includingBoxAttr)
6223 {
6224 attr.GetTextBoxAttr().Reset();
6225 // The background colour will be painted by the container, and we don't
6226 // want to unnecessarily overwrite the background when we're drawing text
6227 // because this may erase the guideline (which appears just under the text
6228 // if there's no padding).
6229 attr.SetFlags(attr.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR);
6230 }
6231 wxRichTextApplyStyle(attr, GetAttributes());
6232 }
6233 else
6234 attr = GetAttributes();
6235
6236 wxRichTextApplyStyle(attr, contentStyle);
6237 return attr;
6238 }
6239
6240 /// Get combined attributes of the base style and paragraph style.
6241 wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr) const
6242 {
6243 wxRichTextAttr attr;
6244 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
6245 if (buf)
6246 {
6247 attr = buf->GetBasicStyle();
6248 if (!includingBoxAttr)
6249 attr.GetTextBoxAttr().Reset();
6250 wxRichTextApplyStyle(attr, GetAttributes());
6251 }
6252 else
6253 attr = GetAttributes();
6254
6255 return attr;
6256 }
6257
6258 // Create default tabstop array
6259 void wxRichTextParagraph::InitDefaultTabs()
6260 {
6261 // create a default tab list at 10 mm each.
6262 for (int i = 0; i < 20; ++i)
6263 {
6264 sm_defaultTabs.Add(i*100);
6265 }
6266 }
6267
6268 // Clear default tabstop array
6269 void wxRichTextParagraph::ClearDefaultTabs()
6270 {
6271 sm_defaultTabs.Clear();
6272 }
6273
6274 void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style, wxRichTextFloatCollector* floatCollector)
6275 {
6276 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
6277 while (node)
6278 {
6279 wxRichTextObject* anchored = node->GetData();
6280 if (anchored && anchored->IsFloating() && !floatCollector->HasFloat(anchored))
6281 {
6282 int x = 0;
6283 wxRichTextAttr parentAttr(GetAttributes());
6284 context.ApplyVirtualAttributes(parentAttr, this);
6285 #if 1
6286 // 27-09-2012
6287 wxRect availableSpace = GetParent()->GetAvailableContentArea(dc, context, rect);
6288
6289 anchored->LayoutToBestSize(dc, context, GetBuffer(),
6290 parentAttr, anchored->GetAttributes(),
6291 parentRect, availableSpace,
6292 style);
6293 wxSize size = anchored->GetCachedSize();
6294 #else
6295 wxSize size;
6296 int descent = 0;
6297 anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, context, style);
6298 #endif
6299
6300 int offsetY = 0;
6301 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
6302 {
6303 offsetY = anchored->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
6304 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6305 {
6306 offsetY = ConvertTenthsMMToPixels(dc, offsetY);
6307 }
6308 }
6309
6310 int pos = floatCollector->GetFitPosition(anchored->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect.y + offsetY, size.y);
6311
6312 /* Update the offset */
6313 int newOffsetY = pos - rect.y;
6314 if (newOffsetY != offsetY)
6315 {
6316 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
6317 newOffsetY = ConvertPixelsToTenthsMM(dc, newOffsetY);
6318 anchored->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY);
6319 }
6320
6321 if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
6322 x = rect.x;
6323 else if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
6324 x = rect.x + rect.width - size.x;
6325
6326 //anchored->SetPosition(wxPoint(x, pos));
6327 anchored->Move(wxPoint(x, pos)); // should move children
6328 anchored->SetCachedSize(size);
6329 floatCollector->CollectFloat(this, anchored);
6330 }
6331
6332 node = node->GetNext();
6333 }
6334 }
6335
6336 // Get the first position from pos that has a line break character.
6337 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos)
6338 {
6339 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
6340 while (node)
6341 {
6342 wxRichTextObject* obj = node->GetData();
6343 if (pos >= obj->GetRange().GetStart() && pos <= obj->GetRange().GetEnd())
6344 {
6345 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
6346 if (textObj)
6347 {
6348 long breakPos = textObj->GetFirstLineBreakPosition(pos);
6349 if (breakPos > -1)
6350 return breakPos;
6351 }
6352 }
6353 node = node->GetNext();
6354 }
6355 return -1;
6356 }
6357
6358 /*!
6359 * wxRichTextLine
6360 * This object represents a line in a paragraph, and stores
6361 * offsets from the start of the paragraph representing the
6362 * start and end positions of the line.
6363 */
6364
6365 wxRichTextLine::wxRichTextLine(wxRichTextParagraph* parent)
6366 {
6367 Init(parent);
6368 }
6369
6370 /// Initialisation
6371 void wxRichTextLine::Init(wxRichTextParagraph* parent)
6372 {
6373 m_parent = parent;
6374 m_range.SetRange(-1, -1);
6375 m_pos = wxPoint(0, 0);
6376 m_size = wxSize(0, 0);
6377 m_descent = 0;
6378 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6379 m_objectSizes.Clear();
6380 #endif
6381 }
6382
6383 /// Copy
6384 void wxRichTextLine::Copy(const wxRichTextLine& obj)
6385 {
6386 m_range = obj.m_range;
6387 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
6388 m_objectSizes = obj.m_objectSizes;
6389 #endif
6390 }
6391
6392 /// Get the absolute object position
6393 wxPoint wxRichTextLine::GetAbsolutePosition() const
6394 {
6395 return m_parent->GetPosition() + m_pos;
6396 }
6397
6398 /// Get the absolute range
6399 wxRichTextRange wxRichTextLine::GetAbsoluteRange() const
6400 {
6401 wxRichTextRange range(m_range.GetStart() + m_parent->GetRange().GetStart(), 0);
6402 range.SetEnd(range.GetStart() + m_range.GetLength()-1);
6403 return range;
6404 }
6405
6406 /*!
6407 * wxRichTextPlainText
6408 * This object represents a single piece of text.
6409 */
6410
6411 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText, wxRichTextObject)
6412
6413 wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* style):
6414 wxRichTextObject(parent)
6415 {
6416 if (style)
6417 SetAttributes(*style);
6418
6419 m_text = text;
6420 }
6421
6422 #define USE_KERNING_FIX 1
6423
6424 // If insufficient tabs are defined, this is the tab width used
6425 #define WIDTH_FOR_DEFAULT_TABS 50
6426
6427 /// Draw the item
6428 bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
6429 {
6430 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6431 wxASSERT (para != NULL);
6432
6433 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
6434 context.ApplyVirtualAttributes(textAttr, this);
6435
6436 // Let's make the assumption for now that for content in a paragraph, including
6437 // text, we never have a discontinuous selection. So we only deal with a
6438 // single range.
6439 wxRichTextRange selectionRange;
6440 if (selection.IsValid())
6441 {
6442 wxRichTextRangeArray selectionRanges = selection.GetSelectionForObject(this);
6443 if (selectionRanges.GetCount() > 0)
6444 selectionRange = selectionRanges[0];
6445 else
6446 selectionRange = wxRICHTEXT_NO_SELECTION;
6447 }
6448 else
6449 selectionRange = wxRICHTEXT_NO_SELECTION;
6450
6451 int offset = GetRange().GetStart();
6452
6453 wxString str = m_text;
6454 if (context.HasVirtualText(this))
6455 {
6456 if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
6457 str = m_text;
6458 }
6459
6460 // Replace line break characters with spaces
6461 wxString toRemove = wxRichTextLineBreakChar;
6462 str.Replace(toRemove, wxT(" "));
6463 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
6464 str.MakeUpper();
6465
6466 long len = range.GetLength();
6467 wxString stringChunk = str.Mid(range.GetStart() - offset, (size_t) len);
6468
6469 // Test for the optimized situations where all is selected, or none
6470 // is selected.
6471
6472 wxFont textFont(GetBuffer()->GetFontTable().FindFont(textAttr));
6473 wxCheckSetFont(dc, textFont);
6474 int charHeight = dc.GetCharHeight();
6475
6476 int x, y;
6477 if ( textFont.IsOk() )
6478 {
6479 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
6480 {
6481 textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
6482 wxCheckSetFont(dc, textFont);
6483 charHeight = dc.GetCharHeight();
6484 }
6485
6486 if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT) )
6487 {
6488 if (textFont.IsUsingSizeInPixels())
6489 {
6490 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6491 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6492 x = rect.x;
6493 y = rect.y;
6494 }
6495 else
6496 {
6497 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6498 textFont.SetPointSize(static_cast<int>(size));
6499 x = rect.x;
6500 y = rect.y;
6501 }
6502 wxCheckSetFont(dc, textFont);
6503 }
6504 else if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) )
6505 {
6506 if (textFont.IsUsingSizeInPixels())
6507 {
6508 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6509 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6510 x = rect.x;
6511 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
6512 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6513 }
6514 else
6515 {
6516 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6517 textFont.SetPointSize(static_cast<int>(size));
6518 x = rect.x;
6519 int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
6520 y = rect.y + (rect.height - sub_height + (descent - m_descent));
6521 }
6522 wxCheckSetFont(dc, textFont);
6523 }
6524 else
6525 {
6526 x = rect.x;
6527 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6528 }
6529 }
6530 else
6531 {
6532 x = rect.x;
6533 y = rect.y + (rect.height - charHeight - (descent - m_descent));
6534 }
6535
6536 // TODO: new selection code
6537
6538 // (a) All selected.
6539 if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd())
6540 {
6541 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, true);
6542 }
6543 // (b) None selected.
6544 else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd())
6545 {
6546 // Draw all unselected
6547 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, false);
6548 }
6549 else
6550 {
6551 // (c) Part selected, part not
6552 // Let's draw unselected chunk, selected chunk, then unselected chunk.
6553
6554 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
6555
6556 // 1. Initial unselected chunk, if any, up until start of selection.
6557 if (selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd())
6558 {
6559 int r1 = range.GetStart();
6560 int s1 = selectionRange.GetStart()-1;
6561 int fragmentLen = s1 - r1 + 1;
6562 if (fragmentLen < 0)
6563 {
6564 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 - offset), (int)fragmentLen);
6565 }
6566 wxString stringFragment = str.Mid(r1 - offset, fragmentLen);
6567
6568 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
6569
6570 #if USE_KERNING_FIX
6571 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6572 {
6573 // Compensate for kerning difference
6574 wxString stringFragment2(str.Mid(r1 - offset, fragmentLen+1));
6575 wxString stringFragment3(str.Mid(r1 - offset + fragmentLen, 1));
6576
6577 wxCoord w1, h1, w2, h2, w3, h3;
6578 dc.GetTextExtent(stringFragment, & w1, & h1);
6579 dc.GetTextExtent(stringFragment2, & w2, & h2);
6580 dc.GetTextExtent(stringFragment3, & w3, & h3);
6581
6582 int kerningDiff = (w1 + w3) - w2;
6583 x = x - kerningDiff;
6584 }
6585 #endif
6586 }
6587
6588 // 2. Selected chunk, if any.
6589 if (selectionRange.GetEnd() >= range.GetStart())
6590 {
6591 int s1 = wxMax(selectionRange.GetStart(), range.GetStart());
6592 int s2 = wxMin(selectionRange.GetEnd(), range.GetEnd());
6593
6594 int fragmentLen = s2 - s1 + 1;
6595 if (fragmentLen < 0)
6596 {
6597 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 - offset), (int)fragmentLen);
6598 }
6599 wxString stringFragment = str.Mid(s1 - offset, fragmentLen);
6600
6601 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, true);
6602
6603 #if USE_KERNING_FIX
6604 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6605 {
6606 // Compensate for kerning difference
6607 wxString stringFragment2(str.Mid(s1 - offset, fragmentLen+1));
6608 wxString stringFragment3(str.Mid(s1 - offset + fragmentLen, 1));
6609
6610 wxCoord w1, h1, w2, h2, w3, h3;
6611 dc.GetTextExtent(stringFragment, & w1, & h1);
6612 dc.GetTextExtent(stringFragment2, & w2, & h2);
6613 dc.GetTextExtent(stringFragment3, & w3, & h3);
6614
6615 int kerningDiff = (w1 + w3) - w2;
6616 x = x - kerningDiff;
6617 }
6618 #endif
6619 }
6620
6621 // 3. Remaining unselected chunk, if any
6622 if (selectionRange.GetEnd() < range.GetEnd())
6623 {
6624 int s2 = wxMin(selectionRange.GetEnd()+1, range.GetEnd());
6625 int r2 = range.GetEnd();
6626
6627 int fragmentLen = r2 - s2 + 1;
6628 if (fragmentLen < 0)
6629 {
6630 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 - offset), (int)fragmentLen);
6631 }
6632 wxString stringFragment = str.Mid(s2 - offset, fragmentLen);
6633
6634 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
6635 }
6636 }
6637
6638 return true;
6639 }
6640
6641 bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect,wxString& str, wxCoord& x, wxCoord& y, bool selected)
6642 {
6643 bool hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6644
6645 wxArrayInt tabArray;
6646 int tabCount;
6647 if (hasTabs)
6648 {
6649 if (attr.GetTabs().IsEmpty())
6650 tabArray = wxRichTextParagraph::GetDefaultTabs();
6651 else
6652 tabArray = attr.GetTabs();
6653 tabCount = tabArray.GetCount();
6654
6655 for (int i = 0; i < tabCount; ++i)
6656 {
6657 int pos = tabArray[i];
6658 pos = ConvertTenthsMMToPixels(dc, pos);
6659 tabArray[i] = pos;
6660 }
6661 }
6662 else
6663 tabCount = 0;
6664
6665 int nextTabPos = -1;
6666 int tabPos = -1;
6667 wxCoord w, h;
6668
6669 if (selected)
6670 {
6671 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
6672 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
6673
6674 wxCheckSetBrush(dc, wxBrush(highlightColour));
6675 wxCheckSetPen(dc, wxPen(highlightColour));
6676 dc.SetTextForeground(highlightTextColour);
6677 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
6678 }
6679 else
6680 {
6681 dc.SetTextForeground(attr.GetTextColour());
6682
6683 if (attr.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR) && attr.GetBackgroundColour().IsOk())
6684 {
6685 dc.SetBackgroundMode(wxBRUSHSTYLE_SOLID);
6686 dc.SetTextBackground(attr.GetBackgroundColour());
6687 }
6688 else
6689 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
6690 }
6691
6692 wxCoord x_orig = GetParent()->GetPosition().x;
6693 while (hasTabs)
6694 {
6695 // the string has a tab
6696 // break up the string at the Tab
6697 wxString stringChunk = str.BeforeFirst(wxT('\t'));
6698 str = str.AfterFirst(wxT('\t'));
6699 dc.GetTextExtent(stringChunk, & w, & h);
6700 tabPos = x + w;
6701 bool not_found = true;
6702 for (int i = 0; i < tabCount && not_found; ++i)
6703 {
6704 nextTabPos = tabArray.Item(i) + x_orig;
6705
6706 // Find the next tab position.
6707 // Even if we're at the end of the tab array, we must still draw the chunk.
6708
6709 if (nextTabPos > tabPos || (i == (tabCount - 1)))
6710 {
6711 if (nextTabPos <= tabPos)
6712 {
6713 int defaultTabWidth = ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6714 nextTabPos = tabPos + defaultTabWidth;
6715 }
6716
6717 not_found = false;
6718 if (selected)
6719 {
6720 w = nextTabPos - x;
6721 wxRect selRect(x, rect.y, w, rect.GetHeight());
6722 dc.DrawRectangle(selRect);
6723 }
6724 dc.DrawText(stringChunk, x, y);
6725
6726 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6727 {
6728 wxPen oldPen = dc.GetPen();
6729 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
6730 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
6731 wxCheckSetPen(dc, oldPen);
6732 }
6733
6734 x = nextTabPos;
6735 }
6736 }
6737 hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6738 }
6739
6740 if (!str.IsEmpty())
6741 {
6742 dc.GetTextExtent(str, & w, & h);
6743 if (selected)
6744 {
6745 wxRect selRect(x, rect.y, w, rect.GetHeight());
6746 dc.DrawRectangle(selRect);
6747 }
6748 dc.DrawText(str, x, y);
6749
6750 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6751 {
6752 wxPen oldPen = dc.GetPen();
6753 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
6754 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
6755 wxCheckSetPen(dc, oldPen);
6756 }
6757
6758 x += w;
6759 }
6760
6761 return true;
6762 }
6763
6764 /// Lay the item out
6765 bool wxRichTextPlainText::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
6766 {
6767 // Only lay out if we haven't already cached the size
6768 if (m_size.x == -1)
6769 GetRangeSize(GetRange(), m_size, m_descent, dc, context, 0, wxPoint(0, 0));
6770 m_maxSize = m_size;
6771 // Eventually we want to have a reasonable estimate of minimum size.
6772 m_minSize = wxSize(0, 0);
6773 return true;
6774 }
6775
6776 /// Copy
6777 void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj)
6778 {
6779 wxRichTextObject::Copy(obj);
6780
6781 m_text = obj.m_text;
6782 }
6783
6784 /// Get/set the object size for the given range. Returns false if the range
6785 /// is invalid for this object.
6786 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
6787 {
6788 if (!range.IsWithin(GetRange()))
6789 return false;
6790
6791 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6792 wxASSERT (para != NULL);
6793
6794 int relativeX = position.x - GetParent()->GetPosition().x;
6795
6796 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6797 context.ApplyVirtualAttributes(textAttr, (wxRichTextObject*) this);
6798
6799 // Always assume unformatted text, since at this level we have no knowledge
6800 // of line breaks - and we don't need it, since we'll calculate size within
6801 // formatted text by doing it in chunks according to the line ranges
6802
6803 bool bScript(false);
6804 wxFont font(GetBuffer()->GetFontTable().FindFont(textAttr));
6805 if (font.IsOk())
6806 {
6807 if ( textAttr.HasTextEffects() && ( (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT)
6808 || (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) )
6809 {
6810 wxFont textFont = font;
6811 if (textFont.IsUsingSizeInPixels())
6812 {
6813 double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
6814 textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
6815 }
6816 else
6817 {
6818 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6819 textFont.SetPointSize(static_cast<int>(size));
6820 }
6821 wxCheckSetFont(dc, textFont);
6822 bScript = true;
6823 }
6824 else if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SMALL_CAPITALS))
6825 {
6826 wxFont textFont = font;
6827 textFont.SetPointSize((int) (textFont.GetPointSize()*0.75));
6828 wxCheckSetFont(dc, textFont);
6829 bScript = true;
6830 }
6831 else
6832 {
6833 wxCheckSetFont(dc, font);
6834 }
6835 }
6836
6837 bool haveDescent = false;
6838 int startPos = range.GetStart() - GetRange().GetStart();
6839 long len = range.GetLength();
6840
6841 wxString str(m_text);
6842 if (context.HasVirtualText(this))
6843 {
6844 if (!context.GetVirtualText(this, str) || str.Length() != m_text.Length())
6845 str = m_text;
6846 }
6847
6848 wxString toReplace = wxRichTextLineBreakChar;
6849 str.Replace(toReplace, wxT(" "));
6850
6851 wxString stringChunk = str.Mid(startPos, (size_t) len);
6852
6853 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & (wxTEXT_ATTR_EFFECT_CAPITALS|wxTEXT_ATTR_EFFECT_SMALL_CAPITALS)))
6854 stringChunk.MakeUpper();
6855
6856 wxCoord w, h;
6857 int width = 0;
6858 if (stringChunk.Find(wxT('\t')) != wxNOT_FOUND)
6859 {
6860 // the string has a tab
6861 wxArrayInt tabArray;
6862 if (textAttr.GetTabs().IsEmpty())
6863 tabArray = wxRichTextParagraph::GetDefaultTabs();
6864 else
6865 tabArray = textAttr.GetTabs();
6866
6867 int tabCount = tabArray.GetCount();
6868
6869 for (int i = 0; i < tabCount; ++i)
6870 {
6871 int pos = tabArray[i];
6872 pos = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, pos);
6873 tabArray[i] = pos;
6874 }
6875
6876 int nextTabPos = -1;
6877
6878 while (stringChunk.Find(wxT('\t')) >= 0)
6879 {
6880 int absoluteWidth = 0;
6881
6882 // the string has a tab
6883 // break up the string at the Tab
6884 wxString stringFragment = stringChunk.BeforeFirst(wxT('\t'));
6885 stringChunk = stringChunk.AfterFirst(wxT('\t'));
6886
6887 if (partialExtents)
6888 {
6889 int oldWidth;
6890 if (partialExtents->GetCount() > 0)
6891 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6892 else
6893 oldWidth = 0;
6894
6895 // Add these partial extents
6896 wxArrayInt p;
6897 dc.GetPartialTextExtents(stringFragment, p);
6898 size_t j;
6899 for (j = 0; j < p.GetCount(); j++)
6900 partialExtents->Add(oldWidth + p[j]);
6901
6902 if (partialExtents->GetCount() > 0)
6903 absoluteWidth = (*partialExtents)[(*partialExtents).GetCount()-1] + relativeX;
6904 else
6905 absoluteWidth = relativeX;
6906 }
6907 else
6908 {
6909 dc.GetTextExtent(stringFragment, & w, & h);
6910 width += w;
6911 absoluteWidth = width + relativeX;
6912 haveDescent = true;
6913 }
6914
6915 bool notFound = true;
6916 for (int i = 0; i < tabCount && notFound; ++i)
6917 {
6918 nextTabPos = tabArray.Item(i);
6919
6920 // Find the next tab position.
6921 // Even if we're at the end of the tab array, we must still process the chunk.
6922
6923 if (nextTabPos > absoluteWidth || (i == (tabCount - 1)))
6924 {
6925 if (nextTabPos <= absoluteWidth)
6926 {
6927 int defaultTabWidth = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6928 nextTabPos = absoluteWidth + defaultTabWidth;
6929 }
6930
6931 notFound = false;
6932 width = nextTabPos - relativeX;
6933
6934 if (partialExtents)
6935 partialExtents->Add(width);
6936 }
6937 }
6938 }
6939 }
6940
6941 if (!stringChunk.IsEmpty())
6942 {
6943 if (partialExtents)
6944 {
6945 int oldWidth;
6946 if (partialExtents->GetCount() > 0)
6947 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6948 else
6949 oldWidth = 0;
6950
6951 // Add these partial extents
6952 wxArrayInt p;
6953 dc.GetPartialTextExtents(stringChunk, p);
6954 size_t j;
6955 for (j = 0; j < p.GetCount(); j++)
6956 partialExtents->Add(oldWidth + p[j]);
6957 }
6958 else
6959 {
6960 dc.GetTextExtent(stringChunk, & w, & h, & descent);
6961 width += w;
6962 haveDescent = true;
6963 }
6964 }
6965
6966 if (partialExtents)
6967 {
6968 int charHeight = dc.GetCharHeight();
6969 if ((*partialExtents).GetCount() > 0)
6970 w = (*partialExtents)[partialExtents->GetCount()-1];
6971 else
6972 w = 0;
6973 size = wxSize(w, charHeight);
6974 }
6975 else
6976 {
6977 size = wxSize(width, dc.GetCharHeight());
6978 }
6979
6980 if (!haveDescent)
6981 dc.GetTextExtent(wxT("X"), & w, & h, & descent);
6982
6983 if ( bScript )
6984 dc.SetFont(font);
6985
6986 return true;
6987 }
6988
6989 /// Do a split, returning an object containing the second part, and setting
6990 /// the first part in 'this'.
6991 wxRichTextObject* wxRichTextPlainText::DoSplit(long pos)
6992 {
6993 long index = pos - GetRange().GetStart();
6994
6995 if (index < 0 || index >= (int) m_text.length())
6996 return NULL;
6997
6998 wxString firstPart = m_text.Mid(0, index);
6999 wxString secondPart = m_text.Mid(index);
7000
7001 m_text = firstPart;
7002
7003 wxRichTextPlainText* newObject = new wxRichTextPlainText(secondPart);
7004 newObject->SetAttributes(GetAttributes());
7005 newObject->SetProperties(GetProperties());
7006
7007 newObject->SetRange(wxRichTextRange(pos, GetRange().GetEnd()));
7008 GetRange().SetEnd(pos-1);
7009
7010 return newObject;
7011 }
7012
7013 /// Calculate range
7014 void wxRichTextPlainText::CalculateRange(long start, long& end)
7015 {
7016 end = start + m_text.length() - 1;
7017 m_range.SetRange(start, end);
7018 }
7019
7020 /// Delete range
7021 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange& range)
7022 {
7023 wxRichTextRange r = range;
7024
7025 r.LimitTo(GetRange());
7026
7027 if (r.GetStart() == GetRange().GetStart() && r.GetEnd() == GetRange().GetEnd())
7028 {
7029 m_text.Empty();
7030 return true;
7031 }
7032
7033 long startIndex = r.GetStart() - GetRange().GetStart();
7034 long len = r.GetLength();
7035
7036 m_text = m_text.Mid(0, startIndex) + m_text.Mid(startIndex+len);
7037 return true;
7038 }
7039
7040 /// Get text for the given range.
7041 wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) const
7042 {
7043 wxRichTextRange r = range;
7044
7045 r.LimitTo(GetRange());
7046
7047 long startIndex = r.GetStart() - GetRange().GetStart();
7048 long len = r.GetLength();
7049
7050 return m_text.Mid(startIndex, len);
7051 }
7052
7053 /// Returns true if this object can merge itself with the given one.
7054 bool wxRichTextPlainText::CanMerge(wxRichTextObject* object, wxRichTextDrawingContext& context) const
7055 {
7056 // JACS 2013-01-27
7057 if (!context.GetVirtualAttributesEnabled())
7058 {
7059 return object->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText) &&
7060 (m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties()));
7061 }
7062 else
7063 {
7064 wxRichTextPlainText* otherObj = wxDynamicCast(object, wxRichTextPlainText);
7065 if (!otherObj || m_text.empty())
7066 return false;
7067
7068 if (!wxTextAttrEq(GetAttributes(), object->GetAttributes()) || !(m_properties == object->GetProperties()))
7069 return false;
7070
7071 // Check if differing virtual attributes makes it impossible to merge
7072 // these strings.
7073
7074 bool hasVirtualAttr1 = context.HasVirtualAttributes((wxRichTextObject*) this);
7075 bool hasVirtualAttr2 = context.HasVirtualAttributes((wxRichTextObject*) object);
7076 if (!hasVirtualAttr1 && !hasVirtualAttr2)
7077 return true;
7078 else if (hasVirtualAttr1 != hasVirtualAttr2)
7079 return false;
7080 else
7081 {
7082 wxRichTextAttr virtualAttr1 = context.GetVirtualAttributes((wxRichTextObject*) this);
7083 wxRichTextAttr virtualAttr2 = context.GetVirtualAttributes((wxRichTextObject*) object);
7084 return virtualAttr1 == virtualAttr2;
7085 }
7086 }
7087 }
7088
7089 /// Returns true if this object merged itself with the given one.
7090 /// The calling code will then delete the given object.
7091 bool wxRichTextPlainText::Merge(wxRichTextObject* object, wxRichTextDrawingContext& WXUNUSED(context))
7092 {
7093 wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText);
7094 wxASSERT( textObject != NULL );
7095
7096 if (textObject)
7097 {
7098 m_text += textObject->GetText();
7099 wxRichTextApplyStyle(m_attributes, textObject->GetAttributes());
7100 return true;
7101 }
7102 else
7103 return false;
7104 }
7105
7106 bool wxRichTextPlainText::CanSplit(wxRichTextDrawingContext& context) const
7107 {
7108 // If this object has any virtual attributes at all, whether for the whole object
7109 // or individual ones, we should try splitting it by calling Split.
7110 // Must be more than one character in order to be able to split.
7111 return m_text.Length() > 1 && context.HasVirtualAttributes((wxRichTextObject*) this);
7112 }
7113
7114 wxRichTextObject* wxRichTextPlainText::Split(wxRichTextDrawingContext& context)
7115 {
7116 int count = context.GetVirtualSubobjectAttributesCount(this);
7117 if (count > 0 && GetParent())
7118 {
7119 wxRichTextCompositeObject* parent = wxDynamicCast(GetParent(), wxRichTextCompositeObject);
7120 wxRichTextObjectList::compatibility_iterator node = parent->GetChildren().Find(this);
7121 if (node)
7122 {
7123 const wxRichTextAttr emptyAttr;
7124 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7125
7126 wxArrayInt positions;
7127 wxRichTextAttrArray attributes;
7128 if (context.GetVirtualSubobjectAttributes(this, positions, attributes) && positions.GetCount() > 0)
7129 {
7130 wxASSERT(positions.GetCount() == attributes.GetCount());
7131
7132 // We will gather up runs of text with the same virtual attributes
7133
7134 int len = m_text.Length();
7135 int i = 0;
7136
7137 // runStart and runEnd represent the accumulated run with a consistent attribute
7138 // that hasn't yet been appended
7139 int runStart = -1;
7140 int runEnd = -1;
7141 wxRichTextAttr currentAttr;
7142 wxString text = m_text;
7143 wxRichTextPlainText* lastPlainText = this;
7144
7145 for (i = 0; i < (int) positions.GetCount(); i++)
7146 {
7147 int pos = positions[i];
7148 wxASSERT(pos >= 0 && pos < len);
7149 if (pos >= 0 && pos < len)
7150 {
7151 const wxRichTextAttr& attr = attributes[i];
7152
7153 if (pos == 0)
7154 {
7155 runStart = 0;
7156 currentAttr = attr;
7157 }
7158 // Check if there was a gap from the last known attribute and this.
7159 // In that case, we need to do something with the span of non-attributed text.
7160 else if ((pos-1) > runEnd)
7161 {
7162 if (runEnd == -1)
7163 {
7164 // We hadn't processed anything previously, so the previous run is from the text start
7165 // to just before this position. The current attribute remains empty.
7166 runStart = 0;
7167 runEnd = pos-1;
7168 }
7169 else
7170 {
7171 // If the previous attribute matches the gap's attribute (i.e., no attributes)
7172 // then just extend the run.
7173 if (currentAttr.IsDefault())
7174 {
7175 runEnd = pos-1;
7176 }
7177 else
7178 {
7179 // We need to add an object, or reuse the existing one.
7180 if (runStart == 0)
7181 {
7182 lastPlainText = this;
7183 SetText(text.Mid(runStart, runEnd - runStart + 1));
7184 }
7185 else
7186 {
7187 wxRichTextPlainText* obj = new wxRichTextPlainText;
7188 lastPlainText = obj;
7189 obj->SetAttributes(GetAttributes());
7190 obj->SetProperties(GetProperties());
7191 obj->SetParent(parent);
7192
7193 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7194 if (next)
7195 parent->GetChildren().Insert(next, obj);
7196 else
7197 parent->GetChildren().Append(obj);
7198 }
7199
7200 runStart = runEnd+1;
7201 runEnd = pos-1;
7202
7203 currentAttr = emptyAttr;
7204 }
7205 }
7206 }
7207
7208 wxASSERT(runEnd == pos-1);
7209
7210 // Now we only have to deal with the previous run
7211 if (currentAttr == attr)
7212 {
7213 // If we still have the same attributes, then we
7214 // simply increase the run size.
7215 runEnd = pos;
7216 }
7217 else
7218 {
7219 if (runEnd >= 0)
7220 {
7221 // We need to add an object, or reuse the existing one.
7222 if (runStart == 0)
7223 {
7224 lastPlainText = this;
7225 SetText(text.Mid(runStart, runEnd - runStart + 1));
7226 }
7227 else
7228 {
7229 wxRichTextPlainText* obj = new wxRichTextPlainText;
7230 lastPlainText = obj;
7231 obj->SetAttributes(GetAttributes());
7232 obj->SetProperties(GetProperties());
7233 obj->SetParent(parent);
7234
7235 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7236 if (next)
7237 parent->GetChildren().Insert(next, obj);
7238 else
7239 parent->GetChildren().Append(obj);
7240 }
7241 }
7242
7243 runStart = pos;
7244 runEnd = pos;
7245
7246 currentAttr = attr;
7247 }
7248 }
7249 }
7250
7251 // We may still have a run to add, and possibly a no-attribute text fragment after that.
7252 // If the whole string was already a single attribute (the run covers the whole string), don't split.
7253 if ((runStart != -1) && !(runStart == 0 && runEnd == (len-1)))
7254 {
7255 // If the current attribute is empty, merge the run with the next fragment
7256 // which by definition (because it's not specified) has empty attributes.
7257 if (currentAttr.IsDefault())
7258 runEnd = (len-1);
7259
7260 if (runEnd < (len-1))
7261 {
7262 // We need to add an object, or reuse the existing one.
7263 if (runStart == 0)
7264 {
7265 lastPlainText = this;
7266 SetText(text.Mid(runStart, runEnd - runStart + 1));
7267 }
7268 else
7269 {
7270 wxRichTextPlainText* obj = new wxRichTextPlainText;
7271 lastPlainText = obj;
7272 obj->SetAttributes(GetAttributes());
7273 obj->SetProperties(GetProperties());
7274 obj->SetParent(parent);
7275
7276 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7277 if (next)
7278 parent->GetChildren().Insert(next, obj);
7279 else
7280 parent->GetChildren().Append(obj);
7281 }
7282
7283 runStart = runEnd+1;
7284 runEnd = (len-1);
7285 }
7286
7287 // Now the last, non-attributed fragment at the end, if any
7288 if ((runStart < len) && !(runStart == 0 && runEnd == (len-1)))
7289 {
7290 wxASSERT(runStart != 0);
7291
7292 wxRichTextPlainText* obj = new wxRichTextPlainText;
7293 obj->SetAttributes(GetAttributes());
7294 obj->SetProperties(GetProperties());
7295 obj->SetParent(parent);
7296
7297 obj->SetText(text.Mid(runStart, runEnd - runStart + 1));
7298 if (next)
7299 parent->GetChildren().Insert(next, obj);
7300 else
7301 parent->GetChildren().Append(obj);
7302
7303 lastPlainText = obj;
7304 }
7305 }
7306
7307 return lastPlainText;
7308 }
7309 }
7310 }
7311 return this;
7312 }
7313
7314 /// Dump to output stream for debugging
7315 void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
7316 {
7317 wxRichTextObject::Dump(stream);
7318 stream << m_text << wxT("\n");
7319 }
7320
7321 /// Get the first position from pos that has a line break character.
7322 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos)
7323 {
7324 int i;
7325 int len = m_text.length();
7326 int startPos = pos - m_range.GetStart();
7327 for (i = startPos; i < len; i++)
7328 {
7329 wxChar ch = m_text[i];
7330 if (ch == wxRichTextLineBreakChar)
7331 {
7332 return i + m_range.GetStart();
7333 }
7334 }
7335 return -1;
7336 }
7337
7338 /*!
7339 * wxRichTextBuffer
7340 * This is a kind of box, used to represent the whole buffer
7341 */
7342
7343 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox)
7344
7345 wxList wxRichTextBuffer::sm_handlers;
7346 wxList wxRichTextBuffer::sm_drawingHandlers;
7347 wxRichTextFieldTypeHashMap wxRichTextBuffer::sm_fieldTypes;
7348 wxRichTextRenderer* wxRichTextBuffer::sm_renderer = NULL;
7349 int wxRichTextBuffer::sm_bulletRightMargin = 20;
7350 float wxRichTextBuffer::sm_bulletProportion = (float) 0.3;
7351 bool wxRichTextBuffer::sm_floatingLayoutMode = true;
7352
7353 /// Initialisation
7354 void wxRichTextBuffer::Init()
7355 {
7356 m_commandProcessor = new wxCommandProcessor;
7357 m_styleSheet = NULL;
7358 m_modified = false;
7359 m_batchedCommandDepth = 0;
7360 m_batchedCommand = NULL;
7361 m_suppressUndo = 0;
7362 m_handlerFlags = 0;
7363 m_scale = 1.0;
7364 m_dimensionScale = 1.0;
7365 m_fontScale = 1.0;
7366 SetMargins(4);
7367 }
7368
7369 /// Initialisation
7370 wxRichTextBuffer::~wxRichTextBuffer()
7371 {
7372 delete m_commandProcessor;
7373 delete m_batchedCommand;
7374
7375 ClearStyleStack();
7376 ClearEventHandlers();
7377 }
7378
7379 void wxRichTextBuffer::ResetAndClearCommands()
7380 {
7381 Reset();
7382
7383 GetCommandProcessor()->ClearCommands();
7384
7385 Modify(false);
7386 Invalidate(wxRICHTEXT_ALL);
7387 }
7388
7389 void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj)
7390 {
7391 wxRichTextParagraphLayoutBox::Copy(obj);
7392
7393 m_styleSheet = obj.m_styleSheet;
7394 m_modified = obj.m_modified;
7395 m_batchedCommandDepth = 0;
7396 if (m_batchedCommand)
7397 delete m_batchedCommand;
7398 m_batchedCommand = NULL;
7399 m_suppressUndo = obj.m_suppressUndo;
7400 m_invalidRange = obj.m_invalidRange;
7401 m_dimensionScale = obj.m_dimensionScale;
7402 m_fontScale = obj.m_fontScale;
7403 }
7404
7405 /// Push style sheet to top of stack
7406 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet* styleSheet)
7407 {
7408 if (m_styleSheet)
7409 styleSheet->InsertSheet(m_styleSheet);
7410
7411 SetStyleSheet(styleSheet);
7412
7413 return true;
7414 }
7415
7416 /// Pop style sheet from top of stack
7417 wxRichTextStyleSheet* wxRichTextBuffer::PopStyleSheet()
7418 {
7419 if (m_styleSheet)
7420 {
7421 wxRichTextStyleSheet* oldSheet = m_styleSheet;
7422 m_styleSheet = oldSheet->GetNextSheet();
7423 oldSheet->Unlink();
7424
7425 return oldSheet;
7426 }
7427 else
7428 return NULL;
7429 }
7430
7431 /// Submit command to insert paragraphs
7432 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int flags)
7433 {
7434 return ctrl->GetFocusObject()->InsertParagraphsWithUndo(this, pos, paragraphs, ctrl, flags);
7435 }
7436
7437 /// Submit command to insert paragraphs
7438 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int WXUNUSED(flags))
7439 {
7440 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7441
7442 action->GetNewParagraphs() = paragraphs;
7443
7444 action->SetPosition(pos);
7445
7446 wxRichTextRange range = wxRichTextRange(pos, pos + paragraphs.GetOwnRange().GetEnd() - 1);
7447 if (!paragraphs.GetPartialParagraph())
7448 range.SetEnd(range.GetEnd()+1);
7449
7450 // Set the range we'll need to delete in Undo
7451 action->SetRange(range);
7452
7453 buffer->SubmitAction(action);
7454
7455 return true;
7456 }
7457
7458 /// Submit command to insert the given text
7459 bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
7460 {
7461 if (ctrl)
7462 return ctrl->GetFocusObject()->InsertTextWithUndo(this, pos, text, ctrl, flags);
7463 else
7464 return wxRichTextParagraphLayoutBox::InsertTextWithUndo(this, pos, text, ctrl, flags);
7465 }
7466
7467 /// Submit command to insert the given text
7468 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
7469 {
7470 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7471
7472 wxRichTextAttr* p = NULL;
7473 wxRichTextAttr paraAttr;
7474 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7475 {
7476 // Get appropriate paragraph style
7477 paraAttr = GetStyleForNewParagraph(buffer, pos, false, false);
7478 if (!paraAttr.IsDefault())
7479 p = & paraAttr;
7480 }
7481
7482 action->GetNewParagraphs().AddParagraphs(text, p);
7483
7484 int length = action->GetNewParagraphs().GetOwnRange().GetLength();
7485
7486 if (!text.empty() && text.Last() != wxT('\n'))
7487 {
7488 // Don't count the newline when undoing
7489 length --;
7490 action->GetNewParagraphs().SetPartialParagraph(true);
7491 }
7492 else if (!text.empty() && text.Last() == wxT('\n'))
7493 length --;
7494
7495 action->SetPosition(pos);
7496
7497 // Set the range we'll need to delete in Undo
7498 action->SetRange(wxRichTextRange(pos, pos + length - 1));
7499
7500 buffer->SubmitAction(action);
7501
7502 return true;
7503 }
7504
7505 /// Submit command to insert the given text
7506 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl, int flags)
7507 {
7508 return ctrl->GetFocusObject()->InsertNewlineWithUndo(this, pos, ctrl, flags);
7509 }
7510
7511 /// Submit command to insert the given text
7512 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextCtrl* ctrl, int flags)
7513 {
7514 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7515
7516 wxRichTextAttr* p = NULL;
7517 wxRichTextAttr paraAttr;
7518 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7519 {
7520 paraAttr = GetStyleForNewParagraph(buffer, pos, false, true /* look for next paragraph style */);
7521 if (!paraAttr.IsDefault())
7522 p = & paraAttr;
7523 }
7524
7525 wxRichTextAttr attr(buffer->GetDefaultStyle());
7526 // Don't include box attributes such as margins
7527 attr.GetTextBoxAttr().Reset();
7528
7529 wxRichTextParagraph* newPara = new wxRichTextParagraph(wxEmptyString, this, & attr);
7530 action->GetNewParagraphs().AppendChild(newPara);
7531 action->GetNewParagraphs().UpdateRanges();
7532 action->GetNewParagraphs().SetPartialParagraph(false);
7533 wxRichTextParagraph* para = GetParagraphAtPosition(pos, false);
7534 long pos1 = pos;
7535
7536 if (p)
7537 newPara->SetAttributes(*p);
7538
7539 if (flags & wxRICHTEXT_INSERT_INTERACTIVE)
7540 {
7541 if (para && para->GetRange().GetEnd() == pos)
7542 pos1 ++;
7543
7544 // Now see if we need to number the paragraph.
7545 if (newPara->GetAttributes().HasBulletNumber())
7546 {
7547 wxRichTextAttr numberingAttr;
7548 if (FindNextParagraphNumber(para, numberingAttr))
7549 wxRichTextApplyStyle(newPara->GetAttributes(), (const wxRichTextAttr&) numberingAttr);
7550 }
7551 }
7552
7553 action->SetPosition(pos);
7554
7555 // Use the default character style
7556 if (!buffer->GetDefaultStyle().IsDefault() && newPara->GetChildren().GetFirst())
7557 {
7558 // Check whether the default style merely reflects the paragraph/basic style,
7559 // in which case don't apply it.
7560 wxRichTextAttr defaultStyle(buffer->GetDefaultStyle());
7561 defaultStyle.GetTextBoxAttr().Reset();
7562 wxRichTextAttr toApply;
7563 if (para)
7564 {
7565 wxRichTextAttr combinedAttr = para->GetCombinedAttributes(true /* include box attributes */);
7566 wxRichTextAttr newAttr;
7567 // This filters out attributes that are accounted for by the current
7568 // paragraph/basic style
7569 wxRichTextApplyStyle(toApply, defaultStyle, & combinedAttr);
7570 }
7571 else
7572 toApply = defaultStyle;
7573
7574 if (!toApply.IsDefault())
7575 newPara->GetChildren().GetFirst()->GetData()->SetAttributes(toApply);
7576 }
7577
7578 // Set the range we'll need to delete in Undo
7579 action->SetRange(wxRichTextRange(pos1, pos1));
7580
7581 buffer->SubmitAction(action);
7582
7583 return true;
7584 }
7585
7586 /// Submit command to insert the given image
7587 bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock, wxRichTextCtrl* ctrl, int flags,
7588 const wxRichTextAttr& textAttr)
7589 {
7590 return ctrl->GetFocusObject()->InsertImageWithUndo(this, pos, imageBlock, ctrl, flags, textAttr);
7591 }
7592
7593 /// Submit command to insert the given image
7594 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextImageBlock& imageBlock,
7595 wxRichTextCtrl* ctrl, int flags,
7596 const wxRichTextAttr& textAttr)
7597 {
7598 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7599
7600 wxRichTextAttr* p = NULL;
7601 wxRichTextAttr paraAttr;
7602 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7603 {
7604 paraAttr = GetStyleForNewParagraph(buffer, pos);
7605 if (!paraAttr.IsDefault())
7606 p = & paraAttr;
7607 }
7608
7609 wxRichTextAttr attr(buffer->GetDefaultStyle());
7610
7611 // Don't include box attributes such as margins
7612 attr.GetTextBoxAttr().Reset();
7613
7614 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7615 if (p)
7616 newPara->SetAttributes(*p);
7617
7618 wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
7619 newPara->AppendChild(imageObject);
7620 imageObject->SetAttributes(textAttr);
7621 action->GetNewParagraphs().AppendChild(newPara);
7622 action->GetNewParagraphs().UpdateRanges();
7623
7624 action->GetNewParagraphs().SetPartialParagraph(true);
7625
7626 action->SetPosition(pos);
7627
7628 // Set the range we'll need to delete in Undo
7629 action->SetRange(wxRichTextRange(pos, pos));
7630
7631 buffer->SubmitAction(action);
7632
7633 return true;
7634 }
7635
7636 // Insert an object with no change of it
7637 wxRichTextObject* wxRichTextBuffer::InsertObjectWithUndo(long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
7638 {
7639 return ctrl->GetFocusObject()->InsertObjectWithUndo(this, pos, object, ctrl, flags);
7640 }
7641
7642 // Insert an object with no change of it
7643 wxRichTextObject* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
7644 {
7645 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Object"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7646
7647 wxRichTextAttr* p = NULL;
7648 wxRichTextAttr paraAttr;
7649 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7650 {
7651 paraAttr = GetStyleForNewParagraph(buffer, pos);
7652 if (!paraAttr.IsDefault())
7653 p = & paraAttr;
7654 }
7655
7656 wxRichTextAttr attr(buffer->GetDefaultStyle());
7657
7658 // Don't include box attributes such as margins
7659 attr.GetTextBoxAttr().Reset();
7660
7661 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7662 if (p)
7663 newPara->SetAttributes(*p);
7664
7665 newPara->AppendChild(object);
7666 action->GetNewParagraphs().AppendChild(newPara);
7667 action->GetNewParagraphs().UpdateRanges();
7668
7669 action->GetNewParagraphs().SetPartialParagraph(true);
7670
7671 action->SetPosition(pos);
7672
7673 // Set the range we'll need to delete in Undo
7674 action->SetRange(wxRichTextRange(pos, pos));
7675
7676 buffer->SubmitAction(action);
7677
7678 wxRichTextObject* obj = GetLeafObjectAtPosition(pos);
7679 return obj;
7680 }
7681
7682 wxRichTextField* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& fieldType,
7683 const wxRichTextProperties& properties,
7684 wxRichTextCtrl* ctrl, int flags,
7685 const wxRichTextAttr& textAttr)
7686 {
7687 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Field"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
7688
7689 wxRichTextAttr* p = NULL;
7690 wxRichTextAttr paraAttr;
7691 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
7692 {
7693 paraAttr = GetStyleForNewParagraph(buffer, pos);
7694 if (!paraAttr.IsDefault())
7695 p = & paraAttr;
7696 }
7697
7698 wxRichTextAttr attr(buffer->GetDefaultStyle());
7699
7700 // Don't include box attributes such as margins
7701 attr.GetTextBoxAttr().Reset();
7702
7703 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
7704 if (p)
7705 newPara->SetAttributes(*p);
7706
7707 wxRichTextField* fieldObject = new wxRichTextField();
7708 fieldObject->wxRichTextObject::SetProperties(properties);
7709 fieldObject->SetFieldType(fieldType);
7710 fieldObject->SetAttributes(textAttr);
7711 newPara->AppendChild(fieldObject);
7712 action->GetNewParagraphs().AppendChild(newPara);
7713 action->GetNewParagraphs().UpdateRanges();
7714 action->GetNewParagraphs().SetPartialParagraph(true);
7715 action->SetPosition(pos);
7716
7717 // Set the range we'll need to delete in Undo
7718 action->SetRange(wxRichTextRange(pos, pos));
7719
7720 buffer->SubmitAction(action);
7721
7722 wxRichTextField* obj = wxDynamicCast(GetLeafObjectAtPosition(pos), wxRichTextField);
7723 return obj;
7724 }
7725
7726 bool wxRichTextParagraphLayoutBox::SetObjectPropertiesWithUndo(wxRichTextObject& obj, const wxRichTextProperties& properties, wxRichTextObject* objToSet)
7727 {
7728 wxRichTextBuffer* buffer = GetBuffer();
7729 wxCHECK_MSG(buffer, false, wxT("Invalid buffer"));
7730 wxRichTextCtrl* rtc = buffer->GetRichTextCtrl();
7731 wxCHECK_MSG(rtc, false, wxT("Invalid rtc"));
7732
7733 wxRichTextAction* action = NULL;
7734 wxRichTextObject* clone = NULL;
7735
7736 if (rtc->SuppressingUndo())
7737 obj.SetProperties(properties);
7738 else
7739 {
7740 clone = obj.Clone();
7741 if (objToSet)
7742 {
7743 // Necessary e.g. if when setting a wxRichTextCell's properties, when obj will be the parent table
7744 objToSet->SetProperties(properties);
7745 }
7746 else
7747 {
7748 obj.SetProperties(properties);
7749 }
7750
7751 // The 'true' parameter in the next line says "Ignore first time"; otherwise the objects are prematurely switched
7752 action = new wxRichTextAction(NULL, _("Change Properties"), wxRICHTEXT_CHANGE_OBJECT, buffer, obj.GetParentContainer(), rtc, true);
7753 action->SetOldAndNewObjects(& obj, clone);
7754 action->SetPosition(obj.GetRange().GetStart());
7755 action->SetRange(obj.GetRange());
7756 buffer->SubmitAction(action);
7757 }
7758
7759 return true;
7760 }
7761
7762 /// Get the style that is appropriate for a new paragraph at this position.
7763 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
7764 /// style.
7765 wxRichTextAttr wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer* buffer, long pos, bool caretPosition, bool lookUpNewParaStyle) const
7766 {
7767 wxRichTextParagraph* para = GetParagraphAtPosition(pos, caretPosition);
7768 if (para)
7769 {
7770 wxRichTextAttr attr;
7771 bool foundAttributes = false;
7772
7773 // Look for a matching paragraph style
7774 if (lookUpNewParaStyle && !para->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer->GetStyleSheet())
7775 {
7776 wxRichTextParagraphStyleDefinition* paraDef = buffer->GetStyleSheet()->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
7777 if (paraDef)
7778 {
7779 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
7780 if (para->GetRange().GetEnd() == pos && !paraDef->GetNextStyle().IsEmpty())
7781 {
7782 wxRichTextParagraphStyleDefinition* nextParaDef = buffer->GetStyleSheet()->FindParagraphStyle(paraDef->GetNextStyle());
7783 if (nextParaDef)
7784 {
7785 foundAttributes = true;
7786 attr = nextParaDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
7787 }
7788 }
7789
7790 // If we didn't find the 'next style', use this style instead.
7791 if (!foundAttributes)
7792 {
7793 foundAttributes = true;
7794 attr = paraDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
7795 }
7796 }
7797 }
7798
7799 // Also apply list style if present
7800 if (lookUpNewParaStyle && !para->GetAttributes().GetListStyleName().IsEmpty() && buffer->GetStyleSheet())
7801 {
7802 wxRichTextListStyleDefinition* listDef = buffer->GetStyleSheet()->FindListStyle(para->GetAttributes().GetListStyleName());
7803 if (listDef)
7804 {
7805 int thisIndent = para->GetAttributes().GetLeftIndent();
7806 int thisLevel = para->GetAttributes().HasOutlineLevel() ? para->GetAttributes().GetOutlineLevel() : listDef->FindLevelForIndent(thisIndent);
7807
7808 // Apply the overall list style, and item style for this level
7809 wxRichTextAttr listStyle(listDef->GetCombinedStyleForLevel(thisLevel, buffer->GetStyleSheet()));
7810 wxRichTextApplyStyle(attr, listStyle);
7811 attr.SetOutlineLevel(thisLevel);
7812 if (para->GetAttributes().HasBulletNumber())
7813 attr.SetBulletNumber(para->GetAttributes().GetBulletNumber());
7814 }
7815 }
7816
7817 if (!foundAttributes)
7818 {
7819 attr = para->GetAttributes();
7820 int flags = attr.GetFlags();
7821
7822 // Eliminate character styles
7823 flags &= ( (~ wxTEXT_ATTR_FONT) |
7824 (~ wxTEXT_ATTR_TEXT_COLOUR) |
7825 (~ wxTEXT_ATTR_BACKGROUND_COLOUR) );
7826 attr.SetFlags(flags);
7827 }
7828
7829 return attr;
7830 }
7831 else
7832 return wxRichTextAttr();
7833 }
7834
7835 /// Submit command to delete this range
7836 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl)
7837 {
7838 return ctrl->GetFocusObject()->DeleteRangeWithUndo(range, ctrl, this);
7839 }
7840
7841 /// Submit command to delete this range
7842 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer)
7843 {
7844 wxRichTextAction* action = new wxRichTextAction(NULL, _("Delete"), wxRICHTEXT_DELETE, buffer, this, ctrl);
7845
7846 action->SetPosition(ctrl->GetCaretPosition());
7847
7848 // Set the range to delete
7849 action->SetRange(range);
7850
7851 // Copy the fragment that we'll need to restore in Undo
7852 CopyFragment(range, action->GetOldParagraphs());
7853
7854 // See if we're deleting a paragraph marker, in which case we need to
7855 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
7856 if (range.GetStart() == range.GetEnd())
7857 {
7858 wxRichTextParagraph* para = GetParagraphAtPosition(range.GetStart());
7859 if (para && para->GetRange().GetEnd() == range.GetEnd())
7860 {
7861 wxRichTextParagraph* nextPara = GetParagraphAtPosition(range.GetStart()+1);
7862 if (nextPara && nextPara != para)
7863 {
7864 action->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara->GetAttributes());
7865 action->GetOldParagraphs().GetAttributes().SetFlags(action->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE);
7866 }
7867 }
7868 }
7869
7870 buffer->SubmitAction(action);
7871
7872 return true;
7873 }
7874
7875 /// Collapse undo/redo commands
7876 bool wxRichTextBuffer::BeginBatchUndo(const wxString& cmdName)
7877 {
7878 if (m_batchedCommandDepth == 0)
7879 {
7880 wxASSERT(m_batchedCommand == NULL);
7881 if (m_batchedCommand)
7882 {
7883 GetCommandProcessor()->Store(m_batchedCommand);
7884 }
7885 m_batchedCommand = new wxRichTextCommand(cmdName);
7886 }
7887
7888 m_batchedCommandDepth ++;
7889
7890 return true;
7891 }
7892
7893 /// Collapse undo/redo commands
7894 bool wxRichTextBuffer::EndBatchUndo()
7895 {
7896 m_batchedCommandDepth --;
7897
7898 wxASSERT(m_batchedCommandDepth >= 0);
7899 wxASSERT(m_batchedCommand != NULL);
7900
7901 if (m_batchedCommandDepth == 0)
7902 {
7903 GetCommandProcessor()->Store(m_batchedCommand);
7904 m_batchedCommand = NULL;
7905 }
7906
7907 return true;
7908 }
7909
7910 /// Submit immediately, or delay according to whether collapsing is on
7911 bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action)
7912 {
7913 if (action && !action->GetNewParagraphs().IsEmpty())
7914 PrepareContent(action->GetNewParagraphs());
7915
7916 if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
7917 {
7918 if (!action->GetIgnoreFirstTime())
7919 {
7920 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7921 cmd->AddAction(action);
7922 cmd->Do();
7923 cmd->GetActions().Clear();
7924 delete cmd;
7925 }
7926
7927 m_batchedCommand->AddAction(action);
7928 }
7929 else
7930 {
7931 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7932 cmd->AddAction(action);
7933
7934 // Only store it if we're not suppressing undo.
7935 if (!action->GetIgnoreFirstTime())
7936 {
7937 return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
7938 }
7939 else if (!SuppressingUndo())
7940 {
7941 GetCommandProcessor()->Store(cmd); // Just store it, without Do()ing anything
7942 }
7943 }
7944
7945 return true;
7946 }
7947
7948 /// Begin suppressing undo/redo commands.
7949 bool wxRichTextBuffer::BeginSuppressUndo()
7950 {
7951 m_suppressUndo ++;
7952
7953 return true;
7954 }
7955
7956 /// End suppressing undo/redo commands.
7957 bool wxRichTextBuffer::EndSuppressUndo()
7958 {
7959 m_suppressUndo --;
7960
7961 return true;
7962 }
7963
7964 /// Begin using a style
7965 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr& style)
7966 {
7967 wxRichTextAttr newStyle(GetDefaultStyle());
7968 newStyle.GetTextBoxAttr().Reset();
7969
7970 // Save the old default style
7971 m_attributeStack.Append((wxObject*) new wxRichTextAttr(newStyle));
7972
7973 wxRichTextApplyStyle(newStyle, style);
7974 newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags());
7975
7976 SetDefaultStyle(newStyle);
7977
7978 return true;
7979 }
7980
7981 /// End the style
7982 bool wxRichTextBuffer::EndStyle()
7983 {
7984 if (!m_attributeStack.GetFirst())
7985 {
7986 wxLogDebug(_("Too many EndStyle calls!"));
7987 return false;
7988 }
7989
7990 wxList::compatibility_iterator node = m_attributeStack.GetLast();
7991 wxRichTextAttr* attr = (wxRichTextAttr*)node->GetData();
7992 m_attributeStack.Erase(node);
7993
7994 SetDefaultStyle(*attr);
7995
7996 delete attr;
7997 return true;
7998 }
7999
8000 /// End all styles
8001 bool wxRichTextBuffer::EndAllStyles()
8002 {
8003 while (m_attributeStack.GetCount() != 0)
8004 EndStyle();
8005 return true;
8006 }
8007
8008 /// Clear the style stack
8009 void wxRichTextBuffer::ClearStyleStack()
8010 {
8011 for (wxList::compatibility_iterator node = m_attributeStack.GetFirst(); node; node = node->GetNext())
8012 delete (wxRichTextAttr*) node->GetData();
8013 m_attributeStack.Clear();
8014 }
8015
8016 /// Begin using bold
8017 bool wxRichTextBuffer::BeginBold()
8018 {
8019 wxRichTextAttr attr;
8020 attr.SetFontWeight(wxFONTWEIGHT_BOLD);
8021
8022 return BeginStyle(attr);
8023 }
8024
8025 /// Begin using italic
8026 bool wxRichTextBuffer::BeginItalic()
8027 {
8028 wxRichTextAttr attr;
8029 attr.SetFontStyle(wxFONTSTYLE_ITALIC);
8030
8031 return BeginStyle(attr);
8032 }
8033
8034 /// Begin using underline
8035 bool wxRichTextBuffer::BeginUnderline()
8036 {
8037 wxRichTextAttr attr;
8038 attr.SetFontUnderlined(true);
8039
8040 return BeginStyle(attr);
8041 }
8042
8043 /// Begin using point size
8044 bool wxRichTextBuffer::BeginFontSize(int pointSize)
8045 {
8046 wxRichTextAttr attr;
8047 attr.SetFontSize(pointSize);
8048
8049 return BeginStyle(attr);
8050 }
8051
8052 /// Begin using this font
8053 bool wxRichTextBuffer::BeginFont(const wxFont& font)
8054 {
8055 wxRichTextAttr attr;
8056 attr.SetFont(font);
8057
8058 return BeginStyle(attr);
8059 }
8060
8061 /// Begin using this colour
8062 bool wxRichTextBuffer::BeginTextColour(const wxColour& colour)
8063 {
8064 wxRichTextAttr attr;
8065 attr.SetFlags(wxTEXT_ATTR_TEXT_COLOUR);
8066 attr.SetTextColour(colour);
8067
8068 return BeginStyle(attr);
8069 }
8070
8071 /// Begin using alignment
8072 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment)
8073 {
8074 wxRichTextAttr attr;
8075 attr.SetFlags(wxTEXT_ATTR_ALIGNMENT);
8076 attr.SetAlignment(alignment);
8077
8078 return BeginStyle(attr);
8079 }
8080
8081 /// Begin left indent
8082 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent, int leftSubIndent)
8083 {
8084 wxRichTextAttr attr;
8085 attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
8086 attr.SetLeftIndent(leftIndent, leftSubIndent);
8087
8088 return BeginStyle(attr);
8089 }
8090
8091 /// Begin right indent
8092 bool wxRichTextBuffer::BeginRightIndent(int rightIndent)
8093 {
8094 wxRichTextAttr attr;
8095 attr.SetFlags(wxTEXT_ATTR_RIGHT_INDENT);
8096 attr.SetRightIndent(rightIndent);
8097
8098 return BeginStyle(attr);
8099 }
8100
8101 /// Begin paragraph spacing
8102 bool wxRichTextBuffer::BeginParagraphSpacing(int before, int after)
8103 {
8104 long flags = 0;
8105 if (before != 0)
8106 flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
8107 if (after != 0)
8108 flags |= wxTEXT_ATTR_PARA_SPACING_AFTER;
8109
8110 wxRichTextAttr attr;
8111 attr.SetFlags(flags);
8112 attr.SetParagraphSpacingBefore(before);
8113 attr.SetParagraphSpacingAfter(after);
8114
8115 return BeginStyle(attr);
8116 }
8117
8118 /// Begin line spacing
8119 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing)
8120 {
8121 wxRichTextAttr attr;
8122 attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
8123 attr.SetLineSpacing(lineSpacing);
8124
8125 return BeginStyle(attr);
8126 }
8127
8128 /// Begin numbered bullet
8129 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber, int leftIndent, int leftSubIndent, int bulletStyle)
8130 {
8131 wxRichTextAttr attr;
8132 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
8133 attr.SetBulletStyle(bulletStyle);
8134 attr.SetBulletNumber(bulletNumber);
8135 attr.SetLeftIndent(leftIndent, leftSubIndent);
8136
8137 return BeginStyle(attr);
8138 }
8139
8140 /// Begin symbol bullet
8141 bool wxRichTextBuffer::BeginSymbolBullet(const wxString& symbol, int leftIndent, int leftSubIndent, int bulletStyle)
8142 {
8143 wxRichTextAttr attr;
8144 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
8145 attr.SetBulletStyle(bulletStyle);
8146 attr.SetLeftIndent(leftIndent, leftSubIndent);
8147 attr.SetBulletText(symbol);
8148
8149 return BeginStyle(attr);
8150 }
8151
8152 /// Begin standard bullet
8153 bool wxRichTextBuffer::BeginStandardBullet(const wxString& bulletName, int leftIndent, int leftSubIndent, int bulletStyle)
8154 {
8155 wxRichTextAttr attr;
8156 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
8157 attr.SetBulletStyle(bulletStyle);
8158 attr.SetLeftIndent(leftIndent, leftSubIndent);
8159 attr.SetBulletName(bulletName);
8160
8161 return BeginStyle(attr);
8162 }
8163
8164 /// Begin named character style
8165 bool wxRichTextBuffer::BeginCharacterStyle(const wxString& characterStyle)
8166 {
8167 if (GetStyleSheet())
8168 {
8169 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
8170 if (def)
8171 {
8172 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
8173 return BeginStyle(attr);
8174 }
8175 }
8176 return false;
8177 }
8178
8179 /// Begin named paragraph style
8180 bool wxRichTextBuffer::BeginParagraphStyle(const wxString& paragraphStyle)
8181 {
8182 if (GetStyleSheet())
8183 {
8184 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(paragraphStyle);
8185 if (def)
8186 {
8187 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
8188 return BeginStyle(attr);
8189 }
8190 }
8191 return false;
8192 }
8193
8194 /// Begin named list style
8195 bool wxRichTextBuffer::BeginListStyle(const wxString& listStyle, int level, int number)
8196 {
8197 if (GetStyleSheet())
8198 {
8199 wxRichTextListStyleDefinition* def = GetStyleSheet()->FindListStyle(listStyle);
8200 if (def)
8201 {
8202 wxRichTextAttr attr(def->GetCombinedStyleForLevel(level));
8203
8204 attr.SetBulletNumber(number);
8205
8206 return BeginStyle(attr);
8207 }
8208 }
8209 return false;
8210 }
8211
8212 /// Begin URL
8213 bool wxRichTextBuffer::BeginURL(const wxString& url, const wxString& characterStyle)
8214 {
8215 wxRichTextAttr attr;
8216
8217 if (!characterStyle.IsEmpty() && GetStyleSheet())
8218 {
8219 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
8220 if (def)
8221 {
8222 attr = def->GetStyleMergedWithBase(GetStyleSheet());
8223 }
8224 }
8225 attr.SetURL(url);
8226
8227 return BeginStyle(attr);
8228 }
8229
8230 /// Adds a handler to the end
8231 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler *handler)
8232 {
8233 sm_handlers.Append(handler);
8234 }
8235
8236 /// Inserts a handler at the front
8237 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler *handler)
8238 {
8239 sm_handlers.Insert( handler );
8240 }
8241
8242 /// Removes a handler
8243 bool wxRichTextBuffer::RemoveHandler(const wxString& name)
8244 {
8245 wxRichTextFileHandler *handler = FindHandler(name);
8246 if (handler)
8247 {
8248 sm_handlers.DeleteObject(handler);
8249 delete handler;
8250 return true;
8251 }
8252 else
8253 return false;
8254 }
8255
8256 /// Finds a handler by filename or, if supplied, type
8257 wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename,
8258 wxRichTextFileType imageType)
8259 {
8260 if (imageType != wxRICHTEXT_TYPE_ANY)
8261 return FindHandler(imageType);
8262 else if (!filename.IsEmpty())
8263 {
8264 wxString path, file, ext;
8265 wxFileName::SplitPath(filename, & path, & file, & ext);
8266 return FindHandler(ext, imageType);
8267 }
8268 else
8269 return NULL;
8270 }
8271
8272
8273 /// Finds a handler by name
8274 wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& name)
8275 {
8276 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8277 while (node)
8278 {
8279 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
8280 if (handler->GetName().Lower() == name.Lower()) return handler;
8281
8282 node = node->GetNext();
8283 }
8284 return NULL;
8285 }
8286
8287 /// Finds a handler by extension and type
8288 wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, wxRichTextFileType type)
8289 {
8290 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8291 while (node)
8292 {
8293 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
8294 if ( handler->GetExtension().Lower() == extension.Lower() &&
8295 (type == wxRICHTEXT_TYPE_ANY || handler->GetType() == type) )
8296 return handler;
8297 node = node->GetNext();
8298 }
8299 return 0;
8300 }
8301
8302 /// Finds a handler by type
8303 wxRichTextFileHandler* wxRichTextBuffer::FindHandler(wxRichTextFileType type)
8304 {
8305 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8306 while (node)
8307 {
8308 wxRichTextFileHandler *handler = (wxRichTextFileHandler *)node->GetData();
8309 if (handler->GetType() == type) return handler;
8310 node = node->GetNext();
8311 }
8312 return NULL;
8313 }
8314
8315 void wxRichTextBuffer::InitStandardHandlers()
8316 {
8317 if (!FindHandler(wxRICHTEXT_TYPE_TEXT))
8318 AddHandler(new wxRichTextPlainTextHandler);
8319 }
8320
8321 void wxRichTextBuffer::CleanUpHandlers()
8322 {
8323 wxList::compatibility_iterator node = sm_handlers.GetFirst();
8324 while (node)
8325 {
8326 wxRichTextFileHandler* handler = (wxRichTextFileHandler*)node->GetData();
8327 wxList::compatibility_iterator next = node->GetNext();
8328 delete handler;
8329 node = next;
8330 }
8331
8332 sm_handlers.Clear();
8333 }
8334
8335 wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* types)
8336 {
8337 if (types)
8338 types->Clear();
8339
8340 wxString wildcard;
8341
8342 wxList::compatibility_iterator node = GetHandlers().GetFirst();
8343 int count = 0;
8344 while (node)
8345 {
8346 wxRichTextFileHandler* handler = (wxRichTextFileHandler*) node->GetData();
8347 if (handler->IsVisible() && ((save && handler->CanSave()) || (!save && handler->CanLoad())))
8348 {
8349 if (combine)
8350 {
8351 if (count > 0)
8352 wildcard += wxT(";");
8353 wildcard += wxT("*.") + handler->GetExtension();
8354 }
8355 else
8356 {
8357 if (count > 0)
8358 wildcard += wxT("|");
8359 wildcard += handler->GetName();
8360 wildcard += wxT(" ");
8361 wildcard += _("files");
8362 wildcard += wxT(" (*.");
8363 wildcard += handler->GetExtension();
8364 wildcard += wxT(")|*.");
8365 wildcard += handler->GetExtension();
8366 if (types)
8367 types->Add(handler->GetType());
8368 }
8369 count ++;
8370 }
8371
8372 node = node->GetNext();
8373 }
8374
8375 if (combine)
8376 wildcard = wxT("(") + wildcard + wxT(")|") + wildcard;
8377 return wildcard;
8378 }
8379
8380 #if wxUSE_FFILE && wxUSE_STREAMS
8381 /// Load a file
8382 bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type)
8383 {
8384 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
8385 if (handler)
8386 {
8387 SetDefaultStyle(wxRichTextAttr());
8388 handler->SetFlags(GetHandlerFlags());
8389 bool success = handler->LoadFile(this, filename);
8390 Invalidate(wxRICHTEXT_ALL);
8391 return success;
8392 }
8393 else
8394 return false;
8395 }
8396
8397 /// Save a file
8398 bool wxRichTextBuffer::SaveFile(const wxString& filename, wxRichTextFileType type)
8399 {
8400 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
8401 if (handler)
8402 {
8403 handler->SetFlags(GetHandlerFlags());
8404 return handler->SaveFile(this, filename);
8405 }
8406 else
8407 return false;
8408 }
8409 #endif // wxUSE_FFILE && wxUSE_STREAMS
8410
8411 #if wxUSE_STREAMS
8412 /// Load from a stream
8413 bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type)
8414 {
8415 wxRichTextFileHandler* handler = FindHandler(type);
8416 if (handler)
8417 {
8418 SetDefaultStyle(wxRichTextAttr());
8419 handler->SetFlags(GetHandlerFlags());
8420 bool success = handler->LoadFile(this, stream);
8421 Invalidate(wxRICHTEXT_ALL);
8422 return success;
8423 }
8424 else
8425 return false;
8426 }
8427
8428 /// Save to a stream
8429 bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, wxRichTextFileType type)
8430 {
8431 wxRichTextFileHandler* handler = FindHandler(type);
8432 if (handler)
8433 {
8434 handler->SetFlags(GetHandlerFlags());
8435 return handler->SaveFile(this, stream);
8436 }
8437 else
8438 return false;
8439 }
8440 #endif // wxUSE_STREAMS
8441
8442 /// Copy the range to the clipboard
8443 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
8444 {
8445 bool success = false;
8446 wxRichTextParagraphLayoutBox* container = this;
8447 if (GetRichTextCtrl())
8448 container = GetRichTextCtrl()->GetFocusObject();
8449
8450 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8451
8452 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
8453 {
8454 wxTheClipboard->Clear();
8455
8456 // Add composite object
8457
8458 wxDataObjectComposite* compositeObject = new wxDataObjectComposite();
8459
8460 {
8461 wxString text = container->GetTextForRange(range);
8462
8463 #ifdef __WXMSW__
8464 text = wxTextFile::Translate(text, wxTextFileType_Dos);
8465 #endif
8466
8467 compositeObject->Add(new wxTextDataObject(text), false /* not preferred */);
8468 }
8469
8470 // Add rich text buffer data object. This needs the XML handler to be present.
8471
8472 if (FindHandler(wxRICHTEXT_TYPE_XML))
8473 {
8474 wxRichTextBuffer* richTextBuf = new wxRichTextBuffer;
8475 container->CopyFragment(range, *richTextBuf);
8476
8477 compositeObject->Add(new wxRichTextBufferDataObject(richTextBuf), true /* preferred */);
8478 }
8479
8480 if (wxTheClipboard->SetData(compositeObject))
8481 success = true;
8482
8483 wxTheClipboard->Close();
8484 }
8485
8486 #else
8487 wxUnusedVar(range);
8488 #endif
8489 return success;
8490 }
8491
8492 /// Paste the clipboard content to the buffer
8493 bool wxRichTextBuffer::PasteFromClipboard(long position)
8494 {
8495 bool success = false;
8496 wxRichTextParagraphLayoutBox* container = this;
8497 if (GetRichTextCtrl())
8498 container = GetRichTextCtrl()->GetFocusObject();
8499
8500 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8501 if (CanPasteFromClipboard())
8502 {
8503 if (wxTheClipboard->Open())
8504 {
8505 if (wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
8506 {
8507 wxRichTextBufferDataObject data;
8508 wxTheClipboard->GetData(data);
8509 wxRichTextBuffer* richTextBuffer = data.GetRichTextBuffer();
8510 if (richTextBuffer)
8511 {
8512 container->InsertParagraphsWithUndo(this, position+1, *richTextBuffer, GetRichTextCtrl(), 0);
8513 if (GetRichTextCtrl())
8514 GetRichTextCtrl()->ShowPosition(position + richTextBuffer->GetOwnRange().GetEnd());
8515 delete richTextBuffer;
8516 }
8517 }
8518 else if (wxTheClipboard->IsSupported(wxDF_TEXT)
8519 #if wxUSE_UNICODE
8520 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8521 #endif
8522 )
8523 {
8524 wxTextDataObject data;
8525 wxTheClipboard->GetData(data);
8526 wxString text(data.GetText());
8527 #ifdef __WXMSW__
8528 wxString text2;
8529 text2.Alloc(text.Length()+1);
8530 size_t i;
8531 for (i = 0; i < text.Length(); i++)
8532 {
8533 wxChar ch = text[i];
8534 if (ch != wxT('\r'))
8535 text2 += ch;
8536 }
8537 #else
8538 wxString text2 = text;
8539 #endif
8540 container->InsertTextWithUndo(this, position+1, text2, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
8541
8542 if (GetRichTextCtrl())
8543 GetRichTextCtrl()->ShowPosition(position + text2.Length());
8544
8545 success = true;
8546 }
8547 else if (wxTheClipboard->IsSupported(wxDF_BITMAP))
8548 {
8549 wxBitmapDataObject data;
8550 wxTheClipboard->GetData(data);
8551 wxBitmap bitmap(data.GetBitmap());
8552 wxImage image(bitmap.ConvertToImage());
8553
8554 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, container, GetRichTextCtrl(), false);
8555
8556 action->GetNewParagraphs().AddImage(image);
8557
8558 if (action->GetNewParagraphs().GetChildCount() == 1)
8559 action->GetNewParagraphs().SetPartialParagraph(true);
8560
8561 action->SetPosition(position+1);
8562
8563 // Set the range we'll need to delete in Undo
8564 action->SetRange(wxRichTextRange(position+1, position+1));
8565
8566 SubmitAction(action);
8567
8568 success = true;
8569 }
8570 wxTheClipboard->Close();
8571 }
8572 }
8573 #else
8574 wxUnusedVar(position);
8575 #endif
8576 return success;
8577 }
8578
8579 /// Can we paste from the clipboard?
8580 bool wxRichTextBuffer::CanPasteFromClipboard() const
8581 {
8582 bool canPaste = false;
8583 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
8584 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
8585 {
8586 if (wxTheClipboard->IsSupported(wxDF_TEXT)
8587 #if wxUSE_UNICODE
8588 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
8589 #endif
8590 || wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
8591 wxTheClipboard->IsSupported(wxDF_BITMAP))
8592 {
8593 canPaste = true;
8594 }
8595 wxTheClipboard->Close();
8596 }
8597 #endif
8598 return canPaste;
8599 }
8600
8601 /// Dumps contents of buffer for debugging purposes
8602 void wxRichTextBuffer::Dump()
8603 {
8604 wxString text;
8605 {
8606 wxStringOutputStream stream(& text);
8607 wxTextOutputStream textStream(stream);
8608 Dump(textStream);
8609 }
8610
8611 wxLogDebug(text);
8612 }
8613
8614 /// Add an event handler
8615 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler* handler)
8616 {
8617 m_eventHandlers.Append(handler);
8618 return true;
8619 }
8620
8621 /// Remove an event handler
8622 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler* handler, bool deleteHandler)
8623 {
8624 wxList::compatibility_iterator node = m_eventHandlers.Find(handler);
8625 if (node)
8626 {
8627 m_eventHandlers.Erase(node);
8628 if (deleteHandler)
8629 delete handler;
8630
8631 return true;
8632 }
8633 else
8634 return false;
8635 }
8636
8637 /// Clear event handlers
8638 void wxRichTextBuffer::ClearEventHandlers()
8639 {
8640 m_eventHandlers.Clear();
8641 }
8642
8643 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
8644 /// otherwise will stop at the first successful one.
8645 bool wxRichTextBuffer::SendEvent(wxEvent& event, bool sendToAll)
8646 {
8647 bool success = false;
8648 for (wxList::compatibility_iterator node = m_eventHandlers.GetFirst(); node; node = node->GetNext())
8649 {
8650 wxEvtHandler* handler = (wxEvtHandler*) node->GetData();
8651 if (handler->ProcessEvent(event))
8652 {
8653 success = true;
8654 if (!sendToAll)
8655 return true;
8656 }
8657 }
8658 return success;
8659 }
8660
8661 /// Set style sheet and notify of the change
8662 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet* sheet)
8663 {
8664 wxRichTextStyleSheet* oldSheet = GetStyleSheet();
8665
8666 wxWindowID winid = wxID_ANY;
8667 if (GetRichTextCtrl())
8668 winid = GetRichTextCtrl()->GetId();
8669
8670 wxRichTextEvent event(wxEVT_RICHTEXT_STYLESHEET_REPLACING, winid);
8671 event.SetEventObject(GetRichTextCtrl());
8672 event.SetContainer(GetRichTextCtrl()->GetFocusObject());
8673 event.SetOldStyleSheet(oldSheet);
8674 event.SetNewStyleSheet(sheet);
8675 event.Allow();
8676
8677 if (SendEvent(event) && !event.IsAllowed())
8678 {
8679 if (sheet != oldSheet)
8680 delete sheet;
8681
8682 return false;
8683 }
8684
8685 if (oldSheet && oldSheet != sheet)
8686 delete oldSheet;
8687
8688 SetStyleSheet(sheet);
8689
8690 event.SetEventType(wxEVT_RICHTEXT_STYLESHEET_REPLACED);
8691 event.SetOldStyleSheet(NULL);
8692 event.Allow();
8693
8694 return SendEvent(event);
8695 }
8696
8697 /// Set renderer, deleting old one
8698 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer)
8699 {
8700 if (sm_renderer)
8701 delete sm_renderer;
8702 sm_renderer = renderer;
8703 }
8704
8705 /// Hit-testing: returns a flag indicating hit test details, plus
8706 /// information about position
8707 int wxRichTextBuffer::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
8708 {
8709 int ret = wxRichTextParagraphLayoutBox::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
8710 if (ret != wxRICHTEXT_HITTEST_NONE)
8711 {
8712 return ret;
8713 }
8714 else
8715 {
8716 textPosition = m_ownRange.GetEnd()-1;
8717 *obj = this;
8718 *contextObj = this;
8719 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
8720 }
8721 }
8722
8723 void wxRichTextBuffer::SetFontScale(double fontScale)
8724 {
8725 m_fontScale = fontScale;
8726 m_fontTable.SetFontScale(fontScale);
8727 }
8728
8729 void wxRichTextBuffer::SetDimensionScale(double dimScale)
8730 {
8731 m_dimensionScale = dimScale;
8732 }
8733
8734 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& bulletAttr, const wxRect& rect)
8735 {
8736 if (bulletAttr.GetTextColour().IsOk())
8737 {
8738 wxCheckSetPen(dc, wxPen(bulletAttr.GetTextColour()));
8739 wxCheckSetBrush(dc, wxBrush(bulletAttr.GetTextColour()));
8740 }
8741 else
8742 {
8743 wxCheckSetPen(dc, *wxBLACK_PEN);
8744 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
8745 }
8746
8747 wxFont font;
8748 if (bulletAttr.HasFont())
8749 {
8750 font = paragraph->GetBuffer()->GetFontTable().FindFont(bulletAttr);
8751 }
8752 else
8753 font = (*wxNORMAL_FONT);
8754
8755 wxCheckSetFont(dc, font);
8756
8757 int charHeight = dc.GetCharHeight();
8758
8759 int bulletWidth = (int) (((float) charHeight) * wxRichTextBuffer::GetBulletProportion());
8760 int bulletHeight = bulletWidth;
8761
8762 int x = rect.x;
8763
8764 // Calculate the top position of the character (as opposed to the whole line height)
8765 int y = rect.y + (rect.height - charHeight);
8766
8767 // Calculate where the bullet should be positioned
8768 y = y + (charHeight+1)/2 - (bulletHeight+1)/2;
8769
8770 // The margin between a bullet and text.
8771 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
8772
8773 if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8774 x = rect.x + rect.width - bulletWidth - margin;
8775 else if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8776 x = x + (rect.width)/2 - bulletWidth/2;
8777
8778 if (bulletAttr.GetBulletName() == wxT("standard/square"))
8779 {
8780 dc.DrawRectangle(x, y, bulletWidth, bulletHeight);
8781 }
8782 else if (bulletAttr.GetBulletName() == wxT("standard/diamond"))
8783 {
8784 wxPoint pts[5];
8785 pts[0].x = x; pts[0].y = y + bulletHeight/2;
8786 pts[1].x = x + bulletWidth/2; pts[1].y = y;
8787 pts[2].x = x + bulletWidth; pts[2].y = y + bulletHeight/2;
8788 pts[3].x = x + bulletWidth/2; pts[3].y = y + bulletHeight;
8789
8790 dc.DrawPolygon(4, pts);
8791 }
8792 else if (bulletAttr.GetBulletName() == wxT("standard/triangle"))
8793 {
8794 wxPoint pts[3];
8795 pts[0].x = x; pts[0].y = y;
8796 pts[1].x = x + bulletWidth; pts[1].y = y + bulletHeight/2;
8797 pts[2].x = x; pts[2].y = y + bulletHeight;
8798
8799 dc.DrawPolygon(3, pts);
8800 }
8801 else if (bulletAttr.GetBulletName() == wxT("standard/circle-outline"))
8802 {
8803 wxCheckSetBrush(dc, *wxTRANSPARENT_BRUSH);
8804 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
8805 }
8806 else // "standard/circle", and catch-all
8807 {
8808 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
8809 }
8810
8811 return true;
8812 }
8813
8814 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect, const wxString& text)
8815 {
8816 if (!text.empty())
8817 {
8818 wxFont font;
8819 if ((attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL) && !attr.GetBulletFont().IsEmpty() && attr.HasFont())
8820 {
8821 wxRichTextAttr fontAttr;
8822 if (attr.HasFontPixelSize())
8823 fontAttr.SetFontPixelSize(attr.GetFontSize());
8824 else
8825 fontAttr.SetFontPointSize(attr.GetFontSize());
8826 fontAttr.SetFontStyle(attr.GetFontStyle());
8827 fontAttr.SetFontWeight(attr.GetFontWeight());
8828 fontAttr.SetFontUnderlined(attr.GetFontUnderlined());
8829 fontAttr.SetFontFaceName(attr.GetBulletFont());
8830 font = paragraph->GetBuffer()->GetFontTable().FindFont(fontAttr);
8831 }
8832 else if (attr.HasFont())
8833 font = paragraph->GetBuffer()->GetFontTable().FindFont(attr);
8834 else
8835 font = (*wxNORMAL_FONT);
8836
8837 wxCheckSetFont(dc, font);
8838
8839 if (attr.GetTextColour().IsOk())
8840 dc.SetTextForeground(attr.GetTextColour());
8841
8842 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
8843
8844 int charHeight = dc.GetCharHeight();
8845 wxCoord tw, th;
8846 dc.GetTextExtent(text, & tw, & th);
8847
8848 int x = rect.x;
8849
8850 // Calculate the top position of the character (as opposed to the whole line height)
8851 int y = rect.y + (rect.height - charHeight);
8852
8853 // The margin between a bullet and text.
8854 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
8855
8856 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
8857 x = (rect.x + rect.width) - tw - margin;
8858 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
8859 x = x + (rect.width)/2 - tw/2;
8860
8861 dc.DrawText(text, x, y);
8862
8863 return true;
8864 }
8865 else
8866 return false;
8867 }
8868
8869 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& WXUNUSED(dc), const wxRichTextAttr& WXUNUSED(attr), const wxRect& WXUNUSED(rect))
8870 {
8871 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
8872 // with the buffer. The store will allow retrieval from memory, disk or other means.
8873 return false;
8874 }
8875
8876 /// Enumerate the standard bullet names currently supported
8877 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString& bulletNames)
8878 {
8879 bulletNames.Add(wxTRANSLATE("standard/circle"));
8880 bulletNames.Add(wxTRANSLATE("standard/circle-outline"));
8881 bulletNames.Add(wxTRANSLATE("standard/square"));
8882 bulletNames.Add(wxTRANSLATE("standard/diamond"));
8883 bulletNames.Add(wxTRANSLATE("standard/triangle"));
8884
8885 return true;
8886 }
8887
8888 /*!
8889 * wxRichTextBox
8890 */
8891
8892 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextParagraphLayoutBox)
8893
8894 wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
8895 wxRichTextParagraphLayoutBox(parent)
8896 {
8897 }
8898
8899 /// Draw the item
8900 bool wxRichTextBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8901 {
8902 if (!IsShown())
8903 return true;
8904
8905 // TODO: if the active object in the control, draw an indication.
8906 // We need to add the concept of active object, and not just focus object,
8907 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
8908 // Ultimately we would like to be able to interactively resize an active object
8909 // using drag handles.
8910 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
8911 }
8912
8913 /// Copy
8914 void wxRichTextBox::Copy(const wxRichTextBox& obj)
8915 {
8916 wxRichTextParagraphLayoutBox::Copy(obj);
8917 }
8918
8919 // Edit properties via a GUI
8920 bool wxRichTextBox::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8921 {
8922 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Box Properties"));
8923 boxDlg.SetAttributes(GetAttributes());
8924
8925 if (boxDlg.ShowModal() == wxID_OK)
8926 {
8927 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
8928 // indeterminate in the object.
8929 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8930 return true;
8931 }
8932 else
8933 return false;
8934 }
8935
8936 /*!
8937 * wxRichTextField
8938 */
8939
8940 IMPLEMENT_DYNAMIC_CLASS(wxRichTextField, wxRichTextParagraphLayoutBox)
8941
8942 wxRichTextField::wxRichTextField(const wxString& fieldType, wxRichTextObject* parent):
8943 wxRichTextParagraphLayoutBox(parent)
8944 {
8945 SetFieldType(fieldType);
8946 }
8947
8948 /// Draw the item
8949 bool wxRichTextField::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8950 {
8951 if (!IsShown())
8952 return true;
8953
8954 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8955 if (fieldType && fieldType->Draw(this, dc, context, range, selection, rect, descent, style))
8956 return true;
8957
8958 // Fallback; but don't draw guidelines.
8959 style &= ~wxRICHTEXT_DRAW_GUIDELINES;
8960 return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
8961 }
8962
8963 bool wxRichTextField::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
8964 {
8965 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8966 if (fieldType && fieldType->Layout(this, dc, context, rect, parentRect, style))
8967 return true;
8968
8969 // Fallback
8970 return wxRichTextParagraphLayoutBox::Layout(dc, context, rect, parentRect, style);
8971 }
8972
8973 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
8974 {
8975 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
8976 if (fieldType)
8977 return fieldType->GetRangeSize((wxRichTextField*) this, range, size, descent, dc, context, flags, position, parentSize, partialExtents);
8978
8979 return wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize, partialExtents);
8980 }
8981
8982 /// Calculate range
8983 void wxRichTextField::CalculateRange(long start, long& end)
8984 {
8985 if (IsTopLevel())
8986 wxRichTextParagraphLayoutBox::CalculateRange(start, end);
8987 else
8988 wxRichTextObject::CalculateRange(start, end);
8989 }
8990
8991 /// Copy
8992 void wxRichTextField::Copy(const wxRichTextField& obj)
8993 {
8994 wxRichTextParagraphLayoutBox::Copy(obj);
8995
8996 UpdateField(GetBuffer());
8997 }
8998
8999 // Edit properties via a GUI
9000 bool wxRichTextField::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
9001 {
9002 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
9003 if (fieldType)
9004 return fieldType->EditProperties(this, parent, buffer);
9005
9006 return false;
9007 }
9008
9009 bool wxRichTextField::CanEditProperties() const
9010 {
9011 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
9012 if (fieldType)
9013 return fieldType->CanEditProperties((wxRichTextField*) this);
9014
9015 return false;
9016 }
9017
9018 wxString wxRichTextField::GetPropertiesMenuLabel() const
9019 {
9020 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
9021 if (fieldType)
9022 return fieldType->GetPropertiesMenuLabel((wxRichTextField*) this);
9023
9024 return wxEmptyString;
9025 }
9026
9027 bool wxRichTextField::UpdateField(wxRichTextBuffer* buffer)
9028 {
9029 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
9030 if (fieldType)
9031 return fieldType->UpdateField(buffer, (wxRichTextField*) this);
9032
9033 return false;
9034 }
9035
9036 bool wxRichTextField::IsTopLevel() const
9037 {
9038 wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
9039 if (fieldType)
9040 return fieldType->IsTopLevel((wxRichTextField*) this);
9041
9042 return true;
9043 }
9044
9045 IMPLEMENT_CLASS(wxRichTextFieldType, wxObject)
9046
9047 IMPLEMENT_CLASS(wxRichTextFieldTypeStandard, wxRichTextFieldType)
9048
9049 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxString& label, int displayStyle)
9050 {
9051 Init();
9052
9053 SetName(name);
9054 SetLabel(label);
9055 SetDisplayStyle(displayStyle);
9056 }
9057
9058 wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxBitmap& bitmap, int displayStyle)
9059 {
9060 Init();
9061
9062 SetName(name);
9063 SetBitmap(bitmap);
9064 SetDisplayStyle(displayStyle);
9065 }
9066
9067 void wxRichTextFieldTypeStandard::Init()
9068 {
9069 m_displayStyle = wxRICHTEXT_FIELD_STYLE_RECTANGLE;
9070 m_font = wxFont(6, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
9071 m_textColour = *wxWHITE;
9072 m_borderColour = *wxBLACK;
9073 m_backgroundColour = *wxBLACK;
9074 m_verticalPadding = 1;
9075 m_horizontalPadding = 3;
9076 m_horizontalMargin = 2;
9077 m_verticalMargin = 0;
9078 }
9079
9080 void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard& field)
9081 {
9082 wxRichTextFieldType::Copy(field);
9083
9084 m_label = field.m_label;
9085 m_displayStyle = field.m_displayStyle;
9086 m_font = field.m_font;
9087 m_textColour = field.m_textColour;
9088 m_borderColour = field.m_borderColour;
9089 m_backgroundColour = field.m_backgroundColour;
9090 m_verticalPadding = field.m_verticalPadding;
9091 m_horizontalPadding = field.m_horizontalPadding;
9092 m_horizontalMargin = field.m_horizontalMargin;
9093 m_bitmap = field.m_bitmap;
9094 }
9095
9096 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))
9097 {
9098 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
9099 return false; // USe default composite drawing
9100 else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
9101 {
9102 int borderSize = 1;
9103
9104 wxPen borderPen(m_borderColour, 1, wxSOLID);
9105 wxBrush backgroundBrush(m_backgroundColour);
9106 wxColour textColour(m_textColour);
9107
9108 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
9109 {
9110 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
9111 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
9112
9113 borderPen = wxPen(highlightTextColour, 1, wxSOLID);
9114 backgroundBrush = wxBrush(highlightColour);
9115
9116 wxCheckSetBrush(dc, backgroundBrush);
9117 wxCheckSetPen(dc, wxPen(highlightColour, 1, wxSOLID));
9118 dc.DrawRectangle(rect);
9119 }
9120
9121 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
9122 borderSize = 0;
9123
9124 // objectRect is the area where the content is drawn, after margins around it have been taken into account
9125 wxRect objectRect = wxRect(wxPoint(rect.x + m_horizontalMargin, rect.y + wxMax(0, rect.height - descent - obj->GetCachedSize().y)),
9126 wxSize(obj->GetCachedSize().x - 2*m_horizontalMargin - borderSize, obj->GetCachedSize().y));
9127
9128 // clientArea is where the text is actually written
9129 wxRect clientArea = objectRect;
9130
9131 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE)
9132 {
9133 dc.SetPen(borderPen);
9134 dc.SetBrush(backgroundBrush);
9135 dc.DrawRoundedRectangle(objectRect, 4.0);
9136 }
9137 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG)
9138 {
9139 int arrowLength = objectRect.height/2;
9140 clientArea.width -= (arrowLength - m_horizontalPadding);
9141
9142 wxPoint pts[5];
9143 pts[0].x = objectRect.x; pts[0].y = objectRect.y;
9144 pts[1].x = objectRect.x + objectRect.width - arrowLength; pts[1].y = objectRect.y;
9145 pts[2].x = objectRect.x + objectRect.width; pts[2].y = objectRect.y + (objectRect.height/2);
9146 pts[3].x = objectRect.x + objectRect.width - arrowLength; pts[3].y = objectRect.y + objectRect.height;
9147 pts[4].x = objectRect.x; pts[4].y = objectRect.y + objectRect.height;
9148 dc.SetPen(borderPen);
9149 dc.SetBrush(backgroundBrush);
9150 dc.DrawPolygon(5, pts);
9151 }
9152 else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
9153 {
9154 int arrowLength = objectRect.height/2;
9155 clientArea.width -= (arrowLength - m_horizontalPadding);
9156 clientArea.x += (arrowLength - m_horizontalPadding);
9157
9158 wxPoint pts[5];
9159 pts[0].x = objectRect.x + objectRect.width; pts[0].y = objectRect.y;
9160 pts[1].x = objectRect.x + arrowLength; pts[1].y = objectRect.y;
9161 pts[2].x = objectRect.x; pts[2].y = objectRect.y + (objectRect.height/2);
9162 pts[3].x = objectRect.x + arrowLength; pts[3].y = objectRect.y + objectRect.height;
9163 pts[4].x = objectRect.x + objectRect.width; pts[4].y = objectRect.y + objectRect.height;
9164 dc.SetPen(borderPen);
9165 dc.SetBrush(backgroundBrush);
9166 dc.DrawPolygon(5, pts);
9167 }
9168
9169 if (m_bitmap.IsOk())
9170 {
9171 int x = clientArea.x + (clientArea.width - m_bitmap.GetWidth())/2;
9172 int y = clientArea.y + m_verticalPadding;
9173 dc.DrawBitmap(m_bitmap, x, y, true);
9174
9175 if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
9176 {
9177 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
9178 wxCheckSetPen(dc, *wxBLACK_PEN);
9179 dc.SetLogicalFunction(wxINVERT);
9180 dc.DrawRectangle(wxRect(x, y, m_bitmap.GetWidth(), m_bitmap.GetHeight()));
9181 dc.SetLogicalFunction(wxCOPY);
9182 }
9183 }
9184 else
9185 {
9186 wxString label(m_label);
9187 if (label.IsEmpty())
9188 label = wxT("??");
9189 int w, h, maxDescent;
9190 dc.SetFont(m_font);
9191 dc.GetTextExtent(m_label, & w, &h, & maxDescent);
9192 dc.SetTextForeground(textColour);
9193
9194 int x = clientArea.x + (clientArea.width - w)/2;
9195 int y = clientArea.y + (clientArea.height - (h - maxDescent))/2;
9196 dc.DrawText(m_label, x, y);
9197 }
9198 }
9199
9200 return true;
9201 }
9202
9203 bool wxRichTextFieldTypeStandard::Layout(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int style)
9204 {
9205 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
9206 return false; // USe default composite layout
9207
9208 wxSize size = GetSize(obj, dc, context, style);
9209 obj->SetCachedSize(size);
9210 obj->SetMinSize(size);
9211 obj->SetMaxSize(size);
9212 return true;
9213 }
9214
9215 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
9216 {
9217 if (IsTopLevel(obj))
9218 return obj->wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize);
9219 else
9220 {
9221 wxSize sz = GetSize(obj, dc, context, 0);
9222 if (partialExtents)
9223 {
9224 int lastSize;
9225 if (partialExtents->GetCount() > 0)
9226 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
9227 else
9228 lastSize = 0;
9229 partialExtents->Add(lastSize + sz.x);
9230 }
9231 size = sz;
9232 return true;
9233 }
9234 }
9235
9236 wxSize wxRichTextFieldTypeStandard::GetSize(wxRichTextField* WXUNUSED(obj), wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), int WXUNUSED(style)) const
9237 {
9238 int borderSize = 1;
9239 int w = 0, h = 0, maxDescent = 0;
9240
9241 wxSize sz;
9242 if (m_bitmap.IsOk())
9243 {
9244 w = m_bitmap.GetWidth();
9245 h = m_bitmap.GetHeight();
9246
9247 sz = wxSize(w + m_horizontalMargin*2, h + m_verticalMargin*2);
9248 }
9249 else
9250 {
9251 wxString label(m_label);
9252 if (label.IsEmpty())
9253 label = wxT("??");
9254 dc.SetFont(m_font);
9255 dc.GetTextExtent(label, & w, &h, & maxDescent);
9256
9257 sz = wxSize(w + m_horizontalPadding*2 + m_horizontalMargin*2, h + m_verticalPadding *2 + m_verticalMargin*2);
9258 }
9259
9260 if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
9261 {
9262 sz.x += borderSize*2;
9263 sz.y += borderSize*2;
9264 }
9265
9266 if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG || m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
9267 {
9268 // Add space for the arrow
9269 sz.x += (sz.y/2 - m_horizontalPadding);
9270 }
9271
9272 return sz;
9273 }
9274
9275 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell, wxRichTextBox)
9276
9277 wxRichTextCell::wxRichTextCell(wxRichTextObject* parent):
9278 wxRichTextBox(parent)
9279 {
9280 }
9281
9282 /// Draw the item
9283 bool wxRichTextCell::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
9284 {
9285 return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
9286 }
9287
9288 int wxRichTextCell::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
9289 {
9290 int ret = wxRichTextParagraphLayoutBox::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
9291 if (ret != wxRICHTEXT_HITTEST_NONE)
9292 {
9293 return ret;
9294 }
9295 else
9296 {
9297 textPosition = m_ownRange.GetEnd()-1;
9298 *obj = this;
9299 *contextObj = this;
9300 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
9301 }
9302 }
9303
9304 /// Copy
9305 void wxRichTextCell::Copy(const wxRichTextCell& obj)
9306 {
9307 wxRichTextBox::Copy(obj);
9308 }
9309
9310 // Edit properties via a GUI
9311 bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
9312 {
9313 // We need to gather common attributes for all selected cells.
9314
9315 wxRichTextTable* table = wxDynamicCast(GetParent(), wxRichTextTable);
9316 bool multipleCells = false;
9317 wxRichTextAttr attr;
9318
9319 if (table && buffer && buffer->GetRichTextCtrl() && buffer->GetRichTextCtrl()->GetSelection().IsValid() &&
9320 buffer->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
9321 {
9322 wxRichTextAttr clashingAttr, absentAttr;
9323 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
9324 size_t i;
9325 int selectedCellCount = 0;
9326 for (i = 0; i < sel.GetCount(); i++)
9327 {
9328 const wxRichTextRange& range = sel[i];
9329 wxRichTextCell* cell = table->GetCell(range.GetStart());
9330 if (cell)
9331 {
9332 wxRichTextAttr cellStyle = cell->GetAttributes();
9333
9334 CollectStyle(attr, cellStyle, clashingAttr, absentAttr);
9335
9336 selectedCellCount ++;
9337 }
9338 }
9339 multipleCells = selectedCellCount > 1;
9340 }
9341 else
9342 {
9343 attr = GetAttributes();
9344 }
9345
9346 wxString caption;
9347 if (multipleCells)
9348 caption = _("Multiple Cell Properties");
9349 else
9350 caption = _("Cell Properties");
9351
9352 // We don't want position and floating controls for a cell.
9353 wxRichTextSizePage::ShowPositionControls(false);
9354 wxRichTextSizePage::ShowFloatingControls(false);
9355 wxRichTextSizePage::ShowAlignmentControls(true);
9356
9357 wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption);
9358 cellDlg.SetAttributes(attr);
9359
9360 bool ok = (cellDlg.ShowModal() == wxID_OK);
9361
9362 wxRichTextSizePage::ShowPositionControls(true);
9363 wxRichTextSizePage::ShowFloatingControls(true);
9364
9365 if (ok)
9366 {
9367 if (multipleCells)
9368 {
9369 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
9370 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
9371 // since it may represent clashing attributes across multiple objects.
9372 table->SetCellStyle(sel, attr);
9373 }
9374 else
9375 // For a single object, indeterminate attributes set by the user should be reflected in the
9376 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
9377 // the style directly instead of applying (which ignores indeterminate attributes,
9378 // leaving them as they were).
9379 cellDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
9380 return true;
9381 }
9382 else
9383 return false;
9384 }
9385
9386 // The next 2 methods return span values. Note that the default is 1, not 0
9387 int wxRichTextCell::GetColspan() const
9388 {
9389 int span = 1;
9390 if (GetProperties().HasProperty(wxT("colspan")))
9391 {
9392 span = GetProperties().GetPropertyLong(wxT("colspan"));
9393 }
9394
9395 return span;
9396 }
9397
9398 int wxRichTextCell::GetRowspan() const
9399 {
9400 int span = 1;
9401 if (GetProperties().HasProperty(wxT("rowspan")))
9402 {
9403 span = GetProperties().GetPropertyLong(wxT("rowspan"));
9404 }
9405
9406 return span;
9407 }
9408
9409 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray)
9410
9411 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable, wxRichTextBox)
9412
9413 wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent)
9414 {
9415 m_rowCount = 0;
9416 m_colCount = 0;
9417 }
9418
9419 // Draws the object.
9420 bool wxRichTextTable::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
9421 {
9422 wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
9423
9424 int colCount = GetColumnCount();
9425 int rowCount = GetRowCount();
9426 int col, row;
9427 for (col = 0; col < colCount; col++)
9428 {
9429 for (row = 0; row < rowCount; row++)
9430 {
9431 if (row == 0 || row == (rowCount-1) || col == 0 || col == (colCount-1))
9432 {
9433 wxRichTextCell* cell = GetCell(row, col);
9434 if (cell && cell->IsShown() && !cell->GetRange().IsOutside(range))
9435 {
9436 wxRect childRect(cell->GetPosition(), cell->GetCachedSize());
9437 wxRichTextAttr attr(cell->GetAttributes());
9438 if (row != 0)
9439 attr.GetTextBoxAttr().GetBorder().GetTop().Reset();
9440 if (row != (rowCount-1))
9441 attr.GetTextBoxAttr().GetBorder().GetBottom().Reset();
9442 if (col != 0)
9443 attr.GetTextBoxAttr().GetBorder().GetLeft().Reset();
9444 if (col != (colCount-1))
9445 attr.GetTextBoxAttr().GetBorder().GetRight().Reset();
9446
9447 if (attr.GetTextBoxAttr().GetBorder().IsValid())
9448 {
9449 wxRect boxRect(cell->GetPosition(), cell->GetCachedSize());
9450 wxRect marginRect = boxRect;
9451 wxRect contentRect, borderRect, paddingRect, outlineRect;
9452
9453 cell->GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
9454 cell->DrawBorder(dc, GetBuffer(), attr.GetTextBoxAttr().GetBorder(), borderRect);
9455 }
9456 }
9457 }
9458 }
9459 }
9460
9461 return true;
9462 }
9463
9464 // Helper function for Layout() that clears the space needed by a cell with rowspan > 1
9465 int GetRowspanDisplacement(const wxRichTextTable* table, int row, int col, int paddingX, const wxArrayInt& colWidths)
9466 {
9467 // If one or more cells above-left of this one has rowspan > 1, the affected cells below it
9468 // will have been hidden and have width 0. As a result they are ignored by the layout algorithm,
9469 // and all cells to their right are effectively shifted left. As a result there's no hole for
9470 // the spanning cell to fill.
9471 // So search back along the current row for hidden cells. However there's also the annoying issue of a
9472 // rowspanning cell that also has colspam. So we can't rely on the rowspanning cell being directly above
9473 // the first hidden one we come to. We also can't rely on a cell being hidden only by one type of span;
9474 // there's nothing to stop a cell being hidden by colspan, and then again hidden from above by rowspan.
9475 // The answer is to look above each hidden cell in turn, which I think covers all bases.
9476 int deltaX = 0;
9477 for (int prevcol = 0; prevcol < col; ++prevcol)
9478 {
9479 if (!table->GetCell(row, prevcol)->IsShown())
9480 {
9481 // We've found a hidden cell. If it's hidden because of colspan to its left, it's
9482 // already been taken into account; but not if there's a rowspanning cell above
9483 for (int prevrow = row-1; prevrow >= 0; --prevrow)
9484 {
9485 wxRichTextCell* cell = table->GetCell(prevrow, prevcol);
9486 if (cell && cell->IsShown())
9487 {
9488 int rowSpan = cell->GetRowspan();
9489 if (rowSpan > 1 && rowSpan > (row-prevrow))
9490 {
9491 // There is a rowspanning cell above above the hidden one, so we need
9492 // to right-shift the index cell by this column's width. Furthermore,
9493 // if the cell also colspans, we need to shift by all affected columns
9494 for (int colSpan = 0; colSpan < cell->GetColspan(); ++colSpan)
9495 deltaX += (colWidths[prevcol+colSpan] + paddingX);
9496 break;
9497 }
9498 }
9499 }
9500 }
9501 }
9502 return deltaX;
9503 }
9504
9505 // Helper function for Layout() that expands any cell with rowspan > 1
9506 void ExpandCellsWithRowspan(const wxRichTextTable* table, int paddingY, int& bottomY, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& availableSpace, int style)
9507 {
9508 // This is called when the table's cell layout is otherwise complete.
9509 // For any cell with rowspan > 1, expand downwards into the row(s) below.
9510
9511 // Start by finding the current 'y' of the top of each row, plus the bottom of the available area for cells.
9512 // Deduce this from the top of a visible cell in the row below. (If none are visible, the row will be invisible anyway and can be ignored.)
9513 const int rowCount = table->GetRowCount();
9514 const int colCount = table->GetColumnCount();
9515 wxArrayInt rowTops;
9516 rowTops.Add(0, rowCount+1);
9517 int row;
9518 for (row = 0; row < rowCount; ++row)
9519 {
9520 for (int column = 0; column < colCount; ++column)
9521 {
9522 wxRichTextCell* cell = table->GetCell(row, column);
9523 if (cell && cell->IsShown())
9524 {
9525 rowTops[row] = cell->GetPosition().y;
9526 break;
9527 }
9528 }
9529 }
9530 rowTops[rowCount] = bottomY + paddingY; // The table bottom, which was passed to us
9531
9532 bool needsRelay = false;
9533
9534 for (row = 0; row < rowCount-1; ++row) // -1 as the bottom row can't rowspan
9535 {
9536 for (int col = 0; col < colCount; ++col)
9537 {
9538 wxRichTextCell* cell = table->GetCell(row, col);
9539 if (cell && cell->IsShown())
9540 {
9541 int span = cell->GetRowspan();
9542 if (span > 1)
9543 {
9544 span = wxMin(span, rowCount-row); // Don't try to span below the table!
9545 if (span < 2)
9546 continue;
9547
9548 int availableHeight = rowTops[row+span] - rowTops[row] - paddingY;
9549 wxSize newSize = wxSize(cell->GetCachedSize().GetWidth(), availableHeight);
9550 wxRect availableCellSpace = wxRect(cell->GetPosition(), newSize);
9551 cell->Invalidate(wxRICHTEXT_ALL);
9552 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
9553 // Ensure there's room in the span to display its contents, else it'll overwrite lower rows
9554 int overhang = cell->GetCachedSize().GetHeight() - availableHeight;
9555 cell->SetCachedSize(newSize);
9556
9557 if (overhang > 0)
9558 {
9559 // There are 3 things to get right:
9560 // 1) The easiest is the rows below the span: they need to be downshifted by the overhang, and so does the table bottom itself
9561 // 2) The rows within the span, including the one holding this cell, need to be deepened by their share of the overhang
9562 // e.g. if rowspan == 3, each row should increase in depth by 1/3rd of the overhang.
9563 // 3) The cell with the rowspan shouldn't be touched in 2); its height will be set to the whole span later.
9564 int deltaY = overhang / span;
9565 int spare = overhang % span;
9566
9567 // Each row in the span needs to by deepened by its share of the overhang (give the first row any spare).
9568 // This is achieved by increasing the value stored in the following row's rowTops
9569 for (int spannedRows = 0; spannedRows < span; ++spannedRows)
9570 {
9571 rowTops[row+spannedRows+1] += ((deltaY * (spannedRows+1)) + (spannedRows == 0 ? spare:0));
9572 }
9573
9574 // Any rows below the span need shifting down
9575 for (int rowsBelow = row + span+1; rowsBelow <= rowCount; ++rowsBelow)
9576 {
9577 rowTops[rowsBelow] += overhang;
9578 }
9579
9580 needsRelay = true;
9581 }
9582 }
9583 }
9584 }
9585 }
9586
9587 if (!needsRelay)
9588 return;
9589
9590 // There were overflowing rowspanning cells, so layout yet again to make the increased row depths show
9591 for (row = 0; row < rowCount; ++row)
9592 {
9593 for (int col = 0; col < colCount; ++col)
9594 {
9595 wxRichTextCell* cell = table->GetCell(row, col);
9596 if (cell && cell->IsShown())
9597 {
9598 wxPoint position(cell->GetPosition().x, rowTops[row]);
9599
9600 // GetRowspan() will usually return 1, but may be greater
9601 wxSize size(cell->GetCachedSize().GetWidth(), rowTops[row + cell->GetRowspan()] - rowTops[row] - paddingY);
9602
9603 wxRect availableCellSpace = wxRect(position, size);
9604 cell->Invalidate(wxRICHTEXT_ALL);
9605 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
9606 cell->SetCachedSize(size);
9607 }
9608 }
9609
9610 bottomY = rowTops[rowCount] - paddingY;
9611 }
9612 }
9613
9614 // Lays the object out. rect is the space available for layout. Often it will
9615 // be the specified overall space for this object, if trying to constrain
9616 // layout to a particular size, or it could be the total space available in the
9617 // parent. rect is the overall size, so we must subtract margins and padding.
9618 // to get the actual available space.
9619 bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int style)
9620 {
9621 SetPosition(rect.GetPosition());
9622
9623 // The meaty bit. Calculate sizes of all cells and rows. Try to use
9624 // minimum size if within alloted size, then divide up remaining size
9625 // between rows/cols.
9626
9627 double scale = 1.0;
9628 wxRichTextBuffer* buffer = GetBuffer();
9629 if (buffer) scale = buffer->GetScale();
9630
9631 wxRect availableSpace = GetAvailableContentArea(dc, context, rect);
9632 wxTextAttrDimensionConverter converter(dc, scale, availableSpace.GetSize());
9633
9634 wxRichTextAttr attr(GetAttributes());
9635 context.ApplyVirtualAttributes(attr, this);
9636
9637 bool tableHasPercentWidth = (attr.GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE);
9638 // If we have no fixed table size, and assuming we're not pushed for
9639 // space, then we don't have to try to stretch the table to fit the contents.
9640 bool stretchToFitTableWidth = tableHasPercentWidth;
9641
9642 int tableWidth = rect.width;
9643 if (attr.GetTextBoxAttr().GetWidth().IsValid() && !tableHasPercentWidth)
9644 {
9645 tableWidth = converter.GetPixels(attr.GetTextBoxAttr().GetWidth());
9646
9647 // Fixed table width, so we do want to stretch columns out if necessary.
9648 stretchToFitTableWidth = true;
9649
9650 // Shouldn't be able to exceed the size passed to this function
9651 tableWidth = wxMin(rect.width, tableWidth);
9652 }
9653
9654 // Get internal padding
9655 int paddingLeft = 0, paddingTop = 0;
9656 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
9657 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
9658 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
9659 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
9660
9661 // Assume that left and top padding are also used for inter-cell padding.
9662 int paddingX = paddingLeft;
9663 int paddingY = paddingTop;
9664
9665 int totalLeftMargin = 0, totalRightMargin = 0, totalTopMargin = 0, totalBottomMargin = 0;
9666 GetTotalMargin(dc, buffer, attr, totalLeftMargin, totalRightMargin, totalTopMargin, totalBottomMargin);
9667
9668 // Internal table width - the area for content
9669 int internalTableWidth = tableWidth - totalLeftMargin - totalRightMargin;
9670
9671 int rowCount = m_cells.GetCount();
9672 if (m_colCount == 0 || rowCount == 0)
9673 {
9674 wxRect overallRect(rect.x, rect.y, totalLeftMargin + totalRightMargin, totalTopMargin + totalBottomMargin);
9675 SetCachedSize(overallRect.GetSize());
9676
9677 // Zero content size
9678 SetMinSize(overallRect.GetSize());
9679 SetMaxSize(GetMinSize());
9680 return true;
9681 }
9682
9683 // The final calculated widths
9684 wxArrayInt colWidths;
9685 colWidths.Add(0, m_colCount);
9686
9687 wxArrayInt absoluteColWidths;
9688 absoluteColWidths.Add(0, m_colCount);
9689
9690 wxArrayInt percentageColWidths;
9691 percentageColWidths.Add(0, m_colCount);
9692 // wxArrayInt percentageColWidthsSpanning(m_colCount);
9693 // These are only relevant when the first column contains spanning information.
9694 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
9695 wxArrayInt maxColWidths;
9696 maxColWidths.Add(0, m_colCount);
9697 wxArrayInt minColWidths;
9698 minColWidths.Add(0, m_colCount);
9699
9700 wxSize tableSize(tableWidth, 0);
9701
9702 int i, j, k;
9703
9704 for (i = 0; i < m_colCount; i++)
9705 {
9706 absoluteColWidths[i] = 0;
9707 // absoluteColWidthsSpanning[i] = 0;
9708 percentageColWidths[i] = -1;
9709 // percentageColWidthsSpanning[i] = -1;
9710 colWidths[i] = 0;
9711 maxColWidths[i] = 0;
9712 minColWidths[i] = 0;
9713 // columnSpans[i] = 1;
9714 }
9715
9716 // (0) Determine which cells are visible according to spans
9717 // 1 2 3 4 5
9718 // __________________
9719 // | | | | | 1
9720 // |------| |----|
9721 // |------| | | 2
9722 // |------| | | 3
9723 // |------------------|
9724 // |__________________| 4
9725
9726 // To calculate cell visibility:
9727 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
9728 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
9729 // that cell, hide the cell.
9730
9731 // We can also use this array to match the size of spanning cells to the grid. Or just do
9732 // this when we iterate through all cells.
9733
9734 // 0.1: add spanning cells to an array
9735 wxRichTextRectArray rectArray;
9736 for (j = 0; j < m_rowCount; j++)
9737 {
9738 for (i = 0; i < m_colCount; i++)
9739 {
9740 wxRichTextCell* cell = GetCell(j, i);
9741 int colSpan = cell->GetColspan();
9742 int rowSpan = cell->GetRowspan();
9743 if (colSpan > 1 || rowSpan > 1)
9744 {
9745 rectArray.Add(wxRect(i, j, colSpan, rowSpan));
9746 }
9747 }
9748 }
9749 // 0.2: find which cells are subsumed by a spanning cell
9750 for (j = 0; j < m_rowCount; j++)
9751 {
9752 for (i = 0; i < m_colCount; i++)
9753 {
9754 wxRichTextCell* cell = GetCell(j, i);
9755 if (rectArray.GetCount() == 0)
9756 {
9757 cell->Show(true);
9758 }
9759 else
9760 {
9761 int colSpan = cell->GetColspan();
9762 int rowSpan = cell->GetRowspan();
9763
9764 if (colSpan > 1 || rowSpan > 1)
9765 {
9766 // Assume all spanning cells are shown
9767 cell->Show(true);
9768 }
9769 else
9770 {
9771 bool shown = true;
9772 for (k = 0; k < (int) rectArray.GetCount(); k++)
9773 {
9774 if (rectArray[k].Contains(wxPoint(i, j)))
9775 {
9776 shown = false;
9777 break;
9778 }
9779 }
9780 cell->Show(shown);
9781 }
9782 }
9783 }
9784 }
9785
9786 // Find the first spanned cell in each row that spans the most columns and doesn't
9787 // overlap with a spanned cell starting at a previous column position.
9788 // This means we need to keep an array of rects so we can check. However
9789 // it does also mean that some spans simply may not be taken into account
9790 // where there are different spans happening on different rows. In these cases,
9791 // they will simply be as wide as their constituent columns.
9792
9793 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
9794 // the absolute or percentage width of each column.
9795
9796 for (j = 0; j < m_rowCount; j++)
9797 {
9798 // First get the overall margins so we can calculate percentage widths based on
9799 // the available content space for all cells on the row
9800
9801 int overallRowContentMargin = 0;
9802 int visibleCellCount = 0;
9803
9804 for (i = 0; i < m_colCount; i++)
9805 {
9806 wxRichTextBox* cell = GetCell(j, i);
9807 if (cell->IsShown())
9808 {
9809 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9810 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9811
9812 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9813 visibleCellCount ++;
9814 }
9815 }
9816
9817 // Add in inter-cell padding
9818 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9819
9820 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9821 wxSize rowTableSize(rowContentWidth, 0);
9822 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9823
9824 for (i = 0; i < m_colCount; i++)
9825 {
9826 wxRichTextCell* cell = GetCell(j, i);
9827 if (cell->IsShown())
9828 {
9829 int colSpan = cell->GetColspan();
9830
9831 // Lay out cell to find min/max widths
9832 cell->Invalidate(wxRICHTEXT_ALL);
9833 cell->Layout(dc, context, availableSpace, availableSpace, style);
9834
9835 if (colSpan == 1)
9836 {
9837 int absoluteCellWidth = -1;
9838 int percentageCellWidth = -1;
9839
9840 // I think we need to calculate percentages from the internal table size,
9841 // minus the padding between cells which we'll need to calculate from the
9842 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
9843 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
9844 // so if we want to conform to that we'll need to add in the overall cell margins.
9845 // However, this will make it difficult to specify percentages that add up to
9846 // 100% and still fit within the table width.
9847 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
9848 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
9849 // If we're using internal content size for the width, we would calculate the
9850 // the overall cell width for n cells as:
9851 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
9852 // + thisOverallCellMargin
9853 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
9854 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
9855
9856 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9857 {
9858 int w = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9859 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
9860 {
9861 percentageCellWidth = w;
9862 }
9863 else
9864 {
9865 absoluteCellWidth = w;
9866 }
9867 // Override absolute width with minimum width if necessary
9868 if (cell->GetMinSize().x > 0 && absoluteCellWidth !=1 && cell->GetMinSize().x > absoluteCellWidth)
9869 absoluteCellWidth = cell->GetMinSize().x;
9870 }
9871
9872 if (absoluteCellWidth != -1)
9873 {
9874 if (absoluteCellWidth > absoluteColWidths[i])
9875 absoluteColWidths[i] = absoluteCellWidth;
9876 }
9877
9878 if (percentageCellWidth != -1)
9879 {
9880 if (percentageCellWidth > percentageColWidths[i])
9881 percentageColWidths[i] = percentageCellWidth;
9882 }
9883
9884 if (colSpan == 1 && cell->GetMinSize().x && cell->GetMinSize().x > minColWidths[i])
9885 minColWidths[i] = cell->GetMinSize().x;
9886 if (colSpan == 1 && cell->GetMaxSize().x && cell->GetMaxSize().x > maxColWidths[i])
9887 maxColWidths[i] = cell->GetMaxSize().x;
9888 }
9889 }
9890 }
9891 }
9892
9893 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
9894 // TODO: simply merge this into (1).
9895 for (i = 0; i < m_colCount; i++)
9896 {
9897 if (absoluteColWidths[i] > 0)
9898 {
9899 colWidths[i] = absoluteColWidths[i];
9900 }
9901 else if (percentageColWidths[i] > 0)
9902 {
9903 colWidths[i] = percentageColWidths[i];
9904
9905 // This is rubbish - we calculated the absolute widths from percentages, so
9906 // we can't do it again here.
9907 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
9908 }
9909 }
9910
9911 // (3) Process absolute or proportional widths of spanning columns,
9912 // now that we know what our fixed column widths are going to be.
9913 // Spanned cells will try to adjust columns so the span will fit.
9914 // Even existing fixed column widths can be expanded if necessary.
9915 // Actually, currently fixed columns widths aren't adjusted; instead,
9916 // the algorithm favours earlier rows and adjusts unspecified column widths
9917 // the first time only. After that, we can't know whether the column has been
9918 // specified explicitly or not. (We could make a note if necessary.)
9919 for (j = 0; j < m_rowCount; j++)
9920 {
9921 // First get the overall margins so we can calculate percentage widths based on
9922 // the available content space for all cells on the row
9923
9924 int overallRowContentMargin = 0;
9925 int visibleCellCount = 0;
9926
9927 for (i = 0; i < m_colCount; i++)
9928 {
9929 wxRichTextBox* cell = GetCell(j, i);
9930 if (cell->IsShown())
9931 {
9932 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
9933 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
9934
9935 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
9936 visibleCellCount ++;
9937 }
9938 }
9939
9940 // Add in inter-cell padding
9941 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
9942
9943 int rowContentWidth = internalTableWidth - overallRowContentMargin;
9944 wxSize rowTableSize(rowContentWidth, 0);
9945 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
9946
9947 for (i = 0; i < m_colCount; i++)
9948 {
9949 wxRichTextCell* cell = GetCell(j, i);
9950 if (cell->IsShown())
9951 {
9952 int colSpan = cell->GetColspan();
9953 if (colSpan > 1)
9954 {
9955 int spans = wxMin(colSpan, m_colCount - i);
9956 int cellWidth = 0;
9957 if (spans > 0)
9958 {
9959 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
9960 {
9961 cellWidth = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
9962 // Override absolute width with minimum width if necessary
9963 if (cell->GetMinSize().x > 0 && cellWidth !=1 && cell->GetMinSize().x > cellWidth)
9964 cellWidth = cell->GetMinSize().x;
9965 }
9966 else
9967 {
9968 // Do we want to do this? It's the only chance we get to
9969 // use the cell's min/max sizes, so we need to work out
9970 // how we're going to balance the unspecified spanning cell
9971 // width with the possibility more-constrained constituent cell widths.
9972 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
9973 // don't want to constraint all the spanned columns to fit into this cell.
9974 // OK, let's say that if any of the constituent columns don't fit,
9975 // then we simply stop constraining the columns; instead, we'll just fit the spanning
9976 // cells to the columns later.
9977 cellWidth = cell->GetMinSize().x;
9978 if (cell->GetMaxSize().x > cellWidth)
9979 cellWidth = cell->GetMaxSize().x;
9980 }
9981
9982 // Subtract the padding between cells
9983 int spanningWidth = cellWidth;
9984 spanningWidth -= paddingX * (spans-1);
9985
9986 if (spanningWidth > 0)
9987 {
9988 // Now share the spanning width between columns within that span
9989 // TODO: take into account min widths of columns within the span
9990 int spanningWidthLeft = spanningWidth;
9991 int stretchColCount = 0;
9992 for (k = i; k < (i+spans); k++)
9993 {
9994 if (colWidths[k] > 0) // absolute or proportional width has been specified
9995 spanningWidthLeft -= colWidths[k];
9996 else
9997 stretchColCount ++;
9998 }
9999 // Now divide what's left between the remaining columns
10000 int colShare = 0;
10001 if (stretchColCount > 0)
10002 colShare = spanningWidthLeft / stretchColCount;
10003 int colShareRemainder = spanningWidthLeft - (colShare * stretchColCount);
10004
10005 // If fixed-width columns are currently too big, then we'll later
10006 // stretch the spanned cell to fit.
10007
10008 if (spanningWidthLeft > 0)
10009 {
10010 for (k = i; k < (i+spans); k++)
10011 {
10012 if (colWidths[k] <= 0) // absolute or proportional width has not been specified
10013 {
10014 int newWidth = colShare;
10015 if (k == (i+spans-1))
10016 newWidth += colShareRemainder; // ensure all pixels are filled
10017 colWidths[k] = newWidth;
10018 }
10019 }
10020 }
10021 }
10022 }
10023 }
10024 }
10025 }
10026 }
10027
10028 // (4) Next, share any remaining space out between columns that have not yet been calculated.
10029 // TODO: take into account min widths of columns within the span
10030 int tableWidthMinusPadding = internalTableWidth - (m_colCount-1)*paddingX;
10031 int widthLeft = tableWidthMinusPadding;
10032 int stretchColCount = 0;
10033 for (i = 0; i < m_colCount; i++)
10034 {
10035 // Subtract min width from width left, then
10036 // add the colShare to the min width
10037 if (colWidths[i] > 0) // absolute or proportional width has been specified
10038 widthLeft -= colWidths[i];
10039 else
10040 {
10041 if (minColWidths[i] > 0)
10042 widthLeft -= minColWidths[i];
10043
10044 stretchColCount ++;
10045 }
10046 }
10047
10048 // Now divide what's left between the remaining columns
10049 int colShare = 0;
10050 if (stretchColCount > 0)
10051 colShare = widthLeft / stretchColCount;
10052 int colShareRemainder = widthLeft - (colShare * stretchColCount);
10053
10054 // Check we don't have enough space, in which case shrink all columns, overriding
10055 // any absolute/proportional widths
10056 // TODO: actually we would like to divide up the shrinkage according to size.
10057 // How do we calculate the proportions that will achieve this?
10058 // Could first choose an arbitrary value for stretching cells, and then calculate
10059 // factors to multiply each width by.
10060 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
10061 if (widthLeft < 0 || (stretchToFitTableWidth && (stretchColCount == 0)))
10062 {
10063 colShare = tableWidthMinusPadding / m_colCount;
10064 colShareRemainder = tableWidthMinusPadding - (colShare * m_colCount);
10065 for (i = 0; i < m_colCount; i++)
10066 {
10067 colWidths[i] = 0;
10068 minColWidths[i] = 0;
10069 }
10070 }
10071
10072 // We have to adjust the columns if either we need to shrink the
10073 // table to fit the parent/table width, or we explicitly set the
10074 // table width and need to stretch out the table.
10075 if (widthLeft < 0 || stretchToFitTableWidth)
10076 {
10077 for (i = 0; i < m_colCount; i++)
10078 {
10079 if (colWidths[i] <= 0) // absolute or proportional width has not been specified
10080 {
10081 if (minColWidths[i] > 0)
10082 colWidths[i] = minColWidths[i] + colShare;
10083 else
10084 colWidths[i] = colShare;
10085 if (i == (m_colCount-1))
10086 colWidths[i] += colShareRemainder; // ensure all pixels are filled
10087 }
10088 }
10089 }
10090
10091 // TODO: if spanned cells have no specified or max width, make them the
10092 // as big as the columns they span. Do this for all spanned cells in all
10093 // rows, of course. Size any spanned cells left over at the end - even if they
10094 // have width > 0, make sure they're limited to the appropriate column edge.
10095
10096
10097 /*
10098 Sort out confusion between content width
10099 and overall width later. For now, assume we specify overall width.
10100
10101 So, now we've laid out the table to fit into the given space
10102 and have used specified widths and minimum widths.
10103
10104 Now we need to consider how we will try to take maximum width into account.
10105
10106 */
10107
10108 // (??) TODO: take max width into account
10109
10110 // (6) Lay out all cells again with the current values
10111
10112 int maxRight = 0;
10113 int y = availableSpace.y;
10114 for (j = 0; j < m_rowCount; j++)
10115 {
10116 int x = availableSpace.x; // TODO: take into account centering etc.
10117 int maxCellHeight = 0;
10118 int maxSpecifiedCellHeight = 0;
10119
10120 wxArrayInt actualWidths;
10121 actualWidths.Add(0, m_colCount);
10122
10123 wxTextAttrDimensionConverter converter(dc, scale);
10124 for (i = 0; i < m_colCount; i++)
10125 {
10126 wxRichTextCell* cell = GetCell(j, i);
10127 if (cell->IsShown())
10128 {
10129 // Get max specified cell height
10130 // Don't handle percentages for height
10131 if (cell->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE)
10132 {
10133 int h = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetHeight());
10134 if (h > maxSpecifiedCellHeight)
10135 maxSpecifiedCellHeight = h;
10136 }
10137
10138 if (colWidths[i] > 0) // absolute or proportional width has been specified
10139 {
10140 int colSpan = cell->GetColspan();
10141 wxRect availableCellSpace;
10142
10143 // Take into account spans
10144 if (colSpan > 1)
10145 {
10146 // Calculate the size of this spanning cell from its constituent columns
10147 int xx = 0;
10148 int spans = wxMin(colSpan, m_colCount - i);
10149 for (k = i; k < (i+spans); k++)
10150 {
10151 if (k != i)
10152 xx += paddingX;
10153 xx += colWidths[k];
10154 }
10155 availableCellSpace = wxRect(x, y, xx, -1);
10156 }
10157 else
10158 availableCellSpace = wxRect(x, y, colWidths[i], -1);
10159
10160 // Store actual width so we can force cell to be the appropriate width on the final loop
10161 actualWidths[i] = availableCellSpace.GetWidth();
10162
10163 // We now need to shift right by the width of any rowspanning cells above-left of us
10164 int deltaX = GetRowspanDisplacement(this, j, i, paddingX, colWidths);
10165 availableCellSpace.SetX(availableCellSpace.GetX() + deltaX);
10166
10167 // Lay out cell
10168 cell->Invalidate(wxRICHTEXT_ALL);
10169 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
10170
10171 // TODO: use GetCachedSize().x to compute 'natural' size
10172
10173 x += (availableCellSpace.GetWidth() + paddingX);
10174 if ((cell->GetCachedSize().y > maxCellHeight) && (cell->GetRowspan() < 2))
10175 maxCellHeight = cell->GetCachedSize().y;
10176 }
10177 }
10178 }
10179
10180 maxCellHeight = wxMax(maxCellHeight, maxSpecifiedCellHeight);
10181
10182 for (i = 0; i < m_colCount; i++)
10183 {
10184 wxRichTextCell* cell = GetCell(j, i);
10185 if (cell->IsShown())
10186 {
10187 wxRect availableCellSpace = wxRect(cell->GetPosition(), wxSize(actualWidths[i], maxCellHeight));
10188 // Lay out cell with new height
10189 cell->Invalidate(wxRICHTEXT_ALL);
10190 cell->Layout(dc, context, availableCellSpace, availableSpace, style);
10191
10192 // Make sure the cell size really is the appropriate size,
10193 // not the calculated box size
10194 cell->SetCachedSize(wxSize(actualWidths[i], maxCellHeight));
10195
10196 maxRight = wxMax(maxRight, cell->GetPosition().x + cell->GetCachedSize().x);
10197 }
10198 }
10199
10200 y += maxCellHeight;
10201 if (j < (m_rowCount-1))
10202 y += paddingY;
10203 }
10204
10205 // Finally we need to expand any cell with rowspan > 1. We couldn't earlier; lower rows' heights weren't known
10206 ExpandCellsWithRowspan(this, paddingY, y, dc, context, availableSpace, style);
10207
10208 // We need to add back the margins etc.
10209 {
10210 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
10211 contentRect = wxRect(wxPoint(0, 0), wxSize(maxRight - availableSpace.x, y - availableSpace.y));
10212 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
10213 SetCachedSize(marginRect.GetSize());
10214 }
10215
10216 // TODO: calculate max size
10217 {
10218 SetMaxSize(GetCachedSize());
10219 }
10220
10221 // TODO: calculate min size
10222 {
10223 SetMinSize(GetCachedSize());
10224 }
10225
10226 // TODO: currently we use either a fixed table width or the parent's size.
10227 // We also want to be able to calculate the table width from its content,
10228 // whether using fixed column widths or cell content min/max width.
10229 // Probably need a boolean flag to say whether we need to stretch cells
10230 // to fit the table width, or to simply use min/max cell widths. The
10231 // trouble with this is that if cell widths are not specified, they
10232 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
10233 // Anyway, ignoring that problem, we probably need to factor layout into a function
10234 // that can can calculate the maximum unconstrained layout in case table size is
10235 // not specified. Then LayoutToBestSize() can choose to use either parent size to
10236 // constrain Layout(), or the previously-calculated max size to constraint layout.
10237
10238 return true;
10239 }
10240
10241 // Finds the absolute position and row height for the given character position
10242 bool wxRichTextTable::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
10243 {
10244 wxRichTextCell* child = GetCell(index+1);
10245 if (child)
10246 {
10247 // Find the position at the start of the child cell, since the table doesn't
10248 // have any caret position of its own.
10249 return child->FindPosition(dc, context, -1, pt, height, forceLineStart);
10250 }
10251 else
10252 return false;
10253 }
10254
10255 // Get the cell at the given character position (in the range of the table).
10256 wxRichTextCell* wxRichTextTable::GetCell(long pos) const
10257 {
10258 int row = 0, col = 0;
10259 if (GetCellRowColumnPosition(pos, row, col))
10260 {
10261 return GetCell(row, col);
10262 }
10263 else
10264 return NULL;
10265 }
10266
10267 // Get the row/column for a given character position
10268 bool wxRichTextTable::GetCellRowColumnPosition(long pos, int& row, int& col) const
10269 {
10270 if (m_colCount == 0 || m_rowCount == 0)
10271 return false;
10272
10273 row = (int) (pos / m_colCount);
10274 col = pos - (row * m_colCount);
10275
10276 wxASSERT(row < m_rowCount && col < m_colCount);
10277
10278 if (row < m_rowCount && col < m_colCount)
10279 return true;
10280 else
10281 return false;
10282 }
10283
10284 // Calculate range, taking row/cell ordering into account instead of relying
10285 // on list ordering.
10286 void wxRichTextTable::CalculateRange(long start, long& end)
10287 {
10288 long current = start;
10289 long lastEnd = current;
10290
10291 if (IsTopLevel())
10292 {
10293 current = 0;
10294 lastEnd = 0;
10295 }
10296
10297 int i, j;
10298 for (i = 0; i < m_rowCount; i++)
10299 {
10300 for (j = 0; j < m_colCount; j++)
10301 {
10302 wxRichTextCell* child = GetCell(i, j);
10303 if (child)
10304 {
10305 long childEnd = 0;
10306
10307 child->CalculateRange(current, childEnd);
10308
10309 lastEnd = childEnd;
10310 current = childEnd + 1;
10311 }
10312 }
10313 }
10314
10315 // A top-level object always has a range of size 1,
10316 // because its children don't count at this level.
10317 end = start;
10318 m_range.SetRange(start, start);
10319
10320 // An object with no children has zero length
10321 if (m_children.GetCount() == 0)
10322 lastEnd --;
10323 m_ownRange.SetRange(0, lastEnd);
10324 }
10325
10326 // Gets the range size.
10327 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
10328 {
10329 return wxRichTextBox::GetRangeSize(range, size, descent, dc, context, flags, position, parentSize, partialExtents);
10330 }
10331
10332 // Deletes content in the given range.
10333 bool wxRichTextTable::DeleteRange(const wxRichTextRange& WXUNUSED(range))
10334 {
10335 // TODO: implement deletion of cells
10336 return true;
10337 }
10338
10339 // Gets any text in this object for the given range.
10340 wxString wxRichTextTable::GetTextForRange(const wxRichTextRange& range) const
10341 {
10342 return wxRichTextBox::GetTextForRange(range);
10343 }
10344
10345 // Copies this object.
10346 void wxRichTextTable::Copy(const wxRichTextTable& obj)
10347 {
10348 wxRichTextBox::Copy(obj);
10349
10350 ClearTable();
10351
10352 m_rowCount = obj.m_rowCount;
10353 m_colCount = obj.m_colCount;
10354
10355 m_cells.Add(wxRichTextObjectPtrArray(), m_rowCount);
10356
10357 int i, j;
10358 for (i = 0; i < m_rowCount; i++)
10359 {
10360 wxRichTextObjectPtrArray& colArray = m_cells[i];
10361 for (j = 0; j < m_colCount; j++)
10362 {
10363 wxRichTextCell* cell = wxDynamicCast(obj.GetCell(i, j)->Clone(), wxRichTextCell);
10364 AppendChild(cell);
10365
10366 colArray.Add(cell);
10367 }
10368 }
10369 }
10370
10371 void wxRichTextTable::ClearTable()
10372 {
10373 m_cells.Clear();
10374 DeleteChildren();
10375 m_rowCount = 0;
10376 m_colCount = 0;
10377 }
10378
10379 bool wxRichTextTable::CreateTable(int rows, int cols)
10380 {
10381 ClearTable();
10382
10383 wxRichTextAttr cellattr;
10384 cellattr.SetTextColour(GetBasicStyle().GetTextColour());
10385
10386 m_rowCount = rows;
10387 m_colCount = cols;
10388
10389 m_cells.Add(wxRichTextObjectPtrArray(), rows);
10390
10391 int i, j;
10392 for (i = 0; i < rows; i++)
10393 {
10394 wxRichTextObjectPtrArray& colArray = m_cells[i];
10395 for (j = 0; j < cols; j++)
10396 {
10397 wxRichTextCell* cell = new wxRichTextCell;
10398 cell->GetAttributes() = cellattr;
10399
10400 AppendChild(cell);
10401 cell->AddParagraph(wxEmptyString);
10402
10403 colArray.Add(cell);
10404 }
10405 }
10406
10407 return true;
10408 }
10409
10410 wxRichTextCell* wxRichTextTable::GetCell(int row, int col) const
10411 {
10412 wxASSERT(row < m_rowCount);
10413 wxASSERT(col < m_colCount);
10414
10415 if (row < m_rowCount && col < m_colCount)
10416 {
10417 wxRichTextObjectPtrArray& colArray = m_cells[row];
10418 wxRichTextObject* obj = colArray[col];
10419 return wxDynamicCast(obj, wxRichTextCell);
10420 }
10421 else
10422 return NULL;
10423 }
10424
10425 // Returns a selection object specifying the selections between start and end character positions.
10426 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
10427 wxRichTextSelection wxRichTextTable::GetSelection(long start, long end) const
10428 {
10429 wxRichTextSelection selection;
10430 selection.SetContainer((wxRichTextTable*) this);
10431
10432 if (start > end)
10433 {
10434 long tmp = end;
10435 end = start;
10436 start = tmp;
10437 }
10438
10439 wxASSERT( start >= 0 && end < (m_colCount * m_rowCount));
10440
10441 if (end >= (m_colCount * m_rowCount))
10442 return selection;
10443
10444 // We need to find the rectangle of cells that is described by the rectangle
10445 // with start, end as the diagonal. Make sure we don't add cells that are
10446 // not currenty visible because they are overlapped by spanning cells.
10447 /*
10448 --------------------------
10449 | 0 | 1 | 2 | 3 | 4 |
10450 --------------------------
10451 | 5 | 6 | 7 | 8 | 9 |
10452 --------------------------
10453 | 10 | 11 | 12 | 13 | 14 |
10454 --------------------------
10455 | 15 | 16 | 17 | 18 | 19 |
10456 --------------------------
10457
10458 Let's say we select 6 -> 18.
10459
10460 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
10461 which is left and which is right.
10462
10463 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
10464
10465 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
10466 and (b) shown.
10467
10468
10469 */
10470
10471 int leftCol = start - m_colCount * int(start/m_colCount);
10472 int rightCol = end - m_colCount * int(end/m_colCount);
10473
10474 int topRow = int(start/m_colCount);
10475 int bottomRow = int(end/m_colCount);
10476
10477 if (leftCol > rightCol)
10478 {
10479 int tmp = rightCol;
10480 rightCol = leftCol;
10481 leftCol = tmp;
10482 }
10483
10484 if (topRow > bottomRow)
10485 {
10486 int tmp = bottomRow;
10487 bottomRow = topRow;
10488 topRow = tmp;
10489 }
10490
10491 int i, j;
10492 for (i = topRow; i <= bottomRow; i++)
10493 {
10494 for (j = leftCol; j <= rightCol; j++)
10495 {
10496 wxRichTextCell* cell = GetCell(i, j);
10497 if (cell && cell->IsShown())
10498 selection.Add(cell->GetRange());
10499 }
10500 }
10501
10502 return selection;
10503 }
10504
10505 // Sets the attributes for the cells specified by the selection.
10506 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection& selection, const wxRichTextAttr& style, int flags)
10507 {
10508 if (selection.GetContainer() != this)
10509 return false;
10510
10511 wxRichTextBuffer* buffer = GetBuffer();
10512 bool haveControl = (buffer && buffer->GetRichTextCtrl() != NULL);
10513 bool withUndo = haveControl && ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
10514
10515 if (withUndo)
10516 buffer->BeginBatchUndo(_("Set Cell Style"));
10517
10518 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
10519 while (node)
10520 {
10521 wxRichTextCell* cell = wxDynamicCast(node->GetData(), wxRichTextCell);
10522 if (cell && selection.WithinSelection(cell->GetRange().GetStart()))
10523 SetStyle(cell, style, flags);
10524 node = node->GetNext();
10525 }
10526
10527 // Do action, or delay it until end of batch.
10528 if (withUndo)
10529 buffer->EndBatchUndo();
10530
10531 return true;
10532 }
10533
10534 wxPosition wxRichTextTable::GetFocusedCell() const
10535 {
10536 wxPosition position(-1, -1);
10537 const wxRichTextObject* focus = GetBuffer()->GetRichTextCtrl()->GetFocusObject();
10538
10539 for (int row = 0; row < GetRowCount(); ++row)
10540 {
10541 for (int col = 0; col < GetColumnCount(); ++col)
10542 {
10543 if (GetCell(row, col) == focus)
10544 {
10545 position.SetRow(row);
10546 position.SetCol(col);
10547 return position;
10548 }
10549 }
10550 }
10551
10552 return position;
10553 }
10554
10555 int wxRichTextTable::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
10556 {
10557 for (int row = 0; row < GetRowCount(); ++row)
10558 {
10559 for (int col = 0; col < GetColumnCount(); ++col)
10560 {
10561 wxRichTextCell* cell = GetCell(row, col);
10562 if (cell->wxRichTextObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags) != wxRICHTEXT_HITTEST_NONE)
10563 {
10564 return cell->HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
10565 }
10566 }
10567 }
10568
10569 return wxRICHTEXT_HITTEST_NONE;
10570 }
10571
10572 bool wxRichTextTable::DeleteRows(int startRow, int noRows)
10573 {
10574 wxASSERT((startRow + noRows) <= m_rowCount);
10575 if ((startRow + noRows) > m_rowCount)
10576 return false;
10577
10578 wxCHECK_MSG(noRows != m_rowCount, false, "Trying to delete all the cells in a table");
10579
10580 wxRichTextBuffer* buffer = GetBuffer();
10581 wxRichTextCtrl* rtc = buffer->GetRichTextCtrl();
10582
10583 wxRichTextAction* action = NULL;
10584 wxRichTextTable* clone = NULL;
10585 if (!rtc->SuppressingUndo())
10586 {
10587 // Create a clone containing the current state of the table. It will be used to Undo the action
10588 clone = wxStaticCast(this->Clone(), wxRichTextTable);
10589 clone->SetParent(GetParent());
10590 action = new wxRichTextAction(NULL, _("Delete Row"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, rtc);
10591 action->SetObject(this);
10592 action->SetPosition(GetRange().GetStart());
10593 }
10594
10595 int i, j;
10596 for (i = startRow; i < (startRow+noRows); i++)
10597 {
10598 wxRichTextObjectPtrArray& colArray = m_cells[startRow];
10599 for (j = 0; j < (int) colArray.GetCount(); j++)
10600 {
10601 wxRichTextObject* cell = colArray[j];
10602 RemoveChild(cell, true);
10603 }
10604
10605 // Keep deleting at the same position, since we move all
10606 // the others up
10607 m_cells.RemoveAt(startRow);
10608 }
10609
10610 m_rowCount = m_rowCount - noRows;
10611
10612 if (!rtc->SuppressingUndo())
10613 {
10614 buffer->SubmitAction(action);
10615 // Finally store the original-state clone; doing so earlier would cause various failures
10616 action->StoreObject(clone);
10617 }
10618
10619 return true;
10620 }
10621
10622 bool wxRichTextTable::DeleteColumns(int startCol, int noCols)
10623 {
10624 wxASSERT((startCol + noCols) <= m_colCount);
10625 if ((startCol + noCols) > m_colCount)
10626 return false;
10627
10628 wxCHECK_MSG(noCols != m_colCount, false, "Trying to delete all the cells in a table");
10629
10630 wxRichTextBuffer* buffer = GetBuffer();
10631 wxRichTextCtrl* rtc = buffer->GetRichTextCtrl();
10632
10633 wxRichTextAction* action = NULL;
10634 wxRichTextTable* clone = NULL;
10635 if (!rtc->SuppressingUndo())
10636 {
10637 // Create a clone containing the current state of the table. It will be used to Undo the action
10638 clone = wxStaticCast(this->Clone(), wxRichTextTable);
10639 clone->SetParent(GetParent());
10640 action = new wxRichTextAction(NULL, _("Delete Column"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, rtc);
10641 action->SetObject(this);
10642 action->SetPosition(GetRange().GetStart());
10643 }
10644
10645 bool deleteRows = (noCols == m_colCount);
10646
10647 int i, j;
10648 for (i = 0; i < m_rowCount; i++)
10649 {
10650 wxRichTextObjectPtrArray& colArray = m_cells[deleteRows ? 0 : i];
10651 for (j = 0; j < noCols; j++)
10652 {
10653 wxRichTextObject* cell = colArray[startCol];
10654 RemoveChild(cell, true);
10655 colArray.RemoveAt(startCol);
10656 }
10657
10658 if (deleteRows)
10659 m_cells.RemoveAt(0);
10660 }
10661
10662 if (deleteRows)
10663 m_rowCount = 0;
10664 m_colCount = m_colCount - noCols;
10665
10666 if (!rtc->SuppressingUndo())
10667 {
10668 buffer->SubmitAction(action);
10669 // Finally store the original-state clone; doing so earlier would cause various failures
10670 action->StoreObject(clone);
10671 }
10672
10673 return true;
10674 }
10675
10676 bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& attr)
10677 {
10678 wxASSERT(startRow <= m_rowCount);
10679 if (startRow > m_rowCount)
10680 return false;
10681
10682 wxRichTextBuffer* buffer = GetBuffer();
10683 wxRichTextAction* action = NULL;
10684 wxRichTextTable* clone = NULL;
10685
10686 if (!buffer->GetRichTextCtrl()->SuppressingUndo())
10687 {
10688 // Create a clone containing the current state of the table. It will be used to Undo the action
10689 clone = wxStaticCast(this->Clone(), wxRichTextTable);
10690 clone->SetParent(GetParent());
10691 action = new wxRichTextAction(NULL, _("Add Row"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, buffer->GetRichTextCtrl());
10692 action->SetObject(this);
10693 action->SetPosition(GetRange().GetStart());
10694 }
10695
10696 wxRichTextAttr cellattr = attr;
10697 if (!cellattr.GetTextColour().IsOk())
10698 cellattr.SetTextColour(buffer->GetBasicStyle().GetTextColour());
10699
10700 int i, j;
10701 for (i = 0; i < noRows; i++)
10702 {
10703 int idx;
10704 if (startRow == m_rowCount)
10705 {
10706 m_cells.Add(wxRichTextObjectPtrArray());
10707 idx = m_cells.GetCount() - 1;
10708 }
10709 else
10710 {
10711 m_cells.Insert(wxRichTextObjectPtrArray(), startRow+i);
10712 idx = startRow+i;
10713 }
10714
10715 wxRichTextObjectPtrArray& colArray = m_cells[idx];
10716 for (j = 0; j < m_colCount; j++)
10717 {
10718 wxRichTextCell* cell = new wxRichTextCell;
10719 cell->GetAttributes() = cellattr;
10720
10721 AppendChild(cell);
10722 cell->AddParagraph(wxEmptyString);
10723 colArray.Add(cell);
10724 }
10725 }
10726
10727 m_rowCount = m_rowCount + noRows;
10728
10729 if (!buffer->GetRichTextCtrl()->SuppressingUndo())
10730 {
10731 buffer->SubmitAction(action);
10732 // Finally store the original-state clone; doing so earlier would cause various failures
10733 action->StoreObject(clone);
10734 }
10735
10736 return true;
10737 }
10738
10739 bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& attr)
10740 {
10741 wxASSERT(startCol <= m_colCount);
10742 if (startCol > m_colCount)
10743 return false;
10744
10745 wxRichTextBuffer* buffer = GetBuffer();
10746 wxRichTextAction* action = NULL;
10747 wxRichTextTable* clone = NULL;
10748
10749 if (!buffer->GetRichTextCtrl()->SuppressingUndo())
10750 {
10751 // Create a clone containing the current state of the table. It will be used to Undo the action
10752 clone = wxStaticCast(this->Clone(), wxRichTextTable);
10753 clone->SetParent(GetParent());
10754 action = new wxRichTextAction(NULL, _("Add Column"), wxRICHTEXT_CHANGE_OBJECT, buffer, this, buffer->GetRichTextCtrl());
10755 action->SetObject(this);
10756 action->SetPosition(GetRange().GetStart());
10757 }
10758
10759 wxRichTextAttr cellattr = attr;
10760 if (!cellattr.GetTextColour().IsOk())
10761 cellattr.SetTextColour(buffer->GetBasicStyle().GetTextColour());
10762
10763 int i, j;
10764 for (i = 0; i < m_rowCount; i++)
10765 {
10766 wxRichTextObjectPtrArray& colArray = m_cells[i];
10767 for (j = 0; j < noCols; j++)
10768 {
10769 wxRichTextCell* cell = new wxRichTextCell;
10770 cell->GetAttributes() = cellattr;
10771
10772 AppendChild(cell);
10773 cell->AddParagraph(wxEmptyString);
10774
10775 if (startCol == m_colCount)
10776 colArray.Add(cell);
10777 else
10778 colArray.Insert(cell, startCol+j);
10779 }
10780 }
10781
10782 m_colCount = m_colCount + noCols;
10783
10784 if (!buffer->GetRichTextCtrl()->SuppressingUndo())
10785 {
10786 buffer->SubmitAction(action);
10787 // Finally store the original-state clone; doing so earlier would cause various failures
10788 action->StoreObject(clone);
10789 }
10790
10791 return true;
10792 }
10793
10794 // Edit properties via a GUI
10795 bool wxRichTextTable::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
10796 {
10797 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Table Properties"));
10798 boxDlg.SetAttributes(GetAttributes());
10799
10800 if (boxDlg.ShowModal() == wxID_OK)
10801 {
10802 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
10803 return true;
10804 }
10805 else
10806 return false;
10807 }
10808
10809 bool wxRichTextTableBlock::ComputeBlockForSelection(wxRichTextTable* table, wxRichTextCtrl* ctrl, bool requireCellSelection)
10810 {
10811 if (!ctrl)
10812 return false;
10813
10814 ColStart() = 0;
10815 ColEnd() = table->GetColumnCount()-1;
10816 RowStart() = 0;
10817 RowEnd() = table->GetRowCount()-1;
10818
10819 wxRichTextSelection selection = ctrl->GetSelection();
10820 if (selection.IsValid() && selection.GetContainer() == table)
10821 {
10822 // Start with an invalid block and increase.
10823 wxRichTextTableBlock selBlock(-1, -1, -1, -1);
10824 wxRichTextRangeArray ranges = selection.GetRanges();
10825 int row, col;
10826 for (row = 0; row < table->GetRowCount(); row++)
10827 {
10828 for (col = 0; col < table->GetColumnCount(); col++)
10829 {
10830 if (selection.WithinSelection(table->GetCell(row, col)->GetRange().GetStart()))
10831 {
10832 if (selBlock.ColStart() == -1)
10833 selBlock.ColStart() = col;
10834 if (selBlock.ColEnd() == -1)
10835 selBlock.ColEnd() = col;
10836 if (col < selBlock.ColStart())
10837 selBlock.ColStart() = col;
10838 if (col > selBlock.ColEnd())
10839 selBlock.ColEnd() = col;
10840
10841 if (selBlock.RowStart() == -1)
10842 selBlock.RowStart() = row;
10843 if (selBlock.RowEnd() == -1)
10844 selBlock.RowEnd() = row;
10845 if (row < selBlock.RowStart())
10846 selBlock.RowStart() = row;
10847 if (row > selBlock.RowEnd())
10848 selBlock.RowEnd() = row;
10849 }
10850 }
10851 }
10852
10853 if (selBlock.RowStart() != -1 && selBlock.RowEnd() != -1 && selBlock.ColStart() != -1 && selBlock.ColEnd() != -1)
10854 (*this) = selBlock;
10855 }
10856 else
10857 {
10858 // See if a whole cell's contents is selected, in which case we can treat the cell as selected.
10859 // wxRTC lacks the ability to select a single cell.
10860 wxRichTextCell* cell = wxDynamicCast(ctrl->GetFocusObject(), wxRichTextCell);
10861 if (cell && (!requireCellSelection || (ctrl->HasSelection() && ctrl->GetSelectionRange() == cell->GetOwnRange())))
10862 {
10863 int row, col;
10864 if (table->GetCellRowColumnPosition(cell->GetRange().GetStart(), row, col))
10865 {
10866 RowStart() = row;
10867 RowEnd() = row;
10868 ColStart() = col;
10869 ColEnd() = col;
10870 }
10871 }
10872 }
10873
10874 return true;
10875 }
10876
10877 // Does this block represent the whole table?
10878 bool wxRichTextTableBlock::IsWholeTable(wxRichTextTable* table) const
10879 {
10880 return (ColStart() == 0 && RowStart() == 0 && ColEnd() == (table->GetColumnCount()-1) && RowEnd() == (table->GetRowCount()-1));
10881 }
10882
10883 // Returns the cell focused in the table, if any
10884 wxRichTextCell* wxRichTextTableBlock::GetFocusedCell(wxRichTextCtrl* ctrl)
10885 {
10886 if (!ctrl)
10887 return NULL;
10888
10889 wxRichTextCell* cell = wxDynamicCast(ctrl->GetFocusObject(), wxRichTextCell);
10890 return cell;
10891 }
10892
10893 /*
10894 * Module to initialise and clean up handlers
10895 */
10896
10897 class wxRichTextModule: public wxModule
10898 {
10899 DECLARE_DYNAMIC_CLASS(wxRichTextModule)
10900 public:
10901 wxRichTextModule() {}
10902 bool OnInit()
10903 {
10904 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer);
10905 wxRichTextBuffer::InitStandardHandlers();
10906 wxRichTextParagraph::InitDefaultTabs();
10907
10908 wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
10909 wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
10910 wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
10911 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
10912 wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
10913 wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
10914 wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
10915 wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
10916 wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
10917
10918 return true;
10919 }
10920 void OnExit()
10921 {
10922 wxRichTextBuffer::CleanUpHandlers();
10923 wxRichTextBuffer::CleanUpDrawingHandlers();
10924 wxRichTextBuffer::CleanUpFieldTypes();
10925 wxRichTextXMLHandler::ClearNodeToClassMap();
10926 wxRichTextDecimalToRoman(-1);
10927 wxRichTextParagraph::ClearDefaultTabs();
10928 wxRichTextCtrl::ClearAvailableFontNames();
10929 wxRichTextBuffer::SetRenderer(NULL);
10930 }
10931 };
10932
10933 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule)
10934
10935
10936 // If the richtext lib is dynamically loaded after the app has already started
10937 // (such as from wxPython) then the built-in module system will not init this
10938 // module. Provide this function to do it manually.
10939 void wxRichTextModuleInit()
10940 {
10941 wxModule* module = new wxRichTextModule;
10942 wxModule::RegisterModule(module);
10943 wxModule::InitializeModules();
10944 }
10945
10946
10947 /*!
10948 * Commands for undo/redo
10949 *
10950 */
10951
10952 wxRichTextCommand::wxRichTextCommand(const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
10953 wxRichTextParagraphLayoutBox* container, wxRichTextCtrl* ctrl, bool ignoreFirstTime): wxCommand(true, name)
10954 {
10955 /* wxRichTextAction* action = */ new wxRichTextAction(this, name, id, buffer, container, ctrl, ignoreFirstTime);
10956 }
10957
10958 wxRichTextCommand::wxRichTextCommand(const wxString& name): wxCommand(true, name)
10959 {
10960 }
10961
10962 wxRichTextCommand::~wxRichTextCommand()
10963 {
10964 ClearActions();
10965 }
10966
10967 void wxRichTextCommand::AddAction(wxRichTextAction* action)
10968 {
10969 if (!m_actions.Member(action))
10970 m_actions.Append(action);
10971 }
10972
10973 bool wxRichTextCommand::Do()
10974 {
10975 for (wxList::compatibility_iterator node = m_actions.GetFirst(); node; node = node->GetNext())
10976 {
10977 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10978 action->Do();
10979 }
10980
10981 return true;
10982 }
10983
10984 bool wxRichTextCommand::Undo()
10985 {
10986 for (wxList::compatibility_iterator node = m_actions.GetLast(); node; node = node->GetPrevious())
10987 {
10988 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
10989 action->Undo();
10990 }
10991
10992 return true;
10993 }
10994
10995 void wxRichTextCommand::ClearActions()
10996 {
10997 WX_CLEAR_LIST(wxList, m_actions);
10998 }
10999
11000 /*!
11001 * Individual action
11002 *
11003 */
11004
11005 wxRichTextAction::wxRichTextAction(wxRichTextCommand* cmd, const wxString& name, wxRichTextCommandId id,
11006 wxRichTextBuffer* buffer, wxRichTextParagraphLayoutBox* container,
11007 wxRichTextCtrl* ctrl, bool ignoreFirstTime)
11008 {
11009 m_buffer = buffer;
11010 m_object = NULL;
11011 m_containerAddress.Create(buffer, container);
11012 m_ignoreThis = ignoreFirstTime;
11013 m_cmdId = id;
11014 m_position = -1;
11015 m_ctrl = ctrl;
11016 m_name = name;
11017 m_newParagraphs.SetDefaultStyle(buffer->GetDefaultStyle());
11018 m_newParagraphs.SetBasicStyle(buffer->GetBasicStyle());
11019 if (cmd)
11020 cmd->AddAction(this);
11021 }
11022
11023 wxRichTextAction::~wxRichTextAction()
11024 {
11025 if (m_object)
11026 delete m_object;
11027 }
11028
11029 // Returns the container that this action refers to, using the container address and top-level buffer.
11030 wxRichTextParagraphLayoutBox* wxRichTextAction::GetContainer() const
11031 {
11032 wxRichTextParagraphLayoutBox* container = wxDynamicCast(GetContainerAddress().GetObject(m_buffer), wxRichTextParagraphLayoutBox);
11033 return container;
11034 }
11035
11036
11037 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions)
11038 {
11039 // Store a list of line start character and y positions so we can figure out which area
11040 // we need to refresh
11041
11042 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11043 wxRichTextParagraphLayoutBox* container = GetContainer();
11044 wxASSERT(container != NULL);
11045 if (!container)
11046 return;
11047
11048 // NOTE: we're assuming that the buffer is laid out correctly at this point.
11049 // If we had several actions, which only invalidate and leave layout until the
11050 // paint handler is called, then this might not be true. So we may need to switch
11051 // optimisation on only when we're simply adding text and not simultaneously
11052 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
11053 // first, but of course this means we'll be doing it twice.
11054 if (!m_buffer->IsDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
11055 {
11056 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
11057 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
11058 int lastY = firstVisiblePt.y + clientSize.y;
11059
11060 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetRange().GetStart());
11061 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
11062 while (node)
11063 {
11064 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
11065 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
11066 while (node2)
11067 {
11068 wxRichTextLine* line = node2->GetData();
11069 wxPoint pt = line->GetAbsolutePosition();
11070 wxRichTextRange range = line->GetAbsoluteRange();
11071
11072 if (pt.y > lastY)
11073 {
11074 node2 = wxRichTextLineList::compatibility_iterator();
11075 node = wxRichTextObjectList::compatibility_iterator();
11076 }
11077 else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
11078 {
11079 optimizationLineCharPositions.Add(range.GetStart());
11080 optimizationLineYPositions.Add(pt.y);
11081 }
11082
11083 if (node2)
11084 node2 = node2->GetNext();
11085 }
11086
11087 if (node)
11088 node = node->GetNext();
11089 }
11090 }
11091 #endif
11092 }
11093
11094 bool wxRichTextAction::Do()
11095 {
11096 m_buffer->Modify(true);
11097
11098 wxRichTextParagraphLayoutBox* container = GetContainer();
11099 wxASSERT(container != NULL);
11100 if (!container)
11101 return false;
11102
11103 switch (m_cmdId)
11104 {
11105 case wxRICHTEXT_INSERT:
11106 {
11107 // Store a list of line start character and y positions so we can figure out which area
11108 // we need to refresh
11109 wxArrayInt optimizationLineCharPositions;
11110 wxArrayInt optimizationLineYPositions;
11111
11112 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11113 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
11114 #endif
11115
11116 container->InsertFragment(GetRange().GetStart(), m_newParagraphs);
11117 container->UpdateRanges();
11118
11119 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11120 // Layout() would stop prematurely at the top level.
11121 container->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
11122
11123 long newCaretPosition = GetPosition() + m_newParagraphs.GetOwnRange().GetLength();
11124
11125 // Character position to caret position
11126 newCaretPosition --;
11127
11128 // Don't take into account the last newline
11129 if (m_newParagraphs.GetPartialParagraph())
11130 newCaretPosition --;
11131 else
11132 if (m_newParagraphs.GetChildren().GetCount() > 1)
11133 {
11134 wxRichTextObject* p = (wxRichTextObject*) m_newParagraphs.GetChildren().GetLast()->GetData();
11135 if (p->GetRange().GetLength() == 1)
11136 newCaretPosition --;
11137 }
11138
11139 newCaretPosition = wxMin(newCaretPosition, (container->GetOwnRange().GetEnd()-1));
11140
11141 UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
11142
11143 wxRichTextEvent cmdEvent(
11144 wxEVT_RICHTEXT_CONTENT_INSERTED,
11145 m_ctrl ? m_ctrl->GetId() : -1);
11146 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
11147 cmdEvent.SetRange(GetRange());
11148 cmdEvent.SetPosition(GetRange().GetStart());
11149 cmdEvent.SetContainer(container);
11150
11151 m_buffer->SendEvent(cmdEvent);
11152
11153 break;
11154 }
11155 case wxRICHTEXT_DELETE:
11156 {
11157 wxArrayInt optimizationLineCharPositions;
11158 wxArrayInt optimizationLineYPositions;
11159
11160 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11161 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
11162 #endif
11163
11164 container->DeleteRange(GetRange());
11165 container->UpdateRanges();
11166 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11167 // Layout() would stop prematurely at the top level.
11168 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
11169
11170 long caretPos = GetRange().GetStart()-1;
11171 if (caretPos >= container->GetOwnRange().GetEnd())
11172 caretPos --;
11173
11174 UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
11175
11176 wxRichTextEvent cmdEvent(
11177 wxEVT_RICHTEXT_CONTENT_DELETED,
11178 m_ctrl ? m_ctrl->GetId() : -1);
11179 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
11180 cmdEvent.SetRange(GetRange());
11181 cmdEvent.SetPosition(GetRange().GetStart());
11182 cmdEvent.SetContainer(container);
11183
11184 m_buffer->SendEvent(cmdEvent);
11185
11186 break;
11187 }
11188 case wxRICHTEXT_CHANGE_STYLE:
11189 case wxRICHTEXT_CHANGE_PROPERTIES:
11190 {
11191 ApplyParagraphs(GetNewParagraphs());
11192
11193 // Invalidate the whole buffer if there were floating objects
11194 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
11195 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
11196 else
11197 {
11198 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11199 // Layout() would stop prematurely at the top level.
11200 container->InvalidateHierarchy(GetRange());
11201 }
11202
11203 UpdateAppearance(GetPosition());
11204
11205 wxRichTextEvent cmdEvent(
11206 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_RICHTEXT_STYLE_CHANGED : wxEVT_RICHTEXT_PROPERTIES_CHANGED,
11207 m_ctrl ? m_ctrl->GetId() : -1);
11208 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
11209 cmdEvent.SetRange(GetRange());
11210 cmdEvent.SetPosition(GetRange().GetStart());
11211 cmdEvent.SetContainer(container);
11212
11213 m_buffer->SendEvent(cmdEvent);
11214
11215 break;
11216 }
11217 case wxRICHTEXT_CHANGE_ATTRIBUTES:
11218 {
11219 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer); // container->GetChildAtPosition(GetRange().GetStart());
11220 if (obj)
11221 {
11222 wxRichTextAttr oldAttr = obj->GetAttributes();
11223 obj->GetAttributes() = m_attributes;
11224 m_attributes = oldAttr;
11225 }
11226
11227 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11228 // Layout() would stop prematurely at the top level.
11229 // Invalidate the whole buffer if there were floating objects
11230 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
11231 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
11232 else
11233 container->InvalidateHierarchy(GetRange());
11234
11235 UpdateAppearance(GetPosition());
11236
11237 wxRichTextEvent cmdEvent(
11238 wxEVT_RICHTEXT_STYLE_CHANGED,
11239 m_ctrl ? m_ctrl->GetId() : -1);
11240 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
11241 cmdEvent.SetRange(GetRange());
11242 cmdEvent.SetPosition(GetRange().GetStart());
11243 cmdEvent.SetContainer(container);
11244
11245 m_buffer->SendEvent(cmdEvent);
11246
11247 break;
11248 }
11249 case wxRICHTEXT_CHANGE_OBJECT:
11250 {
11251 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer);
11252 if (obj && m_object && m_ctrl)
11253 {
11254 // The plan is to swap the current object with the stored, previous-state, clone
11255 // We can't get 'node' from the containing buffer (as it doesn't directly store objects)
11256 // so use the parent paragraph
11257 wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph);
11258 wxCHECK_MSG(para, false, "Invalid parent paragraph");
11259
11260 // The stored object, m_object, may have a stale parent paragraph. This would cause
11261 // a crash during layout, so use obj's parent para, which should be the correct one.
11262 // (An alternative would be to return the parent too from m_objectAddress.GetObject(),
11263 // or to set obj's parent there before returning)
11264 m_object->SetParent(para);
11265
11266 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().Find(obj);
11267 if (node)
11268 {
11269 wxRichTextObject* obj = node->GetData();
11270 node->SetData(m_object);
11271 m_object = obj;
11272 }
11273 }
11274
11275 // We can't rely on the current focus-object remaining valid, if it's e.g. a table's cell.
11276 // And we can't cope with this in the calling code: a user may later click in the cell
11277 // before deciding to Undo() or Redo(). So play safe and set focus to the buffer.
11278 if (m_ctrl)
11279 m_ctrl->SetFocusObject(m_buffer, false);
11280
11281 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11282 // Layout() would stop prematurely at the top level.
11283 // Invalidate the whole buffer if there were floating objects
11284 if (wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0)
11285 m_buffer->InvalidateHierarchy(wxRICHTEXT_ALL);
11286 else
11287 container->InvalidateHierarchy(GetRange());
11288
11289 UpdateAppearance(GetPosition(), true);
11290
11291 // TODO: send new kind of modification event
11292
11293 break;
11294 }
11295 default:
11296 break;
11297 }
11298
11299 return true;
11300 }
11301
11302 bool wxRichTextAction::Undo()
11303 {
11304 m_buffer->Modify(true);
11305
11306 wxRichTextParagraphLayoutBox* container = GetContainer();
11307 wxASSERT(container != NULL);
11308 if (!container)
11309 return false;
11310
11311 switch (m_cmdId)
11312 {
11313 case wxRICHTEXT_INSERT:
11314 {
11315 wxArrayInt optimizationLineCharPositions;
11316 wxArrayInt optimizationLineYPositions;
11317
11318 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11319 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
11320 #endif
11321
11322 container->DeleteRange(GetRange());
11323 container->UpdateRanges();
11324
11325 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11326 // Layout() would stop prematurely at the top level.
11327 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
11328
11329 long newCaretPosition = GetPosition() - 1;
11330
11331 UpdateAppearance(newCaretPosition, true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
11332
11333 wxRichTextEvent cmdEvent(
11334 wxEVT_RICHTEXT_CONTENT_DELETED,
11335 m_ctrl ? m_ctrl->GetId() : -1);
11336 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
11337 cmdEvent.SetRange(GetRange());
11338 cmdEvent.SetPosition(GetRange().GetStart());
11339 cmdEvent.SetContainer(container);
11340
11341 m_buffer->SendEvent(cmdEvent);
11342
11343 break;
11344 }
11345 case wxRICHTEXT_DELETE:
11346 {
11347 wxArrayInt optimizationLineCharPositions;
11348 wxArrayInt optimizationLineYPositions;
11349
11350 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11351 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
11352 #endif
11353
11354 container->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
11355 container->UpdateRanges();
11356
11357 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11358 // Layout() would stop prematurely at the top level.
11359 container->InvalidateHierarchy(GetRange());
11360
11361 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
11362
11363 wxRichTextEvent cmdEvent(
11364 wxEVT_RICHTEXT_CONTENT_INSERTED,
11365 m_ctrl ? m_ctrl->GetId() : -1);
11366 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
11367 cmdEvent.SetRange(GetRange());
11368 cmdEvent.SetPosition(GetRange().GetStart());
11369 cmdEvent.SetContainer(container);
11370
11371 m_buffer->SendEvent(cmdEvent);
11372
11373 break;
11374 }
11375 case wxRICHTEXT_CHANGE_STYLE:
11376 case wxRICHTEXT_CHANGE_PROPERTIES:
11377 {
11378 ApplyParagraphs(GetOldParagraphs());
11379 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
11380 // Layout() would stop prematurely at the top level.
11381 container->InvalidateHierarchy(GetRange());
11382
11383 UpdateAppearance(GetPosition());
11384
11385 wxRichTextEvent cmdEvent(
11386 m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_RICHTEXT_STYLE_CHANGED : wxEVT_RICHTEXT_PROPERTIES_CHANGED,
11387 m_ctrl ? m_ctrl->GetId() : -1);
11388 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
11389 cmdEvent.SetRange(GetRange());
11390 cmdEvent.SetPosition(GetRange().GetStart());
11391 cmdEvent.SetContainer(container);
11392
11393 m_buffer->SendEvent(cmdEvent);
11394
11395 break;
11396 }
11397 case wxRICHTEXT_CHANGE_ATTRIBUTES:
11398 case wxRICHTEXT_CHANGE_OBJECT:
11399 {
11400 return Do();
11401 }
11402 default:
11403 break;
11404 }
11405
11406 return true;
11407 }
11408
11409 /// Update the control appearance
11410 void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions, bool isDoCmd)
11411 {
11412 wxRichTextParagraphLayoutBox* container = GetContainer();
11413 wxASSERT(container != NULL);
11414 if (!container)
11415 return;
11416
11417 if (m_ctrl)
11418 {
11419 m_ctrl->SetFocusObject(container);
11420 m_ctrl->SetCaretPosition(caretPosition);
11421
11422 if (!m_ctrl->IsFrozen())
11423 {
11424 wxRect containerRect = container->GetRect();
11425
11426 m_ctrl->LayoutContent();
11427
11428 // Refresh everything if there were floating objects or the container changed size
11429 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
11430 if ((wxRichTextBuffer::GetFloatingLayoutMode() && container->GetFloatingObjectCount() > 0) || (container->GetParent() && containerRect != container->GetRect()))
11431 {
11432 m_ctrl->Refresh(false);
11433 }
11434 else
11435
11436 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
11437 // Find refresh rectangle if we are in a position to optimise refresh
11438 if ((m_cmdId == wxRICHTEXT_INSERT || m_cmdId == wxRICHTEXT_DELETE) && optimizationLineCharPositions)
11439 {
11440 size_t i;
11441
11442 wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
11443 wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
11444
11445 // Start/end positions
11446 int firstY = 0;
11447 int lastY = firstVisiblePt.y + clientSize.y;
11448
11449 bool foundEnd = false;
11450
11451 // position offset - how many characters were inserted
11452 int positionOffset = GetRange().GetLength();
11453
11454 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
11455 if ((m_cmdId == wxRICHTEXT_DELETE && isDoCmd) || (m_cmdId == wxRICHTEXT_INSERT && !isDoCmd))
11456 positionOffset = - positionOffset;
11457
11458 // find the first line which is being drawn at the same position as it was
11459 // before. Since we're talking about a simple insertion, we can assume
11460 // that the rest of the window does not need to be redrawn.
11461 long pos = GetRange().GetStart();
11462
11463 wxRichTextParagraph* para = container->GetParagraphAtPosition(pos, false /* is not caret pos */);
11464 // Since we support floating layout, we should redraw the whole para instead of just
11465 // the first line touching the invalid range.
11466 if (para)
11467 {
11468 // In case something was drawn above the paragraph,
11469 // such as a line break, allow a little extra.
11470 firstY = para->GetPosition().y - 4;
11471 }
11472
11473 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
11474 while (node)
11475 {
11476 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
11477 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
11478 while (node2)
11479 {
11480 wxRichTextLine* line = node2->GetData();
11481 wxPoint pt = line->GetAbsolutePosition();
11482 wxRichTextRange range = line->GetAbsoluteRange();
11483
11484 // we want to find the first line that is in the same position
11485 // as before. This will mean we're at the end of the changed text.
11486
11487 if (pt.y > lastY) // going past the end of the window, no more info
11488 {
11489 node2 = wxRichTextLineList::compatibility_iterator();
11490 node = wxRichTextObjectList::compatibility_iterator();
11491 }
11492 // Detect last line in the buffer
11493 else if (!node2->GetNext() && para->GetRange().Contains(container->GetOwnRange().GetEnd()))
11494 {
11495 // If deleting text, make sure we refresh below as well as above
11496 if (positionOffset >= 0)
11497 {
11498 foundEnd = true;
11499 lastY = pt.y + line->GetSize().y;
11500 }
11501
11502 node2 = wxRichTextLineList::compatibility_iterator();
11503 node = wxRichTextObjectList::compatibility_iterator();
11504
11505 break;
11506 }
11507 else
11508 {
11509 // search for this line being at the same position as before
11510 for (i = 0; i < optimizationLineCharPositions->GetCount(); i++)
11511 {
11512 if (((*optimizationLineCharPositions)[i] + positionOffset == range.GetStart()) &&
11513 ((*optimizationLineYPositions)[i] == pt.y))
11514 {
11515 // Stop, we're now the same as we were
11516 foundEnd = true;
11517
11518 lastY = pt.y + line->GetSize().y;
11519
11520 node2 = wxRichTextLineList::compatibility_iterator();
11521 node = wxRichTextObjectList::compatibility_iterator();
11522
11523 break;
11524 }
11525 }
11526 }
11527
11528 if (node2)
11529 node2 = node2->GetNext();
11530 }
11531
11532 if (node)
11533 node = node->GetNext();
11534 }
11535
11536 firstY = wxMax(firstVisiblePt.y, firstY);
11537 if (!foundEnd)
11538 lastY = firstVisiblePt.y + clientSize.y;
11539
11540 // Convert to device coordinates
11541 wxRect rect(m_ctrl->GetPhysicalPoint(m_ctrl->GetScaledPoint(wxPoint(firstVisiblePt.x, firstY))), m_ctrl->GetScaledSize(wxSize(clientSize.x, lastY - firstY)));
11542 m_ctrl->RefreshRect(rect);
11543 }
11544 else
11545 #endif
11546 m_ctrl->Refresh(false);
11547
11548 m_ctrl->PositionCaret();
11549
11550 // This causes styles to persist when doing programmatic
11551 // content creation except when Freeze/Thaw is used, so
11552 // disable this and check for the consequences.
11553 // m_ctrl->SetDefaultStyleToCursorStyle();
11554
11555 if (sendUpdateEvent)
11556 wxTextCtrl::SendTextUpdatedEvent(m_ctrl);
11557 }
11558 }
11559 }
11560
11561 /// Replace the buffer paragraphs with the new ones.
11562 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment)
11563 {
11564 wxRichTextParagraphLayoutBox* container = GetContainer();
11565 wxASSERT(container != NULL);
11566 if (!container)
11567 return;
11568
11569 wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst();
11570 while (node)
11571 {
11572 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
11573 wxASSERT (para != NULL);
11574
11575 // We'll replace the existing paragraph by finding the paragraph at this position,
11576 // delete its node data, and setting a copy as the new node data.
11577 // TODO: make more efficient by simply swapping old and new paragraph objects.
11578
11579 wxRichTextParagraph* existingPara = container->GetParagraphAtPosition(para->GetRange().GetStart());
11580 if (existingPara)
11581 {
11582 wxRichTextObjectList::compatibility_iterator bufferParaNode = container->GetChildren().Find(existingPara);
11583 if (bufferParaNode)
11584 {
11585 wxRichTextParagraph* newPara = new wxRichTextParagraph(*para);
11586 newPara->SetParent(container);
11587
11588 bufferParaNode->SetData(newPara);
11589
11590 delete existingPara;
11591 }
11592 }
11593
11594 node = node->GetNext();
11595 }
11596 }
11597
11598
11599 /*!
11600 * wxRichTextRange
11601 * This stores beginning and end positions for a range of data.
11602 */
11603
11604 WX_DEFINE_OBJARRAY(wxRichTextRangeArray);
11605
11606 /// Limit this range to be within 'range'
11607 bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
11608 {
11609 if (m_start < range.m_start)
11610 m_start = range.m_start;
11611
11612 if (m_end > range.m_end)
11613 m_end = range.m_end;
11614
11615 return true;
11616 }
11617
11618 /*!
11619 * wxRichTextImage implementation
11620 * This object represents an image.
11621 */
11622
11623 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
11624
11625 wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle):
11626 wxRichTextObject(parent)
11627 {
11628 Init();
11629 m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG);
11630 if (charStyle)
11631 SetAttributes(*charStyle);
11632 }
11633
11634 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle):
11635 wxRichTextObject(parent)
11636 {
11637 Init();
11638 m_imageBlock = imageBlock;
11639 if (charStyle)
11640 SetAttributes(*charStyle);
11641 }
11642
11643 wxRichTextImage::~wxRichTextImage()
11644 {
11645 }
11646
11647 void wxRichTextImage::Init()
11648 {
11649 m_originalImageSize = wxSize(-1, -1);
11650 }
11651
11652 /// Create a cached image at the required size
11653 bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache, const wxSize& parentSize)
11654 {
11655 if (!m_imageBlock.IsOk())
11656 return false;
11657
11658 // If we have an original image size, use that to compute the cached bitmap size
11659 // instead of loading the image each time. This way we can avoid loading
11660 // the image so long as the new cached bitmap size hasn't changed.
11661
11662 wxImage image;
11663 if (resetCache || m_originalImageSize.GetWidth() <= 0 || m_originalImageSize.GetHeight() <= 0)
11664 {
11665 m_imageCache = wxNullBitmap;
11666
11667 m_imageBlock.Load(image);
11668 if (!image.IsOk())
11669 return false;
11670
11671 m_originalImageSize = wxSize(image.GetWidth(), image.GetHeight());
11672 }
11673
11674 int width = m_originalImageSize.GetWidth();
11675 int height = m_originalImageSize.GetHeight();
11676
11677 int parentWidth = 0;
11678 int parentHeight = 0;
11679
11680 int maxWidth = -1;
11681 int maxHeight = -1;
11682
11683 wxSize sz = parentSize;
11684 if (sz == wxDefaultSize)
11685 {
11686 if (GetParent() && GetParent()->GetParent())
11687 sz = GetParent()->GetParent()->GetCachedSize();
11688 }
11689
11690 if (sz != wxDefaultSize)
11691 {
11692 wxRichTextBuffer* buffer = GetBuffer();
11693 if (buffer)
11694 {
11695 // Find the actual space available when margin is taken into account
11696 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11697 marginRect = wxRect(0, 0, sz.x, sz.y);
11698 if (GetParent() && GetParent()->GetParent())
11699 {
11700 buffer->GetBoxRects(dc, buffer, GetParent()->GetParent()->GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
11701 sz = contentRect.GetSize();
11702 }
11703
11704 // Use a minimum size to stop images becoming very small
11705 parentWidth = wxMax(100, sz.GetWidth());
11706 parentHeight = wxMax(100, sz.GetHeight());
11707
11708 if (buffer->GetRichTextCtrl())
11709 // Start with a maximum width of the control size, even if not specified by the content,
11710 // to minimize the amount of picture overlapping the right-hand side
11711 maxWidth = parentWidth;
11712 }
11713 }
11714
11715 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
11716 {
11717 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11718 width = (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth)/100.0);
11719 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11720 width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
11721 else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11722 width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
11723 }
11724
11725 // Limit to max width
11726
11727 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
11728 {
11729 int mw = -1;
11730
11731 if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11732 mw = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth)/100.0);
11733 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11734 mw = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
11735 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11736 mw = GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
11737
11738 // If we already have a smaller max width due to the constraints of the control size,
11739 // don't use the larger max width.
11740 if (mw != -1 && ((maxWidth == -1) || (mw < maxWidth)))
11741 maxWidth = mw;
11742 }
11743
11744 if (maxWidth > 0 && width > maxWidth)
11745 width = maxWidth;
11746
11747 // Preserve the aspect ratio
11748 if (width != m_originalImageSize.GetWidth())
11749 height = (int) (float(m_originalImageSize.GetHeight()) * (float(width)/float(m_originalImageSize.GetWidth())));
11750
11751 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
11752 {
11753 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11754 height = (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight)/100.0);
11755 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11756 height = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
11757 else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11758 height = GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
11759
11760 // Preserve the aspect ratio
11761 if (height != m_originalImageSize.GetHeight())
11762 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
11763 }
11764
11765 // Limit to max height
11766
11767 if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
11768 {
11769 if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11770 maxHeight = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight)/100.0);
11771 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11772 maxHeight = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
11773 else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11774 maxHeight = GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
11775 }
11776
11777 if (maxHeight > 0 && height > maxHeight)
11778 {
11779 height = maxHeight;
11780
11781 // Preserve the aspect ratio
11782 if (height != m_originalImageSize.GetHeight())
11783 width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
11784 }
11785
11786 // Prevent the use of zero size
11787 width = wxMax(1, width);
11788 height = wxMax(1, height);
11789
11790 if (m_imageCache.IsOk() && m_imageCache.GetWidth() == width && m_imageCache.GetHeight() == height)
11791 {
11792 // Do nothing, we didn't need to change the image cache
11793 }
11794 else
11795 {
11796 if (!image.IsOk())
11797 {
11798 m_imageBlock.Load(image);
11799 if (!image.IsOk())
11800 return false;
11801 }
11802
11803 if (image.GetWidth() == width && image.GetHeight() == height)
11804 m_imageCache = wxBitmap(image);
11805 else
11806 {
11807 // If the original width and height is small, e.g. 400 or below,
11808 // scale up and then down to improve image quality. This can make
11809 // a big difference, with not much performance hit.
11810 int upscaleThreshold = 400;
11811 wxImage img;
11812 if (image.GetWidth() <= upscaleThreshold || image.GetHeight() <= upscaleThreshold)
11813 {
11814 img = image.Scale(image.GetWidth()*2, image.GetHeight()*2);
11815 img.Rescale(width, height, wxIMAGE_QUALITY_HIGH);
11816 }
11817 else
11818 img = image.Scale(width, height, wxIMAGE_QUALITY_HIGH);
11819 m_imageCache = wxBitmap(img);
11820 }
11821 }
11822
11823 return m_imageCache.IsOk();
11824 }
11825
11826 /// Draw the item
11827 bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
11828 {
11829 if (!IsShown())
11830 return true;
11831
11832 // Don't need cached size AFAIK
11833 // wxSize size = GetCachedSize();
11834 if (!LoadImageCache(dc))
11835 return false;
11836
11837 wxRichTextAttr attr(GetAttributes());
11838 context.ApplyVirtualAttributes(attr, this);
11839
11840 DrawBoxAttributes(dc, GetBuffer(), attr, wxRect(rect.GetPosition(), GetCachedSize()));
11841
11842 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11843 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11844 marginRect = rect; // outer rectangle, will calculate contentRect
11845 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
11846
11847 dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true);
11848
11849 if (selection.WithinSelection(GetRange().GetStart(), this))
11850 {
11851 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
11852 wxCheckSetPen(dc, *wxBLACK_PEN);
11853 dc.SetLogicalFunction(wxINVERT);
11854 dc.DrawRectangle(contentRect);
11855 dc.SetLogicalFunction(wxCOPY);
11856 }
11857
11858 return true;
11859 }
11860
11861 /// Lay the item out
11862 bool wxRichTextImage::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
11863 {
11864 if (!LoadImageCache(dc))
11865 return false;
11866
11867 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11868 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11869 contentRect = wxRect(wxPoint(0,0), imageSize);
11870
11871 wxRichTextAttr attr(GetAttributes());
11872 context.ApplyVirtualAttributes(attr, this);
11873
11874 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
11875
11876 wxSize overallSize = marginRect.GetSize();
11877
11878 SetCachedSize(overallSize);
11879 SetMaxSize(overallSize);
11880 SetMinSize(overallSize);
11881 SetPosition(rect.GetPosition());
11882
11883 return true;
11884 }
11885
11886 /// Get/set the object size for the given range. Returns false if the range
11887 /// is invalid for this object.
11888 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
11889 {
11890 if (!range.IsWithin(GetRange()))
11891 return false;
11892
11893 if (!((wxRichTextImage*)this)->LoadImageCache(dc, false, parentSize))
11894 {
11895 size.x = 0; size.y = 0;
11896 if (partialExtents)
11897 partialExtents->Add(0);
11898 return false;
11899 }
11900
11901 wxRichTextAttr attr(GetAttributes());
11902 context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
11903
11904 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
11905 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
11906 contentRect = wxRect(wxPoint(0,0), imageSize);
11907 GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
11908
11909 wxSize overallSize = marginRect.GetSize();
11910
11911 if (partialExtents)
11912 partialExtents->Add(overallSize.x);
11913
11914 size = overallSize;
11915
11916 return true;
11917 }
11918
11919 // Get the 'natural' size for an object. For an image, it would be the
11920 // image size.
11921 wxTextAttrSize wxRichTextImage::GetNaturalSize() const
11922 {
11923 wxTextAttrSize size;
11924 if (GetImageCache().IsOk())
11925 {
11926 size.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
11927 size.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS);
11928 }
11929 return size;
11930 }
11931
11932
11933 /// Copy
11934 void wxRichTextImage::Copy(const wxRichTextImage& obj)
11935 {
11936 wxRichTextObject::Copy(obj);
11937
11938 m_imageBlock = obj.m_imageBlock;
11939 m_originalImageSize = obj.m_originalImageSize;
11940 }
11941
11942 /// Edit properties via a GUI
11943 bool wxRichTextImage::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
11944 {
11945 wxRichTextObjectPropertiesDialog imageDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Picture Properties"));
11946 imageDlg.SetAttributes(GetAttributes());
11947
11948 if (imageDlg.ShowModal() == wxID_OK)
11949 {
11950 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
11951 // indeterminate in the object.
11952 imageDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
11953 return true;
11954 }
11955 else
11956 return false;
11957 }
11958
11959 /*!
11960 * Utilities
11961 *
11962 */
11963
11964 /// Compare two attribute objects
11965 bool wxTextAttrEq(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
11966 {
11967 return (attr1 == attr2);
11968 }
11969
11970 /// Compare tabs
11971 bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2)
11972 {
11973 if (tabs1.GetCount() != tabs2.GetCount())
11974 return false;
11975
11976 size_t i;
11977 for (i = 0; i < tabs1.GetCount(); i++)
11978 {
11979 if (tabs1[i] != tabs2[i])
11980 return false;
11981 }
11982 return true;
11983 }
11984
11985 bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
11986 {
11987 return destStyle.Apply(style, compareWith);
11988 }
11989
11990 // Remove attributes
11991 bool wxRichTextRemoveStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style)
11992 {
11993 return destStyle.RemoveStyle(style);
11994 }
11995
11996 /// Combine two bitlists, specifying the bits of interest with separate flags.
11997 bool wxRichTextCombineBitlists(int& valueA, int valueB, int& flagsA, int flagsB)
11998 {
11999 return wxRichTextAttr::CombineBitlists(valueA, valueB, flagsA, flagsB);
12000 }
12001
12002 /// Compare two bitlists
12003 bool wxRichTextBitlistsEqPartial(int valueA, int valueB, int flags)
12004 {
12005 return wxRichTextAttr::BitlistsEqPartial(valueA, valueB, flags);
12006 }
12007
12008 /// Split into paragraph and character styles
12009 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr& style, wxRichTextAttr& parStyle, wxRichTextAttr& charStyle)
12010 {
12011 return wxRichTextAttr::SplitParaCharStyles(style, parStyle, charStyle);
12012 }
12013
12014 /// Convert a decimal to Roman numerals
12015 wxString wxRichTextDecimalToRoman(long n)
12016 {
12017 static wxArrayInt decimalNumbers;
12018 static wxArrayString romanNumbers;
12019
12020 // Clean up arrays
12021 if (n == -1)
12022 {
12023 decimalNumbers.Clear();
12024 romanNumbers.Clear();
12025 return wxEmptyString;
12026 }
12027
12028 if (decimalNumbers.GetCount() == 0)
12029 {
12030 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
12031
12032 wxRichTextAddDecRom(1000, wxT("M"));
12033 wxRichTextAddDecRom(900, wxT("CM"));
12034 wxRichTextAddDecRom(500, wxT("D"));
12035 wxRichTextAddDecRom(400, wxT("CD"));
12036 wxRichTextAddDecRom(100, wxT("C"));
12037 wxRichTextAddDecRom(90, wxT("XC"));
12038 wxRichTextAddDecRom(50, wxT("L"));
12039 wxRichTextAddDecRom(40, wxT("XL"));
12040 wxRichTextAddDecRom(10, wxT("X"));
12041 wxRichTextAddDecRom(9, wxT("IX"));
12042 wxRichTextAddDecRom(5, wxT("V"));
12043 wxRichTextAddDecRom(4, wxT("IV"));
12044 wxRichTextAddDecRom(1, wxT("I"));
12045 }
12046
12047 int i = 0;
12048 wxString roman;
12049
12050 while (n > 0 && i < 13)
12051 {
12052 if (n >= decimalNumbers[i])
12053 {
12054 n -= decimalNumbers[i];
12055 roman += romanNumbers[i];
12056 }
12057 else
12058 {
12059 i ++;
12060 }
12061 }
12062 if (roman.IsEmpty())
12063 roman = wxT("0");
12064 return roman;
12065 }
12066
12067 /*!
12068 * wxRichTextFileHandler
12069 * Base class for file handlers
12070 */
12071
12072 IMPLEMENT_CLASS(wxRichTextFileHandler, wxObject)
12073
12074 #if wxUSE_FFILE && wxUSE_STREAMS
12075 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer *buffer, const wxString& filename)
12076 {
12077 wxFFileInputStream stream(filename);
12078 if (stream.IsOk())
12079 return LoadFile(buffer, stream);
12080
12081 return false;
12082 }
12083
12084 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer *buffer, const wxString& filename)
12085 {
12086 wxFFileOutputStream stream(filename);
12087 if (stream.IsOk())
12088 return SaveFile(buffer, stream);
12089
12090 return false;
12091 }
12092 #endif // wxUSE_FFILE && wxUSE_STREAMS
12093
12094 /// Can we handle this filename (if using files)? By default, checks the extension.
12095 bool wxRichTextFileHandler::CanHandle(const wxString& filename) const
12096 {
12097 wxString path, file, ext;
12098 wxFileName::SplitPath(filename, & path, & file, & ext);
12099
12100 return (ext.Lower() == GetExtension());
12101 }
12102
12103 /*!
12104 * wxRichTextTextHandler
12105 * Plain text handler
12106 */
12107
12108 IMPLEMENT_CLASS(wxRichTextPlainTextHandler, wxRichTextFileHandler)
12109
12110 #if wxUSE_STREAMS
12111 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
12112 {
12113 if (!stream.IsOk())
12114 return false;
12115
12116 wxString str;
12117 int lastCh = 0;
12118
12119 while (!stream.Eof())
12120 {
12121 int ch = stream.GetC();
12122
12123 if (!stream.Eof())
12124 {
12125 if (ch == 10 && lastCh != 13)
12126 str += wxT('\n');
12127
12128 if (ch > 0 && ch != 10)
12129 str += wxChar(ch);
12130
12131 lastCh = ch;
12132 }
12133 }
12134
12135 buffer->ResetAndClearCommands();
12136 buffer->Clear();
12137 buffer->AddParagraphs(str);
12138 buffer->UpdateRanges();
12139
12140 return true;
12141 }
12142
12143 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
12144 {
12145 if (!stream.IsOk())
12146 return false;
12147
12148 wxString text = buffer->GetText();
12149
12150 wxString newLine = wxRichTextLineBreakChar;
12151 text.Replace(newLine, wxT("\n"));
12152
12153 wxCharBuffer buf = text.ToAscii();
12154
12155 stream.Write((const char*) buf, text.length());
12156 return true;
12157 }
12158 #endif // wxUSE_STREAMS
12159
12160 /*
12161 * Stores information about an image, in binary in-memory form
12162 */
12163
12164 wxRichTextImageBlock::wxRichTextImageBlock()
12165 {
12166 Init();
12167 }
12168
12169 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock& block):wxObject()
12170 {
12171 Init();
12172 Copy(block);
12173 }
12174
12175 wxRichTextImageBlock::~wxRichTextImageBlock()
12176 {
12177 wxDELETEA(m_data);
12178 }
12179
12180 void wxRichTextImageBlock::Init()
12181 {
12182 m_data = NULL;
12183 m_dataSize = 0;
12184 m_imageType = wxBITMAP_TYPE_INVALID;
12185 }
12186
12187 void wxRichTextImageBlock::Clear()
12188 {
12189 wxDELETEA(m_data);
12190 m_dataSize = 0;
12191 m_imageType = wxBITMAP_TYPE_INVALID;
12192 }
12193
12194
12195 // Load the original image into a memory block.
12196 // If the image is not a JPEG, we must convert it into a JPEG
12197 // to conserve space.
12198 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
12199 // load the image a 2nd time.
12200
12201 bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, wxBitmapType imageType,
12202 wxImage& image, bool convertToJPEG)
12203 {
12204 m_imageType = imageType;
12205
12206 wxString filenameToRead(filename);
12207 bool removeFile = false;
12208
12209 if (imageType == wxBITMAP_TYPE_INVALID)
12210 return false; // Could not determine image type
12211
12212 if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
12213 {
12214 wxString tempFile =
12215 wxFileName::CreateTempFileName(_("image"));
12216
12217 wxASSERT(!tempFile.IsEmpty());
12218
12219 image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
12220 filenameToRead = tempFile;
12221 removeFile = true;
12222
12223 m_imageType = wxBITMAP_TYPE_JPEG;
12224 }
12225 wxFile file;
12226 if (!file.Open(filenameToRead))
12227 return false;
12228
12229 m_dataSize = (size_t) file.Length();
12230 file.Close();
12231
12232 if (m_data)
12233 delete[] m_data;
12234 m_data = ReadBlock(filenameToRead, m_dataSize);
12235
12236 if (removeFile)
12237 wxRemoveFile(filenameToRead);
12238
12239 return (m_data != NULL);
12240 }
12241
12242 // Make an image block from the wxImage in the given
12243 // format.
12244 bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, wxBitmapType imageType, int quality)
12245 {
12246 image.SetOption(wxT("quality"), quality);
12247
12248 if (imageType == wxBITMAP_TYPE_INVALID)
12249 return false; // Could not determine image type
12250
12251 return DoMakeImageBlock(image, imageType);
12252 }
12253
12254 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
12255 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage& image, wxBitmapType imageType)
12256 {
12257 if (imageType == wxBITMAP_TYPE_INVALID)
12258 return false; // Could not determine image type
12259
12260 return DoMakeImageBlock(image, imageType);
12261 }
12262
12263 // Makes the image block
12264 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage& image, wxBitmapType imageType)
12265 {
12266 wxMemoryOutputStream memStream;
12267 if (!image.SaveFile(memStream, imageType))
12268 {
12269 return false;
12270 }
12271
12272 unsigned char* block = new unsigned char[memStream.GetSize()];
12273 if (!block)
12274 return false;
12275
12276 if (m_data)
12277 delete[] m_data;
12278 m_data = block;
12279
12280 m_imageType = imageType;
12281 m_dataSize = memStream.GetSize();
12282
12283 memStream.CopyTo(m_data, m_dataSize);
12284
12285 return (m_data != NULL);
12286 }
12287
12288 // Write to a file
12289 bool wxRichTextImageBlock::Write(const wxString& filename)
12290 {
12291 return WriteBlock(filename, m_data, m_dataSize);
12292 }
12293
12294 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock& block)
12295 {
12296 m_imageType = block.m_imageType;
12297 wxDELETEA(m_data);
12298 m_dataSize = block.m_dataSize;
12299 if (m_dataSize == 0)
12300 return;
12301
12302 m_data = new unsigned char[m_dataSize];
12303 unsigned int i;
12304 for (i = 0; i < m_dataSize; i++)
12305 m_data[i] = block.m_data[i];
12306 }
12307
12308 //// Operators
12309 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock& block)
12310 {
12311 Copy(block);
12312 }
12313
12314 // Load a wxImage from the block
12315 bool wxRichTextImageBlock::Load(wxImage& image)
12316 {
12317 if (!m_data)
12318 return false;
12319
12320 // Read in the image.
12321 #if wxUSE_STREAMS
12322 wxMemoryInputStream mstream(m_data, m_dataSize);
12323 bool success = image.LoadFile(mstream, GetImageType());
12324 #else
12325 wxString tempFile = wxFileName::CreateTempFileName(_("image"));
12326 wxASSERT(!tempFile.IsEmpty());
12327
12328 if (!WriteBlock(tempFile, m_data, m_dataSize))
12329 {
12330 return false;
12331 }
12332 success = image.LoadFile(tempFile, GetImageType());
12333 wxRemoveFile(tempFile);
12334 #endif
12335
12336 return success;
12337 }
12338
12339 // Write data in hex to a stream
12340 bool wxRichTextImageBlock::WriteHex(wxOutputStream& stream)
12341 {
12342 if (m_dataSize == 0)
12343 return true;
12344
12345 int bufSize = 100000;
12346 if (int(2*m_dataSize) < bufSize)
12347 bufSize = 2*m_dataSize;
12348 char* buf = new char[bufSize+1];
12349
12350 int left = m_dataSize;
12351 int n, i, j;
12352 j = 0;
12353 while (left > 0)
12354 {
12355 if (left*2 > bufSize)
12356 {
12357 n = bufSize; left -= (bufSize/2);
12358 }
12359 else
12360 {
12361 n = left*2; left = 0;
12362 }
12363
12364 char* b = buf;
12365 for (i = 0; i < (n/2); i++)
12366 {
12367 wxDecToHex(m_data[j], b, b+1);
12368 b += 2; j ++;
12369 }
12370
12371 buf[n] = 0;
12372 stream.Write((const char*) buf, n);
12373 }
12374 delete[] buf;
12375 return true;
12376 }
12377
12378 // Read data in hex from a stream
12379 bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, wxBitmapType imageType)
12380 {
12381 int dataSize = length/2;
12382
12383 if (m_data)
12384 delete[] m_data;
12385
12386 // create a null terminated temporary string:
12387 char str[3];
12388 str[2] = '\0';
12389
12390 m_data = new unsigned char[dataSize];
12391 int i;
12392 for (i = 0; i < dataSize; i ++)
12393 {
12394 str[0] = (char)stream.GetC();
12395 str[1] = (char)stream.GetC();
12396
12397 m_data[i] = (unsigned char)wxHexToDec(str);
12398 }
12399
12400 m_dataSize = dataSize;
12401 m_imageType = imageType;
12402
12403 return true;
12404 }
12405
12406 // Allocate and read from stream as a block of memory
12407 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream& stream, size_t size)
12408 {
12409 unsigned char* block = new unsigned char[size];
12410 if (!block)
12411 return NULL;
12412
12413 stream.Read(block, size);
12414
12415 return block;
12416 }
12417
12418 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString& filename, size_t size)
12419 {
12420 wxFileInputStream stream(filename);
12421 if (!stream.IsOk())
12422 return NULL;
12423
12424 return ReadBlock(stream, size);
12425 }
12426
12427 // Write memory block to stream
12428 bool wxRichTextImageBlock::WriteBlock(wxOutputStream& stream, unsigned char* block, size_t size)
12429 {
12430 stream.Write((void*) block, size);
12431 return stream.IsOk();
12432
12433 }
12434
12435 // Write memory block to file
12436 bool wxRichTextImageBlock::WriteBlock(const wxString& filename, unsigned char* block, size_t size)
12437 {
12438 wxFileOutputStream outStream(filename);
12439 if (!outStream.IsOk())
12440 return false;
12441
12442 return WriteBlock(outStream, block, size);
12443 }
12444
12445 // Gets the extension for the block's type
12446 wxString wxRichTextImageBlock::GetExtension() const
12447 {
12448 wxImageHandler* handler = wxImage::FindHandler(GetImageType());
12449 if (handler)
12450 return handler->GetExtension();
12451 else
12452 return wxEmptyString;
12453 }
12454
12455 #if wxUSE_DATAOBJ
12456
12457 /*!
12458 * The data object for a wxRichTextBuffer
12459 */
12460
12461 const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxRichText");
12462
12463 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer)
12464 {
12465 m_richTextBuffer = richTextBuffer;
12466
12467 // this string should uniquely identify our format, but is otherwise
12468 // arbitrary
12469 m_formatRichTextBuffer.SetId(GetRichTextBufferFormatId());
12470
12471 SetFormat(m_formatRichTextBuffer);
12472 }
12473
12474 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
12475 {
12476 delete m_richTextBuffer;
12477 }
12478
12479 // after a call to this function, the richTextBuffer is owned by the caller and it
12480 // is responsible for deleting it!
12481 wxRichTextBuffer* wxRichTextBufferDataObject::GetRichTextBuffer()
12482 {
12483 wxRichTextBuffer* richTextBuffer = m_richTextBuffer;
12484 m_richTextBuffer = NULL;
12485
12486 return richTextBuffer;
12487 }
12488
12489 wxDataFormat wxRichTextBufferDataObject::GetPreferredFormat(Direction WXUNUSED(dir)) const
12490 {
12491 return m_formatRichTextBuffer;
12492 }
12493
12494 size_t wxRichTextBufferDataObject::GetDataSize() const
12495 {
12496 if (!m_richTextBuffer)
12497 return 0;
12498
12499 wxString bufXML;
12500
12501 {
12502 wxStringOutputStream stream(& bufXML);
12503 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
12504 {
12505 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
12506 return 0;
12507 }
12508 }
12509
12510 #if wxUSE_UNICODE
12511 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
12512 return strlen(buffer) + 1;
12513 #else
12514 return bufXML.Length()+1;
12515 #endif
12516 }
12517
12518 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf) const
12519 {
12520 if (!pBuf || !m_richTextBuffer)
12521 return false;
12522
12523 wxString bufXML;
12524
12525 {
12526 wxStringOutputStream stream(& bufXML);
12527 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
12528 {
12529 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
12530 return 0;
12531 }
12532 }
12533
12534 #if wxUSE_UNICODE
12535 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
12536 size_t len = strlen(buffer);
12537 memcpy((char*) pBuf, (const char*) buffer, len);
12538 ((char*) pBuf)[len] = 0;
12539 #else
12540 size_t len = bufXML.Length();
12541 memcpy((char*) pBuf, (const char*) bufXML.c_str(), len);
12542 ((char*) pBuf)[len] = 0;
12543 #endif
12544
12545 return true;
12546 }
12547
12548 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len), const void *buf)
12549 {
12550 wxDELETE(m_richTextBuffer);
12551
12552 wxString bufXML((const char*) buf, wxConvUTF8);
12553
12554 m_richTextBuffer = new wxRichTextBuffer;
12555
12556 wxStringInputStream stream(bufXML);
12557 if (!m_richTextBuffer->LoadFile(stream, wxRICHTEXT_TYPE_XML))
12558 {
12559 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
12560
12561 wxDELETE(m_richTextBuffer);
12562
12563 return false;
12564 }
12565 return true;
12566 }
12567
12568 #endif
12569 // wxUSE_DATAOBJ
12570
12571
12572 /*
12573 * wxRichTextFontTable
12574 * Manages quick access to a pool of fonts for rendering rich text
12575 */
12576
12577 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont, wxRichTextFontTableHashMap, class WXDLLIMPEXP_RICHTEXT);
12578
12579 class wxRichTextFontTableData: public wxObjectRefData
12580 {
12581 public:
12582 wxRichTextFontTableData() {}
12583
12584 wxFont FindFont(const wxRichTextAttr& fontSpec, double fontScale);
12585
12586 wxRichTextFontTableHashMap m_hashMap;
12587 };
12588
12589 wxFont wxRichTextFontTableData::FindFont(const wxRichTextAttr& fontSpec, double fontScale)
12590 {
12591 wxString facename(fontSpec.GetFontFaceName());
12592
12593 int fontSize = fontSpec.GetFontSize();
12594 if (fontScale != 1.0)
12595 fontSize = (int) ((double(fontSize) * fontScale) + 0.5);
12596
12597 wxString units;
12598 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
12599 units = wxT("px");
12600 else
12601 units = wxT("pt");
12602 wxString spec = wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
12603 fontSize, units.c_str(), fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), (int) fontSpec.GetFontUnderlined(), (int) fontSpec.GetFontStrikethrough(),
12604 facename.c_str(), (int) fontSpec.GetFontEncoding());
12605
12606 wxRichTextFontTableHashMap::iterator entry = m_hashMap.find(spec);
12607 if ( entry == m_hashMap.end() )
12608 {
12609 if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
12610 {
12611 wxFont font(wxSize(0, fontSize), wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename);
12612 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
12613 font.SetStrikethrough(true);
12614 m_hashMap[spec] = font;
12615 return font;
12616 }
12617 else
12618 {
12619 wxFont font(fontSize, wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename.c_str());
12620 if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
12621 font.SetStrikethrough(true);
12622
12623 m_hashMap[spec] = font;
12624 return font;
12625 }
12626 }
12627 else
12628 {
12629 return entry->second;
12630 }
12631 }
12632
12633 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable, wxObject)
12634
12635 wxRichTextFontTable::wxRichTextFontTable()
12636 {
12637 m_refData = new wxRichTextFontTableData;
12638 m_fontScale = 1.0;
12639 }
12640
12641 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table)
12642 : wxObject()
12643 {
12644 (*this) = table;
12645 }
12646
12647 wxRichTextFontTable::~wxRichTextFontTable()
12648 {
12649 UnRef();
12650 }
12651
12652 bool wxRichTextFontTable::operator == (const wxRichTextFontTable& table) const
12653 {
12654 return (m_refData == table.m_refData);
12655 }
12656
12657 void wxRichTextFontTable::operator= (const wxRichTextFontTable& table)
12658 {
12659 Ref(table);
12660 m_fontScale = table.m_fontScale;
12661 }
12662
12663 wxFont wxRichTextFontTable::FindFont(const wxRichTextAttr& fontSpec)
12664 {
12665 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
12666 if (data)
12667 return data->FindFont(fontSpec, m_fontScale);
12668 else
12669 return wxFont();
12670 }
12671
12672 void wxRichTextFontTable::Clear()
12673 {
12674 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
12675 if (data)
12676 data->m_hashMap.clear();
12677 }
12678
12679 void wxRichTextFontTable::SetFontScale(double fontScale)
12680 {
12681 if (fontScale != m_fontScale)
12682 Clear();
12683 m_fontScale = fontScale;
12684 }
12685
12686 // wxTextBoxAttr
12687
12688 void wxTextBoxAttr::Reset()
12689 {
12690 m_flags = 0;
12691 m_floatMode = wxTEXT_BOX_ATTR_FLOAT_NONE;
12692 m_clearMode = wxTEXT_BOX_ATTR_CLEAR_NONE;
12693 m_collapseMode = wxTEXT_BOX_ATTR_COLLAPSE_NONE;
12694 m_verticalAlignment = wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE;
12695 m_boxStyleName = wxEmptyString;
12696
12697 m_margins.Reset();
12698 m_padding.Reset();
12699 m_position.Reset();
12700
12701 m_size.Reset();
12702 m_minSize.Reset();
12703 m_maxSize.Reset();
12704
12705 m_border.Reset();
12706 m_outline.Reset();
12707 }
12708
12709 // Equality test
12710 bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const
12711 {
12712 return (
12713 m_flags == attr.m_flags &&
12714 m_floatMode == attr.m_floatMode &&
12715 m_clearMode == attr.m_clearMode &&
12716 m_collapseMode == attr.m_collapseMode &&
12717 m_verticalAlignment == attr.m_verticalAlignment &&
12718
12719 m_margins == attr.m_margins &&
12720 m_padding == attr.m_padding &&
12721 m_position == attr.m_position &&
12722
12723 m_size == attr.m_size &&
12724 m_minSize == attr.m_minSize &&
12725 m_maxSize == attr.m_maxSize &&
12726
12727 m_border == attr.m_border &&
12728 m_outline == attr.m_outline &&
12729
12730 m_boxStyleName == attr.m_boxStyleName
12731 );
12732 }
12733
12734 // Partial equality test
12735 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr, bool weakTest) const
12736 {
12737 if (!weakTest &&
12738 ((!HasFloatMode() && attr.HasFloatMode()) ||
12739 (!HasClearMode() && attr.HasClearMode()) ||
12740 (!HasCollapseBorders() && attr.HasCollapseBorders()) ||
12741 (!HasVerticalAlignment() && attr.HasVerticalAlignment()) ||
12742 (!HasBoxStyleName() && attr.HasBoxStyleName())))
12743 {
12744 return false;
12745 }
12746 if (attr.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr.GetFloatMode()))
12747 return false;
12748
12749 if (attr.HasClearMode() && HasClearMode() && (GetClearMode() != attr.GetClearMode()))
12750 return false;
12751
12752 if (attr.HasCollapseBorders() && HasCollapseBorders() && (attr.GetCollapseBorders() != GetCollapseBorders()))
12753 return false;
12754
12755 if (attr.HasVerticalAlignment() && HasVerticalAlignment() && (attr.GetVerticalAlignment() != GetVerticalAlignment()))
12756 return false;
12757
12758 if (attr.HasBoxStyleName() && HasBoxStyleName() && (attr.GetBoxStyleName() != GetBoxStyleName()))
12759 return false;
12760
12761 // Position
12762
12763 if (!m_position.EqPartial(attr.m_position, weakTest))
12764 return false;
12765
12766 // Size
12767
12768 if (!m_size.EqPartial(attr.m_size, weakTest))
12769 return false;
12770 if (!m_minSize.EqPartial(attr.m_minSize, weakTest))
12771 return false;
12772 if (!m_maxSize.EqPartial(attr.m_maxSize, weakTest))
12773 return false;
12774
12775 // Margins
12776
12777 if (!m_margins.EqPartial(attr.m_margins, weakTest))
12778 return false;
12779
12780 // Padding
12781
12782 if (!m_padding.EqPartial(attr.m_padding, weakTest))
12783 return false;
12784
12785 // Border
12786
12787 if (!GetBorder().EqPartial(attr.GetBorder(), weakTest))
12788 return false;
12789
12790 // Outline
12791
12792 if (!GetOutline().EqPartial(attr.GetOutline(), weakTest))
12793 return false;
12794
12795 return true;
12796 }
12797
12798 // Merges the given attributes. If compareWith
12799 // is non-NULL, then it will be used to mask out those attributes that are the same in style
12800 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
12801 bool wxTextBoxAttr::Apply(const wxTextBoxAttr& attr, const wxTextBoxAttr* compareWith)
12802 {
12803 if (attr.HasFloatMode())
12804 {
12805 if (!(compareWith && compareWith->HasFloatMode() && compareWith->GetFloatMode() == attr.GetFloatMode()))
12806 SetFloatMode(attr.GetFloatMode());
12807 }
12808
12809 if (attr.HasClearMode())
12810 {
12811 if (!(compareWith && compareWith->HasClearMode() && compareWith->GetClearMode() == attr.GetClearMode()))
12812 SetClearMode(attr.GetClearMode());
12813 }
12814
12815 if (attr.HasCollapseBorders())
12816 {
12817 if (!(compareWith && compareWith->HasCollapseBorders() && compareWith->GetCollapseBorders() == attr.GetCollapseBorders()))
12818 SetCollapseBorders(attr.GetCollapseBorders());
12819 }
12820
12821 if (attr.HasVerticalAlignment())
12822 {
12823 if (!(compareWith && compareWith->HasVerticalAlignment() && compareWith->GetVerticalAlignment() == attr.GetVerticalAlignment()))
12824 SetVerticalAlignment(attr.GetVerticalAlignment());
12825 }
12826
12827 if (attr.HasBoxStyleName())
12828 {
12829 if (!(compareWith && compareWith->HasBoxStyleName() && compareWith->GetBoxStyleName() == attr.GetBoxStyleName()))
12830 SetBoxStyleName(attr.GetBoxStyleName());
12831 }
12832
12833 m_margins.Apply(attr.m_margins, compareWith ? (& attr.m_margins) : (const wxTextAttrDimensions*) NULL);
12834 m_padding.Apply(attr.m_padding, compareWith ? (& attr.m_padding) : (const wxTextAttrDimensions*) NULL);
12835 m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextAttrDimensions*) NULL);
12836
12837 m_size.Apply(attr.m_size, compareWith ? (& attr.m_size) : (const wxTextAttrSize*) NULL);
12838 m_minSize.Apply(attr.m_minSize, compareWith ? (& attr.m_minSize) : (const wxTextAttrSize*) NULL);
12839 m_maxSize.Apply(attr.m_maxSize, compareWith ? (& attr.m_maxSize) : (const wxTextAttrSize*) NULL);
12840
12841 m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextAttrBorders*) NULL);
12842 m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextAttrBorders*) NULL);
12843
12844 return true;
12845 }
12846
12847 // Remove specified attributes from this object
12848 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr& attr)
12849 {
12850 if (attr.HasFloatMode())
12851 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
12852
12853 if (attr.HasClearMode())
12854 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
12855
12856 if (attr.HasCollapseBorders())
12857 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12858
12859 if (attr.HasVerticalAlignment())
12860 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12861
12862 if (attr.HasBoxStyleName())
12863 {
12864 SetBoxStyleName(wxEmptyString);
12865 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12866 }
12867
12868 m_margins.RemoveStyle(attr.m_margins);
12869 m_padding.RemoveStyle(attr.m_padding);
12870 m_position.RemoveStyle(attr.m_position);
12871
12872 m_size.RemoveStyle(attr.m_size);
12873 m_minSize.RemoveStyle(attr.m_minSize);
12874 m_maxSize.RemoveStyle(attr.m_maxSize);
12875
12876 m_border.RemoveStyle(attr.m_border);
12877 m_outline.RemoveStyle(attr.m_outline);
12878
12879 return true;
12880 }
12881
12882 // Collects the attributes that are common to a range of content, building up a note of
12883 // which attributes are absent in some objects and which clash in some objects.
12884 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBoxAttr& clashingAttr, wxTextBoxAttr& absentAttr)
12885 {
12886 if (attr.HasFloatMode())
12887 {
12888 if (!clashingAttr.HasFloatMode() && !absentAttr.HasFloatMode())
12889 {
12890 if (HasFloatMode())
12891 {
12892 if (GetFloatMode() != attr.GetFloatMode())
12893 {
12894 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
12895 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
12896 }
12897 }
12898 else
12899 SetFloatMode(attr.GetFloatMode());
12900 }
12901 }
12902 else
12903 absentAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
12904
12905 if (attr.HasClearMode())
12906 {
12907 if (!clashingAttr.HasClearMode() && !absentAttr.HasClearMode())
12908 {
12909 if (HasClearMode())
12910 {
12911 if (GetClearMode() != attr.GetClearMode())
12912 {
12913 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
12914 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
12915 }
12916 }
12917 else
12918 SetClearMode(attr.GetClearMode());
12919 }
12920 }
12921 else
12922 absentAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
12923
12924 if (attr.HasCollapseBorders())
12925 {
12926 if (!clashingAttr.HasCollapseBorders() && !absentAttr.HasCollapseBorders())
12927 {
12928 if (HasCollapseBorders())
12929 {
12930 if (GetCollapseBorders() != attr.GetCollapseBorders())
12931 {
12932 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12933 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12934 }
12935 }
12936 else
12937 SetCollapseBorders(attr.GetCollapseBorders());
12938 }
12939 }
12940 else
12941 absentAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
12942
12943 if (attr.HasVerticalAlignment())
12944 {
12945 if (!clashingAttr.HasVerticalAlignment() && !absentAttr.HasVerticalAlignment())
12946 {
12947 if (HasVerticalAlignment())
12948 {
12949 if (GetVerticalAlignment() != attr.GetVerticalAlignment())
12950 {
12951 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12952 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12953 }
12954 }
12955 else
12956 SetVerticalAlignment(attr.GetVerticalAlignment());
12957 }
12958 }
12959 else
12960 absentAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
12961
12962 if (attr.HasBoxStyleName())
12963 {
12964 if (!clashingAttr.HasBoxStyleName() && !absentAttr.HasBoxStyleName())
12965 {
12966 if (HasBoxStyleName())
12967 {
12968 if (GetBoxStyleName() != attr.GetBoxStyleName())
12969 {
12970 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12971 RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12972 }
12973 }
12974 else
12975 SetBoxStyleName(attr.GetBoxStyleName());
12976 }
12977 }
12978 else
12979 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
12980
12981 m_margins.CollectCommonAttributes(attr.m_margins, clashingAttr.m_margins, absentAttr.m_margins);
12982 m_padding.CollectCommonAttributes(attr.m_padding, clashingAttr.m_padding, absentAttr.m_padding);
12983 m_position.CollectCommonAttributes(attr.m_position, clashingAttr.m_position, absentAttr.m_position);
12984
12985 m_size.CollectCommonAttributes(attr.m_size, clashingAttr.m_size, absentAttr.m_size);
12986 m_minSize.CollectCommonAttributes(attr.m_minSize, clashingAttr.m_minSize, absentAttr.m_minSize);
12987 m_maxSize.CollectCommonAttributes(attr.m_maxSize, clashingAttr.m_maxSize, absentAttr.m_maxSize);
12988
12989 m_border.CollectCommonAttributes(attr.m_border, clashingAttr.m_border, absentAttr.m_border);
12990 m_outline.CollectCommonAttributes(attr.m_outline, clashingAttr.m_outline, absentAttr.m_outline);
12991 }
12992
12993 bool wxTextBoxAttr::IsDefault() const
12994 {
12995 return GetFlags() == 0 && !m_border.IsValid() && !m_outline.IsValid() &&
12996 !m_size.IsValid() && !m_minSize.IsValid() && !m_maxSize.IsValid() &&
12997 !m_position.IsValid() && !m_padding.IsValid() && !m_margins.IsValid();
12998 }
12999
13000 // wxRichTextAttr
13001
13002 void wxRichTextAttr::Copy(const wxRichTextAttr& attr)
13003 {
13004 wxTextAttr::Copy(attr);
13005
13006 m_textBoxAttr = attr.m_textBoxAttr;
13007 }
13008
13009 bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const
13010 {
13011 if (!(wxTextAttr::operator==(attr)))
13012 return false;
13013
13014 return (m_textBoxAttr == attr.m_textBoxAttr);
13015 }
13016
13017 // Partial equality test
13018 bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr, bool weakTest) const
13019 {
13020 if (!(wxTextAttr::EqPartial(attr, weakTest)))
13021 return false;
13022
13023 return m_textBoxAttr.EqPartial(attr.m_textBoxAttr, weakTest);
13024 }
13025
13026 // Merges the given attributes. If compareWith
13027 // is non-NULL, then it will be used to mask out those attributes that are the same in style
13028 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
13029 bool wxRichTextAttr::Apply(const wxRichTextAttr& style, const wxRichTextAttr* compareWith)
13030 {
13031 wxTextAttr::Apply(style, compareWith);
13032
13033 return m_textBoxAttr.Apply(style.m_textBoxAttr, compareWith ? (& compareWith->m_textBoxAttr) : (const wxTextBoxAttr*) NULL);
13034 }
13035
13036 // Remove specified attributes from this object
13037 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr& attr)
13038 {
13039 wxTextAttr::RemoveStyle(*this, attr);
13040
13041 return m_textBoxAttr.RemoveStyle(attr.m_textBoxAttr);
13042 }
13043
13044 // Collects the attributes that are common to a range of content, building up a note of
13045 // which attributes are absent in some objects and which clash in some objects.
13046 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr& attr, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
13047 {
13048 wxTextAttrCollectCommonAttributes(*this, attr, clashingAttr, absentAttr);
13049
13050 m_textBoxAttr.CollectCommonAttributes(attr.m_textBoxAttr, clashingAttr.m_textBoxAttr, absentAttr.m_textBoxAttr);
13051 }
13052
13053 // Partial equality test
13054 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border, bool weakTest) const
13055 {
13056 if (!weakTest &&
13057 ((!HasStyle() && border.HasStyle()) ||
13058 (!HasColour() && border.HasColour()) ||
13059 (!HasWidth() && border.HasWidth())))
13060 {
13061 return false;
13062 }
13063
13064 if (border.HasStyle() && HasStyle() && (border.GetStyle() != GetStyle()))
13065 return false;
13066
13067 if (border.HasColour() && HasColour() && (border.GetColourLong() != GetColourLong()))
13068 return false;
13069
13070 if (border.HasWidth() && HasWidth() && !(border.GetWidth() == GetWidth()))
13071 return false;
13072
13073 return true;
13074 }
13075
13076 // Apply border to 'this', but not if the same as compareWith
13077 bool wxTextAttrBorder::Apply(const wxTextAttrBorder& border, const wxTextAttrBorder* compareWith)
13078 {
13079 if (border.HasStyle())
13080 {
13081 if (!(compareWith && (border.GetStyle() == compareWith->GetStyle())))
13082 SetStyle(border.GetStyle());
13083 }
13084 if (border.HasColour())
13085 {
13086 if (!(compareWith && (border.GetColourLong() == compareWith->GetColourLong())))
13087 SetColour(border.GetColourLong());
13088 }
13089 if (border.HasWidth())
13090 {
13091 if (!(compareWith && (border.GetWidth() == compareWith->GetWidth())))
13092 SetWidth(border.GetWidth());
13093 }
13094
13095 return true;
13096 }
13097
13098 // Remove specified attributes from this object
13099 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder& attr)
13100 {
13101 if (attr.HasStyle() && HasStyle())
13102 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE);
13103 if (attr.HasColour() && HasColour())
13104 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR);
13105 if (attr.HasWidth() && HasWidth())
13106 m_borderWidth.Reset();
13107
13108 return true;
13109 }
13110
13111 // Collects the attributes that are common to a range of content, building up a note of
13112 // which attributes are absent in some objects and which clash in some objects.
13113 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder& attr, wxTextAttrBorder& clashingAttr, wxTextAttrBorder& absentAttr)
13114 {
13115 if (attr.HasStyle())
13116 {
13117 if (!clashingAttr.HasStyle() && !absentAttr.HasStyle())
13118 {
13119 if (HasStyle())
13120 {
13121 if (GetStyle() != attr.GetStyle())
13122 {
13123 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
13124 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
13125 }
13126 }
13127 else
13128 SetStyle(attr.GetStyle());
13129 }
13130 }
13131 else
13132 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
13133
13134 if (attr.HasColour())
13135 {
13136 if (!clashingAttr.HasColour() && !absentAttr.HasColour())
13137 {
13138 if (HasColour())
13139 {
13140 if (GetColour() != attr.GetColour())
13141 {
13142 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
13143 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
13144 }
13145 }
13146 else
13147 SetColour(attr.GetColourLong());
13148 }
13149 }
13150 else
13151 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
13152
13153 m_borderWidth.CollectCommonAttributes(attr.m_borderWidth, clashingAttr.m_borderWidth, absentAttr.m_borderWidth);
13154 }
13155
13156 // Partial equality test
13157 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders, bool weakTest) const
13158 {
13159 return m_left.EqPartial(borders.m_left, weakTest) && m_right.EqPartial(borders.m_right, weakTest) &&
13160 m_top.EqPartial(borders.m_top, weakTest) && m_bottom.EqPartial(borders.m_bottom, weakTest);
13161 }
13162
13163 // Apply border to 'this', but not if the same as compareWith
13164 bool wxTextAttrBorders::Apply(const wxTextAttrBorders& borders, const wxTextAttrBorders* compareWith)
13165 {
13166 m_left.Apply(borders.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrBorder*) NULL);
13167 m_right.Apply(borders.m_right, compareWith ? (& compareWith->m_right) : (const wxTextAttrBorder*) NULL);
13168 m_top.Apply(borders.m_top, compareWith ? (& compareWith->m_top) : (const wxTextAttrBorder*) NULL);
13169 m_bottom.Apply(borders.m_bottom, compareWith ? (& compareWith->m_bottom) : (const wxTextAttrBorder*) NULL);
13170 return true;
13171 }
13172
13173 // Remove specified attributes from this object
13174 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders& attr)
13175 {
13176 m_left.RemoveStyle(attr.m_left);
13177 m_right.RemoveStyle(attr.m_right);
13178 m_top.RemoveStyle(attr.m_top);
13179 m_bottom.RemoveStyle(attr.m_bottom);
13180 return true;
13181 }
13182
13183 // Collects the attributes that are common to a range of content, building up a note of
13184 // which attributes are absent in some objects and which clash in some objects.
13185 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders& attr, wxTextAttrBorders& clashingAttr, wxTextAttrBorders& absentAttr)
13186 {
13187 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
13188 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
13189 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
13190 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
13191 }
13192
13193 // Set style of all borders
13194 void wxTextAttrBorders::SetStyle(int style)
13195 {
13196 m_left.SetStyle(style);
13197 m_right.SetStyle(style);
13198 m_top.SetStyle(style);
13199 m_bottom.SetStyle(style);
13200 }
13201
13202 // Set colour of all borders
13203 void wxTextAttrBorders::SetColour(unsigned long colour)
13204 {
13205 m_left.SetColour(colour);
13206 m_right.SetColour(colour);
13207 m_top.SetColour(colour);
13208 m_bottom.SetColour(colour);
13209 }
13210
13211 void wxTextAttrBorders::SetColour(const wxColour& colour)
13212 {
13213 m_left.SetColour(colour);
13214 m_right.SetColour(colour);
13215 m_top.SetColour(colour);
13216 m_bottom.SetColour(colour);
13217 }
13218
13219 // Set width of all borders
13220 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension& width)
13221 {
13222 m_left.SetWidth(width);
13223 m_right.SetWidth(width);
13224 m_top.SetWidth(width);
13225 m_bottom.SetWidth(width);
13226 }
13227
13228 // Partial equality test
13229 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension& dim, bool weakTest) const
13230 {
13231 if (!weakTest && !IsValid() && dim.IsValid())
13232 return false;
13233
13234 if (dim.IsValid() && IsValid() && !((*this) == dim))
13235 return false;
13236 else
13237 return true;
13238 }
13239
13240 bool wxTextAttrDimension::Apply(const wxTextAttrDimension& dim, const wxTextAttrDimension* compareWith)
13241 {
13242 if (dim.IsValid())
13243 {
13244 if (!(compareWith && dim == (*compareWith)))
13245 (*this) = dim;
13246 }
13247
13248 return true;
13249 }
13250
13251 // Collects the attributes that are common to a range of content, building up a note of
13252 // which attributes are absent in some objects and which clash in some objects.
13253 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension& attr, wxTextAttrDimension& clashingAttr, wxTextAttrDimension& absentAttr)
13254 {
13255 if (attr.IsValid())
13256 {
13257 if (!clashingAttr.IsValid() && !absentAttr.IsValid())
13258 {
13259 if (IsValid())
13260 {
13261 if (!((*this) == attr))
13262 {
13263 clashingAttr.SetValid(true);
13264 SetValid(false);
13265 }
13266 }
13267 else
13268 (*this) = attr;
13269 }
13270 }
13271 else
13272 absentAttr.SetValid(true);
13273 }
13274
13275 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC& dc, double scale, const wxSize& parentSize)
13276 {
13277 m_ppi = dc.GetPPI().x; m_scale = scale; m_parentSize = parentSize;
13278 }
13279
13280 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi, double scale, const wxSize& parentSize)
13281 {
13282 m_ppi = ppi; m_scale = scale; m_parentSize = parentSize;
13283 }
13284
13285 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units) const
13286 {
13287 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi, units, m_scale);
13288 }
13289
13290 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels) const
13291 {
13292 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi, pixels, m_scale);
13293 }
13294
13295 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension& dim, int direction) const
13296 {
13297 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
13298 return ConvertTenthsMMToPixels(dim.GetValue());
13299 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
13300 return dim.GetValue();
13301 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
13302 {
13303 wxASSERT(m_parentSize != wxDefaultSize);
13304 if (direction == wxHORIZONTAL)
13305 return (int) (double(m_parentSize.x) * double(dim.GetValue()) / 100.0);
13306 else
13307 return (int) (double(m_parentSize.y) * double(dim.GetValue()) / 100.0);
13308 }
13309 else
13310 {
13311 wxASSERT(false);
13312 return 0;
13313 }
13314 }
13315
13316 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension& dim) const
13317 {
13318 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
13319 return dim.GetValue();
13320 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
13321 return ConvertPixelsToTenthsMM(dim.GetValue());
13322 else
13323 {
13324 wxASSERT(false);
13325 return 0;
13326 }
13327 }
13328
13329 // Partial equality test
13330 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims, bool weakTest) const
13331 {
13332 if (!m_left.EqPartial(dims.m_left, weakTest))
13333 return false;
13334
13335 if (!m_right.EqPartial(dims.m_right, weakTest))
13336 return false;
13337
13338 if (!m_top.EqPartial(dims.m_top, weakTest))
13339 return false;
13340
13341 if (!m_bottom.EqPartial(dims.m_bottom, weakTest))
13342 return false;
13343
13344 return true;
13345 }
13346
13347 // Apply border to 'this', but not if the same as compareWith
13348 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions& dims, const wxTextAttrDimensions* compareWith)
13349 {
13350 m_left.Apply(dims.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrDimension*) NULL);
13351 m_right.Apply(dims.m_right, compareWith ? (& compareWith->m_right): (const wxTextAttrDimension*) NULL);
13352 m_top.Apply(dims.m_top, compareWith ? (& compareWith->m_top): (const wxTextAttrDimension*) NULL);
13353 m_bottom.Apply(dims.m_bottom, compareWith ? (& compareWith->m_bottom): (const wxTextAttrDimension*) NULL);
13354
13355 return true;
13356 }
13357
13358 // Remove specified attributes from this object
13359 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions& attr)
13360 {
13361 if (attr.m_left.IsValid())
13362 m_left.Reset();
13363 if (attr.m_right.IsValid())
13364 m_right.Reset();
13365 if (attr.m_top.IsValid())
13366 m_top.Reset();
13367 if (attr.m_bottom.IsValid())
13368 m_bottom.Reset();
13369
13370 return true;
13371 }
13372
13373 // Collects the attributes that are common to a range of content, building up a note of
13374 // which attributes are absent in some objects and which clash in some objects.
13375 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions& attr, wxTextAttrDimensions& clashingAttr, wxTextAttrDimensions& absentAttr)
13376 {
13377 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
13378 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
13379 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
13380 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
13381 }
13382
13383 // Partial equality test
13384 bool wxTextAttrSize::EqPartial(const wxTextAttrSize& size, bool weakTest) const
13385 {
13386 if (!m_width.EqPartial(size.m_width, weakTest))
13387 return false;
13388
13389 if (!m_height.EqPartial(size.m_height, weakTest))
13390 return false;
13391
13392 return true;
13393 }
13394
13395 // Apply border to 'this', but not if the same as compareWith
13396 bool wxTextAttrSize::Apply(const wxTextAttrSize& size, const wxTextAttrSize* compareWith)
13397 {
13398 m_width.Apply(size.m_width, compareWith ? (& compareWith->m_width) : (const wxTextAttrDimension*) NULL);
13399 m_height.Apply(size.m_height, compareWith ? (& compareWith->m_height): (const wxTextAttrDimension*) NULL);
13400
13401 return true;
13402 }
13403
13404 // Remove specified attributes from this object
13405 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize& attr)
13406 {
13407 if (attr.m_width.IsValid())
13408 m_width.Reset();
13409 if (attr.m_height.IsValid())
13410 m_height.Reset();
13411
13412 return true;
13413 }
13414
13415 // Collects the attributes that are common to a range of content, building up a note of
13416 // which attributes are absent in some objects and which clash in some objects.
13417 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize& attr, wxTextAttrSize& clashingAttr, wxTextAttrSize& absentAttr)
13418 {
13419 m_width.CollectCommonAttributes(attr.m_width, clashingAttr.m_width, absentAttr.m_width);
13420 m_height.CollectCommonAttributes(attr.m_height, clashingAttr.m_height, absentAttr.m_height);
13421 }
13422
13423 // Collects the attributes that are common to a range of content, building up a note of
13424 // which attributes are absent in some objects and which clash in some objects.
13425 void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAttr& attr, wxTextAttr& clashingAttr, wxTextAttr& absentAttr)
13426 {
13427 absentAttr.SetFlags(absentAttr.GetFlags() | (~attr.GetFlags() & wxTEXT_ATTR_ALL));
13428 absentAttr.SetTextEffectFlags(absentAttr.GetTextEffectFlags() | (~attr.GetTextEffectFlags() & 0xFFFF));
13429
13430 long forbiddenFlags = clashingAttr.GetFlags()|absentAttr.GetFlags();
13431
13432 // If different font size units are being used, this is a clash.
13433 if (((attr.GetFlags() & wxTEXT_ATTR_FONT_SIZE) | (currentStyle.GetFlags() & wxTEXT_ATTR_FONT_SIZE)) == wxTEXT_ATTR_FONT_SIZE)
13434 {
13435 currentStyle.SetFontSize(0);
13436 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE);
13437 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE);
13438 }
13439 else
13440 {
13441 if (attr.HasFontPointSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_POINT_SIZE))
13442 {
13443 if (currentStyle.HasFontPointSize())
13444 {
13445 if (currentStyle.GetFontSize() != attr.GetFontSize())
13446 {
13447 // Clash of attr - mark as such
13448 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
13449 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
13450 }
13451 }
13452 else
13453 currentStyle.SetFontSize(attr.GetFontSize());
13454 }
13455 else if (!attr.HasFontPointSize() && currentStyle.HasFontPointSize())
13456 {
13457 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
13458 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
13459 }
13460
13461 if (attr.HasFontPixelSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_PIXEL_SIZE))
13462 {
13463 if (currentStyle.HasFontPixelSize())
13464 {
13465 if (currentStyle.GetFontSize() != attr.GetFontSize())
13466 {
13467 // Clash of attr - mark as such
13468 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
13469 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
13470 }
13471 }
13472 else
13473 currentStyle.SetFontPixelSize(attr.GetFontSize());
13474 }
13475 else if (!attr.HasFontPixelSize() && currentStyle.HasFontPixelSize())
13476 {
13477 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
13478 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
13479 }
13480 }
13481
13482 if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC))
13483 {
13484 if (currentStyle.HasFontItalic())
13485 {
13486 if (currentStyle.GetFontStyle() != attr.GetFontStyle())
13487 {
13488 // Clash of attr - mark as such
13489 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
13490 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
13491 }
13492 }
13493 else
13494 currentStyle.SetFontStyle(attr.GetFontStyle());
13495 }
13496 else if (!attr.HasFontItalic() && currentStyle.HasFontItalic())
13497 {
13498 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
13499 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
13500 }
13501
13502 if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY))
13503 {
13504 if (currentStyle.HasFontFamily())
13505 {
13506 if (currentStyle.GetFontFamily() != attr.GetFontFamily())
13507 {
13508 // Clash of attr - mark as such
13509 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
13510 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
13511 }
13512 }
13513 else
13514 currentStyle.SetFontFamily(attr.GetFontFamily());
13515 }
13516 else if (!attr.HasFontFamily() && currentStyle.HasFontFamily())
13517 {
13518 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
13519 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
13520 }
13521
13522 if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT))
13523 {
13524 if (currentStyle.HasFontWeight())
13525 {
13526 if (currentStyle.GetFontWeight() != attr.GetFontWeight())
13527 {
13528 // Clash of attr - mark as such
13529 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
13530 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
13531 }
13532 }
13533 else
13534 currentStyle.SetFontWeight(attr.GetFontWeight());
13535 }
13536 else if (!attr.HasFontWeight() && currentStyle.HasFontWeight())
13537 {
13538 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
13539 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
13540 }
13541
13542 if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE))
13543 {
13544 if (currentStyle.HasFontFaceName())
13545 {
13546 wxString faceName1(currentStyle.GetFontFaceName());
13547 wxString faceName2(attr.GetFontFaceName());
13548
13549 if (faceName1 != faceName2)
13550 {
13551 // Clash of attr - mark as such
13552 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
13553 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
13554 }
13555 }
13556 else
13557 currentStyle.SetFontFaceName(attr.GetFontFaceName());
13558 }
13559 else if (!attr.HasFontFaceName() && currentStyle.HasFontFaceName())
13560 {
13561 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
13562 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
13563 }
13564
13565 if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE))
13566 {
13567 if (currentStyle.HasFontUnderlined())
13568 {
13569 if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined())
13570 {
13571 // Clash of attr - mark as such
13572 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13573 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13574 }
13575 }
13576 else
13577 currentStyle.SetFontUnderlined(attr.GetFontUnderlined());
13578 }
13579 else if (!attr.HasFontUnderlined() && currentStyle.HasFontUnderlined())
13580 {
13581 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13582 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
13583 }
13584
13585 if (attr.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_STRIKETHROUGH))
13586 {
13587 if (currentStyle.HasFontStrikethrough())
13588 {
13589 if (currentStyle.GetFontStrikethrough() != attr.GetFontStrikethrough())
13590 {
13591 // Clash of attr - mark as such
13592 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13593 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13594 }
13595 }
13596 else
13597 currentStyle.SetFontStrikethrough(attr.GetFontStrikethrough());
13598 }
13599 else if (!attr.HasFontStrikethrough() && currentStyle.HasFontStrikethrough())
13600 {
13601 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13602 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
13603 }
13604
13605 if (attr.HasTextColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TEXT_COLOUR))
13606 {
13607 if (currentStyle.HasTextColour())
13608 {
13609 if (currentStyle.GetTextColour() != attr.GetTextColour())
13610 {
13611 // Clash of attr - mark as such
13612 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
13613 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
13614 }
13615 }
13616 else
13617 currentStyle.SetTextColour(attr.GetTextColour());
13618 }
13619 else if (!attr.HasTextColour() && currentStyle.HasTextColour())
13620 {
13621 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
13622 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
13623 }
13624
13625 if (attr.HasBackgroundColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BACKGROUND_COLOUR))
13626 {
13627 if (currentStyle.HasBackgroundColour())
13628 {
13629 if (currentStyle.GetBackgroundColour() != attr.GetBackgroundColour())
13630 {
13631 // Clash of attr - mark as such
13632 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13633 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13634 }
13635 }
13636 else
13637 currentStyle.SetBackgroundColour(attr.GetBackgroundColour());
13638 }
13639 else if (!attr.HasBackgroundColour() && currentStyle.HasBackgroundColour())
13640 {
13641 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13642 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
13643 }
13644
13645 if (attr.HasAlignment() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_ALIGNMENT))
13646 {
13647 if (currentStyle.HasAlignment())
13648 {
13649 if (currentStyle.GetAlignment() != attr.GetAlignment())
13650 {
13651 // Clash of attr - mark as such
13652 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
13653 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
13654 }
13655 }
13656 else
13657 currentStyle.SetAlignment(attr.GetAlignment());
13658 }
13659 else if (!attr.HasAlignment() && currentStyle.HasAlignment())
13660 {
13661 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
13662 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
13663 }
13664
13665 if (attr.HasTabs() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TABS))
13666 {
13667 if (currentStyle.HasTabs())
13668 {
13669 if (!wxRichTextTabsEq(currentStyle.GetTabs(), attr.GetTabs()))
13670 {
13671 // Clash of attr - mark as such
13672 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
13673 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
13674 }
13675 }
13676 else
13677 currentStyle.SetTabs(attr.GetTabs());
13678 }
13679 else if (!attr.HasTabs() && currentStyle.HasTabs())
13680 {
13681 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
13682 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
13683 }
13684
13685 if (attr.HasLeftIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LEFT_INDENT))
13686 {
13687 if (currentStyle.HasLeftIndent())
13688 {
13689 if (currentStyle.GetLeftIndent() != attr.GetLeftIndent() || currentStyle.GetLeftSubIndent() != attr.GetLeftSubIndent())
13690 {
13691 // Clash of attr - mark as such
13692 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
13693 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
13694 }
13695 }
13696 else
13697 currentStyle.SetLeftIndent(attr.GetLeftIndent(), attr.GetLeftSubIndent());
13698 }
13699 else if (!attr.HasLeftIndent() && currentStyle.HasLeftIndent())
13700 {
13701 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
13702 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
13703 }
13704
13705 if (attr.HasRightIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_RIGHT_INDENT))
13706 {
13707 if (currentStyle.HasRightIndent())
13708 {
13709 if (currentStyle.GetRightIndent() != attr.GetRightIndent())
13710 {
13711 // Clash of attr - mark as such
13712 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
13713 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
13714 }
13715 }
13716 else
13717 currentStyle.SetRightIndent(attr.GetRightIndent());
13718 }
13719 else if (!attr.HasRightIndent() && currentStyle.HasRightIndent())
13720 {
13721 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
13722 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
13723 }
13724
13725 if (attr.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_AFTER))
13726 {
13727 if (currentStyle.HasParagraphSpacingAfter())
13728 {
13729 if (currentStyle.GetParagraphSpacingAfter() != attr.GetParagraphSpacingAfter())
13730 {
13731 // Clash of attr - mark as such
13732 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13733 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13734 }
13735 }
13736 else
13737 currentStyle.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter());
13738 }
13739 else if (!attr.HasParagraphSpacingAfter() && currentStyle.HasParagraphSpacingAfter())
13740 {
13741 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13742 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
13743 }
13744
13745 if (attr.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_BEFORE))
13746 {
13747 if (currentStyle.HasParagraphSpacingBefore())
13748 {
13749 if (currentStyle.GetParagraphSpacingBefore() != attr.GetParagraphSpacingBefore())
13750 {
13751 // Clash of attr - mark as such
13752 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13753 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13754 }
13755 }
13756 else
13757 currentStyle.SetParagraphSpacingBefore(attr.GetParagraphSpacingBefore());
13758 }
13759 else if (!attr.HasParagraphSpacingBefore() && currentStyle.HasParagraphSpacingBefore())
13760 {
13761 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13762 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
13763 }
13764
13765 if (attr.HasLineSpacing() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LINE_SPACING))
13766 {
13767 if (currentStyle.HasLineSpacing())
13768 {
13769 if (currentStyle.GetLineSpacing() != attr.GetLineSpacing())
13770 {
13771 // Clash of attr - mark as such
13772 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
13773 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
13774 }
13775 }
13776 else
13777 currentStyle.SetLineSpacing(attr.GetLineSpacing());
13778 }
13779 else if (!attr.HasLineSpacing() && currentStyle.HasLineSpacing())
13780 {
13781 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
13782 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
13783 }
13784
13785 if (attr.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
13786 {
13787 if (currentStyle.HasCharacterStyleName())
13788 {
13789 if (currentStyle.GetCharacterStyleName() != attr.GetCharacterStyleName())
13790 {
13791 // Clash of attr - mark as such
13792 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13793 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13794 }
13795 }
13796 else
13797 currentStyle.SetCharacterStyleName(attr.GetCharacterStyleName());
13798 }
13799 else if (!attr.HasCharacterStyleName() && currentStyle.HasCharacterStyleName())
13800 {
13801 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13802 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
13803 }
13804
13805 if (attr.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
13806 {
13807 if (currentStyle.HasParagraphStyleName())
13808 {
13809 if (currentStyle.GetParagraphStyleName() != attr.GetParagraphStyleName())
13810 {
13811 // Clash of attr - mark as such
13812 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13813 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13814 }
13815 }
13816 else
13817 currentStyle.SetParagraphStyleName(attr.GetParagraphStyleName());
13818 }
13819 else if (!attr.HasParagraphStyleName() && currentStyle.HasParagraphStyleName())
13820 {
13821 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13822 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
13823 }
13824
13825 if (attr.HasListStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LIST_STYLE_NAME))
13826 {
13827 if (currentStyle.HasListStyleName())
13828 {
13829 if (currentStyle.GetListStyleName() != attr.GetListStyleName())
13830 {
13831 // Clash of attr - mark as such
13832 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13833 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13834 }
13835 }
13836 else
13837 currentStyle.SetListStyleName(attr.GetListStyleName());
13838 }
13839 else if (!attr.HasListStyleName() && currentStyle.HasListStyleName())
13840 {
13841 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13842 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
13843 }
13844
13845 if (attr.HasBulletStyle() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_STYLE))
13846 {
13847 if (currentStyle.HasBulletStyle())
13848 {
13849 if (currentStyle.GetBulletStyle() != attr.GetBulletStyle())
13850 {
13851 // Clash of attr - mark as such
13852 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
13853 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
13854 }
13855 }
13856 else
13857 currentStyle.SetBulletStyle(attr.GetBulletStyle());
13858 }
13859 else if (!attr.HasBulletStyle() && currentStyle.HasBulletStyle())
13860 {
13861 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
13862 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
13863 }
13864
13865 if (attr.HasBulletNumber() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NUMBER))
13866 {
13867 if (currentStyle.HasBulletNumber())
13868 {
13869 if (currentStyle.GetBulletNumber() != attr.GetBulletNumber())
13870 {
13871 // Clash of attr - mark as such
13872 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
13873 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
13874 }
13875 }
13876 else
13877 currentStyle.SetBulletNumber(attr.GetBulletNumber());
13878 }
13879 else if (!attr.HasBulletNumber() && currentStyle.HasBulletNumber())
13880 {
13881 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
13882 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
13883 }
13884
13885 if (attr.HasBulletText() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_TEXT))
13886 {
13887 if (currentStyle.HasBulletText())
13888 {
13889 if (currentStyle.GetBulletText() != attr.GetBulletText())
13890 {
13891 // Clash of attr - mark as such
13892 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
13893 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
13894 }
13895 }
13896 else
13897 {
13898 currentStyle.SetBulletText(attr.GetBulletText());
13899 currentStyle.SetBulletFont(attr.GetBulletFont());
13900 }
13901 }
13902 else if (!attr.HasBulletText() && currentStyle.HasBulletText())
13903 {
13904 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
13905 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
13906 }
13907
13908 if (attr.HasBulletName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NAME))
13909 {
13910 if (currentStyle.HasBulletName())
13911 {
13912 if (currentStyle.GetBulletName() != attr.GetBulletName())
13913 {
13914 // Clash of attr - mark as such
13915 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
13916 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
13917 }
13918 }
13919 else
13920 {
13921 currentStyle.SetBulletName(attr.GetBulletName());
13922 }
13923 }
13924 else if (!attr.HasBulletName() && currentStyle.HasBulletName())
13925 {
13926 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
13927 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
13928 }
13929
13930 if (attr.HasURL() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_URL))
13931 {
13932 if (currentStyle.HasURL())
13933 {
13934 if (currentStyle.GetURL() != attr.GetURL())
13935 {
13936 // Clash of attr - mark as such
13937 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
13938 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
13939 }
13940 }
13941 else
13942 {
13943 currentStyle.SetURL(attr.GetURL());
13944 }
13945 }
13946 else if (!attr.HasURL() && currentStyle.HasURL())
13947 {
13948 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
13949 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
13950 }
13951
13952 if (attr.HasTextEffects() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_EFFECTS))
13953 {
13954 if (currentStyle.HasTextEffects())
13955 {
13956 // We need to find the bits in the new attr that are different:
13957 // just look at those bits that are specified by the new attr.
13958
13959 // We need to remove the bits and flags that are not common between current attr
13960 // and new attr. In so doing we need to take account of the styles absent from one or more of the
13961 // previous styles.
13962
13963 int currentRelevantTextEffects = currentStyle.GetTextEffects() & attr.GetTextEffectFlags();
13964 int newRelevantTextEffects = attr.GetTextEffects() & attr.GetTextEffectFlags();
13965
13966 if (currentRelevantTextEffects != newRelevantTextEffects)
13967 {
13968 // Find the text effects that were different, using XOR
13969 int differentEffects = currentRelevantTextEffects ^ newRelevantTextEffects;
13970
13971 // Clash of attr - mark as such
13972 clashingAttr.SetTextEffectFlags(clashingAttr.GetTextEffectFlags() | differentEffects);
13973 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~differentEffects);
13974 }
13975 }
13976 else
13977 {
13978 currentStyle.SetTextEffects(attr.GetTextEffects());
13979 currentStyle.SetTextEffectFlags(attr.GetTextEffectFlags());
13980 }
13981
13982 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
13983 // that we've looked at so far
13984 currentStyle.SetTextEffects(currentStyle.GetTextEffects() & ~absentAttr.GetTextEffectFlags());
13985 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~absentAttr.GetTextEffectFlags());
13986
13987 if (currentStyle.GetTextEffectFlags() == 0)
13988 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
13989 }
13990 else if (!attr.HasTextEffects() && currentStyle.HasTextEffects())
13991 {
13992 clashingAttr.AddFlag(wxTEXT_ATTR_EFFECTS);
13993 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
13994 }
13995
13996 if (attr.HasOutlineLevel() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_OUTLINE_LEVEL))
13997 {
13998 if (currentStyle.HasOutlineLevel())
13999 {
14000 if (currentStyle.GetOutlineLevel() != attr.GetOutlineLevel())
14001 {
14002 // Clash of attr - mark as such
14003 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
14004 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
14005 }
14006 }
14007 else
14008 currentStyle.SetOutlineLevel(attr.GetOutlineLevel());
14009 }
14010 else if (!attr.HasOutlineLevel() && currentStyle.HasOutlineLevel())
14011 {
14012 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
14013 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
14014 }
14015 }
14016
14017 WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
14018 WX_DEFINE_OBJARRAY(wxRichTextAttrArray);
14019 WX_DEFINE_OBJARRAY(wxRichTextRectArray);
14020
14021 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
14022
14023 bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const
14024 {
14025 if (m_properties.GetCount() != props.GetCount())
14026 return false;
14027
14028 size_t i;
14029 for (i = 0; i < m_properties.GetCount(); i++)
14030 {
14031 const wxVariant& var1 = m_properties[i];
14032 int idx = props.Find(var1.GetName());
14033 if (idx == -1)
14034 return false;
14035 const wxVariant& var2 = props.m_properties[idx];
14036 if (!(var1 == var2))
14037 return false;
14038 }
14039
14040 return true;
14041 }
14042
14043 wxArrayString wxRichTextProperties::GetPropertyNames() const
14044 {
14045 wxArrayString arr;
14046 size_t i;
14047 for (i = 0; i < m_properties.GetCount(); i++)
14048 {
14049 arr.Add(m_properties[i].GetName());
14050 }
14051 return arr;
14052 }
14053
14054 int wxRichTextProperties::Find(const wxString& name) const
14055 {
14056 size_t i;
14057 for (i = 0; i < m_properties.GetCount(); i++)
14058 {
14059 if (m_properties[i].GetName() == name)
14060 return (int) i;
14061 }
14062 return -1;
14063 }
14064
14065 bool wxRichTextProperties::Remove(const wxString& name)
14066 {
14067 int idx = Find(name);
14068 if (idx != -1)
14069 {
14070 m_properties.RemoveAt(idx);
14071 return true;
14072 }
14073 else
14074 return false;
14075 }
14076
14077 wxVariant* wxRichTextProperties::FindOrCreateProperty(const wxString& name)
14078 {
14079 int idx = Find(name);
14080 if (idx == wxNOT_FOUND)
14081 SetProperty(name, wxString());
14082 idx = Find(name);
14083 if (idx != wxNOT_FOUND)
14084 {
14085 return & (*this)[idx];
14086 }
14087 else
14088 return NULL;
14089 }
14090
14091 const wxVariant& wxRichTextProperties::GetProperty(const wxString& name) const
14092 {
14093 static const wxVariant nullVariant;
14094 int idx = Find(name);
14095 if (idx != -1)
14096 return m_properties[idx];
14097 else
14098 return nullVariant;
14099 }
14100
14101 wxString wxRichTextProperties::GetPropertyString(const wxString& name) const
14102 {
14103 return GetProperty(name).GetString();
14104 }
14105
14106 long wxRichTextProperties::GetPropertyLong(const wxString& name) const
14107 {
14108 return GetProperty(name).GetLong();
14109 }
14110
14111 bool wxRichTextProperties::GetPropertyBool(const wxString& name) const
14112 {
14113 return GetProperty(name).GetBool();
14114 }
14115
14116 double wxRichTextProperties::GetPropertyDouble(const wxString& name) const
14117 {
14118 return GetProperty(name).GetDouble();
14119 }
14120
14121 void wxRichTextProperties::SetProperty(const wxVariant& variant)
14122 {
14123 wxASSERT(!variant.GetName().IsEmpty());
14124
14125 int idx = Find(variant.GetName());
14126
14127 if (idx == -1)
14128 m_properties.Add(variant);
14129 else
14130 m_properties[idx] = variant;
14131 }
14132
14133 void wxRichTextProperties::SetProperty(const wxString& name, const wxVariant& variant)
14134 {
14135 int idx = Find(name);
14136 wxVariant var(variant);
14137 var.SetName(name);
14138
14139 if (idx == -1)
14140 m_properties.Add(var);
14141 else
14142 m_properties[idx] = var;
14143 }
14144
14145 void wxRichTextProperties::SetProperty(const wxString& name, const wxString& value)
14146 {
14147 SetProperty(name, wxVariant(value, name));
14148 }
14149
14150 void wxRichTextProperties::SetProperty(const wxString& name, long value)
14151 {
14152 SetProperty(name, wxVariant(value, name));
14153 }
14154
14155 void wxRichTextProperties::SetProperty(const wxString& name, double value)
14156 {
14157 SetProperty(name, wxVariant(value, name));
14158 }
14159
14160 void wxRichTextProperties::SetProperty(const wxString& name, bool value)
14161 {
14162 SetProperty(name, wxVariant(value, name));
14163 }
14164
14165 void wxRichTextProperties::RemoveProperties(const wxRichTextProperties& properties)
14166 {
14167 size_t i;
14168 for (i = 0; i < properties.GetCount(); i++)
14169 {
14170 wxString name = properties.GetProperties()[i].GetName();
14171 if (HasProperty(name))
14172 Remove(name);
14173 }
14174 }
14175
14176 void wxRichTextProperties::MergeProperties(const wxRichTextProperties& properties)
14177 {
14178 size_t i;
14179 for (i = 0; i < properties.GetCount(); i++)
14180 {
14181 SetProperty(properties.GetProperties()[i]);
14182 }
14183 }
14184
14185 wxRichTextObject* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox* topLevelContainer) const
14186 {
14187 if (m_address.GetCount() == 0)
14188 return topLevelContainer;
14189
14190 wxRichTextCompositeObject* p = topLevelContainer;
14191 size_t i = 0;
14192 while (p && i < m_address.GetCount())
14193 {
14194 int pos = m_address[i];
14195 wxASSERT(pos >= 0 && pos < (int) p->GetChildren().GetCount());
14196 if (pos < 0 || pos >= (int) p->GetChildren().GetCount())
14197 return NULL;
14198
14199 wxRichTextObject* p1 = p->GetChild(pos);
14200 if (i == (m_address.GetCount()-1))
14201 return p1;
14202
14203 p = wxDynamicCast(p1, wxRichTextCompositeObject);
14204 i ++;
14205 }
14206 return NULL;
14207 }
14208
14209 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox* topLevelContainer, wxRichTextObject* obj)
14210 {
14211 m_address.Clear();
14212
14213 if (topLevelContainer == obj)
14214 return true;
14215
14216 wxRichTextObject* o = obj;
14217 while (o)
14218 {
14219 wxRichTextCompositeObject* p = wxDynamicCast(o->GetParent(), wxRichTextCompositeObject);
14220 if (!p)
14221 return false;
14222
14223 int pos = p->GetChildren().IndexOf(o);
14224 if (pos == -1)
14225 return false;
14226
14227 m_address.Insert(pos, 0);
14228
14229 if (p == topLevelContainer)
14230 return true;
14231
14232 o = p;
14233 }
14234 return false;
14235 }
14236
14237 // Equality test
14238 bool wxRichTextSelection::operator==(const wxRichTextSelection& sel) const
14239 {
14240 if (m_container != sel.m_container)
14241 return false;
14242 if (m_ranges.GetCount() != sel.m_ranges.GetCount())
14243 return false;
14244 size_t i;
14245 for (i = 0; i < m_ranges.GetCount(); i++)
14246 if (!(m_ranges[i] == sel.m_ranges[i]))
14247 return false;
14248 return true;
14249 }
14250
14251 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
14252 // or none at the level of the object's container.
14253 wxRichTextRangeArray wxRichTextSelection::GetSelectionForObject(wxRichTextObject* obj) const
14254 {
14255 if (IsValid())
14256 {
14257 wxRichTextParagraphLayoutBox* container = obj->GetParentContainer();
14258
14259 if (container == m_container)
14260 return m_ranges;
14261
14262 container = obj->GetContainer();
14263 while (container)
14264 {
14265 if (container->GetParent())
14266 {
14267 // If we found that our object's container is within the range of
14268 // a selection higher up, then assume the whole original object
14269 // is also selected.
14270 wxRichTextParagraphLayoutBox* parentContainer = container->GetParentContainer();
14271 if (parentContainer == m_container)
14272 {
14273 if (WithinSelection(container->GetRange().GetStart(), m_ranges))
14274 {
14275 wxRichTextRangeArray ranges;
14276 ranges.Add(obj->GetRange());
14277 return ranges;
14278 }
14279 }
14280
14281 container = parentContainer;
14282 }
14283 else
14284 {
14285 container = NULL;
14286 break;
14287 }
14288 }
14289 }
14290 return wxRichTextRangeArray();
14291 }
14292
14293 // Is the given position within the selection?
14294 bool wxRichTextSelection::WithinSelection(long pos, wxRichTextObject* obj) const
14295 {
14296 if (!IsValid())
14297 return false;
14298 else
14299 {
14300 wxRichTextRangeArray selectionRanges = GetSelectionForObject(obj);
14301 return WithinSelection(pos, selectionRanges);
14302 }
14303 }
14304
14305 // Is the given position within the selection range?
14306 bool wxRichTextSelection::WithinSelection(long pos, const wxRichTextRangeArray& ranges)
14307 {
14308 size_t i;
14309 for (i = 0; i < ranges.GetCount(); i++)
14310 {
14311 const wxRichTextRange& range = ranges[i];
14312 if (pos >= range.GetStart() && pos <= range.GetEnd())
14313 return true;
14314 }
14315 return false;
14316 }
14317
14318 // Is the given range completely within the selection range?
14319 bool wxRichTextSelection::WithinSelection(const wxRichTextRange& range, const wxRichTextRangeArray& ranges)
14320 {
14321 size_t i;
14322 for (i = 0; i < ranges.GetCount(); i++)
14323 {
14324 const wxRichTextRange& eachRange = ranges[i];
14325 if (range.IsWithin(eachRange))
14326 return true;
14327 }
14328 return false;
14329 }
14330
14331 IMPLEMENT_CLASS(wxRichTextDrawingHandler, wxObject)
14332 IMPLEMENT_CLASS(wxRichTextDrawingContext, wxObject)
14333
14334 wxRichTextDrawingContext::wxRichTextDrawingContext(wxRichTextBuffer* buffer)
14335 {
14336 Init();
14337 m_buffer = buffer;
14338 if (m_buffer && m_buffer->GetRichTextCtrl())
14339 EnableVirtualAttributes(m_buffer->GetRichTextCtrl()->GetVirtualAttributesEnabled());
14340 }
14341
14342 bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject* obj) const
14343 {
14344 if (!GetVirtualAttributesEnabled())
14345 return false;
14346
14347 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
14348 while (node)
14349 {
14350 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
14351 if (handler->HasVirtualAttributes(obj))
14352 return true;
14353
14354 node = node->GetNext();
14355 }
14356 return false;
14357 }
14358
14359 wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* obj) const
14360 {
14361 wxRichTextAttr attr;
14362 if (!GetVirtualAttributesEnabled())
14363 return attr;
14364
14365 // We apply all handlers, so we can may combine several different attributes
14366 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
14367 while (node)
14368 {
14369 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
14370 if (handler->HasVirtualAttributes(obj))
14371 {
14372 bool success = handler->GetVirtualAttributes(attr, obj);
14373 wxASSERT(success);
14374 wxUnusedVar(success);
14375 }
14376
14377 node = node->GetNext();
14378 }
14379 return attr;
14380 }
14381
14382 bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const
14383 {
14384 if (!GetVirtualAttributesEnabled())
14385 return false;
14386
14387 if (HasVirtualAttributes(obj))
14388 {
14389 wxRichTextAttr a(GetVirtualAttributes(obj));
14390 attr.Apply(a);
14391 return true;
14392 }
14393 else
14394 return false;
14395 }
14396
14397 int wxRichTextDrawingContext::GetVirtualSubobjectAttributesCount(wxRichTextObject* obj) const
14398 {
14399 if (!GetVirtualAttributesEnabled())
14400 return 0;
14401
14402 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
14403 while (node)
14404 {
14405 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
14406 int count = handler->GetVirtualSubobjectAttributesCount(obj);
14407 if (count > 0)
14408 return count;
14409
14410 node = node->GetNext();
14411 }
14412 return 0;
14413 }
14414
14415 int wxRichTextDrawingContext::GetVirtualSubobjectAttributes(wxRichTextObject* obj, wxArrayInt& positions, wxRichTextAttrArray& attributes) const
14416 {
14417 if (!GetVirtualAttributesEnabled())
14418 return 0;
14419
14420 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
14421 while (node)
14422 {
14423 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
14424 if (handler->GetVirtualSubobjectAttributes(obj, positions, attributes))
14425 return positions.GetCount();
14426
14427 node = node->GetNext();
14428 }
14429 return 0;
14430 }
14431
14432 bool wxRichTextDrawingContext::HasVirtualText(const wxRichTextPlainText* obj) const
14433 {
14434 if (!GetVirtualAttributesEnabled())
14435 return false;
14436
14437 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
14438 while (node)
14439 {
14440 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
14441 if (handler->HasVirtualText(obj))
14442 return true;
14443
14444 node = node->GetNext();
14445 }
14446 return false;
14447 }
14448
14449 bool wxRichTextDrawingContext::GetVirtualText(const wxRichTextPlainText* obj, wxString& text) const
14450 {
14451 if (!GetVirtualAttributesEnabled())
14452 return false;
14453
14454 wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
14455 while (node)
14456 {
14457 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
14458 if (handler->GetVirtualText(obj, text))
14459 return true;
14460
14461 node = node->GetNext();
14462 }
14463 return false;
14464 }
14465
14466 /// Adds a handler to the end
14467 void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler *handler)
14468 {
14469 sm_drawingHandlers.Append(handler);
14470 }
14471
14472 /// Inserts a handler at the front
14473 void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler *handler)
14474 {
14475 sm_drawingHandlers.Insert( handler );
14476 }
14477
14478 /// Removes a handler
14479 bool wxRichTextBuffer::RemoveDrawingHandler(const wxString& name)
14480 {
14481 wxRichTextDrawingHandler *handler = FindDrawingHandler(name);
14482 if (handler)
14483 {
14484 sm_drawingHandlers.DeleteObject(handler);
14485 delete handler;
14486 return true;
14487 }
14488 else
14489 return false;
14490 }
14491
14492 wxRichTextDrawingHandler* wxRichTextBuffer::FindDrawingHandler(const wxString& name)
14493 {
14494 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
14495 while (node)
14496 {
14497 wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
14498 if (handler->GetName().Lower() == name.Lower()) return handler;
14499
14500 node = node->GetNext();
14501 }
14502 return NULL;
14503 }
14504
14505 void wxRichTextBuffer::CleanUpDrawingHandlers()
14506 {
14507 wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
14508 while (node)
14509 {
14510 wxRichTextDrawingHandler* handler = (wxRichTextDrawingHandler*)node->GetData();
14511 wxList::compatibility_iterator next = node->GetNext();
14512 delete handler;
14513 node = next;
14514 }
14515
14516 sm_drawingHandlers.Clear();
14517 }
14518
14519 void wxRichTextBuffer::AddFieldType(wxRichTextFieldType *fieldType)
14520 {
14521 sm_fieldTypes[fieldType->GetName()] = fieldType;
14522 }
14523
14524 bool wxRichTextBuffer::RemoveFieldType(const wxString& name)
14525 {
14526 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
14527 if (it == sm_fieldTypes.end())
14528 return false;
14529 else
14530 {
14531 wxRichTextFieldType* fieldType = it->second;
14532 sm_fieldTypes.erase(it);
14533 delete fieldType;
14534 return true;
14535 }
14536 }
14537
14538 wxRichTextFieldType *wxRichTextBuffer::FindFieldType(const wxString& name)
14539 {
14540 wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
14541 if (it == sm_fieldTypes.end())
14542 return NULL;
14543 else
14544 return it->second;
14545 }
14546
14547 void wxRichTextBuffer::CleanUpFieldTypes()
14548 {
14549 wxRichTextFieldTypeHashMap::iterator it;
14550 for( it = sm_fieldTypes.begin(); it != sm_fieldTypes.end(); ++it )
14551 {
14552 wxRichTextFieldType* fieldType = it->second;
14553 delete fieldType;
14554 }
14555
14556 sm_fieldTypes.clear();
14557 }
14558
14559 #endif
14560 // wxUSE_RICHTEXT