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