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