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