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