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