wxTinderbox build fixes.
[wxWidgets.git] / src / richtext / richtextbuffer.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: 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 #ifndef WX_PRECOMP
20 #include "wx/wx.h"
21 #endif
22
23 #include "wx/image.h"
24
25 #if wxUSE_RICHTEXT
26
27 #include "wx/filename.h"
28 #include "wx/clipbrd.h"
29 #include "wx/wfstream.h"
30 #include "wx/module.h"
31 #include "wx/mstream.h"
32 #include "wx/sstream.h"
33
34 #include "wx/richtext/richtextbuffer.h"
35 #include "wx/richtext/richtextctrl.h"
36 #include "wx/richtext/richtextstyles.h"
37
38 #include "wx/listimpl.cpp"
39
40 WX_DEFINE_LIST(wxRichTextObjectList);
41 WX_DEFINE_LIST(wxRichTextLineList);
42
43 /*!
44 * wxRichTextObject
45 * This is the base for drawable objects.
46 */
47
48 IMPLEMENT_CLASS(wxRichTextObject, wxObject)
49
50 wxRichTextObject::wxRichTextObject(wxRichTextObject* parent)
51 {
52 m_dirty = false;
53 m_refCount = 1;
54 m_parent = parent;
55 m_leftMargin = 0;
56 m_rightMargin = 0;
57 m_topMargin = 0;
58 m_bottomMargin = 0;
59 m_descent = 0;
60 }
61
62 wxRichTextObject::~wxRichTextObject()
63 {
64 }
65
66 void wxRichTextObject::Dereference()
67 {
68 m_refCount --;
69 if (m_refCount <= 0)
70 delete this;
71 }
72
73 /// Copy
74 void wxRichTextObject::Copy(const wxRichTextObject& obj)
75 {
76 m_size = obj.m_size;
77 m_pos = obj.m_pos;
78 m_dirty = obj.m_dirty;
79 m_range = obj.m_range;
80 m_attributes = obj.m_attributes;
81 m_descent = obj.m_descent;
82
83 if (!m_attributes.GetFont().Ok())
84 wxLogDebug(wxT("No font!"));
85 if (!obj.m_attributes.GetFont().Ok())
86 wxLogDebug(wxT("Parent has no font!"));
87 }
88
89 void wxRichTextObject::SetMargins(int margin)
90 {
91 m_leftMargin = m_rightMargin = m_topMargin = m_bottomMargin = margin;
92 }
93
94 void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin, int bottomMargin)
95 {
96 m_leftMargin = leftMargin;
97 m_rightMargin = rightMargin;
98 m_topMargin = topMargin;
99 m_bottomMargin = bottomMargin;
100 }
101
102 // Convert units in tends of a millimetre to device units
103 int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units)
104 {
105 int ppi = dc.GetPPI().x;
106
107 // There are ppi pixels in 254.1 "1/10 mm"
108
109 double pixels = ((double) units * (double)ppi) / 254.1;
110
111 return (int) pixels;
112 }
113
114 /// Dump to output stream for debugging
115 void wxRichTextObject::Dump(wxTextOutputStream& stream)
116 {
117 stream << GetClassInfo()->GetClassName() << wxT("\n");
118 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");
119 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");
120 }
121
122
123 /*!
124 * wxRichTextCompositeObject
125 * This is the base for drawable objects.
126 */
127
128 IMPLEMENT_CLASS(wxRichTextCompositeObject, wxRichTextObject)
129
130 wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject* parent):
131 wxRichTextObject(parent)
132 {
133 }
134
135 wxRichTextCompositeObject::~wxRichTextCompositeObject()
136 {
137 DeleteChildren();
138 }
139
140 /// Get the nth child
141 wxRichTextObject* wxRichTextCompositeObject::GetChild(size_t n) const
142 {
143 wxASSERT ( n < m_children.GetCount() );
144
145 return m_children.Item(n)->GetData();
146 }
147
148 /// Append a child, returning the position
149 size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject* child)
150 {
151 m_children.Append(child);
152 child->SetParent(this);
153 return m_children.GetCount() - 1;
154 }
155
156 /// Insert the child in front of the given object, or at the beginning
157 bool wxRichTextCompositeObject::InsertChild(wxRichTextObject* child, wxRichTextObject* inFrontOf)
158 {
159 if (inFrontOf)
160 {
161 wxRichTextObjectList::compatibility_iterator node = m_children.Find(inFrontOf);
162 m_children.Insert(node, child);
163 }
164 else
165 m_children.Insert(child);
166 child->SetParent(this);
167
168 return true;
169 }
170
171 /// Delete the child
172 bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject* child, bool deleteChild)
173 {
174 wxRichTextObjectList::compatibility_iterator node = m_children.Find(child);
175 if (node)
176 {
177 if (deleteChild)
178 delete node->GetData();
179 delete node;
180
181 return true;
182 }
183 return false;
184 }
185
186 /// Delete all children
187 bool wxRichTextCompositeObject::DeleteChildren()
188 {
189 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
190 while (node)
191 {
192 wxRichTextObjectList::compatibility_iterator oldNode = node;
193
194 wxRichTextObject* child = node->GetData();
195 child->Dereference(); // Only delete if reference count is zero
196
197 node = node->GetNext();
198 delete oldNode;
199 }
200
201 return true;
202 }
203
204 /// Get the child count
205 size_t wxRichTextCompositeObject::GetChildCount() const
206 {
207 return m_children.GetCount();
208 }
209
210 /// Copy
211 void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject& obj)
212 {
213 wxRichTextObject::Copy(obj);
214
215 DeleteChildren();
216
217 wxRichTextObjectList::compatibility_iterator node = obj.m_children.GetFirst();
218 while (node)
219 {
220 wxRichTextObject* child = node->GetData();
221 m_children.Append(child->Clone());
222
223 node = node->GetNext();
224 }
225 }
226
227 /// Hit-testing: returns a flag indicating hit test details, plus
228 /// information about position
229 int wxRichTextCompositeObject::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition)
230 {
231 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
232 while (node)
233 {
234 wxRichTextObject* child = node->GetData();
235
236 int ret = child->HitTest(dc, pt, textPosition);
237 if (ret != wxRICHTEXT_HITTEST_NONE)
238 return ret;
239
240 node = node->GetNext();
241 }
242
243 return wxRICHTEXT_HITTEST_NONE;
244 }
245
246 /// Finds the absolute position and row height for the given character position
247 bool wxRichTextCompositeObject::FindPosition(wxDC& dc, long index, wxPoint& pt, int* height, bool forceLineStart)
248 {
249 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
250 while (node)
251 {
252 wxRichTextObject* child = node->GetData();
253
254 if (child->FindPosition(dc, index, pt, height, forceLineStart))
255 return true;
256
257 node = node->GetNext();
258 }
259
260 return false;
261 }
262
263 /// Calculate range
264 void wxRichTextCompositeObject::CalculateRange(long start, long& end)
265 {
266 long current = start;
267 long lastEnd = current;
268
269 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
270 while (node)
271 {
272 wxRichTextObject* child = node->GetData();
273 long childEnd = 0;
274
275 child->CalculateRange(current, childEnd);
276 lastEnd = childEnd;
277
278 current = childEnd + 1;
279
280 node = node->GetNext();
281 }
282
283 end = lastEnd;
284
285 // An object with no children has zero length
286 if (m_children.GetCount() == 0)
287 end --;
288
289 m_range.SetRange(start, end);
290 }
291
292 /// Delete range from layout.
293 bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange& range)
294 {
295 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
296
297 while (node)
298 {
299 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
300 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
301
302 // Delete the range in each paragraph
303
304 // When a chunk has been deleted, internally the content does not
305 // now match the ranges.
306 // However, so long as deletion is not done on the same object twice this is OK.
307 // If you may delete content from the same object twice, recalculate
308 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
309 // adjust the range you're deleting accordingly.
310
311 if (!obj->GetRange().IsOutside(range))
312 {
313 obj->DeleteRange(range);
314
315 // Delete an empty object, or paragraph within this range.
316 if (obj->IsEmpty() ||
317 (range.GetStart() <= obj->GetRange().GetStart() && range.GetEnd() >= obj->GetRange().GetEnd()))
318 {
319 // An empty paragraph has length 1, so won't be deleted unless the
320 // whole range is deleted.
321 RemoveChild(obj, true);
322 }
323 }
324
325 node = next;
326 }
327
328 return true;
329 }
330
331 /// Get any text in this object for the given range
332 wxString wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange& range) const
333 {
334 wxString text;
335 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
336 while (node)
337 {
338 wxRichTextObject* child = node->GetData();
339 wxRichTextRange childRange = range;
340 if (!child->GetRange().IsOutside(range))
341 {
342 childRange.LimitTo(child->GetRange());
343
344 wxString childText = child->GetTextForRange(childRange);
345
346 text += childText;
347 }
348 node = node->GetNext();
349 }
350
351 return text;
352 }
353
354 /// Recursively merge all pieces that can be merged.
355 bool wxRichTextCompositeObject::Defragment()
356 {
357 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
358 while (node)
359 {
360 wxRichTextObject* child = node->GetData();
361 wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject);
362 if (composite)
363 composite->Defragment();
364
365 if (node->GetNext())
366 {
367 wxRichTextObject* nextChild = node->GetNext()->GetData();
368 if (child->CanMerge(nextChild) && child->Merge(nextChild))
369 {
370 nextChild->Dereference();
371 delete node->GetNext();
372
373 // Don't set node -- we'll see if we can merge again with the next
374 // child.
375 }
376 else
377 node = node->GetNext();
378 }
379 else
380 node = node->GetNext();
381 }
382
383 return true;
384 }
385
386 /// Dump to output stream for debugging
387 void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
388 {
389 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
390 while (node)
391 {
392 wxRichTextObject* child = node->GetData();
393 child->Dump(stream);
394 node = node->GetNext();
395 }
396 }
397
398
399 /*!
400 * wxRichTextBox
401 * This defines a 2D space to lay out objects
402 */
403
404 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextCompositeObject)
405
406 wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
407 wxRichTextCompositeObject(parent)
408 {
409 }
410
411 /// Draw the item
412 bool wxRichTextBox::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& WXUNUSED(rect), int descent, int style)
413 {
414 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
415 while (node)
416 {
417 wxRichTextObject* child = node->GetData();
418
419 wxRect childRect = wxRect(child->GetPosition(), child->GetCachedSize());
420 child->Draw(dc, range, selectionRange, childRect, descent, style);
421
422 node = node->GetNext();
423 }
424 return true;
425 }
426
427 /// Lay the item out
428 bool wxRichTextBox::Layout(wxDC& dc, const wxRect& rect, int style)
429 {
430 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
431 while (node)
432 {
433 wxRichTextObject* child = node->GetData();
434 child->Layout(dc, rect, style);
435
436 node = node->GetNext();
437 }
438 m_dirty = false;
439 return true;
440 }
441
442 /// Get/set the size for the given range. Assume only has one child.
443 bool wxRichTextBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags) const
444 {
445 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
446 if (node)
447 {
448 wxRichTextObject* child = node->GetData();
449 return child->GetRangeSize(range, size, descent, dc, flags);
450 }
451 else
452 return false;
453 }
454
455 /// Copy
456 void wxRichTextBox::Copy(const wxRichTextBox& obj)
457 {
458 wxRichTextCompositeObject::Copy(obj);
459 }
460
461
462 /*!
463 * wxRichTextParagraphLayoutBox
464 * This box knows how to lay out paragraphs.
465 */
466
467 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextBox)
468
469 wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject* parent):
470 wxRichTextBox(parent)
471 {
472 Init();
473 }
474
475 /// Initialize the object.
476 void wxRichTextParagraphLayoutBox::Init()
477 {
478 m_ctrl = NULL;
479
480 // For now, assume is the only box and has no initial size.
481 m_range = wxRichTextRange(0, -1);
482
483 m_invalidRange.SetRange(-1, -1);
484 m_leftMargin = 4;
485 m_rightMargin = 4;
486 m_topMargin = 4;
487 m_bottomMargin = 4;
488 }
489
490 /// Draw the item
491 bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int descent, int style)
492 {
493 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
494 while (node)
495 {
496 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
497 wxASSERT (child != NULL);
498
499 if (child && !child->GetRange().IsOutside(range))
500 {
501 wxRect childRect(child->GetPosition(), child->GetCachedSize());
502
503 if (childRect.GetTop() > rect.GetBottom() || childRect.GetBottom() < rect.GetTop())
504 {
505 // Skip
506 }
507 else
508 child->Draw(dc, child->GetRange(), selectionRange, childRect, descent, style);
509 }
510
511 node = node->GetNext();
512 }
513 return true;
514 }
515
516 /// Lay the item out
517 bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, int style)
518 {
519 wxRect availableSpace;
520 bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
521
522 // If only laying out a specific area, the passed rect has a different meaning:
523 // the visible part of the buffer.
524 if (formatRect)
525 {
526 availableSpace = wxRect(0 + m_leftMargin,
527 0 + m_topMargin,
528 rect.width - m_leftMargin - m_rightMargin,
529 rect.height);
530
531 // Invalidate the part of the buffer from the first visible line
532 // to the end. If other parts of the buffer are currently invalid,
533 // then they too will be taken into account if they are above
534 // the visible point.
535 long startPos = 0;
536 wxRichTextLine* line = GetLineAtYPosition(rect.y);
537 if (line)
538 startPos = line->GetAbsoluteRange().GetStart();
539
540 Invalidate(wxRichTextRange(startPos, GetRange().GetEnd()));
541 }
542 else
543 availableSpace = wxRect(rect.x + m_leftMargin,
544 rect.y + m_topMargin,
545 rect.width - m_leftMargin - m_rightMargin,
546 rect.height - m_topMargin - m_bottomMargin);
547
548 int maxWidth = 0;
549
550 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
551
552 bool layoutAll = true;
553
554 // Get invalid range, rounding to paragraph start/end.
555 wxRichTextRange invalidRange = GetInvalidRange(true);
556
557 if (invalidRange == wxRICHTEXT_NONE && !formatRect)
558 return true;
559
560 if (invalidRange == wxRICHTEXT_ALL)
561 layoutAll = true;
562 else // If we know what range is affected, start laying out from that point on.
563 if (invalidRange.GetStart() > GetRange().GetStart())
564 {
565 wxRichTextParagraph* firstParagraph = GetParagraphAtPosition(invalidRange.GetStart());
566 if (firstParagraph)
567 {
568 wxRichTextObjectList::compatibility_iterator firstNode = m_children.Find(firstParagraph);
569 wxRichTextObjectList::compatibility_iterator previousNode = firstNode ? firstNode->GetPrevious() : (wxRichTextObjectList::compatibility_iterator) NULL;
570 if (firstNode && previousNode)
571 {
572 wxRichTextParagraph* previousParagraph = wxDynamicCast(previousNode->GetData(), wxRichTextParagraph);
573 availableSpace.y = previousParagraph->GetPosition().y + previousParagraph->GetCachedSize().y;
574
575 // Now we're going to start iterating from the first affected paragraph.
576 node = firstNode;
577
578 layoutAll = false;
579 }
580 }
581 }
582
583 // A way to force speedy rest-of-buffer layout (the 'else' below)
584 bool forceQuickLayout = false;
585
586 while (node)
587 {
588 // Assume this box only contains paragraphs
589
590 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
591 wxASSERT (child != NULL);
592
593 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
594 if (child && !forceQuickLayout && (layoutAll || child->GetLines().GetCount() == 0 || !child->GetRange().IsOutside(invalidRange)))
595 {
596 child->Layout(dc, availableSpace, style);
597
598 // Layout must set the cached size
599 availableSpace.y += child->GetCachedSize().y;
600 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
601
602 // If we're just formatting the visible part of the buffer,
603 // and we're now past the bottom of the window, start quick
604 // layout.
605 if (formatRect && child->GetPosition().y > rect.GetBottom())
606 forceQuickLayout = true;
607 }
608 else
609 {
610 // We're outside the immediately affected range, so now let's just
611 // move everything up or down. This assumes that all the children have previously
612 // been laid out and have wrapped line lists associated with them.
613 // TODO: check all paragraphs before the affected range.
614
615 int inc = availableSpace.y - child->GetPosition().y;
616
617 while (node)
618 {
619 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
620 if (child)
621 {
622 if (child->GetLines().GetCount() == 0)
623 child->Layout(dc, availableSpace, style);
624 else
625 child->SetPosition(wxPoint(child->GetPosition().x, child->GetPosition().y + inc));
626
627 availableSpace.y += child->GetCachedSize().y;
628 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
629 }
630
631 node = node->GetNext();
632 }
633 break;
634 }
635
636 node = node->GetNext();
637 }
638
639 SetCachedSize(wxSize(maxWidth, availableSpace.y));
640
641 m_dirty = false;
642 m_invalidRange = wxRICHTEXT_NONE;
643
644 return true;
645 }
646
647 /// Copy
648 void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
649 {
650 wxRichTextBox::Copy(obj);
651 }
652
653 /// Get/set the size for the given range.
654 bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags) const
655 {
656 wxSize sz;
657
658 wxRichTextObjectList::compatibility_iterator startPara = NULL;
659 wxRichTextObjectList::compatibility_iterator endPara = NULL;
660
661 // First find the first paragraph whose starting position is within the range.
662 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
663 while (node)
664 {
665 // child is a paragraph
666 wxRichTextObject* child = node->GetData();
667 const wxRichTextRange& r = child->GetRange();
668
669 if (r.GetStart() <= range.GetStart() && r.GetEnd() >= range.GetStart())
670 {
671 startPara = node;
672 break;
673 }
674
675 node = node->GetNext();
676 }
677
678 // Next find the last paragraph containing part of the range
679 node = m_children.GetFirst();
680 while (node)
681 {
682 // child is a paragraph
683 wxRichTextObject* child = node->GetData();
684 const wxRichTextRange& r = child->GetRange();
685
686 if (r.GetStart() <= range.GetEnd() && r.GetEnd() >= range.GetEnd())
687 {
688 endPara = node;
689 break;
690 }
691
692 node = node->GetNext();
693 }
694
695 if (!startPara || !endPara)
696 return false;
697
698 // Now we can add up the sizes
699 for (node = startPara; node ; node = node->GetNext())
700 {
701 // child is a paragraph
702 wxRichTextObject* child = node->GetData();
703 const wxRichTextRange& childRange = child->GetRange();
704 wxRichTextRange rangeToFind = range;
705 rangeToFind.LimitTo(childRange);
706
707 wxSize childSize;
708
709 int childDescent = 0;
710 child->GetRangeSize(rangeToFind, childSize, childDescent, dc, flags);
711
712 descent = wxMax(childDescent, descent);
713
714 sz.x = wxMax(sz.x, childSize.x);
715 sz.y += childSize.y;
716
717 if (node == endPara)
718 break;
719 }
720
721 size = sz;
722
723 return true;
724 }
725
726 /// Get the paragraph at the given position
727 wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos, bool caretPosition) const
728 {
729 if (caretPosition)
730 pos ++;
731
732 // First find the first paragraph whose starting position is within the range.
733 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
734 while (node)
735 {
736 // child is a paragraph
737 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
738 wxASSERT (child != NULL);
739
740 // Return first child in buffer if position is -1
741 // if (pos == -1)
742 // return child;
743
744 if (child->GetRange().Contains(pos))
745 return child;
746
747 node = node->GetNext();
748 }
749 return NULL;
750 }
751
752 /// Get the line at the given position
753 wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos, bool caretPosition) const
754 {
755 if (caretPosition)
756 pos ++;
757
758 // First find the first paragraph whose starting position is within the range.
759 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
760 while (node)
761 {
762 // child is a paragraph
763 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
764 wxASSERT (child != NULL);
765
766 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
767 while (node2)
768 {
769 wxRichTextLine* line = node2->GetData();
770
771 wxRichTextRange range = line->GetAbsoluteRange();
772
773 if (range.Contains(pos) ||
774
775 // If the position is end-of-paragraph, then return the last line of
776 // of the paragraph.
777 (range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd()))
778 return line;
779
780 node2 = node2->GetNext();
781 }
782
783 node = node->GetNext();
784 }
785
786 int lineCount = GetLineCount();
787 if (lineCount > 0)
788 return GetLineForVisibleLineNumber(lineCount-1);
789 else
790 return NULL;
791 }
792
793 /// Get the line at the given y pixel position, or the last line.
794 wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y) const
795 {
796 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
797 while (node)
798 {
799 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
800 wxASSERT (child != NULL);
801
802 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
803 while (node2)
804 {
805 wxRichTextLine* line = node2->GetData();
806
807 wxRect rect(line->GetRect());
808
809 if (y <= rect.GetBottom())
810 return line;
811
812 node2 = node2->GetNext();
813 }
814
815 node = node->GetNext();
816 }
817
818 // Return last line
819 int lineCount = GetLineCount();
820 if (lineCount > 0)
821 return GetLineForVisibleLineNumber(lineCount-1);
822 else
823 return NULL;
824 }
825
826 /// Get the number of visible lines
827 int wxRichTextParagraphLayoutBox::GetLineCount() const
828 {
829 int count = 0;
830
831 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
832 while (node)
833 {
834 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
835 wxASSERT (child != NULL);
836
837 count += child->GetLines().GetCount();
838 node = node->GetNext();
839 }
840 return count;
841 }
842
843
844 /// Get the paragraph for a given line
845 wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine* line) const
846 {
847 return GetParagraphAtPosition(line->GetAbsoluteRange().GetStart());
848 }
849
850 /// Get the line size at the given position
851 wxSize wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos, bool caretPosition) const
852 {
853 wxRichTextLine* line = GetLineAtPosition(pos, caretPosition);
854 if (line)
855 {
856 return line->GetSize();
857 }
858 else
859 return wxSize(0, 0);
860 }
861
862
863 /// Convenience function to add a paragraph of text
864 wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph(const wxString& text)
865 {
866 wxTextAttrEx style(GetAttributes());
867
868 // Apply default style. If the style has no attributes set,
869 // then the attributes will remain the 'basic style' (i.e. the
870 // layout box's style).
871 wxRichTextApplyStyle(style, GetDefaultStyle());
872
873 wxRichTextParagraph* para = new wxRichTextParagraph(text, this, & style);
874
875 AppendChild(para);
876
877 UpdateRanges();
878 SetDirty(true);
879
880 return para->GetRange();
881 }
882
883 /// Adds multiple paragraphs, based on newlines.
884 wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text)
885 {
886 wxTextAttrEx style(GetAttributes());
887 //wxLogDebug("Initial style = %s", style.GetFont().GetFaceName());
888 //wxLogDebug("Initial size = %d", style.GetFont().GetPointSize());
889
890 // Apply default style. If the style has no attributes set,
891 // then the attributes will remain the 'basic style' (i.e. the
892 // layout box's style).
893 wxRichTextApplyStyle(style, GetDefaultStyle());
894
895 //wxLogDebug("Style after applying default style = %s", style.GetFont().GetFaceName());
896 //wxLogDebug("Size after applying default style = %d", style.GetFont().GetPointSize());
897
898 wxRichTextParagraph* firstPara = NULL;
899 wxRichTextParagraph* lastPara = NULL;
900
901 wxRichTextRange range(-1, -1);
902 size_t i = 0;
903 size_t len = text.Length();
904 wxString line;
905 while (i < len)
906 {
907 wxChar ch = text[i];
908 if (ch == wxT('\n') || ch == wxT('\r'))
909 {
910 wxRichTextParagraph* para = new wxRichTextParagraph(line, this, & style);
911
912 AppendChild(para);
913 if (!firstPara)
914 firstPara = para;
915 lastPara = para;
916 line = wxEmptyString;
917 }
918 else
919 line += ch;
920
921 i ++;
922 }
923 if (!line.empty())
924 {
925 lastPara = new wxRichTextParagraph(line, this, & style);
926 //wxLogDebug("Para Face = %s", lastPara->GetAttributes().GetFont().GetFaceName());
927 AppendChild(lastPara);
928 }
929
930 if (firstPara)
931 range.SetStart(firstPara->GetRange().GetStart());
932 else if (lastPara)
933 range.SetStart(lastPara->GetRange().GetStart());
934
935 if (lastPara)
936 range.SetEnd(lastPara->GetRange().GetEnd());
937 else if (firstPara)
938 range.SetEnd(firstPara->GetRange().GetEnd());
939
940 UpdateRanges();
941 SetDirty(false);
942
943 return GetRange();
944 }
945
946 /// Convenience function to add an image
947 wxRichTextRange wxRichTextParagraphLayoutBox::AddImage(const wxImage& image)
948 {
949 wxTextAttrEx style(GetAttributes());
950
951 // Apply default style. If the style has no attributes set,
952 // then the attributes will remain the 'basic style' (i.e. the
953 // layout box's style).
954 wxRichTextApplyStyle(style, GetDefaultStyle());
955
956 wxRichTextParagraph* para = new wxRichTextParagraph(this, & style);
957 AppendChild(para);
958 para->AppendChild(new wxRichTextImage(image, this));
959
960 UpdateRanges();
961 SetDirty(true);
962
963 return para->GetRange();
964 }
965
966
967 /// Insert fragment into this box at the given position. If partialParagraph is true,
968 /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
969 /// marker.
970 /// TODO: if fragment is inserted inside styled fragment, must apply that style to
971 /// to the data (if it has a default style, anyway).
972
973 bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextFragment& fragment)
974 {
975 SetDirty(true);
976
977 // First, find the first paragraph whose starting position is within the range.
978 wxRichTextParagraph* para = GetParagraphAtPosition(position);
979 if (para)
980 {
981 wxRichTextObjectList::compatibility_iterator node = m_children.Find(para);
982
983 // Now split at this position, returning the object to insert the new
984 // ones in front of.
985 wxRichTextObject* nextObject = para->SplitAt(position);
986
987 // Special case: partial paragraph, just one paragraph. Might be a small amount of
988 // text, for example, so let's optimize.
989
990 if (fragment.GetPartialParagraph() && fragment.GetChildren().GetCount() == 1)
991 {
992 // Add the first para to this para...
993 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
994 if (!firstParaNode)
995 return false;
996
997 // Iterate through the fragment paragraph inserting the content into this paragraph.
998 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
999 wxASSERT (firstPara != NULL);
1000
1001 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
1002 while (objectNode)
1003 {
1004 wxRichTextObject* newObj = objectNode->GetData()->Clone();
1005
1006 if (!nextObject)
1007 {
1008 // Append
1009 para->AppendChild(newObj);
1010 }
1011 else
1012 {
1013 // Insert before nextObject
1014 para->InsertChild(newObj, nextObject);
1015 }
1016
1017 objectNode = objectNode->GetNext();
1018 }
1019
1020 return true;
1021 }
1022 else
1023 {
1024 // Procedure for inserting a fragment consisting of a number of
1025 // paragraphs:
1026 //
1027 // 1. Remove and save the content that's after the insertion point, for adding
1028 // back once we've added the fragment.
1029 // 2. Add the content from the first fragment paragraph to the current
1030 // paragraph.
1031 // 3. Add remaining fragment paragraphs after the current paragraph.
1032 // 4. Add back the saved content from the first paragraph. If partialParagraph
1033 // is true, add it to the last paragraph added and not a new one.
1034
1035 // 1. Remove and save objects after split point.
1036 wxList savedObjects;
1037 if (nextObject)
1038 para->MoveToList(nextObject, savedObjects);
1039
1040 // 2. Add the content from the 1st fragment paragraph.
1041 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
1042 if (!firstParaNode)
1043 return false;
1044
1045 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
1046 wxASSERT(firstPara != NULL);
1047
1048 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
1049 while (objectNode)
1050 {
1051 wxRichTextObject* newObj = objectNode->GetData()->Clone();
1052
1053 // Append
1054 para->AppendChild(newObj);
1055
1056 objectNode = objectNode->GetNext();
1057 }
1058
1059 // 3. Add remaining fragment paragraphs after the current paragraph.
1060 wxRichTextObjectList::compatibility_iterator nextParagraphNode = node->GetNext();
1061 wxRichTextObject* nextParagraph = NULL;
1062 if (nextParagraphNode)
1063 nextParagraph = nextParagraphNode->GetData();
1064
1065 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst()->GetNext();
1066 wxRichTextParagraph* finalPara = para;
1067
1068 // If there was only one paragraph, we need to insert a new one.
1069 if (!i)
1070 {
1071 finalPara = new wxRichTextParagraph;
1072
1073 // TODO: These attributes should come from the subsequent paragraph
1074 // when originally deleted, since the subsequent para takes on
1075 // the previous para's attributes.
1076 finalPara->SetAttributes(firstPara->GetAttributes());
1077
1078 if (nextParagraph)
1079 InsertChild(finalPara, nextParagraph);
1080 else
1081 AppendChild(finalPara);
1082 }
1083 else while (i)
1084 {
1085 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
1086 wxASSERT( para != NULL );
1087
1088 finalPara = (wxRichTextParagraph*) para->Clone();
1089
1090 if (nextParagraph)
1091 InsertChild(finalPara, nextParagraph);
1092 else
1093 AppendChild(finalPara);
1094
1095 i = i->GetNext();
1096 }
1097
1098 // 4. Add back the remaining content.
1099 if (finalPara)
1100 {
1101 finalPara->MoveFromList(savedObjects);
1102
1103 // Ensure there's at least one object
1104 if (finalPara->GetChildCount() == 0)
1105 {
1106 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
1107 text->SetAttributes(finalPara->GetAttributes());
1108
1109 finalPara->AppendChild(text);
1110 }
1111 }
1112
1113 return true;
1114 }
1115 }
1116 else
1117 {
1118 // Append
1119 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst();
1120 while (i)
1121 {
1122 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
1123 wxASSERT( para != NULL );
1124
1125 AppendChild(para->Clone());
1126
1127 i = i->GetNext();
1128 }
1129
1130 return true;
1131 }
1132
1133 return false;
1134 }
1135
1136 /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
1137 /// If there was an incomplete paragraph at the end, partialParagraph is set to true.
1138 bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange& range, wxRichTextFragment& fragment)
1139 {
1140 wxRichTextObjectList::compatibility_iterator i = GetChildren().GetFirst();
1141 while (i)
1142 {
1143 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
1144 wxASSERT( para != NULL );
1145
1146 if (!para->GetRange().IsOutside(range))
1147 {
1148 fragment.AppendChild(para->Clone());
1149 }
1150 i = i->GetNext();
1151 }
1152
1153 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
1154 if (!fragment.IsEmpty())
1155 {
1156 wxRichTextRange topTailRange(range);
1157
1158 wxRichTextParagraph* firstPara = wxDynamicCast(fragment.GetChildren().GetFirst()->GetData(), wxRichTextParagraph);
1159 wxASSERT( firstPara != NULL );
1160
1161 // Chop off the start of the paragraph
1162 if (topTailRange.GetStart() > firstPara->GetRange().GetStart())
1163 {
1164 wxRichTextRange r(firstPara->GetRange().GetStart(), topTailRange.GetStart()-1);
1165 firstPara->DeleteRange(r);
1166
1167 // Make sure the numbering is correct
1168 long end;
1169 fragment.CalculateRange(firstPara->GetRange().GetStart(), end);
1170
1171 // Now, we've deleted some positions, so adjust the range
1172 // accordingly.
1173 topTailRange.SetEnd(topTailRange.GetEnd() - r.GetLength());
1174 }
1175
1176 wxRichTextParagraph* lastPara = wxDynamicCast(fragment.GetChildren().GetLast()->GetData(), wxRichTextParagraph);
1177 wxASSERT( lastPara != NULL );
1178
1179 if (topTailRange.GetEnd() < (lastPara->GetRange().GetEnd()-1))
1180 {
1181 wxRichTextRange r(topTailRange.GetEnd()+1, lastPara->GetRange().GetEnd()-1); /* -1 since actual text ends 1 position before end of para marker */
1182 lastPara->DeleteRange(r);
1183
1184 // Make sure the numbering is correct
1185 long end;
1186 fragment.CalculateRange(firstPara->GetRange().GetStart(), end);
1187
1188 // We only have part of a paragraph at the end
1189 fragment.SetPartialParagraph(true);
1190 }
1191 else
1192 {
1193 if (topTailRange.GetEnd() == (lastPara->GetRange().GetEnd() - 1))
1194 // We have a partial paragraph (don't save last new paragraph marker)
1195 fragment.SetPartialParagraph(true);
1196 else
1197 // We have a complete paragraph
1198 fragment.SetPartialParagraph(false);
1199 }
1200 }
1201
1202 return true;
1203 }
1204
1205 /// Given a position, get the number of the visible line (potentially many to a paragraph),
1206 /// starting from zero at the start of the buffer.
1207 long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos, bool caretPosition, bool startOfLine) const
1208 {
1209 if (caretPosition)
1210 pos ++;
1211
1212 int lineCount = 0;
1213
1214 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1215 while (node)
1216 {
1217 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1218 wxASSERT( child != NULL );
1219
1220 if (child->GetRange().Contains(pos))
1221 {
1222 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
1223 while (node2)
1224 {
1225 wxRichTextLine* line = node2->GetData();
1226 wxRichTextRange lineRange = line->GetAbsoluteRange();
1227
1228 if (lineRange.Contains(pos))
1229 {
1230 // If the caret is displayed at the end of the previous wrapped line,
1231 // we want to return the line it's _displayed_ at (not the actual line
1232 // containing the position).
1233 if (lineRange.GetStart() == pos && !startOfLine && child->GetRange().GetStart() != pos)
1234 return lineCount - 1;
1235 else
1236 return lineCount;
1237 }
1238
1239 lineCount ++;
1240
1241 node2 = node2->GetNext();
1242 }
1243 // If we didn't find it in the lines, it must be
1244 // the last position of the paragraph. So return the last line.
1245 return lineCount-1;
1246 }
1247 else
1248 lineCount += child->GetLines().GetCount();
1249
1250 node = node->GetNext();
1251 }
1252
1253 // Not found
1254 return -1;
1255 }
1256
1257 /// Given a line number, get the corresponding wxRichTextLine object.
1258 wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber) const
1259 {
1260 int lineCount = 0;
1261
1262 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1263 while (node)
1264 {
1265 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1266 wxASSERT(child != NULL);
1267
1268 if (lineNumber < (int) (child->GetLines().GetCount() + lineCount))
1269 {
1270 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
1271 while (node2)
1272 {
1273 wxRichTextLine* line = node2->GetData();
1274
1275 if (lineCount == lineNumber)
1276 return line;
1277
1278 lineCount ++;
1279
1280 node2 = node2->GetNext();
1281 }
1282 }
1283 else
1284 lineCount += child->GetLines().GetCount();
1285
1286 node = node->GetNext();
1287 }
1288
1289 // Didn't find it
1290 return NULL;
1291 }
1292
1293 /// Delete range from layout.
1294 bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange& range)
1295 {
1296 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1297
1298 while (node)
1299 {
1300 wxRichTextParagraph* obj = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1301 wxASSERT (obj != NULL);
1302
1303 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
1304
1305 // Delete the range in each paragraph
1306
1307 if (!obj->GetRange().IsOutside(range))
1308 {
1309 // Deletes the content of this object within the given range
1310 obj->DeleteRange(range);
1311
1312 // If the whole paragraph is within the range to delete,
1313 // delete the whole thing.
1314 if (range.GetStart() <= obj->GetRange().GetStart() && range.GetEnd() >= obj->GetRange().GetEnd())
1315 {
1316 // Delete the whole object
1317 RemoveChild(obj, true);
1318 }
1319 // If the range includes the paragraph end, we need to join this
1320 // and the next paragraph.
1321 else if (range.Contains(obj->GetRange().GetEnd()))
1322 {
1323 // We need to move the objects from the next paragraph
1324 // to this paragraph
1325
1326 if (next)
1327 {
1328 wxRichTextParagraph* nextParagraph = wxDynamicCast(next->GetData(), wxRichTextParagraph);
1329 next = next->GetNext();
1330 if (nextParagraph)
1331 {
1332 // Delete the stuff we need to delete
1333 nextParagraph->DeleteRange(range);
1334
1335 // Move the objects to the previous para
1336 wxRichTextObjectList::compatibility_iterator node1 = nextParagraph->GetChildren().GetFirst();
1337
1338 while (node1)
1339 {
1340 wxRichTextObject* obj1 = node1->GetData();
1341
1342 // If the object is empty, optimise it out
1343 if (obj1->IsEmpty())
1344 {
1345 delete obj1;
1346 }
1347 else
1348 {
1349 obj->AppendChild(obj1);
1350 }
1351
1352 wxRichTextObjectList::compatibility_iterator next1 = node1->GetNext();
1353 delete node1;
1354
1355 node1 = next1;
1356 }
1357
1358 // Delete the paragraph
1359 RemoveChild(nextParagraph, true);
1360
1361 }
1362 }
1363
1364 }
1365 }
1366
1367 node = next;
1368 }
1369
1370 return true;
1371 }
1372
1373 /// Get any text in this object for the given range
1374 wxString wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange& range) const
1375 {
1376 int lineCount = 0;
1377 wxString text;
1378 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1379 while (node)
1380 {
1381 wxRichTextObject* child = node->GetData();
1382 if (!child->GetRange().IsOutside(range))
1383 {
1384 if (lineCount > 0)
1385 text += wxT("\n");
1386 wxRichTextRange childRange = range;
1387 childRange.LimitTo(child->GetRange());
1388
1389 wxString childText = child->GetTextForRange(childRange);
1390
1391 text += childText;
1392
1393 lineCount ++;
1394 }
1395 node = node->GetNext();
1396 }
1397
1398 return text;
1399 }
1400
1401 /// Get all the text
1402 wxString wxRichTextParagraphLayoutBox::GetText() const
1403 {
1404 return GetTextForRange(GetRange());
1405 }
1406
1407 /// Get the paragraph by number
1408 wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber) const
1409 {
1410 if ((size_t) paragraphNumber <= GetChildCount())
1411 return NULL;
1412
1413 return (wxRichTextParagraph*) GetChild((size_t) paragraphNumber);
1414 }
1415
1416 /// Get the length of the paragraph
1417 int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber) const
1418 {
1419 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
1420 if (para)
1421 return para->GetRange().GetLength() - 1; // don't include newline
1422 else
1423 return 0;
1424 }
1425
1426 /// Get the text of the paragraph
1427 wxString wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber) const
1428 {
1429 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
1430 if (para)
1431 return para->GetTextForRange(para->GetRange());
1432 else
1433 return wxEmptyString;
1434 }
1435
1436 /// Convert zero-based line column and paragraph number to a position.
1437 long wxRichTextParagraphLayoutBox::XYToPosition(long x, long y) const
1438 {
1439 wxRichTextParagraph* para = GetParagraphAtLine(y);
1440 if (para)
1441 {
1442 return para->GetRange().GetStart() + x;
1443 }
1444 else
1445 return -1;
1446 }
1447
1448 /// Convert zero-based position to line column and paragraph number
1449 bool wxRichTextParagraphLayoutBox::PositionToXY(long pos, long* x, long* y) const
1450 {
1451 wxRichTextParagraph* para = GetParagraphAtPosition(pos);
1452 if (para)
1453 {
1454 int count = 0;
1455 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1456 while (node)
1457 {
1458 wxRichTextObject* child = node->GetData();
1459 if (child == para)
1460 break;
1461 count ++;
1462 node = node->GetNext();
1463 }
1464
1465 *y = count;
1466 *x = pos - para->GetRange().GetStart();
1467
1468 return true;
1469 }
1470 else
1471 return false;
1472 }
1473
1474 /// Get the leaf object in a paragraph at this position.
1475 /// Given a line number, get the corresponding wxRichTextLine object.
1476 wxRichTextObject* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position) const
1477 {
1478 wxRichTextParagraph* para = GetParagraphAtPosition(position);
1479 if (para)
1480 {
1481 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
1482
1483 while (node)
1484 {
1485 wxRichTextObject* child = node->GetData();
1486 if (child->GetRange().Contains(position))
1487 return child;
1488
1489 node = node->GetNext();
1490 }
1491 if (position == para->GetRange().GetEnd() && para->GetChildCount() > 0)
1492 return para->GetChildren().GetLast()->GetData();
1493 }
1494 return NULL;
1495 }
1496
1497 /// Set character or paragraph text attributes: apply character styles only to immediate text nodes
1498 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, bool withUndo)
1499 {
1500 bool characterStyle = false;
1501 bool paragraphStyle = false;
1502
1503 if (style.IsCharacterStyle())
1504 characterStyle = true;
1505 if (style.IsParagraphStyle())
1506 paragraphStyle = true;
1507
1508 // If we are associated with a control, make undoable; otherwise, apply immediately
1509 // to the data.
1510
1511 bool haveControl = (GetRichTextCtrl() != NULL);
1512
1513 wxRichTextAction* action = NULL;
1514
1515 if (haveControl && withUndo)
1516 {
1517 action = new wxRichTextAction(NULL, _("Change Style"), wxRICHTEXT_CHANGE_STYLE, & GetRichTextCtrl()->GetBuffer(), GetRichTextCtrl());
1518 action->SetRange(range);
1519 action->SetPosition(GetRichTextCtrl()->GetCaretPosition());
1520 }
1521
1522 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1523 while (node)
1524 {
1525 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1526 wxASSERT (para != NULL);
1527
1528 if (para && para->GetChildCount() > 0)
1529 {
1530 // Stop searching if we're beyond the range of interest
1531 if (para->GetRange().GetStart() > range.GetEnd())
1532 break;
1533
1534 if (!para->GetRange().IsOutside(range))
1535 {
1536 // We'll be using a copy of the paragraph to make style changes,
1537 // not updating the buffer directly.
1538 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
1539
1540 if (haveControl && withUndo)
1541 {
1542 newPara = new wxRichTextParagraph(*para);
1543 action->GetNewParagraphs().AppendChild(newPara);
1544
1545 // Also store the old ones for Undo
1546 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
1547 }
1548 else
1549 newPara = para;
1550
1551 if (paragraphStyle)
1552 wxRichTextApplyStyle(newPara->GetAttributes(), style);
1553
1554 if (characterStyle && range.GetStart() != newPara->GetRange().GetEnd())
1555 {
1556 wxRichTextRange childRange(range);
1557 childRange.LimitTo(newPara->GetRange());
1558
1559 // Find the starting position and if necessary split it so
1560 // we can start applying a different style.
1561 // TODO: check that the style actually changes or is different
1562 // from style outside of range
1563 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
1564 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
1565
1566 if (childRange.GetStart() == newPara->GetRange().GetStart())
1567 firstObject = newPara->GetChildren().GetFirst()->GetData();
1568 else
1569 firstObject = newPara->SplitAt(range.GetStart());
1570
1571 // Increment by 1 because we're apply the style one _after_ the split point
1572 long splitPoint = childRange.GetEnd();
1573 if (splitPoint != newPara->GetRange().GetEnd())
1574 splitPoint ++;
1575
1576 // Find last object
1577 if (splitPoint == newPara->GetRange().GetEnd() || splitPoint == (newPara->GetRange().GetEnd() - 1))
1578 lastObject = newPara->GetChildren().GetLast()->GetData();
1579 else
1580 // lastObject is set as a side-effect of splitting. It's
1581 // returned as the object before the new object.
1582 (void) newPara->SplitAt(splitPoint, & lastObject);
1583
1584 wxASSERT(firstObject != NULL);
1585 wxASSERT(lastObject != NULL);
1586
1587 if (!firstObject || !lastObject)
1588 continue;
1589
1590 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
1591 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
1592
1593 wxASSERT(firstNode != NULL);
1594 wxASSERT(lastNode != NULL);
1595
1596 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
1597
1598 while (node2)
1599 {
1600 wxRichTextObject* child = node2->GetData();
1601
1602 wxRichTextApplyStyle(child->GetAttributes(), style);
1603 if (node2 == lastNode)
1604 break;
1605
1606 node2 = node2->GetNext();
1607 }
1608 }
1609 }
1610 }
1611
1612 node = node->GetNext();
1613 }
1614
1615 // Do action, or delay it until end of batch.
1616 if (haveControl && withUndo)
1617 GetRichTextCtrl()->GetBuffer().SubmitAction(action);
1618
1619 return true;
1620 }
1621
1622 /// Set text attributes
1623 bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxTextAttrEx& style, bool withUndo)
1624 {
1625 wxRichTextAttr richStyle = style;
1626 return SetStyle(range, richStyle, withUndo);
1627 }
1628
1629 /// Get the text attributes for this position.
1630 bool wxRichTextParagraphLayoutBox::GetStyle(long position, wxTextAttrEx& style) const
1631 {
1632 wxRichTextObject* obj wxDUMMY_INITIALIZE(NULL);
1633
1634 if (style.IsParagraphStyle())
1635 obj = GetParagraphAtPosition(position);
1636 else
1637 obj = GetLeafObjectAtPosition(position);
1638
1639 if (obj)
1640 {
1641 style = obj->GetAttributes();
1642 return true;
1643 }
1644 else
1645 return false;
1646 }
1647
1648 /// Get the text attributes for this position.
1649 bool wxRichTextParagraphLayoutBox::GetStyle(long position, wxRichTextAttr& style) const
1650 {
1651 wxRichTextObject* obj wxDUMMY_INITIALIZE(NULL);
1652
1653 if (style.IsParagraphStyle())
1654 obj = GetParagraphAtPosition(position);
1655 else
1656 obj = GetLeafObjectAtPosition(position);
1657
1658 if (obj)
1659 {
1660 style = obj->GetAttributes();
1661 return true;
1662 }
1663 else
1664 return false;
1665 }
1666
1667 /// Set default style
1668 bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxTextAttrEx& style)
1669 {
1670 m_defaultAttributes = style;
1671
1672 return true;
1673 }
1674
1675 /// Test if this whole range has character attributes of the specified kind. If any
1676 /// of the attributes are different within the range, the test fails. You
1677 /// can use this to implement, for example, bold button updating. style must have
1678 /// flags indicating which attributes are of interest.
1679 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
1680 {
1681 int foundCount = 0;
1682 int matchingCount = 0;
1683
1684 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1685 while (node)
1686 {
1687 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1688 wxASSERT (para != NULL);
1689
1690 if (para)
1691 {
1692 // Stop searching if we're beyond the range of interest
1693 if (para->GetRange().GetStart() > range.GetEnd())
1694 return foundCount == matchingCount;
1695
1696 if (!para->GetRange().IsOutside(range))
1697 {
1698 wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
1699
1700 while (node2)
1701 {
1702 wxRichTextObject* child = node2->GetData();
1703 if (!child->GetRange().IsOutside(range) && child->IsKindOf(CLASSINFO(wxRichTextPlainText)))
1704 {
1705 foundCount ++;
1706 if (wxTextAttrEqPartial(child->GetAttributes(), style, style.GetFlags()))
1707 matchingCount ++;
1708 }
1709
1710 node2 = node2->GetNext();
1711 }
1712 }
1713 }
1714
1715 node = node->GetNext();
1716 }
1717
1718 return foundCount == matchingCount;
1719 }
1720
1721 bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& range, const wxTextAttrEx& style) const
1722 {
1723 wxRichTextAttr richStyle = style;
1724 return HasCharacterAttributes(range, richStyle);
1725 }
1726
1727 /// Test if this whole range has paragraph attributes of the specified kind. If any
1728 /// of the attributes are different within the range, the test fails. You
1729 /// can use this to implement, for example, centering button updating. style must have
1730 /// flags indicating which attributes are of interest.
1731 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
1732 {
1733 int foundCount = 0;
1734 int matchingCount = 0;
1735
1736 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1737 while (node)
1738 {
1739 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1740 wxASSERT (para != NULL);
1741
1742 if (para)
1743 {
1744 // Stop searching if we're beyond the range of interest
1745 if (para->GetRange().GetStart() > range.GetEnd())
1746 return foundCount == matchingCount;
1747
1748 if (!para->GetRange().IsOutside(range))
1749 {
1750 foundCount ++;
1751 if (wxTextAttrEqPartial(para->GetAttributes(), style, style.GetFlags()))
1752 matchingCount ++;
1753 }
1754 }
1755
1756 node = node->GetNext();
1757 }
1758 return foundCount == matchingCount;
1759 }
1760
1761 bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& range, const wxTextAttrEx& style) const
1762 {
1763 wxRichTextAttr richStyle = style;
1764 return HasParagraphAttributes(range, richStyle);
1765 }
1766
1767 void wxRichTextParagraphLayoutBox::Clear()
1768 {
1769 DeleteChildren();
1770 }
1771
1772 void wxRichTextParagraphLayoutBox::Reset()
1773 {
1774 Clear();
1775
1776 AddParagraph(wxEmptyString);
1777 }
1778
1779 /// Invalidate the buffer. With no argument, invalidates whole buffer.
1780 void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange& invalidRange)
1781 {
1782 SetDirty(true);
1783
1784 if (invalidRange == wxRICHTEXT_ALL)
1785 {
1786 m_invalidRange = wxRICHTEXT_ALL;
1787 return;
1788 }
1789
1790 // Already invalidating everything
1791 if (m_invalidRange == wxRICHTEXT_ALL)
1792 return;
1793
1794 if ((invalidRange.GetStart() < m_invalidRange.GetStart()) || m_invalidRange.GetStart() == -1)
1795 m_invalidRange.SetStart(invalidRange.GetStart());
1796 if (invalidRange.GetEnd() > m_invalidRange.GetEnd())
1797 m_invalidRange.SetEnd(invalidRange.GetEnd());
1798 }
1799
1800 /// Get invalid range, rounding to entire paragraphs if argument is true.
1801 wxRichTextRange wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs) const
1802 {
1803 if (m_invalidRange == wxRICHTEXT_ALL || m_invalidRange == wxRICHTEXT_NONE)
1804 return m_invalidRange;
1805
1806 wxRichTextRange range = m_invalidRange;
1807
1808 if (wholeParagraphs)
1809 {
1810 wxRichTextParagraph* para1 = GetParagraphAtPosition(range.GetStart());
1811 wxRichTextParagraph* para2 = GetParagraphAtPosition(range.GetEnd());
1812 if (para1)
1813 range.SetStart(para1->GetRange().GetStart());
1814 if (para2)
1815 range.SetEnd(para2->GetRange().GetEnd());
1816 }
1817 return range;
1818 }
1819
1820 /*!
1821 * wxRichTextFragment class declaration
1822 * This is a lind of paragraph layout box used for storing
1823 * paragraphs for Undo/Redo, for example.
1824 */
1825
1826 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFragment, wxRichTextParagraphLayoutBox)
1827
1828 /// Initialise
1829 void wxRichTextFragment::Init()
1830 {
1831 m_partialParagraph = false;
1832 }
1833
1834 /// Copy
1835 void wxRichTextFragment::Copy(const wxRichTextFragment& obj)
1836 {
1837 wxRichTextParagraphLayoutBox::Copy(obj);
1838
1839 m_partialParagraph = obj.m_partialParagraph;
1840 }
1841
1842 /*!
1843 * wxRichTextParagraph
1844 * This object represents a single paragraph (or in a straight text editor, a line).
1845 */
1846
1847 IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph, wxRichTextBox)
1848
1849 wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject* parent, wxTextAttrEx* style):
1850 wxRichTextBox(parent)
1851 {
1852 if (parent && !style)
1853 SetAttributes(parent->GetAttributes());
1854 if (style)
1855 SetAttributes(*style);
1856 }
1857
1858 wxRichTextParagraph::wxRichTextParagraph(const wxString& text, wxRichTextObject* parent, wxTextAttrEx* style):
1859 wxRichTextBox(parent)
1860 {
1861 if (parent && !style)
1862 SetAttributes(parent->GetAttributes());
1863 if (style)
1864 SetAttributes(*style);
1865
1866 AppendChild(new wxRichTextPlainText(text, this));
1867 }
1868
1869 wxRichTextParagraph::~wxRichTextParagraph()
1870 {
1871 ClearLines();
1872 }
1873
1874 /// Draw the item
1875 bool wxRichTextParagraph::Draw(wxDC& dc, const wxRichTextRange& WXUNUSED(range), const wxRichTextRange& selectionRange, const wxRect& WXUNUSED(rect), int WXUNUSED(descent), int style)
1876 {
1877 // Draw the bullet, if any
1878 if (GetAttributes().GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
1879 {
1880 if (GetAttributes().GetLeftSubIndent() != 0)
1881 {
1882 int spaceBeforePara = ConvertTenthsMMToPixels(dc, GetAttributes().GetParagraphSpacingBefore());
1883 // int spaceAfterPara = ConvertTenthsMMToPixels(dc, GetAttributes().GetParagraphSpacingAfter());
1884 int leftIndent = ConvertTenthsMMToPixels(dc, GetAttributes().GetLeftIndent());
1885 // int leftSubIndent = ConvertTenthsMMToPixels(dc, GetAttributes().GetLeftSubIndent());
1886 // int rightIndent = ConvertTenthsMMToPixels(dc, GetAttributes().GetRightIndent());
1887
1888 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP)
1889 {
1890 // TODO
1891 }
1892 else
1893 {
1894 wxString bulletText = GetBulletText();
1895 if (!bulletText.empty())
1896 {
1897 if (GetAttributes().GetFont().Ok())
1898 dc.SetFont(GetAttributes().GetFont());
1899
1900 if (GetAttributes().GetTextColour().Ok())
1901 dc.SetTextForeground(GetAttributes().GetTextColour());
1902
1903 dc.SetBackgroundMode(wxTRANSPARENT);
1904
1905 // Get line height from first line, if any
1906 wxRichTextLine* line = m_cachedLines.GetFirst() ? (wxRichTextLine* ) m_cachedLines.GetFirst()->GetData() : (wxRichTextLine*) NULL;
1907
1908 wxPoint linePos;
1909 int lineHeight wxDUMMY_INITIALIZE(0);
1910 if (line)
1911 {
1912 lineHeight = line->GetSize().y;
1913 linePos = line->GetPosition() + GetPosition();
1914 }
1915 else
1916 {
1917 lineHeight = dc.GetCharHeight();
1918 linePos = GetPosition();
1919 linePos.y += spaceBeforePara;
1920 }
1921
1922 int charHeight = dc.GetCharHeight();
1923
1924 int x = GetPosition().x + leftIndent;
1925 int y = linePos.y + (lineHeight - charHeight);
1926
1927 dc.DrawText(bulletText, x, y);
1928 }
1929 }
1930 }
1931 }
1932
1933 // Draw the range for each line, one object at a time.
1934
1935 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
1936 while (node)
1937 {
1938 wxRichTextLine* line = node->GetData();
1939 wxRichTextRange lineRange = line->GetAbsoluteRange();
1940
1941 int maxDescent = line->GetDescent();
1942
1943 // Lines are specified relative to the paragraph
1944
1945 wxPoint linePosition = line->GetPosition() + GetPosition();
1946 wxPoint objectPosition = linePosition;
1947
1948 // Loop through objects until we get to the one within range
1949 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
1950 while (node2)
1951 {
1952 wxRichTextObject* child = node2->GetData();
1953 if (!child->GetRange().IsOutside(lineRange))
1954 {
1955 // Draw this part of the line at the correct position
1956 wxRichTextRange objectRange(child->GetRange());
1957 objectRange.LimitTo(lineRange);
1958
1959 wxSize objectSize;
1960 int descent = 0;
1961 child->GetRangeSize(objectRange, objectSize, descent, dc, wxRICHTEXT_UNFORMATTED);
1962
1963 // Use the child object's width, but the whole line's height
1964 wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
1965 child->Draw(dc, objectRange, selectionRange, childRect, maxDescent, style);
1966
1967 objectPosition.x += objectSize.x;
1968 }
1969 else if (child->GetRange().GetStart() > lineRange.GetEnd())
1970 // Can break out of inner loop now since we've passed this line's range
1971 break;
1972
1973 node2 = node2->GetNext();
1974 }
1975
1976 node = node->GetNext();
1977 }
1978
1979 return true;
1980 }
1981
1982 /// Lay the item out
1983 bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, int style)
1984 {
1985 // ClearLines();
1986
1987 // Increase the size of the paragraph due to spacing
1988 int spaceBeforePara = ConvertTenthsMMToPixels(dc, GetAttributes().GetParagraphSpacingBefore());
1989 int spaceAfterPara = ConvertTenthsMMToPixels(dc, GetAttributes().GetParagraphSpacingAfter());
1990 int leftIndent = ConvertTenthsMMToPixels(dc, GetAttributes().GetLeftIndent());
1991 int leftSubIndent = ConvertTenthsMMToPixels(dc, GetAttributes().GetLeftSubIndent());
1992 int rightIndent = ConvertTenthsMMToPixels(dc, GetAttributes().GetRightIndent());
1993
1994 int lineSpacing = 0;
1995
1996 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
1997 if (GetAttributes().GetLineSpacing() > 10 && GetAttributes().GetFont().Ok())
1998 {
1999 dc.SetFont(GetAttributes().GetFont());
2000 lineSpacing = (ConvertTenthsMMToPixels(dc, dc.GetCharHeight()) * GetAttributes().GetLineSpacing())/10;
2001 }
2002
2003 // Available space for text on each line differs.
2004 int availableTextSpaceFirstLine = rect.GetWidth() - leftIndent - rightIndent;
2005
2006 // Bullets start the text at the same position as subsequent lines
2007 if (GetAttributes().GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
2008 availableTextSpaceFirstLine -= leftSubIndent;
2009
2010 int availableTextSpaceSubsequentLines = rect.GetWidth() - leftIndent - rightIndent - leftSubIndent;
2011
2012 // Start position for each line relative to the paragraph
2013 int startPositionFirstLine = leftIndent;
2014 int startPositionSubsequentLines = leftIndent + leftSubIndent;
2015
2016 // If we have a bullet in this paragraph, the start position for the first line's text
2017 // is actually leftIndent + leftSubIndent.
2018 if (GetAttributes().GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
2019 startPositionFirstLine = startPositionSubsequentLines;
2020
2021 //bool restrictWidth = wxRichTextHasStyle(style, wxRICHTEXT_FIXED_WIDTH);
2022 //bool restrictHeight = wxRichTextHasStyle(style, wxRICHTEXT_FIXED_HEIGHT);
2023
2024 long lastEndPos = GetRange().GetStart()-1;
2025 long lastCompletedEndPos = lastEndPos;
2026
2027 int currentWidth = 0;
2028 SetPosition(rect.GetPosition());
2029
2030 wxPoint currentPosition(0, spaceBeforePara); // We will calculate lines relative to paragraph
2031 int lineHeight = 0;
2032 int maxWidth = 0;
2033 int maxDescent = 0;
2034
2035 int lineCount = 0;
2036
2037 // Split up lines
2038
2039 // We may need to go back to a previous child, in which case create the new line,
2040 // find the child corresponding to the start position of the string, and
2041 // continue.
2042
2043 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2044 while (node)
2045 {
2046 wxRichTextObject* child = node->GetData();
2047
2048 // If this is e.g. a composite text box, it will need to be laid out itself.
2049 // But if just a text fragment or image, for example, this will
2050 // do nothing. NB: won't we need to set the position after layout?
2051 // since for example if position is dependent on vertical line size, we
2052 // can't tell the position until the size is determined. So possibly introduce
2053 // another layout phase.
2054
2055 child->Layout(dc, rect, style);
2056
2057 // Available width depends on whether we're on the first or subsequent lines
2058 int availableSpaceForText = (lineCount == 0 ? availableTextSpaceFirstLine : availableTextSpaceSubsequentLines);
2059
2060 currentPosition.x = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
2061
2062 // We may only be looking at part of a child, if we searched back for wrapping
2063 // and found a suitable point some way into the child. So get the size for the fragment
2064 // if necessary.
2065
2066 wxSize childSize;
2067 int childDescent = 0;
2068 if (lastEndPos == child->GetRange().GetStart() - 1)
2069 {
2070 childSize = child->GetCachedSize();
2071 childDescent = child->GetDescent();
2072 }
2073 else
2074 GetRangeSize(wxRichTextRange(lastEndPos+1, child->GetRange().GetEnd()), childSize, childDescent, dc, wxRICHTEXT_UNFORMATTED);
2075
2076 if (childSize.x + currentWidth > availableSpaceForText)
2077 {
2078 long wrapPosition = 0;
2079
2080 // Find a place to wrap. This may walk back to previous children,
2081 // for example if a word spans several objects.
2082 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, availableSpaceForText, wrapPosition))
2083 {
2084 // If the function failed, just cut it off at the end of this child.
2085 wrapPosition = child->GetRange().GetEnd();
2086 }
2087
2088 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
2089 if (wrapPosition <= lastCompletedEndPos)
2090 wrapPosition = wxMax(lastCompletedEndPos+1,child->GetRange().GetEnd());
2091
2092 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
2093
2094 // Let's find the actual size of the current line now
2095 wxSize actualSize;
2096 wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
2097 GetRangeSize(actualRange, actualSize, childDescent, dc, wxRICHTEXT_UNFORMATTED);
2098 currentWidth = actualSize.x;
2099 lineHeight = wxMax(lineHeight, actualSize.y);
2100 maxDescent = wxMax(childDescent, maxDescent);
2101
2102 // Add a new line
2103 wxRichTextLine* line = AllocateLine(lineCount);
2104
2105 // Set relative range so we won't have to change line ranges when paragraphs are moved
2106 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
2107 line->SetPosition(currentPosition);
2108 line->SetSize(wxSize(currentWidth, lineHeight));
2109 line->SetDescent(maxDescent);
2110
2111 // Now move down a line. TODO: add margins, spacing
2112 currentPosition.y += lineHeight;
2113 currentPosition.y += lineSpacing;
2114 currentWidth = 0;
2115 maxDescent = 0;
2116 maxWidth = wxMax(maxWidth, currentWidth);
2117
2118 lineCount ++;
2119
2120 // TODO: account for zero-length objects, such as fields
2121 wxASSERT(wrapPosition > lastCompletedEndPos);
2122
2123 lastEndPos = wrapPosition;
2124 lastCompletedEndPos = lastEndPos;
2125
2126 lineHeight = 0;
2127
2128 // May need to set the node back to a previous one, due to searching back in wrapping
2129 wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition(wrapPosition+1);
2130 if (childAfterWrapPosition)
2131 node = m_children.Find(childAfterWrapPosition);
2132 else
2133 node = node->GetNext();
2134 }
2135 else
2136 {
2137 // We still fit, so don't add a line, and keep going
2138 currentWidth += childSize.x;
2139 lineHeight = wxMax(lineHeight, childSize.y);
2140 maxDescent = wxMax(childDescent, maxDescent);
2141
2142 maxWidth = wxMax(maxWidth, currentWidth);
2143 lastEndPos = child->GetRange().GetEnd();
2144
2145 node = node->GetNext();
2146 }
2147 }
2148
2149 // Add the last line - it's the current pos -> last para pos
2150 // Substract -1 because the last position is always the end-paragraph position.
2151 if (lastCompletedEndPos <= GetRange().GetEnd()-1)
2152 {
2153 currentPosition.x = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
2154
2155 wxRichTextLine* line = AllocateLine(lineCount);
2156
2157 wxRichTextRange actualRange(lastCompletedEndPos+1, GetRange().GetEnd()-1);
2158
2159 // Set relative range so we won't have to change line ranges when paragraphs are moved
2160 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
2161
2162 line->SetPosition(currentPosition);
2163
2164 if (lineHeight == 0)
2165 {
2166 if (GetAttributes().GetFont().Ok())
2167 dc.SetFont(GetAttributes().GetFont());
2168 lineHeight = dc.GetCharHeight();
2169 }
2170 if (maxDescent == 0)
2171 {
2172 int w, h;
2173 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
2174 }
2175
2176 line->SetSize(wxSize(currentWidth, lineHeight));
2177 line->SetDescent(maxDescent);
2178 currentPosition.y += lineHeight;
2179 currentPosition.y += lineSpacing;
2180 lineCount ++;
2181 }
2182
2183 // Remove remaining unused line objects, if any
2184 ClearUnusedLines(lineCount);
2185
2186 // Apply styles to wrapped lines
2187 ApplyParagraphStyle(rect);
2188
2189 SetCachedSize(wxSize(maxWidth, currentPosition.y + spaceBeforePara + spaceAfterPara));
2190
2191 m_dirty = false;
2192
2193 return true;
2194 }
2195
2196 /// Apply paragraph styles, such as centering, to wrapped lines
2197 void wxRichTextParagraph::ApplyParagraphStyle(const wxRect& rect)
2198 {
2199 if (!GetAttributes().HasAlignment())
2200 return;
2201
2202 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
2203 while (node)
2204 {
2205 wxRichTextLine* line = node->GetData();
2206
2207 wxPoint pos = line->GetPosition();
2208 wxSize size = line->GetSize();
2209
2210 // centering, right-justification
2211 if (GetAttributes().HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
2212 {
2213 pos.x = (rect.GetWidth() - size.x)/2 + pos.x;
2214 line->SetPosition(pos);
2215 }
2216 else if (GetAttributes().HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
2217 {
2218 pos.x = rect.GetRight() - size.x;
2219 line->SetPosition(pos);
2220 }
2221
2222 node = node->GetNext();
2223 }
2224 }
2225
2226 /// Insert text at the given position
2227 bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
2228 {
2229 wxRichTextObject* childToUse = NULL;
2230 wxRichTextObjectList::compatibility_iterator nodeToUse = NULL;
2231
2232 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2233 while (node)
2234 {
2235 wxRichTextObject* child = node->GetData();
2236 if (child->GetRange().Contains(pos) && child->GetRange().GetLength() > 0)
2237 {
2238 childToUse = child;
2239 nodeToUse = node;
2240 break;
2241 }
2242
2243 node = node->GetNext();
2244 }
2245
2246 if (childToUse)
2247 {
2248 wxRichTextPlainText* textObject = wxDynamicCast(childToUse, wxRichTextPlainText);
2249 if (textObject)
2250 {
2251 int posInString = pos - textObject->GetRange().GetStart();
2252
2253 wxString newText = textObject->GetText().Mid(0, posInString) +
2254 text + textObject->GetText().Mid(posInString);
2255 textObject->SetText(newText);
2256
2257 int textLength = text.Length();
2258
2259 textObject->SetRange(wxRichTextRange(textObject->GetRange().GetStart(),
2260 textObject->GetRange().GetEnd() + textLength));
2261
2262 // Increment the end range of subsequent fragments in this paragraph.
2263 // We'll set the paragraph range itself at a higher level.
2264
2265 wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
2266 while (node)
2267 {
2268 wxRichTextObject* child = node->GetData();
2269 child->SetRange(wxRichTextRange(textObject->GetRange().GetStart() + textLength,
2270 textObject->GetRange().GetEnd() + textLength));
2271
2272 node = node->GetNext();
2273 }
2274
2275 return true;
2276 }
2277 else
2278 {
2279 // TODO: if not a text object, insert at closest position, e.g. in front of it
2280 }
2281 }
2282 else
2283 {
2284 // Add at end.
2285 // Don't pass parent initially to suppress auto-setting of parent range.
2286 // We'll do that at a higher level.
2287 wxRichTextPlainText* textObject = new wxRichTextPlainText(text, this);
2288
2289 AppendChild(textObject);
2290 return true;
2291 }
2292
2293 return false;
2294 }
2295
2296 void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
2297 {
2298 wxRichTextBox::Copy(obj);
2299 }
2300
2301 /// Clear the cached lines
2302 void wxRichTextParagraph::ClearLines()
2303 {
2304 WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
2305 }
2306
2307 /// Get/set the object size for the given range. Returns false if the range
2308 /// is invalid for this object.
2309 bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags) const
2310 {
2311 if (!range.IsWithin(GetRange()))
2312 return false;
2313
2314 if (flags & wxRICHTEXT_UNFORMATTED)
2315 {
2316 // Just use unformatted data, assume no line breaks
2317 // TODO: take into account line breaks
2318
2319 wxSize sz;
2320
2321 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2322 while (node)
2323 {
2324 wxRichTextObject* child = node->GetData();
2325 if (!child->GetRange().IsOutside(range))
2326 {
2327 wxSize childSize;
2328
2329 wxRichTextRange rangeToUse = range;
2330 rangeToUse.LimitTo(child->GetRange());
2331 int childDescent = 0;
2332
2333 if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, flags))
2334 {
2335 sz.y = wxMax(sz.y, childSize.y);
2336 sz.x += childSize.x;
2337 descent = wxMax(descent, childDescent);
2338 }
2339 }
2340
2341 node = node->GetNext();
2342 }
2343 size = sz;
2344 }
2345 else
2346 {
2347 // Use formatted data, with line breaks
2348 wxSize sz;
2349
2350 // We're going to loop through each line, and then for each line,
2351 // call GetRangeSize for the fragment that comprises that line.
2352 // Only we have to do that multiple times within the line, because
2353 // the line may be broken into pieces. For now ignore line break commands
2354 // (so we can assume that getting the unformatted size for a fragment
2355 // within a line is the actual size)
2356
2357 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
2358 while (node)
2359 {
2360 wxRichTextLine* line = node->GetData();
2361 wxRichTextRange lineRange = line->GetAbsoluteRange();
2362 if (!lineRange.IsOutside(range))
2363 {
2364 wxSize lineSize;
2365
2366 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
2367 while (node2)
2368 {
2369 wxRichTextObject* child = node2->GetData();
2370
2371 if (!child->GetRange().IsOutside(lineRange))
2372 {
2373 wxRichTextRange rangeToUse = lineRange;
2374 rangeToUse.LimitTo(child->GetRange());
2375
2376 wxSize childSize;
2377 int childDescent = 0;
2378 if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, flags))
2379 {
2380 lineSize.y = wxMax(lineSize.y, childSize.y);
2381 lineSize.x += childSize.x;
2382 }
2383 descent = wxMax(descent, childDescent);
2384 }
2385
2386 node2 = node2->GetNext();
2387 }
2388
2389 // Increase size by a line (TODO: paragraph spacing)
2390 sz.y += lineSize.y;
2391 sz.x = wxMax(sz.x, lineSize.x);
2392 }
2393 node = node->GetNext();
2394 }
2395 size = sz;
2396 }
2397 return true;
2398 }
2399
2400 /// Finds the absolute position and row height for the given character position
2401 bool wxRichTextParagraph::FindPosition(wxDC& dc, long index, wxPoint& pt, int* height, bool forceLineStart)
2402 {
2403 if (index == -1)
2404 {
2405 wxRichTextLine* line = ((wxRichTextParagraphLayoutBox*)GetParent())->GetLineAtPosition(0);
2406 if (line)
2407 *height = line->GetSize().y;
2408 else
2409 *height = dc.GetCharHeight();
2410
2411 // -1 means 'the start of the buffer'.
2412 pt = GetPosition();
2413 if (line)
2414 pt = pt + line->GetPosition();
2415
2416 *height = dc.GetCharHeight();
2417
2418 return true;
2419 }
2420
2421 // The final position in a paragraph is taken to mean the position
2422 // at the start of the next paragraph.
2423 if (index == GetRange().GetEnd())
2424 {
2425 wxRichTextParagraphLayoutBox* parent = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
2426 wxASSERT( parent != NULL );
2427
2428 // Find the height at the next paragraph, if any
2429 wxRichTextLine* line = parent->GetLineAtPosition(index + 1);
2430 if (line)
2431 {
2432 *height = line->GetSize().y;
2433 pt = line->GetAbsolutePosition();
2434 }
2435 else
2436 {
2437 *height = dc.GetCharHeight();
2438 int indent = ConvertTenthsMMToPixels(dc, m_attributes.GetLeftIndent());
2439 pt = wxPoint(indent, GetCachedSize().y);
2440 }
2441
2442 return true;
2443 }
2444
2445 if (index < GetRange().GetStart() || index > GetRange().GetEnd())
2446 return false;
2447
2448 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
2449 while (node)
2450 {
2451 wxRichTextLine* line = node->GetData();
2452 wxRichTextRange lineRange = line->GetAbsoluteRange();
2453 if (index >= lineRange.GetStart() && index <= lineRange.GetEnd())
2454 {
2455 // If this is the last point in the line, and we're forcing the
2456 // returned value to be the start of the next line, do the required
2457 // thing.
2458 if (index == lineRange.GetEnd() && forceLineStart)
2459 {
2460 if (node->GetNext())
2461 {
2462 wxRichTextLine* nextLine = node->GetNext()->GetData();
2463 *height = nextLine->GetSize().y;
2464 pt = nextLine->GetAbsolutePosition();
2465 return true;
2466 }
2467 }
2468
2469 pt.y = line->GetPosition().y + GetPosition().y;
2470
2471 wxRichTextRange r(lineRange.GetStart(), index);
2472 wxSize rangeSize;
2473 int descent = 0;
2474
2475 // We find the size of the line up to this point,
2476 // then we can add this size to the line start position and
2477 // paragraph start position to find the actual position.
2478
2479 if (GetRangeSize(r, rangeSize, descent, dc, wxRICHTEXT_UNFORMATTED))
2480 {
2481 pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
2482 *height = line->GetSize().y;
2483
2484 return true;
2485 }
2486
2487 }
2488
2489 node = node->GetNext();
2490 }
2491
2492 return false;
2493 }
2494
2495 /// Hit-testing: returns a flag indicating hit test details, plus
2496 /// information about position
2497 int wxRichTextParagraph::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition)
2498 {
2499 wxPoint paraPos = GetPosition();
2500
2501 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
2502 while (node)
2503 {
2504 wxRichTextLine* line = node->GetData();
2505 wxPoint linePos = paraPos + line->GetPosition();
2506 wxSize lineSize = line->GetSize();
2507 wxRichTextRange lineRange = line->GetAbsoluteRange();
2508
2509 if (pt.y >= linePos.y && pt.y <= linePos.y + lineSize.y)
2510 {
2511 if (pt.x < linePos.x)
2512 {
2513 textPosition = lineRange.GetStart();
2514 return wxRICHTEXT_HITTEST_BEFORE;
2515 }
2516 else if (pt.x >= (linePos.x + lineSize.x))
2517 {
2518 textPosition = lineRange.GetEnd();
2519 return wxRICHTEXT_HITTEST_AFTER;
2520 }
2521 else
2522 {
2523 long i;
2524 int lastX = linePos.x;
2525 for (i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++)
2526 {
2527 wxSize childSize;
2528 int descent = 0;
2529
2530 wxRichTextRange rangeToUse(lineRange.GetStart(), i);
2531
2532 GetRangeSize(rangeToUse, childSize, descent, dc, wxRICHTEXT_UNFORMATTED);
2533
2534 int nextX = childSize.x + linePos.x;
2535
2536 if (pt.x >= lastX && pt.x <= nextX)
2537 {
2538 textPosition = i;
2539
2540 // So now we know it's between i-1 and i.
2541 // Let's see if we can be more precise about
2542 // which side of the position it's on.
2543
2544 int midPoint = (nextX - lastX)/2 + lastX;
2545 if (pt.x >= midPoint)
2546 return wxRICHTEXT_HITTEST_AFTER;
2547 else
2548 return wxRICHTEXT_HITTEST_BEFORE;
2549 }
2550 else
2551 {
2552 lastX = nextX;
2553 }
2554 }
2555 }
2556 }
2557
2558 node = node->GetNext();
2559 }
2560
2561 return wxRICHTEXT_HITTEST_NONE;
2562 }
2563
2564 /// Split an object at this position if necessary, and return
2565 /// the previous object, or NULL if inserting at beginning.
2566 wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** previousObject)
2567 {
2568 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2569 while (node)
2570 {
2571 wxRichTextObject* child = node->GetData();
2572
2573 if (pos == child->GetRange().GetStart())
2574 {
2575 if (previousObject)
2576 {
2577 if (node->GetPrevious())
2578 *previousObject = node->GetPrevious()->GetData();
2579 else
2580 *previousObject = NULL;
2581 }
2582
2583 return child;
2584 }
2585
2586 if (child->GetRange().Contains(pos))
2587 {
2588 // This should create a new object, transferring part of
2589 // the content to the old object and the rest to the new object.
2590 wxRichTextObject* newObject = child->DoSplit(pos);
2591
2592 // If we couldn't split this object, just insert in front of it.
2593 if (!newObject)
2594 {
2595 // Maybe this is an empty string, try the next one
2596 // return child;
2597 }
2598 else
2599 {
2600 // Insert the new object after 'child'
2601 if (node->GetNext())
2602 m_children.Insert(node->GetNext(), newObject);
2603 else
2604 m_children.Append(newObject);
2605 newObject->SetParent(this);
2606
2607 if (previousObject)
2608 *previousObject = child;
2609
2610 return newObject;
2611 }
2612 }
2613
2614 node = node->GetNext();
2615 }
2616 if (previousObject)
2617 *previousObject = NULL;
2618 return NULL;
2619 }
2620
2621 /// Move content to a list from obj on
2622 void wxRichTextParagraph::MoveToList(wxRichTextObject* obj, wxList& list)
2623 {
2624 wxRichTextObjectList::compatibility_iterator node = m_children.Find(obj);
2625 while (node)
2626 {
2627 wxRichTextObject* child = node->GetData();
2628 list.Append(child);
2629
2630 wxRichTextObjectList::compatibility_iterator oldNode = node;
2631
2632 node = node->GetNext();
2633
2634 m_children.DeleteNode(oldNode);
2635 }
2636 }
2637
2638 /// Add content back from list
2639 void wxRichTextParagraph::MoveFromList(wxList& list)
2640 {
2641 for (wxNode* node = list.GetFirst(); node; node = node->GetNext())
2642 {
2643 AppendChild((wxRichTextObject*) node->GetData());
2644 }
2645 }
2646
2647 /// Calculate range
2648 void wxRichTextParagraph::CalculateRange(long start, long& end)
2649 {
2650 wxRichTextCompositeObject::CalculateRange(start, end);
2651
2652 // Add one for end of paragraph
2653 end ++;
2654
2655 m_range.SetRange(start, end);
2656 }
2657
2658 /// Find the object at the given position
2659 wxRichTextObject* wxRichTextParagraph::FindObjectAtPosition(long position)
2660 {
2661 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2662 while (node)
2663 {
2664 wxRichTextObject* obj = node->GetData();
2665 if (obj->GetRange().Contains(position))
2666 return obj;
2667
2668 node = node->GetNext();
2669 }
2670 return NULL;
2671 }
2672
2673 /// Get the plain text searching from the start or end of the range.
2674 /// The resulting string may be shorter than the range given.
2675 bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTextRange& range, bool fromStart)
2676 {
2677 text = wxEmptyString;
2678
2679 if (fromStart)
2680 {
2681 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2682 while (node)
2683 {
2684 wxRichTextObject* obj = node->GetData();
2685 if (!obj->GetRange().IsOutside(range))
2686 {
2687 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
2688 if (textObj)
2689 {
2690 text += textObj->GetTextForRange(range);
2691 }
2692 else
2693 return true;
2694 }
2695
2696 node = node->GetNext();
2697 }
2698 }
2699 else
2700 {
2701 wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
2702 while (node)
2703 {
2704 wxRichTextObject* obj = node->GetData();
2705 if (!obj->GetRange().IsOutside(range))
2706 {
2707 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
2708 if (textObj)
2709 {
2710 text = textObj->GetTextForRange(range) + text;
2711 }
2712 else
2713 return true;
2714 }
2715
2716 node = node->GetPrevious();
2717 }
2718 }
2719
2720 return true;
2721 }
2722
2723 /// Find a suitable wrap position.
2724 bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, int availableSpace, long& wrapPosition)
2725 {
2726 // Find the first position where the line exceeds the available space.
2727 wxSize sz;
2728 long i;
2729 long breakPosition = range.GetEnd();
2730 for (i = range.GetStart(); i <= range.GetEnd(); i++)
2731 {
2732 int descent = 0;
2733 GetRangeSize(wxRichTextRange(range.GetStart(), i), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
2734
2735 if (sz.x > availableSpace)
2736 {
2737 breakPosition = i-1;
2738 break;
2739 }
2740 }
2741
2742 // Now we know the last position on the line.
2743 // Let's try to find a word break.
2744
2745 wxString plainText;
2746 if (GetContiguousPlainText(plainText, wxRichTextRange(range.GetStart(), breakPosition), false))
2747 {
2748 int spacePos = plainText.Find(wxT(' '), true);
2749 if (spacePos != wxNOT_FOUND)
2750 {
2751 int positionsFromEndOfString = plainText.Length() - spacePos - 1;
2752 breakPosition = breakPosition - positionsFromEndOfString;
2753 }
2754 }
2755
2756 wrapPosition = breakPosition;
2757
2758 return true;
2759 }
2760
2761 /// Get the bullet text for this paragraph.
2762 wxString wxRichTextParagraph::GetBulletText()
2763 {
2764 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ||
2765 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP))
2766 return wxEmptyString;
2767
2768 int number = GetAttributes().GetBulletNumber();
2769
2770 wxString text;
2771 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC)
2772 {
2773 text.Printf(wxT("%d"), number);
2774 }
2775 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER)
2776 {
2777 // TODO: Unicode, and also check if number > 26
2778 text.Printf(wxT("%c"), (wxChar) (number+64));
2779 }
2780 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER)
2781 {
2782 // TODO: Unicode, and also check if number > 26
2783 text.Printf(wxT("%c"), (wxChar) (number+96));
2784 }
2785 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
2786 {
2787 // TODO: convert from number to roman numeral
2788 if (number == 1)
2789 text = wxT("I");
2790 else if (number == 2)
2791 text = wxT("II");
2792 else if (number == 3)
2793 text = wxT("III");
2794 else if (number == 4)
2795 text = wxT("IV");
2796 else
2797 text = wxT("TODO");
2798 }
2799 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
2800 {
2801 // TODO: convert from number to roman numeral
2802 if (number == 1)
2803 text = wxT("i");
2804 else if (number == 2)
2805 text = wxT("ii");
2806 else if (number == 3)
2807 text = wxT("iii");
2808 else if (number == 4)
2809 text = wxT("iv");
2810 else
2811 text = wxT("TODO");
2812 }
2813 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
2814 {
2815 text = GetAttributes().GetBulletSymbol();
2816 }
2817
2818 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES)
2819 {
2820 text = wxT("(") + text + wxT(")");
2821 }
2822 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD)
2823 {
2824 text += wxT(".");
2825 }
2826
2827 return text;
2828 }
2829
2830 /// Allocate or reuse a line object
2831 wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
2832 {
2833 if (pos < (int) m_cachedLines.GetCount())
2834 {
2835 wxRichTextLine* line = m_cachedLines.Item(pos)->GetData();
2836 line->Init(this);
2837 return line;
2838 }
2839 else
2840 {
2841 wxRichTextLine* line = new wxRichTextLine(this);
2842 m_cachedLines.Append(line);
2843 return line;
2844 }
2845 }
2846
2847 /// Clear remaining unused line objects, if any
2848 bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
2849 {
2850 int cachedLineCount = m_cachedLines.GetCount();
2851 if ((int) cachedLineCount > lineCount)
2852 {
2853 for (int i = 0; i < (int) (cachedLineCount - lineCount); i ++)
2854 {
2855 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetLast();
2856 wxRichTextLine* line = node->GetData();
2857 m_cachedLines.Erase(node);
2858 delete line;
2859 }
2860 }
2861 return true;
2862 }
2863
2864
2865 /*!
2866 * wxRichTextLine
2867 * This object represents a line in a paragraph, and stores
2868 * offsets from the start of the paragraph representing the
2869 * start and end positions of the line.
2870 */
2871
2872 wxRichTextLine::wxRichTextLine(wxRichTextParagraph* parent)
2873 {
2874 Init(parent);
2875 }
2876
2877 /// Initialisation
2878 void wxRichTextLine::Init(wxRichTextParagraph* parent)
2879 {
2880 m_parent = parent;
2881 m_range.SetRange(-1, -1);
2882 m_pos = wxPoint(0, 0);
2883 m_size = wxSize(0, 0);
2884 m_descent = 0;
2885 }
2886
2887 /// Copy
2888 void wxRichTextLine::Copy(const wxRichTextLine& obj)
2889 {
2890 m_range = obj.m_range;
2891 }
2892
2893 /// Get the absolute object position
2894 wxPoint wxRichTextLine::GetAbsolutePosition() const
2895 {
2896 return m_parent->GetPosition() + m_pos;
2897 }
2898
2899 /// Get the absolute range
2900 wxRichTextRange wxRichTextLine::GetAbsoluteRange() const
2901 {
2902 wxRichTextRange range(m_range.GetStart() + m_parent->GetRange().GetStart(), 0);
2903 range.SetEnd(range.GetStart() + m_range.GetLength()-1);
2904 return range;
2905 }
2906
2907 /*!
2908 * wxRichTextPlainText
2909 * This object represents a single piece of text.
2910 */
2911
2912 IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText, wxRichTextObject)
2913
2914 wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* parent, wxTextAttrEx* style):
2915 wxRichTextObject(parent)
2916 {
2917 if (parent && !style)
2918 SetAttributes(parent->GetAttributes());
2919 if (style)
2920 SetAttributes(*style);
2921
2922 m_text = text;
2923 }
2924
2925 /// Draw the item
2926 bool wxRichTextPlainText::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int descent, int WXUNUSED(style))
2927 {
2928 int offset = GetRange().GetStart();
2929
2930 long len = range.GetLength();
2931 wxString stringChunk = m_text.Mid(range.GetStart() - offset, (size_t) len);
2932
2933 int charHeight = dc.GetCharHeight();
2934
2935 int x = rect.x;
2936 int y = rect.y + (rect.height - charHeight - (descent - m_descent));
2937
2938 // Test for the optimized situations where all is selected, or none
2939 // is selected.
2940
2941 if (GetAttributes().GetFont().Ok())
2942 dc.SetFont(GetAttributes().GetFont());
2943
2944 // (a) All selected.
2945 if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd())
2946 {
2947 // Draw all selected
2948 dc.SetBrush(*wxBLACK_BRUSH);
2949 dc.SetPen(*wxBLACK_PEN);
2950 wxCoord w, h;
2951 dc.GetTextExtent(stringChunk, & w, & h);
2952 wxRect selRect(x, rect.y, w, rect.GetHeight());
2953 dc.DrawRectangle(selRect);
2954 dc.SetTextForeground(*wxWHITE);
2955 dc.SetBackgroundMode(wxTRANSPARENT);
2956 dc.DrawText(stringChunk, x, y);
2957 }
2958 // (b) None selected.
2959 else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd())
2960 {
2961 // Draw all unselected
2962 dc.SetTextForeground(GetAttributes().GetTextColour());
2963 dc.SetBackgroundMode(wxTRANSPARENT);
2964 dc.DrawText(stringChunk, x, y);
2965 }
2966 else
2967 {
2968 // (c) Part selected, part not
2969 // Let's draw unselected chunk, selected chunk, then unselected chunk.
2970
2971 dc.SetBackgroundMode(wxTRANSPARENT);
2972
2973 // 1. Initial unselected chunk, if any, up until start of selection.
2974 if (selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd())
2975 {
2976 int r1 = range.GetStart();
2977 int s1 = selectionRange.GetStart()-1;
2978 int fragmentLen = s1 - r1 + 1;
2979 if (fragmentLen < 0)
2980 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 - offset), (int)fragmentLen);
2981 wxString stringFragment = m_text.Mid(r1 - offset, fragmentLen);
2982
2983 dc.SetTextForeground(GetAttributes().GetTextColour());
2984 dc.DrawText(stringFragment, x, y);
2985
2986 wxCoord w, h;
2987 dc.GetTextExtent(stringFragment, & w, & h);
2988 x += w;
2989 }
2990
2991 // 2. Selected chunk, if any.
2992 if (selectionRange.GetEnd() >= range.GetStart())
2993 {
2994 int s1 = wxMax(selectionRange.GetStart(), range.GetStart());
2995 int s2 = wxMin(selectionRange.GetEnd(), range.GetEnd());
2996
2997 int fragmentLen = s2 - s1 + 1;
2998 if (fragmentLen < 0)
2999 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 - offset), (int)fragmentLen);
3000 wxString stringFragment = m_text.Mid(s1 - offset, fragmentLen);
3001
3002 wxCoord w, h;
3003 dc.GetTextExtent(stringFragment, & w, & h);
3004 wxRect selRect(x, rect.y, w, rect.GetHeight());
3005
3006 dc.SetBrush(*wxBLACK_BRUSH);
3007 dc.SetPen(*wxBLACK_PEN);
3008 dc.DrawRectangle(selRect);
3009 dc.SetTextForeground(*wxWHITE);
3010 dc.DrawText(stringFragment, x, y);
3011
3012 x += w;
3013 }
3014
3015 // 3. Remaining unselected chunk, if any
3016 if (selectionRange.GetEnd() < range.GetEnd())
3017 {
3018 int s2 = wxMin(selectionRange.GetEnd()+1, range.GetEnd());
3019 int r2 = range.GetEnd();
3020
3021 int fragmentLen = r2 - s2 + 1;
3022 if (fragmentLen < 0)
3023 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 - offset), (int)fragmentLen);
3024 wxString stringFragment = m_text.Mid(s2 - offset, fragmentLen);
3025
3026 dc.SetTextForeground(GetAttributes().GetTextColour());
3027 dc.DrawText(stringFragment, x, y);
3028 }
3029 }
3030
3031 return true;
3032 }
3033
3034 /// Lay the item out
3035 bool wxRichTextPlainText::Layout(wxDC& dc, const wxRect& WXUNUSED(rect), int WXUNUSED(style))
3036 {
3037 if (GetAttributes().GetFont().Ok())
3038 dc.SetFont(GetAttributes().GetFont());
3039
3040 wxCoord w, h;
3041 dc.GetTextExtent(m_text, & w, & h, & m_descent);
3042 m_size = wxSize(w, dc.GetCharHeight());
3043
3044 return true;
3045 }
3046
3047 /// Copy
3048 void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj)
3049 {
3050 wxRichTextObject::Copy(obj);
3051
3052 m_text = obj.m_text;
3053 }
3054
3055 /// Get/set the object size for the given range. Returns false if the range
3056 /// is invalid for this object.
3057 bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int WXUNUSED(flags)) const
3058 {
3059 if (!range.IsWithin(GetRange()))
3060 return false;
3061
3062 // Always assume unformatted text, since at this level we have no knowledge
3063 // of line breaks - and we don't need it, since we'll calculate size within
3064 // formatted text by doing it in chunks according to the line ranges
3065
3066 if (GetAttributes().GetFont().Ok())
3067 dc.SetFont(GetAttributes().GetFont());
3068
3069 int startPos = range.GetStart() - GetRange().GetStart();
3070 long len = range.GetLength();
3071 wxString stringChunk = m_text.Mid(startPos, (size_t) len);
3072 wxCoord w, h;
3073 dc.GetTextExtent(stringChunk, & w, & h, & descent);
3074 size = wxSize(w, dc.GetCharHeight());
3075
3076 return true;
3077 }
3078
3079 /// Do a split, returning an object containing the second part, and setting
3080 /// the first part in 'this'.
3081 wxRichTextObject* wxRichTextPlainText::DoSplit(long pos)
3082 {
3083 int index = pos - GetRange().GetStart();
3084 if (index < 0 || index >= (int) m_text.Length())
3085 return NULL;
3086
3087 wxString firstPart = m_text.Mid(0, index);
3088 wxString secondPart = m_text.Mid(index);
3089
3090 m_text = firstPart;
3091
3092 wxRichTextPlainText* newObject = new wxRichTextPlainText(secondPart);
3093 newObject->SetAttributes(GetAttributes());
3094
3095 newObject->SetRange(wxRichTextRange(pos, GetRange().GetEnd()));
3096 GetRange().SetEnd(pos-1);
3097
3098 return newObject;
3099 }
3100
3101 /// Calculate range
3102 void wxRichTextPlainText::CalculateRange(long start, long& end)
3103 {
3104 end = start + m_text.Length() - 1;
3105 m_range.SetRange(start, end);
3106 }
3107
3108 /// Delete range
3109 bool wxRichTextPlainText::DeleteRange(const wxRichTextRange& range)
3110 {
3111 wxRichTextRange r = range;
3112
3113 r.LimitTo(GetRange());
3114
3115 if (r.GetStart() == GetRange().GetStart() && r.GetEnd() == GetRange().GetEnd())
3116 {
3117 m_text.Empty();
3118 return true;
3119 }
3120
3121 long startIndex = r.GetStart() - GetRange().GetStart();
3122 long len = r.GetLength();
3123
3124 m_text = m_text.Mid(0, startIndex) + m_text.Mid(startIndex+len);
3125 return true;
3126 }
3127
3128 /// Get text for the given range.
3129 wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) const
3130 {
3131 wxRichTextRange r = range;
3132
3133 r.LimitTo(GetRange());
3134
3135 long startIndex = r.GetStart() - GetRange().GetStart();
3136 long len = r.GetLength();
3137
3138 return m_text.Mid(startIndex, len);
3139 }
3140
3141 /// Returns true if this object can merge itself with the given one.
3142 bool wxRichTextPlainText::CanMerge(wxRichTextObject* object) const
3143 {
3144 return object->GetClassInfo() == CLASSINFO(wxRichTextPlainText) &&
3145 (m_text.empty() || wxTextAttrEq(GetAttributes(), object->GetAttributes()));
3146 }
3147
3148 /// Returns true if this object merged itself with the given one.
3149 /// The calling code will then delete the given object.
3150 bool wxRichTextPlainText::Merge(wxRichTextObject* object)
3151 {
3152 wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText);
3153 wxASSERT( textObject != NULL );
3154
3155 if (textObject)
3156 {
3157 m_text += textObject->GetText();
3158 return true;
3159 }
3160 else
3161 return false;
3162 }
3163
3164 /// Dump to output stream for debugging
3165 void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
3166 {
3167 wxRichTextObject::Dump(stream);
3168 stream << m_text << wxT("\n");
3169 }
3170
3171 /*!
3172 * wxRichTextBuffer
3173 * This is a kind of box, used to represent the whole buffer
3174 */
3175
3176 IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox)
3177
3178 wxList wxRichTextBuffer::sm_handlers;
3179
3180 /// Initialisation
3181 void wxRichTextBuffer::Init()
3182 {
3183 m_commandProcessor = new wxCommandProcessor;
3184 m_styleSheet = NULL;
3185 m_modified = false;
3186 m_batchedCommandDepth = 0;
3187 m_batchedCommand = NULL;
3188 m_suppressUndo = 0;
3189 }
3190
3191 /// Initialisation
3192 wxRichTextBuffer::~wxRichTextBuffer()
3193 {
3194 delete m_commandProcessor;
3195 delete m_batchedCommand;
3196
3197 ClearStyleStack();
3198 }
3199
3200 void wxRichTextBuffer::Clear()
3201 {
3202 DeleteChildren();
3203 GetCommandProcessor()->ClearCommands();
3204 Modify(false);
3205 Invalidate(wxRICHTEXT_ALL);
3206 }
3207
3208 void wxRichTextBuffer::Reset()
3209 {
3210 DeleteChildren();
3211 AddParagraph(wxEmptyString);
3212 GetCommandProcessor()->ClearCommands();
3213 Modify(false);
3214 Invalidate(wxRICHTEXT_ALL);
3215 }
3216
3217 /// Submit command to insert the given text
3218 bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl)
3219 {
3220 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, this, ctrl, false);
3221
3222 action->GetNewParagraphs().AddParagraphs(text);
3223 if (action->GetNewParagraphs().GetChildCount() == 1)
3224 action->GetNewParagraphs().SetPartialParagraph(true);
3225
3226 action->SetPosition(pos);
3227
3228 // Set the range we'll need to delete in Undo
3229 action->SetRange(wxRichTextRange(pos, pos + text.Length() - 1));
3230
3231 SubmitAction(action);
3232
3233 return true;
3234 }
3235
3236 /// Submit command to insert the given text
3237 bool wxRichTextBuffer::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl)
3238 {
3239 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, this, ctrl, false);
3240
3241 wxTextAttrEx attr(GetBasicStyle());
3242 wxRichTextApplyStyle(attr, GetDefaultStyle());
3243
3244 wxRichTextParagraph* newPara = new wxRichTextParagraph(wxEmptyString, this, & attr);
3245 action->GetNewParagraphs().AppendChild(newPara);
3246 action->GetNewParagraphs().UpdateRanges();
3247 action->GetNewParagraphs().SetPartialParagraph(false);
3248 action->SetPosition(pos);
3249
3250 // Set the range we'll need to delete in Undo
3251 action->SetRange(wxRichTextRange(pos, pos));
3252
3253 SubmitAction(action);
3254
3255 return true;
3256 }
3257
3258 /// Submit command to insert the given image
3259 bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock, wxRichTextCtrl* ctrl)
3260 {
3261 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, ctrl, false);
3262
3263 wxTextAttrEx attr(GetBasicStyle());
3264 wxRichTextApplyStyle(attr, GetDefaultStyle());
3265
3266 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
3267 wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
3268 newPara->AppendChild(imageObject);
3269 action->GetNewParagraphs().AppendChild(newPara);
3270 action->GetNewParagraphs().UpdateRanges();
3271
3272 action->GetNewParagraphs().SetPartialParagraph(true);
3273
3274 action->SetPosition(pos);
3275
3276 // Set the range we'll need to delete in Undo
3277 action->SetRange(wxRichTextRange(pos, pos));
3278
3279 SubmitAction(action);
3280
3281 return true;
3282 }
3283
3284 /// Submit command to delete this range
3285 bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange& range, long initialCaretPosition, long WXUNUSED(newCaretPositon), wxRichTextCtrl* ctrl)
3286 {
3287 wxRichTextAction* action = new wxRichTextAction(NULL, _("Delete"), wxRICHTEXT_DELETE, this, ctrl);
3288
3289 action->SetPosition(initialCaretPosition);
3290
3291 // Set the range to delete
3292 action->SetRange(range);
3293
3294 // Copy the fragment that we'll need to restore in Undo
3295 CopyFragment(range, action->GetOldParagraphs());
3296
3297 // Special case: if there is only one (non-partial) paragraph,
3298 // we must save the *next* paragraph's style, because that
3299 // is the style we must apply when inserting the content back
3300 // when undoing the delete. (This is because we're merging the
3301 // paragraph with the previous paragraph and throwing away
3302 // the style, and we need to restore it.)
3303 if (!action->GetOldParagraphs().GetPartialParagraph() && action->GetOldParagraphs().GetChildCount() == 1)
3304 {
3305 wxRichTextParagraph* lastPara = GetParagraphAtPosition(range.GetStart());
3306 if (lastPara)
3307 {
3308 wxRichTextParagraph* nextPara = GetParagraphAtPosition(range.GetEnd()+1);
3309 if (nextPara)
3310 {
3311 wxRichTextParagraph* para = (wxRichTextParagraph*) action->GetOldParagraphs().GetChild(0);
3312 para->SetAttributes(nextPara->GetAttributes());
3313 }
3314 }
3315 }
3316
3317 SubmitAction(action);
3318
3319 return true;
3320 }
3321
3322 /// Collapse undo/redo commands
3323 bool wxRichTextBuffer::BeginBatchUndo(const wxString& cmdName)
3324 {
3325 if (m_batchedCommandDepth == 0)
3326 {
3327 wxASSERT(m_batchedCommand == NULL);
3328 if (m_batchedCommand)
3329 {
3330 GetCommandProcessor()->Submit(m_batchedCommand);
3331 }
3332 m_batchedCommand = new wxRichTextCommand(cmdName);
3333 }
3334
3335 m_batchedCommandDepth ++;
3336
3337 return true;
3338 }
3339
3340 /// Collapse undo/redo commands
3341 bool wxRichTextBuffer::EndBatchUndo()
3342 {
3343 m_batchedCommandDepth --;
3344
3345 wxASSERT(m_batchedCommandDepth >= 0);
3346 wxASSERT(m_batchedCommand != NULL);
3347
3348 if (m_batchedCommandDepth == 0)
3349 {
3350 GetCommandProcessor()->Submit(m_batchedCommand);
3351 m_batchedCommand = NULL;
3352 }
3353
3354 return true;
3355 }
3356
3357 /// Submit immediately, or delay according to whether collapsing is on
3358 bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action)
3359 {
3360 if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
3361 m_batchedCommand->AddAction(action);
3362 else
3363 {
3364 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
3365 cmd->AddAction(action);
3366
3367 // Only store it if we're not suppressing undo.
3368 return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
3369 }
3370
3371 return true;
3372 }
3373
3374 /// Begin suppressing undo/redo commands.
3375 bool wxRichTextBuffer::BeginSuppressUndo()
3376 {
3377 m_suppressUndo ++;
3378
3379 return true;
3380 }
3381
3382 /// End suppressing undo/redo commands.
3383 bool wxRichTextBuffer::EndSuppressUndo()
3384 {
3385 m_suppressUndo --;
3386
3387 return true;
3388 }
3389
3390 /// Begin using a style
3391 bool wxRichTextBuffer::BeginStyle(const wxTextAttrEx& style)
3392 {
3393 wxTextAttrEx newStyle(GetDefaultStyle());
3394
3395 // Save the old default style
3396 m_attributeStack.Append((wxObject*) new wxTextAttrEx(GetDefaultStyle()));
3397
3398 wxRichTextApplyStyle(newStyle, style);
3399 newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags());
3400
3401 SetDefaultStyle(newStyle);
3402
3403 // wxLogDebug("Default style size = %d", GetDefaultStyle().GetFont().GetPointSize());
3404
3405 return true;
3406 }
3407
3408 /// End the style
3409 bool wxRichTextBuffer::EndStyle()
3410 {
3411 if (m_attributeStack.GetFirst() == NULL)
3412 {
3413 wxLogDebug(_("Too many EndStyle calls!"));
3414 return false;
3415 }
3416
3417 wxNode* node = m_attributeStack.GetLast();
3418 wxTextAttrEx* attr = (wxTextAttrEx*)node->GetData();
3419 delete node;
3420
3421 SetDefaultStyle(*attr);
3422
3423 delete attr;
3424 return true;
3425 }
3426
3427 /// End all styles
3428 bool wxRichTextBuffer::EndAllStyles()
3429 {
3430 while (m_attributeStack.GetCount() != 0)
3431 EndStyle();
3432 return true;
3433 }
3434
3435 /// Clear the style stack
3436 void wxRichTextBuffer::ClearStyleStack()
3437 {
3438 for (wxNode* node = m_attributeStack.GetFirst(); node; node = node->GetNext())
3439 delete (wxTextAttrEx*) node->GetData();
3440 m_attributeStack.Clear();
3441 }
3442
3443 /// Begin using bold
3444 bool wxRichTextBuffer::BeginBold()
3445 {
3446 wxFont font(GetBasicStyle().GetFont());
3447 font.SetWeight(wxBOLD);
3448
3449 wxTextAttrEx attr;
3450 attr.SetFont(font,wxTEXT_ATTR_FONT_WEIGHT);
3451
3452 return BeginStyle(attr);
3453 }
3454
3455 /// Begin using italic
3456 bool wxRichTextBuffer::BeginItalic()
3457 {
3458 wxFont font(GetBasicStyle().GetFont());
3459 font.SetStyle(wxITALIC);
3460
3461 wxTextAttrEx attr;
3462 attr.SetFont(font, wxTEXT_ATTR_FONT_ITALIC);
3463
3464 return BeginStyle(attr);
3465 }
3466
3467 /// Begin using underline
3468 bool wxRichTextBuffer::BeginUnderline()
3469 {
3470 wxFont font(GetBasicStyle().GetFont());
3471 font.SetUnderlined(true);
3472
3473 wxTextAttrEx attr;
3474 attr.SetFont(font, wxTEXT_ATTR_FONT_UNDERLINE);
3475
3476 return BeginStyle(attr);
3477 }
3478
3479 /// Begin using point size
3480 bool wxRichTextBuffer::BeginFontSize(int pointSize)
3481 {
3482 wxFont font(GetBasicStyle().GetFont());
3483 font.SetPointSize(pointSize);
3484
3485 wxTextAttrEx attr;
3486 attr.SetFont(font, wxTEXT_ATTR_FONT_SIZE);
3487
3488 return BeginStyle(attr);
3489 }
3490
3491 /// Begin using this font
3492 bool wxRichTextBuffer::BeginFont(const wxFont& font)
3493 {
3494 wxTextAttrEx attr;
3495 attr.SetFlags(wxTEXT_ATTR_FONT);
3496 attr.SetFont(font);
3497
3498 return BeginStyle(attr);
3499 }
3500
3501 /// Begin using this colour
3502 bool wxRichTextBuffer::BeginTextColour(const wxColour& colour)
3503 {
3504 wxTextAttrEx attr;
3505 attr.SetFlags(wxTEXT_ATTR_TEXT_COLOUR);
3506 attr.SetTextColour(colour);
3507
3508 return BeginStyle(attr);
3509 }
3510
3511 /// Begin using alignment
3512 bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment)
3513 {
3514 wxTextAttrEx attr;
3515 attr.SetFlags(wxTEXT_ATTR_ALIGNMENT);
3516 attr.SetAlignment(alignment);
3517
3518 return BeginStyle(attr);
3519 }
3520
3521 /// Begin left indent
3522 bool wxRichTextBuffer::BeginLeftIndent(int leftIndent, int leftSubIndent)
3523 {
3524 wxTextAttrEx attr;
3525 attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
3526 attr.SetLeftIndent(leftIndent, leftSubIndent);
3527
3528 return BeginStyle(attr);
3529 }
3530
3531 /// Begin right indent
3532 bool wxRichTextBuffer::BeginRightIndent(int rightIndent)
3533 {
3534 wxTextAttrEx attr;
3535 attr.SetFlags(wxTEXT_ATTR_RIGHT_INDENT);
3536 attr.SetRightIndent(rightIndent);
3537
3538 return BeginStyle(attr);
3539 }
3540
3541 /// Begin paragraph spacing
3542 bool wxRichTextBuffer::BeginParagraphSpacing(int before, int after)
3543 {
3544 long flags = 0;
3545 if (before != 0)
3546 flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
3547 if (after != 0)
3548 flags |= wxTEXT_ATTR_PARA_SPACING_AFTER;
3549
3550 wxTextAttrEx attr;
3551 attr.SetFlags(flags);
3552 attr.SetParagraphSpacingBefore(before);
3553 attr.SetParagraphSpacingAfter(after);
3554
3555 return BeginStyle(attr);
3556 }
3557
3558 /// Begin line spacing
3559 bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing)
3560 {
3561 wxTextAttrEx attr;
3562 attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
3563 attr.SetLineSpacing(lineSpacing);
3564
3565 return BeginStyle(attr);
3566 }
3567
3568 /// Begin numbered bullet
3569 bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber, int leftIndent, int leftSubIndent, int bulletStyle)
3570 {
3571 wxTextAttrEx attr;
3572 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER|wxTEXT_ATTR_LEFT_INDENT);
3573 attr.SetBulletStyle(bulletStyle);
3574 attr.SetBulletNumber(bulletNumber);
3575 attr.SetLeftIndent(leftIndent, leftSubIndent);
3576
3577 return BeginStyle(attr);
3578 }
3579
3580 /// Begin symbol bullet
3581 bool wxRichTextBuffer::BeginSymbolBullet(wxChar symbol, int leftIndent, int leftSubIndent, int bulletStyle)
3582 {
3583 wxTextAttrEx attr;
3584 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_SYMBOL|wxTEXT_ATTR_LEFT_INDENT);
3585 attr.SetBulletStyle(bulletStyle);
3586 attr.SetLeftIndent(leftIndent, leftSubIndent);
3587 attr.SetBulletSymbol(symbol);
3588
3589 return BeginStyle(attr);
3590 }
3591
3592 /// Begin named character style
3593 bool wxRichTextBuffer::BeginCharacterStyle(const wxString& characterStyle)
3594 {
3595 if (GetStyleSheet())
3596 {
3597 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
3598 if (def)
3599 {
3600 wxTextAttrEx attr;
3601 def->GetStyle().CopyTo(attr);
3602 return BeginStyle(attr);
3603 }
3604 }
3605 return false;
3606 }
3607
3608 /// Begin named paragraph style
3609 bool wxRichTextBuffer::BeginParagraphStyle(const wxString& paragraphStyle)
3610 {
3611 if (GetStyleSheet())
3612 {
3613 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(paragraphStyle);
3614 if (def)
3615 {
3616 wxTextAttrEx attr;
3617 def->GetStyle().CopyTo(attr);
3618 return BeginStyle(attr);
3619 }
3620 }
3621 return false;
3622 }
3623
3624 /// Adds a handler to the end
3625 void wxRichTextBuffer::AddHandler(wxRichTextFileHandler *handler)
3626 {
3627 sm_handlers.Append(handler);
3628 }
3629
3630 /// Inserts a handler at the front
3631 void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler *handler)
3632 {
3633 sm_handlers.Insert( handler );
3634 }
3635
3636 /// Removes a handler
3637 bool wxRichTextBuffer::RemoveHandler(const wxString& name)
3638 {
3639 wxRichTextFileHandler *handler = FindHandler(name);
3640 if (handler)
3641 {
3642 sm_handlers.DeleteObject(handler);
3643 delete handler;
3644 return true;
3645 }
3646 else
3647 return false;
3648 }
3649
3650 /// Finds a handler by filename or, if supplied, type
3651 wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename, int imageType)
3652 {
3653 if (imageType != wxRICHTEXT_TYPE_ANY)
3654 return FindHandler(imageType);
3655 else
3656 {
3657 wxString path, file, ext;
3658 wxSplitPath(filename, & path, & file, & ext);
3659 return FindHandler(ext, imageType);
3660 }
3661 }
3662
3663
3664 /// Finds a handler by name
3665 wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& name)
3666 {
3667 wxList::compatibility_iterator node = sm_handlers.GetFirst();
3668 while (node)
3669 {
3670 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
3671 if (handler->GetName().Lower() == name.Lower()) return handler;
3672
3673 node = node->GetNext();
3674 }
3675 return NULL;
3676 }
3677
3678 /// Finds a handler by extension and type
3679 wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, int type)
3680 {
3681 wxList::compatibility_iterator node = sm_handlers.GetFirst();
3682 while (node)
3683 {
3684 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
3685 if ( handler->GetExtension().Lower() == extension.Lower() &&
3686 (type == wxRICHTEXT_TYPE_ANY || handler->GetType() == type) )
3687 return handler;
3688 node = node->GetNext();
3689 }
3690 return 0;
3691 }
3692
3693 /// Finds a handler by type
3694 wxRichTextFileHandler* wxRichTextBuffer::FindHandler(int type)
3695 {
3696 wxList::compatibility_iterator node = sm_handlers.GetFirst();
3697 while (node)
3698 {
3699 wxRichTextFileHandler *handler = (wxRichTextFileHandler *)node->GetData();
3700 if (handler->GetType() == type) return handler;
3701 node = node->GetNext();
3702 }
3703 return NULL;
3704 }
3705
3706 void wxRichTextBuffer::InitStandardHandlers()
3707 {
3708 if (!FindHandler(wxRICHTEXT_TYPE_TEXT))
3709 AddHandler(new wxRichTextPlainTextHandler);
3710 }
3711
3712 void wxRichTextBuffer::CleanUpHandlers()
3713 {
3714 wxList::compatibility_iterator node = sm_handlers.GetFirst();
3715 while (node)
3716 {
3717 wxRichTextFileHandler* handler = (wxRichTextFileHandler*)node->GetData();
3718 wxList::compatibility_iterator next = node->GetNext();
3719 delete handler;
3720 node = next;
3721 }
3722
3723 sm_handlers.Clear();
3724 }
3725
3726 wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* types)
3727 {
3728 if (types)
3729 types->Clear();
3730
3731 wxString wildcard;
3732
3733 wxList::compatibility_iterator node = GetHandlers().GetFirst();
3734 int count = 0;
3735 while (node)
3736 {
3737 wxRichTextFileHandler* handler = (wxRichTextFileHandler*) node->GetData();
3738 if (handler->IsVisible() && ((save && handler->CanSave()) || !save && handler->CanLoad()))
3739 {
3740 if (combine)
3741 {
3742 if (count > 0)
3743 wildcard += wxT(";");
3744 wildcard += wxT("*.") + handler->GetExtension();
3745 }
3746 else
3747 {
3748 if (count > 0)
3749 wildcard += wxT("|");
3750 wildcard += handler->GetName();
3751 wildcard += wxT(" ");
3752 wildcard += _("files");
3753 wildcard += wxT(" (*.");
3754 wildcard += handler->GetExtension();
3755 wildcard += wxT(")|*.");
3756 wildcard += handler->GetExtension();
3757 if (types)
3758 types->Add(handler->GetType());
3759 }
3760 count ++;
3761 }
3762
3763 node = node->GetNext();
3764 }
3765
3766 if (combine)
3767 wildcard = wxT("(") + wildcard + wxT(")|") + wildcard;
3768 return wildcard;
3769 }
3770
3771 /// Load a file
3772 bool wxRichTextBuffer::LoadFile(const wxString& filename, int type)
3773 {
3774 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
3775 if (handler)
3776 {
3777 SetDefaultStyle(wxTextAttrEx());
3778
3779 bool success = handler->LoadFile(this, filename);
3780 Invalidate(wxRICHTEXT_ALL);
3781 return success;
3782 }
3783 else
3784 return false;
3785 }
3786
3787 /// Save a file
3788 bool wxRichTextBuffer::SaveFile(const wxString& filename, int type)
3789 {
3790 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
3791 if (handler)
3792 return handler->SaveFile(this, filename);
3793 else
3794 return false;
3795 }
3796
3797 /// Load from a stream
3798 bool wxRichTextBuffer::LoadFile(wxInputStream& stream, int type)
3799 {
3800 wxRichTextFileHandler* handler = FindHandler(type);
3801 if (handler)
3802 {
3803 SetDefaultStyle(wxTextAttrEx());
3804 bool success = handler->LoadFile(this, stream);
3805 Invalidate(wxRICHTEXT_ALL);
3806 return success;
3807 }
3808 else
3809 return false;
3810 }
3811
3812 /// Save to a stream
3813 bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, int type)
3814 {
3815 wxRichTextFileHandler* handler = FindHandler(type);
3816 if (handler)
3817 return handler->SaveFile(this, stream);
3818 else
3819 return false;
3820 }
3821
3822 /// Copy the range to the clipboard
3823 bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
3824 {
3825 bool success = false;
3826 #if wxUSE_CLIPBOARD
3827 wxString text = GetTextForRange(range);
3828 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
3829 {
3830 success = wxTheClipboard->SetData(new wxTextDataObject(text));
3831 wxTheClipboard->Close();
3832 }
3833 #else
3834 wxUnusedVar(range);
3835 #endif
3836 return success;
3837 }
3838
3839 /// Paste the clipboard content to the buffer
3840 bool wxRichTextBuffer::PasteFromClipboard(long position)
3841 {
3842 bool success = false;
3843 #if wxUSE_CLIPBOARD
3844 if (CanPasteFromClipboard())
3845 {
3846 if (wxTheClipboard->Open())
3847 {
3848 if (wxTheClipboard->IsSupported(wxDF_TEXT))
3849 {
3850 wxTextDataObject data;
3851 wxTheClipboard->GetData(data);
3852 wxString text(data.GetText());
3853
3854 InsertTextWithUndo(position+1, text, GetRichTextCtrl());
3855
3856 success = true;
3857 }
3858 else if (wxTheClipboard->IsSupported(wxDF_BITMAP))
3859 {
3860 wxBitmapDataObject data;
3861 wxTheClipboard->GetData(data);
3862 wxBitmap bitmap(data.GetBitmap());
3863 wxImage image(bitmap.ConvertToImage());
3864
3865 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, GetRichTextCtrl(), false);
3866
3867 action->GetNewParagraphs().AddImage(image);
3868
3869 if (action->GetNewParagraphs().GetChildCount() == 1)
3870 action->GetNewParagraphs().SetPartialParagraph(true);
3871
3872 action->SetPosition(position);
3873
3874 // Set the range we'll need to delete in Undo
3875 action->SetRange(wxRichTextRange(position, position));
3876
3877 SubmitAction(action);
3878
3879 success = true;
3880 }
3881 wxTheClipboard->Close();
3882 }
3883 }
3884 #else
3885 wxUnusedVar(position);
3886 #endif
3887 return success;
3888 }
3889
3890 /// Can we paste from the clipboard?
3891 bool wxRichTextBuffer::CanPasteFromClipboard() const
3892 {
3893 bool canPaste = false;
3894 #if wxUSE_CLIPBOARD
3895 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
3896 {
3897 if (wxTheClipboard->IsSupported(wxDF_TEXT) || wxTheClipboard->IsSupported(wxDF_BITMAP))
3898 {
3899 canPaste = true;
3900 }
3901 wxTheClipboard->Close();
3902 }
3903 #endif
3904 return canPaste;
3905 }
3906
3907 /// Dumps contents of buffer for debugging purposes
3908 void wxRichTextBuffer::Dump()
3909 {
3910 wxString text;
3911 {
3912 wxStringOutputStream stream(& text);
3913 wxTextOutputStream textStream(stream);
3914 Dump(textStream);
3915 }
3916
3917 wxLogDebug(text);
3918 }
3919
3920
3921 /*
3922 * Module to initialise and clean up handlers
3923 */
3924
3925 class wxRichTextModule: public wxModule
3926 {
3927 DECLARE_DYNAMIC_CLASS(wxRichTextModule)
3928 public:
3929 wxRichTextModule() {}
3930 bool OnInit() { wxRichTextBuffer::InitStandardHandlers(); return true; };
3931 void OnExit() { wxRichTextBuffer::CleanUpHandlers(); };
3932 };
3933
3934 IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule)
3935
3936
3937 /*!
3938 * Commands for undo/redo
3939 *
3940 */
3941
3942 wxRichTextCommand::wxRichTextCommand(const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
3943 wxRichTextCtrl* ctrl, bool ignoreFirstTime): wxCommand(true, name)
3944 {
3945 /* wxRichTextAction* action = */ new wxRichTextAction(this, name, id, buffer, ctrl, ignoreFirstTime);
3946 }
3947
3948 wxRichTextCommand::wxRichTextCommand(const wxString& name): wxCommand(true, name)
3949 {
3950 }
3951
3952 wxRichTextCommand::~wxRichTextCommand()
3953 {
3954 ClearActions();
3955 }
3956
3957 void wxRichTextCommand::AddAction(wxRichTextAction* action)
3958 {
3959 if (!m_actions.Member(action))
3960 m_actions.Append(action);
3961 }
3962
3963 bool wxRichTextCommand::Do()
3964 {
3965 for (wxNode* node = m_actions.GetFirst(); node; node = node->GetNext())
3966 {
3967 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
3968 action->Do();
3969 }
3970
3971 return true;
3972 }
3973
3974 bool wxRichTextCommand::Undo()
3975 {
3976 for (wxNode* node = m_actions.GetLast(); node; node = node->GetPrevious())
3977 {
3978 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
3979 action->Undo();
3980 }
3981
3982 return true;
3983 }
3984
3985 void wxRichTextCommand::ClearActions()
3986 {
3987 WX_CLEAR_LIST(wxList, m_actions);
3988 }
3989
3990 /*!
3991 * Individual action
3992 *
3993 */
3994
3995 wxRichTextAction::wxRichTextAction(wxRichTextCommand* cmd, const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
3996 wxRichTextCtrl* ctrl, bool ignoreFirstTime)
3997 {
3998 m_buffer = buffer;
3999 m_ignoreThis = ignoreFirstTime;
4000 m_cmdId = id;
4001 m_position = -1;
4002 m_ctrl = ctrl;
4003 m_name = name;
4004 m_newParagraphs.SetDefaultStyle(buffer->GetDefaultStyle());
4005 m_newParagraphs.SetBasicStyle(buffer->GetBasicStyle());
4006 if (cmd)
4007 cmd->AddAction(this);
4008 }
4009
4010 wxRichTextAction::~wxRichTextAction()
4011 {
4012 }
4013
4014 bool wxRichTextAction::Do()
4015 {
4016 m_buffer->Modify(true);
4017
4018 switch (m_cmdId)
4019 {
4020 case wxRICHTEXT_INSERT:
4021 {
4022 m_buffer->InsertFragment(GetPosition(), m_newParagraphs);
4023 m_buffer->UpdateRanges();
4024 m_buffer->Invalidate(GetRange());
4025
4026 long newCaretPosition = GetPosition() + m_newParagraphs.GetRange().GetLength() - 1;
4027 if (m_newParagraphs.GetPartialParagraph())
4028 newCaretPosition --;
4029
4030 UpdateAppearance(newCaretPosition, true /* send update event */);
4031
4032 break;
4033 }
4034 case wxRICHTEXT_DELETE:
4035 {
4036 m_buffer->DeleteRange(GetRange());
4037 m_buffer->UpdateRanges();
4038 m_buffer->Invalidate(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
4039
4040 UpdateAppearance(GetRange().GetStart()-1, true /* send update event */);
4041
4042 break;
4043 }
4044 case wxRICHTEXT_CHANGE_STYLE:
4045 {
4046 ApplyParagraphs(GetNewParagraphs());
4047 m_buffer->Invalidate(GetRange());
4048
4049 UpdateAppearance(GetPosition());
4050
4051 break;
4052 }
4053 default:
4054 break;
4055 }
4056
4057 return true;
4058 }
4059
4060 bool wxRichTextAction::Undo()
4061 {
4062 m_buffer->Modify(true);
4063
4064 switch (m_cmdId)
4065 {
4066 case wxRICHTEXT_INSERT:
4067 {
4068 m_buffer->DeleteRange(GetRange());
4069 m_buffer->UpdateRanges();
4070 m_buffer->Invalidate(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
4071
4072 long newCaretPosition = GetPosition() - 1;
4073 // if (m_newParagraphs.GetPartialParagraph())
4074 // newCaretPosition --;
4075
4076 UpdateAppearance(newCaretPosition, true /* send update event */);
4077
4078 break;
4079 }
4080 case wxRICHTEXT_DELETE:
4081 {
4082 m_buffer->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
4083 m_buffer->UpdateRanges();
4084 m_buffer->Invalidate(GetRange());
4085
4086 UpdateAppearance(GetPosition(), true /* send update event */);
4087
4088 break;
4089 }
4090 case wxRICHTEXT_CHANGE_STYLE:
4091 {
4092 ApplyParagraphs(GetOldParagraphs());
4093 m_buffer->Invalidate(GetRange());
4094
4095 UpdateAppearance(GetPosition());
4096
4097 break;
4098 }
4099 default:
4100 break;
4101 }
4102
4103 return true;
4104 }
4105
4106 /// Update the control appearance
4107 void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent)
4108 {
4109 if (m_ctrl)
4110 {
4111 m_ctrl->SetCaretPosition(caretPosition);
4112 if (!m_ctrl->IsFrozen())
4113 {
4114 m_ctrl->Layout();
4115 m_ctrl->PositionCaret();
4116 m_ctrl->Refresh();
4117
4118 if (sendUpdateEvent)
4119 m_ctrl->SendUpdateEvent();
4120 }
4121 }
4122 }
4123
4124 /// Replace the buffer paragraphs with the new ones.
4125 void wxRichTextAction::ApplyParagraphs(const wxRichTextFragment& fragment)
4126 {
4127 wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst();
4128 while (node)
4129 {
4130 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
4131 wxASSERT (para != NULL);
4132
4133 // We'll replace the existing paragraph by finding the paragraph at this position,
4134 // delete its node data, and setting a copy as the new node data.
4135 // TODO: make more efficient by simply swapping old and new paragraph objects.
4136
4137 wxRichTextParagraph* existingPara = m_buffer->GetParagraphAtPosition(para->GetRange().GetStart());
4138 if (existingPara)
4139 {
4140 wxRichTextObjectList::compatibility_iterator bufferParaNode = m_buffer->GetChildren().Find(existingPara);
4141 if (bufferParaNode)
4142 {
4143 wxRichTextParagraph* newPara = new wxRichTextParagraph(*para);
4144 newPara->SetParent(m_buffer);
4145
4146 bufferParaNode->SetData(newPara);
4147
4148 delete existingPara;
4149 }
4150 }
4151
4152 node = node->GetNext();
4153 }
4154 }
4155
4156
4157 /*!
4158 * wxRichTextRange
4159 * This stores beginning and end positions for a range of data.
4160 */
4161
4162 /// Limit this range to be within 'range'
4163 bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
4164 {
4165 if (m_start < range.m_start)
4166 m_start = range.m_start;
4167
4168 if (m_end > range.m_end)
4169 m_end = range.m_end;
4170
4171 return true;
4172 }
4173
4174 /*!
4175 * wxRichTextImage implementation
4176 * This object represents an image.
4177 */
4178
4179 IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
4180
4181 wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent):
4182 wxRichTextObject(parent)
4183 {
4184 m_image = image;
4185 }
4186
4187 wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent):
4188 wxRichTextObject(parent)
4189 {
4190 m_imageBlock = imageBlock;
4191 m_imageBlock.Load(m_image);
4192 }
4193
4194 /// Load wxImage from the block
4195 bool wxRichTextImage::LoadFromBlock()
4196 {
4197 m_imageBlock.Load(m_image);
4198 return m_imageBlock.Ok();
4199 }
4200
4201 /// Make block from the wxImage
4202 bool wxRichTextImage::MakeBlock()
4203 {
4204 if (m_imageBlock.GetImageType() == wxBITMAP_TYPE_ANY || m_imageBlock.GetImageType() == -1)
4205 m_imageBlock.SetImageType(wxBITMAP_TYPE_PNG);
4206
4207 m_imageBlock.MakeImageBlock(m_image, m_imageBlock.GetImageType());
4208 return m_imageBlock.Ok();
4209 }
4210
4211
4212 /// Draw the item
4213 bool wxRichTextImage::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
4214 {
4215 if (!m_image.Ok() && m_imageBlock.Ok())
4216 LoadFromBlock();
4217
4218 if (!m_image.Ok())
4219 return false;
4220
4221 if (m_image.Ok() && !m_bitmap.Ok())
4222 m_bitmap = wxBitmap(m_image);
4223
4224 int y = rect.y + (rect.height - m_image.GetHeight());
4225
4226 if (m_bitmap.Ok())
4227 dc.DrawBitmap(m_bitmap, rect.x, y, true);
4228
4229 if (selectionRange.Contains(range.GetStart()))
4230 {
4231 dc.SetBrush(*wxBLACK_BRUSH);
4232 dc.SetPen(*wxBLACK_PEN);
4233 dc.SetLogicalFunction(wxINVERT);
4234 dc.DrawRectangle(rect);
4235 dc.SetLogicalFunction(wxCOPY);
4236 }
4237
4238 return true;
4239 }
4240
4241 /// Lay the item out
4242 bool wxRichTextImage::Layout(wxDC& WXUNUSED(dc), const wxRect& rect, int WXUNUSED(style))
4243 {
4244 if (!m_image.Ok())
4245 LoadFromBlock();
4246
4247 if (m_image.Ok())
4248 {
4249 SetCachedSize(wxSize(m_image.GetWidth(), m_image.GetHeight()));
4250 SetPosition(rect.GetPosition());
4251 }
4252
4253 return true;
4254 }
4255
4256 /// Get/set the object size for the given range. Returns false if the range
4257 /// is invalid for this object.
4258 bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& WXUNUSED(dc), int WXUNUSED(flags)) const
4259 {
4260 if (!range.IsWithin(GetRange()))
4261 return false;
4262
4263 if (!m_image.Ok())
4264 return false;
4265
4266 size.x = m_image.GetWidth();
4267 size.y = m_image.GetHeight();
4268
4269 return true;
4270 }
4271
4272 /// Copy
4273 void wxRichTextImage::Copy(const wxRichTextImage& obj)
4274 {
4275 m_image = obj.m_image;
4276 m_imageBlock = obj.m_imageBlock;
4277 }
4278
4279 /*!
4280 * Utilities
4281 *
4282 */
4283
4284 /// Compare two attribute objects
4285 bool wxTextAttrEq(const wxTextAttrEx& attr1, const wxTextAttrEx& attr2)
4286 {
4287 return (
4288 attr1.GetTextColour() == attr2.GetTextColour() &&
4289 attr1.GetBackgroundColour() == attr2.GetBackgroundColour() &&
4290 attr1.GetFont() == attr2.GetFont() &&
4291 attr1.GetAlignment() == attr2.GetAlignment() &&
4292 attr1.GetLeftIndent() == attr2.GetLeftIndent() &&
4293 attr1.GetRightIndent() == attr2.GetRightIndent() &&
4294 attr1.GetLeftSubIndent() == attr2.GetLeftSubIndent() &&
4295 attr1.GetTabs().GetCount() == attr2.GetTabs().GetCount() && // heuristic
4296 attr1.GetLineSpacing() == attr2.GetLineSpacing() &&
4297 attr1.GetParagraphSpacingAfter() == attr2.GetParagraphSpacingAfter() &&
4298 attr1.GetParagraphSpacingBefore() == attr2.GetParagraphSpacingBefore() &&
4299 attr1.GetBulletStyle() == attr2.GetBulletStyle() &&
4300 attr1.GetBulletNumber() == attr2.GetBulletNumber() &&
4301 attr1.GetBulletSymbol() == attr2.GetBulletSymbol() &&
4302 attr1.GetCharacterStyleName() == attr2.GetCharacterStyleName() &&
4303 attr1.GetParagraphStyleName() == attr2.GetParagraphStyleName());
4304 }
4305
4306 bool wxTextAttrEq(const wxTextAttrEx& attr1, const wxRichTextAttr& attr2)
4307 {
4308 return (
4309 attr1.GetTextColour() == attr2.GetTextColour() &&
4310 attr1.GetBackgroundColour() == attr2.GetBackgroundColour() &&
4311 attr1.GetFont().GetPointSize() == attr2.GetFontSize() &&
4312 attr1.GetFont().GetStyle() == attr2.GetFontStyle() &&
4313 attr1.GetFont().GetWeight() == attr2.GetFontWeight() &&
4314 attr1.GetFont().GetFaceName() == attr2.GetFontFaceName() &&
4315 attr1.GetFont().GetUnderlined() == attr2.GetFontUnderlined() &&
4316 attr1.GetAlignment() == attr2.GetAlignment() &&
4317 attr1.GetLeftIndent() == attr2.GetLeftIndent() &&
4318 attr1.GetRightIndent() == attr2.GetRightIndent() &&
4319 attr1.GetLeftSubIndent() == attr2.GetLeftSubIndent() &&
4320 attr1.GetTabs().GetCount() == attr2.GetTabs().GetCount() && // heuristic
4321 attr1.GetLineSpacing() == attr2.GetLineSpacing() &&
4322 attr1.GetParagraphSpacingAfter() == attr2.GetParagraphSpacingAfter() &&
4323 attr1.GetParagraphSpacingBefore() == attr2.GetParagraphSpacingBefore() &&
4324 attr1.GetBulletStyle() == attr2.GetBulletStyle() &&
4325 attr1.GetBulletNumber() == attr2.GetBulletNumber() &&
4326 attr1.GetBulletSymbol() == attr2.GetBulletSymbol() &&
4327 attr1.GetCharacterStyleName() == attr2.GetCharacterStyleName() &&
4328 attr1.GetParagraphStyleName() == attr2.GetParagraphStyleName());
4329 }
4330
4331 /// Compare two attribute objects, but take into account the flags
4332 /// specifying attributes of interest.
4333 bool wxTextAttrEqPartial(const wxTextAttrEx& attr1, const wxTextAttrEx& attr2, int flags)
4334 {
4335 if ((flags & wxTEXT_ATTR_TEXT_COLOUR) && attr1.GetTextColour() != attr2.GetTextColour())
4336 return false;
4337
4338 if ((flags & wxTEXT_ATTR_BACKGROUND_COLOUR) && attr1.GetBackgroundColour() != attr2.GetBackgroundColour())
4339 return false;
4340
4341 if ((flags & wxTEXT_ATTR_FONT_FACE) && attr1.GetFont().Ok() && attr2.GetFont().Ok() &&
4342 attr1.GetFont().GetFaceName() != attr2.GetFont().GetFaceName())
4343 return false;
4344
4345 if ((flags & wxTEXT_ATTR_FONT_SIZE) && attr1.GetFont().Ok() && attr2.GetFont().Ok() &&
4346 attr1.GetFont().GetPointSize() != attr2.GetFont().GetPointSize())
4347 return false;
4348
4349 if ((flags & wxTEXT_ATTR_FONT_WEIGHT) && attr1.GetFont().Ok() && attr2.GetFont().Ok() &&
4350 attr1.GetFont().GetWeight() != attr2.GetFont().GetWeight())
4351 return false;
4352
4353 if ((flags & wxTEXT_ATTR_FONT_ITALIC) && attr1.GetFont().Ok() && attr2.GetFont().Ok() &&
4354 attr1.GetFont().GetStyle() != attr2.GetFont().GetStyle())
4355 return false;
4356
4357 if ((flags & wxTEXT_ATTR_FONT_UNDERLINE) && attr1.GetFont().Ok() && attr2.GetFont().Ok() &&
4358 attr1.GetFont().GetUnderlined() != attr2.GetFont().GetUnderlined())
4359 return false;
4360
4361 if ((flags & wxTEXT_ATTR_ALIGNMENT) && attr1.GetAlignment() != attr2.GetAlignment())
4362 return false;
4363
4364 if ((flags & wxTEXT_ATTR_LEFT_INDENT) &&
4365 ((attr1.GetLeftIndent() != attr2.GetLeftIndent()) || (attr1.GetLeftSubIndent() != attr2.GetLeftSubIndent())))
4366 return false;
4367
4368 if ((flags & wxTEXT_ATTR_RIGHT_INDENT) &&
4369 (attr1.GetRightIndent() != attr2.GetRightIndent()))
4370 return false;
4371
4372 if ((flags & wxTEXT_ATTR_PARA_SPACING_AFTER) &&
4373 (attr1.GetParagraphSpacingAfter() != attr2.GetParagraphSpacingAfter()))
4374 return false;
4375
4376 if ((flags & wxTEXT_ATTR_PARA_SPACING_BEFORE) &&
4377 (attr1.GetParagraphSpacingBefore() != attr2.GetParagraphSpacingBefore()))
4378 return false;
4379
4380 if ((flags & wxTEXT_ATTR_LINE_SPACING) &&
4381 (attr1.GetLineSpacing() != attr2.GetLineSpacing()))
4382 return false;
4383
4384 if ((flags & wxTEXT_ATTR_CHARACTER_STYLE_NAME) &&
4385 (attr1.GetCharacterStyleName() != attr2.GetCharacterStyleName()))
4386 return false;
4387
4388 if ((flags & wxTEXT_ATTR_PARAGRAPH_STYLE_NAME) &&
4389 (attr1.GetParagraphStyleName() != attr2.GetParagraphStyleName()))
4390 return false;
4391
4392 if ((flags & wxTEXT_ATTR_BULLET_STYLE) &&
4393 (attr1.GetBulletStyle() != attr2.GetBulletStyle()))
4394 return false;
4395
4396 if ((flags & wxTEXT_ATTR_BULLET_NUMBER) &&
4397 (attr1.GetBulletNumber() != attr2.GetBulletNumber()))
4398 return false;
4399
4400 if ((flags & wxTEXT_ATTR_BULLET_SYMBOL) &&
4401 (attr1.GetBulletSymbol() != attr2.GetBulletSymbol()))
4402 return false;
4403
4404 /* TODO
4405 if ((flags & wxTEXT_ATTR_TABS) &&
4406 return false;
4407 */
4408
4409 return true;
4410 }
4411
4412 bool wxTextAttrEqPartial(const wxTextAttrEx& attr1, const wxRichTextAttr& attr2, int flags)
4413 {
4414 if ((flags & wxTEXT_ATTR_TEXT_COLOUR) && attr1.GetTextColour() != attr2.GetTextColour())
4415 return false;
4416
4417 if ((flags & wxTEXT_ATTR_BACKGROUND_COLOUR) && attr1.GetBackgroundColour() != attr2.GetBackgroundColour())
4418 return false;
4419
4420 if ((flags & (wxTEXT_ATTR_FONT)) && !attr1.GetFont().Ok())
4421 return false;
4422
4423 if ((flags & wxTEXT_ATTR_FONT_FACE) && attr1.GetFont().Ok() &&
4424 attr1.GetFont().GetFaceName() != attr2.GetFontFaceName())
4425 return false;
4426
4427 if ((flags & wxTEXT_ATTR_FONT_SIZE) && attr1.GetFont().Ok() &&
4428 attr1.GetFont().GetPointSize() != attr2.GetFontSize())
4429 return false;
4430
4431 if ((flags & wxTEXT_ATTR_FONT_WEIGHT) && attr1.GetFont().Ok() &&
4432 attr1.GetFont().GetWeight() != attr2.GetFontWeight())
4433 return false;
4434
4435 if ((flags & wxTEXT_ATTR_FONT_ITALIC) && attr1.GetFont().Ok() &&
4436 attr1.GetFont().GetStyle() != attr2.GetFontStyle())
4437 return false;
4438
4439 if ((flags & wxTEXT_ATTR_FONT_UNDERLINE) && attr1.GetFont().Ok() &&
4440 attr1.GetFont().GetUnderlined() != attr2.GetFontUnderlined())
4441 return false;
4442
4443 if ((flags & wxTEXT_ATTR_ALIGNMENT) && attr1.GetAlignment() != attr2.GetAlignment())
4444 return false;
4445
4446 if ((flags & wxTEXT_ATTR_LEFT_INDENT) &&
4447 ((attr1.GetLeftIndent() != attr2.GetLeftIndent()) || (attr1.GetLeftSubIndent() != attr2.GetLeftSubIndent())))
4448 return false;
4449
4450 if ((flags & wxTEXT_ATTR_RIGHT_INDENT) &&
4451 (attr1.GetRightIndent() != attr2.GetRightIndent()))
4452 return false;
4453
4454 if ((flags & wxTEXT_ATTR_PARA_SPACING_AFTER) &&
4455 (attr1.GetParagraphSpacingAfter() != attr2.GetParagraphSpacingAfter()))
4456 return false;
4457
4458 if ((flags & wxTEXT_ATTR_PARA_SPACING_BEFORE) &&
4459 (attr1.GetParagraphSpacingBefore() != attr2.GetParagraphSpacingBefore()))
4460 return false;
4461
4462 if ((flags & wxTEXT_ATTR_LINE_SPACING) &&
4463 (attr1.GetLineSpacing() != attr2.GetLineSpacing()))
4464 return false;
4465
4466 if ((flags & wxTEXT_ATTR_CHARACTER_STYLE_NAME) &&
4467 (attr1.GetCharacterStyleName() != attr2.GetCharacterStyleName()))
4468 return false;
4469
4470 if ((flags & wxTEXT_ATTR_PARAGRAPH_STYLE_NAME) &&
4471 (attr1.GetParagraphStyleName() != attr2.GetParagraphStyleName()))
4472 return false;
4473
4474 if ((flags & wxTEXT_ATTR_BULLET_STYLE) &&
4475 (attr1.GetBulletStyle() != attr2.GetBulletStyle()))
4476 return false;
4477
4478 if ((flags & wxTEXT_ATTR_BULLET_NUMBER) &&
4479 (attr1.GetBulletNumber() != attr2.GetBulletNumber()))
4480 return false;
4481
4482 if ((flags & wxTEXT_ATTR_BULLET_SYMBOL) &&
4483 (attr1.GetBulletSymbol() != attr2.GetBulletSymbol()))
4484 return false;
4485
4486 /* TODO
4487 if ((flags & wxTEXT_ATTR_TABS) &&
4488 return false;
4489 */
4490
4491 return true;
4492 }
4493
4494
4495 /// Apply one style to another
4496 bool wxRichTextApplyStyle(wxTextAttrEx& destStyle, const wxTextAttrEx& style)
4497 {
4498 // Whole font
4499 if (style.GetFont().Ok() && ((style.GetFlags() & (wxTEXT_ATTR_FONT)) == (wxTEXT_ATTR_FONT)))
4500 destStyle.SetFont(style.GetFont());
4501 else if (style.GetFont().Ok())
4502 {
4503 wxFont font = destStyle.GetFont();
4504
4505 if (style.GetFlags() & wxTEXT_ATTR_FONT_FACE)
4506 font.SetFaceName(style.GetFont().GetFaceName());
4507
4508 if (style.GetFlags() & wxTEXT_ATTR_FONT_SIZE)
4509 font.SetPointSize(style.GetFont().GetPointSize());
4510
4511 if (style.GetFlags() & wxTEXT_ATTR_FONT_ITALIC)
4512 font.SetStyle(style.GetFont().GetStyle());
4513
4514 if (style.GetFlags() & wxTEXT_ATTR_FONT_WEIGHT)
4515 font.SetWeight(style.GetFont().GetWeight());
4516
4517 if (style.GetFlags() & wxTEXT_ATTR_FONT_UNDERLINE)
4518 font.SetUnderlined(style.GetFont().GetUnderlined());
4519
4520 if (font != destStyle.GetFont())
4521 destStyle.SetFont(font);
4522 }
4523
4524 if ( style.GetTextColour().Ok() && style.HasTextColour())
4525 destStyle.SetTextColour(style.GetTextColour());
4526
4527 if ( style.GetBackgroundColour().Ok() && style.HasBackgroundColour())
4528 destStyle.SetBackgroundColour(style.GetBackgroundColour());
4529
4530 if (style.HasAlignment())
4531 destStyle.SetAlignment(style.GetAlignment());
4532
4533 if (style.HasTabs())
4534 destStyle.SetTabs(style.GetTabs());
4535
4536 if (style.HasLeftIndent())
4537 destStyle.SetLeftIndent(style.GetLeftIndent(), style.GetLeftSubIndent());
4538
4539 if (style.HasRightIndent())
4540 destStyle.SetRightIndent(style.GetRightIndent());
4541
4542 if (style.HasParagraphSpacingAfter())
4543 destStyle.SetParagraphSpacingAfter(style.GetParagraphSpacingAfter());
4544
4545 if (style.HasParagraphSpacingBefore())
4546 destStyle.SetParagraphSpacingBefore(style.GetParagraphSpacingBefore());
4547
4548 if (style.HasLineSpacing())
4549 destStyle.SetLineSpacing(style.GetLineSpacing());
4550
4551 if (style.HasCharacterStyleName())
4552 destStyle.SetCharacterStyleName(style.GetCharacterStyleName());
4553
4554 if (style.HasParagraphStyleName())
4555 destStyle.SetParagraphStyleName(style.GetParagraphStyleName());
4556
4557 if (style.HasBulletStyle())
4558 {
4559 destStyle.SetBulletStyle(style.GetBulletStyle());
4560 destStyle.SetBulletSymbol(style.GetBulletSymbol());
4561 }
4562
4563 if (style.HasBulletNumber())
4564 destStyle.SetBulletNumber(style.GetBulletNumber());
4565
4566 return true;
4567 }
4568
4569 bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxTextAttrEx& style)
4570 {
4571 wxTextAttrEx destStyle2;
4572 destStyle.CopyTo(destStyle2);
4573 wxRichTextApplyStyle(destStyle2, style);
4574 destStyle = destStyle2;
4575 return true;
4576 }
4577
4578 bool wxRichTextApplyStyle(wxTextAttrEx& destStyle, const wxRichTextAttr& style)
4579 {
4580
4581 // Whole font. Avoiding setting individual attributes if possible, since
4582 // it recreates the font each time.
4583 if ((style.GetFlags() & (wxTEXT_ATTR_FONT)) == (wxTEXT_ATTR_FONT))
4584 {
4585 destStyle.SetFont(wxFont(style.GetFontSize(), destStyle.GetFont().Ok() ? destStyle.GetFont().GetFamily() : wxDEFAULT,
4586 style.GetFontStyle(), style.GetFontWeight(), style.GetFontUnderlined(), style.GetFontFaceName()));
4587 }
4588 else if (style.GetFlags() & (wxTEXT_ATTR_FONT))
4589 {
4590 wxFont font = destStyle.GetFont();
4591
4592 if (style.GetFlags() & wxTEXT_ATTR_FONT_FACE)
4593 font.SetFaceName(style.GetFontFaceName());
4594
4595 if (style.GetFlags() & wxTEXT_ATTR_FONT_SIZE)
4596 font.SetPointSize(style.GetFontSize());
4597
4598 if (style.GetFlags() & wxTEXT_ATTR_FONT_ITALIC)
4599 font.SetStyle(style.GetFontStyle());
4600
4601 if (style.GetFlags() & wxTEXT_ATTR_FONT_WEIGHT)
4602 font.SetWeight(style.GetFontWeight());
4603
4604 if (style.GetFlags() & wxTEXT_ATTR_FONT_UNDERLINE)
4605 font.SetUnderlined(style.GetFontUnderlined());
4606
4607 if (font != destStyle.GetFont())
4608 destStyle.SetFont(font);
4609 }
4610
4611 if ( style.GetTextColour().Ok() && style.HasTextColour())
4612 destStyle.SetTextColour(style.GetTextColour());
4613
4614 if ( style.GetBackgroundColour().Ok() && style.HasBackgroundColour())
4615 destStyle.SetBackgroundColour(style.GetBackgroundColour());
4616
4617 if (style.HasAlignment())
4618 destStyle.SetAlignment(style.GetAlignment());
4619
4620 if (style.HasTabs())
4621 destStyle.SetTabs(style.GetTabs());
4622
4623 if (style.HasLeftIndent())
4624 destStyle.SetLeftIndent(style.GetLeftIndent(), style.GetLeftSubIndent());
4625
4626 if (style.HasRightIndent())
4627 destStyle.SetRightIndent(style.GetRightIndent());
4628
4629 if (style.HasParagraphSpacingAfter())
4630 destStyle.SetParagraphSpacingAfter(style.GetParagraphSpacingAfter());
4631
4632 if (style.HasParagraphSpacingBefore())
4633 destStyle.SetParagraphSpacingBefore(style.GetParagraphSpacingBefore());
4634
4635 if (style.HasLineSpacing())
4636 destStyle.SetLineSpacing(style.GetLineSpacing());
4637
4638 if (style.HasCharacterStyleName())
4639 destStyle.SetCharacterStyleName(style.GetCharacterStyleName());
4640
4641 if (style.HasParagraphStyleName())
4642 destStyle.SetParagraphStyleName(style.GetParagraphStyleName());
4643
4644 if (style.HasBulletStyle())
4645 {
4646 destStyle.SetBulletStyle(style.GetBulletStyle());
4647 destStyle.SetBulletSymbol(style.GetBulletSymbol());
4648 }
4649
4650 if (style.HasBulletNumber())
4651 destStyle.SetBulletNumber(style.GetBulletNumber());
4652
4653 return true;
4654 }
4655
4656
4657 /*!
4658 * wxRichTextAttr stores attributes without a wxFont object, so is a much more
4659 * efficient way to query styles.
4660 */
4661
4662 // ctors
4663 wxRichTextAttr::wxRichTextAttr(const wxColour& colText,
4664 const wxColour& colBack,
4665 wxTextAttrAlignment alignment): m_textAlignment(alignment), m_colText(colText), m_colBack(colBack)
4666 {
4667 Init();
4668
4669 if (m_colText.Ok()) m_flags |= wxTEXT_ATTR_TEXT_COLOUR;
4670 if (m_colBack.Ok()) m_flags |= wxTEXT_ATTR_BACKGROUND_COLOUR;
4671 if (alignment != wxTEXT_ALIGNMENT_DEFAULT)
4672 m_flags |= wxTEXT_ATTR_ALIGNMENT;
4673 }
4674
4675 wxRichTextAttr::wxRichTextAttr(const wxTextAttrEx& attr)
4676 {
4677 Init();
4678
4679 (*this) = attr;
4680 }
4681
4682 // operations
4683 void wxRichTextAttr::Init()
4684 {
4685 m_textAlignment = wxTEXT_ALIGNMENT_DEFAULT;
4686 m_flags = 0;
4687 m_leftIndent = 0;
4688 m_leftSubIndent = 0;
4689 m_rightIndent = 0;
4690
4691 m_fontSize = 12;
4692 m_fontStyle = wxNORMAL;
4693 m_fontWeight = wxNORMAL;
4694 m_fontUnderlined = false;
4695
4696 m_paragraphSpacingAfter = 0;
4697 m_paragraphSpacingBefore = 0;
4698 m_lineSpacing = 0;
4699 m_bulletStyle = wxTEXT_ATTR_BULLET_STYLE_NONE;
4700 m_bulletNumber = 0;
4701 m_bulletSymbol = wxT('*');
4702 }
4703
4704 // operators
4705 void wxRichTextAttr::operator= (const wxRichTextAttr& attr)
4706 {
4707 m_colText = attr.m_colText;
4708 m_colBack = attr.m_colBack;
4709 m_textAlignment = attr.m_textAlignment;
4710 m_leftIndent = attr.m_leftIndent;
4711 m_leftSubIndent = attr.m_leftSubIndent;
4712 m_rightIndent = attr.m_rightIndent;
4713 m_tabs = attr.m_tabs;
4714 m_flags = attr.m_flags;
4715
4716 m_fontSize = attr.m_fontSize;
4717 m_fontStyle = attr.m_fontStyle;
4718 m_fontWeight = attr.m_fontWeight;
4719 m_fontUnderlined = attr.m_fontUnderlined;
4720 m_fontFaceName = attr.m_fontFaceName;
4721
4722 m_paragraphSpacingAfter = attr.m_paragraphSpacingAfter;
4723 m_paragraphSpacingBefore = attr.m_paragraphSpacingBefore;
4724 m_lineSpacing = attr.m_lineSpacing;
4725 m_characterStyleName = attr.m_characterStyleName;
4726 m_paragraphStyleName = attr.m_paragraphStyleName;
4727 m_bulletStyle = attr.m_bulletStyle;
4728 m_bulletNumber = attr.m_bulletNumber;
4729 m_bulletSymbol = attr.m_bulletSymbol;
4730 }
4731
4732 // operators
4733 void wxRichTextAttr::operator= (const wxTextAttrEx& attr)
4734 {
4735 m_colText = attr.GetTextColour();
4736 m_colBack = attr.GetBackgroundColour();
4737 m_textAlignment = attr.GetAlignment();
4738 m_leftIndent = attr.GetLeftIndent();
4739 m_leftSubIndent = attr.GetLeftSubIndent();
4740 m_rightIndent = attr.GetRightIndent();
4741 m_tabs = attr.GetTabs();
4742 m_flags = attr.GetFlags();
4743
4744 m_paragraphSpacingAfter = attr.GetParagraphSpacingAfter();
4745 m_paragraphSpacingBefore = attr.GetParagraphSpacingBefore();
4746 m_lineSpacing = attr.GetLineSpacing();
4747 m_characterStyleName = attr.GetCharacterStyleName();
4748 m_paragraphStyleName = attr.GetParagraphStyleName();
4749
4750 if (attr.GetFont().Ok())
4751 GetFontAttributes(attr.GetFont());
4752 }
4753
4754 // Making a wxTextAttrEx object.
4755 wxRichTextAttr::operator wxTextAttrEx () const
4756 {
4757 wxTextAttrEx attr;
4758 CopyTo(attr);
4759 return attr;
4760 }
4761
4762 // Copy to a wxTextAttr
4763 void wxRichTextAttr::CopyTo(wxTextAttrEx& attr) const
4764 {
4765 attr.SetTextColour(GetTextColour());
4766 attr.SetBackgroundColour(GetBackgroundColour());
4767 attr.SetAlignment(GetAlignment());
4768 attr.SetTabs(GetTabs());
4769 attr.SetLeftIndent(GetLeftIndent(), GetLeftSubIndent());
4770 attr.SetRightIndent(GetRightIndent());
4771 attr.SetFont(CreateFont());
4772 attr.SetFlags(GetFlags()); // Important: set after SetFont, since SetFont sets flags
4773
4774 attr.SetParagraphSpacingAfter(m_paragraphSpacingAfter);
4775 attr.SetParagraphSpacingBefore(m_paragraphSpacingBefore);
4776 attr.SetLineSpacing(m_lineSpacing);
4777 attr.SetBulletStyle(m_bulletStyle);
4778 attr.SetBulletNumber(m_bulletNumber);
4779 attr.SetBulletSymbol(m_bulletSymbol);
4780 attr.SetCharacterStyleName(m_characterStyleName);
4781 attr.SetParagraphStyleName(m_paragraphStyleName);
4782
4783 }
4784
4785 // Create font from font attributes.
4786 wxFont wxRichTextAttr::CreateFont() const
4787 {
4788 wxFont font(m_fontSize, wxDEFAULT, m_fontStyle, m_fontWeight, m_fontUnderlined, m_fontFaceName);
4789 #ifdef __WXMAC__
4790 font.SetNoAntiAliasing(true);
4791 #endif
4792 return font;
4793 }
4794
4795 // Get attributes from font.
4796 bool wxRichTextAttr::GetFontAttributes(const wxFont& font)
4797 {
4798 if (!font.Ok())
4799 return false;
4800
4801 m_fontSize = font.GetPointSize();
4802 m_fontStyle = font.GetStyle();
4803 m_fontWeight = font.GetWeight();
4804 m_fontUnderlined = font.GetUnderlined();
4805 m_fontFaceName = font.GetFaceName();
4806
4807 return true;
4808 }
4809
4810 /*!
4811 * wxTextAttrEx is an extended version of wxTextAttr with more paragraph attributes.
4812 */
4813
4814 wxTextAttrEx::wxTextAttrEx(const wxTextAttrEx& attr): wxTextAttr(attr)
4815 {
4816 m_paragraphSpacingAfter = attr.m_paragraphSpacingAfter;
4817 m_paragraphSpacingBefore = attr.m_paragraphSpacingBefore;
4818 m_lineSpacing = attr.m_lineSpacing;
4819 m_paragraphStyleName = attr.m_paragraphStyleName;
4820 m_characterStyleName = attr.m_characterStyleName;
4821 m_bulletStyle = attr.m_bulletStyle;
4822 m_bulletNumber = attr.m_bulletNumber;
4823 m_bulletSymbol = attr.m_bulletSymbol;
4824 }
4825
4826 // Initialise this object.
4827 void wxTextAttrEx::Init()
4828 {
4829 m_paragraphSpacingAfter = 0;
4830 m_paragraphSpacingBefore = 0;
4831 m_lineSpacing = 0;
4832 m_bulletStyle = wxTEXT_ATTR_BULLET_STYLE_NONE;
4833 m_bulletNumber = 0;
4834 m_bulletSymbol = 0;
4835 m_bulletSymbol = wxT('*');
4836 }
4837
4838 // Assignment from a wxTextAttrEx object
4839 void wxTextAttrEx::operator= (const wxTextAttrEx& attr)
4840 {
4841 wxTextAttr::operator= (attr);
4842
4843 m_paragraphSpacingAfter = attr.m_paragraphSpacingAfter;
4844 m_paragraphSpacingBefore = attr.m_paragraphSpacingBefore;
4845 m_lineSpacing = attr.m_lineSpacing;
4846 m_characterStyleName = attr.m_characterStyleName;
4847 m_paragraphStyleName = attr.m_paragraphStyleName;
4848 m_bulletStyle = attr.m_bulletStyle;
4849 m_bulletNumber = attr.m_bulletNumber;
4850 m_bulletSymbol = attr.m_bulletSymbol;
4851 }
4852
4853 // Assignment from a wxTextAttr object.
4854 void wxTextAttrEx::operator= (const wxTextAttr& attr)
4855 {
4856 wxTextAttr::operator= (attr);
4857 }
4858
4859 /*!
4860 * wxRichTextFileHandler
4861 * Base class for file handlers
4862 */
4863
4864 IMPLEMENT_CLASS(wxRichTextFileHandler, wxObject)
4865
4866 #if wxUSE_STREAMS
4867 bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer *buffer, const wxString& filename)
4868 {
4869 wxFFileInputStream stream(filename);
4870 if (stream.Ok())
4871 return LoadFile(buffer, stream);
4872 else
4873 return false;
4874 }
4875
4876 bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer *buffer, const wxString& filename)
4877 {
4878 wxFFileOutputStream stream(filename);
4879 if (stream.Ok())
4880 return SaveFile(buffer, stream);
4881 else
4882 return false;
4883 }
4884 #endif // wxUSE_STREAMS
4885
4886 /// Can we handle this filename (if using files)? By default, checks the extension.
4887 bool wxRichTextFileHandler::CanHandle(const wxString& filename) const
4888 {
4889 wxString path, file, ext;
4890 wxSplitPath(filename, & path, & file, & ext);
4891
4892 return (ext.Lower() == GetExtension());
4893 }
4894
4895 /*!
4896 * wxRichTextTextHandler
4897 * Plain text handler
4898 */
4899
4900 IMPLEMENT_CLASS(wxRichTextPlainTextHandler, wxRichTextFileHandler)
4901
4902 #if wxUSE_STREAMS
4903 bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
4904 {
4905 if (!stream.IsOk())
4906 return false;
4907
4908 wxString str;
4909 int lastCh = 0;
4910
4911 while (!stream.Eof())
4912 {
4913 int ch = stream.GetC();
4914
4915 if (!stream.Eof())
4916 {
4917 if (ch == 10 && lastCh != 13)
4918 str += wxT('\n');
4919
4920 if (ch > 0 && ch != 10)
4921 str += wxChar(ch);
4922
4923 lastCh = ch;
4924 }
4925 }
4926
4927 buffer->Clear();
4928 buffer->AddParagraphs(str);
4929 buffer->UpdateRanges();
4930
4931 return true;
4932
4933 }
4934
4935 bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
4936 {
4937 if (!stream.IsOk())
4938 return false;
4939
4940 wxString text = buffer->GetText();
4941 wxCharBuffer buf = text.ToAscii();
4942
4943 stream.Write((const char*) buf, text.Length());
4944 return true;
4945 }
4946 #endif // wxUSE_STREAMS
4947
4948 /*
4949 * Stores information about an image, in binary in-memory form
4950 */
4951
4952 wxRichTextImageBlock::wxRichTextImageBlock()
4953 {
4954 Init();
4955 }
4956
4957 wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock& block):wxObject()
4958 {
4959 Init();
4960 Copy(block);
4961 }
4962
4963 wxRichTextImageBlock::~wxRichTextImageBlock()
4964 {
4965 if (m_data)
4966 {
4967 delete[] m_data;
4968 m_data = NULL;
4969 }
4970 }
4971
4972 void wxRichTextImageBlock::Init()
4973 {
4974 m_data = NULL;
4975 m_dataSize = 0;
4976 m_imageType = -1;
4977 }
4978
4979 void wxRichTextImageBlock::Clear()
4980 {
4981 if (m_data)
4982 delete m_data;
4983 m_data = NULL;
4984 m_dataSize = 0;
4985 m_imageType = -1;
4986 }
4987
4988
4989 // Load the original image into a memory block.
4990 // If the image is not a JPEG, we must convert it into a JPEG
4991 // to conserve space.
4992 // If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
4993 // load the image a 2nd time.
4994
4995 bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, int imageType, wxImage& image, bool convertToJPEG)
4996 {
4997 m_imageType = imageType;
4998
4999 wxString filenameToRead(filename);
5000 bool removeFile = false;
5001
5002 if (imageType == -1)
5003 return false; // Could not determine image type
5004
5005 if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
5006 {
5007 wxString tempFile;
5008 bool success = wxGetTempFileName(_("image"), tempFile) ;
5009
5010 wxASSERT(success);
5011
5012 wxUnusedVar(success);
5013
5014 image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
5015 filenameToRead = tempFile;
5016 removeFile = true;
5017
5018 m_imageType = wxBITMAP_TYPE_JPEG;
5019 }
5020 wxFile file;
5021 if (!file.Open(filenameToRead))
5022 return false;
5023
5024 m_dataSize = (size_t) file.Length();
5025 file.Close();
5026
5027 if (m_data)
5028 delete[] m_data;
5029 m_data = ReadBlock(filenameToRead, m_dataSize);
5030
5031 if (removeFile)
5032 wxRemoveFile(filenameToRead);
5033
5034 return (m_data != NULL);
5035 }
5036
5037 // Make an image block from the wxImage in the given
5038 // format.
5039 bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, int imageType, int quality)
5040 {
5041 m_imageType = imageType;
5042 image.SetOption(wxT("quality"), quality);
5043
5044 if (imageType == -1)
5045 return false; // Could not determine image type
5046
5047 wxString tempFile;
5048 bool success = wxGetTempFileName(_("image"), tempFile) ;
5049
5050 wxASSERT(success);
5051 wxUnusedVar(success);
5052
5053 if (!image.SaveFile(tempFile, m_imageType))
5054 {
5055 if (wxFileExists(tempFile))
5056 wxRemoveFile(tempFile);
5057 return false;
5058 }
5059
5060 wxFile file;
5061 if (!file.Open(tempFile))
5062 return false;
5063
5064 m_dataSize = (size_t) file.Length();
5065 file.Close();
5066
5067 if (m_data)
5068 delete[] m_data;
5069 m_data = ReadBlock(tempFile, m_dataSize);
5070
5071 wxRemoveFile(tempFile);
5072
5073 return (m_data != NULL);
5074 }
5075
5076
5077 // Write to a file
5078 bool wxRichTextImageBlock::Write(const wxString& filename)
5079 {
5080 return WriteBlock(filename, m_data, m_dataSize);
5081 }
5082
5083 void wxRichTextImageBlock::Copy(const wxRichTextImageBlock& block)
5084 {
5085 m_imageType = block.m_imageType;
5086 if (m_data)
5087 {
5088 delete[] m_data;
5089 m_data = NULL;
5090 }
5091 m_dataSize = block.m_dataSize;
5092 if (m_dataSize == 0)
5093 return;
5094
5095 m_data = new unsigned char[m_dataSize];
5096 unsigned int i;
5097 for (i = 0; i < m_dataSize; i++)
5098 m_data[i] = block.m_data[i];
5099 }
5100
5101 //// Operators
5102 void wxRichTextImageBlock::operator=(const wxRichTextImageBlock& block)
5103 {
5104 Copy(block);
5105 }
5106
5107 // Load a wxImage from the block
5108 bool wxRichTextImageBlock::Load(wxImage& image)
5109 {
5110 if (!m_data)
5111 return false;
5112
5113 // Read in the image.
5114 #if 1
5115 wxMemoryInputStream mstream(m_data, m_dataSize);
5116 bool success = image.LoadFile(mstream, GetImageType());
5117 #else
5118 wxString tempFile;
5119 bool success = wxGetTempFileName(_("image"), tempFile) ;
5120 wxASSERT(success);
5121
5122 if (!WriteBlock(tempFile, m_data, m_dataSize))
5123 {
5124 return false;
5125 }
5126 success = image.LoadFile(tempFile, GetImageType());
5127 wxRemoveFile(tempFile);
5128 #endif
5129
5130 return success;
5131 }
5132
5133 // Write data in hex to a stream
5134 bool wxRichTextImageBlock::WriteHex(wxOutputStream& stream)
5135 {
5136 wxString hex;
5137 int i;
5138 for (i = 0; i < (int) m_dataSize; i++)
5139 {
5140 hex = wxDecToHex(m_data[i]);
5141 wxCharBuffer buf = hex.ToAscii();
5142
5143 stream.Write((const char*) buf, hex.Length());
5144 }
5145
5146 return true;
5147 }
5148
5149 // Read data in hex from a stream
5150 bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, int imageType)
5151 {
5152 int dataSize = length/2;
5153
5154 if (m_data)
5155 delete[] m_data;
5156
5157 wxString str(wxT(" "));
5158 m_data = new unsigned char[dataSize];
5159 int i;
5160 for (i = 0; i < dataSize; i ++)
5161 {
5162 str[0] = stream.GetC();
5163 str[1] = stream.GetC();
5164
5165 m_data[i] = (unsigned char)wxHexToDec(str);
5166 }
5167
5168 m_dataSize = dataSize;
5169 m_imageType = imageType;
5170
5171 return true;
5172 }
5173
5174
5175 // Allocate and read from stream as a block of memory
5176 unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream& stream, size_t size)
5177 {
5178 unsigned char* block = new unsigned char[size];
5179 if (!block)
5180 return NULL;
5181
5182 stream.Read(block, size);
5183
5184 return block;
5185 }
5186
5187 unsigned char* wxRichTextImageBlock::ReadBlock(const wxString& filename, size_t size)
5188 {
5189 wxFileInputStream stream(filename);
5190 if (!stream.Ok())
5191 return NULL;
5192
5193 return ReadBlock(stream, size);
5194 }
5195
5196 // Write memory block to stream
5197 bool wxRichTextImageBlock::WriteBlock(wxOutputStream& stream, unsigned char* block, size_t size)
5198 {
5199 stream.Write((void*) block, size);
5200 return stream.IsOk();
5201
5202 }
5203
5204 // Write memory block to file
5205 bool wxRichTextImageBlock::WriteBlock(const wxString& filename, unsigned char* block, size_t size)
5206 {
5207 wxFileOutputStream outStream(filename);
5208 if (!outStream.Ok())
5209 return false;
5210
5211 return WriteBlock(outStream, block, size);
5212 }
5213
5214 #endif
5215 // wxUSE_RICHTEXT