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