Fixed a problem with paste resetting the content paragraph style.
[wxWidgets.git] / src / richtext / richtextbuffer.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/richtext/richtextbuffer.cpp
3 // Purpose: Buffer for wxRichTextCtrl
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 2005-09-30
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #if wxUSE_RICHTEXT
20
21 #include "wx/richtext/richtextbuffer.h"
22
23 #ifndef WX_PRECOMP
24 #include "wx/dc.h"
25 #include "wx/intl.h"
26 #include "wx/log.h"
27 #include "wx/dataobj.h"
28 #include "wx/module.h"
29 #endif
30
31 #include "wx/settings.h"
32 #include "wx/filename.h"
33 #include "wx/clipbrd.h"
34 #include "wx/wfstream.h"
35 #include "wx/mstream.h"
36 #include "wx/sstream.h"
37 #include "wx/textfile.h"
38 #include "wx/hashmap.h"
39 #include "wx/dynarray.h"
40
41 #include "wx/richtext/richtextctrl.h"
42 #include "wx/richtext/richtextstyles.h"
43 #include "wx/richtext/richtextimagedlg.h"
44 #include "wx/richtext/richtextsizepage.h"
45
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, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
109
110 // HitTest the floats
111 int HitTest(wxDC& dc, 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, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
134
135 static int HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& WXUNUSED(dc), 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, 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, r, selection, wxRect(obj->GetPosition(), obj->GetCachedSize()), descent, style);
415 i++;
416 }
417 }
418
419 void wxRichTextFloatCollector::Draw(wxDC& dc, 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, range, selection, rect, descent, style);
423 if (m_right.GetCount() > 0)
424 DrawFloat(m_right, dc, range, selection, rect, descent, style);
425 }
426
427 int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& WXUNUSED(dc), 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, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags)
455 {
456 int ret = HitTestFloat(m_left, dc, pt, textPosition, obj, flags);
457 if (ret == wxRICHTEXT_HITTEST_NONE)
458 {
459 ret = HitTestFloat(m_right, dc, 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 // JACS: did I do this some time ago when testing? Should we re-enable it?
468 #if 0
469 const wxFont& font1 = dc.GetFont();
470 if (font1.IsOk() && font.IsOk())
471 {
472 if (font1.GetPointSize() == font.GetPointSize() &&
473 font1.GetFamily() == font.GetFamily() &&
474 font1.GetStyle() == font.GetStyle() &&
475 font1.GetWeight() == font.GetWeight() &&
476 font1.GetUnderlined() == font.GetUnderlined() &&
477 font1.GetFamily() == font.GetFamily() &&
478 font1.GetFaceName() == font.GetFaceName())
479 return;
480 }
481 #endif
482 dc.SetFont(font);
483 }
484
485 inline void wxCheckSetPen(wxDC& dc, const wxPen& pen)
486 {
487 const wxPen& pen1 = dc.GetPen();
488 if (pen1.IsOk() && pen.IsOk())
489 {
490 if (pen1.GetWidth() == pen.GetWidth() &&
491 pen1.GetStyle() == pen.GetStyle() &&
492 pen1.GetColour() == pen.GetColour())
493 return;
494 }
495 dc.SetPen(pen);
496 }
497
498 inline void wxCheckSetBrush(wxDC& dc, const wxBrush& brush)
499 {
500 const wxBrush& brush1 = dc.GetBrush();
501 if (brush1.IsOk() && brush.IsOk())
502 {
503 if (brush1.GetStyle() == brush.GetStyle() &&
504 brush1.GetColour() == brush.GetColour())
505 return;
506 }
507 dc.SetBrush(brush);
508 }
509
510 /*!
511 * wxRichTextObject
512 * This is the base for drawable objects.
513 */
514
515 IMPLEMENT_CLASS(wxRichTextObject, wxObject)
516
517 wxRichTextObject::wxRichTextObject(wxRichTextObject* parent)
518 {
519 m_refCount = 1;
520 m_parent = parent;
521 m_descent = 0;
522 m_show = true;
523 }
524
525 wxRichTextObject::~wxRichTextObject()
526 {
527 }
528
529 void wxRichTextObject::Dereference()
530 {
531 m_refCount --;
532 if (m_refCount <= 0)
533 delete this;
534 }
535
536 /// Copy
537 void wxRichTextObject::Copy(const wxRichTextObject& obj)
538 {
539 m_size = obj.m_size;
540 m_maxSize = obj.m_maxSize;
541 m_minSize = obj.m_minSize;
542 m_pos = obj.m_pos;
543 m_range = obj.m_range;
544 m_ownRange = obj.m_ownRange;
545 m_attributes = obj.m_attributes;
546 m_properties = obj.m_properties;
547 m_descent = obj.m_descent;
548 m_show = obj.m_show;
549 }
550
551 // Get/set the top-level container of this object.
552 wxRichTextParagraphLayoutBox* wxRichTextObject::GetContainer() const
553 {
554 const wxRichTextObject* p = this;
555 while (p)
556 {
557 if (p->IsTopLevel())
558 {
559 return wxDynamicCast(p, wxRichTextParagraphLayoutBox);
560 }
561 p = p->GetParent();
562 }
563 return NULL;
564 }
565
566 void wxRichTextObject::SetMargins(int margin)
567 {
568 SetMargins(margin, margin, margin, margin);
569 }
570
571 void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin, int bottomMargin)
572 {
573 GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin, wxTEXT_ATTR_UNITS_PIXELS);
574 GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin, wxTEXT_ATTR_UNITS_PIXELS);
575 GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin, wxTEXT_ATTR_UNITS_PIXELS);
576 GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin, wxTEXT_ATTR_UNITS_PIXELS);
577 }
578
579 int wxRichTextObject::GetLeftMargin() const
580 {
581 return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
582 }
583
584 int wxRichTextObject::GetRightMargin() const
585 {
586 return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
587 }
588
589 int wxRichTextObject::GetTopMargin() const
590 {
591 return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
592 }
593
594 int wxRichTextObject::GetBottomMargin() const
595 {
596 return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
597 }
598
599 // Calculate the available content space in the given rectangle, given the
600 // margins, border and padding specified in the object's attributes.
601 wxRect wxRichTextObject::GetAvailableContentArea(wxDC& dc, const wxRect& outerRect) const
602 {
603 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
604 marginRect = outerRect;
605 GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
606 return contentRect;
607 }
608
609 // Invalidate the buffer. With no argument, invalidates whole buffer.
610 void wxRichTextObject::Invalidate(const wxRichTextRange& invalidRange)
611 {
612 if (invalidRange != wxRICHTEXT_NONE)
613 {
614 SetCachedSize(wxDefaultSize);
615 SetMaxSize(wxDefaultSize);
616 SetMinSize(wxDefaultSize);
617 }
618 }
619
620 // Convert units in tenths of a millimetre to device units
621 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units) const
622 {
623 // Unscale
624 double scale = 1.0;
625 if (GetBuffer())
626 scale = GetBuffer()->GetScale();
627 int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units, scale);
628
629 return p;
630 }
631
632 // Convert units in tenths of a millimetre to device units
633 int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units, double scale)
634 {
635 // There are ppi pixels in 254.1 "1/10 mm"
636
637 double pixels = ((double) units * (double)ppi) / 254.1;
638 if (scale != 1.0)
639 pixels /= scale;
640
641 // If the result is very small, make it at least one pixel in size.
642 if (pixels == 0 && units > 0)
643 pixels = 1;
644
645 return (int) pixels;
646 }
647
648 // Convert units in pixels to tenths of a millimetre
649 int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC& dc, int pixels) const
650 {
651 int p = pixels;
652 double scale = 1.0;
653 if (GetBuffer())
654 scale = GetBuffer()->GetScale();
655
656 return ConvertPixelsToTenthsMM(dc.GetPPI().x, p, scale);
657 }
658
659 int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi, int pixels, double scale)
660 {
661 // There are ppi pixels in 254.1 "1/10 mm"
662
663 double p = double(pixels);
664
665 if (scale != 1.0)
666 p *= scale;
667
668 int units = int( p * 254.1 / (double) ppi );
669 return units;
670 }
671
672 // Draw the borders and background for the given rectangle and attributes.
673 // Width and height are taken to be the outer margin size, not the content.
674 bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, const wxRect& boxRect, int flags)
675 {
676 // Assume boxRect is the area around the content
677 wxRect marginRect = boxRect;
678 wxRect contentRect, borderRect, paddingRect, outlineRect;
679
680 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
681
682 // Margin is transparent. Draw background from margin.
683 if (attr.HasBackgroundColour() || (flags & wxRICHTEXT_DRAW_SELECTED))
684 {
685 wxColour colour;
686 if (flags & wxRICHTEXT_DRAW_SELECTED)
687 {
688 // TODO: get selection colour from control?
689 colour = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
690 }
691 else
692 colour = attr.GetBackgroundColour();
693
694 wxPen pen(colour);
695 wxBrush brush(colour);
696
697 dc.SetPen(pen);
698 dc.SetBrush(brush);
699 dc.DrawRectangle(marginRect);
700 }
701
702 if (flags & wxRICHTEXT_DRAW_GUIDELINES)
703 {
704 wxRichTextAttr editBorderAttr = attr;
705 // TODO: make guideline colour configurable
706 editBorderAttr.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY);
707 editBorderAttr.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS);
708 editBorderAttr.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID);
709
710 DrawBorder(dc, buffer, editBorderAttr.GetTextBoxAttr().GetBorder(), borderRect, flags);
711 }
712
713 if (attr.GetTextBoxAttr().GetBorder().IsValid())
714 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetBorder(), borderRect);
715
716 if (attr.GetTextBoxAttr().GetOutline().IsValid())
717 DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetOutline(), outlineRect);
718
719 return true;
720 }
721
722 // Draw a border
723 bool wxRichTextObject::DrawBorder(wxDC& dc, wxRichTextBuffer* buffer, const wxTextAttrBorders& attr, const wxRect& rect, int WXUNUSED(flags))
724 {
725 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
726 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
727
728 if (attr.GetLeft().IsValid() && attr.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
729 {
730 borderLeft = converter.GetPixels(attr.GetLeft().GetWidth());
731 wxColour col(attr.GetLeft().GetColour());
732
733 // If pen width is > 1, resorts to a solid rectangle.
734 if (borderLeft == 1)
735 {
736 int penStyle = wxSOLID;
737 if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
738 penStyle = wxDOT;
739 else if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
740 penStyle = wxLONG_DASH;
741 wxPen pen(col, 1, penStyle);
742 dc.SetPen(pen);
743 dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height);
744
745 }
746 else if (borderLeft > 1)
747 {
748 wxPen pen(col);
749 wxBrush brush(col);
750 dc.SetPen(pen);
751 dc.SetBrush(brush);
752 dc.DrawRectangle(rect.x, rect.y, borderLeft, rect.height);
753 }
754 }
755
756 if (attr.GetRight().IsValid() && attr.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
757 {
758 borderRight = converter.GetPixels(attr.GetRight().GetWidth());
759
760 wxColour col(attr.GetRight().GetColour());
761
762 // If pen width is > 1, resorts to a solid rectangle.
763 if (borderRight == 1)
764 {
765 int penStyle = wxSOLID;
766 if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
767 penStyle = wxDOT;
768 else if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
769 penStyle = wxLONG_DASH;
770 wxPen pen(col, 1, penStyle);
771 dc.SetPen(pen);
772 dc.DrawLine(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height + 1);
773
774 }
775 else if (borderRight > 1)
776 {
777 wxPen pen(col);
778 wxBrush brush(col);
779 dc.SetPen(pen);
780 dc.SetBrush(brush);
781 dc.DrawRectangle(rect.x - borderRight, rect.y, borderRight, rect.height);
782 }
783 }
784
785 if (attr.GetTop().IsValid() && attr.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
786 {
787 borderTop = converter.GetPixels(attr.GetTop().GetWidth());
788
789 wxColour col(attr.GetTop().GetColour());
790
791 // If pen width is > 1, resorts to a solid rectangle.
792 if (borderTop == 1)
793 {
794 int penStyle = wxSOLID;
795 if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
796 penStyle = wxDOT;
797 else if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
798 penStyle = wxLONG_DASH;
799 wxPen pen(col, 1, penStyle);
800 dc.SetPen(pen);
801 dc.DrawLine(rect.x, rect.y, rect.x + rect.width, rect.y);
802
803 }
804 else if (borderTop > 1)
805 {
806 wxPen pen(col);
807 wxBrush brush(col);
808 dc.SetPen(pen);
809 dc.SetBrush(brush);
810 dc.DrawRectangle(rect.x, rect.y, rect.width, borderTop);
811 }
812 }
813
814 if (attr.GetBottom().IsValid() && attr.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
815 {
816 borderBottom = converter.GetPixels(attr.GetBottom().GetWidth());
817 wxColour col(attr.GetTop().GetColour());
818
819 // If pen width is > 1, resorts to a solid rectangle.
820 if (borderBottom == 1)
821 {
822 int penStyle = wxSOLID;
823 if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
824 penStyle = wxDOT;
825 else if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
826 penStyle = wxLONG_DASH;
827 wxPen pen(col, 1, penStyle);
828 dc.SetPen(pen);
829 dc.DrawLine(rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height);
830
831 }
832 else if (borderBottom > 1)
833 {
834 wxPen pen(col);
835 wxBrush brush(col);
836 dc.SetPen(pen);
837 dc.SetBrush(brush);
838 dc.DrawRectangle(rect.x, rect.y - rect.height - borderBottom, rect.width, borderBottom);
839 }
840 }
841
842 return true;
843 }
844
845 // Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
846 // or marginRect (outer), and the other must be the default rectangle (no width or height).
847 // Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
848 // is available.
849 //
850 // | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
851
852 bool wxRichTextObject::GetBoxRects(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, wxRect& marginRect, wxRect& borderRect, wxRect& contentRect, wxRect& paddingRect, wxRect& outlineRect)
853 {
854 int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
855 int outlineLeft = 0, outlineRight = 0, outlineTop = 0, outlineBottom = 0;
856 int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
857 int marginLeft = 0, marginRight = 0, marginTop = 0, marginBottom = 0;
858
859 wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
860
861 if (attr.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
862 marginLeft = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetLeft());
863 if (attr.GetTextBoxAttr().GetMargins().GetRight().IsValid())
864 marginRight = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetRight());
865 if (attr.GetTextBoxAttr().GetMargins().GetTop().IsValid())
866 marginTop = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetTop());
867 if (attr.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
868 marginBottom = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetBottom());
869
870 if (attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
871 borderLeft = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
872 if (attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
873 borderRight = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
874 if (attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
875 borderTop = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
876 if (attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
877 borderBottom = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
878
879 if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
880 paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
881 if (attr.GetTextBoxAttr().GetPadding().GetRight().IsValid())
882 paddingRight = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetRight());
883 if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
884 paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
885 if (attr.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
886 paddingBottom = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetBottom());
887
888 if (attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
889 outlineLeft = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
890 if (attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
891 outlineRight = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
892 if (attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
893 outlineTop = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
894 if (attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
895 outlineBottom = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
896
897 int leftTotal = marginLeft + borderLeft + paddingLeft;
898 int rightTotal = marginRight + borderRight + paddingRight;
899 int topTotal = marginTop + borderTop + paddingTop;
900 int bottomTotal = marginBottom + borderBottom + paddingBottom;
901
902 if (marginRect != wxRect())
903 {
904 contentRect.x = marginRect.x + leftTotal;
905 contentRect.y = marginRect.y + topTotal;
906 contentRect.width = marginRect.width - (leftTotal + rightTotal);
907 contentRect.height = marginRect.height - (topTotal + bottomTotal);
908 }
909 else
910 {
911 marginRect.x = contentRect.x - leftTotal;
912 marginRect.y = contentRect.y - topTotal;
913 marginRect.width = contentRect.width + (leftTotal + rightTotal);
914 marginRect.height = contentRect.height + (topTotal + bottomTotal);
915 }
916
917 borderRect.x = marginRect.x + marginLeft;
918 borderRect.y = marginRect.y + marginTop;
919 borderRect.width = marginRect.width - (marginLeft + marginRight);
920 borderRect.height = marginRect.height - (marginTop + marginBottom);
921
922 paddingRect.x = marginRect.x + marginLeft + borderLeft;
923 paddingRect.y = marginRect.y + marginTop + borderTop;
924 paddingRect.width = marginRect.width - (marginLeft + marginRight + borderLeft + borderRight);
925 paddingRect.height = marginRect.height - (marginTop + marginBottom + borderTop + borderBottom);
926
927 // The outline is outside the margin and doesn't influence the overall box position or content size.
928 outlineRect.x = marginRect.x - outlineLeft;
929 outlineRect.y = marginRect.y - outlineTop;
930 outlineRect.width = marginRect.width + (outlineLeft + outlineRight);
931 outlineRect.height = marginRect.height + (outlineTop + outlineBottom);
932
933 return true;
934 }
935
936 // Get the total margin for the object in pixels, taking into account margin, padding and border size
937 bool wxRichTextObject::GetTotalMargin(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, int& leftMargin, int& rightMargin,
938 int& topMargin, int& bottomMargin)
939 {
940 // Assume boxRect is the area around the content
941 wxRect contentRect, marginRect, borderRect, paddingRect, outlineRect;
942 marginRect = wxRect(0, 0, 1000, 1000);
943
944 GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
945
946 leftMargin = contentRect.GetLeft() - marginRect.GetLeft();
947 rightMargin = marginRect.GetRight() - contentRect.GetRight();
948 topMargin = contentRect.GetTop() - marginRect.GetTop();
949 bottomMargin = marginRect.GetBottom() - contentRect.GetBottom();
950
951 return true;
952 }
953
954 // Returns the rectangle which the child has available to it given restrictions specified in the
955 // child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
956 wxRect wxRichTextObject::AdjustAvailableSpace(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& WXUNUSED(parentAttr), const wxRichTextAttr& childAttr, const wxRect& availableParentSpace)
957 {
958 wxRect rect = availableParentSpace;
959 double scale = 1.0;
960 if (buffer)
961 scale = buffer->GetScale();
962
963 wxTextAttrDimensionConverter converter(dc, scale, availableParentSpace.GetSize());
964
965 if (childAttr.GetTextBoxAttr().GetWidth().IsValid())
966 rect.width = converter.GetPixels(childAttr.GetTextBoxAttr().GetWidth());
967
968 if (childAttr.GetTextBoxAttr().GetHeight().IsValid())
969 rect.height = converter.GetPixels(childAttr.GetTextBoxAttr().GetHeight());
970
971 // Can specify either left or right for the position (we're assuming we can't
972 // set the left and right edges to effectively set the size. Would we want to do that?)
973 if (childAttr.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
974 {
975 rect.x = rect.x + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetLeft());
976 }
977 else if (childAttr.GetTextBoxAttr().GetPosition().GetRight().IsValid())
978 {
979 int x = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetRight());
980 if (childAttr.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
981 rect.x = availableParentSpace.x + availableParentSpace.width - rect.width;
982 else
983 rect.x += x;
984 }
985
986 if (childAttr.GetTextBoxAttr().GetPosition().GetTop().IsValid())
987 {
988 rect.y = rect.y + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetTop());
989 }
990 else if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
991 {
992 int y = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetBottom());
993 if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
994 rect.y = availableParentSpace.y + availableParentSpace.height - rect.height;
995 else
996 rect.y += y;
997 }
998
999 return rect;
1000 }
1001
1002 // Dump to output stream for debugging
1003 void wxRichTextObject::Dump(wxTextOutputStream& stream)
1004 {
1005 stream << GetClassInfo()->GetClassName() << wxT("\n");
1006 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");
1007 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");
1008 }
1009
1010 // Gets the containing buffer
1011 wxRichTextBuffer* wxRichTextObject::GetBuffer() const
1012 {
1013 const wxRichTextObject* obj = this;
1014 while (obj && !obj->IsKindOf(CLASSINFO(wxRichTextBuffer)))
1015 obj = obj->GetParent();
1016 return wxDynamicCast(obj, wxRichTextBuffer);
1017 }
1018
1019 // Get the absolute object position, by traversing up the child/parent hierarchy
1020 wxPoint wxRichTextObject::GetAbsolutePosition() const
1021 {
1022 wxPoint pt = GetPosition();
1023
1024 wxRichTextObject* p = GetParent();
1025 while (p)
1026 {
1027 pt = pt + p->GetPosition();
1028 p = p->GetParent();
1029 }
1030
1031 return pt;
1032 }
1033
1034 // Hit-testing: returns a flag indicating hit test details, plus
1035 // information about position
1036 int wxRichTextObject::HitTest(wxDC& WXUNUSED(dc), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int WXUNUSED(flags))
1037 {
1038 if (!IsShown())
1039 return wxRICHTEXT_HITTEST_NONE;
1040
1041 wxRect rect = GetRect();
1042 if (pt.x >= rect.x && pt.x < rect.x + rect.width &&
1043 pt.y >= rect.y && pt.y < rect.y + rect.height)
1044 {
1045 *obj = this;
1046 *contextObj = GetParentContainer();
1047 textPosition = GetRange().GetStart();
1048 return wxRICHTEXT_HITTEST_ON;
1049 }
1050 else
1051 return wxRICHTEXT_HITTEST_NONE;
1052 }
1053
1054 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1055 // lays out the object again using the maximum ('best') size
1056 bool wxRichTextObject::LayoutToBestSize(wxDC& dc, wxRichTextBuffer* buffer,
1057 const wxRichTextAttr& parentAttr, const wxRichTextAttr& attr, const wxRect& availableParentSpace,
1058 int style)
1059 {
1060 wxRect availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, attr, availableParentSpace);
1061 wxRect originalAvailableRect = availableChildRect;
1062 Layout(dc, availableChildRect, style);
1063
1064 wxSize maxSize = GetMaxSize();
1065
1066 // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
1067 // on this basis
1068 if (!attr.GetTextBoxAttr().GetWidth().IsValid() && maxSize.x < availableChildRect.width /* && maxSize.x > 0 */)
1069 {
1070 // Redo the layout with a fixed, minimum size this time.
1071 Invalidate(wxRICHTEXT_ALL);
1072 wxRichTextAttr newAttr(attr);
1073 newAttr.GetTextBoxAttr().GetWidth().SetValue(maxSize.x, wxTEXT_ATTR_UNITS_PIXELS);
1074 newAttr.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE);
1075
1076 availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, newAttr, availableParentSpace);
1077
1078 // If a paragraph, align the whole paragraph.
1079 // Problem with this: if we're limited by a floating object, a line may be centered
1080 // w.r.t. the smaller resulting box rather than the actual available width.
1081 if (attr.HasAlignment() && !GetContainer()->GetFloatCollector()->HasFloats()) // FIXME: aligning whole paragraph not compatible with floating objects
1082 {
1083 // centering, right-justification
1084 if (GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
1085 {
1086 availableChildRect.x = (originalAvailableRect.GetWidth() - availableChildRect.GetWidth())/2 + availableChildRect.x;
1087 }
1088 else if (GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
1089 {
1090 availableChildRect.x = availableChildRect.x + originalAvailableRect.GetWidth() - availableChildRect.GetWidth();
1091 }
1092 }
1093
1094 Layout(dc, availableChildRect, style);
1095 }
1096
1097 /*
1098 __________________
1099 | ____________ |
1100 | | | |
1101
1102
1103 */
1104
1105 return true;
1106 }
1107
1108 // Move the object recursively, by adding the offset from old to new
1109 void wxRichTextObject::Move(const wxPoint& pt)
1110 {
1111 SetPosition(pt);
1112 }
1113
1114
1115 /*!
1116 * wxRichTextCompositeObject
1117 * This is the base for drawable objects.
1118 */
1119
1120 IMPLEMENT_CLASS(wxRichTextCompositeObject, wxRichTextObject)
1121
1122 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject* parent):
1123 wxRichTextObject(parent)
1124 {
1125 }
1126
1127 wxRichTextCompositeObject::~wxRichTextCompositeObject()
1128 {
1129 DeleteChildren();
1130 }
1131
1132 /// Get the nth child
1133 wxRichTextObject* wxRichTextCompositeObject::GetChild(size_t n) const
1134 {
1135 wxASSERT ( n < m_children.GetCount() );
1136
1137 return m_children.Item(n)->GetData();
1138 }
1139
1140 /// Append a child, returning the position
1141 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject* child)
1142 {
1143 m_children.Append(child);
1144 child->SetParent(this);
1145 return m_children.GetCount() - 1;
1146 }
1147
1148 /// Insert the child in front of the given object, or at the beginning
1149 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject* child, wxRichTextObject* inFrontOf)
1150 {
1151 if (inFrontOf)
1152 {
1153 wxRichTextObjectList::compatibility_iterator node = m_children.Find(inFrontOf);
1154 m_children.Insert(node, child);
1155 }
1156 else
1157 m_children.Insert(child);
1158 child->SetParent(this);
1159
1160 return true;
1161 }
1162
1163 /// Delete the child
1164 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject* child, bool deleteChild)
1165 {
1166 wxRichTextObjectList::compatibility_iterator node = m_children.Find(child);
1167 if (node)
1168 {
1169 wxRichTextObject* obj = node->GetData();
1170 m_children.Erase(node);
1171 if (deleteChild)
1172 delete obj;
1173
1174 return true;
1175 }
1176 return false;
1177 }
1178
1179 /// Delete all children
1180 bool wxRichTextCompositeObject::DeleteChildren()
1181 {
1182 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1183 while (node)
1184 {
1185 wxRichTextObjectList::compatibility_iterator oldNode = node;
1186
1187 wxRichTextObject* child = node->GetData();
1188 child->Dereference(); // Only delete if reference count is zero
1189
1190 node = node->GetNext();
1191 m_children.Erase(oldNode);
1192 }
1193
1194 return true;
1195 }
1196
1197 /// Get the child count
1198 size_t wxRichTextCompositeObject::GetChildCount() const
1199 {
1200 return m_children.GetCount();
1201 }
1202
1203 /// Copy
1204 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject& obj)
1205 {
1206 wxRichTextObject::Copy(obj);
1207
1208 DeleteChildren();
1209
1210 wxRichTextObjectList::compatibility_iterator node = obj.m_children.GetFirst();
1211 while (node)
1212 {
1213 wxRichTextObject* child = node->GetData();
1214 wxRichTextObject* newChild = child->Clone();
1215 newChild->SetParent(this);
1216 m_children.Append(newChild);
1217
1218 node = node->GetNext();
1219 }
1220 }
1221
1222 /// Hit-testing: returns a flag indicating hit test details, plus
1223 /// information about position
1224 int wxRichTextCompositeObject::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
1225 {
1226 if (!IsShown())
1227 return wxRICHTEXT_HITTEST_NONE;
1228
1229 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1230 while (node)
1231 {
1232 wxRichTextObject* child = node->GetData();
1233
1234 if (child->IsShown() && child->IsTopLevel() && (flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS))
1235 {
1236 // Just check if we hit the overall object
1237 int ret = child->wxRichTextObject::HitTest(dc, pt, textPosition, obj, contextObj, flags);
1238 if (ret != wxRICHTEXT_HITTEST_NONE)
1239 return ret;
1240 }
1241 else if (child->IsShown())
1242 {
1243 int ret = child->HitTest(dc, pt, textPosition, obj, contextObj, flags);
1244 if (ret != wxRICHTEXT_HITTEST_NONE)
1245 return ret;
1246 }
1247
1248 node = node->GetNext();
1249 }
1250
1251 return wxRICHTEXT_HITTEST_NONE;
1252 }
1253
1254 /// Finds the absolute position and row height for the given character position
1255 bool wxRichTextCompositeObject::FindPosition(wxDC& dc, long index, wxPoint& pt, int* height, bool forceLineStart)
1256 {
1257 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1258 while (node)
1259 {
1260 wxRichTextObject* child = node->GetData();
1261
1262 // Don't recurse if the child is a top-level object,
1263 // such as a text box, because the character position will no longer
1264 // apply. By definition, a top-level object has its own range of
1265 // character positions.
1266 if (!child->IsTopLevel() && child->FindPosition(dc, index, pt, height, forceLineStart))
1267 return true;
1268
1269 node = node->GetNext();
1270 }
1271
1272 return false;
1273 }
1274
1275 /// Calculate range
1276 void wxRichTextCompositeObject::CalculateRange(long start, long& end)
1277 {
1278 long current = start;
1279 long lastEnd = current;
1280
1281 if (IsTopLevel())
1282 {
1283 current = 0;
1284 lastEnd = 0;
1285 }
1286
1287 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1288 while (node)
1289 {
1290 wxRichTextObject* child = node->GetData();
1291 long childEnd = 0;
1292
1293 child->CalculateRange(current, childEnd);
1294 lastEnd = childEnd;
1295
1296 current = childEnd + 1;
1297
1298 node = node->GetNext();
1299 }
1300
1301 if (IsTopLevel())
1302 {
1303 // A top-level object always has a range of size 1,
1304 // because its children don't count at this level.
1305 end = start;
1306 m_range.SetRange(start, start);
1307
1308 // An object with no children has zero length
1309 if (m_children.GetCount() == 0)
1310 lastEnd --;
1311 m_ownRange.SetRange(0, lastEnd);
1312 }
1313 else
1314 {
1315 end = lastEnd;
1316
1317 // An object with no children has zero length
1318 if (m_children.GetCount() == 0)
1319 end --;
1320
1321 m_range.SetRange(start, end);
1322 }
1323 }
1324
1325 /// Delete range from layout.
1326 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange& range)
1327 {
1328 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1329
1330 while (node)
1331 {
1332 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
1333 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1334
1335 // Delete the range in each paragraph
1336
1337 // When a chunk has been deleted, internally the content does not
1338 // now match the ranges.
1339 // However, so long as deletion is not done on the same object twice this is OK.
1340 // If you may delete content from the same object twice, recalculate
1341 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
1342 // adjust the range you're deleting accordingly.
1343
1344 if (!obj->GetRange().IsOutside(range))
1345 {
1346 // No need to delete within a top-level object; just removing this object will do fine
1347 if (!obj->IsTopLevel())
1348 obj->DeleteRange(range);
1349
1350 // Delete an empty object, or paragraph within this range.
1351 if (obj->IsEmpty() ||
1352 (range.GetStart() <= obj->GetRange().GetStart() && range.GetEnd() >= obj->GetRange().GetEnd()))
1353 {
1354 // An empty paragraph has length 1, so won't be deleted unless the
1355 // whole range is deleted.
1356 RemoveChild(obj, true);
1357 }
1358 }
1359
1360 node = next;
1361 }
1362
1363 return true;
1364 }
1365
1366 /// Get any text in this object for the given range
1367 wxString wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange& range) const
1368 {
1369 wxString text;
1370 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1371 while (node)
1372 {
1373 wxRichTextObject* child = node->GetData();
1374 wxRichTextRange childRange = range;
1375 if (!child->GetRange().IsOutside(range))
1376 {
1377 childRange.LimitTo(child->GetRange());
1378
1379 wxString childText = child->GetTextForRange(childRange);
1380
1381 text += childText;
1382 }
1383 node = node->GetNext();
1384 }
1385
1386 return text;
1387 }
1388
1389 /// Get the child object at the given character position
1390 wxRichTextObject* wxRichTextCompositeObject::GetChildAtPosition(long pos) const
1391 {
1392 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1393 while (node)
1394 {
1395 wxRichTextObject* child = node->GetData();
1396 if (child->GetRange().GetStart() == pos)
1397 return child;
1398 node = node->GetNext();
1399 }
1400 return NULL;
1401 }
1402
1403 /// Recursively merge all pieces that can be merged.
1404 bool wxRichTextCompositeObject::Defragment(const wxRichTextRange& range)
1405 {
1406 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1407 while (node)
1408 {
1409 wxRichTextObject* child = node->GetData();
1410 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
1411 {
1412 wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject);
1413 if (composite)
1414 composite->Defragment();
1415
1416 if (node->GetNext())
1417 {
1418 wxRichTextObject* nextChild = node->GetNext()->GetData();
1419 if (child->CanMerge(nextChild) && child->Merge(nextChild))
1420 {
1421 nextChild->Dereference();
1422 m_children.Erase(node->GetNext());
1423
1424 // Don't set node -- we'll see if we can merge again with the next
1425 // child.
1426 }
1427 else
1428 node = node->GetNext();
1429 }
1430 else
1431 node = node->GetNext();
1432 }
1433 else
1434 node = node->GetNext();
1435 }
1436
1437 // Delete any remaining empty objects, but leave at least one empty object per composite object.
1438 if (GetChildCount() > 1)
1439 {
1440 node = m_children.GetFirst();
1441 while (node)
1442 {
1443 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1444 wxRichTextObject* child = node->GetData();
1445 if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
1446 {
1447 if (child->IsEmpty())
1448 {
1449 child->Dereference();
1450 m_children.Erase(node);
1451 }
1452 node = next;
1453 }
1454 else
1455 node = node->GetNext();
1456 }
1457 }
1458
1459 return true;
1460 }
1461
1462 /// Dump to output stream for debugging
1463 void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
1464 {
1465 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1466 while (node)
1467 {
1468 wxRichTextObject* child = node->GetData();
1469 child->Dump(stream);
1470 node = node->GetNext();
1471 }
1472 }
1473
1474 /// Get/set the object size for the given range. Returns false if the range
1475 /// is invalid for this object.
1476 bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* partialExtents) const
1477 {
1478 if (!range.IsWithin(GetRange()))
1479 return false;
1480
1481 wxSize sz;
1482
1483 wxArrayInt childExtents;
1484 wxArrayInt* p;
1485 if (partialExtents)
1486 p = & childExtents;
1487 else
1488 p = NULL;
1489
1490 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1491 while (node)
1492 {
1493 wxRichTextObject* child = node->GetData();
1494 if (!child->GetRange().IsOutside(range))
1495 {
1496 // Floating objects have a zero size within the paragraph.
1497 if (child->IsFloating())
1498 {
1499 if (partialExtents)
1500 {
1501 int lastSize;
1502 if (partialExtents->GetCount() > 0)
1503 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1504 else
1505 lastSize = 0;
1506
1507 partialExtents->Add(0 /* zero size */ + lastSize);
1508 }
1509 }
1510 else
1511 {
1512 wxSize childSize;
1513
1514 wxRichTextRange rangeToUse = range;
1515 rangeToUse.LimitTo(child->GetRange());
1516 if (child->IsTopLevel())
1517 rangeToUse = child->GetOwnRange();
1518
1519 int childDescent = 0;
1520
1521 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
1522 // but it's only going to be used after caching has taken place.
1523 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
1524 {
1525 childDescent = child->GetDescent();
1526 childSize = child->GetCachedSize();
1527
1528 sz.y = wxMax(sz.y, childSize.y);
1529 sz.x += childSize.x;
1530 descent = wxMax(descent, childDescent);
1531 }
1532 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, flags, wxPoint(position.x + sz.x, position.y), p))
1533 {
1534 sz.y = wxMax(sz.y, childSize.y);
1535 sz.x += childSize.x;
1536 descent = wxMax(descent, childDescent);
1537
1538 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange() || child->IsTopLevel()))
1539 {
1540 child->SetCachedSize(childSize);
1541 child->SetDescent(childDescent);
1542 }
1543
1544 if (partialExtents)
1545 {
1546 int lastSize;
1547 if (partialExtents->GetCount() > 0)
1548 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
1549 else
1550 lastSize = 0;
1551
1552 size_t i;
1553 for (i = 0; i < childExtents.GetCount(); i++)
1554 {
1555 partialExtents->Add(childExtents[i] + lastSize);
1556 }
1557 }
1558 }
1559 }
1560
1561 if (p)
1562 p->Clear();
1563 }
1564
1565 node = node->GetNext();
1566 }
1567 size = sz;
1568 return true;
1569 }
1570
1571 // Invalidate the buffer. With no argument, invalidates whole buffer.
1572 void wxRichTextCompositeObject::Invalidate(const wxRichTextRange& invalidRange)
1573 {
1574 wxRichTextObject::Invalidate(invalidRange);
1575
1576 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1577 while (node)
1578 {
1579 wxRichTextObject* child = node->GetData();
1580 if (invalidRange != wxRICHTEXT_ALL && invalidRange != wxRICHTEXT_NONE && child->GetRange().IsOutside(invalidRange))
1581 {
1582 // Skip
1583 }
1584 else if (child->IsTopLevel())
1585 {
1586 if (invalidRange == wxRICHTEXT_NONE)
1587 child->Invalidate(wxRICHTEXT_NONE);
1588 else
1589 child->Invalidate(wxRICHTEXT_ALL); // All children must be invalidated if within parent range
1590 }
1591 else
1592 child->Invalidate(invalidRange);
1593 node = node->GetNext();
1594 }
1595 }
1596
1597 // Move the object recursively, by adding the offset from old to new
1598 void wxRichTextCompositeObject::Move(const wxPoint& pt)
1599 {
1600 wxPoint oldPos = GetPosition();
1601 SetPosition(pt);
1602 wxPoint offset = pt - oldPos;
1603
1604 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1605 while (node)
1606 {
1607 wxRichTextObject* child = node->GetData();
1608 wxPoint childPos = child->GetPosition() + offset;
1609 child->Move(childPos);
1610 node = node->GetNext();
1611 }
1612 }
1613
1614
1615 /*!
1616 * wxRichTextParagraphLayoutBox
1617 * This box knows how to lay out paragraphs.
1618 */
1619
1620 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextCompositeObject)
1621
1622 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject* parent):
1623 wxRichTextCompositeObject(parent)
1624 {
1625 Init();
1626 }
1627
1628 wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
1629 {
1630 if (m_floatCollector)
1631 {
1632 delete m_floatCollector;
1633 m_floatCollector = NULL;
1634 }
1635 }
1636
1637 /// Initialize the object.
1638 void wxRichTextParagraphLayoutBox::Init()
1639 {
1640 m_ctrl = NULL;
1641
1642 // For now, assume is the only box and has no initial size.
1643 m_range = wxRichTextRange(0, -1);
1644 m_ownRange = wxRichTextRange(0, -1);
1645
1646 m_invalidRange = wxRICHTEXT_ALL;
1647
1648 SetMargins(4);
1649 m_partialParagraph = false;
1650 m_floatCollector = NULL;
1651 }
1652
1653 void wxRichTextParagraphLayoutBox::Clear()
1654 {
1655 DeleteChildren();
1656
1657 if (m_floatCollector)
1658 delete m_floatCollector;
1659 m_floatCollector = NULL;
1660 m_partialParagraph = false;
1661 }
1662
1663 /// Copy
1664 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
1665 {
1666 Clear();
1667
1668 wxRichTextCompositeObject::Copy(obj);
1669
1670 m_partialParagraph = obj.m_partialParagraph;
1671 m_defaultAttributes = obj.m_defaultAttributes;
1672 }
1673
1674 // Gather information about floating objects; only gather floats for those paragraphs that
1675 // will not be formatted again due to optimization, after which floats will be gathered per-paragraph
1676 // during layout.
1677 bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect& availableRect, wxRichTextObject* untilObj)
1678 {
1679 if (m_floatCollector != NULL)
1680 delete m_floatCollector;
1681 m_floatCollector = new wxRichTextFloatCollector(availableRect);
1682 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1683 // Only gather floats up to the point we'll start formatting paragraphs.
1684 while (untilObj && node && node->GetData() != untilObj)
1685 {
1686 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1687 wxASSERT (child != NULL);
1688 if (child)
1689 m_floatCollector->CollectFloat(child);
1690 node = node->GetNext();
1691 }
1692
1693 return true;
1694 }
1695
1696 // Returns the style sheet associated with the overall buffer.
1697 wxRichTextStyleSheet* wxRichTextParagraphLayoutBox::GetStyleSheet() const
1698 {
1699 return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet*) NULL;
1700 }
1701
1702 // Get the number of floating objects at this level
1703 int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
1704 {
1705 if (m_floatCollector)
1706 return m_floatCollector->GetFloatingObjectCount();
1707 else
1708 return 0;
1709 }
1710
1711 // Get a list of floating objects
1712 bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList& objects) const
1713 {
1714 if (m_floatCollector)
1715 {
1716 return m_floatCollector->GetFloatingObjects(objects);
1717 }
1718 else
1719 return false;
1720 }
1721
1722 // Calculate ranges
1723 void wxRichTextParagraphLayoutBox::UpdateRanges()
1724 {
1725 long start = 0;
1726 if (GetParent())
1727 start = GetRange().GetStart();
1728 long end;
1729 CalculateRange(start, end);
1730 }
1731
1732 // HitTest
1733 int wxRichTextParagraphLayoutBox::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
1734 {
1735 if (!IsShown())
1736 return wxRICHTEXT_HITTEST_NONE;
1737
1738 int ret = wxRICHTEXT_HITTEST_NONE;
1739 if (m_floatCollector && (flags & wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS) == 0)
1740 ret = m_floatCollector->HitTest(dc, pt, textPosition, obj, flags);
1741
1742 if (ret == wxRICHTEXT_HITTEST_NONE)
1743 return wxRichTextCompositeObject::HitTest(dc, pt, textPosition, obj, contextObj, flags);
1744 else
1745 {
1746 *contextObj = this;
1747 return ret;
1748 }
1749 }
1750
1751 /// Draw the floating objects
1752 void wxRichTextParagraphLayoutBox::DrawFloats(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
1753 {
1754 if (m_floatCollector)
1755 m_floatCollector->Draw(dc, range, selection, rect, descent, style);
1756 }
1757
1758 void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextObject* obj)
1759 {
1760 if (from == to)
1761 return;
1762
1763 from->RemoveChild(obj);
1764 to->AppendChild(obj);
1765 }
1766
1767 /// Draw the item
1768 bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
1769 {
1770 if (!IsShown())
1771 return true;
1772
1773 wxRect thisRect(GetPosition(), GetCachedSize());
1774
1775 int flags = style;
1776 if (selection.IsValid() && GetParentContainer() != this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer()))
1777 flags |= wxRICHTEXT_DRAW_SELECTED;
1778
1779 // Don't draw guidelines if at top level
1780 int theseFlags = flags;
1781 if (!GetParent())
1782 theseFlags &= ~wxRICHTEXT_DRAW_GUIDELINES;
1783 DrawBoxAttributes(dc, GetBuffer(), GetAttributes(), thisRect, theseFlags);
1784
1785 DrawFloats(dc, range, selection, rect, descent, style);
1786 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1787 while (node)
1788 {
1789 wxRichTextObject* child = node->GetData();
1790
1791 if (child && !child->GetRange().IsOutside(range))
1792 {
1793 wxRect childRect(child->GetPosition(), child->GetCachedSize());
1794 wxRichTextRange childRange = range;
1795 if (child->IsTopLevel())
1796 {
1797 childRange = child->GetOwnRange();
1798 }
1799
1800 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetTop() > rect.GetBottom())
1801 {
1802 // Stop drawing
1803 break;
1804 }
1805 else if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetBottom() < rect.GetTop())
1806 {
1807 // Skip
1808 }
1809 else
1810 child->Draw(dc, childRange, selection, rect, descent, style);
1811 }
1812
1813 node = node->GetNext();
1814 }
1815 return true;
1816 }
1817
1818 /// Lay the item out
1819 bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, int style)
1820 {
1821 SetPosition(rect.GetPosition());
1822
1823 if (!IsShown())
1824 return true;
1825
1826 wxRect availableSpace;
1827 bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
1828
1829 // If only laying out a specific area, the passed rect has a different meaning:
1830 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
1831 // so that during a size, only the visible part will be relaid out, or
1832 // it would take too long causing flicker. As an approximation, we assume that
1833 // everything up to the start of the visible area is laid out correctly.
1834 if (formatRect)
1835 {
1836 wxRect rect2(0, 0, rect.width, rect.height);
1837 availableSpace = GetAvailableContentArea(dc, rect2);
1838
1839 // Invalidate the part of the buffer from the first visible line
1840 // to the end. If other parts of the buffer are currently invalid,
1841 // then they too will be taken into account if they are above
1842 // the visible point.
1843 long startPos = 0;
1844 wxRichTextLine* line = GetLineAtYPosition(rect.y);
1845 if (line)
1846 startPos = line->GetAbsoluteRange().GetStart();
1847
1848 Invalidate(wxRichTextRange(startPos, GetOwnRange().GetEnd()));
1849 }
1850 else
1851 {
1852 availableSpace = GetAvailableContentArea(dc, rect);
1853 }
1854
1855 int leftMargin, rightMargin, topMargin, bottomMargin;
1856 wxRichTextObject::GetTotalMargin(dc, GetBuffer(), GetAttributes(), leftMargin, rightMargin,
1857 topMargin, bottomMargin);
1858
1859 int maxWidth = 0;
1860 int maxHeight = 0;
1861
1862 // The maximum paragraph maximum width, so we can set the overall maximum width for this object
1863 int maxMaxWidth = 0;
1864
1865 // The maximum paragraph minimum width, so we can set the overall minimum width for this object
1866 int maxMinWidth = 0;
1867
1868 // If we have vertical alignment, we must recalculate everything.
1869 bool hasVerticalAlignment = (GetAttributes().GetTextBoxAttr().HasVerticalAlignment() &&
1870 (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP));
1871
1872 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1873
1874 bool layoutAll = true;
1875
1876 // Get invalid range, rounding to paragraph start/end.
1877 wxRichTextRange invalidRange = GetInvalidRange(true);
1878
1879 if (invalidRange == wxRICHTEXT_NONE && !formatRect)
1880 return true;
1881
1882 if (invalidRange == wxRICHTEXT_ALL || hasVerticalAlignment)
1883 layoutAll = true;
1884 else // If we know what range is affected, start laying out from that point on.
1885 if (invalidRange.GetStart() >= GetOwnRange().GetStart())
1886 {
1887 wxRichTextParagraph* firstParagraph = GetParagraphAtPosition(invalidRange.GetStart());
1888 if (firstParagraph)
1889 {
1890 wxRichTextObjectList::compatibility_iterator firstNode = m_children.Find(firstParagraph);
1891 wxRichTextObjectList::compatibility_iterator previousNode;
1892 if ( firstNode )
1893 previousNode = firstNode->GetPrevious();
1894 if (firstNode)
1895 {
1896 if (previousNode)
1897 {
1898 wxRichTextParagraph* previousParagraph = wxDynamicCast(previousNode->GetData(), wxRichTextParagraph);
1899 availableSpace.y = previousParagraph->GetPosition().y + previousParagraph->GetCachedSize().y;
1900 }
1901
1902 // Now we're going to start iterating from the first affected paragraph.
1903 node = firstNode;
1904
1905 layoutAll = false;
1906 }
1907 }
1908 }
1909
1910 // Gather information about only those floating objects that will not be formatted,
1911 // after which floats will be gathered per-paragraph during layout.
1912 UpdateFloatingObjects(availableSpace, node ? node->GetData() : (wxRichTextObject*) NULL);
1913
1914 // A way to force speedy rest-of-buffer layout (the 'else' below)
1915 bool forceQuickLayout = false;
1916
1917 while (node)
1918 {
1919 // Assume this box only contains paragraphs
1920
1921 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1922 // Unsure if this is needed
1923 // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
1924
1925 if (child && child->IsShown())
1926 {
1927 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
1928 if ( !forceQuickLayout &&
1929 (layoutAll ||
1930 child->GetLines().IsEmpty() ||
1931 !child->GetRange().IsOutside(invalidRange)) )
1932 {
1933 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1934 // lays out the object again using the minimum size
1935 child->LayoutToBestSize(dc, GetBuffer(),
1936 GetAttributes(), child->GetAttributes(), availableSpace, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
1937
1938 // Layout must set the cached size
1939 availableSpace.y += child->GetCachedSize().y;
1940 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
1941 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
1942 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
1943
1944 // If we're just formatting the visible part of the buffer,
1945 // and we're now past the bottom of the window, and we don't have any
1946 // floating objects (since they may cause wrapping to change for the rest of the
1947 // the buffer), start quick layout.
1948 if (!hasVerticalAlignment && formatRect && child->GetPosition().y > rect.GetBottom() && GetFloatingObjectCount() == 0)
1949 forceQuickLayout = true;
1950 }
1951 else
1952 {
1953 // We're outside the immediately affected range, so now let's just
1954 // move everything up or down. This assumes that all the children have previously
1955 // been laid out and have wrapped line lists associated with them.
1956 // TODO: check all paragraphs before the affected range.
1957
1958 int inc = availableSpace.y - child->GetPosition().y;
1959
1960 while (node)
1961 {
1962 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1963 if (child)
1964 {
1965 if (child->GetLines().GetCount() == 0)
1966 {
1967 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
1968 // lays out the object again using the minimum size
1969 child->LayoutToBestSize(dc, GetBuffer(),
1970 GetAttributes(), child->GetAttributes(), availableSpace, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
1971
1972 //child->Layout(dc, availableChildRect, style);
1973 }
1974 else
1975 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + inc));
1976
1977 availableSpace.y += child->GetCachedSize().y;
1978 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
1979 maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
1980 maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
1981 }
1982
1983 node = node->GetNext();
1984 }
1985 break;
1986 }
1987 }
1988
1989 node = node->GetNext();
1990 }
1991
1992 node = m_children.GetLast();
1993 if (node && node->GetData()->IsShown())
1994 {
1995 wxRichTextObject* child = node->GetData();
1996 // maxHeight = (child->GetPosition().y - GetPosition().y) + child->GetCachedSize().y;
1997 maxHeight = child->GetPosition().y - (GetPosition().y + topMargin) + child->GetCachedSize().y;
1998 }
1999 else
2000 maxHeight = 0; // topMargin + bottomMargin;
2001
2002 // TODO: (also in para layout) should set the
2003 // object's size to an absolute one if specified,
2004 // but if not specified, calculate it from content.
2005
2006 // We need to add back the margins etc.
2007 {
2008 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2009 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, maxHeight));
2010 GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
2011 SetCachedSize(marginRect.GetSize());
2012 }
2013
2014 // The maximum size is the greatest of all maximum widths for all paragraphs.
2015 {
2016 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2017 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMaxWidth, maxHeight)); // Actually max height is a lie, we can't know it
2018 GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
2019 SetMaxSize(marginRect.GetSize());
2020 }
2021
2022 // The minimum size is the greatest of all minimum widths for all paragraphs.
2023 {
2024 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
2025 contentRect = wxRect(wxPoint(0, 0), wxSize(maxMinWidth, maxHeight)); // Actually max height is a lie, we can't know it
2026 GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
2027 SetMinSize(marginRect.GetSize());
2028 }
2029
2030 if (GetAttributes().GetTextBoxAttr().HasVerticalAlignment() &&
2031 (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP))
2032 {
2033 int yOffset = 0;
2034 int leftOverSpace = availableSpace.height - topMargin - bottomMargin - maxHeight;
2035 if (leftOverSpace > 0)
2036 {
2037 if (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE)
2038 {
2039 yOffset = (leftOverSpace/2);
2040 }
2041 else if (GetAttributes().GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM)
2042 {
2043 yOffset = leftOverSpace;
2044 }
2045 }
2046
2047 // Move all the children to vertically align the content
2048 // This doesn't take into account floating objects, unfortunately.
2049 if (yOffset != 0)
2050 {
2051 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2052 while (node)
2053 {
2054 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2055 if (child)
2056 child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + yOffset));
2057
2058 node = node->GetNext();
2059 }
2060 }
2061 }
2062
2063 m_invalidRange = wxRICHTEXT_NONE;
2064
2065 return true;
2066 }
2067
2068 /// Get/set the size for the given range.
2069 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* WXUNUSED(partialExtents)) const
2070 {
2071 wxSize sz;
2072
2073 wxRichTextObjectList::compatibility_iterator startPara = wxRichTextObjectList::compatibility_iterator();
2074 wxRichTextObjectList::compatibility_iterator endPara = wxRichTextObjectList::compatibility_iterator();
2075
2076 // First find the first paragraph whose starting position is within the range.
2077 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2078 while (node)
2079 {
2080 // child is a paragraph
2081 wxRichTextObject* child = node->GetData();
2082 const wxRichTextRange& r = child->GetRange();
2083
2084 if (r.GetStart() <= range.GetStart() && r.GetEnd() >= range.GetStart())
2085 {
2086 startPara = node;
2087 break;
2088 }
2089
2090 node = node->GetNext();
2091 }
2092
2093 // Next find the last paragraph containing part of the range
2094 node = m_children.GetFirst();
2095 while (node)
2096 {
2097 // child is a paragraph
2098 wxRichTextObject* child = node->GetData();
2099 const wxRichTextRange& r = child->GetRange();
2100
2101 if (r.GetStart() <= range.GetEnd() && r.GetEnd() >= range.GetEnd())
2102 {
2103 endPara = node;
2104 break;
2105 }
2106
2107 node = node->GetNext();
2108 }
2109
2110 if (!startPara || !endPara)
2111 return false;
2112
2113 // Now we can add up the sizes
2114 for (node = startPara; node ; node = node->GetNext())
2115 {
2116 // child is a paragraph
2117 wxRichTextObject* child = node->GetData();
2118 const wxRichTextRange& childRange = child->GetRange();
2119 wxRichTextRange rangeToFind = range;
2120 rangeToFind.LimitTo(childRange);
2121
2122 if (child->IsTopLevel())
2123 rangeToFind = child->GetOwnRange();
2124
2125 wxSize childSize;
2126
2127 int childDescent = 0;
2128 child->GetRangeSize(rangeToFind, childSize, childDescent, dc, flags, position);
2129
2130 descent = wxMax(childDescent, descent);
2131
2132 sz.x = wxMax(sz.x, childSize.x);
2133 sz.y += childSize.y;
2134
2135 if (node == endPara)
2136 break;
2137 }
2138
2139 size = sz;
2140
2141 return true;
2142 }
2143
2144 /// Get the paragraph at the given position
2145 wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos, bool caretPosition) const
2146 {
2147 if (caretPosition)
2148 pos ++;
2149
2150 // First find the first paragraph whose starting position is within the range.
2151 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2152 while (node)
2153 {
2154 // child is a paragraph
2155 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2156 // wxASSERT (child != NULL);
2157
2158 if (child)
2159 {
2160 // Return first child in buffer if position is -1
2161 // if (pos == -1)
2162 // return child;
2163
2164 if (child->GetRange().Contains(pos))
2165 return child;
2166 }
2167
2168 node = node->GetNext();
2169 }
2170 return NULL;
2171 }
2172
2173 /// Get the line at the given position
2174 wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos, bool caretPosition) const
2175 {
2176 if (caretPosition)
2177 pos ++;
2178
2179 // First find the first paragraph whose starting position is within the range.
2180 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2181 while (node)
2182 {
2183 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
2184 if (obj->GetRange().Contains(pos))
2185 {
2186 // child is a paragraph
2187 wxRichTextParagraph* child = wxDynamicCast(obj, wxRichTextParagraph);
2188 // wxASSERT (child != NULL);
2189
2190 if (child)
2191 {
2192 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2193 while (node2)
2194 {
2195 wxRichTextLine* line = node2->GetData();
2196
2197 wxRichTextRange range = line->GetAbsoluteRange();
2198
2199 if (range.Contains(pos) ||
2200
2201 // If the position is end-of-paragraph, then return the last line of
2202 // of the paragraph.
2203 ((range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd())))
2204 return line;
2205
2206 node2 = node2->GetNext();
2207 }
2208 }
2209 }
2210
2211 node = node->GetNext();
2212 }
2213
2214 int lineCount = GetLineCount();
2215 if (lineCount > 0)
2216 return GetLineForVisibleLineNumber(lineCount-1);
2217 else
2218 return NULL;
2219 }
2220
2221 /// Get the line at the given y pixel position, or the last line.
2222 wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y) const
2223 {
2224 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2225 while (node)
2226 {
2227 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2228 // wxASSERT (child != NULL);
2229
2230 if (child)
2231 {
2232 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2233 while (node2)
2234 {
2235 wxRichTextLine* line = node2->GetData();
2236
2237 wxRect rect(line->GetRect());
2238
2239 if (y <= rect.GetBottom())
2240 return line;
2241
2242 node2 = node2->GetNext();
2243 }
2244 }
2245
2246 node = node->GetNext();
2247 }
2248
2249 // Return last line
2250 int lineCount = GetLineCount();
2251 if (lineCount > 0)
2252 return GetLineForVisibleLineNumber(lineCount-1);
2253 else
2254 return NULL;
2255 }
2256
2257 /// Get the number of visible lines
2258 int wxRichTextParagraphLayoutBox::GetLineCount() const
2259 {
2260 int count = 0;
2261
2262 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2263 while (node)
2264 {
2265 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2266 // wxASSERT (child != NULL);
2267
2268 if (child)
2269 count += child->GetLines().GetCount();
2270
2271 node = node->GetNext();
2272 }
2273 return count;
2274 }
2275
2276
2277 /// Get the paragraph for a given line
2278 wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine* line) const
2279 {
2280 return GetParagraphAtPosition(line->GetAbsoluteRange().GetStart());
2281 }
2282
2283 /// Get the line size at the given position
2284 wxSize wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos, bool caretPosition) const
2285 {
2286 wxRichTextLine* line = GetLineAtPosition(pos, caretPosition);
2287 if (line)
2288 {
2289 return line->GetSize();
2290 }
2291 else
2292 return wxSize(0, 0);
2293 }
2294
2295
2296 /// Convenience function to add a paragraph of text
2297 wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph(const wxString& text, wxRichTextAttr* paraStyle)
2298 {
2299 // Don't use the base style, just the default style, and the base style will
2300 // be combined at display time.
2301 // Divide into paragraph and character styles.
2302
2303 wxRichTextAttr defaultCharStyle;
2304 wxRichTextAttr defaultParaStyle;
2305
2306 // If the default style is a named paragraph style, don't apply any character formatting
2307 // to the initial text string.
2308 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2309 {
2310 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2311 if (def)
2312 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2313 }
2314 else
2315 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2316
2317 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2318 wxRichTextAttr* cStyle = & defaultCharStyle;
2319
2320 wxRichTextParagraph* para = new wxRichTextParagraph(text, this, pStyle, cStyle);
2321
2322 AppendChild(para);
2323
2324 UpdateRanges();
2325
2326 return para->GetRange();
2327 }
2328
2329 /// Adds multiple paragraphs, based on newlines.
2330 wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text, wxRichTextAttr* paraStyle)
2331 {
2332 // Don't use the base style, just the default style, and the base style will
2333 // be combined at display time.
2334 // Divide into paragraph and character styles.
2335
2336 wxRichTextAttr defaultCharStyle;
2337 wxRichTextAttr defaultParaStyle;
2338
2339 // If the default style is a named paragraph style, don't apply any character formatting
2340 // to the initial text string.
2341 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2342 {
2343 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2344 if (def)
2345 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2346 }
2347 else
2348 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2349
2350 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2351 wxRichTextAttr* cStyle = & defaultCharStyle;
2352
2353 wxRichTextParagraph* firstPara = NULL;
2354 wxRichTextParagraph* lastPara = NULL;
2355
2356 wxRichTextRange range(-1, -1);
2357
2358 size_t i = 0;
2359 size_t len = text.length();
2360 wxString line;
2361 wxRichTextParagraph* para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
2362
2363 AppendChild(para);
2364
2365 firstPara = para;
2366 lastPara = para;
2367
2368 while (i < len)
2369 {
2370 wxChar ch = text[i];
2371 if (ch == wxT('\n') || ch == wxT('\r'))
2372 {
2373 if (i != (len-1))
2374 {
2375 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2376 plainText->SetText(line);
2377
2378 para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
2379
2380 AppendChild(para);
2381
2382 lastPara = para;
2383 line = wxEmptyString;
2384 }
2385 }
2386 else
2387 line += ch;
2388
2389 i ++;
2390 }
2391
2392 if (!line.empty())
2393 {
2394 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
2395 plainText->SetText(line);
2396 }
2397
2398 UpdateRanges();
2399
2400 return wxRichTextRange(firstPara->GetRange().GetStart(), lastPara->GetRange().GetEnd());
2401 }
2402
2403 /// Convenience function to add an image
2404 wxRichTextRange wxRichTextParagraphLayoutBox::AddImage(const wxImage& image, wxRichTextAttr* paraStyle)
2405 {
2406 // Don't use the base style, just the default style, and the base style will
2407 // be combined at display time.
2408 // Divide into paragraph and character styles.
2409
2410 wxRichTextAttr defaultCharStyle;
2411 wxRichTextAttr defaultParaStyle;
2412
2413 // If the default style is a named paragraph style, don't apply any character formatting
2414 // to the initial text string.
2415 if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
2416 {
2417 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
2418 if (def)
2419 defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
2420 }
2421 else
2422 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
2423
2424 wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
2425 wxRichTextAttr* cStyle = & defaultCharStyle;
2426
2427 wxRichTextParagraph* para = new wxRichTextParagraph(this, pStyle);
2428 AppendChild(para);
2429 para->AppendChild(new wxRichTextImage(image, this, cStyle));
2430
2431 UpdateRanges();
2432
2433 return para->GetRange();
2434 }
2435
2436
2437 /// Insert fragment into this box at the given position. If partialParagraph is true,
2438 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
2439 /// marker.
2440
2441 bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextParagraphLayoutBox& fragment)
2442 {
2443 // First, find the first paragraph whose starting position is within the range.
2444 wxRichTextParagraph* para = GetParagraphAtPosition(position);
2445 if (para)
2446 {
2447 wxRichTextAttr originalAttr = para->GetAttributes();
2448
2449 wxRichTextObjectList::compatibility_iterator node = m_children.Find(para);
2450
2451 // Now split at this position, returning the object to insert the new
2452 // ones in front of.
2453 wxRichTextObject* nextObject = para->SplitAt(position);
2454
2455 // Special case: partial paragraph, just one paragraph. Might be a small amount of
2456 // text, for example, so let's optimize.
2457
2458 if (fragment.GetPartialParagraph() && fragment.GetChildren().GetCount() == 1)
2459 {
2460 // Add the first para to this para...
2461 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2462 if (!firstParaNode)
2463 return false;
2464
2465 // Iterate through the fragment paragraph inserting the content into this paragraph.
2466 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2467 wxASSERT (firstPara != NULL);
2468
2469 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
2470 while (objectNode)
2471 {
2472 wxRichTextObject* newObj = objectNode->GetData()->Clone();
2473
2474 if (!nextObject)
2475 {
2476 // Append
2477 para->AppendChild(newObj);
2478 }
2479 else
2480 {
2481 // Insert before nextObject
2482 para->InsertChild(newObj, nextObject);
2483 }
2484
2485 objectNode = objectNode->GetNext();
2486 }
2487
2488 return true;
2489 }
2490 else
2491 {
2492 // Procedure for inserting a fragment consisting of a number of
2493 // paragraphs:
2494 //
2495 // 1. Remove and save the content that's after the insertion point, for adding
2496 // back once we've added the fragment.
2497 // 2. Add the content from the first fragment paragraph to the current
2498 // paragraph.
2499 // 3. Add remaining fragment paragraphs after the current paragraph.
2500 // 4. Add back the saved content from the first paragraph. If partialParagraph
2501 // is true, add it to the last paragraph added and not a new one.
2502
2503 // 1. Remove and save objects after split point.
2504 wxList savedObjects;
2505 if (nextObject)
2506 para->MoveToList(nextObject, savedObjects);
2507
2508 // 2. Add the content from the 1st fragment paragraph.
2509 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
2510 if (!firstParaNode)
2511 return false;
2512
2513 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
2514 wxASSERT(firstPara != NULL);
2515
2516 if (!(fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE))
2517 para->SetAttributes(firstPara->GetAttributes());
2518
2519 // Save empty paragraph attributes for appending later
2520 // These are character attributes deliberately set for a new paragraph. Without this,
2521 // we couldn't pass default attributes when appending a new paragraph.
2522 wxRichTextAttr emptyParagraphAttributes;
2523
2524 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
2525
2526 if (objectNode && firstPara->GetChildren().GetCount() == 1 && objectNode->GetData()->IsEmpty())
2527 emptyParagraphAttributes = objectNode->GetData()->GetAttributes();
2528
2529 while (objectNode)
2530 {
2531 wxRichTextObject* newObj = objectNode->GetData()->Clone();
2532
2533 // Append
2534 para->AppendChild(newObj);
2535
2536 objectNode = objectNode->GetNext();
2537 }
2538
2539 // 3. Add remaining fragment paragraphs after the current paragraph.
2540 wxRichTextObjectList::compatibility_iterator nextParagraphNode = node->GetNext();
2541 wxRichTextObject* nextParagraph = NULL;
2542 if (nextParagraphNode)
2543 nextParagraph = nextParagraphNode->GetData();
2544
2545 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst()->GetNext();
2546 wxRichTextParagraph* finalPara = para;
2547
2548 bool needExtraPara = (!i || !fragment.GetPartialParagraph());
2549
2550 // If there was only one paragraph, we need to insert a new one.
2551 while (i)
2552 {
2553 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2554 wxASSERT( para != NULL );
2555
2556 finalPara = (wxRichTextParagraph*) para->Clone();
2557
2558 if (nextParagraph)
2559 InsertChild(finalPara, nextParagraph);
2560 else
2561 AppendChild(finalPara);
2562
2563 i = i->GetNext();
2564 }
2565
2566 // If there was only one paragraph, or we have full paragraphs in our fragment,
2567 // we need to insert a new one.
2568 if (needExtraPara)
2569 {
2570 finalPara = new wxRichTextParagraph;
2571
2572 if (nextParagraph)
2573 InsertChild(finalPara, nextParagraph);
2574 else
2575 AppendChild(finalPara);
2576 }
2577
2578 // 4. Add back the remaining content.
2579 if (finalPara)
2580 {
2581 if (nextObject)
2582 finalPara->MoveFromList(savedObjects);
2583
2584 // Ensure there's at least one object
2585 if (finalPara->GetChildCount() == 0)
2586 {
2587 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
2588 text->SetAttributes(emptyParagraphAttributes);
2589
2590 finalPara->AppendChild(text);
2591 }
2592 }
2593
2594 if ((fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE) && firstPara)
2595 finalPara->SetAttributes(firstPara->GetAttributes());
2596 else if (finalPara && finalPara != para)
2597 finalPara->SetAttributes(originalAttr);
2598
2599 return true;
2600 }
2601 }
2602 else
2603 {
2604 // Append
2605 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst();
2606 while (i)
2607 {
2608 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2609 wxASSERT( para != NULL );
2610
2611 AppendChild(para->Clone());
2612
2613 i = i->GetNext();
2614 }
2615
2616 return true;
2617 }
2618 }
2619
2620 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
2621 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
2622 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange& range, wxRichTextParagraphLayoutBox& fragment)
2623 {
2624 wxRichTextObjectList::compatibility_iterator i = GetChildren().GetFirst();
2625 while (i)
2626 {
2627 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
2628 wxASSERT( para != NULL );
2629
2630 if (!para->GetRange().IsOutside(range))
2631 {
2632 fragment.AppendChild(para->Clone());
2633 }
2634 i = i->GetNext();
2635 }
2636
2637 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
2638 if (!fragment.IsEmpty())
2639 {
2640 wxRichTextRange topTailRange(range);
2641
2642 wxRichTextParagraph* firstPara = wxDynamicCast(fragment.GetChildren().GetFirst()->GetData(), wxRichTextParagraph);
2643 wxASSERT( firstPara != NULL );
2644
2645 // Chop off the start of the paragraph
2646 if (topTailRange.GetStart() > firstPara->GetRange().GetStart())
2647 {
2648 wxRichTextRange r(firstPara->GetRange().GetStart(), topTailRange.GetStart()-1);
2649 firstPara->DeleteRange(r);
2650
2651 // Make sure the numbering is correct
2652 long end;
2653 fragment.CalculateRange(firstPara->GetRange().GetStart(), end);
2654
2655 // Now, we've deleted some positions, so adjust the range
2656 // accordingly.
2657 topTailRange.SetEnd(topTailRange.GetEnd() - r.GetLength());
2658 }
2659
2660 wxRichTextParagraph* lastPara = wxDynamicCast(fragment.GetChildren().GetLast()->GetData(), wxRichTextParagraph);
2661 wxASSERT( lastPara != NULL );
2662
2663 if (topTailRange.GetEnd() < (lastPara->GetRange().GetEnd()-1))
2664 {
2665 wxRichTextRange r(topTailRange.GetEnd()+1, lastPara->GetRange().GetEnd()-1); /* -1 since actual text ends 1 position before end of para marker */
2666 lastPara->DeleteRange(r);
2667
2668 // Make sure the numbering is correct
2669 long end;
2670 fragment.CalculateRange(firstPara->GetRange().GetStart(), end);
2671
2672 // We only have part of a paragraph at the end
2673 fragment.SetPartialParagraph(true);
2674 }
2675 else
2676 {
2677 if (topTailRange.GetEnd() == (lastPara->GetRange().GetEnd() - 1))
2678 // We have a partial paragraph (don't save last new paragraph marker)
2679 fragment.SetPartialParagraph(true);
2680 else
2681 // We have a complete paragraph
2682 fragment.SetPartialParagraph(false);
2683 }
2684 }
2685
2686 return true;
2687 }
2688
2689 /// Given a position, get the number of the visible line (potentially many to a paragraph),
2690 /// starting from zero at the start of the buffer.
2691 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos, bool caretPosition, bool startOfLine) const
2692 {
2693 if (caretPosition)
2694 pos ++;
2695
2696 int lineCount = 0;
2697
2698 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2699 while (node)
2700 {
2701 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2702 // wxASSERT( child != NULL );
2703
2704 if (child)
2705 {
2706 if (child->GetRange().Contains(pos))
2707 {
2708 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2709 while (node2)
2710 {
2711 wxRichTextLine* line = node2->GetData();
2712 wxRichTextRange lineRange = line->GetAbsoluteRange();
2713
2714 if (lineRange.Contains(pos) || pos == lineRange.GetStart())
2715 {
2716 // If the caret is displayed at the end of the previous wrapped line,
2717 // we want to return the line it's _displayed_ at (not the actual line
2718 // containing the position).
2719 if (lineRange.GetStart() == pos && !startOfLine && child->GetRange().GetStart() != pos)
2720 return lineCount - 1;
2721 else
2722 return lineCount;
2723 }
2724
2725 lineCount ++;
2726
2727 node2 = node2->GetNext();
2728 }
2729 // If we didn't find it in the lines, it must be
2730 // the last position of the paragraph. So return the last line.
2731 return lineCount-1;
2732 }
2733 else
2734 lineCount += child->GetLines().GetCount();
2735 }
2736
2737 node = node->GetNext();
2738 }
2739
2740 // Not found
2741 return -1;
2742 }
2743
2744 /// Given a line number, get the corresponding wxRichTextLine object.
2745 wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber) const
2746 {
2747 int lineCount = 0;
2748
2749 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2750 while (node)
2751 {
2752 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2753 // wxASSERT(child != NULL);
2754
2755 if (child)
2756 {
2757 if (lineNumber < (int) (child->GetLines().GetCount() + lineCount))
2758 {
2759 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
2760 while (node2)
2761 {
2762 wxRichTextLine* line = node2->GetData();
2763
2764 if (lineCount == lineNumber)
2765 return line;
2766
2767 lineCount ++;
2768
2769 node2 = node2->GetNext();
2770 }
2771 }
2772 else
2773 lineCount += child->GetLines().GetCount();
2774 }
2775
2776 node = node->GetNext();
2777 }
2778
2779 // Didn't find it
2780 return NULL;
2781 }
2782
2783 /// Delete range from layout.
2784 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange& range)
2785 {
2786 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2787
2788 wxRichTextParagraph* firstPara = NULL;
2789 while (node)
2790 {
2791 wxRichTextParagraph* obj = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2792 // wxASSERT (obj != NULL);
2793
2794 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
2795
2796 if (obj)
2797 {
2798 // Delete the range in each paragraph
2799
2800 if (!obj->GetRange().IsOutside(range))
2801 {
2802 // Deletes the content of this object within the given range
2803 obj->DeleteRange(range);
2804
2805 wxRichTextRange thisRange = obj->GetRange();
2806 wxRichTextAttr thisAttr = obj->GetAttributes();
2807
2808 // If the whole paragraph is within the range to delete,
2809 // delete the whole thing.
2810 if (range.GetStart() <= thisRange.GetStart() && range.GetEnd() >= thisRange.GetEnd())
2811 {
2812 // Delete the whole object
2813 RemoveChild(obj, true);
2814 obj = NULL;
2815 }
2816 else if (!firstPara)
2817 firstPara = obj;
2818
2819 // If the range includes the paragraph end, we need to join this
2820 // and the next paragraph.
2821 if (range.GetEnd() <= thisRange.GetEnd())
2822 {
2823 // We need to move the objects from the next paragraph
2824 // to this paragraph
2825
2826 wxRichTextParagraph* nextParagraph = NULL;
2827 if ((range.GetEnd() < thisRange.GetEnd()) && obj)
2828 nextParagraph = obj;
2829 else
2830 {
2831 // We're ending at the end of the paragraph, so merge the _next_ paragraph.
2832 if (next)
2833 nextParagraph = wxDynamicCast(next->GetData(), wxRichTextParagraph);
2834 }
2835
2836 bool applyFinalParagraphStyle = firstPara && nextParagraph && nextParagraph != firstPara;
2837
2838 wxRichTextAttr nextParaAttr;
2839 if (applyFinalParagraphStyle)
2840 {
2841 // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
2842 // not the next one.
2843 if (range.GetStart() == range.GetEnd() && range.GetStart() == thisRange.GetEnd())
2844 nextParaAttr = thisAttr;
2845 else
2846 nextParaAttr = nextParagraph->GetAttributes();
2847 }
2848
2849 if (firstPara && nextParagraph && firstPara != nextParagraph)
2850 {
2851 // Move the objects to the previous para
2852 wxRichTextObjectList::compatibility_iterator node1 = nextParagraph->GetChildren().GetFirst();
2853
2854 while (node1)
2855 {
2856 wxRichTextObject* obj1 = node1->GetData();
2857
2858 firstPara->AppendChild(obj1);
2859
2860 wxRichTextObjectList::compatibility_iterator next1 = node1->GetNext();
2861 nextParagraph->GetChildren().Erase(node1);
2862
2863 node1 = next1;
2864 }
2865
2866 // Delete the paragraph
2867 RemoveChild(nextParagraph, true);
2868 }
2869
2870 // Avoid empty paragraphs
2871 if (firstPara && firstPara->GetChildren().GetCount() == 0)
2872 {
2873 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
2874 firstPara->AppendChild(text);
2875 }
2876
2877 if (applyFinalParagraphStyle)
2878 firstPara->SetAttributes(nextParaAttr);
2879
2880 return true;
2881 }
2882 }
2883 }
2884
2885 node = next;
2886 }
2887
2888 return true;
2889 }
2890
2891 /// Get any text in this object for the given range
2892 wxString wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange& range) const
2893 {
2894 int lineCount = 0;
2895 wxString text;
2896 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2897 while (node)
2898 {
2899 wxRichTextObject* child = node->GetData();
2900 if (!child->GetRange().IsOutside(range))
2901 {
2902 wxRichTextRange childRange = range;
2903 childRange.LimitTo(child->GetRange());
2904
2905 wxString childText = child->GetTextForRange(childRange);
2906
2907 text += childText;
2908
2909 if ((childRange.GetEnd() == child->GetRange().GetEnd()) && node->GetNext())
2910 text += wxT("\n");
2911
2912 lineCount ++;
2913 }
2914 node = node->GetNext();
2915 }
2916
2917 return text;
2918 }
2919
2920 /// Get all the text
2921 wxString wxRichTextParagraphLayoutBox::GetText() const
2922 {
2923 return GetTextForRange(GetOwnRange());
2924 }
2925
2926 /// Get the paragraph by number
2927 wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber) const
2928 {
2929 if ((size_t) paragraphNumber >= GetChildCount())
2930 return NULL;
2931
2932 return (wxRichTextParagraph*) GetChild((size_t) paragraphNumber);
2933 }
2934
2935 /// Get the length of the paragraph
2936 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber) const
2937 {
2938 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
2939 if (para)
2940 return para->GetRange().GetLength() - 1; // don't include newline
2941 else
2942 return 0;
2943 }
2944
2945 /// Get the text of the paragraph
2946 wxString wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber) const
2947 {
2948 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
2949 if (para)
2950 return para->GetTextForRange(para->GetRange());
2951 else
2952 return wxEmptyString;
2953 }
2954
2955 /// Convert zero-based line column and paragraph number to a position.
2956 long wxRichTextParagraphLayoutBox::XYToPosition(long x, long y) const
2957 {
2958 wxRichTextParagraph* para = GetParagraphAtLine(y);
2959 if (para)
2960 {
2961 return para->GetRange().GetStart() + x;
2962 }
2963 else
2964 return -1;
2965 }
2966
2967 /// Convert zero-based position to line column and paragraph number
2968 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos, long* x, long* y) const
2969 {
2970 wxRichTextParagraph* para = GetParagraphAtPosition(pos);
2971 if (para)
2972 {
2973 int count = 0;
2974 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2975 while (node)
2976 {
2977 wxRichTextObject* child = node->GetData();
2978 if (child == para)
2979 break;
2980 count ++;
2981 node = node->GetNext();
2982 }
2983
2984 *y = count;
2985 *x = pos - para->GetRange().GetStart();
2986
2987 return true;
2988 }
2989 else
2990 return false;
2991 }
2992
2993 /// Get the leaf object in a paragraph at this position.
2994 /// Given a line number, get the corresponding wxRichTextLine object.
2995 wxRichTextObject* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position) const
2996 {
2997 wxRichTextParagraph* para = GetParagraphAtPosition(position);
2998 if (para)
2999 {
3000 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
3001
3002 while (node)
3003 {
3004 wxRichTextObject* child = node->GetData();
3005 if (child->GetRange().Contains(position))
3006 return child;
3007
3008 node = node->GetNext();
3009 }
3010 if (position == para->GetRange().GetEnd() && para->GetChildCount() > 0)
3011 return para->GetChildren().GetLast()->GetData();
3012 }
3013 return NULL;
3014 }
3015
3016 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
3017 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, int flags)
3018 {
3019 bool characterStyle = false;
3020 bool paragraphStyle = false;
3021
3022 if (style.IsCharacterStyle())
3023 characterStyle = true;
3024 if (style.IsParagraphStyle())
3025 paragraphStyle = true;
3026
3027 wxRichTextBuffer* buffer = GetBuffer();
3028
3029 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3030 bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3031 bool parasOnly = ((flags & wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY) != 0);
3032 bool charactersOnly = ((flags & wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY) != 0);
3033 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
3034 bool removeStyle = ((flags & wxRICHTEXT_SETSTYLE_REMOVE) != 0);
3035
3036 // Apply paragraph style first, if any
3037 wxRichTextAttr wholeStyle(style);
3038
3039 if (!removeStyle && wholeStyle.HasParagraphStyleName() && buffer->GetStyleSheet())
3040 {
3041 wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->FindParagraphStyle(wholeStyle.GetParagraphStyleName());
3042 if (def)
3043 wxRichTextApplyStyle(wholeStyle, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
3044 }
3045
3046 // Limit the attributes to be set to the content to only character attributes.
3047 wxRichTextAttr characterAttributes(wholeStyle);
3048 characterAttributes.SetFlags(characterAttributes.GetFlags() & (wxTEXT_ATTR_CHARACTER));
3049
3050 if (!removeStyle && characterAttributes.HasCharacterStyleName() && buffer->GetStyleSheet())
3051 {
3052 wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->FindCharacterStyle(characterAttributes.GetCharacterStyleName());
3053 if (def)
3054 wxRichTextApplyStyle(characterAttributes, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
3055 }
3056
3057 // If we are associated with a control, make undoable; otherwise, apply immediately
3058 // to the data.
3059
3060 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3061
3062 wxRichTextAction* action = NULL;
3063
3064 if (haveControl && withUndo)
3065 {
3066 action = new wxRichTextAction(NULL, _("Change Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
3067 action->SetRange(range);
3068 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3069 }
3070
3071 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3072 while (node)
3073 {
3074 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3075 // wxASSERT (para != NULL);
3076
3077 if (para && para->GetChildCount() > 0)
3078 {
3079 // Stop searching if we're beyond the range of interest
3080 if (para->GetRange().GetStart() > range.GetEnd())
3081 break;
3082
3083 if (!para->GetRange().IsOutside(range))
3084 {
3085 // We'll be using a copy of the paragraph to make style changes,
3086 // not updating the buffer directly.
3087 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3088
3089 if (haveControl && withUndo)
3090 {
3091 newPara = new wxRichTextParagraph(*para);
3092 action->GetNewParagraphs().AppendChild(newPara);
3093
3094 // Also store the old ones for Undo
3095 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3096 }
3097 else
3098 newPara = para;
3099
3100 // If we're specifying paragraphs only, then we really mean character formatting
3101 // to be included in the paragraph style
3102 if ((paragraphStyle || parasOnly) && !charactersOnly)
3103 {
3104 if (removeStyle)
3105 {
3106 // Removes the given style from the paragraph
3107 wxRichTextRemoveStyle(newPara->GetAttributes(), style);
3108 }
3109 else if (resetExistingStyle)
3110 newPara->GetAttributes() = wholeStyle;
3111 else
3112 {
3113 if (applyMinimal)
3114 {
3115 // Only apply attributes that will make a difference to the combined
3116 // style as seen on the display
3117 wxRichTextAttr combinedAttr(para->GetCombinedAttributes(true));
3118 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle, & combinedAttr);
3119 }
3120 else
3121 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle);
3122 }
3123 }
3124
3125 // When applying paragraph styles dynamically, don't change the text objects' attributes
3126 // since they will computed as needed. Only apply the character styling if it's _only_
3127 // character styling. This policy is subject to change and might be put under user control.
3128
3129 // Hm. we might well be applying a mix of paragraph and character styles, in which
3130 // case we _do_ want to apply character styles regardless of what para styles are set.
3131 // But if we're applying a paragraph style, which has some character attributes, but
3132 // we only want the paragraphs to hold this character style, then we _don't_ want to
3133 // apply the character style. So we need to be able to choose.
3134
3135 if (!parasOnly && (characterStyle|charactersOnly) && range.GetStart() != newPara->GetRange().GetEnd())
3136 {
3137 wxRichTextRange childRange(range);
3138 childRange.LimitTo(newPara->GetRange());
3139
3140 // Find the starting position and if necessary split it so
3141 // we can start applying a different style.
3142 // TODO: check that the style actually changes or is different
3143 // from style outside of range
3144 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
3145 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
3146
3147 if (childRange.GetStart() == newPara->GetRange().GetStart())
3148 firstObject = newPara->GetChildren().GetFirst()->GetData();
3149 else
3150 firstObject = newPara->SplitAt(range.GetStart());
3151
3152 // Increment by 1 because we're apply the style one _after_ the split point
3153 long splitPoint = childRange.GetEnd();
3154 if (splitPoint != newPara->GetRange().GetEnd())
3155 splitPoint ++;
3156
3157 // Find last object
3158 if (splitPoint == newPara->GetRange().GetEnd())
3159 lastObject = newPara->GetChildren().GetLast()->GetData();
3160 else
3161 // lastObject is set as a side-effect of splitting. It's
3162 // returned as the object before the new object.
3163 (void) newPara->SplitAt(splitPoint, & lastObject);
3164
3165 wxASSERT(firstObject != NULL);
3166 wxASSERT(lastObject != NULL);
3167
3168 if (!firstObject || !lastObject)
3169 continue;
3170
3171 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
3172 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
3173
3174 wxASSERT(firstNode);
3175 wxASSERT(lastNode);
3176
3177 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
3178
3179 while (node2)
3180 {
3181 wxRichTextObject* child = node2->GetData();
3182
3183 if (removeStyle)
3184 {
3185 // Removes the given style from the paragraph
3186 wxRichTextRemoveStyle(child->GetAttributes(), style);
3187 }
3188 else if (resetExistingStyle)
3189 child->GetAttributes() = characterAttributes;
3190 else
3191 {
3192 if (applyMinimal)
3193 {
3194 // Only apply attributes that will make a difference to the combined
3195 // style as seen on the display
3196 wxRichTextAttr combinedAttr(newPara->GetCombinedAttributes(child->GetAttributes(), true));
3197 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes, & combinedAttr);
3198 }
3199 else
3200 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes);
3201 }
3202
3203 if (node2 == lastNode)
3204 break;
3205
3206 node2 = node2->GetNext();
3207 }
3208 }
3209 }
3210 }
3211
3212 node = node->GetNext();
3213 }
3214
3215 // Do action, or delay it until end of batch.
3216 if (haveControl && withUndo)
3217 buffer->SubmitAction(action);
3218
3219 return true;
3220 }
3221
3222 // Just change the attributes for this single object.
3223 void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject* obj, const wxRichTextAttr& textAttr, int flags)
3224 {
3225 wxRichTextBuffer* buffer = GetBuffer();
3226 bool withUndo = flags & wxRICHTEXT_SETSTYLE_WITH_UNDO;
3227 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
3228 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3229
3230 wxRichTextAction *action = NULL;
3231 wxRichTextAttr newAttr = obj->GetAttributes();
3232 if (resetExistingStyle)
3233 newAttr = textAttr;
3234 else
3235 newAttr.Apply(textAttr);
3236
3237 if (haveControl && withUndo)
3238 {
3239 action = new wxRichTextAction(NULL, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES, buffer, obj->GetContainer(), buffer->GetRichTextCtrl());
3240 action->SetRange(obj->GetRange().FromInternal());
3241 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3242 action->MakeObject(obj);
3243
3244 action->GetAttributes() = newAttr;
3245 }
3246 else
3247 obj->GetAttributes() = newAttr;
3248
3249 if (haveControl && withUndo)
3250 buffer->SubmitAction(action);
3251 }
3252
3253 /// Get the text attributes for this position.
3254 bool wxRichTextParagraphLayoutBox::GetStyle(long position, wxRichTextAttr& style)
3255 {
3256 return DoGetStyle(position, style, true);
3257 }
3258
3259 bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position, wxRichTextAttr& style)
3260 {
3261 return DoGetStyle(position, style, false);
3262 }
3263
3264 /// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
3265 /// context attributes.
3266 bool wxRichTextParagraphLayoutBox::DoGetStyle(long position, wxRichTextAttr& style, bool combineStyles)
3267 {
3268 wxRichTextObject* obj wxDUMMY_INITIALIZE(NULL);
3269
3270 if (style.IsParagraphStyle())
3271 {
3272 obj = GetParagraphAtPosition(position);
3273 if (obj)
3274 {
3275 if (combineStyles)
3276 {
3277 // Start with the base style
3278 style = GetAttributes();
3279
3280 // Apply the paragraph style
3281 wxRichTextApplyStyle(style, obj->GetAttributes());
3282 }
3283 else
3284 style = obj->GetAttributes();
3285
3286 return true;
3287 }
3288 }
3289 else
3290 {
3291 obj = GetLeafObjectAtPosition(position);
3292 if (obj)
3293 {
3294 if (combineStyles)
3295 {
3296 wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph);
3297 style = para ? para->GetCombinedAttributes(obj->GetAttributes()) : obj->GetAttributes();
3298 }
3299 else
3300 style = obj->GetAttributes();
3301
3302 return true;
3303 }
3304 }
3305 return false;
3306 }
3307
3308 static bool wxHasStyle(long flags, long style)
3309 {
3310 return (flags & style) != 0;
3311 }
3312
3313 /// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
3314 /// content.
3315 bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr& currentStyle, const wxRichTextAttr& style, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
3316 {
3317 currentStyle.CollectCommonAttributes(style, clashingAttr, absentAttr);
3318
3319 return true;
3320 }
3321
3322 /// Get the combined style for a range - if any attribute is different within the range,
3323 /// that attribute is not present within the flags.
3324 /// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
3325 /// nested.
3326 bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange& range, wxRichTextAttr& style)
3327 {
3328 style = wxRichTextAttr();
3329
3330 wxRichTextAttr clashingAttr;
3331 wxRichTextAttr absentAttrPara, absentAttrChar;
3332
3333 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
3334 while (node)
3335 {
3336 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3337 if (para && !(para->GetRange().GetStart() > range.GetEnd() || para->GetRange().GetEnd() < range.GetStart()))
3338 {
3339 if (para->GetChildren().GetCount() == 0)
3340 {
3341 wxRichTextAttr paraStyle = para->GetCombinedAttributes(true /* use box attributes */);
3342
3343 CollectStyle(style, paraStyle, clashingAttr, absentAttrPara);
3344 }
3345 else
3346 {
3347 wxRichTextRange paraRange(para->GetRange());
3348 paraRange.LimitTo(range);
3349
3350 // First collect paragraph attributes only
3351 wxRichTextAttr paraStyle = para->GetCombinedAttributes();
3352 paraStyle.SetFlags(paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH);
3353 CollectStyle(style, paraStyle, clashingAttr, absentAttrPara);
3354
3355 wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
3356
3357 while (childNode)
3358 {
3359 wxRichTextObject* child = childNode->GetData();
3360 if (!(child->GetRange().GetStart() > range.GetEnd() || child->GetRange().GetEnd() < range.GetStart()))
3361 {
3362 wxRichTextAttr childStyle = para->GetCombinedAttributes(child->GetAttributes(), true /* include box attributes */);
3363
3364 // Now collect character attributes only
3365 childStyle.SetFlags(childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER);
3366
3367 CollectStyle(style, childStyle, clashingAttr, absentAttrChar);
3368 }
3369
3370 childNode = childNode->GetNext();
3371 }
3372 }
3373 }
3374 node = node->GetNext();
3375 }
3376 return true;
3377 }
3378
3379 /// Set default style
3380 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr& style)
3381 {
3382 m_defaultAttributes = style;
3383 return true;
3384 }
3385
3386 /// Test if this whole range has character attributes of the specified kind. If any
3387 /// of the attributes are different within the range, the test fails. You
3388 /// can use this to implement, for example, bold button updating. style must have
3389 /// flags indicating which attributes are of interest.
3390 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3391 {
3392 int foundCount = 0;
3393 int matchingCount = 0;
3394
3395 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3396 while (node)
3397 {
3398 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3399 // wxASSERT (para != NULL);
3400
3401 if (para)
3402 {
3403 // Stop searching if we're beyond the range of interest
3404 if (para->GetRange().GetStart() > range.GetEnd())
3405 return foundCount == matchingCount && foundCount != 0;
3406
3407 if (!para->GetRange().IsOutside(range))
3408 {
3409 wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
3410
3411 while (node2)
3412 {
3413 wxRichTextObject* child = node2->GetData();
3414 // Allow for empty string if no buffer
3415 wxRichTextRange childRange = child->GetRange();
3416 if (childRange.GetLength() == 0 && GetRange().GetLength() == 1)
3417 childRange.SetEnd(childRange.GetEnd()+1);
3418
3419 if (!childRange.IsOutside(range) && child->IsKindOf(CLASSINFO(wxRichTextPlainText)))
3420 {
3421 foundCount ++;
3422 wxRichTextAttr textAttr = para->GetCombinedAttributes(child->GetAttributes());
3423
3424 if (wxTextAttrEqPartial(textAttr, style))
3425 matchingCount ++;
3426 }
3427
3428 node2 = node2->GetNext();
3429 }
3430 }
3431 }
3432
3433 node = node->GetNext();
3434 }
3435
3436 return foundCount == matchingCount && foundCount != 0;
3437 }
3438
3439 /// Test if this whole range has paragraph attributes of the specified kind. If any
3440 /// of the attributes are different within the range, the test fails. You
3441 /// can use this to implement, for example, centering button updating. style must have
3442 /// flags indicating which attributes are of interest.
3443 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
3444 {
3445 int foundCount = 0;
3446 int matchingCount = 0;
3447
3448 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3449 while (node)
3450 {
3451 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3452 // wxASSERT (para != NULL);
3453
3454 if (para)
3455 {
3456 // Stop searching if we're beyond the range of interest
3457 if (para->GetRange().GetStart() > range.GetEnd())
3458 return foundCount == matchingCount && foundCount != 0;
3459
3460 if (!para->GetRange().IsOutside(range))
3461 {
3462 wxRichTextAttr textAttr = GetAttributes();
3463 // Apply the paragraph style
3464 wxRichTextApplyStyle(textAttr, para->GetAttributes());
3465
3466 foundCount ++;
3467 if (wxTextAttrEqPartial(textAttr, style))
3468 matchingCount ++;
3469 }
3470 }
3471
3472 node = node->GetNext();
3473 }
3474 return foundCount == matchingCount && foundCount != 0;
3475 }
3476
3477 void wxRichTextParagraphLayoutBox::Reset()
3478 {
3479 Clear();
3480
3481 wxRichTextBuffer* buffer = GetBuffer();
3482 if (buffer && buffer->GetRichTextCtrl())
3483 {
3484 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET, buffer->GetRichTextCtrl()->GetId());
3485 event.SetEventObject(buffer->GetRichTextCtrl());
3486 event.SetContainer(this);
3487
3488 buffer->SendEvent(event, true);
3489 }
3490
3491 AddParagraph(wxEmptyString);
3492
3493 InvalidateHierarchy(wxRICHTEXT_ALL);
3494 }
3495
3496 /// Invalidate the buffer. With no argument, invalidates whole buffer.
3497 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange& invalidRange)
3498 {
3499 wxRichTextCompositeObject::Invalidate(invalidRange);
3500
3501 DoInvalidate(invalidRange);
3502 }
3503
3504 // Do the (in)validation for this object only
3505 void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange& invalidRange)
3506 {
3507 if (invalidRange == wxRICHTEXT_ALL)
3508 {
3509 m_invalidRange = wxRICHTEXT_ALL;
3510 }
3511 // Already invalidating everything
3512 else if (m_invalidRange == wxRICHTEXT_ALL)
3513 {
3514 }
3515 else
3516 {
3517 if ((invalidRange.GetStart() < m_invalidRange.GetStart()) || m_invalidRange.GetStart() == -1)
3518 m_invalidRange.SetStart(invalidRange.GetStart());
3519 if (invalidRange.GetEnd() > m_invalidRange.GetEnd())
3520 m_invalidRange.SetEnd(invalidRange.GetEnd());
3521 }
3522 }
3523
3524 // Do the (in)validation both up and down the hierarchy
3525 void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange& invalidRange)
3526 {
3527 Invalidate(invalidRange);
3528
3529 if (invalidRange != wxRICHTEXT_NONE)
3530 {
3531 // Now go up the hierarchy
3532 wxRichTextObject* thisObj = this;
3533 wxRichTextObject* p = GetParent();
3534 while (p)
3535 {
3536 wxRichTextParagraphLayoutBox* l = wxDynamicCast(p, wxRichTextParagraphLayoutBox);
3537 if (l)
3538 l->DoInvalidate(thisObj->GetRange());
3539
3540 thisObj = p;
3541 p = p->GetParent();
3542 }
3543 }
3544 }
3545
3546 /// Get invalid range, rounding to entire paragraphs if argument is true.
3547 wxRichTextRange wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs) const
3548 {
3549 if (m_invalidRange == wxRICHTEXT_ALL || m_invalidRange == wxRICHTEXT_NONE)
3550 return m_invalidRange;
3551
3552 wxRichTextRange range = m_invalidRange;
3553
3554 if (wholeParagraphs)
3555 {
3556 wxRichTextParagraph* para1 = GetParagraphAtPosition(range.GetStart());
3557 if (para1)
3558 range.SetStart(para1->GetRange().GetStart());
3559 // floating layout make all child should be relayout
3560 range.SetEnd(GetOwnRange().GetEnd());
3561 }
3562 return range;
3563 }
3564
3565 /// Apply the style sheet to the buffer, for example if the styles have changed.
3566 bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet* styleSheet)
3567 {
3568 wxASSERT(styleSheet != NULL);
3569 if (!styleSheet)
3570 return false;
3571
3572 int foundCount = 0;
3573
3574 wxRichTextAttr attr(GetBasicStyle());
3575 if (GetBasicStyle().HasParagraphStyleName())
3576 {
3577 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
3578 if (paraDef)
3579 {
3580 attr.Apply(paraDef->GetStyleMergedWithBase(styleSheet));
3581 SetBasicStyle(attr);
3582 foundCount ++;
3583 }
3584 }
3585
3586 if (GetBasicStyle().HasCharacterStyleName())
3587 {
3588 wxRichTextCharacterStyleDefinition* charDef = styleSheet->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
3589 if (charDef)
3590 {
3591 attr.Apply(charDef->GetStyleMergedWithBase(styleSheet));
3592 SetBasicStyle(attr);
3593 foundCount ++;
3594 }
3595 }
3596
3597 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3598 while (node)
3599 {
3600 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3601 // wxASSERT (para != NULL);
3602
3603 if (para)
3604 {
3605 // Combine paragraph and list styles. If there is a list style in the original attributes,
3606 // the current indentation overrides anything else and is used to find the item indentation.
3607 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
3608 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
3609 // exception as above).
3610 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
3611 // So when changing a list style interactively, could retrieve level based on current style, then
3612 // set appropriate indent and apply new style.
3613
3614 int outline = -1;
3615 int num = -1;
3616 if (para->GetAttributes().HasOutlineLevel())
3617 outline = para->GetAttributes().GetOutlineLevel();
3618 if (para->GetAttributes().HasBulletNumber())
3619 num = para->GetAttributes().GetBulletNumber();
3620
3621 if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3622 {
3623 int currentIndent = para->GetAttributes().GetLeftIndent();
3624
3625 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3626 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3627 if (paraDef && !listDef)
3628 {
3629 para->GetAttributes() = paraDef->GetStyleMergedWithBase(styleSheet);
3630 foundCount ++;
3631 }
3632 else if (listDef && !paraDef)
3633 {
3634 // Set overall style defined for the list style definition
3635 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
3636
3637 // Apply the style for this level
3638 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3639 foundCount ++;
3640 }
3641 else if (listDef && paraDef)
3642 {
3643 // Combines overall list style, style for level, and paragraph style
3644 para->GetAttributes() = listDef->CombineWithParagraphStyle(currentIndent, paraDef->GetStyleMergedWithBase(styleSheet));
3645 foundCount ++;
3646 }
3647 }
3648 else if (para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
3649 {
3650 int currentIndent = para->GetAttributes().GetLeftIndent();
3651
3652 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
3653
3654 // Overall list definition style
3655 para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
3656
3657 // Style for this level
3658 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
3659
3660 foundCount ++;
3661 }
3662 else if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && para->GetAttributes().GetListStyleName().IsEmpty())
3663 {
3664 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
3665 if (def)
3666 {
3667 para->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
3668 foundCount ++;
3669 }
3670 }
3671
3672 if (outline != -1)
3673 para->GetAttributes().SetOutlineLevel(outline);
3674 if (num != -1)
3675 para->GetAttributes().SetBulletNumber(num);
3676 }
3677
3678 node = node->GetNext();
3679 }
3680 return foundCount != 0;
3681 }
3682
3683 /// Set list style
3684 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
3685 {
3686 wxRichTextBuffer* buffer = GetBuffer();
3687 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
3688
3689 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3690 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3691 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
3692 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
3693
3694 // Current number, if numbering
3695 int n = startFrom;
3696
3697 wxASSERT (!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
3698
3699 // If we are associated with a control, make undoable; otherwise, apply immediately
3700 // to the data.
3701
3702 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3703
3704 wxRichTextAction* action = NULL;
3705
3706 if (haveControl && withUndo)
3707 {
3708 action = new wxRichTextAction(NULL, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
3709 action->SetRange(range);
3710 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3711 }
3712
3713 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3714 while (node)
3715 {
3716 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3717 // wxASSERT (para != NULL);
3718
3719 if (para && para->GetChildCount() > 0)
3720 {
3721 // Stop searching if we're beyond the range of interest
3722 if (para->GetRange().GetStart() > range.GetEnd())
3723 break;
3724
3725 if (!para->GetRange().IsOutside(range))
3726 {
3727 // We'll be using a copy of the paragraph to make style changes,
3728 // not updating the buffer directly.
3729 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3730
3731 if (haveControl && withUndo)
3732 {
3733 newPara = new wxRichTextParagraph(*para);
3734 action->GetNewParagraphs().AppendChild(newPara);
3735
3736 // Also store the old ones for Undo
3737 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3738 }
3739 else
3740 newPara = para;
3741
3742 if (def)
3743 {
3744 int thisIndent = newPara->GetAttributes().GetLeftIndent();
3745 int thisLevel = specifyLevel ? specifiedLevel : def->FindLevelForIndent(thisIndent);
3746
3747 // How is numbering going to work?
3748 // If we are renumbering, or numbering for the first time, we need to keep
3749 // track of the number for each level. But we might be simply applying a different
3750 // list style.
3751 // In Word, applying a style to several paragraphs, even if at different levels,
3752 // reverts the level back to the same one. So we could do the same here.
3753 // Renumbering will need to be done when we promote/demote a paragraph.
3754
3755 // Apply the overall list style, and item style for this level
3756 wxRichTextAttr listStyle(def->GetCombinedStyleForLevel(thisLevel, styleSheet));
3757 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
3758
3759 // Now we need to do numbering
3760 if (renumber)
3761 {
3762 newPara->GetAttributes().SetBulletNumber(n);
3763 }
3764
3765 n ++;
3766 }
3767 else if (!newPara->GetAttributes().GetListStyleName().IsEmpty())
3768 {
3769 // if def is NULL, remove list style, applying any associated paragraph style
3770 // to restore the attributes
3771
3772 newPara->GetAttributes().SetListStyleName(wxEmptyString);
3773 newPara->GetAttributes().SetLeftIndent(0, 0);
3774 newPara->GetAttributes().SetBulletText(wxEmptyString);
3775
3776 // Eliminate the main list-related attributes
3777 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);
3778
3779 if (styleSheet && !newPara->GetAttributes().GetParagraphStyleName().IsEmpty())
3780 {
3781 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(newPara->GetAttributes().GetParagraphStyleName());
3782 if (def)
3783 {
3784 newPara->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
3785 }
3786 }
3787 }
3788 }
3789 }
3790
3791 node = node->GetNext();
3792 }
3793
3794 // Do action, or delay it until end of batch.
3795 if (haveControl && withUndo)
3796 buffer->SubmitAction(action);
3797
3798 return true;
3799 }
3800
3801 bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
3802 {
3803 wxRichTextBuffer* buffer = GetBuffer();
3804 if (buffer && buffer->GetStyleSheet())
3805 {
3806 wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->FindListStyle(defName);
3807 if (def)
3808 return SetListStyle(range, def, flags, startFrom, specifiedLevel);
3809 }
3810 return false;
3811 }
3812
3813 /// Clear list for given range
3814 bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange& range, int flags)
3815 {
3816 return SetListStyle(range, NULL, flags);
3817 }
3818
3819 /// Number/renumber any list elements in the given range
3820 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
3821 {
3822 return DoNumberList(range, range, 0, def, flags, startFrom, specifiedLevel);
3823 }
3824
3825 /// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
3826 bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, const wxRichTextRange& promotionRange, int promoteBy,
3827 wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
3828 {
3829 wxRichTextBuffer* buffer = GetBuffer();
3830 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
3831
3832 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
3833 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3834 #if wxDEBUG_LEVEL
3835 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
3836 #endif
3837
3838 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
3839
3840 // Max number of levels
3841 const int maxLevels = 10;
3842
3843 // The level we're looking at now
3844 int currentLevel = -1;
3845
3846 // The item number for each level
3847 int levels[maxLevels];
3848 int i;
3849
3850 // Reset all numbering
3851 for (i = 0; i < maxLevels; i++)
3852 {
3853 if (startFrom != -1)
3854 levels[i] = startFrom-1;
3855 else if (renumber) // start again
3856 levels[i] = 0;
3857 else
3858 levels[i] = -1; // start from the number we found, if any
3859 }
3860
3861 wxASSERT(!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
3862
3863 // If we are associated with a control, make undoable; otherwise, apply immediately
3864 // to the data.
3865
3866 bool haveControl = (buffer->GetRichTextCtrl() != NULL);
3867
3868 wxRichTextAction* action = NULL;
3869
3870 if (haveControl && withUndo)
3871 {
3872 action = new wxRichTextAction(NULL, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
3873 action->SetRange(range);
3874 action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
3875 }
3876
3877 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3878 while (node)
3879 {
3880 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
3881 // wxASSERT (para != NULL);
3882
3883 if (para && para->GetChildCount() > 0)
3884 {
3885 // Stop searching if we're beyond the range of interest
3886 if (para->GetRange().GetStart() > range.GetEnd())
3887 break;
3888
3889 if (!para->GetRange().IsOutside(range))
3890 {
3891 // We'll be using a copy of the paragraph to make style changes,
3892 // not updating the buffer directly.
3893 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
3894
3895 if (haveControl && withUndo)
3896 {
3897 newPara = new wxRichTextParagraph(*para);
3898 action->GetNewParagraphs().AppendChild(newPara);
3899
3900 // Also store the old ones for Undo
3901 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
3902 }
3903 else
3904 newPara = para;
3905
3906 wxRichTextListStyleDefinition* defToUse = def;
3907 if (!defToUse)
3908 {
3909 if (styleSheet && !newPara->GetAttributes().GetListStyleName().IsEmpty())
3910 defToUse = styleSheet->FindListStyle(newPara->GetAttributes().GetListStyleName());
3911 }
3912
3913 if (defToUse)
3914 {
3915 int thisIndent = newPara->GetAttributes().GetLeftIndent();
3916 int thisLevel = defToUse->FindLevelForIndent(thisIndent);
3917
3918 // If we've specified a level to apply to all, change the level.
3919 if (specifiedLevel != -1)
3920 thisLevel = specifiedLevel;
3921
3922 // Do promotion if specified
3923 if ((promoteBy != 0) && !para->GetRange().IsOutside(promotionRange))
3924 {
3925 thisLevel = thisLevel - promoteBy;
3926 if (thisLevel < 0)
3927 thisLevel = 0;
3928 if (thisLevel > 9)
3929 thisLevel = 9;
3930 }
3931
3932 // Apply the overall list style, and item style for this level
3933 wxRichTextAttr listStyle(defToUse->GetCombinedStyleForLevel(thisLevel, styleSheet));
3934 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
3935
3936 // OK, we've (re)applied the style, now let's get the numbering right.
3937
3938 if (currentLevel == -1)
3939 currentLevel = thisLevel;
3940
3941 // Same level as before, do nothing except increment level's number afterwards
3942 if (currentLevel == thisLevel)
3943 {
3944 }
3945 // A deeper level: start renumbering all levels after current level
3946 else if (thisLevel > currentLevel)
3947 {
3948 for (i = currentLevel+1; i <= thisLevel; i++)
3949 {
3950 levels[i] = 0;
3951 }
3952 currentLevel = thisLevel;
3953 }
3954 else if (thisLevel < currentLevel)
3955 {
3956 currentLevel = thisLevel;
3957 }
3958
3959 // Use the current numbering if -1 and we have a bullet number already
3960 if (levels[currentLevel] == -1)
3961 {
3962 if (newPara->GetAttributes().HasBulletNumber())
3963 levels[currentLevel] = newPara->GetAttributes().GetBulletNumber();
3964 else
3965 levels[currentLevel] = 1;
3966 }
3967 else
3968 {
3969 levels[currentLevel] ++;
3970 }
3971
3972 newPara->GetAttributes().SetBulletNumber(levels[currentLevel]);
3973
3974 // Create the bullet text if an outline list
3975 if (listStyle.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
3976 {
3977 wxString text;
3978 for (i = 0; i <= currentLevel; i++)
3979 {
3980 if (!text.IsEmpty())
3981 text += wxT(".");
3982 text += wxString::Format(wxT("%d"), levels[i]);
3983 }
3984 newPara->GetAttributes().SetBulletText(text);
3985 }
3986 }
3987 }
3988 }
3989
3990 node = node->GetNext();
3991 }
3992
3993 // Do action, or delay it until end of batch.
3994 if (haveControl && withUndo)
3995 buffer->SubmitAction(action);
3996
3997 return true;
3998 }
3999
4000 bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
4001 {
4002 wxRichTextBuffer* buffer = GetBuffer();
4003 if (buffer->GetStyleSheet())
4004 {
4005 wxRichTextListStyleDefinition* def = NULL;
4006 if (!defName.IsEmpty())
4007 def = buffer->GetStyleSheet()->FindListStyle(defName);
4008 return NumberList(range, def, flags, startFrom, specifiedLevel);
4009 }
4010 return false;
4011 }
4012
4013 /// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
4014 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel)
4015 {
4016 // TODO
4017 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
4018 // to NumberList with a flag indicating promotion is required within one of the ranges.
4019 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
4020 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
4021 // We start renumbering from the para after that different para we found. We specify that the numbering of that
4022 // list position will start from 1.
4023 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
4024 // We can end the renumbering at this point.
4025
4026 // For now, only renumber within the promotion range.
4027
4028 return DoNumberList(range, range, promoteBy, def, flags, 1, specifiedLevel);
4029 }
4030
4031 bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel)
4032 {
4033 wxRichTextBuffer* buffer = GetBuffer();
4034 if (buffer->GetStyleSheet())
4035 {
4036 wxRichTextListStyleDefinition* def = NULL;
4037 if (!defName.IsEmpty())
4038 def = buffer->GetStyleSheet()->FindListStyle(defName);
4039 return PromoteList(promoteBy, range, def, flags, specifiedLevel);
4040 }
4041 return false;
4042 }
4043
4044 /// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
4045 /// position of the paragraph that it had to start looking from.
4046 bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr) const
4047 {
4048 if (!previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE)
4049 return false;
4050
4051 wxRichTextBuffer* buffer = GetBuffer();
4052 wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
4053 if (styleSheet && !previousParagraph->GetAttributes().GetListStyleName().IsEmpty())
4054 {
4055 wxRichTextListStyleDefinition* def = styleSheet->FindListStyle(previousParagraph->GetAttributes().GetListStyleName());
4056 if (def)
4057 {
4058 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
4059 // int thisLevel = def->FindLevelForIndent(thisIndent);
4060
4061 bool isOutline = (previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE) != 0;
4062
4063 attr.SetFlags(previousParagraph->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER|wxTEXT_ATTR_BULLET_TEXT|wxTEXT_ATTR_BULLET_NAME));
4064 if (previousParagraph->GetAttributes().HasBulletName())
4065 attr.SetBulletName(previousParagraph->GetAttributes().GetBulletName());
4066 attr.SetBulletStyle(previousParagraph->GetAttributes().GetBulletStyle());
4067 attr.SetListStyleName(previousParagraph->GetAttributes().GetListStyleName());
4068
4069 int nextNumber = previousParagraph->GetAttributes().GetBulletNumber() + 1;
4070 attr.SetBulletNumber(nextNumber);
4071
4072 if (isOutline)
4073 {
4074 wxString text = previousParagraph->GetAttributes().GetBulletText();
4075 if (!text.IsEmpty())
4076 {
4077 int pos = text.Find(wxT('.'), true);
4078 if (pos != wxNOT_FOUND)
4079 {
4080 text = text.Mid(0, text.Length() - pos - 1);
4081 }
4082 else
4083 text = wxEmptyString;
4084 if (!text.IsEmpty())
4085 text += wxT(".");
4086 text += wxString::Format(wxT("%d"), nextNumber);
4087 attr.SetBulletText(text);
4088 }
4089 }
4090
4091 return true;
4092 }
4093 else
4094 return false;
4095 }
4096 else
4097 return false;
4098 }
4099
4100 /*!
4101 * wxRichTextParagraph
4102 * This object represents a single paragraph (or in a straight text editor, a line).
4103 */
4104
4105 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph, wxRichTextCompositeObject)
4106
4107 wxArrayInt wxRichTextParagraph::sm_defaultTabs;
4108
4109 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject* parent, wxRichTextAttr* style):
4110 wxRichTextCompositeObject(parent)
4111 {
4112 if (style)
4113 SetAttributes(*style);
4114 }
4115
4116 wxRichTextParagraph::wxRichTextParagraph(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* paraStyle, wxRichTextAttr* charStyle):
4117 wxRichTextCompositeObject(parent)
4118 {
4119 if (paraStyle)
4120 SetAttributes(*paraStyle);
4121
4122 AppendChild(new wxRichTextPlainText(text, this, charStyle));
4123 }
4124
4125 wxRichTextParagraph::~wxRichTextParagraph()
4126 {
4127 ClearLines();
4128 }
4129
4130 /// Draw the item
4131 bool wxRichTextParagraph::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int style)
4132 {
4133 if (!IsShown())
4134 return true;
4135
4136 // Currently we don't merge these attributes with the parent, but we
4137 // should consider whether we should (e.g. if we set a border colour
4138 // for all paragraphs). But generally box attributes are likely to be
4139 // different for different objects.
4140 wxRect paraRect = GetRect();
4141 DrawBoxAttributes(dc, GetBuffer(), GetAttributes(), paraRect);
4142
4143 wxRichTextAttr attr = GetCombinedAttributes();
4144
4145 // Draw the bullet, if any
4146 if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
4147 {
4148 if (attr.GetLeftSubIndent() != 0)
4149 {
4150 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
4151 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
4152
4153 wxRichTextAttr bulletAttr(GetCombinedAttributes());
4154
4155 // Combine with the font of the first piece of content, if one is specified
4156 if (GetChildren().GetCount() > 0)
4157 {
4158 wxRichTextObject* firstObj = (wxRichTextObject*) GetChildren().GetFirst()->GetData();
4159 if (!firstObj->IsFloatable() && firstObj->GetAttributes().HasFont())
4160 {
4161 wxRichTextApplyStyle(bulletAttr, firstObj->GetAttributes());
4162 }
4163 }
4164
4165 // Get line height from first line, if any
4166 wxRichTextLine* line = m_cachedLines.GetFirst() ? (wxRichTextLine* ) m_cachedLines.GetFirst()->GetData() : NULL;
4167
4168 wxPoint linePos;
4169 int lineHeight wxDUMMY_INITIALIZE(0);
4170 if (line)
4171 {
4172 lineHeight = line->GetSize().y;
4173 linePos = line->GetPosition() + GetPosition();
4174 }
4175 else
4176 {
4177 wxFont font;
4178 if (bulletAttr.HasFont() && GetBuffer())
4179 font = GetBuffer()->GetFontTable().FindFont(bulletAttr);
4180 else
4181 font = (*wxNORMAL_FONT);
4182
4183 wxCheckSetFont(dc, font);
4184
4185 lineHeight = dc.GetCharHeight();
4186 linePos = GetPosition();
4187 linePos.y += spaceBeforePara;
4188 }
4189
4190 wxRect bulletRect(GetPosition().x + leftIndent, linePos.y, linePos.x - (GetPosition().x + leftIndent), lineHeight);
4191
4192 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP)
4193 {
4194 if (wxRichTextBuffer::GetRenderer())
4195 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc, bulletAttr, bulletRect);
4196 }
4197 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD)
4198 {
4199 if (wxRichTextBuffer::GetRenderer())
4200 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc, bulletAttr, bulletRect);
4201 }
4202 else
4203 {
4204 wxString bulletText = GetBulletText();
4205
4206 if (!bulletText.empty() && wxRichTextBuffer::GetRenderer())
4207 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc, bulletAttr, bulletRect, bulletText);
4208 }
4209 }
4210 }
4211
4212 // Draw the range for each line, one object at a time.
4213
4214 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
4215 while (node)
4216 {
4217 wxRichTextLine* line = node->GetData();
4218 wxRichTextRange lineRange = line->GetAbsoluteRange();
4219
4220 // Lines are specified relative to the paragraph
4221
4222 wxPoint linePosition = line->GetPosition() + GetPosition();
4223
4224 // Don't draw if off the screen
4225 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) != 0) || ((linePosition.y + line->GetSize().y) >= rect.y && linePosition.y <= rect.y + rect.height))
4226 {
4227 wxPoint objectPosition = linePosition;
4228 int maxDescent = line->GetDescent();
4229
4230 // Loop through objects until we get to the one within range
4231 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
4232
4233 int i = 0;
4234 while (node2)
4235 {
4236 wxRichTextObject* child = node2->GetData();
4237
4238 if (!child->IsFloating() && child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
4239 {
4240 // Draw this part of the line at the correct position
4241 wxRichTextRange objectRange(child->GetRange());
4242 objectRange.LimitTo(lineRange);
4243
4244 wxSize objectSize;
4245 if (child->IsTopLevel())
4246 {
4247 objectSize = child->GetCachedSize();
4248 objectRange = child->GetOwnRange();
4249 }
4250 else
4251 {
4252 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4253 if (i < (int) line->GetObjectSizes().GetCount())
4254 {
4255 objectSize.x = line->GetObjectSizes()[(size_t) i];
4256 }
4257 else
4258 #endif
4259 {
4260 int descent = 0;
4261 child->GetRangeSize(objectRange, objectSize, descent, dc, wxRICHTEXT_UNFORMATTED, objectPosition);
4262 }
4263 }
4264
4265 // Use the child object's width, but the whole line's height
4266 wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
4267 child->Draw(dc, objectRange, selection, childRect, maxDescent, style);
4268
4269 objectPosition.x += objectSize.x;
4270 i ++;
4271 }
4272 else if (child->GetRange().GetStart() > lineRange.GetEnd())
4273 // Can break out of inner loop now since we've passed this line's range
4274 break;
4275
4276 node2 = node2->GetNext();
4277 }
4278 }
4279
4280 node = node->GetNext();
4281 }
4282
4283 return true;
4284 }
4285
4286 // Get the range width using partial extents calculated for the whole paragraph.
4287 static int wxRichTextGetRangeWidth(const wxRichTextParagraph& para, const wxRichTextRange& range, const wxArrayInt& partialExtents)
4288 {
4289 wxASSERT(partialExtents.GetCount() >= (size_t) range.GetLength());
4290
4291 if (partialExtents.GetCount() < (size_t) range.GetLength())
4292 return 0;
4293
4294 int leftMostPos = 0;
4295 if (range.GetStart() - para.GetRange().GetStart() > 0)
4296 leftMostPos = partialExtents[range.GetStart() - para.GetRange().GetStart() - 1];
4297
4298 int rightMostPos = partialExtents[range.GetEnd() - para.GetRange().GetStart()];
4299
4300 int w = rightMostPos - leftMostPos;
4301
4302 return w;
4303 }
4304
4305 /// Lay the item out
4306 bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, int style)
4307 {
4308 // Deal with floating objects firstly before the normal layout
4309 wxRichTextBuffer* buffer = GetBuffer();
4310 wxASSERT(buffer);
4311 wxRichTextFloatCollector* collector = GetContainer()->GetFloatCollector();
4312 wxASSERT(collector);
4313 LayoutFloat(dc, rect, style, collector);
4314
4315 wxRichTextAttr attr = GetCombinedAttributes();
4316
4317 // ClearLines();
4318
4319 // Increase the size of the paragraph due to spacing
4320 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
4321 int spaceAfterPara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingAfter());
4322 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
4323 int leftSubIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftSubIndent());
4324 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
4325
4326 int lineSpacing = 0;
4327
4328 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
4329 if (attr.HasLineSpacing() && attr.GetLineSpacing() > 0 && attr.GetFont().Ok())
4330 {
4331 wxCheckSetFont(dc, attr.GetFont());
4332 lineSpacing = (int) (double(dc.GetCharHeight()) * (double(attr.GetLineSpacing())/10.0 - 1.0));
4333 }
4334
4335 // Start position for each line relative to the paragraph
4336 int startPositionFirstLine = leftIndent;
4337 int startPositionSubsequentLines = leftIndent + leftSubIndent;
4338
4339 // If we have a bullet in this paragraph, the start position for the first line's text
4340 // is actually leftIndent + leftSubIndent.
4341 if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
4342 startPositionFirstLine = startPositionSubsequentLines;
4343
4344 long lastEndPos = GetRange().GetStart()-1;
4345 long lastCompletedEndPos = lastEndPos;
4346
4347 int currentWidth = 0;
4348 SetPosition(rect.GetPosition());
4349
4350 wxPoint currentPosition(0, spaceBeforePara); // We will calculate lines relative to paragraph
4351 int lineHeight = 0;
4352 int maxWidth = 0;
4353 int maxHeight = currentPosition.y;
4354 int maxAscent = 0;
4355 int maxDescent = 0;
4356 int lineCount = 0;
4357 int lineAscent = 0;
4358 int lineDescent = 0;
4359
4360 wxRichTextObjectList::compatibility_iterator node;
4361
4362 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4363 wxUnusedVar(style);
4364 wxArrayInt partialExtents;
4365
4366 wxSize paraSize;
4367 int paraDescent = 0;
4368
4369 // This calculates the partial text extents
4370 GetRangeSize(GetRange(), paraSize, paraDescent, dc, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, rect.GetPosition(), & partialExtents);
4371 #else
4372 node = m_children.GetFirst();
4373 while (node)
4374 {
4375 wxRichTextObject* child = node->GetData();
4376
4377 //child->SetCachedSize(wxDefaultSize);
4378 child->Layout(dc, rect, style);
4379
4380 node = node->GetNext();
4381 }
4382
4383 #endif
4384
4385 // Split up lines
4386
4387 // We may need to go back to a previous child, in which case create the new line,
4388 // find the child corresponding to the start position of the string, and
4389 // continue.
4390
4391 wxRect availableRect;
4392
4393 node = m_children.GetFirst();
4394 while (node)
4395 {
4396 wxRichTextObject* child = node->GetData();
4397
4398 // If floating, ignore. We already laid out floats.
4399 // Also ignore if empty object, except if we haven't got any
4400 // size yet.
4401 if (child->IsFloating() || !child->IsShown() ||
4402 (child->GetRange().GetLength() == 0 && maxHeight > spaceBeforePara)
4403 )
4404 {
4405 node = node->GetNext();
4406 continue;
4407 }
4408
4409 // If this is e.g. a composite text box, it will need to be laid out itself.
4410 // But if just a text fragment or image, for example, this will
4411 // do nothing. NB: won't we need to set the position after layout?
4412 // since for example if position is dependent on vertical line size, we
4413 // can't tell the position until the size is determined. So possibly introduce
4414 // another layout phase.
4415
4416 // We may only be looking at part of a child, if we searched back for wrapping
4417 // and found a suitable point some way into the child. So get the size for the fragment
4418 // if necessary.
4419
4420 long nextBreakPos = GetFirstLineBreakPosition(lastEndPos+1);
4421 long lastPosToUse = child->GetRange().GetEnd();
4422 bool lineBreakInThisObject = (nextBreakPos > -1 && nextBreakPos <= child->GetRange().GetEnd());
4423
4424 if (lineBreakInThisObject)
4425 lastPosToUse = nextBreakPos;
4426
4427 wxSize childSize;
4428 int childDescent = 0;
4429
4430 int startOffset = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
4431 availableRect = wxRect(rect.x + startOffset, rect.y + currentPosition.y,
4432 rect.width - startOffset - rightIndent, rect.height);
4433
4434 if (child->IsTopLevel())
4435 {
4436 wxSize oldSize = child->GetCachedSize();
4437
4438 child->Invalidate(wxRICHTEXT_ALL);
4439 child->SetPosition(wxPoint(0, 0));
4440
4441 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4442 // lays out the object again using the minimum size
4443 // The position will be determined by its location in its line,
4444 // and not by the child's actual position.
4445 child->LayoutToBestSize(dc, buffer,
4446 GetAttributes(), child->GetAttributes(), availableRect, style);
4447
4448 if (oldSize != child->GetCachedSize())
4449 {
4450 partialExtents.Clear();
4451
4452 // Recalculate the partial text extents since the child object changed size
4453 GetRangeSize(GetRange(), paraSize, paraDescent, dc, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
4454 }
4455 }
4456
4457 // Problem: we need to layout composites here for which we need the available width,
4458 // but we can't get the available width without using the float collector which
4459 // needs to know the object height.
4460
4461 if ((nextBreakPos == -1) && (lastEndPos == child->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
4462 {
4463 childSize = child->GetCachedSize();
4464 childDescent = child->GetDescent();
4465 }
4466 else
4467 {
4468 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4469 // Get height only, then the width using the partial extents
4470 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
4471 childSize.x = wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos+1, lastPosToUse), partialExtents);
4472 #else
4473 GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, wxRICHTEXT_UNFORMATTED, rect.GetPosition());
4474 #endif
4475 }
4476
4477 bool doLoop = true;
4478 int loopIterations = 0;
4479
4480 // If there are nested objects that need to lay themselves out, we have to do this in a
4481 // loop because the height of the object may well depend on the available width.
4482 // And because of floating object positioning, the available width depends on the
4483 // height of the object and whether it will clash with the floating objects.
4484 // So, we see whether the available width changes due to the presence of floating images.
4485 // If it does, then we'll use the new restricted width to find the object height again.
4486 // If this causes another restriction in the available width, we'll try again, until
4487 // either we lose patience or the available width settles down.
4488 do
4489 {
4490 loopIterations ++;
4491
4492 wxRect oldAvailableRect = availableRect;
4493
4494 // Available width depends on the floating objects and the line height.
4495 // Note: the floating objects may be placed vertically along the two side of
4496 // buffer, so we may have different available line widths with different
4497 // [startY, endY]. So, we can't determine how wide the available
4498 // space is until we know the exact line height.
4499 lineDescent = wxMax(childDescent, maxDescent);
4500 lineAscent = wxMax(childSize.y-childDescent, maxAscent);
4501 lineHeight = lineDescent + lineAscent;
4502 wxRect floatAvailableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight);
4503
4504 // Adjust availableRect to the space that is available when taking floating objects into account.
4505
4506 if (floatAvailableRect.x + startOffset > availableRect.x)
4507 {
4508 int newX = floatAvailableRect.x + startOffset;
4509 int newW = availableRect.width - (newX - availableRect.x);
4510 availableRect.x = newX;
4511 availableRect.width = newW;
4512 }
4513
4514 if (floatAvailableRect.width < availableRect.width)
4515 availableRect.width = floatAvailableRect.width;
4516
4517 currentPosition.x = availableRect.x - rect.x;
4518
4519 if (child->IsTopLevel() && loopIterations <= 20)
4520 {
4521 if (availableRect != oldAvailableRect)
4522 {
4523 wxSize oldSize = child->GetCachedSize();
4524
4525 //child->SetCachedSize(wxDefaultSize);
4526 // Lays out the object first with a given amount of space, and then if no width was specified in attr,
4527 // lays out the object again using the minimum size
4528 child->Invalidate(wxRICHTEXT_ALL);
4529 child->LayoutToBestSize(dc, buffer,
4530 GetAttributes(), child->GetAttributes(), availableRect, style);
4531 childSize = child->GetCachedSize();
4532 childDescent = child->GetDescent();
4533 //child->SetPosition(availableRect.GetPosition());
4534
4535 if (oldSize != child->GetCachedSize())
4536 {
4537 partialExtents.Clear();
4538
4539 // Recalculate the partial text extents since the child object changed size
4540 GetRangeSize(GetRange(), paraSize, paraDescent, dc, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
4541 }
4542
4543 // Go around the loop finding the available rect for the given floating objects
4544 }
4545 else
4546 doLoop = false;
4547 }
4548 else
4549 doLoop = false;
4550 }
4551 while (doLoop);
4552
4553 // Cases:
4554 // 1) There was a line break BEFORE the natural break
4555 // 2) There was a line break AFTER the natural break
4556 // 3) It's the last line
4557 // 4) The child still fits (carry on) - 'else' clause
4558
4559 if ((lineBreakInThisObject && (childSize.x + currentWidth <= availableRect.width))
4560 ||
4561 (childSize.x + currentWidth > availableRect.width)
4562 ||
4563 ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext())
4564
4565 )
4566 {
4567 if (child->IsTopLevel())
4568 {
4569 // We can move it to the correct position at this point
4570 child->Move(GetPosition() + wxPoint(currentWidth, currentPosition.y));
4571 }
4572
4573 long wrapPosition = 0;
4574 if ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext() && !lineBreakInThisObject)
4575 wrapPosition = child->GetRange().GetEnd();
4576 else
4577
4578 // Find a place to wrap. This may walk back to previous children,
4579 // for example if a word spans several objects.
4580 // Note: one object must contains only one wxTextAtrr, so the line height will not
4581 // change inside one object. Thus, we can pass the remain line width to the
4582 // FindWrapPosition function.
4583 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, availableRect.width, wrapPosition, & partialExtents))
4584 {
4585 // If the function failed, just cut it off at the end of this child.
4586 wrapPosition = child->GetRange().GetEnd();
4587 }
4588
4589 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
4590 if (wrapPosition <= lastCompletedEndPos)
4591 wrapPosition = wxMax(lastCompletedEndPos+1,child->GetRange().GetEnd());
4592
4593 // Line end position shouldn't be the same as the end, or greater.
4594 if (wrapPosition >= GetRange().GetEnd())
4595 wrapPosition = GetRange().GetEnd()-1;
4596
4597 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
4598
4599 // Let's find the actual size of the current line now
4600 wxSize actualSize;
4601 wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
4602
4603 /// Use previous descent, not the wrapping descent we just found, since this may be too big
4604 /// for the fragment we're about to add.
4605 childDescent = maxDescent;
4606
4607 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4608 if (!child->IsEmpty())
4609 {
4610 // Get height only, then the width using the partial extents
4611 GetRangeSize(actualRange, actualSize, childDescent, dc, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
4612 actualSize.x = wxRichTextGetRangeWidth(*this, actualRange, partialExtents);
4613 }
4614 else
4615 #endif
4616 GetRangeSize(actualRange, actualSize, childDescent, dc, wxRICHTEXT_UNFORMATTED);
4617
4618 currentWidth = actualSize.x;
4619 maxDescent = wxMax(childDescent, maxDescent);
4620 maxAscent = wxMax(actualSize.y-childDescent, maxAscent);
4621 lineHeight = maxDescent + maxAscent;
4622
4623 if (lineHeight == 0 && buffer)
4624 {
4625 wxFont font(buffer->GetFontTable().FindFont(attr));
4626 wxCheckSetFont(dc, font);
4627 lineHeight = dc.GetCharHeight();
4628 }
4629
4630 if (maxDescent == 0)
4631 {
4632 int w, h;
4633 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
4634 }
4635
4636 // Add a new line
4637 wxRichTextLine* line = AllocateLine(lineCount);
4638
4639 // Set relative range so we won't have to change line ranges when paragraphs are moved
4640 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
4641 line->SetPosition(currentPosition);
4642 line->SetSize(wxSize(currentWidth, lineHeight));
4643 line->SetDescent(maxDescent);
4644
4645 maxHeight = currentPosition.y + lineHeight;
4646
4647 // Now move down a line. TODO: add margins, spacing
4648 currentPosition.y += lineHeight;
4649 currentPosition.y += lineSpacing;
4650 maxDescent = 0;
4651 maxAscent = 0;
4652 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
4653 currentWidth = 0;
4654
4655 lineCount ++;
4656
4657 // TODO: account for zero-length objects, such as fields
4658 // wxASSERT(wrapPosition > lastCompletedEndPos);
4659
4660 lastEndPos = wrapPosition;
4661 lastCompletedEndPos = lastEndPos;
4662
4663 lineHeight = 0;
4664
4665 if (wrapPosition < GetRange().GetEnd()-1)
4666 {
4667 // May need to set the node back to a previous one, due to searching back in wrapping
4668 wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition(wrapPosition+1);
4669 if (childAfterWrapPosition)
4670 node = m_children.Find(childAfterWrapPosition);
4671 else
4672 node = node->GetNext();
4673 }
4674 else
4675 node = node->GetNext();
4676
4677 // Apply paragraph styles such as alignment to the wrapped line
4678 ApplyParagraphStyle(line, attr, availableRect, dc);
4679 }
4680 else
4681 {
4682 // We still fit, so don't add a line, and keep going
4683 currentWidth += childSize.x;
4684 maxDescent = wxMax(childDescent, maxDescent);
4685 maxAscent = wxMax(childSize.y-childDescent, maxAscent);
4686 lineHeight = maxDescent + maxAscent;
4687
4688 maxWidth = wxMax(maxWidth, currentWidth+startOffset);
4689 lastEndPos = child->GetRange().GetEnd();
4690
4691 node = node->GetNext();
4692 }
4693 }
4694
4695 //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
4696
4697 // Remove remaining unused line objects, if any
4698 ClearUnusedLines(lineCount);
4699
4700 // We need to add back the margins etc.
4701 {
4702 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
4703 contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, currentPosition.y + spaceAfterPara));
4704 GetBoxRects(dc, buffer, GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
4705 SetCachedSize(marginRect.GetSize());
4706 }
4707
4708 // The maximum size is the length of the paragraph stretched out into a line.
4709 // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
4710 // this size. TODO: take into account line breaks.
4711 {
4712 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
4713 contentRect = wxRect(wxPoint(0, 0), wxSize(paraSize.x, currentPosition.y + spaceAfterPara));
4714 GetBoxRects(dc, buffer, GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
4715 SetMaxSize(marginRect.GetSize());
4716 }
4717
4718 // Find the greatest minimum size. Currently we only look at non-text objects,
4719 // which isn't ideal but it would be slow to find the maximum word width to
4720 // use as the minimum.
4721 {
4722 int minWidth = 0;
4723 node = m_children.GetFirst();
4724 while (node)
4725 {
4726 wxRichTextObject* child = node->GetData();
4727
4728 // If floating, ignore. We already laid out floats.
4729 // Also ignore if empty object, except if we haven't got any
4730 // size yet.
4731 if (!child->IsFloating() && child->GetRange().GetLength() != 0 && !child->IsKindOf(CLASSINFO(wxRichTextPlainText)))
4732 {
4733 if (child->GetCachedSize().x > minWidth)
4734 minWidth = child->GetMinSize().x;
4735 }
4736 node = node->GetNext();
4737 }
4738
4739 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
4740 contentRect = wxRect(wxPoint(0, 0), wxSize(minWidth, currentPosition.y + spaceAfterPara));
4741 GetBoxRects(dc, buffer, GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
4742 SetMinSize(marginRect.GetSize());
4743 }
4744
4745
4746 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
4747 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
4748 // Use the text extents to calculate the size of each fragment in each line
4749 wxRichTextLineList::compatibility_iterator lineNode = m_cachedLines.GetFirst();
4750 while (lineNode)
4751 {
4752 wxRichTextLine* line = lineNode->GetData();
4753 wxRichTextRange lineRange = line->GetAbsoluteRange();
4754
4755 // Loop through objects until we get to the one within range
4756 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
4757
4758 while (node2)
4759 {
4760 wxRichTextObject* child = node2->GetData();
4761
4762 if (child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange))
4763 {
4764 wxRichTextRange rangeToUse = lineRange;
4765 rangeToUse.LimitTo(child->GetRange());
4766
4767 // Find the size of the child from the text extents, and store in an array
4768 // for drawing later
4769 int left = 0;
4770 if (rangeToUse.GetStart() > GetRange().GetStart())
4771 left = partialExtents[(rangeToUse.GetStart()-1) - GetRange().GetStart()];
4772 int right = partialExtents[rangeToUse.GetEnd() - GetRange().GetStart()];
4773 int sz = right - left;
4774 line->GetObjectSizes().Add(sz);
4775 }
4776 else if (child->GetRange().GetStart() > lineRange.GetEnd())
4777 // Can break out of inner loop now since we've passed this line's range
4778 break;
4779
4780 node2 = node2->GetNext();
4781 }
4782
4783 lineNode = lineNode->GetNext();
4784 }
4785 #endif
4786 #endif
4787
4788 return true;
4789 }
4790
4791 /// Apply paragraph styles, such as centering, to wrapped lines
4792 /// TODO: take into account box attributes, possibly
4793 void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRichTextAttr& attr, const wxRect& rect, wxDC& dc)
4794 {
4795 if (!attr.HasAlignment())
4796 return;
4797
4798 wxPoint pos = line->GetPosition();
4799 wxSize size = line->GetSize();
4800
4801 // centering, right-justification
4802 if (attr.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
4803 {
4804 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
4805 pos.x = (rect.GetWidth() - rightIndent - size.x)/2 + pos.x;
4806 line->SetPosition(pos);
4807 }
4808 else if (attr.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
4809 {
4810 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
4811 pos.x = pos.x + rect.GetWidth() - size.x - rightIndent;
4812 line->SetPosition(pos);
4813 }
4814 }
4815
4816 /// Insert text at the given position
4817 bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
4818 {
4819 wxRichTextObject* childToUse = NULL;
4820 wxRichTextObjectList::compatibility_iterator nodeToUse = wxRichTextObjectList::compatibility_iterator();
4821
4822 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4823 while (node)
4824 {
4825 wxRichTextObject* child = node->GetData();
4826 if (child->GetRange().Contains(pos) && child->GetRange().GetLength() > 0)
4827 {
4828 childToUse = child;
4829 nodeToUse = node;
4830 break;
4831 }
4832
4833 node = node->GetNext();
4834 }
4835
4836 if (childToUse)
4837 {
4838 wxRichTextPlainText* textObject = wxDynamicCast(childToUse, wxRichTextPlainText);
4839 if (textObject)
4840 {
4841 int posInString = pos - textObject->GetRange().GetStart();
4842
4843 wxString newText = textObject->GetText().Mid(0, posInString) +
4844 text + textObject->GetText().Mid(posInString);
4845 textObject->SetText(newText);
4846
4847 int textLength = text.length();
4848
4849 textObject->SetRange(wxRichTextRange(textObject->GetRange().GetStart(),
4850 textObject->GetRange().GetEnd() + textLength));
4851
4852 // Increment the end range of subsequent fragments in this paragraph.
4853 // We'll set the paragraph range itself at a higher level.
4854
4855 wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
4856 while (node)
4857 {
4858 wxRichTextObject* child = node->GetData();
4859 child->SetRange(wxRichTextRange(textObject->GetRange().GetStart() + textLength,
4860 textObject->GetRange().GetEnd() + textLength));
4861
4862 node = node->GetNext();
4863 }
4864
4865 return true;
4866 }
4867 else
4868 {
4869 // TODO: if not a text object, insert at closest position, e.g. in front of it
4870 }
4871 }
4872 else
4873 {
4874 // Add at end.
4875 // Don't pass parent initially to suppress auto-setting of parent range.
4876 // We'll do that at a higher level.
4877 wxRichTextPlainText* textObject = new wxRichTextPlainText(text, this);
4878
4879 AppendChild(textObject);
4880 return true;
4881 }
4882
4883 return false;
4884 }
4885
4886 void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
4887 {
4888 wxRichTextCompositeObject::Copy(obj);
4889 }
4890
4891 /// Clear the cached lines
4892 void wxRichTextParagraph::ClearLines()
4893 {
4894 WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
4895 }
4896
4897 /// Get/set the object size for the given range. Returns false if the range
4898 /// is invalid for this object.
4899 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* partialExtents) const
4900 {
4901 if (!range.IsWithin(GetRange()))
4902 return false;
4903
4904 if (flags & wxRICHTEXT_UNFORMATTED)
4905 {
4906 // Just use unformatted data, assume no line breaks
4907 // TODO: take into account line breaks
4908
4909 wxSize sz;
4910
4911 wxArrayInt childExtents;
4912 wxArrayInt* p;
4913 if (partialExtents)
4914 p = & childExtents;
4915 else
4916 p = NULL;
4917
4918 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
4919 while (node)
4920 {
4921
4922 wxRichTextObject* child = node->GetData();
4923 if (!child->GetRange().IsOutside(range))
4924 {
4925 // Floating objects have a zero size within the paragraph.
4926 if (child->IsFloating())
4927 {
4928 if (partialExtents)
4929 {
4930 int lastSize;
4931 if (partialExtents->GetCount() > 0)
4932 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
4933 else
4934 lastSize = 0;
4935
4936 partialExtents->Add(0 /* zero size */ + lastSize);
4937 }
4938 }
4939 else
4940 {
4941 wxSize childSize;
4942
4943 wxRichTextRange rangeToUse = range;
4944 rangeToUse.LimitTo(child->GetRange());
4945 #if 0
4946 if (child->IsTopLevel())
4947 rangeToUse = child->GetOwnRange();
4948 #endif
4949 int childDescent = 0;
4950
4951 // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
4952 // but it's only going to be used after caching has taken place.
4953 if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
4954 {
4955 childDescent = child->GetDescent();
4956 childSize = child->GetCachedSize();
4957
4958 sz.y = wxMax(sz.y, childSize.y);
4959 sz.x += childSize.x;
4960 descent = wxMax(descent, childDescent);
4961 }
4962 else if (child->IsTopLevel())
4963 {
4964 childDescent = child->GetDescent();
4965 childSize = child->GetCachedSize();
4966
4967 sz.y = wxMax(sz.y, childSize.y);
4968 sz.x += childSize.x;
4969 descent = wxMax(descent, childDescent);
4970 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
4971 {
4972 child->SetCachedSize(childSize);
4973 child->SetDescent(childDescent);
4974 }
4975
4976 if (partialExtents)
4977 {
4978 int lastSize;
4979 if (partialExtents->GetCount() > 0)
4980 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
4981 else
4982 lastSize = 0;
4983
4984 partialExtents->Add(childSize.x + lastSize);
4985 }
4986 }
4987 else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, flags, wxPoint(position.x + sz.x, position.y), p))
4988 {
4989 sz.y = wxMax(sz.y, childSize.y);
4990 sz.x += childSize.x;
4991 descent = wxMax(descent, childDescent);
4992
4993 if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
4994 {
4995 child->SetCachedSize(childSize);
4996 child->SetDescent(childDescent);
4997 }
4998
4999 if (partialExtents)
5000 {
5001 int lastSize;
5002 if (partialExtents->GetCount() > 0)
5003 lastSize = (*partialExtents)[partialExtents->GetCount()-1];
5004 else
5005 lastSize = 0;
5006
5007 size_t i;
5008 for (i = 0; i < childExtents.GetCount(); i++)
5009 {
5010 partialExtents->Add(childExtents[i] + lastSize);
5011 }
5012 }
5013 }
5014 }
5015
5016 if (p)
5017 p->Clear();
5018 }
5019
5020 node = node->GetNext();
5021 }
5022 size = sz;
5023 }
5024 else
5025 {
5026 // Use formatted data, with line breaks
5027 wxSize sz;
5028
5029 // We're going to loop through each line, and then for each line,
5030 // call GetRangeSize for the fragment that comprises that line.
5031 // Only we have to do that multiple times within the line, because
5032 // the line may be broken into pieces. For now ignore line break commands
5033 // (so we can assume that getting the unformatted size for a fragment
5034 // within a line is the actual size)
5035
5036 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5037 while (node)
5038 {
5039 wxRichTextLine* line = node->GetData();
5040 wxRichTextRange lineRange = line->GetAbsoluteRange();
5041 if (!lineRange.IsOutside(range))
5042 {
5043 wxSize lineSize;
5044
5045 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
5046 while (node2)
5047 {
5048 wxRichTextObject* child = node2->GetData();
5049
5050 if (!child->IsFloating() && !child->GetRange().IsOutside(lineRange))
5051 {
5052 wxRichTextRange rangeToUse = lineRange;
5053 rangeToUse.LimitTo(child->GetRange());
5054 if (child->IsTopLevel())
5055 rangeToUse = child->GetOwnRange();
5056
5057 wxSize childSize;
5058 int childDescent = 0;
5059 if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, flags, wxPoint(position.x + sz.x, position.y)))
5060 {
5061 lineSize.y = wxMax(lineSize.y, childSize.y);
5062 lineSize.x += childSize.x;
5063 }
5064 descent = wxMax(descent, childDescent);
5065 }
5066
5067 node2 = node2->GetNext();
5068 }
5069
5070 // Increase size by a line (TODO: paragraph spacing)
5071 sz.y += lineSize.y;
5072 sz.x = wxMax(sz.x, lineSize.x);
5073 }
5074 node = node->GetNext();
5075 }
5076 size = sz;
5077 }
5078 return true;
5079 }
5080
5081 /// Finds the absolute position and row height for the given character position
5082 bool wxRichTextParagraph::FindPosition(wxDC& dc, long index, wxPoint& pt, int* height, bool forceLineStart)
5083 {
5084 if (index == -1)
5085 {
5086 wxRichTextLine* line = ((wxRichTextParagraphLayoutBox*)GetParent())->GetLineAtPosition(0);
5087 if (line)
5088 *height = line->GetSize().y;
5089 else
5090 *height = dc.GetCharHeight();
5091
5092 // -1 means 'the start of the buffer'.
5093 pt = GetPosition();
5094 if (line)
5095 pt = pt + line->GetPosition();
5096
5097 return true;
5098 }
5099
5100 // The final position in a paragraph is taken to mean the position
5101 // at the start of the next paragraph.
5102 if (index == GetRange().GetEnd())
5103 {
5104 wxRichTextParagraphLayoutBox* parent = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
5105 wxASSERT( parent != NULL );
5106
5107 // Find the height at the next paragraph, if any
5108 wxRichTextLine* line = parent->GetLineAtPosition(index + 1);
5109 if (line)
5110 {
5111 *height = line->GetSize().y;
5112 pt = line->GetAbsolutePosition();
5113 }
5114 else
5115 {
5116 *height = dc.GetCharHeight();
5117 int indent = ConvertTenthsMMToPixels(dc, m_attributes.GetLeftIndent());
5118 pt = wxPoint(indent, GetCachedSize().y);
5119 }
5120
5121 return true;
5122 }
5123
5124 if (index < GetRange().GetStart() || index > GetRange().GetEnd())
5125 return false;
5126
5127 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5128 while (node)
5129 {
5130 wxRichTextLine* line = node->GetData();
5131 wxRichTextRange lineRange = line->GetAbsoluteRange();
5132 if (index >= lineRange.GetStart() && index <= lineRange.GetEnd())
5133 {
5134 // If this is the last point in the line, and we're forcing the
5135 // returned value to be the start of the next line, do the required
5136 // thing.
5137 if (index == lineRange.GetEnd() && forceLineStart)
5138 {
5139 if (node->GetNext())
5140 {
5141 wxRichTextLine* nextLine = node->GetNext()->GetData();
5142 *height = nextLine->GetSize().y;
5143 pt = nextLine->GetAbsolutePosition();
5144 return true;
5145 }
5146 }
5147
5148 pt.y = line->GetPosition().y + GetPosition().y;
5149
5150 wxRichTextRange r(lineRange.GetStart(), index);
5151 wxSize rangeSize;
5152 int descent = 0;
5153
5154 // We find the size of the line up to this point,
5155 // then we can add this size to the line start position and
5156 // paragraph start position to find the actual position.
5157
5158 if (GetRangeSize(r, rangeSize, descent, dc, wxRICHTEXT_UNFORMATTED, line->GetPosition()+ GetPosition()))
5159 {
5160 pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
5161 *height = line->GetSize().y;
5162
5163 return true;
5164 }
5165
5166 }
5167
5168 node = node->GetNext();
5169 }
5170
5171 return false;
5172 }
5173
5174 /// Hit-testing: returns a flag indicating hit test details, plus
5175 /// information about position
5176 int wxRichTextParagraph::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
5177 {
5178 if (!IsShown())
5179 return wxRICHTEXT_HITTEST_NONE;
5180
5181 // If we're in the top-level container, then we can return
5182 // a suitable hit test code even if the point is outside the container area,
5183 // so that we can position the caret sensibly even if we don't
5184 // click on valid content. If we're not at the top-level, and the point
5185 // is not within this paragraph object, then we don't want to stop more
5186 // precise hit-testing from working prematurely, so return immediately.
5187 // NEW STRATEGY: use the parent boundary to test whether we're in the
5188 // right region, not the paragraph, since the paragraph may be positioned
5189 // some way in from where the user clicks.
5190 {
5191 long tmpPos;
5192 wxRichTextObject* tempObj, *tempContextObj;
5193 if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc, pt, tmpPos, & tempObj, & tempContextObj, flags) == wxRICHTEXT_HITTEST_NONE)
5194 return wxRICHTEXT_HITTEST_NONE;
5195 }
5196
5197 wxRichTextObjectList::compatibility_iterator objNode = m_children.GetFirst();
5198 while (objNode)
5199 {
5200 wxRichTextObject* child = objNode->GetData();
5201 if (child->IsTopLevel() && ((flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS) == 0))
5202 {
5203 {
5204 int hitTest = child->HitTest(dc, pt, textPosition, obj, contextObj);
5205 if (hitTest != wxRICHTEXT_HITTEST_NONE)
5206 return hitTest;
5207 }
5208 }
5209
5210 objNode = objNode->GetNext();
5211 }
5212
5213 wxPoint paraPos = GetPosition();
5214
5215 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
5216 while (node)
5217 {
5218 wxRichTextLine* line = node->GetData();
5219 wxPoint linePos = paraPos + line->GetPosition();
5220 wxSize lineSize = line->GetSize();
5221 wxRichTextRange lineRange = line->GetAbsoluteRange();
5222
5223 if (pt.y <= linePos.y + lineSize.y)
5224 {
5225 if (pt.x < linePos.x)
5226 {
5227 textPosition = lineRange.GetStart();
5228 *obj = FindObjectAtPosition(textPosition);
5229 *contextObj = GetContainer();
5230 return wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_OUTSIDE;
5231 }
5232 else if (pt.x >= (linePos.x + lineSize.x))
5233 {
5234 textPosition = lineRange.GetEnd();
5235 *obj = FindObjectAtPosition(textPosition);
5236 *contextObj = GetContainer();
5237 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
5238 }
5239 else
5240 {
5241 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5242 wxArrayInt partialExtents;
5243
5244 wxSize paraSize;
5245 int paraDescent;
5246
5247 // This calculates the partial text extents
5248 GetRangeSize(lineRange, paraSize, paraDescent, dc, wxRICHTEXT_UNFORMATTED, linePos, & partialExtents);
5249
5250 int lastX = linePos.x;
5251 size_t i;
5252 for (i = 0; i < partialExtents.GetCount(); i++)
5253 {
5254 int nextX = partialExtents[i] + linePos.x;
5255
5256 if (pt.x >= lastX && pt.x <= nextX)
5257 {
5258 textPosition = i + lineRange.GetStart(); // minus 1?
5259
5260 *obj = FindObjectAtPosition(textPosition);
5261 *contextObj = GetContainer();
5262
5263 // So now we know it's between i-1 and i.
5264 // Let's see if we can be more precise about
5265 // which side of the position it's on.
5266
5267 int midPoint = (nextX + lastX)/2;
5268 if (pt.x >= midPoint)
5269 return wxRICHTEXT_HITTEST_AFTER;
5270 else
5271 return wxRICHTEXT_HITTEST_BEFORE;
5272 }
5273
5274 lastX = nextX;
5275 }
5276 #else
5277 long i;
5278 int lastX = linePos.x;
5279 for (i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++)
5280 {
5281 wxSize childSize;
5282 int descent = 0;
5283
5284 wxRichTextRange rangeToUse(lineRange.GetStart(), i);
5285
5286 GetRangeSize(rangeToUse, childSize, descent, dc, wxRICHTEXT_UNFORMATTED, linePos);
5287
5288 int nextX = childSize.x + linePos.x;
5289
5290 if (pt.x >= lastX && pt.x <= nextX)
5291 {
5292 textPosition = i;
5293
5294 *obj = FindObjectAtPosition(textPosition);
5295 *contextObj = GetContainer();
5296
5297 // So now we know it's between i-1 and i.
5298 // Let's see if we can be more precise about
5299 // which side of the position it's on.
5300
5301 int midPoint = (nextX + lastX)/2;
5302 if (pt.x >= midPoint)
5303 return wxRICHTEXT_HITTEST_AFTER;
5304 else
5305 return wxRICHTEXT_HITTEST_BEFORE;
5306 }
5307 else
5308 {
5309 lastX = nextX;
5310 }
5311 }
5312 #endif
5313 }
5314 }
5315
5316 node = node->GetNext();
5317 }
5318
5319 return wxRICHTEXT_HITTEST_NONE;
5320 }
5321
5322 /// Split an object at this position if necessary, and return
5323 /// the previous object, or NULL if inserting at beginning.
5324 wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** previousObject)
5325 {
5326 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5327 while (node)
5328 {
5329 wxRichTextObject* child = node->GetData();
5330
5331 if (pos == child->GetRange().GetStart())
5332 {
5333 if (previousObject)
5334 {
5335 if (node->GetPrevious())
5336 *previousObject = node->GetPrevious()->GetData();
5337 else
5338 *previousObject = NULL;
5339 }
5340
5341 return child;
5342 }
5343
5344 if (child->GetRange().Contains(pos))
5345 {
5346 // This should create a new object, transferring part of
5347 // the content to the old object and the rest to the new object.
5348 wxRichTextObject* newObject = child->DoSplit(pos);
5349
5350 // If we couldn't split this object, just insert in front of it.
5351 if (!newObject)
5352 {
5353 // Maybe this is an empty string, try the next one
5354 // return child;
5355 }
5356 else
5357 {
5358 // Insert the new object after 'child'
5359 if (node->GetNext())
5360 m_children.Insert(node->GetNext(), newObject);
5361 else
5362 m_children.Append(newObject);
5363 newObject->SetParent(this);
5364
5365 if (previousObject)
5366 *previousObject = child;
5367
5368 return newObject;
5369 }
5370 }
5371
5372 node = node->GetNext();
5373 }
5374 if (previousObject)
5375 *previousObject = NULL;
5376 return NULL;
5377 }
5378
5379 /// Move content to a list from obj on
5380 void wxRichTextParagraph::MoveToList(wxRichTextObject* obj, wxList& list)
5381 {
5382 wxRichTextObjectList::compatibility_iterator node = m_children.Find(obj);
5383 while (node)
5384 {
5385 wxRichTextObject* child = node->GetData();
5386 list.Append(child);
5387
5388 wxRichTextObjectList::compatibility_iterator oldNode = node;
5389
5390 node = node->GetNext();
5391
5392 m_children.DeleteNode(oldNode);
5393 }
5394 }
5395
5396 /// Add content back from list
5397 void wxRichTextParagraph::MoveFromList(wxList& list)
5398 {
5399 for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
5400 {
5401 AppendChild((wxRichTextObject*) node->GetData());
5402 }
5403 }
5404
5405 /// Calculate range
5406 void wxRichTextParagraph::CalculateRange(long start, long& end)
5407 {
5408 wxRichTextCompositeObject::CalculateRange(start, end);
5409
5410 // Add one for end of paragraph
5411 end ++;
5412
5413 m_range.SetRange(start, end);
5414 }
5415
5416 /// Find the object at the given position
5417 wxRichTextObject* wxRichTextParagraph::FindObjectAtPosition(long position)
5418 {
5419 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5420 while (node)
5421 {
5422 wxRichTextObject* obj = node->GetData();
5423 if (obj->GetRange().Contains(position) ||
5424 obj->GetRange().GetStart() == position ||
5425 obj->GetRange().GetEnd() == position)
5426 return obj;
5427
5428 node = node->GetNext();
5429 }
5430 return NULL;
5431 }
5432
5433 /// Get the plain text searching from the start or end of the range.
5434 /// The resulting string may be shorter than the range given.
5435 bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTextRange& range, bool fromStart)
5436 {
5437 text = wxEmptyString;
5438
5439 if (fromStart)
5440 {
5441 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5442 while (node)
5443 {
5444 wxRichTextObject* obj = node->GetData();
5445 if (!obj->GetRange().IsOutside(range))
5446 {
5447 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5448 if (textObj)
5449 {
5450 text += textObj->GetTextForRange(range);
5451 }
5452 else
5453 {
5454 text += wxT(" ");
5455 }
5456 }
5457
5458 node = node->GetNext();
5459 }
5460 }
5461 else
5462 {
5463 wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
5464 while (node)
5465 {
5466 wxRichTextObject* obj = node->GetData();
5467 if (!obj->GetRange().IsOutside(range))
5468 {
5469 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5470 if (textObj)
5471 {
5472 text = textObj->GetTextForRange(range) + text;
5473 }
5474 else
5475 {
5476 text = wxT(" ") + text;
5477 }
5478 }
5479
5480 node = node->GetPrevious();
5481 }
5482 }
5483
5484 return true;
5485 }
5486
5487 /// Find a suitable wrap position.
5488 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents)
5489 {
5490 if (range.GetLength() <= 0)
5491 return false;
5492
5493 // Find the first position where the line exceeds the available space.
5494 wxSize sz;
5495 long breakPosition = range.GetEnd();
5496
5497 #if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
5498 if (partialExtents && partialExtents->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
5499 {
5500 int widthBefore;
5501
5502 if (range.GetStart() > GetRange().GetStart())
5503 widthBefore = (*partialExtents)[range.GetStart() - GetRange().GetStart() - 1];
5504 else
5505 widthBefore = 0;
5506
5507 size_t i;
5508 for (i = (size_t) range.GetStart(); i <= (size_t) range.GetEnd(); i++)
5509 {
5510 int widthFromStartOfThisRange = (*partialExtents)[i - GetRange().GetStart()] - widthBefore;
5511
5512 if (widthFromStartOfThisRange > availableSpace)
5513 {
5514 breakPosition = i-1;
5515 break;
5516 }
5517 }
5518 }
5519 else
5520 #endif
5521 {
5522 // Binary chop for speed
5523 long minPos = range.GetStart();
5524 long maxPos = range.GetEnd();
5525 while (true)
5526 {
5527 if (minPos == maxPos)
5528 {
5529 int descent = 0;
5530 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
5531
5532 if (sz.x > availableSpace)
5533 breakPosition = minPos - 1;
5534 break;
5535 }
5536 else if ((maxPos - minPos) == 1)
5537 {
5538 int descent = 0;
5539 GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
5540
5541 if (sz.x > availableSpace)
5542 breakPosition = minPos - 1;
5543 else
5544 {
5545 GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
5546 if (sz.x > availableSpace)
5547 breakPosition = maxPos-1;
5548 }
5549 break;
5550 }
5551 else
5552 {
5553 long nextPos = minPos + ((maxPos - minPos) / 2);
5554
5555 int descent = 0;
5556 GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
5557
5558 if (sz.x > availableSpace)
5559 {
5560 maxPos = nextPos;
5561 }
5562 else
5563 {
5564 minPos = nextPos;
5565 }
5566 }
5567 }
5568 }
5569
5570 // Now we know the last position on the line.
5571 // Let's try to find a word break.
5572
5573 wxString plainText;
5574 if (GetContiguousPlainText(plainText, wxRichTextRange(range.GetStart(), breakPosition), false))
5575 {
5576 int newLinePos = plainText.Find(wxRichTextLineBreakChar);
5577 if (newLinePos != wxNOT_FOUND)
5578 {
5579 breakPosition = wxMax(0, range.GetStart() + newLinePos);
5580 }
5581 else
5582 {
5583 int spacePos = plainText.Find(wxT(' '), true);
5584 int tabPos = plainText.Find(wxT('\t'), true);
5585 int pos = wxMax(spacePos, tabPos);
5586 if (pos != wxNOT_FOUND)
5587 {
5588 int positionsFromEndOfString = plainText.length() - pos - 1;
5589 breakPosition = breakPosition - positionsFromEndOfString;
5590 }
5591 }
5592 }
5593
5594 wrapPosition = breakPosition;
5595
5596 return true;
5597 }
5598
5599 /// Get the bullet text for this paragraph.
5600 wxString wxRichTextParagraph::GetBulletText()
5601 {
5602 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ||
5603 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP))
5604 return wxEmptyString;
5605
5606 int number = GetAttributes().GetBulletNumber();
5607
5608 wxString text;
5609 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE))
5610 {
5611 text.Printf(wxT("%d"), number);
5612 }
5613 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER)
5614 {
5615 // TODO: Unicode, and also check if number > 26
5616 text.Printf(wxT("%c"), (wxChar) (number+64));
5617 }
5618 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER)
5619 {
5620 // TODO: Unicode, and also check if number > 26
5621 text.Printf(wxT("%c"), (wxChar) (number+96));
5622 }
5623 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
5624 {
5625 text = wxRichTextDecimalToRoman(number);
5626 }
5627 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
5628 {
5629 text = wxRichTextDecimalToRoman(number);
5630 text.MakeLower();
5631 }
5632 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
5633 {
5634 text = GetAttributes().GetBulletText();
5635 }
5636
5637 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
5638 {
5639 // The outline style relies on the text being computed statically,
5640 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
5641 // should be stored in the attributes; if not, just use the number for this
5642 // level, as previously computed.
5643 if (!GetAttributes().GetBulletText().IsEmpty())
5644 text = GetAttributes().GetBulletText();
5645 }
5646
5647 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES)
5648 {
5649 text = wxT("(") + text + wxT(")");
5650 }
5651 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS)
5652 {
5653 text = text + wxT(")");
5654 }
5655
5656 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD)
5657 {
5658 text += wxT(".");
5659 }
5660
5661 return text;
5662 }
5663
5664 /// Allocate or reuse a line object
5665 wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
5666 {
5667 if (pos < (int) m_cachedLines.GetCount())
5668 {
5669 wxRichTextLine* line = m_cachedLines.Item(pos)->GetData();
5670 line->Init(this);
5671 return line;
5672 }
5673 else
5674 {
5675 wxRichTextLine* line = new wxRichTextLine(this);
5676 m_cachedLines.Append(line);
5677 return line;
5678 }
5679 }
5680
5681 /// Clear remaining unused line objects, if any
5682 bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
5683 {
5684 int cachedLineCount = m_cachedLines.GetCount();
5685 if ((int) cachedLineCount > lineCount)
5686 {
5687 for (int i = 0; i < (int) (cachedLineCount - lineCount); i ++)
5688 {
5689 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetLast();
5690 wxRichTextLine* line = node->GetData();
5691 m_cachedLines.Erase(node);
5692 delete line;
5693 }
5694 }
5695 return true;
5696 }
5697
5698 /// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
5699 /// retrieve the actual style.
5700 wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr& contentStyle, bool includingBoxAttr) const
5701 {
5702 wxRichTextAttr attr;
5703 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
5704 if (buf)
5705 {
5706 attr = buf->GetBasicStyle();
5707 if (!includingBoxAttr)
5708 {
5709 attr.GetTextBoxAttr().Reset();
5710 // The background colour will be painted by the container, and we don't
5711 // want to unnecessarily overwrite the background when we're drawing text
5712 // because this may erase the guideline (which appears just under the text
5713 // if there's no padding).
5714 attr.SetFlags(attr.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR);
5715 }
5716 wxRichTextApplyStyle(attr, GetAttributes());
5717 }
5718 else
5719 attr = GetAttributes();
5720
5721 wxRichTextApplyStyle(attr, contentStyle);
5722 return attr;
5723 }
5724
5725 /// Get combined attributes of the base style and paragraph style.
5726 wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr) const
5727 {
5728 wxRichTextAttr attr;
5729 wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
5730 if (buf)
5731 {
5732 attr = buf->GetBasicStyle();
5733 if (!includingBoxAttr)
5734 attr.GetTextBoxAttr().Reset();
5735 wxRichTextApplyStyle(attr, GetAttributes());
5736 }
5737 else
5738 attr = GetAttributes();
5739
5740 return attr;
5741 }
5742
5743 // Create default tabstop array
5744 void wxRichTextParagraph::InitDefaultTabs()
5745 {
5746 // create a default tab list at 10 mm each.
5747 for (int i = 0; i < 20; ++i)
5748 {
5749 sm_defaultTabs.Add(i*100);
5750 }
5751 }
5752
5753 // Clear default tabstop array
5754 void wxRichTextParagraph::ClearDefaultTabs()
5755 {
5756 sm_defaultTabs.Clear();
5757 }
5758
5759 void wxRichTextParagraph::LayoutFloat(wxDC& dc, const wxRect& rect, int style, wxRichTextFloatCollector* floatCollector)
5760 {
5761 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
5762 while (node)
5763 {
5764 wxRichTextObject* anchored = node->GetData();
5765 if (anchored && anchored->IsFloating() && !floatCollector->HasFloat(anchored))
5766 {
5767 wxSize size;
5768 int descent, x = 0;
5769 anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, style);
5770
5771 int offsetY = 0;
5772 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
5773 {
5774 offsetY = anchored->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
5775 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
5776 {
5777 offsetY = ConvertTenthsMMToPixels(dc, offsetY);
5778 }
5779 }
5780
5781 int pos = floatCollector->GetFitPosition(anchored->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect.y + offsetY, size.y);
5782
5783 /* Update the offset */
5784 int newOffsetY = pos - rect.y;
5785 if (newOffsetY != offsetY)
5786 {
5787 if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
5788 newOffsetY = ConvertPixelsToTenthsMM(dc, newOffsetY);
5789 anchored->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY);
5790 }
5791
5792 if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
5793 x = rect.x;
5794 else if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
5795 x = rect.x + rect.width - size.x;
5796
5797 anchored->SetPosition(wxPoint(x, pos));
5798 anchored->SetCachedSize(size);
5799 floatCollector->CollectFloat(this, anchored);
5800 }
5801
5802 node = node->GetNext();
5803 }
5804 }
5805
5806 // Get the first position from pos that has a line break character.
5807 long wxRichTextParagraph::GetFirstLineBreakPosition(long pos)
5808 {
5809 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
5810 while (node)
5811 {
5812 wxRichTextObject* obj = node->GetData();
5813 if (pos >= obj->GetRange().GetStart() && pos <= obj->GetRange().GetEnd())
5814 {
5815 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
5816 if (textObj)
5817 {
5818 long breakPos = textObj->GetFirstLineBreakPosition(pos);
5819 if (breakPos > -1)
5820 return breakPos;
5821 }
5822 }
5823 node = node->GetNext();
5824 }
5825 return -1;
5826 }
5827
5828 /*!
5829 * wxRichTextLine
5830 * This object represents a line in a paragraph, and stores
5831 * offsets from the start of the paragraph representing the
5832 * start and end positions of the line.
5833 */
5834
5835 wxRichTextLine::wxRichTextLine(wxRichTextParagraph* parent)
5836 {
5837 Init(parent);
5838 }
5839
5840 /// Initialisation
5841 void wxRichTextLine::Init(wxRichTextParagraph* parent)
5842 {
5843 m_parent = parent;
5844 m_range.SetRange(-1, -1);
5845 m_pos = wxPoint(0, 0);
5846 m_size = wxSize(0, 0);
5847 m_descent = 0;
5848 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5849 m_objectSizes.Clear();
5850 #endif
5851 }
5852
5853 /// Copy
5854 void wxRichTextLine::Copy(const wxRichTextLine& obj)
5855 {
5856 m_range = obj.m_range;
5857 #if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
5858 m_objectSizes = obj.m_objectSizes;
5859 #endif
5860 }
5861
5862 /// Get the absolute object position
5863 wxPoint wxRichTextLine::GetAbsolutePosition() const
5864 {
5865 return m_parent->GetPosition() + m_pos;
5866 }
5867
5868 /// Get the absolute range
5869 wxRichTextRange wxRichTextLine::GetAbsoluteRange() const
5870 {
5871 wxRichTextRange range(m_range.GetStart() + m_parent->GetRange().GetStart(), 0);
5872 range.SetEnd(range.GetStart() + m_range.GetLength()-1);
5873 return range;
5874 }
5875
5876 /*!
5877 * wxRichTextPlainText
5878 * This object represents a single piece of text.
5879 */
5880
5881 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText, wxRichTextObject)
5882
5883 wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* style):
5884 wxRichTextObject(parent)
5885 {
5886 if (style)
5887 SetAttributes(*style);
5888
5889 m_text = text;
5890 }
5891
5892 #define USE_KERNING_FIX 1
5893
5894 // If insufficient tabs are defined, this is the tab width used
5895 #define WIDTH_FOR_DEFAULT_TABS 50
5896
5897 /// Draw the item
5898 bool wxRichTextPlainText::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
5899 {
5900 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
5901 wxASSERT (para != NULL);
5902
5903 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
5904
5905 // Let's make the assumption for now that for content in a paragraph, including
5906 // text, we never have a discontinuous selection. So we only deal with a
5907 // single range.
5908 wxRichTextRange selectionRange;
5909 if (selection.IsValid())
5910 {
5911 wxRichTextRangeArray selectionRanges = selection.GetSelectionForObject(this);
5912 if (selectionRanges.GetCount() > 0)
5913 selectionRange = selectionRanges[0];
5914 else
5915 selectionRange = wxRICHTEXT_NO_SELECTION;
5916 }
5917 else
5918 selectionRange = wxRICHTEXT_NO_SELECTION;
5919
5920 int offset = GetRange().GetStart();
5921
5922 // Replace line break characters with spaces
5923 wxString str = m_text;
5924 wxString toRemove = wxRichTextLineBreakChar;
5925 str.Replace(toRemove, wxT(" "));
5926 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
5927 str.MakeUpper();
5928
5929 long len = range.GetLength();
5930 wxString stringChunk = str.Mid(range.GetStart() - offset, (size_t) len);
5931
5932 // Test for the optimized situations where all is selected, or none
5933 // is selected.
5934
5935 wxFont textFont(GetBuffer()->GetFontTable().FindFont(textAttr));
5936 wxCheckSetFont(dc, textFont);
5937 int charHeight = dc.GetCharHeight();
5938
5939 int x, y;
5940 if ( textFont.Ok() )
5941 {
5942 if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT) )
5943 {
5944 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
5945 textFont.SetPointSize( static_cast<int>(size) );
5946 x = rect.x;
5947 y = rect.y;
5948 wxCheckSetFont(dc, textFont);
5949 }
5950 else if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) )
5951 {
5952 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
5953 textFont.SetPointSize( static_cast<int>(size) );
5954 x = rect.x;
5955 int sub_height = static_cast<int>( static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
5956 y = rect.y + (rect.height - sub_height + (descent - m_descent));
5957 wxCheckSetFont(dc, textFont);
5958 }
5959 else
5960 {
5961 x = rect.x;
5962 y = rect.y + (rect.height - charHeight - (descent - m_descent));
5963 }
5964 }
5965 else
5966 {
5967 x = rect.x;
5968 y = rect.y + (rect.height - charHeight - (descent - m_descent));
5969 }
5970
5971 // TODO: new selection code
5972
5973 // (a) All selected.
5974 if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd())
5975 {
5976 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, true);
5977 }
5978 // (b) None selected.
5979 else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd())
5980 {
5981 // Draw all unselected
5982 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, false);
5983 }
5984 else
5985 {
5986 // (c) Part selected, part not
5987 // Let's draw unselected chunk, selected chunk, then unselected chunk.
5988
5989 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
5990
5991 // 1. Initial unselected chunk, if any, up until start of selection.
5992 if (selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd())
5993 {
5994 int r1 = range.GetStart();
5995 int s1 = selectionRange.GetStart()-1;
5996 int fragmentLen = s1 - r1 + 1;
5997 if (fragmentLen < 0)
5998 {
5999 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 - offset), (int)fragmentLen);
6000 }
6001 wxString stringFragment = str.Mid(r1 - offset, fragmentLen);
6002
6003 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
6004
6005 #if USE_KERNING_FIX
6006 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6007 {
6008 // Compensate for kerning difference
6009 wxString stringFragment2(str.Mid(r1 - offset, fragmentLen+1));
6010 wxString stringFragment3(str.Mid(r1 - offset + fragmentLen, 1));
6011
6012 wxCoord w1, h1, w2, h2, w3, h3;
6013 dc.GetTextExtent(stringFragment, & w1, & h1);
6014 dc.GetTextExtent(stringFragment2, & w2, & h2);
6015 dc.GetTextExtent(stringFragment3, & w3, & h3);
6016
6017 int kerningDiff = (w1 + w3) - w2;
6018 x = x - kerningDiff;
6019 }
6020 #endif
6021 }
6022
6023 // 2. Selected chunk, if any.
6024 if (selectionRange.GetEnd() >= range.GetStart())
6025 {
6026 int s1 = wxMax(selectionRange.GetStart(), range.GetStart());
6027 int s2 = wxMin(selectionRange.GetEnd(), range.GetEnd());
6028
6029 int fragmentLen = s2 - s1 + 1;
6030 if (fragmentLen < 0)
6031 {
6032 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 - offset), (int)fragmentLen);
6033 }
6034 wxString stringFragment = str.Mid(s1 - offset, fragmentLen);
6035
6036 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, true);
6037
6038 #if USE_KERNING_FIX
6039 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
6040 {
6041 // Compensate for kerning difference
6042 wxString stringFragment2(str.Mid(s1 - offset, fragmentLen+1));
6043 wxString stringFragment3(str.Mid(s1 - offset + fragmentLen, 1));
6044
6045 wxCoord w1, h1, w2, h2, w3, h3;
6046 dc.GetTextExtent(stringFragment, & w1, & h1);
6047 dc.GetTextExtent(stringFragment2, & w2, & h2);
6048 dc.GetTextExtent(stringFragment3, & w3, & h3);
6049
6050 int kerningDiff = (w1 + w3) - w2;
6051 x = x - kerningDiff;
6052 }
6053 #endif
6054 }
6055
6056 // 3. Remaining unselected chunk, if any
6057 if (selectionRange.GetEnd() < range.GetEnd())
6058 {
6059 int s2 = wxMin(selectionRange.GetEnd()+1, range.GetEnd());
6060 int r2 = range.GetEnd();
6061
6062 int fragmentLen = r2 - s2 + 1;
6063 if (fragmentLen < 0)
6064 {
6065 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 - offset), (int)fragmentLen);
6066 }
6067 wxString stringFragment = str.Mid(s2 - offset, fragmentLen);
6068
6069 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
6070 }
6071 }
6072
6073 return true;
6074 }
6075
6076 bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect,wxString& str, wxCoord& x, wxCoord& y, bool selected)
6077 {
6078 bool hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6079
6080 wxArrayInt tabArray;
6081 int tabCount;
6082 if (hasTabs)
6083 {
6084 if (attr.GetTabs().IsEmpty())
6085 tabArray = wxRichTextParagraph::GetDefaultTabs();
6086 else
6087 tabArray = attr.GetTabs();
6088 tabCount = tabArray.GetCount();
6089
6090 for (int i = 0; i < tabCount; ++i)
6091 {
6092 int pos = tabArray[i];
6093 pos = ConvertTenthsMMToPixels(dc, pos);
6094 tabArray[i] = pos;
6095 }
6096 }
6097 else
6098 tabCount = 0;
6099
6100 int nextTabPos = -1;
6101 int tabPos = -1;
6102 wxCoord w, h;
6103
6104 if (selected)
6105 {
6106 wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
6107 wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
6108
6109 wxCheckSetBrush(dc, wxBrush(highlightColour));
6110 wxCheckSetPen(dc, wxPen(highlightColour));
6111 dc.SetTextForeground(highlightTextColour);
6112 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
6113 }
6114 else
6115 {
6116 dc.SetTextForeground(attr.GetTextColour());
6117
6118 if (attr.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR) && attr.GetBackgroundColour().IsOk())
6119 {
6120 dc.SetBackgroundMode(wxBRUSHSTYLE_SOLID);
6121 dc.SetTextBackground(attr.GetBackgroundColour());
6122 }
6123 else
6124 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
6125 }
6126
6127 wxCoord x_orig = GetParent()->GetPosition().x;
6128 while (hasTabs)
6129 {
6130 // the string has a tab
6131 // break up the string at the Tab
6132 wxString stringChunk = str.BeforeFirst(wxT('\t'));
6133 str = str.AfterFirst(wxT('\t'));
6134 dc.GetTextExtent(stringChunk, & w, & h);
6135 tabPos = x + w;
6136 bool not_found = true;
6137 for (int i = 0; i < tabCount && not_found; ++i)
6138 {
6139 nextTabPos = tabArray.Item(i) + x_orig;
6140
6141 // Find the next tab position.
6142 // Even if we're at the end of the tab array, we must still draw the chunk.
6143
6144 if (nextTabPos > tabPos || (i == (tabCount - 1)))
6145 {
6146 if (nextTabPos <= tabPos)
6147 {
6148 int defaultTabWidth = ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6149 nextTabPos = tabPos + defaultTabWidth;
6150 }
6151
6152 not_found = false;
6153 if (selected)
6154 {
6155 w = nextTabPos - x;
6156 wxRect selRect(x, rect.y, w, rect.GetHeight());
6157 dc.DrawRectangle(selRect);
6158 }
6159 dc.DrawText(stringChunk, x, y);
6160
6161 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6162 {
6163 wxPen oldPen = dc.GetPen();
6164 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
6165 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
6166 wxCheckSetPen(dc, oldPen);
6167 }
6168
6169 x = nextTabPos;
6170 }
6171 }
6172 hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
6173 }
6174
6175 if (!str.IsEmpty())
6176 {
6177 dc.GetTextExtent(str, & w, & h);
6178 if (selected)
6179 {
6180 wxRect selRect(x, rect.y, w, rect.GetHeight());
6181 dc.DrawRectangle(selRect);
6182 }
6183 dc.DrawText(str, x, y);
6184
6185 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
6186 {
6187 wxPen oldPen = dc.GetPen();
6188 wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
6189 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
6190 wxCheckSetPen(dc, oldPen);
6191 }
6192
6193 x += w;
6194 }
6195 return true;
6196
6197 }
6198
6199 /// Lay the item out
6200 bool wxRichTextPlainText::Layout(wxDC& dc, const wxRect& WXUNUSED(rect), int WXUNUSED(style))
6201 {
6202 // Only lay out if we haven't already cached the size
6203 if (m_size.x == -1)
6204 GetRangeSize(GetRange(), m_size, m_descent, dc, 0, wxPoint(0, 0));
6205 m_maxSize = m_size;
6206 // Eventually we want to have a reasonable estimate of minimum size.
6207 m_minSize = wxSize(0, 0);
6208 return true;
6209 }
6210
6211 /// Copy
6212 void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj)
6213 {
6214 wxRichTextObject::Copy(obj);
6215
6216 m_text = obj.m_text;
6217 }
6218
6219 /// Get/set the object size for the given range. Returns false if the range
6220 /// is invalid for this object.
6221 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int WXUNUSED(flags), wxPoint position, wxArrayInt* partialExtents) const
6222 {
6223 if (!range.IsWithin(GetRange()))
6224 return false;
6225
6226 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
6227 wxASSERT (para != NULL);
6228
6229 int relativeX = position.x - GetParent()->GetPosition().x;
6230
6231 wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes());
6232
6233 // Always assume unformatted text, since at this level we have no knowledge
6234 // of line breaks - and we don't need it, since we'll calculate size within
6235 // formatted text by doing it in chunks according to the line ranges
6236
6237 bool bScript(false);
6238 wxFont font(GetBuffer()->GetFontTable().FindFont(textAttr));
6239 if (font.Ok())
6240 {
6241 if ( textAttr.HasTextEffects() && ( (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT)
6242 || (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) )
6243 {
6244 wxFont textFont = font;
6245 double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
6246 textFont.SetPointSize( static_cast<int>(size) );
6247 wxCheckSetFont(dc, textFont);
6248 bScript = true;
6249 }
6250 else
6251 {
6252 wxCheckSetFont(dc, font);
6253 }
6254 }
6255
6256 bool haveDescent = false;
6257 int startPos = range.GetStart() - GetRange().GetStart();
6258 long len = range.GetLength();
6259
6260 wxString str(m_text);
6261 wxString toReplace = wxRichTextLineBreakChar;
6262 str.Replace(toReplace, wxT(" "));
6263
6264 wxString stringChunk = str.Mid(startPos, (size_t) len);
6265
6266 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
6267 stringChunk.MakeUpper();
6268
6269 wxCoord w, h;
6270 int width = 0;
6271 if (stringChunk.Find(wxT('\t')) != wxNOT_FOUND)
6272 {
6273 // the string has a tab
6274 wxArrayInt tabArray;
6275 if (textAttr.GetTabs().IsEmpty())
6276 tabArray = wxRichTextParagraph::GetDefaultTabs();
6277 else
6278 tabArray = textAttr.GetTabs();
6279
6280 int tabCount = tabArray.GetCount();
6281
6282 for (int i = 0; i < tabCount; ++i)
6283 {
6284 int pos = tabArray[i];
6285 pos = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, pos);
6286 tabArray[i] = pos;
6287 }
6288
6289 int nextTabPos = -1;
6290
6291 while (stringChunk.Find(wxT('\t')) >= 0)
6292 {
6293 int absoluteWidth = 0;
6294
6295 // the string has a tab
6296 // break up the string at the Tab
6297 wxString stringFragment = stringChunk.BeforeFirst(wxT('\t'));
6298 stringChunk = stringChunk.AfterFirst(wxT('\t'));
6299
6300 if (partialExtents)
6301 {
6302 int oldWidth;
6303 if (partialExtents->GetCount() > 0)
6304 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6305 else
6306 oldWidth = 0;
6307
6308 // Add these partial extents
6309 wxArrayInt p;
6310 dc.GetPartialTextExtents(stringFragment, p);
6311 size_t j;
6312 for (j = 0; j < p.GetCount(); j++)
6313 partialExtents->Add(oldWidth + p[j]);
6314
6315 if (partialExtents->GetCount() > 0)
6316 absoluteWidth = (*partialExtents)[(*partialExtents).GetCount()-1] + relativeX;
6317 else
6318 absoluteWidth = relativeX;
6319 }
6320 else
6321 {
6322 dc.GetTextExtent(stringFragment, & w, & h);
6323 width += w;
6324 absoluteWidth = width + relativeX;
6325 haveDescent = true;
6326 }
6327
6328 bool notFound = true;
6329 for (int i = 0; i < tabCount && notFound; ++i)
6330 {
6331 nextTabPos = tabArray.Item(i);
6332
6333 // Find the next tab position.
6334 // Even if we're at the end of the tab array, we must still process the chunk.
6335
6336 if (nextTabPos > absoluteWidth || (i == (tabCount - 1)))
6337 {
6338 if (nextTabPos <= absoluteWidth)
6339 {
6340 int defaultTabWidth = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
6341 nextTabPos = absoluteWidth + defaultTabWidth;
6342 }
6343
6344 notFound = false;
6345 width = nextTabPos - relativeX;
6346
6347 if (partialExtents)
6348 partialExtents->Add(width);
6349 }
6350 }
6351 }
6352 }
6353
6354 if (!stringChunk.IsEmpty())
6355 {
6356 if (partialExtents)
6357 {
6358 int oldWidth;
6359 if (partialExtents->GetCount() > 0)
6360 oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
6361 else
6362 oldWidth = 0;
6363
6364 // Add these partial extents
6365 wxArrayInt p;
6366 dc.GetPartialTextExtents(stringChunk, p);
6367 size_t j;
6368 for (j = 0; j < p.GetCount(); j++)
6369 partialExtents->Add(oldWidth + p[j]);
6370 }
6371 else
6372 {
6373 dc.GetTextExtent(stringChunk, & w, & h, & descent);
6374 width += w;
6375 haveDescent = true;
6376 }
6377 }
6378
6379 if (partialExtents)
6380 {
6381 int charHeight = dc.GetCharHeight();
6382 if ((*partialExtents).GetCount() > 0)
6383 w = (*partialExtents)[partialExtents->GetCount()-1];
6384 else
6385 w = 0;
6386 size = wxSize(w, charHeight);
6387 }
6388 else
6389 {
6390 size = wxSize(width, dc.GetCharHeight());
6391 }
6392
6393 if (!haveDescent)
6394 dc.GetTextExtent(wxT("X"), & w, & h, & descent);
6395
6396 if ( bScript )
6397 dc.SetFont(font);
6398
6399 return true;
6400 }
6401
6402 /// Do a split, returning an object containing the second part, and setting
6403 /// the first part in 'this'.
6404 wxRichTextObject* wxRichTextPlainText::DoSplit(long pos)
6405 {
6406 long index = pos - GetRange().GetStart();
6407
6408 if (index < 0 || index >= (int) m_text.length())
6409 return NULL;
6410
6411 wxString firstPart = m_text.Mid(0, index);
6412 wxString secondPart = m_text.Mid(index);
6413
6414 m_text = firstPart;
6415
6416 wxRichTextPlainText* newObject = new wxRichTextPlainText(secondPart);
6417 newObject->SetAttributes(GetAttributes());
6418
6419 newObject->SetRange(wxRichTextRange(pos, GetRange().GetEnd()));
6420 GetRange().SetEnd(pos-1);
6421
6422 return newObject;
6423 }
6424
6425 /// Calculate range
6426 void wxRichTextPlainText::CalculateRange(long start, long& end)
6427 {
6428 end = start + m_text.length() - 1;
6429 m_range.SetRange(start, end);
6430 }
6431
6432 /// Delete range
6433 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange& range)
6434 {
6435 wxRichTextRange r = range;
6436
6437 r.LimitTo(GetRange());
6438
6439 if (r.GetStart() == GetRange().GetStart() && r.GetEnd() == GetRange().GetEnd())
6440 {
6441 m_text.Empty();
6442 return true;
6443 }
6444
6445 long startIndex = r.GetStart() - GetRange().GetStart();
6446 long len = r.GetLength();
6447
6448 m_text = m_text.Mid(0, startIndex) + m_text.Mid(startIndex+len);
6449 return true;
6450 }
6451
6452 /// Get text for the given range.
6453 wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) const
6454 {
6455 wxRichTextRange r = range;
6456
6457 r.LimitTo(GetRange());
6458
6459 long startIndex = r.GetStart() - GetRange().GetStart();
6460 long len = r.GetLength();
6461
6462 return m_text.Mid(startIndex, len);
6463 }
6464
6465 /// Returns true if this object can merge itself with the given one.
6466 bool wxRichTextPlainText::CanMerge(wxRichTextObject* object) const
6467 {
6468 return object->GetClassInfo() == CLASSINFO(wxRichTextPlainText) &&
6469 (m_text.empty() || wxTextAttrEq(GetAttributes(), object->GetAttributes()));
6470 }
6471
6472 /// Returns true if this object merged itself with the given one.
6473 /// The calling code will then delete the given object.
6474 bool wxRichTextPlainText::Merge(wxRichTextObject* object)
6475 {
6476 wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText);
6477 wxASSERT( textObject != NULL );
6478
6479 if (textObject)
6480 {
6481 m_text += textObject->GetText();
6482 wxRichTextApplyStyle(m_attributes, textObject->GetAttributes());
6483 return true;
6484 }
6485 else
6486 return false;
6487 }
6488
6489 /// Dump to output stream for debugging
6490 void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
6491 {
6492 wxRichTextObject::Dump(stream);
6493 stream << m_text << wxT("\n");
6494 }
6495
6496 /// Get the first position from pos that has a line break character.
6497 long wxRichTextPlainText::GetFirstLineBreakPosition(long pos)
6498 {
6499 int i;
6500 int len = m_text.length();
6501 int startPos = pos - m_range.GetStart();
6502 for (i = startPos; i < len; i++)
6503 {
6504 wxChar ch = m_text[i];
6505 if (ch == wxRichTextLineBreakChar)
6506 {
6507 return i + m_range.GetStart();
6508 }
6509 }
6510 return -1;
6511 }
6512
6513 /*!
6514 * wxRichTextBuffer
6515 * This is a kind of box, used to represent the whole buffer
6516 */
6517
6518 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox)
6519
6520 wxList wxRichTextBuffer::sm_handlers;
6521 wxRichTextRenderer* wxRichTextBuffer::sm_renderer = NULL;
6522 int wxRichTextBuffer::sm_bulletRightMargin = 20;
6523 float wxRichTextBuffer::sm_bulletProportion = (float) 0.3;
6524
6525 /// Initialisation
6526 void wxRichTextBuffer::Init()
6527 {
6528 m_commandProcessor = new wxCommandProcessor;
6529 m_styleSheet = NULL;
6530 m_modified = false;
6531 m_batchedCommandDepth = 0;
6532 m_batchedCommand = NULL;
6533 m_suppressUndo = 0;
6534 m_handlerFlags = 0;
6535 m_scale = 1.0;
6536 }
6537
6538 /// Initialisation
6539 wxRichTextBuffer::~wxRichTextBuffer()
6540 {
6541 delete m_commandProcessor;
6542 delete m_batchedCommand;
6543
6544 ClearStyleStack();
6545 ClearEventHandlers();
6546 }
6547
6548 void wxRichTextBuffer::ResetAndClearCommands()
6549 {
6550 Reset();
6551
6552 GetCommandProcessor()->ClearCommands();
6553
6554 Modify(false);
6555 Invalidate(wxRICHTEXT_ALL);
6556 }
6557
6558 void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj)
6559 {
6560 wxRichTextParagraphLayoutBox::Copy(obj);
6561
6562 m_styleSheet = obj.m_styleSheet;
6563 m_modified = obj.m_modified;
6564 m_batchedCommandDepth = 0;
6565 if (m_batchedCommand)
6566 delete m_batchedCommand;
6567 m_batchedCommand = NULL;
6568 m_suppressUndo = obj.m_suppressUndo;
6569 m_invalidRange = obj.m_invalidRange;
6570 }
6571
6572 /// Push style sheet to top of stack
6573 bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet* styleSheet)
6574 {
6575 if (m_styleSheet)
6576 styleSheet->InsertSheet(m_styleSheet);
6577
6578 SetStyleSheet(styleSheet);
6579
6580 return true;
6581 }
6582
6583 /// Pop style sheet from top of stack
6584 wxRichTextStyleSheet* wxRichTextBuffer::PopStyleSheet()
6585 {
6586 if (m_styleSheet)
6587 {
6588 wxRichTextStyleSheet* oldSheet = m_styleSheet;
6589 m_styleSheet = oldSheet->GetNextSheet();
6590 oldSheet->Unlink();
6591
6592 return oldSheet;
6593 }
6594 else
6595 return NULL;
6596 }
6597
6598 /// Submit command to insert paragraphs
6599 bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int flags)
6600 {
6601 return ctrl->GetFocusObject()->InsertParagraphsWithUndo(pos, paragraphs, ctrl, this, flags);
6602 }
6603
6604 /// Submit command to insert paragraphs
6605 bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer, int WXUNUSED(flags))
6606 {
6607 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
6608
6609 action->GetNewParagraphs() = paragraphs;
6610
6611 action->SetPosition(pos);
6612
6613 wxRichTextRange range = wxRichTextRange(pos, pos + paragraphs.GetOwnRange().GetEnd() - 1);
6614 if (!paragraphs.GetPartialParagraph())
6615 range.SetEnd(range.GetEnd()+1);
6616
6617 // Set the range we'll need to delete in Undo
6618 action->SetRange(range);
6619
6620 buffer->SubmitAction(action);
6621
6622 return true;
6623 }
6624
6625 /// Submit command to insert the given text
6626 bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
6627 {
6628 return ctrl->GetFocusObject()->InsertTextWithUndo(pos, text, ctrl, this, flags);
6629 }
6630
6631 /// Submit command to insert the given text
6632 bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer, int flags)
6633 {
6634 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
6635
6636 wxRichTextAttr* p = NULL;
6637 wxRichTextAttr paraAttr;
6638 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
6639 {
6640 // Get appropriate paragraph style
6641 paraAttr = GetStyleForNewParagraph(buffer, pos, false, false);
6642 if (!paraAttr.IsDefault())
6643 p = & paraAttr;
6644 }
6645
6646 action->GetNewParagraphs().AddParagraphs(text, p);
6647
6648 int length = action->GetNewParagraphs().GetOwnRange().GetLength();
6649
6650 if (!text.empty() && text.Last() != wxT('\n'))
6651 {
6652 // Don't count the newline when undoing
6653 length --;
6654 action->GetNewParagraphs().SetPartialParagraph(true);
6655 }
6656 else if (!text.empty() && text.Last() == wxT('\n'))
6657 length --;
6658
6659 action->SetPosition(pos);
6660
6661 // Set the range we'll need to delete in Undo
6662 action->SetRange(wxRichTextRange(pos, pos + length - 1));
6663
6664 buffer->SubmitAction(action);
6665
6666 return true;
6667 }
6668
6669 /// Submit command to insert the given text
6670 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl, int flags)
6671 {
6672 return ctrl->GetFocusObject()->InsertNewlineWithUndo(pos, ctrl, this, flags);
6673 }
6674
6675 /// Submit command to insert the given text
6676 bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer, int flags)
6677 {
6678 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
6679
6680 wxRichTextAttr* p = NULL;
6681 wxRichTextAttr paraAttr;
6682 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
6683 {
6684 paraAttr = GetStyleForNewParagraph(buffer, pos, false, true /* look for next paragraph style */);
6685 if (!paraAttr.IsDefault())
6686 p = & paraAttr;
6687 }
6688
6689 wxRichTextAttr attr(buffer->GetDefaultStyle());
6690
6691 wxRichTextParagraph* newPara = new wxRichTextParagraph(wxEmptyString, this, & attr);
6692 action->GetNewParagraphs().AppendChild(newPara);
6693 action->GetNewParagraphs().UpdateRanges();
6694 action->GetNewParagraphs().SetPartialParagraph(false);
6695 wxRichTextParagraph* para = GetParagraphAtPosition(pos, false);
6696 long pos1 = pos;
6697
6698 if (p)
6699 newPara->SetAttributes(*p);
6700
6701 if (flags & wxRICHTEXT_INSERT_INTERACTIVE)
6702 {
6703 if (para && para->GetRange().GetEnd() == pos)
6704 pos1 ++;
6705
6706 // Now see if we need to number the paragraph.
6707 if (newPara->GetAttributes().HasBulletNumber())
6708 {
6709 wxRichTextAttr numberingAttr;
6710 if (FindNextParagraphNumber(para, numberingAttr))
6711 wxRichTextApplyStyle(newPara->GetAttributes(), (const wxRichTextAttr&) numberingAttr);
6712 }
6713 }
6714
6715 action->SetPosition(pos);
6716
6717 // Use the default character style
6718 // Use the default character style
6719 if (!buffer->GetDefaultStyle().IsDefault() && newPara->GetChildren().GetFirst())
6720 {
6721 // Check whether the default style merely reflects the paragraph/basic style,
6722 // in which case don't apply it.
6723 wxRichTextAttr defaultStyle(buffer->GetDefaultStyle());
6724 wxRichTextAttr toApply;
6725 if (para)
6726 {
6727 wxRichTextAttr combinedAttr = para->GetCombinedAttributes(true /* include box attributes */);
6728 wxRichTextAttr newAttr;
6729 // This filters out attributes that are accounted for by the current
6730 // paragraph/basic style
6731 wxRichTextApplyStyle(toApply, defaultStyle, & combinedAttr);
6732 }
6733 else
6734 toApply = defaultStyle;
6735
6736 if (!toApply.IsDefault())
6737 newPara->GetChildren().GetFirst()->GetData()->SetAttributes(toApply);
6738 }
6739
6740 // Set the range we'll need to delete in Undo
6741 action->SetRange(wxRichTextRange(pos1, pos1));
6742
6743 buffer->SubmitAction(action);
6744
6745 return true;
6746 }
6747
6748 /// Submit command to insert the given image
6749 bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock, wxRichTextCtrl* ctrl, int flags,
6750 const wxRichTextAttr& textAttr)
6751 {
6752 return ctrl->GetFocusObject()->InsertImageWithUndo(pos, imageBlock, ctrl, this, flags, textAttr);
6753 }
6754
6755 /// Submit command to insert the given image
6756 bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock,
6757 wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer, int flags,
6758 const wxRichTextAttr& textAttr)
6759 {
6760 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
6761
6762 wxRichTextAttr* p = NULL;
6763 wxRichTextAttr paraAttr;
6764 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
6765 {
6766 paraAttr = GetStyleForNewParagraph(buffer, pos);
6767 if (!paraAttr.IsDefault())
6768 p = & paraAttr;
6769 }
6770
6771 wxRichTextAttr attr(buffer->GetDefaultStyle());
6772
6773 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
6774 if (p)
6775 newPara->SetAttributes(*p);
6776
6777 wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
6778 newPara->AppendChild(imageObject);
6779 imageObject->SetAttributes(textAttr);
6780 action->GetNewParagraphs().AppendChild(newPara);
6781 action->GetNewParagraphs().UpdateRanges();
6782
6783 action->GetNewParagraphs().SetPartialParagraph(true);
6784
6785 action->SetPosition(pos);
6786
6787 // Set the range we'll need to delete in Undo
6788 action->SetRange(wxRichTextRange(pos, pos));
6789
6790 buffer->SubmitAction(action);
6791
6792 return true;
6793 }
6794
6795 // Insert an object with no change of it
6796 wxRichTextObject* wxRichTextBuffer::InsertObjectWithUndo(long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
6797 {
6798 return ctrl->GetFocusObject()->InsertObjectWithUndo(pos, object, ctrl, this, flags);
6799 }
6800
6801 // Insert an object with no change of it
6802 wxRichTextObject* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer, int flags)
6803 {
6804 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Object"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
6805
6806 wxRichTextAttr* p = NULL;
6807 wxRichTextAttr paraAttr;
6808 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
6809 {
6810 paraAttr = GetStyleForNewParagraph(buffer, pos);
6811 if (!paraAttr.IsDefault())
6812 p = & paraAttr;
6813 }
6814
6815 wxRichTextAttr attr(buffer->GetDefaultStyle());
6816
6817 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
6818 if (p)
6819 newPara->SetAttributes(*p);
6820
6821 newPara->AppendChild(object);
6822 action->GetNewParagraphs().AppendChild(newPara);
6823 action->GetNewParagraphs().UpdateRanges();
6824
6825 action->GetNewParagraphs().SetPartialParagraph(true);
6826
6827 action->SetPosition(pos);
6828
6829 // Set the range we'll need to delete in Undo
6830 action->SetRange(wxRichTextRange(pos, pos));
6831
6832 buffer->SubmitAction(action);
6833
6834 wxRichTextObject* obj = GetLeafObjectAtPosition(pos);
6835 return obj;
6836 }
6837
6838 /// Get the style that is appropriate for a new paragraph at this position.
6839 /// If the previous paragraph has a paragraph style name, look up the next-paragraph
6840 /// style.
6841 wxRichTextAttr wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer* buffer, long pos, bool caretPosition, bool lookUpNewParaStyle) const
6842 {
6843 wxRichTextParagraph* para = GetParagraphAtPosition(pos, caretPosition);
6844 if (para)
6845 {
6846 wxRichTextAttr attr;
6847 bool foundAttributes = false;
6848
6849 // Look for a matching paragraph style
6850 if (lookUpNewParaStyle && !para->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer->GetStyleSheet())
6851 {
6852 wxRichTextParagraphStyleDefinition* paraDef = buffer->GetStyleSheet()->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
6853 if (paraDef)
6854 {
6855 // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
6856 if (para->GetRange().GetEnd() == pos && !paraDef->GetNextStyle().IsEmpty())
6857 {
6858 wxRichTextParagraphStyleDefinition* nextParaDef = buffer->GetStyleSheet()->FindParagraphStyle(paraDef->GetNextStyle());
6859 if (nextParaDef)
6860 {
6861 foundAttributes = true;
6862 attr = nextParaDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
6863 }
6864 }
6865
6866 // If we didn't find the 'next style', use this style instead.
6867 if (!foundAttributes)
6868 {
6869 foundAttributes = true;
6870 attr = paraDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
6871 }
6872 }
6873 }
6874
6875 // Also apply list style if present
6876 if (lookUpNewParaStyle && !para->GetAttributes().GetListStyleName().IsEmpty() && buffer->GetStyleSheet())
6877 {
6878 wxRichTextListStyleDefinition* listDef = buffer->GetStyleSheet()->FindListStyle(para->GetAttributes().GetListStyleName());
6879 if (listDef)
6880 {
6881 int thisIndent = para->GetAttributes().GetLeftIndent();
6882 int thisLevel = para->GetAttributes().HasOutlineLevel() ? para->GetAttributes().GetOutlineLevel() : listDef->FindLevelForIndent(thisIndent);
6883
6884 // Apply the overall list style, and item style for this level
6885 wxRichTextAttr listStyle(listDef->GetCombinedStyleForLevel(thisLevel, buffer->GetStyleSheet()));
6886 wxRichTextApplyStyle(attr, listStyle);
6887 attr.SetOutlineLevel(thisLevel);
6888 if (para->GetAttributes().HasBulletNumber())
6889 attr.SetBulletNumber(para->GetAttributes().GetBulletNumber());
6890 }
6891 }
6892
6893 if (!foundAttributes)
6894 {
6895 attr = para->GetAttributes();
6896 int flags = attr.GetFlags();
6897
6898 // Eliminate character styles
6899 flags &= ( (~ wxTEXT_ATTR_FONT) |
6900 (~ wxTEXT_ATTR_TEXT_COLOUR) |
6901 (~ wxTEXT_ATTR_BACKGROUND_COLOUR) );
6902 attr.SetFlags(flags);
6903 }
6904
6905 return attr;
6906 }
6907 else
6908 return wxRichTextAttr();
6909 }
6910
6911 /// Submit command to delete this range
6912 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl)
6913 {
6914 return ctrl->GetFocusObject()->DeleteRangeWithUndo(range, ctrl, this);
6915 }
6916
6917 /// Submit command to delete this range
6918 bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer)
6919 {
6920 wxRichTextAction* action = new wxRichTextAction(NULL, _("Delete"), wxRICHTEXT_DELETE, buffer, this, ctrl);
6921
6922 action->SetPosition(ctrl->GetCaretPosition());
6923
6924 // Set the range to delete
6925 action->SetRange(range);
6926
6927 // Copy the fragment that we'll need to restore in Undo
6928 CopyFragment(range, action->GetOldParagraphs());
6929
6930 // See if we're deleting a paragraph marker, in which case we need to
6931 // make a note not to copy the attributes from the 2nd paragraph to the 1st.
6932 if (range.GetStart() == range.GetEnd())
6933 {
6934 wxRichTextParagraph* para = GetParagraphAtPosition(range.GetStart());
6935 if (para && para->GetRange().GetEnd() == range.GetEnd())
6936 {
6937 wxRichTextParagraph* nextPara = GetParagraphAtPosition(range.GetStart()+1);
6938 if (nextPara && nextPara != para)
6939 {
6940 action->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara->GetAttributes());
6941 action->GetOldParagraphs().GetAttributes().SetFlags(action->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE);
6942 }
6943 }
6944 }
6945
6946 buffer->SubmitAction(action);
6947
6948 return true;
6949 }
6950
6951 /// Collapse undo/redo commands
6952 bool wxRichTextBuffer::BeginBatchUndo(const wxString& cmdName)
6953 {
6954 if (m_batchedCommandDepth == 0)
6955 {
6956 wxASSERT(m_batchedCommand == NULL);
6957 if (m_batchedCommand)
6958 {
6959 GetCommandProcessor()->Store(m_batchedCommand);
6960 }
6961 m_batchedCommand = new wxRichTextCommand(cmdName);
6962 }
6963
6964 m_batchedCommandDepth ++;
6965
6966 return true;
6967 }
6968
6969 /// Collapse undo/redo commands
6970 bool wxRichTextBuffer::EndBatchUndo()
6971 {
6972 m_batchedCommandDepth --;
6973
6974 wxASSERT(m_batchedCommandDepth >= 0);
6975 wxASSERT(m_batchedCommand != NULL);
6976
6977 if (m_batchedCommandDepth == 0)
6978 {
6979 GetCommandProcessor()->Store(m_batchedCommand);
6980 m_batchedCommand = NULL;
6981 }
6982
6983 return true;
6984 }
6985
6986 /// Submit immediately, or delay according to whether collapsing is on
6987 bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action)
6988 {
6989 if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
6990 {
6991 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
6992 cmd->AddAction(action);
6993 cmd->Do();
6994 cmd->GetActions().Clear();
6995 delete cmd;
6996
6997 m_batchedCommand->AddAction(action);
6998 }
6999 else
7000 {
7001 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
7002 cmd->AddAction(action);
7003
7004 // Only store it if we're not suppressing undo.
7005 return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
7006 }
7007
7008 return true;
7009 }
7010
7011 /// Begin suppressing undo/redo commands.
7012 bool wxRichTextBuffer::BeginSuppressUndo()
7013 {
7014 m_suppressUndo ++;
7015
7016 return true;
7017 }
7018
7019 /// End suppressing undo/redo commands.
7020 bool wxRichTextBuffer::EndSuppressUndo()
7021 {
7022 m_suppressUndo --;
7023
7024 return true;
7025 }
7026
7027 /// Begin using a style
7028 bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr& style)
7029 {
7030 wxRichTextAttr newStyle(GetDefaultStyle());
7031
7032 // Save the old default style
7033 m_attributeStack.Append((wxObject*) new wxRichTextAttr(GetDefaultStyle()));
7034
7035 wxRichTextApplyStyle(newStyle, style);
7036 newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags());
7037
7038 SetDefaultStyle(newStyle);
7039
7040 return true;
7041 }
7042
7043 /// End the style
7044 bool wxRichTextBuffer::EndStyle()
7045 {
7046 if (!m_attributeStack.GetFirst())
7047 {
7048 wxLogDebug(_("Too many EndStyle calls!"));
7049 return false;
7050 }
7051
7052 wxList::compatibility_iterator node = m_attributeStack.GetLast();
7053 wxRichTextAttr* attr = (wxRichTextAttr*)node->GetData();
7054 m_attributeStack.Erase(node);
7055
7056 SetDefaultStyle(*attr);
7057
7058 delete attr;
7059 return true;
7060 }
7061
7062 /// End all styles
7063 bool wxRichTextBuffer::EndAllStyles()
7064 {
7065 while (m_attributeStack.GetCount() != 0)
7066 EndStyle();
7067 return true;
7068 }
7069
7070 /// Clear the style stack
7071 void wxRichTextBuffer::ClearStyleStack()
7072 {
7073 for (wxList::compatibility_iterator node = m_attributeStack.GetFirst(); node; node = node->GetNext())
7074 delete (wxRichTextAttr*) node->GetData();
7075 m_attributeStack.Clear();
7076 }
7077
7078 /// Begin using bold
7079 bool wxRichTextBuffer::BeginBold()
7080 {
7081 wxRichTextAttr attr;
7082 attr.SetFontWeight(wxFONTWEIGHT_BOLD);
7083
7084 return BeginStyle(attr);
7085 }
7086
7087 /// Begin using italic
7088 bool wxRichTextBuffer::BeginItalic()
7089 {
7090 wxRichTextAttr attr;
7091 attr.SetFontStyle(wxFONTSTYLE_ITALIC);
7092
7093 return BeginStyle(attr);
7094 }
7095
7096 /// Begin using underline
7097 bool wxRichTextBuffer::BeginUnderline()
7098 {
7099 wxRichTextAttr attr;
7100 attr.SetFontUnderlined(true);
7101
7102 return BeginStyle(attr);
7103 }
7104
7105 /// Begin using point size
7106 bool wxRichTextBuffer::BeginFontSize(int pointSize)
7107 {
7108 wxRichTextAttr attr;
7109 attr.SetFontSize(pointSize);
7110
7111 return BeginStyle(attr);
7112 }
7113
7114 /// Begin using this font
7115 bool wxRichTextBuffer::BeginFont(const wxFont& font)
7116 {
7117 wxRichTextAttr attr;
7118 attr.SetFont(font);
7119
7120 return BeginStyle(attr);
7121 }
7122
7123 /// Begin using this colour
7124 bool wxRichTextBuffer::BeginTextColour(const wxColour& colour)
7125 {
7126 wxRichTextAttr attr;
7127 attr.SetFlags(wxTEXT_ATTR_TEXT_COLOUR);
7128 attr.SetTextColour(colour);
7129
7130 return BeginStyle(attr);
7131 }
7132
7133 /// Begin using alignment
7134 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment)
7135 {
7136 wxRichTextAttr attr;
7137 attr.SetFlags(wxTEXT_ATTR_ALIGNMENT);
7138 attr.SetAlignment(alignment);
7139
7140 return BeginStyle(attr);
7141 }
7142
7143 /// Begin left indent
7144 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent, int leftSubIndent)
7145 {
7146 wxRichTextAttr attr;
7147 attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
7148 attr.SetLeftIndent(leftIndent, leftSubIndent);
7149
7150 return BeginStyle(attr);
7151 }
7152
7153 /// Begin right indent
7154 bool wxRichTextBuffer::BeginRightIndent(int rightIndent)
7155 {
7156 wxRichTextAttr attr;
7157 attr.SetFlags(wxTEXT_ATTR_RIGHT_INDENT);
7158 attr.SetRightIndent(rightIndent);
7159
7160 return BeginStyle(attr);
7161 }
7162
7163 /// Begin paragraph spacing
7164 bool wxRichTextBuffer::BeginParagraphSpacing(int before, int after)
7165 {
7166 long flags = 0;
7167 if (before != 0)
7168 flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
7169 if (after != 0)
7170 flags |= wxTEXT_ATTR_PARA_SPACING_AFTER;
7171
7172 wxRichTextAttr attr;
7173 attr.SetFlags(flags);
7174 attr.SetParagraphSpacingBefore(before);
7175 attr.SetParagraphSpacingAfter(after);
7176
7177 return BeginStyle(attr);
7178 }
7179
7180 /// Begin line spacing
7181 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing)
7182 {
7183 wxRichTextAttr attr;
7184 attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
7185 attr.SetLineSpacing(lineSpacing);
7186
7187 return BeginStyle(attr);
7188 }
7189
7190 /// Begin numbered bullet
7191 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber, int leftIndent, int leftSubIndent, int bulletStyle)
7192 {
7193 wxRichTextAttr attr;
7194 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
7195 attr.SetBulletStyle(bulletStyle);
7196 attr.SetBulletNumber(bulletNumber);
7197 attr.SetLeftIndent(leftIndent, leftSubIndent);
7198
7199 return BeginStyle(attr);
7200 }
7201
7202 /// Begin symbol bullet
7203 bool wxRichTextBuffer::BeginSymbolBullet(const wxString& symbol, int leftIndent, int leftSubIndent, int bulletStyle)
7204 {
7205 wxRichTextAttr attr;
7206 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
7207 attr.SetBulletStyle(bulletStyle);
7208 attr.SetLeftIndent(leftIndent, leftSubIndent);
7209 attr.SetBulletText(symbol);
7210
7211 return BeginStyle(attr);
7212 }
7213
7214 /// Begin standard bullet
7215 bool wxRichTextBuffer::BeginStandardBullet(const wxString& bulletName, int leftIndent, int leftSubIndent, int bulletStyle)
7216 {
7217 wxRichTextAttr attr;
7218 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
7219 attr.SetBulletStyle(bulletStyle);
7220 attr.SetLeftIndent(leftIndent, leftSubIndent);
7221 attr.SetBulletName(bulletName);
7222
7223 return BeginStyle(attr);
7224 }
7225
7226 /// Begin named character style
7227 bool wxRichTextBuffer::BeginCharacterStyle(const wxString& characterStyle)
7228 {
7229 if (GetStyleSheet())
7230 {
7231 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
7232 if (def)
7233 {
7234 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
7235 return BeginStyle(attr);
7236 }
7237 }
7238 return false;
7239 }
7240
7241 /// Begin named paragraph style
7242 bool wxRichTextBuffer::BeginParagraphStyle(const wxString& paragraphStyle)
7243 {
7244 if (GetStyleSheet())
7245 {
7246 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(paragraphStyle);
7247 if (def)
7248 {
7249 wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
7250 return BeginStyle(attr);
7251 }
7252 }
7253 return false;
7254 }
7255
7256 /// Begin named list style
7257 bool wxRichTextBuffer::BeginListStyle(const wxString& listStyle, int level, int number)
7258 {
7259 if (GetStyleSheet())
7260 {
7261 wxRichTextListStyleDefinition* def = GetStyleSheet()->FindListStyle(listStyle);
7262 if (def)
7263 {
7264 wxRichTextAttr attr(def->GetCombinedStyleForLevel(level));
7265
7266 attr.SetBulletNumber(number);
7267
7268 return BeginStyle(attr);
7269 }
7270 }
7271 return false;
7272 }
7273
7274 /// Begin URL
7275 bool wxRichTextBuffer::BeginURL(const wxString& url, const wxString& characterStyle)
7276 {
7277 wxRichTextAttr attr;
7278
7279 if (!characterStyle.IsEmpty() && GetStyleSheet())
7280 {
7281 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
7282 if (def)
7283 {
7284 attr = def->GetStyleMergedWithBase(GetStyleSheet());
7285 }
7286 }
7287 attr.SetURL(url);
7288
7289 return BeginStyle(attr);
7290 }
7291
7292 /// Adds a handler to the end
7293 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler *handler)
7294 {
7295 sm_handlers.Append(handler);
7296 }
7297
7298 /// Inserts a handler at the front
7299 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler *handler)
7300 {
7301 sm_handlers.Insert( handler );
7302 }
7303
7304 /// Removes a handler
7305 bool wxRichTextBuffer::RemoveHandler(const wxString& name)
7306 {
7307 wxRichTextFileHandler *handler = FindHandler(name);
7308 if (handler)
7309 {
7310 sm_handlers.DeleteObject(handler);
7311 delete handler;
7312 return true;
7313 }
7314 else
7315 return false;
7316 }
7317
7318 /// Finds a handler by filename or, if supplied, type
7319 wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename,
7320 wxRichTextFileType imageType)
7321 {
7322 if (imageType != wxRICHTEXT_TYPE_ANY)
7323 return FindHandler(imageType);
7324 else if (!filename.IsEmpty())
7325 {
7326 wxString path, file, ext;
7327 wxFileName::SplitPath(filename, & path, & file, & ext);
7328 return FindHandler(ext, imageType);
7329 }
7330 else
7331 return NULL;
7332 }
7333
7334
7335 /// Finds a handler by name
7336 wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& name)
7337 {
7338 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7339 while (node)
7340 {
7341 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
7342 if (handler->GetName().Lower() == name.Lower()) return handler;
7343
7344 node = node->GetNext();
7345 }
7346 return NULL;
7347 }
7348
7349 /// Finds a handler by extension and type
7350 wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, wxRichTextFileType type)
7351 {
7352 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7353 while (node)
7354 {
7355 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
7356 if ( handler->GetExtension().Lower() == extension.Lower() &&
7357 (type == wxRICHTEXT_TYPE_ANY || handler->GetType() == type) )
7358 return handler;
7359 node = node->GetNext();
7360 }
7361 return 0;
7362 }
7363
7364 /// Finds a handler by type
7365 wxRichTextFileHandler* wxRichTextBuffer::FindHandler(wxRichTextFileType type)
7366 {
7367 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7368 while (node)
7369 {
7370 wxRichTextFileHandler *handler = (wxRichTextFileHandler *)node->GetData();
7371 if (handler->GetType() == type) return handler;
7372 node = node->GetNext();
7373 }
7374 return NULL;
7375 }
7376
7377 void wxRichTextBuffer::InitStandardHandlers()
7378 {
7379 if (!FindHandler(wxRICHTEXT_TYPE_TEXT))
7380 AddHandler(new wxRichTextPlainTextHandler);
7381 }
7382
7383 void wxRichTextBuffer::CleanUpHandlers()
7384 {
7385 wxList::compatibility_iterator node = sm_handlers.GetFirst();
7386 while (node)
7387 {
7388 wxRichTextFileHandler* handler = (wxRichTextFileHandler*)node->GetData();
7389 wxList::compatibility_iterator next = node->GetNext();
7390 delete handler;
7391 node = next;
7392 }
7393
7394 sm_handlers.Clear();
7395 }
7396
7397 wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* types)
7398 {
7399 if (types)
7400 types->Clear();
7401
7402 wxString wildcard;
7403
7404 wxList::compatibility_iterator node = GetHandlers().GetFirst();
7405 int count = 0;
7406 while (node)
7407 {
7408 wxRichTextFileHandler* handler = (wxRichTextFileHandler*) node->GetData();
7409 if (handler->IsVisible() && ((save && handler->CanSave()) || (!save && handler->CanLoad())))
7410 {
7411 if (combine)
7412 {
7413 if (count > 0)
7414 wildcard += wxT(";");
7415 wildcard += wxT("*.") + handler->GetExtension();
7416 }
7417 else
7418 {
7419 if (count > 0)
7420 wildcard += wxT("|");
7421 wildcard += handler->GetName();
7422 wildcard += wxT(" ");
7423 wildcard += _("files");
7424 wildcard += wxT(" (*.");
7425 wildcard += handler->GetExtension();
7426 wildcard += wxT(")|*.");
7427 wildcard += handler->GetExtension();
7428 if (types)
7429 types->Add(handler->GetType());
7430 }
7431 count ++;
7432 }
7433
7434 node = node->GetNext();
7435 }
7436
7437 if (combine)
7438 wildcard = wxT("(") + wildcard + wxT(")|") + wildcard;
7439 return wildcard;
7440 }
7441
7442 /// Load a file
7443 bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type)
7444 {
7445 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
7446 if (handler)
7447 {
7448 SetDefaultStyle(wxRichTextAttr());
7449 handler->SetFlags(GetHandlerFlags());
7450 bool success = handler->LoadFile(this, filename);
7451 Invalidate(wxRICHTEXT_ALL);
7452 return success;
7453 }
7454 else
7455 return false;
7456 }
7457
7458 /// Save a file
7459 bool wxRichTextBuffer::SaveFile(const wxString& filename, wxRichTextFileType type)
7460 {
7461 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
7462 if (handler)
7463 {
7464 handler->SetFlags(GetHandlerFlags());
7465 return handler->SaveFile(this, filename);
7466 }
7467 else
7468 return false;
7469 }
7470
7471 /// Load from a stream
7472 bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type)
7473 {
7474 wxRichTextFileHandler* handler = FindHandler(type);
7475 if (handler)
7476 {
7477 SetDefaultStyle(wxRichTextAttr());
7478 handler->SetFlags(GetHandlerFlags());
7479 bool success = handler->LoadFile(this, stream);
7480 Invalidate(wxRICHTEXT_ALL);
7481 return success;
7482 }
7483 else
7484 return false;
7485 }
7486
7487 /// Save to a stream
7488 bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, wxRichTextFileType type)
7489 {
7490 wxRichTextFileHandler* handler = FindHandler(type);
7491 if (handler)
7492 {
7493 handler->SetFlags(GetHandlerFlags());
7494 return handler->SaveFile(this, stream);
7495 }
7496 else
7497 return false;
7498 }
7499
7500 /// Copy the range to the clipboard
7501 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
7502 {
7503 bool success = false;
7504 wxRichTextParagraphLayoutBox* container = this;
7505 if (GetRichTextCtrl())
7506 container = GetRichTextCtrl()->GetFocusObject();
7507
7508 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7509
7510 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
7511 {
7512 wxTheClipboard->Clear();
7513
7514 // Add composite object
7515
7516 wxDataObjectComposite* compositeObject = new wxDataObjectComposite();
7517
7518 {
7519 wxString text = container->GetTextForRange(range);
7520
7521 #ifdef __WXMSW__
7522 text = wxTextFile::Translate(text, wxTextFileType_Dos);
7523 #endif
7524
7525 compositeObject->Add(new wxTextDataObject(text), false /* not preferred */);
7526 }
7527
7528 // Add rich text buffer data object. This needs the XML handler to be present.
7529
7530 if (FindHandler(wxRICHTEXT_TYPE_XML))
7531 {
7532 wxRichTextBuffer* richTextBuf = new wxRichTextBuffer;
7533 container->CopyFragment(range, *richTextBuf);
7534
7535 compositeObject->Add(new wxRichTextBufferDataObject(richTextBuf), true /* preferred */);
7536 }
7537
7538 if (wxTheClipboard->SetData(compositeObject))
7539 success = true;
7540
7541 wxTheClipboard->Close();
7542 }
7543
7544 #else
7545 wxUnusedVar(range);
7546 #endif
7547 return success;
7548 }
7549
7550 /// Paste the clipboard content to the buffer
7551 bool wxRichTextBuffer::PasteFromClipboard(long position)
7552 {
7553 bool success = false;
7554 wxRichTextParagraphLayoutBox* container = this;
7555 if (GetRichTextCtrl())
7556 container = GetRichTextCtrl()->GetFocusObject();
7557
7558 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7559 if (CanPasteFromClipboard())
7560 {
7561 if (wxTheClipboard->Open())
7562 {
7563 if (wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
7564 {
7565 wxRichTextBufferDataObject data;
7566 wxTheClipboard->GetData(data);
7567 wxRichTextBuffer* richTextBuffer = data.GetRichTextBuffer();
7568 if (richTextBuffer)
7569 {
7570 container->InsertParagraphsWithUndo(position+1, *richTextBuffer, GetRichTextCtrl(), this, 0);
7571 if (GetRichTextCtrl())
7572 GetRichTextCtrl()->ShowPosition(position + richTextBuffer->GetOwnRange().GetEnd());
7573 delete richTextBuffer;
7574 }
7575 }
7576 else if (wxTheClipboard->IsSupported(wxDF_TEXT)
7577 #if wxUSE_UNICODE
7578 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
7579 #endif
7580 )
7581 {
7582 wxTextDataObject data;
7583 wxTheClipboard->GetData(data);
7584 wxString text(data.GetText());
7585 #ifdef __WXMSW__
7586 wxString text2;
7587 text2.Alloc(text.Length()+1);
7588 size_t i;
7589 for (i = 0; i < text.Length(); i++)
7590 {
7591 wxChar ch = text[i];
7592 if (ch != wxT('\r'))
7593 text2 += ch;
7594 }
7595 #else
7596 wxString text2 = text;
7597 #endif
7598 container->InsertTextWithUndo(position+1, text2, GetRichTextCtrl(), this, wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
7599
7600 if (GetRichTextCtrl())
7601 GetRichTextCtrl()->ShowPosition(position + text2.Length());
7602
7603 success = true;
7604 }
7605 else if (wxTheClipboard->IsSupported(wxDF_BITMAP))
7606 {
7607 wxBitmapDataObject data;
7608 wxTheClipboard->GetData(data);
7609 wxBitmap bitmap(data.GetBitmap());
7610 wxImage image(bitmap.ConvertToImage());
7611
7612 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, container, GetRichTextCtrl(), false);
7613
7614 action->GetNewParagraphs().AddImage(image);
7615
7616 if (action->GetNewParagraphs().GetChildCount() == 1)
7617 action->GetNewParagraphs().SetPartialParagraph(true);
7618
7619 action->SetPosition(position+1);
7620
7621 // Set the range we'll need to delete in Undo
7622 action->SetRange(wxRichTextRange(position+1, position+1));
7623
7624 SubmitAction(action);
7625
7626 success = true;
7627 }
7628 wxTheClipboard->Close();
7629 }
7630 }
7631 #else
7632 wxUnusedVar(position);
7633 #endif
7634 return success;
7635 }
7636
7637 /// Can we paste from the clipboard?
7638 bool wxRichTextBuffer::CanPasteFromClipboard() const
7639 {
7640 bool canPaste = false;
7641 #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
7642 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
7643 {
7644 if (wxTheClipboard->IsSupported(wxDF_TEXT)
7645 #if wxUSE_UNICODE
7646 || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
7647 #endif
7648 || wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
7649 wxTheClipboard->IsSupported(wxDF_BITMAP))
7650 {
7651 canPaste = true;
7652 }
7653 wxTheClipboard->Close();
7654 }
7655 #endif
7656 return canPaste;
7657 }
7658
7659 /// Dumps contents of buffer for debugging purposes
7660 void wxRichTextBuffer::Dump()
7661 {
7662 wxString text;
7663 {
7664 wxStringOutputStream stream(& text);
7665 wxTextOutputStream textStream(stream);
7666 Dump(textStream);
7667 }
7668
7669 wxLogDebug(text);
7670 }
7671
7672 /// Add an event handler
7673 bool wxRichTextBuffer::AddEventHandler(wxEvtHandler* handler)
7674 {
7675 m_eventHandlers.Append(handler);
7676 return true;
7677 }
7678
7679 /// Remove an event handler
7680 bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler* handler, bool deleteHandler)
7681 {
7682 wxList::compatibility_iterator node = m_eventHandlers.Find(handler);
7683 if (node)
7684 {
7685 m_eventHandlers.Erase(node);
7686 if (deleteHandler)
7687 delete handler;
7688
7689 return true;
7690 }
7691 else
7692 return false;
7693 }
7694
7695 /// Clear event handlers
7696 void wxRichTextBuffer::ClearEventHandlers()
7697 {
7698 m_eventHandlers.Clear();
7699 }
7700
7701 /// Send event to event handlers. If sendToAll is true, will send to all event handlers,
7702 /// otherwise will stop at the first successful one.
7703 bool wxRichTextBuffer::SendEvent(wxEvent& event, bool sendToAll)
7704 {
7705 bool success = false;
7706 for (wxList::compatibility_iterator node = m_eventHandlers.GetFirst(); node; node = node->GetNext())
7707 {
7708 wxEvtHandler* handler = (wxEvtHandler*) node->GetData();
7709 if (handler->ProcessEvent(event))
7710 {
7711 success = true;
7712 if (!sendToAll)
7713 return true;
7714 }
7715 }
7716 return success;
7717 }
7718
7719 /// Set style sheet and notify of the change
7720 bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet* sheet)
7721 {
7722 wxRichTextStyleSheet* oldSheet = GetStyleSheet();
7723
7724 wxWindowID id = wxID_ANY;
7725 if (GetRichTextCtrl())
7726 id = GetRichTextCtrl()->GetId();
7727
7728 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING, id);
7729 event.SetEventObject(GetRichTextCtrl());
7730 event.SetContainer(GetRichTextCtrl()->GetFocusObject());
7731 event.SetOldStyleSheet(oldSheet);
7732 event.SetNewStyleSheet(sheet);
7733 event.Allow();
7734
7735 if (SendEvent(event) && !event.IsAllowed())
7736 {
7737 if (sheet != oldSheet)
7738 delete sheet;
7739
7740 return false;
7741 }
7742
7743 if (oldSheet && oldSheet != sheet)
7744 delete oldSheet;
7745
7746 SetStyleSheet(sheet);
7747
7748 event.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED);
7749 event.SetOldStyleSheet(NULL);
7750 event.Allow();
7751
7752 return SendEvent(event);
7753 }
7754
7755 /// Set renderer, deleting old one
7756 void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer)
7757 {
7758 if (sm_renderer)
7759 delete sm_renderer;
7760 sm_renderer = renderer;
7761 }
7762
7763 /// Hit-testing: returns a flag indicating hit test details, plus
7764 /// information about position
7765 int wxRichTextBuffer::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
7766 {
7767 int ret = wxRichTextParagraphLayoutBox::HitTest(dc, pt, textPosition, obj, contextObj, flags);
7768 if (ret != wxRICHTEXT_HITTEST_NONE)
7769 {
7770 return ret;
7771 }
7772 else
7773 {
7774 textPosition = m_ownRange.GetEnd()-1;
7775 *obj = this;
7776 *contextObj = this;
7777 return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
7778 }
7779 }
7780
7781 bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& bulletAttr, const wxRect& rect)
7782 {
7783 if (bulletAttr.GetTextColour().Ok())
7784 {
7785 wxCheckSetPen(dc, wxPen(bulletAttr.GetTextColour()));
7786 wxCheckSetBrush(dc, wxBrush(bulletAttr.GetTextColour()));
7787 }
7788 else
7789 {
7790 wxCheckSetPen(dc, *wxBLACK_PEN);
7791 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
7792 }
7793
7794 wxFont font;
7795 if (bulletAttr.HasFont())
7796 {
7797 font = paragraph->GetBuffer()->GetFontTable().FindFont(bulletAttr);
7798 }
7799 else
7800 font = (*wxNORMAL_FONT);
7801
7802 wxCheckSetFont(dc, font);
7803
7804 int charHeight = dc.GetCharHeight();
7805
7806 int bulletWidth = (int) (((float) charHeight) * wxRichTextBuffer::GetBulletProportion());
7807 int bulletHeight = bulletWidth;
7808
7809 int x = rect.x;
7810
7811 // Calculate the top position of the character (as opposed to the whole line height)
7812 int y = rect.y + (rect.height - charHeight);
7813
7814 // Calculate where the bullet should be positioned
7815 y = y + (charHeight+1)/2 - (bulletHeight+1)/2;
7816
7817 // The margin between a bullet and text.
7818 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
7819
7820 if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
7821 x = rect.x + rect.width - bulletWidth - margin;
7822 else if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
7823 x = x + (rect.width)/2 - bulletWidth/2;
7824
7825 if (bulletAttr.GetBulletName() == wxT("standard/square"))
7826 {
7827 dc.DrawRectangle(x, y, bulletWidth, bulletHeight);
7828 }
7829 else if (bulletAttr.GetBulletName() == wxT("standard/diamond"))
7830 {
7831 wxPoint pts[5];
7832 pts[0].x = x; pts[0].y = y + bulletHeight/2;
7833 pts[1].x = x + bulletWidth/2; pts[1].y = y;
7834 pts[2].x = x + bulletWidth; pts[2].y = y + bulletHeight/2;
7835 pts[3].x = x + bulletWidth/2; pts[3].y = y + bulletHeight;
7836
7837 dc.DrawPolygon(4, pts);
7838 }
7839 else if (bulletAttr.GetBulletName() == wxT("standard/triangle"))
7840 {
7841 wxPoint pts[3];
7842 pts[0].x = x; pts[0].y = y;
7843 pts[1].x = x + bulletWidth; pts[1].y = y + bulletHeight/2;
7844 pts[2].x = x; pts[2].y = y + bulletHeight;
7845
7846 dc.DrawPolygon(3, pts);
7847 }
7848 else if (bulletAttr.GetBulletName() == wxT("standard/circle-outline"))
7849 {
7850 wxCheckSetBrush(dc, *wxTRANSPARENT_BRUSH);
7851 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
7852 }
7853 else // "standard/circle", and catch-all
7854 {
7855 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
7856 }
7857
7858 return true;
7859 }
7860
7861 bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect, const wxString& text)
7862 {
7863 if (!text.empty())
7864 {
7865 wxFont font;
7866 if ((attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL) && !attr.GetBulletFont().IsEmpty() && attr.HasFont())
7867 {
7868 wxRichTextAttr fontAttr;
7869 fontAttr.SetFontSize(attr.GetFontSize());
7870 fontAttr.SetFontStyle(attr.GetFontStyle());
7871 fontAttr.SetFontWeight(attr.GetFontWeight());
7872 fontAttr.SetFontUnderlined(attr.GetFontUnderlined());
7873 fontAttr.SetFontFaceName(attr.GetBulletFont());
7874 font = paragraph->GetBuffer()->GetFontTable().FindFont(fontAttr);
7875 }
7876 else if (attr.HasFont())
7877 font = paragraph->GetBuffer()->GetFontTable().FindFont(attr);
7878 else
7879 font = (*wxNORMAL_FONT);
7880
7881 wxCheckSetFont(dc, font);
7882
7883 if (attr.GetTextColour().Ok())
7884 dc.SetTextForeground(attr.GetTextColour());
7885
7886 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
7887
7888 int charHeight = dc.GetCharHeight();
7889 wxCoord tw, th;
7890 dc.GetTextExtent(text, & tw, & th);
7891
7892 int x = rect.x;
7893
7894 // Calculate the top position of the character (as opposed to the whole line height)
7895 int y = rect.y + (rect.height - charHeight);
7896
7897 // The margin between a bullet and text.
7898 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
7899
7900 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
7901 x = (rect.x + rect.width) - tw - margin;
7902 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
7903 x = x + (rect.width)/2 - tw/2;
7904
7905 dc.DrawText(text, x, y);
7906
7907 return true;
7908 }
7909 else
7910 return false;
7911 }
7912
7913 bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& WXUNUSED(dc), const wxRichTextAttr& WXUNUSED(attr), const wxRect& WXUNUSED(rect))
7914 {
7915 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
7916 // with the buffer. The store will allow retrieval from memory, disk or other means.
7917 return false;
7918 }
7919
7920 /// Enumerate the standard bullet names currently supported
7921 bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString& bulletNames)
7922 {
7923 bulletNames.Add(wxTRANSLATE("standard/circle"));
7924 bulletNames.Add(wxTRANSLATE("standard/circle-outline"));
7925 bulletNames.Add(wxTRANSLATE("standard/square"));
7926 bulletNames.Add(wxTRANSLATE("standard/diamond"));
7927 bulletNames.Add(wxTRANSLATE("standard/triangle"));
7928
7929 return true;
7930 }
7931
7932 /*!
7933 * wxRichTextBox
7934 */
7935
7936 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextParagraphLayoutBox)
7937
7938 wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
7939 wxRichTextParagraphLayoutBox(parent)
7940 {
7941 }
7942
7943 /// Draw the item
7944 bool wxRichTextBox::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
7945 {
7946 if (!IsShown())
7947 return true;
7948
7949 // TODO: if the active object in the control, draw an indication.
7950 // We need to add the concept of active object, and not just focus object,
7951 // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
7952 // Ultimately we would like to be able to interactively resize an active object
7953 // using drag handles.
7954 return wxRichTextParagraphLayoutBox::Draw(dc, range, selection, rect, descent, style);
7955 }
7956
7957 /// Copy
7958 void wxRichTextBox::Copy(const wxRichTextBox& obj)
7959 {
7960 wxRichTextParagraphLayoutBox::Copy(obj);
7961 }
7962
7963 // Edit properties via a GUI
7964 bool wxRichTextBox::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
7965 {
7966 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Box Properties"));
7967 boxDlg.SetAttributes(GetAttributes());
7968
7969 if (boxDlg.ShowModal() == wxID_OK)
7970 {
7971 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
7972 // indeterminate in the object.
7973 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
7974 return true;
7975 }
7976 else
7977 return false;
7978 }
7979
7980 IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell, wxRichTextBox)
7981
7982 wxRichTextCell::wxRichTextCell(wxRichTextObject* parent):
7983 wxRichTextBox(parent)
7984 {
7985 }
7986
7987 /// Draw the item
7988 bool wxRichTextCell::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
7989 {
7990 return wxRichTextBox::Draw(dc, range, selection, rect, descent, style);
7991 }
7992
7993 /// Copy
7994 void wxRichTextCell::Copy(const wxRichTextCell& obj)
7995 {
7996 wxRichTextBox::Copy(obj);
7997 }
7998
7999 // Edit properties via a GUI
8000 bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
8001 {
8002 // We need to gather common attributes for all selected cells.
8003
8004 wxRichTextTable* table = wxDynamicCast(GetParent(), wxRichTextTable);
8005 bool multipleCells = false;
8006 wxRichTextAttr attr;
8007
8008 if (table && buffer && buffer->GetRichTextCtrl() && buffer->GetRichTextCtrl()->GetSelection().IsValid() &&
8009 buffer->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
8010 {
8011 wxRichTextAttr clashingAttr, absentAttr;
8012 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
8013 size_t i;
8014 int selectedCellCount = 0;
8015 for (i = 0; i < sel.GetCount(); i++)
8016 {
8017 const wxRichTextRange& range = sel[i];
8018 wxRichTextCell* cell = table->GetCell(range.GetStart());
8019 if (cell)
8020 {
8021 wxRichTextAttr cellStyle = cell->GetAttributes();
8022
8023 CollectStyle(attr, cellStyle, clashingAttr, absentAttr);
8024
8025 selectedCellCount ++;
8026 }
8027 }
8028 multipleCells = selectedCellCount > 1;
8029 }
8030 else
8031 {
8032 attr = GetAttributes();
8033 }
8034
8035 wxString caption;
8036 if (multipleCells)
8037 caption = _("Multiple Cell Properties");
8038 else
8039 caption = _("Cell Properties");
8040
8041 wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption);
8042 cellDlg.SetAttributes(attr);
8043
8044 wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(CLASSINFO(wxRichTextSizePage)), wxRichTextSizePage);
8045 if (sizePage)
8046 {
8047 // We don't want position and floating controls for a cell.
8048 sizePage->ShowPositionControls(false);
8049 sizePage->ShowFloatingControls(false);
8050 }
8051
8052 if (cellDlg.ShowModal() == wxID_OK)
8053 {
8054 if (multipleCells)
8055 {
8056 const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
8057 // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
8058 // since it may represent clashing attributes across multiple objects.
8059 table->SetCellStyle(sel, attr);
8060 }
8061 else
8062 // For a single object, indeterminate attributes set by the user should be reflected in the
8063 // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
8064 // the style directly instead of applying (which ignores indeterminate attributes,
8065 // leaving them as they were).
8066 cellDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
8067 return true;
8068 }
8069 else
8070 return false;
8071 }
8072
8073 WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray)
8074
8075 IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable, wxRichTextBox)
8076
8077 wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent)
8078 {
8079 m_rowCount = 0;
8080 m_colCount = 0;
8081 }
8082
8083 // Draws the object.
8084 bool wxRichTextTable::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
8085 {
8086 return wxRichTextBox::Draw(dc, range, selection, rect, descent, style);
8087 }
8088
8089 WX_DECLARE_OBJARRAY(wxRect, wxRichTextRectArray);
8090 WX_DEFINE_OBJARRAY(wxRichTextRectArray);
8091
8092 // Lays the object out. rect is the space available for layout. Often it will
8093 // be the specified overall space for this object, if trying to constrain
8094 // layout to a particular size, or it could be the total space available in the
8095 // parent. rect is the overall size, so we must subtract margins and padding.
8096 // to get the actual available space.
8097 bool wxRichTextTable::Layout(wxDC& dc, const wxRect& rect, int style)
8098 {
8099 SetPosition(rect.GetPosition());
8100
8101 // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
8102 // minimum size if within alloted size, then divide up remaining size
8103 // between rows/cols.
8104
8105 double scale = 1.0;
8106 wxRichTextBuffer* buffer = GetBuffer();
8107 if (buffer) scale = buffer->GetScale();
8108
8109 wxRect availableSpace = GetAvailableContentArea(dc, rect);
8110 wxTextAttrDimensionConverter converter(dc, scale, availableSpace.GetSize());
8111
8112 // If we have no fixed table size, and assuming we're not pushed for
8113 // space, then we don't have to try to stretch the table to fit the contents.
8114 bool stretchToFitTableWidth = false;
8115
8116 int tableWidth = rect.width;
8117 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8118 {
8119 tableWidth = converter.GetPixels(GetAttributes().GetTextBoxAttr().GetWidth());
8120
8121 // Fixed table width, so we do want to stretch columns out if necessary.
8122 stretchToFitTableWidth = true;
8123
8124 // Shouldn't be able to exceed the size passed to this function
8125 tableWidth = wxMin(rect.width, tableWidth);
8126 }
8127
8128 // Get internal padding
8129 int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
8130 if (GetAttributes().GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8131 paddingLeft = converter.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetLeft());
8132 if (GetAttributes().GetTextBoxAttr().GetPadding().GetRight().IsValid())
8133 paddingRight = converter.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetRight());
8134 if (GetAttributes().GetTextBoxAttr().GetPadding().GetTop().IsValid())
8135 paddingTop = converter.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetTop());
8136 if (GetAttributes().GetTextBoxAttr().GetPadding().GetLeft().IsValid())
8137 paddingBottom = converter.GetPixels(GetAttributes().GetTextBoxAttr().GetPadding().GetBottom());
8138
8139 // Assume that left and top padding are also used for inter-cell padding.
8140 int paddingX = paddingLeft;
8141 int paddingY = paddingTop;
8142
8143 int totalLeftMargin = 0, totalRightMargin = 0, totalTopMargin = 0, totalBottomMargin = 0;
8144 GetTotalMargin(dc, buffer, GetAttributes(), totalLeftMargin, totalRightMargin, totalTopMargin, totalBottomMargin);
8145
8146 // Internal table width - the area for content
8147 int internalTableWidth = tableWidth - totalLeftMargin - totalRightMargin;
8148
8149 int rowCount = m_cells.GetCount();
8150 if (m_colCount == 0 || rowCount == 0)
8151 {
8152 wxRect overallRect(rect.x, rect.y, totalLeftMargin + totalRightMargin, totalTopMargin + totalBottomMargin);
8153 SetCachedSize(overallRect.GetSize());
8154
8155 // Zero content size
8156 SetMinSize(overallRect.GetSize());
8157 SetMaxSize(GetMinSize());
8158 return true;
8159 }
8160
8161 // The final calculated widths
8162 wxArrayInt colWidths(m_colCount);
8163
8164 wxArrayInt absoluteColWidths(m_colCount);
8165 // wxArrayInt absoluteColWidthsSpanning(m_colCount);
8166 wxArrayInt percentageColWidths(m_colCount);
8167 // wxArrayInt percentageColWidthsSpanning(m_colCount);
8168 // These are only relevant when the first column contains spanning information.
8169 // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
8170 wxArrayInt maxColWidths(m_colCount);
8171 wxArrayInt minColWidths(m_colCount);
8172
8173 wxSize tableSize(tableWidth, 0);
8174
8175 int i, j, k;
8176
8177 for (i = 0; i < m_colCount; i++)
8178 {
8179 absoluteColWidths[i] = 0;
8180 // absoluteColWidthsSpanning[i] = 0;
8181 percentageColWidths[i] = -1;
8182 // percentageColWidthsSpanning[i] = -1;
8183 colWidths[i] = 0;
8184 maxColWidths[i] = 0;
8185 minColWidths[i] = 0;
8186 // columnSpans[i] = 1;
8187 }
8188
8189 // (0) Determine which cells are visible according to spans
8190 // 1 2 3 4 5
8191 // __________________
8192 // | | | | | 1
8193 // |------| |----|
8194 // |------| | | 2
8195 // |------| | | 3
8196 // |------------------|
8197 // |__________________| 4
8198
8199 // To calculate cell visibility:
8200 // First find all spanning cells. Build an array of span records with start x, y and end x, y.
8201 // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
8202 // that cell, hide the cell.
8203
8204 // We can also use this array to match the size of spanning cells to the grid. Or just do
8205 // this when we iterate through all cells.
8206
8207 // 0.1: add spanning cells to an array
8208 wxRichTextRectArray rectArray;
8209 for (j = 0; j < m_rowCount; j++)
8210 {
8211 for (i = 0; i < m_colCount; i++)
8212 {
8213 wxRichTextBox* cell = GetCell(j, i);
8214 int colSpan = 1, rowSpan = 1;
8215 if (cell->GetProperties().HasProperty(wxT("colspan")))
8216 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
8217 if (cell->GetProperties().HasProperty(wxT("rowspan")))
8218 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
8219 if (colSpan > 1 || rowSpan > 1)
8220 {
8221 rectArray.Add(wxRect(i, j, colSpan, rowSpan));
8222 }
8223 }
8224 }
8225 // 0.2: find which cells are subsumed by a spanning cell
8226 for (j = 0; j < m_rowCount; j++)
8227 {
8228 for (i = 0; i < m_colCount; i++)
8229 {
8230 wxRichTextBox* cell = GetCell(j, i);
8231 if (rectArray.GetCount() == 0)
8232 {
8233 cell->Show(true);
8234 }
8235 else
8236 {
8237 int colSpan = 1, rowSpan = 1;
8238 if (cell->GetProperties().HasProperty(wxT("colspan")))
8239 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
8240 if (cell->GetProperties().HasProperty(wxT("rowspan")))
8241 rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
8242 if (colSpan > 1 || rowSpan > 1)
8243 {
8244 // Assume all spanning cells are shown
8245 cell->Show(true);
8246 }
8247 else
8248 {
8249 bool shown = true;
8250 for (k = 0; k < (int) rectArray.GetCount(); k++)
8251 {
8252 if (rectArray[k].Contains(wxPoint(i, j)))
8253 {
8254 shown = false;
8255 break;
8256 }
8257 }
8258 cell->Show(shown);
8259 }
8260 }
8261 }
8262 }
8263
8264 // TODO: find the first spanned cell in each row that spans the most columns and doesn't
8265 // overlap with a spanned cell starting at a previous column position.
8266 // This means we need to keep an array of rects so we can check. However
8267 // it does also mean that some spans simply may not be taken into account
8268 // where there are different spans happening on different rows. In these cases,
8269 // they will simply be as wide as their constituent columns.
8270
8271 // (1) Do an initial layout for all cells to get minimum and maximum size, and get
8272 // the absolute or percentage width of each column.
8273
8274 for (j = 0; j < m_rowCount; j++)
8275 {
8276 // First get the overall margins so we can calculate percentage widths based on
8277 // the available content space for all cells on the row
8278
8279 int overallRowContentMargin = 0;
8280 int visibleCellCount = 0;
8281
8282 for (i = 0; i < m_colCount; i++)
8283 {
8284 wxRichTextBox* cell = GetCell(j, i);
8285 if (cell->IsShown())
8286 {
8287 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
8288 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
8289
8290 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
8291 visibleCellCount ++;
8292 }
8293 }
8294
8295 // Add in inter-cell padding
8296 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
8297
8298 int rowContentWidth = internalTableWidth - overallRowContentMargin;
8299 wxSize rowTableSize(rowContentWidth, 0);
8300 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
8301
8302 for (i = 0; i < m_colCount; i++)
8303 {
8304 wxRichTextBox* cell = GetCell(j, i);
8305 if (cell->IsShown())
8306 {
8307 int colSpan = 1;
8308 if (cell->GetProperties().HasProperty(wxT("colspan")))
8309 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
8310
8311 // Lay out cell to find min/max widths
8312 cell->Invalidate(wxRICHTEXT_ALL);
8313 cell->Layout(dc, availableSpace, style);
8314
8315 if (colSpan == 1)
8316 {
8317 int absoluteCellWidth = -1;
8318 int percentageCellWidth = -1;
8319
8320 // I think we need to calculate percentages from the internal table size,
8321 // minus the padding between cells which we'll need to calculate from the
8322 // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
8323 // will add up to 100%. In CSS, the width specifies the cell's content rect width,
8324 // so if we want to conform to that we'll need to add in the overall cell margins.
8325 // However, this will make it difficult to specify percentages that add up to
8326 // 100% and still fit within the table width.
8327 // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
8328 // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
8329 // If we're using internal content size for the width, we would calculate the
8330 // the overall cell width for n cells as:
8331 // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
8332 // + thisOverallCellMargin
8333 // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
8334 // Adding this back, we get 240 + 240 + 20 = 500 pixels.
8335
8336 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8337 {
8338 int w = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
8339 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
8340 {
8341 percentageCellWidth = w;
8342 }
8343 else
8344 {
8345 absoluteCellWidth = w;
8346 }
8347 // Override absolute width with minimum width if necessary
8348 if (cell->GetMinSize().x > 0 && absoluteCellWidth !=1 && cell->GetMinSize().x > absoluteCellWidth)
8349 absoluteCellWidth = cell->GetMinSize().x;
8350 }
8351
8352 if (absoluteCellWidth != -1)
8353 {
8354 if (absoluteCellWidth > absoluteColWidths[i])
8355 absoluteColWidths[i] = absoluteCellWidth;
8356 }
8357
8358 if (percentageCellWidth != -1)
8359 {
8360 if (percentageCellWidth > percentageColWidths[i])
8361 percentageColWidths[i] = percentageCellWidth;
8362 }
8363
8364 if (colSpan == 1 && cell->GetMinSize().x && cell->GetMinSize().x > minColWidths[i])
8365 minColWidths[i] = cell->GetMinSize().x;
8366 if (colSpan == 1 && cell->GetMaxSize().x && cell->GetMaxSize().x > maxColWidths[i])
8367 maxColWidths[i] = cell->GetMaxSize().x;
8368 }
8369 }
8370 }
8371 }
8372
8373 // (2) Allocate initial column widths from minimum widths, absolute values and proportions
8374 // TODO: simply merge this into (1).
8375 for (i = 0; i < m_colCount; i++)
8376 {
8377 if (absoluteColWidths[i] > 0)
8378 {
8379 colWidths[i] = absoluteColWidths[i];
8380 }
8381 else if (percentageColWidths[i] > 0)
8382 {
8383 colWidths[i] = percentageColWidths[i];
8384
8385 // This is rubbish - we calculated the absolute widths from percentages, so
8386 // we can't do it again here.
8387 //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
8388 }
8389 }
8390
8391 // (3) Process absolute or proportional widths of spanning columns,
8392 // now that we know what our fixed column widths are going to be.
8393 // Spanned cells will try to adjust columns so the span will fit.
8394 // Even existing fixed column widths can be expanded if necessary.
8395 // Actually, currently fixed columns widths aren't adjusted; instead,
8396 // the algorithm favours earlier rows and adjusts unspecified column widths
8397 // the first time only. After that, we can't know whether the column has been
8398 // specified explicitly or not. (We could make a note if necessary.)
8399 for (j = 0; j < m_rowCount; j++)
8400 {
8401 // First get the overall margins so we can calculate percentage widths based on
8402 // the available content space for all cells on the row
8403
8404 int overallRowContentMargin = 0;
8405 int visibleCellCount = 0;
8406
8407 for (i = 0; i < m_colCount; i++)
8408 {
8409 wxRichTextBox* cell = GetCell(j, i);
8410 if (cell->IsShown())
8411 {
8412 int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
8413 GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
8414
8415 overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
8416 visibleCellCount ++;
8417 }
8418 }
8419
8420 // Add in inter-cell padding
8421 overallRowContentMargin += ((visibleCellCount-1) * paddingX);
8422
8423 int rowContentWidth = internalTableWidth - overallRowContentMargin;
8424 wxSize rowTableSize(rowContentWidth, 0);
8425 wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
8426
8427 for (i = 0; i < m_colCount; i++)
8428 {
8429 wxRichTextBox* cell = GetCell(j, i);
8430 if (cell->IsShown())
8431 {
8432 int colSpan = 1;
8433 if (cell->GetProperties().HasProperty(wxT("colspan")))
8434 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
8435
8436 if (colSpan > 1)
8437 {
8438 int spans = wxMin(colSpan, m_colCount - i);
8439 int cellWidth = 0;
8440 if (spans > 0)
8441 {
8442 if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
8443 {
8444 cellWidth = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
8445 // Override absolute width with minimum width if necessary
8446 if (cell->GetMinSize().x > 0 && cellWidth !=1 && cell->GetMinSize().x > cellWidth)
8447 cellWidth = cell->GetMinSize().x;
8448 }
8449 else
8450 {
8451 // Do we want to do this? It's the only chance we get to
8452 // use the cell's min/max sizes, so we need to work out
8453 // how we're going to balance the unspecified spanning cell
8454 // width with the possibility more-constrained constituent cell widths.
8455 // Say there's a tiny bitmap giving it a max width of 10 pixels. We
8456 // don't want to constraint all the spanned columns to fit into this cell.
8457 // OK, let's say that if any of the constituent columns don't fit,
8458 // then we simply stop constraining the columns; instead, we'll just fit the spanning
8459 // cells to the columns later.
8460 cellWidth = cell->GetMinSize().x;
8461 if (cell->GetMaxSize().x > cellWidth)
8462 cellWidth = cell->GetMaxSize().x;
8463 }
8464
8465 // Subtract the padding between cells
8466 int spanningWidth = cellWidth;
8467 spanningWidth -= paddingX * (spans-1);
8468
8469 if (spanningWidth > 0)
8470 {
8471 // Now share the spanning width between columns within that span
8472 // TODO: take into account min widths of columns within the span
8473 int spanningWidthLeft = spanningWidth;
8474 int stretchColCount = 0;
8475 for (k = i; k < (i+spans); k++)
8476 {
8477 if (colWidths[k] > 0) // absolute or proportional width has been specified
8478 spanningWidthLeft -= colWidths[k];
8479 else
8480 stretchColCount ++;
8481 }
8482 // Now divide what's left between the remaining columns
8483 int colShare = 0;
8484 if (stretchColCount > 0)
8485 colShare = spanningWidthLeft / stretchColCount;
8486 int colShareRemainder = spanningWidthLeft - (colShare * stretchColCount);
8487
8488 // If fixed-width columns are currently too big, then we'll later
8489 // stretch the spanned cell to fit.
8490
8491 if (spanningWidthLeft > 0)
8492 {
8493 for (k = i; k < (i+spans); k++)
8494 {
8495 if (colWidths[k] <= 0) // absolute or proportional width has not been specified
8496 {
8497 int newWidth = colShare;
8498 if (k == (i+spans-1))
8499 newWidth += colShareRemainder; // ensure all pixels are filled
8500 colWidths[k] = newWidth;
8501 }
8502 }
8503 }
8504 }
8505 }
8506 }
8507 }
8508 }
8509 }
8510
8511 // (4) Next, share any remaining space out between columns that have not yet been calculated.
8512 // TODO: take into account min widths of columns within the span
8513 int tableWidthMinusPadding = internalTableWidth - (m_colCount-1)*paddingX;
8514 int widthLeft = tableWidthMinusPadding;
8515 int stretchColCount = 0;
8516 for (i = 0; i < m_colCount; i++)
8517 {
8518 // TODO: we need to take into account min widths.
8519 // Subtract min width from width left, then
8520 // add the colShare to the min width
8521 if (colWidths[i] > 0) // absolute or proportional width has been specified
8522 widthLeft -= colWidths[i];
8523 else
8524 {
8525 if (minColWidths[i] > 0)
8526 widthLeft -= minColWidths[i];
8527
8528 stretchColCount ++;
8529 }
8530 }
8531
8532 // Now divide what's left between the remaining columns
8533 int colShare = 0;
8534 if (stretchColCount > 0)
8535 colShare = widthLeft / stretchColCount;
8536 int colShareRemainder = widthLeft - (colShare * stretchColCount);
8537
8538 // Check we don't have enough space, in which case shrink all columns, overriding
8539 // any absolute/proportional widths
8540 // TODO: actually we would like to divide up the shrinkage according to size.
8541 // How do we calculate the proportions that will achieve this?
8542 // Could first choose an arbitrary value for stretching cells, and then calculate
8543 // factors to multiply each width by.
8544 // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
8545 if (widthLeft < 0 || (stretchToFitTableWidth && (stretchColCount == 0)))
8546 {
8547 colShare = tableWidthMinusPadding / m_colCount;
8548 colShareRemainder = tableWidthMinusPadding - (colShare * m_colCount);
8549 for (i = 0; i < m_colCount; i++)
8550 {
8551 colWidths[i] = 0;
8552 minColWidths[i] = 0;
8553 }
8554 }
8555
8556 // We have to adjust the columns if either we need to shrink the
8557 // table to fit the parent/table width, or we explicitly set the
8558 // table width and need to stretch out the table.
8559 if (widthLeft < 0 || stretchToFitTableWidth)
8560 {
8561 for (i = 0; i < m_colCount; i++)
8562 {
8563 if (colWidths[i] <= 0) // absolute or proportional width has not been specified
8564 {
8565 if (minColWidths[i] > 0)
8566 colWidths[i] = minColWidths[i] + colShare;
8567 else
8568 colWidths[i] = colShare;
8569 if (i == (m_colCount-1))
8570 colWidths[i] += colShareRemainder; // ensure all pixels are filled
8571 }
8572 }
8573 }
8574
8575 // TODO: if spanned cells have no specified or max width, make them the
8576 // as big as the columns they span. Do this for all spanned cells in all
8577 // rows, of course. Size any spanned cells left over at the end - even if they
8578 // have width > 0, make sure they're limited to the appropriate column edge.
8579
8580
8581 /*
8582 Sort out confusion between content width
8583 and overall width later. For now, assume we specify overall width.
8584
8585 So, now we've laid out the table to fit into the given space
8586 and have used specified widths and minimum widths.
8587
8588 Now we need to consider how we will try to take maximum width into account.
8589
8590 */
8591
8592 // (??) TODO: take max width into account
8593
8594 // (6) Lay out all cells again with the current values
8595
8596 int maxRight = 0;
8597 int y = availableSpace.y;
8598 for (j = 0; j < m_rowCount; j++)
8599 {
8600 int x = availableSpace.x; // TODO: take into account centering etc.
8601 int maxCellHeight = 0;
8602 int maxSpecifiedCellHeight = 0;
8603
8604 wxArrayInt actualWidths(m_colCount);
8605
8606 wxTextAttrDimensionConverter converter(dc, scale);
8607 for (i = 0; i < m_colCount; i++)
8608 {
8609 wxRichTextCell* cell = GetCell(j, i);
8610 if (cell->IsShown())
8611 {
8612 // Get max specified cell height
8613 // Don't handle percentages for height
8614 if (cell->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE)
8615 {
8616 int h = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetHeight());
8617 if (h > maxSpecifiedCellHeight)
8618 maxSpecifiedCellHeight = h;
8619 }
8620
8621 if (colWidths[i] > 0) // absolute or proportional width has been specified
8622 {
8623 int colSpan = 1;
8624 if (cell->GetProperties().HasProperty(wxT("colspan")))
8625 colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
8626
8627 wxRect availableCellSpace;
8628
8629 // TODO: take into acount spans
8630 if (colSpan > 1)
8631 {
8632 // Calculate the size of this spanning cell from its constituent columns
8633 int xx = x;
8634 int spans = wxMin(colSpan, m_colCount - i);
8635 for (k = i; k < spans; k++)
8636 {
8637 if (k != i)
8638 xx += paddingX;
8639 xx += colWidths[k];
8640 }
8641 availableCellSpace = wxRect(x, y, xx, -1);
8642 }
8643 else
8644 availableCellSpace = wxRect(x, y, colWidths[i], -1);
8645
8646 // Store actual width so we can force cell to be the appropriate width on the final loop
8647 actualWidths[i] = availableCellSpace.GetWidth();
8648
8649 // Lay out cell
8650 cell->Invalidate(wxRICHTEXT_ALL);
8651 cell->Layout(dc, availableCellSpace, style);
8652
8653 // TODO: use GetCachedSize().x to compute 'natural' size
8654
8655 x += (availableCellSpace.GetWidth() + paddingX);
8656 if (cell->GetCachedSize().y > maxCellHeight)
8657 maxCellHeight = cell->GetCachedSize().y;
8658 }
8659 }
8660 }
8661
8662 maxCellHeight = wxMax(maxCellHeight, maxSpecifiedCellHeight);
8663
8664 for (i = 0; i < m_colCount; i++)
8665 {
8666 wxRichTextCell* cell = GetCell(j, i);
8667 if (cell->IsShown())
8668 {
8669 wxRect availableCellSpace = wxRect(cell->GetPosition(), wxSize(actualWidths[i], maxCellHeight));
8670 // Lay out cell with new height
8671 cell->Invalidate(wxRICHTEXT_ALL);
8672 cell->Layout(dc, availableCellSpace, style);
8673
8674 // Make sure the cell size really is the appropriate size,
8675 // not the calculated box size
8676 cell->SetCachedSize(wxSize(actualWidths[i], maxCellHeight));
8677
8678 maxRight = wxMax(maxRight, cell->GetPosition().x + cell->GetCachedSize().x);
8679 }
8680 }
8681
8682 y += maxCellHeight;
8683 if (j < (m_rowCount-1))
8684 y += paddingY;
8685 }
8686
8687 // We need to add back the margins etc.
8688 {
8689 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
8690 contentRect = wxRect(wxPoint(0, 0), wxSize(maxRight - availableSpace.x, y - availableSpace.y));
8691 GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
8692 SetCachedSize(marginRect.GetSize());
8693 }
8694
8695 // TODO: calculate max size
8696 {
8697 SetMaxSize(GetCachedSize());
8698 }
8699
8700 // TODO: calculate min size
8701 {
8702 SetMinSize(GetCachedSize());
8703 }
8704
8705 // TODO: currently we use either a fixed table width or the parent's size.
8706 // We also want to be able to calculate the table width from its content,
8707 // whether using fixed column widths or cell content min/max width.
8708 // Probably need a boolean flag to say whether we need to stretch cells
8709 // to fit the table width, or to simply use min/max cell widths. The
8710 // trouble with this is that if cell widths are not specified, they
8711 // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
8712 // Anyway, ignoring that problem, we probably need to factor layout into a function
8713 // that can can calculate the maximum unconstrained layout in case table size is
8714 // not specified. Then LayoutToBestSize() can choose to use either parent size to
8715 // constrain Layout(), or the previously-calculated max size to constraint layout.
8716
8717 return true;
8718 }
8719
8720 // Finds the absolute position and row height for the given character position
8721 bool wxRichTextTable::FindPosition(wxDC& dc, long index, wxPoint& pt, int* height, bool forceLineStart)
8722 {
8723 wxRichTextCell* child = GetCell(index+1);
8724 if (child)
8725 {
8726 // Find the position at the start of the child cell, since the table doesn't
8727 // have any caret position of its own.
8728 return child->FindPosition(dc, -1, pt, height, forceLineStart);
8729 }
8730 else
8731 return false;
8732 }
8733
8734 // Get the cell at the given character position (in the range of the table).
8735 wxRichTextCell* wxRichTextTable::GetCell(long pos) const
8736 {
8737 int row = 0, col = 0;
8738 if (GetCellRowColumnPosition(pos, row, col))
8739 {
8740 return GetCell(row, col);
8741 }
8742 else
8743 return NULL;
8744 }
8745
8746 // Get the row/column for a given character position
8747 bool wxRichTextTable::GetCellRowColumnPosition(long pos, int& row, int& col) const
8748 {
8749 if (m_colCount == 0 || m_rowCount == 0)
8750 return false;
8751
8752 row = (int) (pos / m_colCount);
8753 col = pos - (row * m_colCount);
8754
8755 wxASSERT(row < m_rowCount && col < m_colCount);
8756
8757 if (row < m_rowCount && col < m_colCount)
8758 return true;
8759 else
8760 return false;
8761 }
8762
8763 // Calculate range, taking row/cell ordering into account instead of relying
8764 // on list ordering.
8765 void wxRichTextTable::CalculateRange(long start, long& end)
8766 {
8767 long current = start;
8768 long lastEnd = current;
8769
8770 if (IsTopLevel())
8771 {
8772 current = 0;
8773 lastEnd = 0;
8774 }
8775
8776 int i, j;
8777 for (i = 0; i < m_rowCount; i++)
8778 {
8779 for (j = 0; j < m_colCount; j++)
8780 {
8781 wxRichTextCell* child = GetCell(i, j);
8782 if (child)
8783 {
8784 long childEnd = 0;
8785
8786 child->CalculateRange(current, childEnd);
8787
8788 lastEnd = childEnd;
8789 current = childEnd + 1;
8790 }
8791 }
8792 }
8793
8794 // A top-level object always has a range of size 1,
8795 // because its children don't count at this level.
8796 end = start;
8797 m_range.SetRange(start, start);
8798
8799 // An object with no children has zero length
8800 if (m_children.GetCount() == 0)
8801 lastEnd --;
8802 m_ownRange.SetRange(0, lastEnd);
8803 }
8804
8805 // Gets the range size.
8806 bool wxRichTextTable::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position, wxArrayInt* partialExtents) const
8807 {
8808 return wxRichTextBox::GetRangeSize(range, size, descent, dc, flags, position, partialExtents);
8809 }
8810
8811 // Deletes content in the given range.
8812 bool wxRichTextTable::DeleteRange(const wxRichTextRange& WXUNUSED(range))
8813 {
8814 // TODO: implement deletion of cells
8815 return true;
8816 }
8817
8818 // Gets any text in this object for the given range.
8819 wxString wxRichTextTable::GetTextForRange(const wxRichTextRange& range) const
8820 {
8821 return wxRichTextBox::GetTextForRange(range);
8822 }
8823
8824 // Copies this object.
8825 void wxRichTextTable::Copy(const wxRichTextTable& obj)
8826 {
8827 wxRichTextBox::Copy(obj);
8828
8829 ClearTable();
8830
8831 m_rowCount = obj.m_rowCount;
8832 m_colCount = obj.m_colCount;
8833
8834 m_cells.Add(wxRichTextObjectPtrArray(), m_rowCount);
8835
8836 int i, j;
8837 for (i = 0; i < m_rowCount; i++)
8838 {
8839 wxRichTextObjectPtrArray& colArray = m_cells[i];
8840 for (j = 0; j < m_colCount; j++)
8841 {
8842 wxRichTextCell* cell = wxDynamicCast(obj.GetCell(i, j)->Clone(), wxRichTextCell);
8843 AppendChild(cell);
8844
8845 colArray.Add(cell);
8846 }
8847 }
8848 }
8849
8850 void wxRichTextTable::ClearTable()
8851 {
8852 m_cells.Clear();
8853 DeleteChildren();
8854 }
8855
8856 bool wxRichTextTable::CreateTable(int rows, int cols)
8857 {
8858 ClearTable();
8859
8860 m_rowCount = rows;
8861 m_colCount = cols;
8862
8863 m_cells.Add(wxRichTextObjectPtrArray(), rows);
8864
8865 int i, j;
8866 for (i = 0; i < rows; i++)
8867 {
8868 wxRichTextObjectPtrArray& colArray = m_cells[i];
8869 for (j = 0; j < cols; j++)
8870 {
8871 wxRichTextCell* cell = new wxRichTextCell;
8872 AppendChild(cell);
8873 cell->AddParagraph(wxEmptyString);
8874
8875 colArray.Add(cell);
8876 }
8877 }
8878
8879 return true;
8880 }
8881
8882 wxRichTextCell* wxRichTextTable::GetCell(int row, int col) const
8883 {
8884 wxASSERT(row < m_rowCount);
8885 wxASSERT(col < m_colCount);
8886
8887 if (row < m_rowCount && col < m_colCount)
8888 {
8889 wxRichTextObjectPtrArray& colArray = m_cells[row];
8890 wxRichTextObject* obj = colArray[col];
8891 return wxDynamicCast(obj, wxRichTextCell);
8892 }
8893 else
8894 return NULL;
8895 }
8896
8897 // Returns a selection object specifying the selections between start and end character positions.
8898 // For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
8899 wxRichTextSelection wxRichTextTable::GetSelection(long start, long end) const
8900 {
8901 wxRichTextSelection selection;
8902 selection.SetContainer((wxRichTextTable*) this);
8903
8904 if (start > end)
8905 {
8906 long tmp = end;
8907 end = start;
8908 start = tmp;
8909 }
8910
8911 wxASSERT( start >= 0 && end < (m_colCount * m_rowCount));
8912
8913 if (end >= (m_colCount * m_rowCount))
8914 return selection;
8915
8916 // We need to find the rectangle of cells that is described by the rectangle
8917 // with start, end as the diagonal. Make sure we don't add cells that are
8918 // not currenty visible because they are overlapped by spanning cells.
8919 /*
8920 --------------------------
8921 | 0 | 1 | 2 | 3 | 4 |
8922 --------------------------
8923 | 5 | 6 | 7 | 8 | 9 |
8924 --------------------------
8925 | 10 | 11 | 12 | 13 | 14 |
8926 --------------------------
8927 | 15 | 16 | 17 | 18 | 19 |
8928 --------------------------
8929
8930 Let's say we select 6 -> 18.
8931
8932 Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
8933 which is left and which is right.
8934
8935 Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
8936
8937 Now go through rows from 1 to 3 and only add cells that are (a) within above column range
8938 and (b) shown.
8939
8940
8941 */
8942
8943 int leftCol = start - m_colCount * int(start/m_colCount);
8944 int rightCol = end - m_colCount * int(end/m_colCount);
8945
8946 int topRow = int(start/m_colCount);
8947 int bottomRow = int(end/m_colCount);
8948
8949 if (leftCol > rightCol)
8950 {
8951 int tmp = rightCol;
8952 rightCol = leftCol;
8953 leftCol = tmp;
8954 }
8955
8956 if (topRow > bottomRow)
8957 {
8958 int tmp = bottomRow;
8959 bottomRow = topRow;
8960 topRow = tmp;
8961 }
8962
8963 int i, j;
8964 for (i = topRow; i <= bottomRow; i++)
8965 {
8966 for (j = leftCol; j <= rightCol; j++)
8967 {
8968 wxRichTextCell* cell = GetCell(i, j);
8969 if (cell && cell->IsShown())
8970 selection.Add(cell->GetRange());
8971 }
8972 }
8973
8974 return selection;
8975 }
8976
8977 // Sets the attributes for the cells specified by the selection.
8978 bool wxRichTextTable::SetCellStyle(const wxRichTextSelection& selection, const wxRichTextAttr& style, int flags)
8979 {
8980 if (selection.GetContainer() != this)
8981 return false;
8982
8983 wxRichTextBuffer* buffer = GetBuffer();
8984 bool haveControl = (buffer && buffer->GetRichTextCtrl() != NULL);
8985 bool withUndo = haveControl && ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
8986
8987 if (withUndo)
8988 buffer->BeginBatchUndo(_("Set Cell Style"));
8989
8990 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
8991 while (node)
8992 {
8993 wxRichTextCell* cell = wxDynamicCast(node->GetData(), wxRichTextCell);
8994 if (cell && selection.WithinSelection(cell->GetRange().GetStart()))
8995 SetStyle(cell, style, flags);
8996 node = node->GetNext();
8997 }
8998
8999 // Do action, or delay it until end of batch.
9000 if (withUndo)
9001 buffer->EndBatchUndo();
9002
9003 return true;
9004 }
9005
9006 bool wxRichTextTable::DeleteRows(int startRow, int noRows)
9007 {
9008 wxASSERT((startRow + noRows) < m_rowCount);
9009 if ((startRow + noRows) >= m_rowCount)
9010 return false;
9011
9012 int i, j;
9013 for (i = startRow; i < (startRow+noRows); i++)
9014 {
9015 wxRichTextObjectPtrArray& colArray = m_cells[startRow];
9016 for (j = 0; j < (int) colArray.GetCount(); j++)
9017 {
9018 wxRichTextObject* cell = colArray[j];
9019 RemoveChild(cell, true);
9020 }
9021
9022 // Keep deleting at the same position, since we move all
9023 // the others up
9024 m_cells.RemoveAt(startRow);
9025 }
9026
9027 m_rowCount = m_rowCount - noRows;
9028
9029 return true;
9030 }
9031
9032 bool wxRichTextTable::DeleteColumns(int startCol, int noCols)
9033 {
9034 wxASSERT((startCol + noCols) < m_colCount);
9035 if ((startCol + noCols) >= m_colCount)
9036 return false;
9037
9038 bool deleteRows = (noCols == m_colCount);
9039
9040 int i, j;
9041 for (i = 0; i < m_rowCount; i++)
9042 {
9043 wxRichTextObjectPtrArray& colArray = m_cells[deleteRows ? 0 : i];
9044 for (j = startCol; j < (startCol+noCols); j++)
9045 {
9046 wxRichTextObject* cell = colArray[j];
9047 RemoveChild(cell, true);
9048 }
9049
9050 if (deleteRows)
9051 m_cells.RemoveAt(0);
9052 }
9053
9054 if (deleteRows)
9055 m_rowCount = 0;
9056 m_colCount = m_colCount - noCols;
9057
9058 return true;
9059 }
9060
9061 bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& attr)
9062 {
9063 wxASSERT(startRow <= m_rowCount);
9064 if (startRow > m_rowCount)
9065 return false;
9066
9067 int i, j;
9068 for (i = 0; i < noRows; i++)
9069 {
9070 int idx;
9071 if (startRow == m_rowCount)
9072 {
9073 m_cells.Add(wxRichTextObjectPtrArray());
9074 idx = m_cells.GetCount() - 1;
9075 }
9076 else
9077 {
9078 m_cells.Insert(wxRichTextObjectPtrArray(), startRow+i);
9079 idx = startRow+i;
9080 }
9081
9082 wxRichTextObjectPtrArray& colArray = m_cells[idx];
9083 for (j = 0; j < m_colCount; j++)
9084 {
9085 wxRichTextCell* cell = new wxRichTextCell;
9086 cell->GetAttributes() = attr;
9087
9088 AppendChild(cell);
9089 colArray.Add(cell);
9090 }
9091 }
9092
9093 m_rowCount = m_rowCount + noRows;
9094 return true;
9095 }
9096
9097 bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& attr)
9098 {
9099 wxASSERT(startCol <= m_colCount);
9100 if (startCol > m_colCount)
9101 return false;
9102
9103 int i, j;
9104 for (i = 0; i < m_rowCount; i++)
9105 {
9106 wxRichTextObjectPtrArray& colArray = m_cells[i];
9107 for (j = 0; j < noCols; j++)
9108 {
9109 wxRichTextCell* cell = new wxRichTextCell;
9110 cell->GetAttributes() = attr;
9111
9112 AppendChild(cell);
9113
9114 if (startCol == m_colCount)
9115 colArray.Add(cell);
9116 else
9117 colArray.Insert(cell, startCol+j);
9118 }
9119 }
9120
9121 m_colCount = m_colCount + noCols;
9122
9123 return true;
9124 }
9125
9126 // Edit properties via a GUI
9127 bool wxRichTextTable::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
9128 {
9129 wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Table Properties"));
9130 boxDlg.SetAttributes(GetAttributes());
9131
9132 if (boxDlg.ShowModal() == wxID_OK)
9133 {
9134 boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
9135 return true;
9136 }
9137 else
9138 return false;
9139 }
9140
9141 /*
9142 * Module to initialise and clean up handlers
9143 */
9144
9145 class wxRichTextModule: public wxModule
9146 {
9147 DECLARE_DYNAMIC_CLASS(wxRichTextModule)
9148 public:
9149 wxRichTextModule() {}
9150 bool OnInit()
9151 {
9152 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer);
9153 wxRichTextBuffer::InitStandardHandlers();
9154 wxRichTextParagraph::InitDefaultTabs();
9155 return true;
9156 }
9157 void OnExit()
9158 {
9159 wxRichTextBuffer::CleanUpHandlers();
9160 wxRichTextDecimalToRoman(-1);
9161 wxRichTextParagraph::ClearDefaultTabs();
9162 wxRichTextCtrl::ClearAvailableFontNames();
9163 wxRichTextBuffer::SetRenderer(NULL);
9164 }
9165 };
9166
9167 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule)
9168
9169
9170 // If the richtext lib is dynamically loaded after the app has already started
9171 // (such as from wxPython) then the built-in module system will not init this
9172 // module. Provide this function to do it manually.
9173 void wxRichTextModuleInit()
9174 {
9175 wxModule* module = new wxRichTextModule;
9176 module->Init();
9177 wxModule::RegisterModule(module);
9178 }
9179
9180
9181 /*!
9182 * Commands for undo/redo
9183 *
9184 */
9185
9186 wxRichTextCommand::wxRichTextCommand(const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
9187 wxRichTextParagraphLayoutBox* container, wxRichTextCtrl* ctrl, bool ignoreFirstTime): wxCommand(true, name)
9188 {
9189 /* wxRichTextAction* action = */ new wxRichTextAction(this, name, id, buffer, container, ctrl, ignoreFirstTime);
9190 }
9191
9192 wxRichTextCommand::wxRichTextCommand(const wxString& name): wxCommand(true, name)
9193 {
9194 }
9195
9196 wxRichTextCommand::~wxRichTextCommand()
9197 {
9198 ClearActions();
9199 }
9200
9201 void wxRichTextCommand::AddAction(wxRichTextAction* action)
9202 {
9203 if (!m_actions.Member(action))
9204 m_actions.Append(action);
9205 }
9206
9207 bool wxRichTextCommand::Do()
9208 {
9209 for (wxList::compatibility_iterator node = m_actions.GetFirst(); node; node = node->GetNext())
9210 {
9211 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
9212 action->Do();
9213 }
9214
9215 return true;
9216 }
9217
9218 bool wxRichTextCommand::Undo()
9219 {
9220 for (wxList::compatibility_iterator node = m_actions.GetLast(); node; node = node->GetPrevious())
9221 {
9222 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
9223 action->Undo();
9224 }
9225
9226 return true;
9227 }
9228
9229 void wxRichTextCommand::ClearActions()
9230 {
9231 WX_CLEAR_LIST(wxList, m_actions);
9232 }
9233
9234 /*!
9235 * Individual action
9236 *
9237 */
9238
9239 wxRichTextAction::wxRichTextAction(wxRichTextCommand* cmd, const wxString& name, wxRichTextCommandId id,
9240 wxRichTextBuffer* buffer, wxRichTextParagraphLayoutBox* container,
9241 wxRichTextCtrl* ctrl, bool ignoreFirstTime)
9242 {
9243 m_buffer = buffer;
9244 m_object = NULL;
9245 m_containerAddress.Create(buffer, container);
9246 m_ignoreThis = ignoreFirstTime;
9247 m_cmdId = id;
9248 m_position = -1;
9249 m_ctrl = ctrl;
9250 m_name = name;
9251 m_newParagraphs.SetDefaultStyle(buffer->GetDefaultStyle());
9252 m_newParagraphs.SetBasicStyle(buffer->GetBasicStyle());
9253 if (cmd)
9254 cmd->AddAction(this);
9255 }
9256
9257 wxRichTextAction::~wxRichTextAction()
9258 {
9259 if (m_object)
9260 delete m_object;
9261 }
9262
9263 // Returns the container that this action refers to, using the container address and top-level buffer.
9264 wxRichTextParagraphLayoutBox* wxRichTextAction::GetContainer() const
9265 {
9266 wxRichTextParagraphLayoutBox* container = wxDynamicCast(GetContainerAddress().GetObject(m_buffer), wxRichTextParagraphLayoutBox);
9267 return container;
9268 }
9269
9270
9271 void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions)
9272 {
9273 // Store a list of line start character and y positions so we can figure out which area
9274 // we need to refresh
9275
9276 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9277 wxRichTextParagraphLayoutBox* container = GetContainer();
9278 wxASSERT(container != NULL);
9279 if (!container)
9280 return;
9281
9282 // NOTE: we're assuming that the buffer is laid out correctly at this point.
9283 // If we had several actions, which only invalidate and leave layout until the
9284 // paint handler is called, then this might not be true. So we may need to switch
9285 // optimisation on only when we're simply adding text and not simultaneously
9286 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
9287 // first, but of course this means we'll be doing it twice.
9288 if (!m_buffer->IsDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
9289 {
9290 wxSize clientSize = m_ctrl->GetClientSize();
9291 wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint();
9292 int lastY = firstVisiblePt.y + clientSize.y;
9293
9294 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetRange().GetStart());
9295 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
9296 while (node)
9297 {
9298 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
9299 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
9300 while (node2)
9301 {
9302 wxRichTextLine* line = node2->GetData();
9303 wxPoint pt = line->GetAbsolutePosition();
9304 wxRichTextRange range = line->GetAbsoluteRange();
9305
9306 if (pt.y > lastY)
9307 {
9308 node2 = wxRichTextLineList::compatibility_iterator();
9309 node = wxRichTextObjectList::compatibility_iterator();
9310 }
9311 else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
9312 {
9313 optimizationLineCharPositions.Add(range.GetStart());
9314 optimizationLineYPositions.Add(pt.y);
9315 }
9316
9317 if (node2)
9318 node2 = node2->GetNext();
9319 }
9320
9321 if (node)
9322 node = node->GetNext();
9323 }
9324 }
9325 #endif
9326 }
9327
9328 bool wxRichTextAction::Do()
9329 {
9330 m_buffer->Modify(true);
9331
9332 wxRichTextParagraphLayoutBox* container = GetContainer();
9333 wxASSERT(container != NULL);
9334 if (!container)
9335 return false;
9336
9337 switch (m_cmdId)
9338 {
9339 case wxRICHTEXT_INSERT:
9340 {
9341 // Store a list of line start character and y positions so we can figure out which area
9342 // we need to refresh
9343 wxArrayInt optimizationLineCharPositions;
9344 wxArrayInt optimizationLineYPositions;
9345
9346 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9347 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
9348 #endif
9349
9350 container->InsertFragment(GetRange().GetStart(), m_newParagraphs);
9351 container->UpdateRanges();
9352
9353 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9354 // Layout() would stop prematurely at the top level.
9355 container->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
9356
9357 long newCaretPosition = GetPosition() + m_newParagraphs.GetOwnRange().GetLength();
9358
9359 // Character position to caret position
9360 newCaretPosition --;
9361
9362 // Don't take into account the last newline
9363 if (m_newParagraphs.GetPartialParagraph())
9364 newCaretPosition --;
9365 else
9366 if (m_newParagraphs.GetChildren().GetCount() > 1)
9367 {
9368 wxRichTextObject* p = (wxRichTextObject*) m_newParagraphs.GetChildren().GetLast()->GetData();
9369 if (p->GetRange().GetLength() == 1)
9370 newCaretPosition --;
9371 }
9372
9373 newCaretPosition = wxMin(newCaretPosition, (container->GetOwnRange().GetEnd()-1));
9374
9375 UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
9376
9377 wxRichTextEvent cmdEvent(
9378 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
9379 m_ctrl ? m_ctrl->GetId() : -1);
9380 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
9381 cmdEvent.SetRange(GetRange());
9382 cmdEvent.SetPosition(GetRange().GetStart());
9383 cmdEvent.SetContainer(container);
9384
9385 m_buffer->SendEvent(cmdEvent);
9386
9387 break;
9388 }
9389 case wxRICHTEXT_DELETE:
9390 {
9391 wxArrayInt optimizationLineCharPositions;
9392 wxArrayInt optimizationLineYPositions;
9393
9394 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9395 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
9396 #endif
9397
9398 container->DeleteRange(GetRange());
9399 container->UpdateRanges();
9400 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9401 // Layout() would stop prematurely at the top level.
9402 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9403
9404 long caretPos = GetRange().GetStart()-1;
9405 if (caretPos >= container->GetOwnRange().GetEnd())
9406 caretPos --;
9407
9408 UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
9409
9410 wxRichTextEvent cmdEvent(
9411 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
9412 m_ctrl ? m_ctrl->GetId() : -1);
9413 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
9414 cmdEvent.SetRange(GetRange());
9415 cmdEvent.SetPosition(GetRange().GetStart());
9416 cmdEvent.SetContainer(container);
9417
9418 m_buffer->SendEvent(cmdEvent);
9419
9420 break;
9421 }
9422 case wxRICHTEXT_CHANGE_STYLE:
9423 {
9424 ApplyParagraphs(GetNewParagraphs());
9425
9426 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9427 // Layout() would stop prematurely at the top level.
9428 container->InvalidateHierarchy(GetRange());
9429
9430 UpdateAppearance(GetPosition());
9431
9432 wxRichTextEvent cmdEvent(
9433 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED,
9434 m_ctrl ? m_ctrl->GetId() : -1);
9435 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
9436 cmdEvent.SetRange(GetRange());
9437 cmdEvent.SetPosition(GetRange().GetStart());
9438 cmdEvent.SetContainer(container);
9439
9440 m_buffer->SendEvent(cmdEvent);
9441
9442 break;
9443 }
9444 case wxRICHTEXT_CHANGE_ATTRIBUTES:
9445 {
9446 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer); // container->GetChildAtPosition(GetRange().GetStart());
9447 if (obj)
9448 {
9449 wxRichTextAttr oldAttr = obj->GetAttributes();
9450 obj->GetAttributes() = m_attributes;
9451 m_attributes = oldAttr;
9452 }
9453
9454 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9455 // Layout() would stop prematurely at the top level.
9456 container->InvalidateHierarchy(GetRange());
9457
9458 UpdateAppearance(GetPosition());
9459
9460 wxRichTextEvent cmdEvent(
9461 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED,
9462 m_ctrl ? m_ctrl->GetId() : -1);
9463 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
9464 cmdEvent.SetRange(GetRange());
9465 cmdEvent.SetPosition(GetRange().GetStart());
9466 cmdEvent.SetContainer(container);
9467
9468 m_buffer->SendEvent(cmdEvent);
9469
9470 break;
9471 }
9472 case wxRICHTEXT_CHANGE_OBJECT:
9473 {
9474 wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer);
9475 // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
9476 if (obj && m_object)
9477 {
9478 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(obj);
9479 if (node)
9480 {
9481 wxRichTextObject* obj = node->GetData();
9482 node->SetData(m_object);
9483 m_object = obj;
9484 }
9485 }
9486
9487 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9488 // Layout() would stop prematurely at the top level.
9489 container->InvalidateHierarchy(GetRange());
9490
9491 UpdateAppearance(GetPosition());
9492
9493 // TODO: send new kind of modification event
9494
9495 break;
9496 }
9497 default:
9498 break;
9499 }
9500
9501 return true;
9502 }
9503
9504 bool wxRichTextAction::Undo()
9505 {
9506 m_buffer->Modify(true);
9507
9508 wxRichTextParagraphLayoutBox* container = GetContainer();
9509 wxASSERT(container != NULL);
9510 if (!container)
9511 return false;
9512
9513 switch (m_cmdId)
9514 {
9515 case wxRICHTEXT_INSERT:
9516 {
9517 wxArrayInt optimizationLineCharPositions;
9518 wxArrayInt optimizationLineYPositions;
9519
9520 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9521 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
9522 #endif
9523
9524 container->DeleteRange(GetRange());
9525 container->UpdateRanges();
9526 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9527 // Layout() would stop prematurely at the top level.
9528 container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
9529
9530 long newCaretPosition = GetPosition() - 1;
9531
9532 UpdateAppearance(newCaretPosition, true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
9533
9534 wxRichTextEvent cmdEvent(
9535 wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
9536 m_ctrl ? m_ctrl->GetId() : -1);
9537 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
9538 cmdEvent.SetRange(GetRange());
9539 cmdEvent.SetPosition(GetRange().GetStart());
9540 cmdEvent.SetContainer(container);
9541
9542 m_buffer->SendEvent(cmdEvent);
9543
9544 break;
9545 }
9546 case wxRICHTEXT_DELETE:
9547 {
9548 wxArrayInt optimizationLineCharPositions;
9549 wxArrayInt optimizationLineYPositions;
9550
9551 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9552 CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
9553 #endif
9554
9555 container->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
9556 container->UpdateRanges();
9557 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9558 // Layout() would stop prematurely at the top level.
9559 container->InvalidateHierarchy(GetRange());
9560
9561 UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
9562
9563 wxRichTextEvent cmdEvent(
9564 wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
9565 m_ctrl ? m_ctrl->GetId() : -1);
9566 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
9567 cmdEvent.SetRange(GetRange());
9568 cmdEvent.SetPosition(GetRange().GetStart());
9569 cmdEvent.SetContainer(container);
9570
9571 m_buffer->SendEvent(cmdEvent);
9572
9573 break;
9574 }
9575 case wxRICHTEXT_CHANGE_STYLE:
9576 {
9577 ApplyParagraphs(GetOldParagraphs());
9578 // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
9579 // Layout() would stop prematurely at the top level.
9580 container->InvalidateHierarchy(GetRange());
9581
9582 UpdateAppearance(GetPosition());
9583
9584 wxRichTextEvent cmdEvent(
9585 wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED,
9586 m_ctrl ? m_ctrl->GetId() : -1);
9587 cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
9588 cmdEvent.SetRange(GetRange());
9589 cmdEvent.SetPosition(GetRange().GetStart());
9590 cmdEvent.SetContainer(container);
9591
9592 m_buffer->SendEvent(cmdEvent);
9593
9594 break;
9595 }
9596 case wxRICHTEXT_CHANGE_ATTRIBUTES:
9597 case wxRICHTEXT_CHANGE_OBJECT:
9598 {
9599 return Do();
9600 }
9601 default:
9602 break;
9603 }
9604
9605 return true;
9606 }
9607
9608 /// Update the control appearance
9609 void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions, bool isDoCmd)
9610 {
9611 wxRichTextParagraphLayoutBox* container = GetContainer();
9612 wxASSERT(container != NULL);
9613 if (!container)
9614 return;
9615
9616 if (m_ctrl)
9617 {
9618 m_ctrl->SetFocusObject(container);
9619 m_ctrl->SetCaretPosition(caretPosition);
9620
9621 if (!m_ctrl->IsFrozen())
9622 {
9623 wxRect containerRect = container->GetRect();
9624
9625 m_ctrl->LayoutContent();
9626
9627 // Refresh everything if there were floating objects or the container changed size
9628 // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
9629 if (container->GetFloatingObjectCount() > 0 || (container->GetParent() && containerRect != container->GetRect()))
9630 {
9631 m_ctrl->Refresh(false);
9632 }
9633 else
9634
9635 #if wxRICHTEXT_USE_OPTIMIZED_DRAWING
9636 // Find refresh rectangle if we are in a position to optimise refresh
9637 if ((m_cmdId == wxRICHTEXT_INSERT || m_cmdId == wxRICHTEXT_DELETE) && optimizationLineCharPositions)
9638 {
9639 size_t i;
9640
9641 wxSize clientSize = m_ctrl->GetClientSize();
9642 wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint();
9643
9644 // Start/end positions
9645 int firstY = 0;
9646 int lastY = firstVisiblePt.y + clientSize.y;
9647
9648 bool foundEnd = false;
9649
9650 // position offset - how many characters were inserted
9651 int positionOffset = GetRange().GetLength();
9652
9653 // Determine whether this is Do or Undo, and adjust positionOffset accordingly
9654 if ((m_cmdId == wxRICHTEXT_DELETE && isDoCmd) || (m_cmdId == wxRICHTEXT_INSERT && !isDoCmd))
9655 positionOffset = - positionOffset;
9656
9657 // find the first line which is being drawn at the same position as it was
9658 // before. Since we're talking about a simple insertion, we can assume
9659 // that the rest of the window does not need to be redrawn.
9660
9661 wxRichTextParagraph* para = container->GetParagraphAtPosition(GetPosition());
9662 // Since we support floating layout, we should redraw the whole para instead of just
9663 // the first line touching the invalid range.
9664 if (para)
9665 {
9666 firstY = para->GetPosition().y;
9667 }
9668
9669 wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
9670 while (node)
9671 {
9672 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
9673 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
9674 while (node2)
9675 {
9676 wxRichTextLine* line = node2->GetData();
9677 wxPoint pt = line->GetAbsolutePosition();
9678 wxRichTextRange range = line->GetAbsoluteRange();
9679
9680 // we want to find the first line that is in the same position
9681 // as before. This will mean we're at the end of the changed text.
9682
9683 if (pt.y > lastY) // going past the end of the window, no more info
9684 {
9685 node2 = wxRichTextLineList::compatibility_iterator();
9686 node = wxRichTextObjectList::compatibility_iterator();
9687 }
9688 // Detect last line in the buffer
9689 else if (!node2->GetNext() && para->GetRange().Contains(container->GetOwnRange().GetEnd()))
9690 {
9691 // If deleting text, make sure we refresh below as well as above
9692 if (positionOffset >= 0)
9693 {
9694 foundEnd = true;
9695 lastY = pt.y + line->GetSize().y;
9696 }
9697
9698 node2 = wxRichTextLineList::compatibility_iterator();
9699 node = wxRichTextObjectList::compatibility_iterator();
9700
9701 break;
9702 }
9703 else
9704 {
9705 // search for this line being at the same position as before
9706 for (i = 0; i < optimizationLineCharPositions->GetCount(); i++)
9707 {
9708 if (((*optimizationLineCharPositions)[i] + positionOffset == range.GetStart()) &&
9709 ((*optimizationLineYPositions)[i] == pt.y))
9710 {
9711 // Stop, we're now the same as we were
9712 foundEnd = true;
9713
9714 lastY = pt.y;
9715
9716 node2 = wxRichTextLineList::compatibility_iterator();
9717 node = wxRichTextObjectList::compatibility_iterator();
9718
9719 break;
9720 }
9721 }
9722 }
9723
9724 if (node2)
9725 node2 = node2->GetNext();
9726 }
9727
9728 if (node)
9729 node = node->GetNext();
9730 }
9731
9732 firstY = wxMax(firstVisiblePt.y, firstY);
9733 if (!foundEnd)
9734 lastY = firstVisiblePt.y + clientSize.y;
9735
9736 // Convert to device coordinates
9737 wxRect rect(m_ctrl->GetPhysicalPoint(wxPoint(firstVisiblePt.x, firstY)), wxSize(clientSize.x, lastY - firstY));
9738 m_ctrl->RefreshRect(rect);
9739 }
9740 else
9741 #endif
9742 m_ctrl->Refresh(false);
9743
9744 m_ctrl->PositionCaret();
9745
9746 // This causes styles to persist when doing programmatic
9747 // content creation except when Freeze/Thaw is used, so
9748 // disable this and check for the consequences.
9749 // m_ctrl->SetDefaultStyleToCursorStyle();
9750
9751 if (sendUpdateEvent)
9752 wxTextCtrl::SendTextUpdatedEvent(m_ctrl);
9753 }
9754 }
9755 }
9756
9757 /// Replace the buffer paragraphs with the new ones.
9758 void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment)
9759 {
9760 wxRichTextParagraphLayoutBox* container = GetContainer();
9761 wxASSERT(container != NULL);
9762 if (!container)
9763 return;
9764
9765 wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst();
9766 while (node)
9767 {
9768 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
9769 wxASSERT (para != NULL);
9770
9771 // We'll replace the existing paragraph by finding the paragraph at this position,
9772 // delete its node data, and setting a copy as the new node data.
9773 // TODO: make more efficient by simply swapping old and new paragraph objects.
9774
9775 wxRichTextParagraph* existingPara = container->GetParagraphAtPosition(para->GetRange().GetStart());
9776 if (existingPara)
9777 {
9778 wxRichTextObjectList::compatibility_iterator bufferParaNode = container->GetChildren().Find(existingPara);
9779 if (bufferParaNode)
9780 {
9781 wxRichTextParagraph* newPara = new wxRichTextParagraph(*para);
9782 newPara->SetParent(container);
9783
9784 bufferParaNode->SetData(newPara);
9785
9786 delete existingPara;
9787 }
9788 }
9789
9790 node = node->GetNext();
9791 }
9792 }
9793
9794
9795 /*!
9796 * wxRichTextRange
9797 * This stores beginning and end positions for a range of data.
9798 */
9799
9800 WX_DEFINE_OBJARRAY(wxRichTextRangeArray);
9801
9802 /// Limit this range to be within 'range'
9803 bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
9804 {
9805 if (m_start < range.m_start)
9806 m_start = range.m_start;
9807
9808 if (m_end > range.m_end)
9809 m_end = range.m_end;
9810
9811 return true;
9812 }
9813
9814 /*!
9815 * wxRichTextImage implementation
9816 * This object represents an image.
9817 */
9818
9819 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
9820
9821 wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle):
9822 wxRichTextObject(parent)
9823 {
9824 m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG);
9825 if (charStyle)
9826 SetAttributes(*charStyle);
9827 }
9828
9829 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle):
9830 wxRichTextObject(parent)
9831 {
9832 m_imageBlock = imageBlock;
9833 if (charStyle)
9834 SetAttributes(*charStyle);
9835 }
9836
9837 /// Create a cached image at the required size
9838 bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache)
9839 {
9840 if (resetCache || !m_imageCache.IsOk() /* || m_imageCache.GetWidth() != size.x || m_imageCache.GetHeight() != size.y */)
9841 {
9842 if (!m_imageBlock.IsOk())
9843 return false;
9844
9845 wxImage image;
9846 m_imageBlock.Load(image);
9847 if (!image.IsOk())
9848 return false;
9849
9850 int width = image.GetWidth();
9851 int height = image.GetHeight();
9852
9853 if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
9854 {
9855 if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
9856 width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
9857 else
9858 width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
9859 }
9860 if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
9861 {
9862 if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
9863 height = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
9864 else
9865 height = GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
9866 }
9867
9868 if (image.GetWidth() == width && image.GetHeight() == height)
9869 m_imageCache = wxBitmap(image);
9870 else
9871 {
9872 // If the original width and height is small, e.g. 400 or below,
9873 // scale up and then down to improve image quality. This can make
9874 // a big difference, with not much performance hit.
9875 int upscaleThreshold = 400;
9876 wxImage img;
9877 if (image.GetWidth() <= upscaleThreshold || image.GetHeight() <= upscaleThreshold)
9878 {
9879 img = image.Scale(image.GetWidth()*2, image.GetHeight()*2);
9880 img.Rescale(width, height, wxIMAGE_QUALITY_HIGH);
9881 }
9882 else
9883 img = image.Scale(width, height, wxIMAGE_QUALITY_HIGH);
9884 m_imageCache = wxBitmap(img);
9885 }
9886 }
9887
9888 return m_imageCache.IsOk();
9889 }
9890
9891 /// Draw the item
9892 bool wxRichTextImage::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
9893 {
9894 if (!IsShown())
9895 return true;
9896
9897 // Don't need cached size AFAIK
9898 // wxSize size = GetCachedSize();
9899 if (!LoadImageCache(dc))
9900 return false;
9901
9902 DrawBoxAttributes(dc, GetBuffer(), GetAttributes(), wxRect(GetPosition(), GetCachedSize()));
9903
9904 #if 0
9905 int y = rect.y + (rect.height - m_imageCache.GetHeight());
9906
9907 dc.DrawBitmap(m_imageCache, rect.x, y, true);
9908 #endif
9909
9910 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
9911 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
9912 marginRect = rect; // outer rectangle, will calculate contentRect
9913 GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
9914
9915 dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true);
9916
9917 if (selection.WithinSelection(range.GetStart(), this))
9918 {
9919 wxCheckSetBrush(dc, *wxBLACK_BRUSH);
9920 wxCheckSetPen(dc, *wxBLACK_PEN);
9921 dc.SetLogicalFunction(wxINVERT);
9922 dc.DrawRectangle(contentRect);
9923 dc.SetLogicalFunction(wxCOPY);
9924 }
9925
9926 return true;
9927 }
9928
9929 /// Lay the item out
9930 bool wxRichTextImage::Layout(wxDC& dc, const wxRect& rect, int WXUNUSED(style))
9931 {
9932 if (!LoadImageCache(dc))
9933 return false;
9934
9935 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
9936 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
9937 contentRect = wxRect(wxPoint(0,0), imageSize);
9938 GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
9939
9940 wxSize overallSize = marginRect.GetSize();
9941
9942 SetCachedSize(overallSize);
9943 SetMaxSize(overallSize);
9944 SetMinSize(overallSize);
9945 SetPosition(rect.GetPosition());
9946
9947 return true;
9948 }
9949
9950 /// Get/set the object size for the given range. Returns false if the range
9951 /// is invalid for this object.
9952 bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& dc, int WXUNUSED(flags), wxPoint WXUNUSED(position), wxArrayInt* partialExtents) const
9953 {
9954 if (!range.IsWithin(GetRange()))
9955 return false;
9956
9957 if (!((wxRichTextImage*)this)->LoadImageCache(dc))
9958 {
9959 size.x = 0; size.y = 0;
9960 if (partialExtents)
9961 partialExtents->Add(0);
9962 return false;
9963 }
9964
9965 wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
9966 wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
9967 contentRect = wxRect(wxPoint(0,0), imageSize);
9968 GetBoxRects(dc, GetBuffer(), GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
9969
9970 wxSize overallSize = marginRect.GetSize();
9971
9972 if (partialExtents)
9973 partialExtents->Add(overallSize.x);
9974
9975 size = overallSize;
9976
9977 return true;
9978 }
9979
9980 // Get the 'natural' size for an object. For an image, it would be the
9981 // image size.
9982 wxTextAttrSize wxRichTextImage::GetNaturalSize() const
9983 {
9984 wxTextAttrSize size;
9985 if (GetImageCache().IsOk())
9986 {
9987 size.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
9988 size.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS);
9989 }
9990 return size;
9991 }
9992
9993
9994 /// Copy
9995 void wxRichTextImage::Copy(const wxRichTextImage& obj)
9996 {
9997 wxRichTextObject::Copy(obj);
9998
9999 m_imageBlock = obj.m_imageBlock;
10000 }
10001
10002 /// Edit properties via a GUI
10003 bool wxRichTextImage::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
10004 {
10005 wxRichTextObjectPropertiesDialog imageDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Picture Properties"));
10006 imageDlg.SetAttributes(GetAttributes());
10007
10008 if (imageDlg.ShowModal() == wxID_OK)
10009 {
10010 // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
10011 // indeterminate in the object.
10012 imageDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
10013 return true;
10014 }
10015 else
10016 return false;
10017 }
10018
10019 /*!
10020 * Utilities
10021 *
10022 */
10023
10024 /// Compare two attribute objects
10025 bool wxTextAttrEq(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
10026 {
10027 return (attr1 == attr2);
10028 }
10029
10030 // Partial equality test taking flags into account
10031 bool wxTextAttrEqPartial(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
10032 {
10033 return attr1.EqPartial(attr2);
10034 }
10035
10036 /// Compare tabs
10037 bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2)
10038 {
10039 if (tabs1.GetCount() != tabs2.GetCount())
10040 return false;
10041
10042 size_t i;
10043 for (i = 0; i < tabs1.GetCount(); i++)
10044 {
10045 if (tabs1[i] != tabs2[i])
10046 return false;
10047 }
10048 return true;
10049 }
10050
10051 bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
10052 {
10053 return destStyle.Apply(style, compareWith);
10054 }
10055
10056 // Remove attributes
10057 bool wxRichTextRemoveStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style)
10058 {
10059 return destStyle.RemoveStyle(style);
10060 }
10061
10062 /// Combine two bitlists, specifying the bits of interest with separate flags.
10063 bool wxRichTextCombineBitlists(int& valueA, int valueB, int& flagsA, int flagsB)
10064 {
10065 return wxRichTextAttr::CombineBitlists(valueA, valueB, flagsA, flagsB);
10066 }
10067
10068 /// Compare two bitlists
10069 bool wxRichTextBitlistsEqPartial(int valueA, int valueB, int flags)
10070 {
10071 return wxRichTextAttr::BitlistsEqPartial(valueA, valueB, flags);
10072 }
10073
10074 /// Split into paragraph and character styles
10075 bool wxRichTextSplitParaCharStyles(const wxRichTextAttr& style, wxRichTextAttr& parStyle, wxRichTextAttr& charStyle)
10076 {
10077 return wxRichTextAttr::SplitParaCharStyles(style, parStyle, charStyle);
10078 }
10079
10080 /// Convert a decimal to Roman numerals
10081 wxString wxRichTextDecimalToRoman(long n)
10082 {
10083 static wxArrayInt decimalNumbers;
10084 static wxArrayString romanNumbers;
10085
10086 // Clean up arrays
10087 if (n == -1)
10088 {
10089 decimalNumbers.Clear();
10090 romanNumbers.Clear();
10091 return wxEmptyString;
10092 }
10093
10094 if (decimalNumbers.GetCount() == 0)
10095 {
10096 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
10097
10098 wxRichTextAddDecRom(1000, wxT("M"));
10099 wxRichTextAddDecRom(900, wxT("CM"));
10100 wxRichTextAddDecRom(500, wxT("D"));
10101 wxRichTextAddDecRom(400, wxT("CD"));
10102 wxRichTextAddDecRom(100, wxT("C"));
10103 wxRichTextAddDecRom(90, wxT("XC"));
10104 wxRichTextAddDecRom(50, wxT("L"));
10105 wxRichTextAddDecRom(40, wxT("XL"));
10106 wxRichTextAddDecRom(10, wxT("X"));
10107 wxRichTextAddDecRom(9, wxT("IX"));
10108 wxRichTextAddDecRom(5, wxT("V"));
10109 wxRichTextAddDecRom(4, wxT("IV"));
10110 wxRichTextAddDecRom(1, wxT("I"));
10111 }
10112
10113 int i = 0;
10114 wxString roman;
10115
10116 while (n > 0 && i < 13)
10117 {
10118 if (n >= decimalNumbers[i])
10119 {
10120 n -= decimalNumbers[i];
10121 roman += romanNumbers[i];
10122 }
10123 else
10124 {
10125 i ++;
10126 }
10127 }
10128 if (roman.IsEmpty())
10129 roman = wxT("0");
10130 return roman;
10131 }
10132
10133 /*!
10134 * wxRichTextFileHandler
10135 * Base class for file handlers
10136 */
10137
10138 IMPLEMENT_CLASS(wxRichTextFileHandler, wxObject)
10139
10140 #if wxUSE_FFILE && wxUSE_STREAMS
10141 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer *buffer, const wxString& filename)
10142 {
10143 wxFFileInputStream stream(filename);
10144 if (stream.Ok())
10145 return LoadFile(buffer, stream);
10146
10147 return false;
10148 }
10149
10150 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer *buffer, const wxString& filename)
10151 {
10152 wxFFileOutputStream stream(filename);
10153 if (stream.Ok())
10154 return SaveFile(buffer, stream);
10155
10156 return false;
10157 }
10158 #endif // wxUSE_FFILE && wxUSE_STREAMS
10159
10160 /// Can we handle this filename (if using files)? By default, checks the extension.
10161 bool wxRichTextFileHandler::CanHandle(const wxString& filename) const
10162 {
10163 wxString path, file, ext;
10164 wxFileName::SplitPath(filename, & path, & file, & ext);
10165
10166 return (ext.Lower() == GetExtension());
10167 }
10168
10169 /*!
10170 * wxRichTextTextHandler
10171 * Plain text handler
10172 */
10173
10174 IMPLEMENT_CLASS(wxRichTextPlainTextHandler, wxRichTextFileHandler)
10175
10176 #if wxUSE_STREAMS
10177 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
10178 {
10179 if (!stream.IsOk())
10180 return false;
10181
10182 wxString str;
10183 int lastCh = 0;
10184
10185 while (!stream.Eof())
10186 {
10187 int ch = stream.GetC();
10188
10189 if (!stream.Eof())
10190 {
10191 if (ch == 10 && lastCh != 13)
10192 str += wxT('\n');
10193
10194 if (ch > 0 && ch != 10)
10195 str += wxChar(ch);
10196
10197 lastCh = ch;
10198 }
10199 }
10200
10201 buffer->ResetAndClearCommands();
10202 buffer->Clear();
10203 buffer->AddParagraphs(str);
10204 buffer->UpdateRanges();
10205
10206 return true;
10207 }
10208
10209 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
10210 {
10211 if (!stream.IsOk())
10212 return false;
10213
10214 wxString text = buffer->GetText();
10215
10216 wxString newLine = wxRichTextLineBreakChar;
10217 text.Replace(newLine, wxT("\n"));
10218
10219 wxCharBuffer buf = text.ToAscii();
10220
10221 stream.Write((const char*) buf, text.length());
10222 return true;
10223 }
10224 #endif // wxUSE_STREAMS
10225
10226 /*
10227 * Stores information about an image, in binary in-memory form
10228 */
10229
10230 wxRichTextImageBlock::wxRichTextImageBlock()
10231 {
10232 Init();
10233 }
10234
10235 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock& block):wxObject()
10236 {
10237 Init();
10238 Copy(block);
10239 }
10240
10241 wxRichTextImageBlock::~wxRichTextImageBlock()
10242 {
10243 wxDELETEA(m_data);
10244 }
10245
10246 void wxRichTextImageBlock::Init()
10247 {
10248 m_data = NULL;
10249 m_dataSize = 0;
10250 m_imageType = wxBITMAP_TYPE_INVALID;
10251 }
10252
10253 void wxRichTextImageBlock::Clear()
10254 {
10255 wxDELETEA(m_data);
10256 m_dataSize = 0;
10257 m_imageType = wxBITMAP_TYPE_INVALID;
10258 }
10259
10260
10261 // Load the original image into a memory block.
10262 // If the image is not a JPEG, we must convert it into a JPEG
10263 // to conserve space.
10264 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
10265 // load the image a 2nd time.
10266
10267 bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, wxBitmapType imageType,
10268 wxImage& image, bool convertToJPEG)
10269 {
10270 m_imageType = imageType;
10271
10272 wxString filenameToRead(filename);
10273 bool removeFile = false;
10274
10275 if (imageType == wxBITMAP_TYPE_INVALID)
10276 return false; // Could not determine image type
10277
10278 if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
10279 {
10280 wxString tempFile =
10281 wxFileName::CreateTempFileName(_("image"));
10282
10283 wxASSERT(!tempFile.IsEmpty());
10284
10285 image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
10286 filenameToRead = tempFile;
10287 removeFile = true;
10288
10289 m_imageType = wxBITMAP_TYPE_JPEG;
10290 }
10291 wxFile file;
10292 if (!file.Open(filenameToRead))
10293 return false;
10294
10295 m_dataSize = (size_t) file.Length();
10296 file.Close();
10297
10298 if (m_data)
10299 delete[] m_data;
10300 m_data = ReadBlock(filenameToRead, m_dataSize);
10301
10302 if (removeFile)
10303 wxRemoveFile(filenameToRead);
10304
10305 return (m_data != NULL);
10306 }
10307
10308 // Make an image block from the wxImage in the given
10309 // format.
10310 bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, wxBitmapType imageType, int quality)
10311 {
10312 image.SetOption(wxT("quality"), quality);
10313
10314 if (imageType == wxBITMAP_TYPE_INVALID)
10315 return false; // Could not determine image type
10316
10317 return DoMakeImageBlock(image, imageType);
10318 }
10319
10320 // Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
10321 bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage& image, wxBitmapType imageType)
10322 {
10323 if (imageType == wxBITMAP_TYPE_INVALID)
10324 return false; // Could not determine image type
10325
10326 return DoMakeImageBlock(image, imageType);
10327 }
10328
10329 // Makes the image block
10330 bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage& image, wxBitmapType imageType)
10331 {
10332 wxMemoryOutputStream memStream;
10333 if (!image.SaveFile(memStream, imageType))
10334 {
10335 return false;
10336 }
10337
10338 unsigned char* block = new unsigned char[memStream.GetSize()];
10339 if (!block)
10340 return false;
10341
10342 if (m_data)
10343 delete[] m_data;
10344 m_data = block;
10345
10346 m_imageType = imageType;
10347 m_dataSize = memStream.GetSize();
10348
10349 memStream.CopyTo(m_data, m_dataSize);
10350
10351 return (m_data != NULL);
10352 }
10353
10354 // Write to a file
10355 bool wxRichTextImageBlock::Write(const wxString& filename)
10356 {
10357 return WriteBlock(filename, m_data, m_dataSize);
10358 }
10359
10360 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock& block)
10361 {
10362 m_imageType = block.m_imageType;
10363 wxDELETEA(m_data);
10364 m_dataSize = block.m_dataSize;
10365 if (m_dataSize == 0)
10366 return;
10367
10368 m_data = new unsigned char[m_dataSize];
10369 unsigned int i;
10370 for (i = 0; i < m_dataSize; i++)
10371 m_data[i] = block.m_data[i];
10372 }
10373
10374 //// Operators
10375 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock& block)
10376 {
10377 Copy(block);
10378 }
10379
10380 // Load a wxImage from the block
10381 bool wxRichTextImageBlock::Load(wxImage& image)
10382 {
10383 if (!m_data)
10384 return false;
10385
10386 // Read in the image.
10387 #if wxUSE_STREAMS
10388 wxMemoryInputStream mstream(m_data, m_dataSize);
10389 bool success = image.LoadFile(mstream, GetImageType());
10390 #else
10391 wxString tempFile = wxFileName::CreateTempFileName(_("image"));
10392 wxASSERT(!tempFile.IsEmpty());
10393
10394 if (!WriteBlock(tempFile, m_data, m_dataSize))
10395 {
10396 return false;
10397 }
10398 success = image.LoadFile(tempFile, GetImageType());
10399 wxRemoveFile(tempFile);
10400 #endif
10401
10402 return success;
10403 }
10404
10405 // Write data in hex to a stream
10406 bool wxRichTextImageBlock::WriteHex(wxOutputStream& stream)
10407 {
10408 const int bufSize = 512;
10409 char buf[bufSize+1];
10410
10411 int left = m_dataSize;
10412 int n, i, j;
10413 j = 0;
10414 while (left > 0)
10415 {
10416 if (left*2 > bufSize)
10417 {
10418 n = bufSize; left -= (bufSize/2);
10419 }
10420 else
10421 {
10422 n = left*2; left = 0;
10423 }
10424
10425 char* b = buf;
10426 for (i = 0; i < (n/2); i++)
10427 {
10428 wxDecToHex(m_data[j], b, b+1);
10429 b += 2; j ++;
10430 }
10431
10432 buf[n] = 0;
10433 stream.Write((const char*) buf, n);
10434 }
10435 return true;
10436 }
10437
10438 // Read data in hex from a stream
10439 bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, wxBitmapType imageType)
10440 {
10441 int dataSize = length/2;
10442
10443 if (m_data)
10444 delete[] m_data;
10445
10446 // create a null terminated temporary string:
10447 char str[3];
10448 str[2] = '\0';
10449
10450 m_data = new unsigned char[dataSize];
10451 int i;
10452 for (i = 0; i < dataSize; i ++)
10453 {
10454 str[0] = (char)stream.GetC();
10455 str[1] = (char)stream.GetC();
10456
10457 m_data[i] = (unsigned char)wxHexToDec(str);
10458 }
10459
10460 m_dataSize = dataSize;
10461 m_imageType = imageType;
10462
10463 return true;
10464 }
10465
10466 // Allocate and read from stream as a block of memory
10467 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream& stream, size_t size)
10468 {
10469 unsigned char* block = new unsigned char[size];
10470 if (!block)
10471 return NULL;
10472
10473 stream.Read(block, size);
10474
10475 return block;
10476 }
10477
10478 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString& filename, size_t size)
10479 {
10480 wxFileInputStream stream(filename);
10481 if (!stream.Ok())
10482 return NULL;
10483
10484 return ReadBlock(stream, size);
10485 }
10486
10487 // Write memory block to stream
10488 bool wxRichTextImageBlock::WriteBlock(wxOutputStream& stream, unsigned char* block, size_t size)
10489 {
10490 stream.Write((void*) block, size);
10491 return stream.IsOk();
10492
10493 }
10494
10495 // Write memory block to file
10496 bool wxRichTextImageBlock::WriteBlock(const wxString& filename, unsigned char* block, size_t size)
10497 {
10498 wxFileOutputStream outStream(filename);
10499 if (!outStream.Ok())
10500 return false;
10501
10502 return WriteBlock(outStream, block, size);
10503 }
10504
10505 // Gets the extension for the block's type
10506 wxString wxRichTextImageBlock::GetExtension() const
10507 {
10508 wxImageHandler* handler = wxImage::FindHandler(GetImageType());
10509 if (handler)
10510 return handler->GetExtension();
10511 else
10512 return wxEmptyString;
10513 }
10514
10515 #if wxUSE_DATAOBJ
10516
10517 /*!
10518 * The data object for a wxRichTextBuffer
10519 */
10520
10521 const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxShape");
10522
10523 wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer)
10524 {
10525 m_richTextBuffer = richTextBuffer;
10526
10527 // this string should uniquely identify our format, but is otherwise
10528 // arbitrary
10529 m_formatRichTextBuffer.SetId(GetRichTextBufferFormatId());
10530
10531 SetFormat(m_formatRichTextBuffer);
10532 }
10533
10534 wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
10535 {
10536 delete m_richTextBuffer;
10537 }
10538
10539 // after a call to this function, the richTextBuffer is owned by the caller and it
10540 // is responsible for deleting it!
10541 wxRichTextBuffer* wxRichTextBufferDataObject::GetRichTextBuffer()
10542 {
10543 wxRichTextBuffer* richTextBuffer = m_richTextBuffer;
10544 m_richTextBuffer = NULL;
10545
10546 return richTextBuffer;
10547 }
10548
10549 wxDataFormat wxRichTextBufferDataObject::GetPreferredFormat(Direction WXUNUSED(dir)) const
10550 {
10551 return m_formatRichTextBuffer;
10552 }
10553
10554 size_t wxRichTextBufferDataObject::GetDataSize() const
10555 {
10556 if (!m_richTextBuffer)
10557 return 0;
10558
10559 wxString bufXML;
10560
10561 {
10562 wxStringOutputStream stream(& bufXML);
10563 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
10564 {
10565 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10566 return 0;
10567 }
10568 }
10569
10570 #if wxUSE_UNICODE
10571 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
10572 return strlen(buffer) + 1;
10573 #else
10574 return bufXML.Length()+1;
10575 #endif
10576 }
10577
10578 bool wxRichTextBufferDataObject::GetDataHere(void *pBuf) const
10579 {
10580 if (!pBuf || !m_richTextBuffer)
10581 return false;
10582
10583 wxString bufXML;
10584
10585 {
10586 wxStringOutputStream stream(& bufXML);
10587 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
10588 {
10589 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
10590 return 0;
10591 }
10592 }
10593
10594 #if wxUSE_UNICODE
10595 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
10596 size_t len = strlen(buffer);
10597 memcpy((char*) pBuf, (const char*) buffer, len);
10598 ((char*) pBuf)[len] = 0;
10599 #else
10600 size_t len = bufXML.Length();
10601 memcpy((char*) pBuf, (const char*) bufXML.c_str(), len);
10602 ((char*) pBuf)[len] = 0;
10603 #endif
10604
10605 return true;
10606 }
10607
10608 bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len), const void *buf)
10609 {
10610 wxDELETE(m_richTextBuffer);
10611
10612 wxString bufXML((const char*) buf, wxConvUTF8);
10613
10614 m_richTextBuffer = new wxRichTextBuffer;
10615
10616 wxStringInputStream stream(bufXML);
10617 if (!m_richTextBuffer->LoadFile(stream, wxRICHTEXT_TYPE_XML))
10618 {
10619 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
10620
10621 wxDELETE(m_richTextBuffer);
10622
10623 return false;
10624 }
10625 return true;
10626 }
10627
10628 #endif
10629 // wxUSE_DATAOBJ
10630
10631
10632 /*
10633 * wxRichTextFontTable
10634 * Manages quick access to a pool of fonts for rendering rich text
10635 */
10636
10637 WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont, wxRichTextFontTableHashMap, class WXDLLIMPEXP_RICHTEXT);
10638
10639 class wxRichTextFontTableData: public wxObjectRefData
10640 {
10641 public:
10642 wxRichTextFontTableData() {}
10643
10644 wxFont FindFont(const wxRichTextAttr& fontSpec);
10645
10646 wxRichTextFontTableHashMap m_hashMap;
10647 };
10648
10649 wxFont wxRichTextFontTableData::FindFont(const wxRichTextAttr& fontSpec)
10650 {
10651 wxString facename(fontSpec.GetFontFaceName());
10652 wxString spec(wxString::Format(wxT("%d-%d-%d-%d-%s-%d"), fontSpec.GetFontSize(), fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), (int) fontSpec.GetFontUnderlined(), facename.c_str(), (int) fontSpec.GetFontEncoding()));
10653 wxRichTextFontTableHashMap::iterator entry = m_hashMap.find(spec);
10654
10655 if ( entry == m_hashMap.end() )
10656 {
10657 wxFont font(fontSpec.GetFontSize(), wxDEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename.c_str());
10658 m_hashMap[spec] = font;
10659 return font;
10660 }
10661 else
10662 {
10663 return entry->second;
10664 }
10665 }
10666
10667 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable, wxObject)
10668
10669 wxRichTextFontTable::wxRichTextFontTable()
10670 {
10671 m_refData = new wxRichTextFontTableData;
10672 }
10673
10674 wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table)
10675 : wxObject()
10676 {
10677 (*this) = table;
10678 }
10679
10680 wxRichTextFontTable::~wxRichTextFontTable()
10681 {
10682 UnRef();
10683 }
10684
10685 bool wxRichTextFontTable::operator == (const wxRichTextFontTable& table) const
10686 {
10687 return (m_refData == table.m_refData);
10688 }
10689
10690 void wxRichTextFontTable::operator= (const wxRichTextFontTable& table)
10691 {
10692 Ref(table);
10693 }
10694
10695 wxFont wxRichTextFontTable::FindFont(const wxRichTextAttr& fontSpec)
10696 {
10697 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
10698 if (data)
10699 return data->FindFont(fontSpec);
10700 else
10701 return wxFont();
10702 }
10703
10704 void wxRichTextFontTable::Clear()
10705 {
10706 wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
10707 if (data)
10708 data->m_hashMap.clear();
10709 }
10710
10711 // wxTextBoxAttr
10712
10713
10714 void wxTextBoxAttr::Reset()
10715 {
10716 m_flags = 0;
10717 m_floatMode = wxTEXT_BOX_ATTR_FLOAT_NONE;
10718 m_clearMode = wxTEXT_BOX_ATTR_CLEAR_NONE;
10719 m_collapseMode = wxTEXT_BOX_ATTR_COLLAPSE_NONE;
10720 m_verticalAlignment = wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE;
10721
10722 m_margins.Reset();
10723 m_padding.Reset();
10724 m_position.Reset();
10725
10726 m_size.Reset();
10727
10728 m_border.Reset();
10729 m_outline.Reset();
10730 }
10731
10732 // Equality test
10733 bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const
10734 {
10735 return (
10736 m_flags == attr.m_flags &&
10737 m_floatMode == attr.m_floatMode &&
10738 m_clearMode == attr.m_clearMode &&
10739 m_collapseMode == attr.m_collapseMode &&
10740 m_verticalAlignment == attr.m_verticalAlignment &&
10741
10742 m_margins == attr.m_margins &&
10743 m_padding == attr.m_padding &&
10744 m_position == attr.m_position &&
10745
10746 m_size == attr.m_size &&
10747
10748 m_border == attr.m_border &&
10749 m_outline == attr.m_outline
10750 );
10751 }
10752
10753 // Partial equality test
10754 bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr) const
10755 {
10756 if (attr.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr.GetFloatMode()))
10757 return false;
10758
10759 if (attr.HasClearMode() && HasClearMode() && (GetClearMode() != attr.GetClearMode()))
10760 return false;
10761
10762 if (attr.HasCollapseBorders() && HasCollapseBorders() && (attr.GetCollapseBorders() != GetCollapseBorders()))
10763 return false;
10764
10765 if (attr.HasVerticalAlignment() && HasVerticalAlignment() && (attr.GetVerticalAlignment() != GetVerticalAlignment()))
10766 return false;
10767
10768 // Position
10769
10770 if (!m_position.EqPartial(attr.m_position))
10771 return false;
10772
10773 // Margins
10774
10775 if (!m_margins.EqPartial(attr.m_margins))
10776 return false;
10777
10778 // Padding
10779
10780 if (!m_padding.EqPartial(attr.m_padding))
10781 return false;
10782
10783 // Border
10784
10785 if (!GetBorder().EqPartial(attr.GetBorder()))
10786 return false;
10787
10788 // Outline
10789
10790 if (!GetOutline().EqPartial(attr.GetOutline()))
10791 return false;
10792
10793 return true;
10794 }
10795
10796 // Merges the given attributes. If compareWith
10797 // is non-NULL, then it will be used to mask out those attributes that are the same in style
10798 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
10799 bool wxTextBoxAttr::Apply(const wxTextBoxAttr& attr, const wxTextBoxAttr* compareWith)
10800 {
10801 if (attr.HasFloatMode())
10802 {
10803 if (!(compareWith && compareWith->HasFloatMode() && compareWith->GetFloatMode() == attr.GetFloatMode()))
10804 SetFloatMode(attr.GetFloatMode());
10805 }
10806
10807 if (attr.HasClearMode())
10808 {
10809 if (!(compareWith && compareWith->HasClearMode() && compareWith->GetClearMode() == attr.GetClearMode()))
10810 SetClearMode(attr.GetClearMode());
10811 }
10812
10813 if (attr.HasCollapseBorders())
10814 {
10815 if (!(compareWith && compareWith->HasCollapseBorders() && compareWith->GetCollapseBorders() == attr.GetCollapseBorders()))
10816 SetCollapseBorders(attr.GetCollapseBorders());
10817 }
10818
10819 if (attr.HasVerticalAlignment())
10820 {
10821 if (!(compareWith && compareWith->HasVerticalAlignment() && compareWith->GetVerticalAlignment() == attr.GetVerticalAlignment()))
10822 SetVerticalAlignment(attr.GetVerticalAlignment());
10823 }
10824
10825 m_margins.Apply(attr.m_margins, compareWith ? (& attr.m_margins) : (const wxTextAttrDimensions*) NULL);
10826 m_padding.Apply(attr.m_padding, compareWith ? (& attr.m_padding) : (const wxTextAttrDimensions*) NULL);
10827 m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextAttrDimensions*) NULL);
10828
10829 m_size.Apply(attr.m_size, compareWith ? (& attr.m_size) : (const wxTextAttrSize*) NULL);
10830
10831 m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextAttrBorders*) NULL);
10832 m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextAttrBorders*) NULL);
10833
10834 return true;
10835 }
10836
10837 // Remove specified attributes from this object
10838 bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr& attr)
10839 {
10840 if (attr.HasFloatMode())
10841 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
10842
10843 if (attr.HasClearMode())
10844 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
10845
10846 if (attr.HasCollapseBorders())
10847 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
10848
10849 if (attr.HasVerticalAlignment())
10850 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
10851
10852 m_margins.RemoveStyle(attr.m_margins);
10853 m_padding.RemoveStyle(attr.m_padding);
10854 m_position.RemoveStyle(attr.m_position);
10855
10856 m_size.RemoveStyle(attr.m_size);
10857
10858 m_border.RemoveStyle(attr.m_border);
10859 m_outline.RemoveStyle(attr.m_outline);
10860
10861 return true;
10862 }
10863
10864 // Collects the attributes that are common to a range of content, building up a note of
10865 // which attributes are absent in some objects and which clash in some objects.
10866 void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBoxAttr& clashingAttr, wxTextBoxAttr& absentAttr)
10867 {
10868 if (attr.HasFloatMode())
10869 {
10870 if (!clashingAttr.HasFloatMode() && !absentAttr.HasFloatMode())
10871 {
10872 if (HasFloatMode())
10873 {
10874 if (GetFloatMode() != attr.GetFloatMode())
10875 {
10876 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
10877 RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
10878 }
10879 }
10880 else
10881 SetFloatMode(attr.GetFloatMode());
10882 }
10883 }
10884 else
10885 absentAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
10886
10887 if (attr.HasClearMode())
10888 {
10889 if (!clashingAttr.HasClearMode() && !absentAttr.HasClearMode())
10890 {
10891 if (HasClearMode())
10892 {
10893 if (GetClearMode() != attr.GetClearMode())
10894 {
10895 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
10896 RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
10897 }
10898 }
10899 else
10900 SetClearMode(attr.GetClearMode());
10901 }
10902 }
10903 else
10904 absentAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
10905
10906 if (attr.HasCollapseBorders())
10907 {
10908 if (!clashingAttr.HasCollapseBorders() && !absentAttr.HasCollapseBorders())
10909 {
10910 if (HasCollapseBorders())
10911 {
10912 if (GetCollapseBorders() != attr.GetCollapseBorders())
10913 {
10914 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
10915 RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
10916 }
10917 }
10918 else
10919 SetCollapseBorders(attr.GetCollapseBorders());
10920 }
10921 }
10922 else
10923 absentAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
10924
10925 if (attr.HasVerticalAlignment())
10926 {
10927 if (!clashingAttr.HasVerticalAlignment() && !absentAttr.HasVerticalAlignment())
10928 {
10929 if (HasVerticalAlignment())
10930 {
10931 if (GetVerticalAlignment() != attr.GetVerticalAlignment())
10932 {
10933 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
10934 RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
10935 }
10936 }
10937 else
10938 SetVerticalAlignment(attr.GetVerticalAlignment());
10939 }
10940 }
10941 else
10942 absentAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
10943
10944 m_margins.CollectCommonAttributes(attr.m_margins, clashingAttr.m_margins, absentAttr.m_margins);
10945 m_padding.CollectCommonAttributes(attr.m_padding, clashingAttr.m_padding, absentAttr.m_padding);
10946 m_position.CollectCommonAttributes(attr.m_position, clashingAttr.m_position, absentAttr.m_position);
10947
10948 m_size.CollectCommonAttributes(attr.m_size, clashingAttr.m_size, absentAttr.m_size);
10949
10950 m_border.CollectCommonAttributes(attr.m_border, clashingAttr.m_border, absentAttr.m_border);
10951 m_outline.CollectCommonAttributes(attr.m_outline, clashingAttr.m_outline, absentAttr.m_outline);
10952 }
10953
10954 // wxRichTextAttr
10955
10956 void wxRichTextAttr::Copy(const wxRichTextAttr& attr)
10957 {
10958 wxTextAttr::Copy(attr);
10959
10960 m_textBoxAttr = attr.m_textBoxAttr;
10961 }
10962
10963 bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const
10964 {
10965 if (!(wxTextAttr::operator==(attr)))
10966 return false;
10967
10968 return (m_textBoxAttr == attr.m_textBoxAttr);
10969 }
10970
10971 // Partial equality test taking comparison object into account
10972 bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr) const
10973 {
10974 if (!(wxTextAttr::EqPartial(attr)))
10975 return false;
10976
10977 return m_textBoxAttr.EqPartial(attr.m_textBoxAttr);
10978 }
10979
10980 // Merges the given attributes. If compareWith
10981 // is non-NULL, then it will be used to mask out those attributes that are the same in style
10982 // and compareWith, for situations where we don't want to explicitly set inherited attributes.
10983 bool wxRichTextAttr::Apply(const wxRichTextAttr& style, const wxRichTextAttr* compareWith)
10984 {
10985 wxTextAttr::Apply(style, compareWith);
10986
10987 return m_textBoxAttr.Apply(style.m_textBoxAttr, compareWith ? (& compareWith->m_textBoxAttr) : (const wxTextBoxAttr*) NULL);
10988 }
10989
10990 // Remove specified attributes from this object
10991 bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr& attr)
10992 {
10993 wxTextAttr::RemoveStyle(*this, attr);
10994
10995 return m_textBoxAttr.RemoveStyle(attr.m_textBoxAttr);
10996 }
10997
10998 // Collects the attributes that are common to a range of content, building up a note of
10999 // which attributes are absent in some objects and which clash in some objects.
11000 void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr& attr, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
11001 {
11002 wxTextAttrCollectCommonAttributes(*this, attr, clashingAttr, absentAttr);
11003
11004 m_textBoxAttr.CollectCommonAttributes(attr.m_textBoxAttr, clashingAttr.m_textBoxAttr, absentAttr.m_textBoxAttr);
11005 }
11006
11007 // Partial equality test
11008 bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border) const
11009 {
11010 if (border.HasStyle() && !HasStyle() && (border.GetStyle() != GetStyle()))
11011 return false;
11012
11013 if (border.HasColour() && !HasColour() && (border.GetColourLong() != GetColourLong()))
11014 return false;
11015
11016 if (border.HasWidth() && !HasWidth() && !(border.GetWidth() == GetWidth()))
11017 return false;
11018
11019 return true;
11020 }
11021
11022 // Apply border to 'this', but not if the same as compareWith
11023 bool wxTextAttrBorder::Apply(const wxTextAttrBorder& border, const wxTextAttrBorder* compareWith)
11024 {
11025 if (border.HasStyle())
11026 {
11027 if (!(compareWith && (border.GetStyle() == compareWith->GetStyle())))
11028 SetStyle(border.GetStyle());
11029 }
11030 if (border.HasColour())
11031 {
11032 if (!(compareWith && (border.GetColourLong() == compareWith->GetColourLong())))
11033 SetColour(border.GetColourLong());
11034 }
11035 if (border.HasWidth())
11036 {
11037 if (!(compareWith && (border.GetWidth() == compareWith->GetWidth())))
11038 SetWidth(border.GetWidth());
11039 }
11040
11041 return true;
11042 }
11043
11044 // Remove specified attributes from this object
11045 bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder& attr)
11046 {
11047 if (attr.HasStyle() && HasStyle())
11048 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE);
11049 if (attr.HasColour() && HasColour())
11050 SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR);
11051 if (attr.HasWidth() && HasWidth())
11052 m_borderWidth.Reset();
11053
11054 return true;
11055 }
11056
11057 // Collects the attributes that are common to a range of content, building up a note of
11058 // which attributes are absent in some objects and which clash in some objects.
11059 void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder& attr, wxTextAttrBorder& clashingAttr, wxTextAttrBorder& absentAttr)
11060 {
11061 if (attr.HasStyle())
11062 {
11063 if (!clashingAttr.HasStyle() && !absentAttr.HasStyle())
11064 {
11065 if (HasStyle())
11066 {
11067 if (GetStyle() != attr.GetStyle())
11068 {
11069 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
11070 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
11071 }
11072 }
11073 else
11074 SetStyle(attr.GetStyle());
11075 }
11076 }
11077 else
11078 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
11079
11080 if (attr.HasColour())
11081 {
11082 if (!clashingAttr.HasColour() && !absentAttr.HasColour())
11083 {
11084 if (HasColour())
11085 {
11086 if (GetColour() != attr.GetColour())
11087 {
11088 clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
11089 RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
11090 }
11091 }
11092 else
11093 SetColour(attr.GetColourLong());
11094 }
11095 }
11096 else
11097 absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
11098
11099 m_borderWidth.CollectCommonAttributes(attr.m_borderWidth, clashingAttr.m_borderWidth, absentAttr.m_borderWidth);
11100 }
11101
11102 // Partial equality test
11103 bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders) const
11104 {
11105 return m_left.EqPartial(borders.m_left) && m_right.EqPartial(borders.m_right) &&
11106 m_top.EqPartial(borders.m_top) && m_bottom.EqPartial(borders.m_bottom);
11107 }
11108
11109 // Apply border to 'this', but not if the same as compareWith
11110 bool wxTextAttrBorders::Apply(const wxTextAttrBorders& borders, const wxTextAttrBorders* compareWith)
11111 {
11112 m_left.Apply(borders.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrBorder*) NULL);
11113 m_right.Apply(borders.m_right, compareWith ? (& compareWith->m_right) : (const wxTextAttrBorder*) NULL);
11114 m_top.Apply(borders.m_top, compareWith ? (& compareWith->m_top) : (const wxTextAttrBorder*) NULL);
11115 m_bottom.Apply(borders.m_bottom, compareWith ? (& compareWith->m_bottom) : (const wxTextAttrBorder*) NULL);
11116 return true;
11117 }
11118
11119 // Remove specified attributes from this object
11120 bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders& attr)
11121 {
11122 m_left.RemoveStyle(attr.m_left);
11123 m_right.RemoveStyle(attr.m_right);
11124 m_top.RemoveStyle(attr.m_top);
11125 m_bottom.RemoveStyle(attr.m_bottom);
11126 return true;
11127 }
11128
11129 // Collects the attributes that are common to a range of content, building up a note of
11130 // which attributes are absent in some objects and which clash in some objects.
11131 void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders& attr, wxTextAttrBorders& clashingAttr, wxTextAttrBorders& absentAttr)
11132 {
11133 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
11134 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
11135 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
11136 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
11137 }
11138
11139 // Set style of all borders
11140 void wxTextAttrBorders::SetStyle(int style)
11141 {
11142 m_left.SetStyle(style);
11143 m_right.SetStyle(style);
11144 m_top.SetStyle(style);
11145 m_bottom.SetStyle(style);
11146 }
11147
11148 // Set colour of all borders
11149 void wxTextAttrBorders::SetColour(unsigned long colour)
11150 {
11151 m_left.SetColour(colour);
11152 m_right.SetColour(colour);
11153 m_top.SetColour(colour);
11154 m_bottom.SetColour(colour);
11155 }
11156
11157 void wxTextAttrBorders::SetColour(const wxColour& colour)
11158 {
11159 m_left.SetColour(colour);
11160 m_right.SetColour(colour);
11161 m_top.SetColour(colour);
11162 m_bottom.SetColour(colour);
11163 }
11164
11165 // Set width of all borders
11166 void wxTextAttrBorders::SetWidth(const wxTextAttrDimension& width)
11167 {
11168 m_left.SetWidth(width);
11169 m_right.SetWidth(width);
11170 m_top.SetWidth(width);
11171 m_bottom.SetWidth(width);
11172 }
11173
11174 // Partial equality test
11175 bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension& dim) const
11176 {
11177 if (dim.IsValid() && IsValid() && !((*this) == dim))
11178 return false;
11179 else
11180 return true;
11181 }
11182
11183 bool wxTextAttrDimension::Apply(const wxTextAttrDimension& dim, const wxTextAttrDimension* compareWith)
11184 {
11185 if (dim.IsValid())
11186 {
11187 if (!(compareWith && dim == (*compareWith)))
11188 (*this) = dim;
11189 }
11190
11191 return true;
11192 }
11193
11194 // Collects the attributes that are common to a range of content, building up a note of
11195 // which attributes are absent in some objects and which clash in some objects.
11196 void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension& attr, wxTextAttrDimension& clashingAttr, wxTextAttrDimension& absentAttr)
11197 {
11198 if (attr.IsValid())
11199 {
11200 if (!clashingAttr.IsValid() && !absentAttr.IsValid())
11201 {
11202 if (IsValid())
11203 {
11204 if (!((*this) == attr))
11205 {
11206 clashingAttr.SetValid(true);
11207 SetValid(false);
11208 }
11209 }
11210 else
11211 (*this) = attr;
11212 }
11213 }
11214 else
11215 absentAttr.SetValid(true);
11216 }
11217
11218 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC& dc, double scale, const wxSize& parentSize)
11219 {
11220 m_ppi = dc.GetPPI().x; m_scale = scale; m_parentSize = parentSize;
11221 }
11222
11223 wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi, double scale, const wxSize& parentSize)
11224 {
11225 m_ppi = ppi; m_scale = scale; m_parentSize = parentSize;
11226 }
11227
11228 int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units) const
11229 {
11230 return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi, units, m_scale);
11231 }
11232
11233 int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels) const
11234 {
11235 return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi, pixels, m_scale);
11236 }
11237
11238 int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension& dim, int direction) const
11239 {
11240 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11241 return ConvertTenthsMMToPixels(dim.GetValue());
11242 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11243 return dim.GetValue();
11244 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
11245 {
11246 wxASSERT(m_parentSize != wxDefaultSize);
11247 if (direction == wxHORIZONTAL)
11248 return (int) (double(m_parentSize.x) * double(dim.GetValue()) / 100.0);
11249 else
11250 return (int) (double(m_parentSize.y) * double(dim.GetValue()) / 100.0);
11251 }
11252 else
11253 {
11254 wxASSERT(false);
11255 return 0;
11256 }
11257 }
11258
11259 int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension& dim) const
11260 {
11261 if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
11262 return dim.GetValue();
11263 else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
11264 return ConvertPixelsToTenthsMM(dim.GetValue());
11265 else
11266 {
11267 wxASSERT(false);
11268 return 0;
11269 }
11270 }
11271
11272 // Partial equality test
11273 bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims) const
11274 {
11275 if (!m_left.EqPartial(dims.m_left))
11276 return false;
11277
11278 if (!m_right.EqPartial(dims.m_right))
11279 return false;
11280
11281 if (!m_top.EqPartial(dims.m_top))
11282 return false;
11283
11284 if (!m_bottom.EqPartial(dims.m_bottom))
11285 return false;
11286
11287 return true;
11288 }
11289
11290 // Apply border to 'this', but not if the same as compareWith
11291 bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions& dims, const wxTextAttrDimensions* compareWith)
11292 {
11293 m_left.Apply(dims.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrDimension*) NULL);
11294 m_right.Apply(dims.m_right, compareWith ? (& compareWith->m_right): (const wxTextAttrDimension*) NULL);
11295 m_top.Apply(dims.m_top, compareWith ? (& compareWith->m_top): (const wxTextAttrDimension*) NULL);
11296 m_bottom.Apply(dims.m_bottom, compareWith ? (& compareWith->m_bottom): (const wxTextAttrDimension*) NULL);
11297
11298 return true;
11299 }
11300
11301 // Remove specified attributes from this object
11302 bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions& attr)
11303 {
11304 if (attr.m_left.IsValid())
11305 m_left.Reset();
11306 if (attr.m_right.IsValid())
11307 m_right.Reset();
11308 if (attr.m_top.IsValid())
11309 m_top.Reset();
11310 if (attr.m_bottom.IsValid())
11311 m_bottom.Reset();
11312
11313 return true;
11314 }
11315
11316 // Collects the attributes that are common to a range of content, building up a note of
11317 // which attributes are absent in some objects and which clash in some objects.
11318 void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions& attr, wxTextAttrDimensions& clashingAttr, wxTextAttrDimensions& absentAttr)
11319 {
11320 m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
11321 m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
11322 m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
11323 m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
11324 }
11325
11326 // Partial equality test
11327 bool wxTextAttrSize::EqPartial(const wxTextAttrSize& size) const
11328 {
11329 if (!m_width.EqPartial(size.m_width))
11330 return false;
11331
11332 if (!m_height.EqPartial(size.m_height))
11333 return false;
11334
11335 return true;
11336 }
11337
11338 // Apply border to 'this', but not if the same as compareWith
11339 bool wxTextAttrSize::Apply(const wxTextAttrSize& size, const wxTextAttrSize* compareWith)
11340 {
11341 m_width.Apply(size.m_width, compareWith ? (& compareWith->m_width) : (const wxTextAttrDimension*) NULL);
11342 m_height.Apply(size.m_height, compareWith ? (& compareWith->m_height): (const wxTextAttrDimension*) NULL);
11343
11344 return true;
11345 }
11346
11347 // Remove specified attributes from this object
11348 bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize& attr)
11349 {
11350 if (attr.m_width.IsValid())
11351 m_width.Reset();
11352 if (attr.m_height.IsValid())
11353 m_height.Reset();
11354
11355 return true;
11356 }
11357
11358 // Collects the attributes that are common to a range of content, building up a note of
11359 // which attributes are absent in some objects and which clash in some objects.
11360 void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize& attr, wxTextAttrSize& clashingAttr, wxTextAttrSize& absentAttr)
11361 {
11362 m_width.CollectCommonAttributes(attr.m_width, clashingAttr.m_width, absentAttr.m_width);
11363 m_height.CollectCommonAttributes(attr.m_height, clashingAttr.m_height, absentAttr.m_height);
11364 }
11365
11366 // Collects the attributes that are common to a range of content, building up a note of
11367 // which attributes are absent in some objects and which clash in some objects.
11368 void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAttr& attr, wxTextAttr& clashingAttr, wxTextAttr& absentAttr)
11369 {
11370 absentAttr.SetFlags(absentAttr.GetFlags() | (~attr.GetFlags() & wxTEXT_ATTR_ALL));
11371 absentAttr.SetTextEffectFlags(absentAttr.GetTextEffectFlags() | (~attr.GetTextEffectFlags() & 0xFFFF));
11372
11373 long forbiddenFlags = clashingAttr.GetFlags()|absentAttr.GetFlags();
11374
11375 if (attr.HasFont())
11376 {
11377 if (attr.HasFontSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_SIZE))
11378 {
11379 if (currentStyle.HasFontSize())
11380 {
11381 if (currentStyle.GetFontSize() != attr.GetFontSize())
11382 {
11383 // Clash of attr - mark as such
11384 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE);
11385 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE);
11386 }
11387 }
11388 else
11389 currentStyle.SetFontSize(attr.GetFontSize());
11390 }
11391
11392 if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC))
11393 {
11394 if (currentStyle.HasFontItalic())
11395 {
11396 if (currentStyle.GetFontStyle() != attr.GetFontStyle())
11397 {
11398 // Clash of attr - mark as such
11399 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
11400 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
11401 }
11402 }
11403 else
11404 currentStyle.SetFontStyle(attr.GetFontStyle());
11405 }
11406
11407 if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY))
11408 {
11409 if (currentStyle.HasFontFamily())
11410 {
11411 if (currentStyle.GetFontFamily() != attr.GetFontFamily())
11412 {
11413 // Clash of attr - mark as such
11414 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
11415 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
11416 }
11417 }
11418 else
11419 currentStyle.SetFontFamily(attr.GetFontFamily());
11420 }
11421
11422 if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT))
11423 {
11424 if (currentStyle.HasFontWeight())
11425 {
11426 if (currentStyle.GetFontWeight() != attr.GetFontWeight())
11427 {
11428 // Clash of attr - mark as such
11429 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
11430 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
11431 }
11432 }
11433 else
11434 currentStyle.SetFontWeight(attr.GetFontWeight());
11435 }
11436
11437 if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE))
11438 {
11439 if (currentStyle.HasFontFaceName())
11440 {
11441 wxString faceName1(currentStyle.GetFontFaceName());
11442 wxString faceName2(attr.GetFontFaceName());
11443
11444 if (faceName1 != faceName2)
11445 {
11446 // Clash of attr - mark as such
11447 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
11448 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
11449 }
11450 }
11451 else
11452 currentStyle.SetFontFaceName(attr.GetFontFaceName());
11453 }
11454
11455 if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE))
11456 {
11457 if (currentStyle.HasFontUnderlined())
11458 {
11459 if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined())
11460 {
11461 // Clash of attr - mark as such
11462 clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
11463 currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
11464 }
11465 }
11466 else
11467 currentStyle.SetFontUnderlined(attr.GetFontUnderlined());
11468 }
11469 }
11470
11471 if (attr.HasTextColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TEXT_COLOUR))
11472 {
11473 if (currentStyle.HasTextColour())
11474 {
11475 if (currentStyle.GetTextColour() != attr.GetTextColour())
11476 {
11477 // Clash of attr - mark as such
11478 clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
11479 currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
11480 }
11481 }
11482 else
11483 currentStyle.SetTextColour(attr.GetTextColour());
11484 }
11485
11486 if (attr.HasBackgroundColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BACKGROUND_COLOUR))
11487 {
11488 if (currentStyle.HasBackgroundColour())
11489 {
11490 if (currentStyle.GetBackgroundColour() != attr.GetBackgroundColour())
11491 {
11492 // Clash of attr - mark as such
11493 clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
11494 currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
11495 }
11496 }
11497 else
11498 currentStyle.SetBackgroundColour(attr.GetBackgroundColour());
11499 }
11500
11501 if (attr.HasAlignment() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_ALIGNMENT))
11502 {
11503 if (currentStyle.HasAlignment())
11504 {
11505 if (currentStyle.GetAlignment() != attr.GetAlignment())
11506 {
11507 // Clash of attr - mark as such
11508 clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
11509 currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
11510 }
11511 }
11512 else
11513 currentStyle.SetAlignment(attr.GetAlignment());
11514 }
11515
11516 if (attr.HasTabs() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TABS))
11517 {
11518 if (currentStyle.HasTabs())
11519 {
11520 if (!wxRichTextTabsEq(currentStyle.GetTabs(), attr.GetTabs()))
11521 {
11522 // Clash of attr - mark as such
11523 clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
11524 currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
11525 }
11526 }
11527 else
11528 currentStyle.SetTabs(attr.GetTabs());
11529 }
11530
11531 if (attr.HasLeftIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LEFT_INDENT))
11532 {
11533 if (currentStyle.HasLeftIndent())
11534 {
11535 if (currentStyle.GetLeftIndent() != attr.GetLeftIndent() || currentStyle.GetLeftSubIndent() != attr.GetLeftSubIndent())
11536 {
11537 // Clash of attr - mark as such
11538 clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
11539 currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
11540 }
11541 }
11542 else
11543 currentStyle.SetLeftIndent(attr.GetLeftIndent(), attr.GetLeftSubIndent());
11544 }
11545
11546 if (attr.HasRightIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_RIGHT_INDENT))
11547 {
11548 if (currentStyle.HasRightIndent())
11549 {
11550 if (currentStyle.GetRightIndent() != attr.GetRightIndent())
11551 {
11552 // Clash of attr - mark as such
11553 clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
11554 currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
11555 }
11556 }
11557 else
11558 currentStyle.SetRightIndent(attr.GetRightIndent());
11559 }
11560
11561 if (attr.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_AFTER))
11562 {
11563 if (currentStyle.HasParagraphSpacingAfter())
11564 {
11565 if (currentStyle.GetParagraphSpacingAfter() != attr.GetParagraphSpacingAfter())
11566 {
11567 // Clash of attr - mark as such
11568 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
11569 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
11570 }
11571 }
11572 else
11573 currentStyle.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter());
11574 }
11575
11576 if (attr.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_BEFORE))
11577 {
11578 if (currentStyle.HasParagraphSpacingBefore())
11579 {
11580 if (currentStyle.GetParagraphSpacingBefore() != attr.GetParagraphSpacingBefore())
11581 {
11582 // Clash of attr - mark as such
11583 clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
11584 currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
11585 }
11586 }
11587 else
11588 currentStyle.SetParagraphSpacingBefore(attr.GetParagraphSpacingBefore());
11589 }
11590
11591 if (attr.HasLineSpacing() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LINE_SPACING))
11592 {
11593 if (currentStyle.HasLineSpacing())
11594 {
11595 if (currentStyle.GetLineSpacing() != attr.GetLineSpacing())
11596 {
11597 // Clash of attr - mark as such
11598 clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
11599 currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
11600 }
11601 }
11602 else
11603 currentStyle.SetLineSpacing(attr.GetLineSpacing());
11604 }
11605
11606 if (attr.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
11607 {
11608 if (currentStyle.HasCharacterStyleName())
11609 {
11610 if (currentStyle.GetCharacterStyleName() != attr.GetCharacterStyleName())
11611 {
11612 // Clash of attr - mark as such
11613 clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
11614 currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
11615 }
11616 }
11617 else
11618 currentStyle.SetCharacterStyleName(attr.GetCharacterStyleName());
11619 }
11620
11621 if (attr.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
11622 {
11623 if (currentStyle.HasParagraphStyleName())
11624 {
11625 if (currentStyle.GetParagraphStyleName() != attr.GetParagraphStyleName())
11626 {
11627 // Clash of attr - mark as such
11628 clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
11629 currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
11630 }
11631 }
11632 else
11633 currentStyle.SetParagraphStyleName(attr.GetParagraphStyleName());
11634 }
11635
11636 if (attr.HasListStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LIST_STYLE_NAME))
11637 {
11638 if (currentStyle.HasListStyleName())
11639 {
11640 if (currentStyle.GetListStyleName() != attr.GetListStyleName())
11641 {
11642 // Clash of attr - mark as such
11643 clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
11644 currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
11645 }
11646 }
11647 else
11648 currentStyle.SetListStyleName(attr.GetListStyleName());
11649 }
11650
11651 if (attr.HasBulletStyle() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_STYLE))
11652 {
11653 if (currentStyle.HasBulletStyle())
11654 {
11655 if (currentStyle.GetBulletStyle() != attr.GetBulletStyle())
11656 {
11657 // Clash of attr - mark as such
11658 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
11659 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
11660 }
11661 }
11662 else
11663 currentStyle.SetBulletStyle(attr.GetBulletStyle());
11664 }
11665
11666 if (attr.HasBulletNumber() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NUMBER))
11667 {
11668 if (currentStyle.HasBulletNumber())
11669 {
11670 if (currentStyle.GetBulletNumber() != attr.GetBulletNumber())
11671 {
11672 // Clash of attr - mark as such
11673 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
11674 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
11675 }
11676 }
11677 else
11678 currentStyle.SetBulletNumber(attr.GetBulletNumber());
11679 }
11680
11681 if (attr.HasBulletText() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_TEXT))
11682 {
11683 if (currentStyle.HasBulletText())
11684 {
11685 if (currentStyle.GetBulletText() != attr.GetBulletText())
11686 {
11687 // Clash of attr - mark as such
11688 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
11689 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
11690 }
11691 }
11692 else
11693 {
11694 currentStyle.SetBulletText(attr.GetBulletText());
11695 currentStyle.SetBulletFont(attr.GetBulletFont());
11696 }
11697 }
11698
11699 if (attr.HasBulletName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NAME))
11700 {
11701 if (currentStyle.HasBulletName())
11702 {
11703 if (currentStyle.GetBulletName() != attr.GetBulletName())
11704 {
11705 // Clash of attr - mark as such
11706 clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
11707 currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
11708 }
11709 }
11710 else
11711 {
11712 currentStyle.SetBulletName(attr.GetBulletName());
11713 }
11714 }
11715
11716 if (attr.HasURL() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_URL))
11717 {
11718 if (currentStyle.HasURL())
11719 {
11720 if (currentStyle.GetURL() != attr.GetURL())
11721 {
11722 // Clash of attr - mark as such
11723 clashingAttr.AddFlag(wxTEXT_ATTR_URL);
11724 currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
11725 }
11726 }
11727 else
11728 {
11729 currentStyle.SetURL(attr.GetURL());
11730 }
11731 }
11732
11733 if (attr.HasTextEffects() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_EFFECTS))
11734 {
11735 if (currentStyle.HasTextEffects())
11736 {
11737 // We need to find the bits in the new attr that are different:
11738 // just look at those bits that are specified by the new attr.
11739
11740 // We need to remove the bits and flags that are not common between current attr
11741 // and new attr. In so doing we need to take account of the styles absent from one or more of the
11742 // previous styles.
11743
11744 int currentRelevantTextEffects = currentStyle.GetTextEffects() & attr.GetTextEffectFlags();
11745 int newRelevantTextEffects = attr.GetTextEffects() & attr.GetTextEffectFlags();
11746
11747 if (currentRelevantTextEffects != newRelevantTextEffects)
11748 {
11749 // Find the text effects that were different, using XOR
11750 int differentEffects = currentRelevantTextEffects ^ newRelevantTextEffects;
11751
11752 // Clash of attr - mark as such
11753 clashingAttr.SetTextEffectFlags(clashingAttr.GetTextEffectFlags() | differentEffects);
11754 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~differentEffects);
11755 }
11756 }
11757 else
11758 {
11759 currentStyle.SetTextEffects(attr.GetTextEffects());
11760 currentStyle.SetTextEffectFlags(attr.GetTextEffectFlags());
11761 }
11762
11763 // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
11764 // that we've looked at so far
11765 currentStyle.SetTextEffects(currentStyle.GetTextEffects() & ~absentAttr.GetTextEffectFlags());
11766 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~absentAttr.GetTextEffectFlags());
11767
11768 if (currentStyle.GetTextEffectFlags() == 0)
11769 currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
11770 }
11771
11772 if (attr.HasOutlineLevel() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_OUTLINE_LEVEL))
11773 {
11774 if (currentStyle.HasOutlineLevel())
11775 {
11776 if (currentStyle.GetOutlineLevel() != attr.GetOutlineLevel())
11777 {
11778 // Clash of attr - mark as such
11779 clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
11780 currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
11781 }
11782 }
11783 else
11784 currentStyle.SetOutlineLevel(attr.GetOutlineLevel());
11785 }
11786 }
11787
11788 WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
11789
11790 IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
11791
11792 bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const
11793 {
11794 if (m_properties.GetCount() != props.GetCount())
11795 return false;
11796
11797 size_t i;
11798 for (i = 0; i < m_properties.GetCount(); i++)
11799 {
11800 const wxVariant& var1 = m_properties[i];
11801 int idx = props.Find(var1.GetName());
11802 if (idx == -1)
11803 return false;
11804 const wxVariant& var2 = props.m_properties[idx];
11805 if (!(var1 == var2))
11806 return false;
11807 }
11808
11809 return true;
11810 }
11811
11812 wxArrayString wxRichTextProperties::GetPropertyNames() const
11813 {
11814 wxArrayString arr;
11815 size_t i;
11816 for (i = 0; i < m_properties.GetCount(); i++)
11817 {
11818 arr.Add(m_properties[i].GetName());
11819 }
11820 return arr;
11821 }
11822
11823 int wxRichTextProperties::Find(const wxString& name) const
11824 {
11825 size_t i;
11826 for (i = 0; i < m_properties.GetCount(); i++)
11827 {
11828 if (m_properties[i].GetName() == name)
11829 return (int) i;
11830 }
11831 return -1;
11832 }
11833
11834 wxVariant* wxRichTextProperties::FindOrCreateProperty(const wxString& name)
11835 {
11836 int idx = Find(name);
11837 if (idx == wxNOT_FOUND)
11838 SetProperty(name, wxString());
11839 idx = Find(name);
11840 if (idx != wxNOT_FOUND)
11841 {
11842 return & (*this)[idx];
11843 }
11844 else
11845 return NULL;
11846 }
11847
11848 const wxVariant& wxRichTextProperties::GetProperty(const wxString& name) const
11849 {
11850 static const wxVariant nullVariant;
11851 int idx = Find(name);
11852 if (idx != -1)
11853 return m_properties[idx];
11854 else
11855 return nullVariant;
11856 }
11857
11858 wxString wxRichTextProperties::GetPropertyString(const wxString& name) const
11859 {
11860 return GetProperty(name).GetString();
11861 }
11862
11863 long wxRichTextProperties::GetPropertyLong(const wxString& name) const
11864 {
11865 return GetProperty(name).GetLong();
11866 }
11867
11868 bool wxRichTextProperties::GetPropertyBool(const wxString& name) const
11869 {
11870 return GetProperty(name).GetBool();
11871 }
11872
11873 double wxRichTextProperties::GetPropertyDouble(const wxString& name) const
11874 {
11875 return GetProperty(name).GetDouble();
11876 }
11877
11878 void wxRichTextProperties::SetProperty(const wxVariant& variant)
11879 {
11880 wxASSERT(!variant.GetName().IsEmpty());
11881
11882 int idx = Find(variant.GetName());
11883
11884 if (idx == -1)
11885 m_properties.Add(variant);
11886 else
11887 m_properties[idx] = variant;
11888 }
11889
11890 void wxRichTextProperties::SetProperty(const wxString& name, const wxVariant& variant)
11891 {
11892 int idx = Find(name);
11893 wxVariant var(variant);
11894 var.SetName(name);
11895
11896 if (idx == -1)
11897 m_properties.Add(var);
11898 else
11899 m_properties[idx] = var;
11900 }
11901
11902 void wxRichTextProperties::SetProperty(const wxString& name, const wxString& value)
11903 {
11904 SetProperty(name, wxVariant(value, name));
11905 }
11906
11907 void wxRichTextProperties::SetProperty(const wxString& name, long value)
11908 {
11909 SetProperty(name, wxVariant(value, name));
11910 }
11911
11912 void wxRichTextProperties::SetProperty(const wxString& name, double value)
11913 {
11914 SetProperty(name, wxVariant(value, name));
11915 }
11916
11917 void wxRichTextProperties::SetProperty(const wxString& name, bool value)
11918 {
11919 SetProperty(name, wxVariant(value, name));
11920 }
11921
11922 wxRichTextObject* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox* topLevelContainer) const
11923 {
11924 if (m_address.GetCount() == 0)
11925 return topLevelContainer;
11926
11927 wxRichTextCompositeObject* p = topLevelContainer;
11928 size_t i = 0;
11929 while (p && i < m_address.GetCount())
11930 {
11931 int pos = m_address[i];
11932 wxASSERT(pos >= 0 && pos < (int) p->GetChildren().GetCount());
11933 if (pos < 0 || pos >= (int) p->GetChildren().GetCount())
11934 return NULL;
11935
11936 wxRichTextObject* p1 = p->GetChild(pos);
11937 if (i == (m_address.GetCount()-1))
11938 return p1;
11939
11940 p = wxDynamicCast(p1, wxRichTextCompositeObject);
11941 i ++;
11942 }
11943 return NULL;
11944 }
11945
11946 bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox* topLevelContainer, wxRichTextObject* obj)
11947 {
11948 m_address.Clear();
11949
11950 if (topLevelContainer == obj)
11951 return true;
11952
11953 wxRichTextObject* o = obj;
11954 while (o)
11955 {
11956 wxRichTextCompositeObject* p = wxDynamicCast(o->GetParent(), wxRichTextCompositeObject);
11957 if (!p)
11958 return false;
11959
11960 int pos = p->GetChildren().IndexOf(o);
11961 if (pos == -1)
11962 return false;
11963
11964 m_address.Insert(pos, 0);
11965
11966 if (p == topLevelContainer)
11967 return true;
11968
11969 o = p;
11970 }
11971 return false;
11972 }
11973
11974 // Equality test
11975 bool wxRichTextSelection::operator==(const wxRichTextSelection& sel) const
11976 {
11977 if (m_container != sel.m_container)
11978 return false;
11979 if (m_ranges.GetCount() != sel.m_ranges.GetCount())
11980 return false;
11981 size_t i;
11982 for (i = 0; i < m_ranges.GetCount(); i++)
11983 if (!(m_ranges[i] == sel.m_ranges[i]))
11984 return false;
11985 return true;
11986 }
11987
11988 // Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
11989 // or none at the level of the object's container.
11990 wxRichTextRangeArray wxRichTextSelection::GetSelectionForObject(wxRichTextObject* obj) const
11991 {
11992 if (IsValid())
11993 {
11994 wxRichTextParagraphLayoutBox* container = obj->GetParentContainer();
11995
11996 if (container == m_container)
11997 return m_ranges;
11998
11999 container = obj->GetContainer();
12000 while (container)
12001 {
12002 if (container->GetParent())
12003 {
12004 // If we found that our object's container is within the range of
12005 // a selection higher up, then assume the whole original object
12006 // is also selected.
12007 wxRichTextParagraphLayoutBox* parentContainer = container->GetParentContainer();
12008 if (parentContainer == m_container)
12009 {
12010 if (WithinSelection(container->GetRange().GetStart(), m_ranges))
12011 {
12012 wxRichTextRangeArray ranges;
12013 ranges.Add(obj->GetRange());
12014 return ranges;
12015 }
12016 }
12017
12018 container = parentContainer;
12019 }
12020 else
12021 {
12022 container = NULL;
12023 break;
12024 }
12025 }
12026 }
12027 return wxRichTextRangeArray();
12028 }
12029
12030 // Is the given position within the selection?
12031 bool wxRichTextSelection::WithinSelection(long pos, wxRichTextObject* obj) const
12032 {
12033 if (!IsValid())
12034 return false;
12035 else
12036 {
12037 wxRichTextRangeArray selectionRanges = GetSelectionForObject(obj);
12038 return WithinSelection(pos, selectionRanges);
12039 }
12040 }
12041
12042 // Is the given position within the selection range?
12043 bool wxRichTextSelection::WithinSelection(long pos, const wxRichTextRangeArray& ranges)
12044 {
12045 size_t i;
12046 for (i = 0; i < ranges.GetCount(); i++)
12047 {
12048 const wxRichTextRange& range = ranges[i];
12049 if (pos >= range.GetStart() && pos <= range.GetEnd())
12050 return true;
12051 }
12052 return false;
12053 }
12054
12055 // Is the given range completely within the selection range?
12056 bool wxRichTextSelection::WithinSelection(const wxRichTextRange& range, const wxRichTextRangeArray& ranges)
12057 {
12058 size_t i;
12059 for (i = 0; i < ranges.GetCount(); i++)
12060 {
12061 const wxRichTextRange& eachRange = ranges[i];
12062 if (range.IsWithin(eachRange))
12063 return true;
12064 }
12065 return false;
12066 }
12067
12068
12069 #endif
12070 // wxUSE_RICHTEXT