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