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