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