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