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