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