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