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