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