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