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