]> git.saurik.com Git - wxWidgets.git/blame - src/richtext/richtextbuffer.cpp
Added wxRICHTEXT_SETSTYLE_RESET SetStyleEx() flag to allow for clearing attributes...
[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"
0ca07313 36#include "wx/textfile.h"
5d7836c4 37
5d7836c4
JS
38#include "wx/richtext/richtextctrl.h"
39#include "wx/richtext/richtextstyles.h"
40
41#include "wx/listimpl.cpp"
42
412e0d47
DS
43WX_DEFINE_LIST(wxRichTextObjectList)
44WX_DEFINE_LIST(wxRichTextLineList)
5d7836c4 45
ea160b2e
JS
46// Switch off if the platform doesn't like it for some reason
47#define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
48
5d7836c4
JS
49/*!
50 * wxRichTextObject
51 * This is the base for drawable objects.
52 */
53
54IMPLEMENT_CLASS(wxRichTextObject, wxObject)
55
56wxRichTextObject::wxRichTextObject(wxRichTextObject* parent)
57{
58 m_dirty = false;
59 m_refCount = 1;
60 m_parent = parent;
61 m_leftMargin = 0;
62 m_rightMargin = 0;
63 m_topMargin = 0;
64 m_bottomMargin = 0;
65 m_descent = 0;
66}
67
68wxRichTextObject::~wxRichTextObject()
69{
70}
71
72void wxRichTextObject::Dereference()
73{
74 m_refCount --;
75 if (m_refCount <= 0)
76 delete this;
77}
78
79/// Copy
80void wxRichTextObject::Copy(const wxRichTextObject& obj)
81{
82 m_size = obj.m_size;
83 m_pos = obj.m_pos;
84 m_dirty = obj.m_dirty;
85 m_range = obj.m_range;
86 m_attributes = obj.m_attributes;
87 m_descent = obj.m_descent;
fe5aa22c 88/*
5d7836c4
JS
89 if (!m_attributes.GetFont().Ok())
90 wxLogDebug(wxT("No font!"));
91 if (!obj.m_attributes.GetFont().Ok())
92 wxLogDebug(wxT("Parent has no font!"));
fe5aa22c 93*/
5d7836c4
JS
94}
95
96void wxRichTextObject::SetMargins(int margin)
97{
98 m_leftMargin = m_rightMargin = m_topMargin = m_bottomMargin = margin;
99}
100
101void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin, int bottomMargin)
102{
103 m_leftMargin = leftMargin;
104 m_rightMargin = rightMargin;
105 m_topMargin = topMargin;
106 m_bottomMargin = bottomMargin;
107}
108
44219ff0 109// Convert units in tenths of a millimetre to device units
5d7836c4
JS
110int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units)
111{
44219ff0 112 int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units);
5d7836c4 113
44219ff0
JS
114 // Unscale
115 wxRichTextBuffer* buffer = GetBuffer();
116 if (buffer)
117 p = (int) ((double)p / buffer->GetScale());
118 return p;
119}
120
121// Convert units in tenths of a millimetre to device units
122int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units)
123{
5d7836c4
JS
124 // There are ppi pixels in 254.1 "1/10 mm"
125
126 double pixels = ((double) units * (double)ppi) / 254.1;
127
128 return (int) pixels;
129}
130
131/// Dump to output stream for debugging
132void wxRichTextObject::Dump(wxTextOutputStream& stream)
133{
134 stream << GetClassInfo()->GetClassName() << wxT("\n");
135 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");
136 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");
137}
138
44219ff0
JS
139/// Gets the containing buffer
140wxRichTextBuffer* wxRichTextObject::GetBuffer() const
141{
142 const wxRichTextObject* obj = this;
143 while (obj && !obj->IsKindOf(CLASSINFO(wxRichTextBuffer)))
144 obj = obj->GetParent();
145 return wxDynamicCast(obj, wxRichTextBuffer);
146}
5d7836c4
JS
147
148/*!
149 * wxRichTextCompositeObject
150 * This is the base for drawable objects.
151 */
152
153IMPLEMENT_CLASS(wxRichTextCompositeObject, wxRichTextObject)
154
155wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject* parent):
156 wxRichTextObject(parent)
157{
158}
159
160wxRichTextCompositeObject::~wxRichTextCompositeObject()
161{
162 DeleteChildren();
163}
164
165/// Get the nth child
166wxRichTextObject* wxRichTextCompositeObject::GetChild(size_t n) const
167{
168 wxASSERT ( n < m_children.GetCount() );
169
170 return m_children.Item(n)->GetData();
171}
172
173/// Append a child, returning the position
174size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject* child)
175{
176 m_children.Append(child);
177 child->SetParent(this);
178 return m_children.GetCount() - 1;
179}
180
181/// Insert the child in front of the given object, or at the beginning
182bool wxRichTextCompositeObject::InsertChild(wxRichTextObject* child, wxRichTextObject* inFrontOf)
183{
184 if (inFrontOf)
185 {
186 wxRichTextObjectList::compatibility_iterator node = m_children.Find(inFrontOf);
187 m_children.Insert(node, child);
188 }
189 else
190 m_children.Insert(child);
191 child->SetParent(this);
192
193 return true;
194}
195
196/// Delete the child
197bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject* child, bool deleteChild)
198{
199 wxRichTextObjectList::compatibility_iterator node = m_children.Find(child);
200 if (node)
201 {
efbf6735
JS
202 wxRichTextObject* obj = node->GetData();
203 m_children.Erase(node);
5d7836c4 204 if (deleteChild)
efbf6735 205 delete obj;
5d7836c4
JS
206
207 return true;
208 }
209 return false;
210}
211
212/// Delete all children
213bool wxRichTextCompositeObject::DeleteChildren()
214{
215 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
216 while (node)
217 {
218 wxRichTextObjectList::compatibility_iterator oldNode = node;
219
220 wxRichTextObject* child = node->GetData();
221 child->Dereference(); // Only delete if reference count is zero
222
223 node = node->GetNext();
efbf6735 224 m_children.Erase(oldNode);
5d7836c4
JS
225 }
226
227 return true;
228}
229
230/// Get the child count
231size_t wxRichTextCompositeObject::GetChildCount() const
232{
233 return m_children.GetCount();
234}
235
236/// Copy
237void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject& obj)
238{
239 wxRichTextObject::Copy(obj);
240
241 DeleteChildren();
242
243 wxRichTextObjectList::compatibility_iterator node = obj.m_children.GetFirst();
244 while (node)
245 {
246 wxRichTextObject* child = node->GetData();
fe5aa22c
JS
247 wxRichTextObject* newChild = child->Clone();
248 newChild->SetParent(this);
249 m_children.Append(newChild);
5d7836c4
JS
250
251 node = node->GetNext();
252 }
253}
254
255/// Hit-testing: returns a flag indicating hit test details, plus
256/// information about position
257int wxRichTextCompositeObject::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition)
258{
259 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
260 while (node)
261 {
262 wxRichTextObject* child = node->GetData();
263
264 int ret = child->HitTest(dc, pt, textPosition);
265 if (ret != wxRICHTEXT_HITTEST_NONE)
266 return ret;
267
268 node = node->GetNext();
269 }
270
271 return wxRICHTEXT_HITTEST_NONE;
272}
273
274/// Finds the absolute position and row height for the given character position
275bool wxRichTextCompositeObject::FindPosition(wxDC& dc, long index, wxPoint& pt, int* height, bool forceLineStart)
276{
277 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
278 while (node)
279 {
280 wxRichTextObject* child = node->GetData();
281
282 if (child->FindPosition(dc, index, pt, height, forceLineStart))
283 return true;
284
285 node = node->GetNext();
286 }
287
288 return false;
289}
290
291/// Calculate range
292void wxRichTextCompositeObject::CalculateRange(long start, long& end)
293{
294 long current = start;
295 long lastEnd = current;
296
297 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
298 while (node)
299 {
300 wxRichTextObject* child = node->GetData();
301 long childEnd = 0;
302
303 child->CalculateRange(current, childEnd);
304 lastEnd = childEnd;
305
306 current = childEnd + 1;
307
308 node = node->GetNext();
309 }
310
311 end = lastEnd;
312
313 // An object with no children has zero length
314 if (m_children.GetCount() == 0)
315 end --;
316
317 m_range.SetRange(start, end);
318}
319
320/// Delete range from layout.
321bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange& range)
322{
323 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
7fe8059f 324
5d7836c4
JS
325 while (node)
326 {
327 wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
328 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7fe8059f 329
5d7836c4
JS
330 // Delete the range in each paragraph
331
332 // When a chunk has been deleted, internally the content does not
333 // now match the ranges.
334 // However, so long as deletion is not done on the same object twice this is OK.
335 // If you may delete content from the same object twice, recalculate
336 // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
337 // adjust the range you're deleting accordingly.
7fe8059f 338
5d7836c4
JS
339 if (!obj->GetRange().IsOutside(range))
340 {
341 obj->DeleteRange(range);
342
343 // Delete an empty object, or paragraph within this range.
344 if (obj->IsEmpty() ||
345 (range.GetStart() <= obj->GetRange().GetStart() && range.GetEnd() >= obj->GetRange().GetEnd()))
346 {
347 // An empty paragraph has length 1, so won't be deleted unless the
348 // whole range is deleted.
7fe8059f 349 RemoveChild(obj, true);
5d7836c4
JS
350 }
351 }
7fe8059f 352
5d7836c4
JS
353 node = next;
354 }
7fe8059f 355
5d7836c4
JS
356 return true;
357}
358
359/// Get any text in this object for the given range
360wxString wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange& range) const
361{
362 wxString text;
363 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
364 while (node)
365 {
366 wxRichTextObject* child = node->GetData();
367 wxRichTextRange childRange = range;
368 if (!child->GetRange().IsOutside(range))
369 {
370 childRange.LimitTo(child->GetRange());
7fe8059f 371
5d7836c4 372 wxString childText = child->GetTextForRange(childRange);
7fe8059f 373
5d7836c4
JS
374 text += childText;
375 }
376 node = node->GetNext();
377 }
378
379 return text;
380}
381
382/// Recursively merge all pieces that can be merged.
383bool wxRichTextCompositeObject::Defragment()
384{
385 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
386 while (node)
387 {
388 wxRichTextObject* child = node->GetData();
389 wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject);
7fe8059f 390 if (composite)
5d7836c4
JS
391 composite->Defragment();
392
393 if (node->GetNext())
394 {
395 wxRichTextObject* nextChild = node->GetNext()->GetData();
396 if (child->CanMerge(nextChild) && child->Merge(nextChild))
397 {
398 nextChild->Dereference();
9e31a660 399 m_children.Erase(node->GetNext());
5d7836c4
JS
400
401 // Don't set node -- we'll see if we can merge again with the next
402 // child.
403 }
404 else
405 node = node->GetNext();
406 }
407 else
408 node = node->GetNext();
409 }
410
411 return true;
412}
413
414/// Dump to output stream for debugging
415void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
416{
417 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
418 while (node)
419 {
420 wxRichTextObject* child = node->GetData();
421 child->Dump(stream);
422 node = node->GetNext();
423 }
424}
425
426
427/*!
428 * wxRichTextBox
429 * This defines a 2D space to lay out objects
430 */
431
432IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextCompositeObject)
433
434wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
435 wxRichTextCompositeObject(parent)
436{
437}
438
439/// Draw the item
440bool wxRichTextBox::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& WXUNUSED(rect), int descent, int style)
441{
442 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
443 while (node)
444 {
445 wxRichTextObject* child = node->GetData();
446
447 wxRect childRect = wxRect(child->GetPosition(), child->GetCachedSize());
448 child->Draw(dc, range, selectionRange, childRect, descent, style);
449
450 node = node->GetNext();
451 }
452 return true;
453}
454
455/// Lay the item out
38113684 456bool wxRichTextBox::Layout(wxDC& dc, const wxRect& rect, int style)
5d7836c4
JS
457{
458 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
459 while (node)
460 {
461 wxRichTextObject* child = node->GetData();
38113684 462 child->Layout(dc, rect, style);
5d7836c4
JS
463
464 node = node->GetNext();
465 }
466 m_dirty = false;
467 return true;
468}
469
470/// Get/set the size for the given range. Assume only has one child.
7f0d9d71 471bool wxRichTextBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position) const
5d7836c4
JS
472{
473 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
474 if (node)
475 {
476 wxRichTextObject* child = node->GetData();
7f0d9d71 477 return child->GetRangeSize(range, size, descent, dc, flags, position);
5d7836c4
JS
478 }
479 else
480 return false;
481}
482
483/// Copy
484void wxRichTextBox::Copy(const wxRichTextBox& obj)
485{
486 wxRichTextCompositeObject::Copy(obj);
487}
488
489
490/*!
491 * wxRichTextParagraphLayoutBox
492 * This box knows how to lay out paragraphs.
493 */
494
495IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextBox)
496
497wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject* parent):
498 wxRichTextBox(parent)
499{
500 Init();
501}
502
503/// Initialize the object.
504void wxRichTextParagraphLayoutBox::Init()
505{
506 m_ctrl = NULL;
507
508 // For now, assume is the only box and has no initial size.
509 m_range = wxRichTextRange(0, -1);
510
38113684 511 m_invalidRange.SetRange(-1, -1);
5d7836c4
JS
512 m_leftMargin = 4;
513 m_rightMargin = 4;
514 m_topMargin = 4;
515 m_bottomMargin = 4;
0ca07313 516 m_partialParagraph = false;
5d7836c4
JS
517}
518
519/// Draw the item
011b3dcb 520bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int descent, int style)
5d7836c4
JS
521{
522 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
523 while (node)
524 {
525 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
526 wxASSERT (child != NULL);
7fe8059f 527
5d7836c4
JS
528 if (child && !child->GetRange().IsOutside(range))
529 {
530 wxRect childRect(child->GetPosition(), child->GetCachedSize());
7fe8059f 531
ea160b2e
JS
532 if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetTop() > rect.GetBottom())
533 {
534 // Stop drawing
535 break;
536 }
537 else if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetBottom() < rect.GetTop())
011b3dcb
JS
538 {
539 // Skip
540 }
541 else
46e7a90e 542 child->Draw(dc, range, selectionRange, childRect, descent, style);
5d7836c4
JS
543 }
544
545 node = node->GetNext();
546 }
547 return true;
548}
549
550/// Lay the item out
38113684 551bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, int style)
5d7836c4 552{
4d551ad5
JS
553 wxRect availableSpace;
554 bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
555
556 // If only laying out a specific area, the passed rect has a different meaning:
44219ff0
JS
557 // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
558 // so that during a size, only the visible part will be relaid out, or
559 // it would take too long causing flicker. As an approximation, we assume that
560 // everything up to the start of the visible area is laid out correctly.
4d551ad5
JS
561 if (formatRect)
562 {
563 availableSpace = wxRect(0 + m_leftMargin,
564 0 + m_topMargin,
565 rect.width - m_leftMargin - m_rightMargin,
566 rect.height);
567
568 // Invalidate the part of the buffer from the first visible line
569 // to the end. If other parts of the buffer are currently invalid,
570 // then they too will be taken into account if they are above
571 // the visible point.
572 long startPos = 0;
573 wxRichTextLine* line = GetLineAtYPosition(rect.y);
574 if (line)
575 startPos = line->GetAbsoluteRange().GetStart();
576
577 Invalidate(wxRichTextRange(startPos, GetRange().GetEnd()));
578 }
579 else
580 availableSpace = wxRect(rect.x + m_leftMargin,
5d7836c4
JS
581 rect.y + m_topMargin,
582 rect.width - m_leftMargin - m_rightMargin,
583 rect.height - m_topMargin - m_bottomMargin);
584
585 int maxWidth = 0;
7fe8059f 586
5d7836c4 587 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
39a1c2f2 588
38113684 589 bool layoutAll = true;
1e967276 590
38113684
JS
591 // Get invalid range, rounding to paragraph start/end.
592 wxRichTextRange invalidRange = GetInvalidRange(true);
593
4d551ad5 594 if (invalidRange == wxRICHTEXT_NONE && !formatRect)
1e967276
JS
595 return true;
596
597 if (invalidRange == wxRICHTEXT_ALL)
598 layoutAll = true;
38113684
JS
599 else // If we know what range is affected, start laying out from that point on.
600 if (invalidRange.GetStart() > GetRange().GetStart())
2c375f42 601 {
38113684 602 wxRichTextParagraph* firstParagraph = GetParagraphAtPosition(invalidRange.GetStart());
2c375f42
JS
603 if (firstParagraph)
604 {
605 wxRichTextObjectList::compatibility_iterator firstNode = m_children.Find(firstParagraph);
0cc70962
VZ
606 wxRichTextObjectList::compatibility_iterator previousNode;
607 if ( firstNode )
608 previousNode = firstNode->GetPrevious();
2c375f42
JS
609 if (firstNode && previousNode)
610 {
611 wxRichTextParagraph* previousParagraph = wxDynamicCast(previousNode->GetData(), wxRichTextParagraph);
612 availableSpace.y = previousParagraph->GetPosition().y + previousParagraph->GetCachedSize().y;
7fe8059f 613
2c375f42
JS
614 // Now we're going to start iterating from the first affected paragraph.
615 node = firstNode;
1e967276
JS
616
617 layoutAll = false;
2c375f42
JS
618 }
619 }
620 }
621
4d551ad5
JS
622 // A way to force speedy rest-of-buffer layout (the 'else' below)
623 bool forceQuickLayout = false;
39a1c2f2 624
5d7836c4
JS
625 while (node)
626 {
627 // Assume this box only contains paragraphs
628
629 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
f120af9d 630 wxCHECK_MSG( child, false, _T("Unknown object in layout") );
7fe8059f 631
1e967276 632 // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
f120af9d
VZ
633 if ( !forceQuickLayout &&
634 (layoutAll ||
635 child->GetLines().IsEmpty() ||
636 !child->GetRange().IsOutside(invalidRange)) )
2c375f42 637 {
38113684 638 child->Layout(dc, availableSpace, style);
5d7836c4 639
2c375f42
JS
640 // Layout must set the cached size
641 availableSpace.y += child->GetCachedSize().y;
642 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
4d551ad5
JS
643
644 // If we're just formatting the visible part of the buffer,
645 // and we're now past the bottom of the window, start quick
646 // layout.
647 if (formatRect && child->GetPosition().y > rect.GetBottom())
648 forceQuickLayout = true;
2c375f42
JS
649 }
650 else
651 {
652 // We're outside the immediately affected range, so now let's just
653 // move everything up or down. This assumes that all the children have previously
654 // been laid out and have wrapped line lists associated with them.
655 // TODO: check all paragraphs before the affected range.
7fe8059f 656
2c375f42 657 int inc = availableSpace.y - child->GetPosition().y;
7fe8059f 658
2c375f42
JS
659 while (node)
660 {
661 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
662 if (child)
663 {
664 if (child->GetLines().GetCount() == 0)
38113684 665 child->Layout(dc, availableSpace, style);
2c375f42
JS
666 else
667 child->SetPosition(wxPoint(child->GetPosition().x, child->GetPosition().y + inc));
7fe8059f 668
2c375f42
JS
669 availableSpace.y += child->GetCachedSize().y;
670 maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
671 }
7fe8059f
WS
672
673 node = node->GetNext();
2c375f42
JS
674 }
675 break;
676 }
5d7836c4
JS
677
678 node = node->GetNext();
679 }
680
681 SetCachedSize(wxSize(maxWidth, availableSpace.y));
682
683 m_dirty = false;
1e967276 684 m_invalidRange = wxRICHTEXT_NONE;
5d7836c4
JS
685
686 return true;
687}
688
689/// Copy
690void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
691{
692 wxRichTextBox::Copy(obj);
0ca07313
JS
693
694 m_partialParagraph = obj.m_partialParagraph;
5d7836c4
JS
695}
696
697/// Get/set the size for the given range.
7f0d9d71 698bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position) const
5d7836c4
JS
699{
700 wxSize sz;
701
09f14108
JS
702 wxRichTextObjectList::compatibility_iterator startPara = wxRichTextObjectList::compatibility_iterator();
703 wxRichTextObjectList::compatibility_iterator endPara = wxRichTextObjectList::compatibility_iterator();
5d7836c4
JS
704
705 // First find the first paragraph whose starting position is within the range.
706 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
707 while (node)
708 {
709 // child is a paragraph
710 wxRichTextObject* child = node->GetData();
711 const wxRichTextRange& r = child->GetRange();
712
713 if (r.GetStart() <= range.GetStart() && r.GetEnd() >= range.GetStart())
714 {
715 startPara = node;
716 break;
717 }
718
719 node = node->GetNext();
720 }
721
722 // Next find the last paragraph containing part of the range
723 node = m_children.GetFirst();
724 while (node)
725 {
726 // child is a paragraph
727 wxRichTextObject* child = node->GetData();
728 const wxRichTextRange& r = child->GetRange();
729
730 if (r.GetStart() <= range.GetEnd() && r.GetEnd() >= range.GetEnd())
731 {
732 endPara = node;
733 break;
734 }
735
736 node = node->GetNext();
737 }
738
739 if (!startPara || !endPara)
740 return false;
741
742 // Now we can add up the sizes
743 for (node = startPara; node ; node = node->GetNext())
744 {
745 // child is a paragraph
746 wxRichTextObject* child = node->GetData();
747 const wxRichTextRange& childRange = child->GetRange();
748 wxRichTextRange rangeToFind = range;
749 rangeToFind.LimitTo(childRange);
750
751 wxSize childSize;
752
753 int childDescent = 0;
7f0d9d71 754 child->GetRangeSize(rangeToFind, childSize, childDescent, dc, flags, position);
5d7836c4
JS
755
756 descent = wxMax(childDescent, descent);
757
758 sz.x = wxMax(sz.x, childSize.x);
759 sz.y += childSize.y;
760
761 if (node == endPara)
762 break;
763 }
764
765 size = sz;
766
767 return true;
768}
769
770/// Get the paragraph at the given position
771wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos, bool caretPosition) const
772{
773 if (caretPosition)
774 pos ++;
775
776 // First find the first paragraph whose starting position is within the range.
777 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
778 while (node)
779 {
780 // child is a paragraph
781 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
782 wxASSERT (child != NULL);
783
784 // Return first child in buffer if position is -1
785 // if (pos == -1)
786 // return child;
787
788 if (child->GetRange().Contains(pos))
789 return child;
790
791 node = node->GetNext();
792 }
793 return NULL;
794}
795
796/// Get the line at the given position
797wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos, bool caretPosition) const
798{
799 if (caretPosition)
800 pos ++;
801
802 // First find the first paragraph whose starting position is within the range.
803 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
804 while (node)
805 {
806 // child is a paragraph
807 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
808 wxASSERT (child != NULL);
809
810 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
811 while (node2)
812 {
813 wxRichTextLine* line = node2->GetData();
814
1e967276
JS
815 wxRichTextRange range = line->GetAbsoluteRange();
816
817 if (range.Contains(pos) ||
5d7836c4
JS
818
819 // If the position is end-of-paragraph, then return the last line of
820 // of the paragraph.
1e967276 821 (range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd()))
5d7836c4
JS
822 return line;
823
824 node2 = node2->GetNext();
7fe8059f 825 }
5d7836c4
JS
826
827 node = node->GetNext();
828 }
829
830 int lineCount = GetLineCount();
831 if (lineCount > 0)
832 return GetLineForVisibleLineNumber(lineCount-1);
833 else
834 return NULL;
835}
836
837/// Get the line at the given y pixel position, or the last line.
838wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y) const
839{
840 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
841 while (node)
842 {
843 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
844 wxASSERT (child != NULL);
845
846 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
847 while (node2)
848 {
849 wxRichTextLine* line = node2->GetData();
850
851 wxRect rect(line->GetRect());
852
853 if (y <= rect.GetBottom())
854 return line;
855
856 node2 = node2->GetNext();
7fe8059f 857 }
5d7836c4
JS
858
859 node = node->GetNext();
860 }
861
862 // Return last line
863 int lineCount = GetLineCount();
864 if (lineCount > 0)
865 return GetLineForVisibleLineNumber(lineCount-1);
866 else
867 return NULL;
868}
869
870/// Get the number of visible lines
871int wxRichTextParagraphLayoutBox::GetLineCount() const
872{
873 int count = 0;
874
875 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
876 while (node)
877 {
878 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
879 wxASSERT (child != NULL);
880
881 count += child->GetLines().GetCount();
882 node = node->GetNext();
883 }
884 return count;
885}
886
887
888/// Get the paragraph for a given line
889wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine* line) const
890{
1e967276 891 return GetParagraphAtPosition(line->GetAbsoluteRange().GetStart());
5d7836c4
JS
892}
893
894/// Get the line size at the given position
895wxSize wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos, bool caretPosition) const
896{
897 wxRichTextLine* line = GetLineAtPosition(pos, caretPosition);
898 if (line)
899 {
900 return line->GetSize();
901 }
902 else
903 return wxSize(0, 0);
904}
905
906
907/// Convenience function to add a paragraph of text
fe5aa22c 908wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph(const wxString& text, wxTextAttrEx* paraStyle)
5d7836c4 909{
fe5aa22c
JS
910#if wxRICHTEXT_USE_DYNAMIC_STYLES
911 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
912 // be combined at display time.
913 // Divide into paragraph and character styles.
914
915 wxTextAttrEx defaultCharStyle;
916 wxTextAttrEx defaultParaStyle;
917 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
fe5aa22c 918#else
5d7836c4
JS
919 wxTextAttrEx style(GetAttributes());
920
921 // Apply default style. If the style has no attributes set,
922 // then the attributes will remain the 'basic style' (i.e. the
923 // layout box's style).
924 wxRichTextApplyStyle(style, GetDefaultStyle());
4f32b3cf
JS
925
926 wxTextAttrEx defaultCharStyle = style;
927 wxTextAttrEx defaultParaStyle = style;
fe5aa22c 928#endif
4f32b3cf
JS
929 wxTextAttrEx* pStyle = paraStyle ? paraStyle : (wxTextAttrEx*) & defaultParaStyle;
930 wxTextAttrEx* cStyle = & defaultCharStyle;
931
932 wxRichTextParagraph* para = new wxRichTextParagraph(text, this, pStyle, cStyle);
5d7836c4
JS
933
934 AppendChild(para);
935
936 UpdateRanges();
937 SetDirty(true);
938
939 return para->GetRange();
940}
941
942/// Adds multiple paragraphs, based on newlines.
fe5aa22c 943wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text, wxTextAttrEx* paraStyle)
5d7836c4 944{
fe5aa22c
JS
945#if wxRICHTEXT_USE_DYNAMIC_STYLES
946 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
947 // be combined at display time.
948 // Divide into paragraph and character styles.
949
950 wxTextAttrEx defaultCharStyle;
951 wxTextAttrEx defaultParaStyle;
952 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
fe5aa22c 953#else
5d7836c4 954 wxTextAttrEx style(GetAttributes());
fe5aa22c 955
5d7836c4
JS
956 // Apply default style. If the style has no attributes set,
957 // then the attributes will remain the 'basic style' (i.e. the
958 // layout box's style).
959 wxRichTextApplyStyle(style, GetDefaultStyle());
960
4f32b3cf
JS
961 wxTextAttrEx defaultCharStyle = style;
962 wxTextAttrEx defaultParaStyle = style;
fe5aa22c 963#endif
5d7836c4 964
4f32b3cf
JS
965 wxTextAttrEx* pStyle = paraStyle ? paraStyle : (wxTextAttrEx*) & defaultParaStyle;
966 wxTextAttrEx* cStyle = & defaultCharStyle;
967
5d7836c4
JS
968 wxRichTextParagraph* firstPara = NULL;
969 wxRichTextParagraph* lastPara = NULL;
970
971 wxRichTextRange range(-1, -1);
0ca07313 972
5d7836c4 973 size_t i = 0;
28f92d74 974 size_t len = text.length();
5d7836c4 975 wxString line;
4f32b3cf 976 wxRichTextParagraph* para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
0ca07313
JS
977
978 AppendChild(para);
979
980 firstPara = para;
981 lastPara = para;
982
5d7836c4
JS
983 while (i < len)
984 {
985 wxChar ch = text[i];
986 if (ch == wxT('\n') || ch == wxT('\r'))
987 {
0ca07313
JS
988 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
989 plainText->SetText(line);
990
4f32b3cf 991 para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
5d7836c4
JS
992
993 AppendChild(para);
0ca07313 994
5d7836c4
JS
995 lastPara = para;
996 line = wxEmptyString;
997 }
998 else
999 line += ch;
1000
1001 i ++;
1002 }
0ca07313 1003
7fe8059f 1004 if (!line.empty())
5d7836c4 1005 {
0ca07313
JS
1006 wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
1007 plainText->SetText(line);
5d7836c4
JS
1008 }
1009
5d7836c4 1010 UpdateRanges();
0ca07313 1011
5d7836c4
JS
1012 SetDirty(false);
1013
0ca07313 1014 return wxRichTextRange(firstPara->GetRange().GetStart(), lastPara->GetRange().GetEnd());
5d7836c4
JS
1015}
1016
1017/// Convenience function to add an image
fe5aa22c 1018wxRichTextRange wxRichTextParagraphLayoutBox::AddImage(const wxImage& image, wxTextAttrEx* paraStyle)
5d7836c4 1019{
fe5aa22c
JS
1020#if wxRICHTEXT_USE_DYNAMIC_STYLES
1021 // Don't use the base style, just the default style, and the base style will
4f32b3cf
JS
1022 // be combined at display time.
1023 // Divide into paragraph and character styles.
1024
1025 wxTextAttrEx defaultCharStyle;
1026 wxTextAttrEx defaultParaStyle;
1027 wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
fe5aa22c 1028#else
5d7836c4
JS
1029 wxTextAttrEx style(GetAttributes());
1030
1031 // Apply default style. If the style has no attributes set,
1032 // then the attributes will remain the 'basic style' (i.e. the
1033 // layout box's style).
1034 wxRichTextApplyStyle(style, GetDefaultStyle());
4f32b3cf
JS
1035
1036 wxTextAttrEx defaultCharStyle = style;
1037 wxTextAttrEx defaultParaStyle = style;
fe5aa22c 1038#endif
5d7836c4 1039
4f32b3cf
JS
1040 wxTextAttrEx* pStyle = paraStyle ? paraStyle : (wxTextAttrEx*) & defaultParaStyle;
1041 wxTextAttrEx* cStyle = & defaultCharStyle;
5d7836c4 1042
4f32b3cf
JS
1043 wxRichTextParagraph* para = new wxRichTextParagraph(this, pStyle);
1044 AppendChild(para);
1045 para->AppendChild(new wxRichTextImage(image, this, cStyle));
fe5aa22c 1046
5d7836c4
JS
1047 UpdateRanges();
1048 SetDirty(true);
1049
1050 return para->GetRange();
1051}
1052
1053
1054/// Insert fragment into this box at the given position. If partialParagraph is true,
1055/// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
1056/// marker.
5d7836c4 1057
0ca07313 1058bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextParagraphLayoutBox& fragment)
5d7836c4
JS
1059{
1060 SetDirty(true);
1061
1062 // First, find the first paragraph whose starting position is within the range.
1063 wxRichTextParagraph* para = GetParagraphAtPosition(position);
1064 if (para)
1065 {
1066 wxRichTextObjectList::compatibility_iterator node = m_children.Find(para);
1067
1068 // Now split at this position, returning the object to insert the new
1069 // ones in front of.
1070 wxRichTextObject* nextObject = para->SplitAt(position);
1071
1072 // Special case: partial paragraph, just one paragraph. Might be a small amount of
1073 // text, for example, so let's optimize.
1074
1075 if (fragment.GetPartialParagraph() && fragment.GetChildren().GetCount() == 1)
1076 {
1077 // Add the first para to this para...
1078 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
1079 if (!firstParaNode)
1080 return false;
1081
1082 // Iterate through the fragment paragraph inserting the content into this paragraph.
1083 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
1084 wxASSERT (firstPara != NULL);
1085
4f32b3cf
JS
1086 // Apply the new paragraph attributes to the existing paragraph
1087 wxTextAttrEx attr(para->GetAttributes());
1088 wxRichTextApplyStyle(attr, firstPara->GetAttributes());
1089 para->SetAttributes(attr);
1090
5d7836c4
JS
1091 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
1092 while (objectNode)
1093 {
1094 wxRichTextObject* newObj = objectNode->GetData()->Clone();
7fe8059f 1095
5d7836c4
JS
1096 if (!nextObject)
1097 {
1098 // Append
1099 para->AppendChild(newObj);
1100 }
1101 else
1102 {
1103 // Insert before nextObject
1104 para->InsertChild(newObj, nextObject);
1105 }
7fe8059f 1106
5d7836c4
JS
1107 objectNode = objectNode->GetNext();
1108 }
1109
1110 return true;
1111 }
1112 else
1113 {
1114 // Procedure for inserting a fragment consisting of a number of
1115 // paragraphs:
1116 //
1117 // 1. Remove and save the content that's after the insertion point, for adding
1118 // back once we've added the fragment.
1119 // 2. Add the content from the first fragment paragraph to the current
1120 // paragraph.
1121 // 3. Add remaining fragment paragraphs after the current paragraph.
1122 // 4. Add back the saved content from the first paragraph. If partialParagraph
1123 // is true, add it to the last paragraph added and not a new one.
1124
1125 // 1. Remove and save objects after split point.
1126 wxList savedObjects;
1127 if (nextObject)
1128 para->MoveToList(nextObject, savedObjects);
1129
1130 // 2. Add the content from the 1st fragment paragraph.
1131 wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
1132 if (!firstParaNode)
1133 return false;
1134
1135 wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
1136 wxASSERT(firstPara != NULL);
1137
1138 wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
1139 while (objectNode)
1140 {
1141 wxRichTextObject* newObj = objectNode->GetData()->Clone();
7fe8059f 1142
5d7836c4
JS
1143 // Append
1144 para->AppendChild(newObj);
7fe8059f 1145
5d7836c4
JS
1146 objectNode = objectNode->GetNext();
1147 }
1148
1149 // 3. Add remaining fragment paragraphs after the current paragraph.
1150 wxRichTextObjectList::compatibility_iterator nextParagraphNode = node->GetNext();
1151 wxRichTextObject* nextParagraph = NULL;
1152 if (nextParagraphNode)
1153 nextParagraph = nextParagraphNode->GetData();
1154
1155 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst()->GetNext();
1156 wxRichTextParagraph* finalPara = para;
1157
1158 // If there was only one paragraph, we need to insert a new one.
1159 if (!i)
1160 {
1161 finalPara = new wxRichTextParagraph;
1162
1163 // TODO: These attributes should come from the subsequent paragraph
1164 // when originally deleted, since the subsequent para takes on
1165 // the previous para's attributes.
1166 finalPara->SetAttributes(firstPara->GetAttributes());
1167
1168 if (nextParagraph)
1169 InsertChild(finalPara, nextParagraph);
1170 else
7fe8059f 1171 AppendChild(finalPara);
5d7836c4
JS
1172 }
1173 else while (i)
1174 {
1175 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
1176 wxASSERT( para != NULL );
1177
1178 finalPara = (wxRichTextParagraph*) para->Clone();
1179
1180 if (nextParagraph)
1181 InsertChild(finalPara, nextParagraph);
1182 else
1183 AppendChild(finalPara);
7fe8059f 1184
5d7836c4
JS
1185 i = i->GetNext();
1186 }
1187
1188 // 4. Add back the remaining content.
1189 if (finalPara)
1190 {
1191 finalPara->MoveFromList(savedObjects);
1192
1193 // Ensure there's at least one object
1194 if (finalPara->GetChildCount() == 0)
1195 {
7fe8059f 1196 wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
5d7836c4
JS
1197
1198 finalPara->AppendChild(text);
1199 }
1200 }
1201
1202 return true;
1203 }
1204 }
1205 else
1206 {
1207 // Append
1208 wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst();
1209 while (i)
1210 {
1211 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
1212 wxASSERT( para != NULL );
7fe8059f 1213
5d7836c4 1214 AppendChild(para->Clone());
7fe8059f 1215
5d7836c4
JS
1216 i = i->GetNext();
1217 }
1218
1219 return true;
1220 }
5d7836c4
JS
1221}
1222
1223/// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
1224/// If there was an incomplete paragraph at the end, partialParagraph is set to true.
0ca07313 1225bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange& range, wxRichTextParagraphLayoutBox& fragment)
5d7836c4
JS
1226{
1227 wxRichTextObjectList::compatibility_iterator i = GetChildren().GetFirst();
1228 while (i)
1229 {
1230 wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
1231 wxASSERT( para != NULL );
1232
1233 if (!para->GetRange().IsOutside(range))
1234 {
1235 fragment.AppendChild(para->Clone());
7fe8059f 1236 }
5d7836c4
JS
1237 i = i->GetNext();
1238 }
1239
1240 // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
1241 if (!fragment.IsEmpty())
1242 {
1243 wxRichTextRange topTailRange(range);
1244
1245 wxRichTextParagraph* firstPara = wxDynamicCast(fragment.GetChildren().GetFirst()->GetData(), wxRichTextParagraph);
1246 wxASSERT( firstPara != NULL );
1247
1248 // Chop off the start of the paragraph
1249 if (topTailRange.GetStart() > firstPara->GetRange().GetStart())
1250 {
1251 wxRichTextRange r(firstPara->GetRange().GetStart(), topTailRange.GetStart()-1);
1252 firstPara->DeleteRange(r);
1253
1254 // Make sure the numbering is correct
1255 long end;
1256 fragment.CalculateRange(firstPara->GetRange().GetStart(), end);
1257
1258 // Now, we've deleted some positions, so adjust the range
1259 // accordingly.
1260 topTailRange.SetEnd(topTailRange.GetEnd() - r.GetLength());
1261 }
1262
1263 wxRichTextParagraph* lastPara = wxDynamicCast(fragment.GetChildren().GetLast()->GetData(), wxRichTextParagraph);
1264 wxASSERT( lastPara != NULL );
1265
1266 if (topTailRange.GetEnd() < (lastPara->GetRange().GetEnd()-1))
1267 {
1268 wxRichTextRange r(topTailRange.GetEnd()+1, lastPara->GetRange().GetEnd()-1); /* -1 since actual text ends 1 position before end of para marker */
1269 lastPara->DeleteRange(r);
1270
1271 // Make sure the numbering is correct
1272 long end;
1273 fragment.CalculateRange(firstPara->GetRange().GetStart(), end);
1274
1275 // We only have part of a paragraph at the end
1276 fragment.SetPartialParagraph(true);
1277 }
1278 else
1279 {
1280 if (topTailRange.GetEnd() == (lastPara->GetRange().GetEnd() - 1))
1281 // We have a partial paragraph (don't save last new paragraph marker)
1282 fragment.SetPartialParagraph(true);
1283 else
1284 // We have a complete paragraph
1285 fragment.SetPartialParagraph(false);
1286 }
1287 }
1288
1289 return true;
1290}
1291
1292/// Given a position, get the number of the visible line (potentially many to a paragraph),
1293/// starting from zero at the start of the buffer.
1294long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos, bool caretPosition, bool startOfLine) const
1295{
1296 if (caretPosition)
1297 pos ++;
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 (child->GetRange().Contains(pos))
1308 {
1309 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
1310 while (node2)
1311 {
1312 wxRichTextLine* line = node2->GetData();
1e967276 1313 wxRichTextRange lineRange = line->GetAbsoluteRange();
7fe8059f 1314
1e967276 1315 if (lineRange.Contains(pos))
5d7836c4
JS
1316 {
1317 // If the caret is displayed at the end of the previous wrapped line,
1318 // we want to return the line it's _displayed_ at (not the actual line
1319 // containing the position).
1e967276 1320 if (lineRange.GetStart() == pos && !startOfLine && child->GetRange().GetStart() != pos)
5d7836c4
JS
1321 return lineCount - 1;
1322 else
1323 return lineCount;
1324 }
1325
1326 lineCount ++;
7fe8059f 1327
5d7836c4
JS
1328 node2 = node2->GetNext();
1329 }
1330 // If we didn't find it in the lines, it must be
1331 // the last position of the paragraph. So return the last line.
1332 return lineCount-1;
1333 }
1334 else
1335 lineCount += child->GetLines().GetCount();
1336
1337 node = node->GetNext();
1338 }
1339
1340 // Not found
1341 return -1;
1342}
1343
1344/// Given a line number, get the corresponding wxRichTextLine object.
1345wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber) const
1346{
1347 int lineCount = 0;
1348
1349 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1350 while (node)
1351 {
1352 wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1353 wxASSERT(child != NULL);
1354
1355 if (lineNumber < (int) (child->GetLines().GetCount() + lineCount))
1356 {
1357 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
1358 while (node2)
1359 {
1360 wxRichTextLine* line = node2->GetData();
7fe8059f 1361
5d7836c4
JS
1362 if (lineCount == lineNumber)
1363 return line;
1364
1365 lineCount ++;
7fe8059f 1366
5d7836c4 1367 node2 = node2->GetNext();
7fe8059f 1368 }
5d7836c4
JS
1369 }
1370 else
1371 lineCount += child->GetLines().GetCount();
1372
1373 node = node->GetNext();
1374 }
1375
1376 // Didn't find it
1377 return NULL;
1378}
1379
1380/// Delete range from layout.
1381bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange& range)
1382{
1383 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
7fe8059f 1384
5d7836c4
JS
1385 while (node)
1386 {
1387 wxRichTextParagraph* obj = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1388 wxASSERT (obj != NULL);
1389
1390 wxRichTextObjectList::compatibility_iterator next = node->GetNext();
7fe8059f 1391
5d7836c4
JS
1392 // Delete the range in each paragraph
1393
1394 if (!obj->GetRange().IsOutside(range))
1395 {
1396 // Deletes the content of this object within the given range
1397 obj->DeleteRange(range);
1398
1399 // If the whole paragraph is within the range to delete,
1400 // delete the whole thing.
1401 if (range.GetStart() <= obj->GetRange().GetStart() && range.GetEnd() >= obj->GetRange().GetEnd())
1402 {
1403 // Delete the whole object
1404 RemoveChild(obj, true);
1405 }
1406 // If the range includes the paragraph end, we need to join this
1407 // and the next paragraph.
1408 else if (range.Contains(obj->GetRange().GetEnd()))
1409 {
1410 // We need to move the objects from the next paragraph
1411 // to this paragraph
1412
1413 if (next)
1414 {
1415 wxRichTextParagraph* nextParagraph = wxDynamicCast(next->GetData(), wxRichTextParagraph);
1416 next = next->GetNext();
1417 if (nextParagraph)
1418 {
1419 // Delete the stuff we need to delete
1420 nextParagraph->DeleteRange(range);
1421
1422 // Move the objects to the previous para
1423 wxRichTextObjectList::compatibility_iterator node1 = nextParagraph->GetChildren().GetFirst();
1424
1425 while (node1)
1426 {
1427 wxRichTextObject* obj1 = node1->GetData();
1428
1429 // If the object is empty, optimise it out
1430 if (obj1->IsEmpty())
1431 {
1432 delete obj1;
1433 }
1434 else
1435 {
1436 obj->AppendChild(obj1);
1437 }
1438
1439 wxRichTextObjectList::compatibility_iterator next1 = node1->GetNext();
9e31a660 1440 nextParagraph->GetChildren().Erase(node1);
5d7836c4
JS
1441
1442 node1 = next1;
1443 }
1444
1445 // Delete the paragraph
1446 RemoveChild(nextParagraph, true);
1447
1448 }
7fe8059f 1449 }
5d7836c4
JS
1450
1451 }
1452 }
7fe8059f 1453
5d7836c4
JS
1454 node = next;
1455 }
7fe8059f 1456
5d7836c4
JS
1457 return true;
1458}
1459
1460/// Get any text in this object for the given range
1461wxString wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange& range) const
1462{
1463 int lineCount = 0;
1464 wxString text;
1465 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1466 while (node)
1467 {
1468 wxRichTextObject* child = node->GetData();
1469 if (!child->GetRange().IsOutside(range))
1470 {
fe5aa22c
JS
1471// if (lineCount > 0)
1472// text += wxT("\n");
5d7836c4
JS
1473 wxRichTextRange childRange = range;
1474 childRange.LimitTo(child->GetRange());
7fe8059f 1475
5d7836c4 1476 wxString childText = child->GetTextForRange(childRange);
7fe8059f 1477
5d7836c4
JS
1478 text += childText;
1479
fe5aa22c
JS
1480 if (childRange.GetEnd() == child->GetRange().GetEnd())
1481 text += wxT("\n");
1482
5d7836c4
JS
1483 lineCount ++;
1484 }
1485 node = node->GetNext();
1486 }
1487
1488 return text;
1489}
1490
1491/// Get all the text
1492wxString wxRichTextParagraphLayoutBox::GetText() const
1493{
1494 return GetTextForRange(GetRange());
1495}
1496
1497/// Get the paragraph by number
1498wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber) const
1499{
27e20452 1500 if ((size_t) paragraphNumber >= GetChildCount())
5d7836c4
JS
1501 return NULL;
1502
1503 return (wxRichTextParagraph*) GetChild((size_t) paragraphNumber);
1504}
1505
1506/// Get the length of the paragraph
1507int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber) const
1508{
1509 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
1510 if (para)
1511 return para->GetRange().GetLength() - 1; // don't include newline
1512 else
1513 return 0;
1514}
1515
1516/// Get the text of the paragraph
1517wxString wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber) const
1518{
1519 wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
1520 if (para)
1521 return para->GetTextForRange(para->GetRange());
1522 else
1523 return wxEmptyString;
1524}
1525
1526/// Convert zero-based line column and paragraph number to a position.
1527long wxRichTextParagraphLayoutBox::XYToPosition(long x, long y) const
1528{
1529 wxRichTextParagraph* para = GetParagraphAtLine(y);
1530 if (para)
1531 {
1532 return para->GetRange().GetStart() + x;
1533 }
1534 else
1535 return -1;
1536}
1537
1538/// Convert zero-based position to line column and paragraph number
1539bool wxRichTextParagraphLayoutBox::PositionToXY(long pos, long* x, long* y) const
1540{
1541 wxRichTextParagraph* para = GetParagraphAtPosition(pos);
1542 if (para)
1543 {
1544 int count = 0;
1545 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1546 while (node)
1547 {
1548 wxRichTextObject* child = node->GetData();
1549 if (child == para)
1550 break;
1551 count ++;
1552 node = node->GetNext();
1553 }
1554
1555 *y = count;
1556 *x = pos - para->GetRange().GetStart();
1557
1558 return true;
1559 }
1560 else
1561 return false;
1562}
1563
1564/// Get the leaf object in a paragraph at this position.
1565/// Given a line number, get the corresponding wxRichTextLine object.
1566wxRichTextObject* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position) const
1567{
1568 wxRichTextParagraph* para = GetParagraphAtPosition(position);
1569 if (para)
1570 {
1571 wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
7fe8059f 1572
5d7836c4
JS
1573 while (node)
1574 {
1575 wxRichTextObject* child = node->GetData();
1576 if (child->GetRange().Contains(position))
1577 return child;
7fe8059f 1578
5d7836c4
JS
1579 node = node->GetNext();
1580 }
1581 if (position == para->GetRange().GetEnd() && para->GetChildCount() > 0)
1582 return para->GetChildren().GetLast()->GetData();
1583 }
1584 return NULL;
1585}
1586
1587/// Set character or paragraph text attributes: apply character styles only to immediate text nodes
59509217 1588bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, int flags)
5d7836c4
JS
1589{
1590 bool characterStyle = false;
1591 bool paragraphStyle = false;
1592
1593 if (style.IsCharacterStyle())
1594 characterStyle = true;
1595 if (style.IsParagraphStyle())
1596 paragraphStyle = true;
1597
59509217
JS
1598 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
1599 bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
1600 bool parasOnly = ((flags & wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY) != 0);
1601 bool charactersOnly = ((flags & wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY) != 0);
523d2f14
JS
1602 bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
1603
1604 // Apply paragraph style first, if any
1605 wxRichTextAttr wholeStyle(style);
1606
1607 if (wholeStyle.HasParagraphStyleName() && GetStyleSheet())
1608 {
1609 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(wholeStyle.GetParagraphStyleName());
1610 if (def)
1611 wxRichTextApplyStyle(wholeStyle, def->GetStyle());
1612 }
59509217
JS
1613
1614 // Limit the attributes to be set to the content to only character attributes.
523d2f14 1615 wxRichTextAttr characterAttributes(wholeStyle);
59509217
JS
1616 characterAttributes.SetFlags(characterAttributes.GetFlags() & (wxTEXT_ATTR_CHARACTER));
1617
523d2f14
JS
1618 if (characterAttributes.HasCharacterStyleName() && GetStyleSheet())
1619 {
1620 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterAttributes.GetCharacterStyleName());
1621 if (def)
1622 wxRichTextApplyStyle(characterAttributes, def->GetStyle());
1623 }
1624
5d7836c4
JS
1625 // If we are associated with a control, make undoable; otherwise, apply immediately
1626 // to the data.
1627
1628 bool haveControl = (GetRichTextCtrl() != NULL);
1629
1630 wxRichTextAction* action = NULL;
7fe8059f 1631
5d7836c4
JS
1632 if (haveControl && withUndo)
1633 {
1634 action = new wxRichTextAction(NULL, _("Change Style"), wxRICHTEXT_CHANGE_STYLE, & GetRichTextCtrl()->GetBuffer(), GetRichTextCtrl());
1635 action->SetRange(range);
1636 action->SetPosition(GetRichTextCtrl()->GetCaretPosition());
1637 }
1638
1639 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
1640 while (node)
1641 {
1642 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
1643 wxASSERT (para != NULL);
1644
1645 if (para && para->GetChildCount() > 0)
1646 {
1647 // Stop searching if we're beyond the range of interest
1648 if (para->GetRange().GetStart() > range.GetEnd())
1649 break;
1650
1651 if (!para->GetRange().IsOutside(range))
1652 {
1653 // We'll be using a copy of the paragraph to make style changes,
1654 // not updating the buffer directly.
4e09ebe8 1655 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
7fe8059f 1656
5d7836c4
JS
1657 if (haveControl && withUndo)
1658 {
1659 newPara = new wxRichTextParagraph(*para);
1660 action->GetNewParagraphs().AppendChild(newPara);
1661
1662 // Also store the old ones for Undo
1663 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
1664 }
1665 else
1666 newPara = para;
41a85215 1667
a7ed48a5
JS
1668 // If we're specifying paragraphs only, then we really mean character formatting
1669 // to be included in the paragraph style
1670 if ((paragraphStyle || parasOnly) && !charactersOnly)
59509217 1671 {
523d2f14
JS
1672 if (resetExistingStyle)
1673 newPara->GetAttributes() = wholeStyle;
1674 else
59509217 1675 {
523d2f14
JS
1676 if (applyMinimal)
1677 {
1678 // Only apply attributes that will make a difference to the combined
1679 // style as seen on the display
1680 wxRichTextAttr combinedAttr(para->GetCombinedAttributes());
1681 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle, & combinedAttr);
1682 }
1683 else
1684 wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle);
59509217 1685 }
59509217 1686 }
5d7836c4 1687
fe5aa22c
JS
1688#if wxRICHTEXT_USE_DYNAMIC_STYLES
1689 // If applying paragraph styles dynamically, don't change the text objects' attributes
1690 // since they will computed as needed. Only apply the character styling if it's _only_
1691 // character styling. This policy is subject to change and might be put under user control.
1692
59509217
JS
1693 // Hm. we might well be applying a mix of paragraph and character styles, in which
1694 // case we _do_ want to apply character styles regardless of what para styles are set.
1695 // But if we're applying a paragraph style, which has some character attributes, but
1696 // we only want the paragraphs to hold this character style, then we _don't_ want to
1697 // apply the character style. So we need to be able to choose.
1698
1699 // if (!paragraphStyle && characterStyle && range.GetStart() != newPara->GetRange().GetEnd())
1700 if (!parasOnly && characterStyle && range.GetStart() != newPara->GetRange().GetEnd())
fe5aa22c 1701#else
5d7836c4 1702 if (characterStyle && range.GetStart() != newPara->GetRange().GetEnd())
fe5aa22c 1703#endif
5d7836c4
JS
1704 {
1705 wxRichTextRange childRange(range);
1706 childRange.LimitTo(newPara->GetRange());
7fe8059f 1707
5d7836c4
JS
1708 // Find the starting position and if necessary split it so
1709 // we can start applying a different style.
1710 // TODO: check that the style actually changes or is different
1711 // from style outside of range
4e09ebe8
JS
1712 wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
1713 wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
7fe8059f 1714
5d7836c4
JS
1715 if (childRange.GetStart() == newPara->GetRange().GetStart())
1716 firstObject = newPara->GetChildren().GetFirst()->GetData();
1717 else
1718 firstObject = newPara->SplitAt(range.GetStart());
7fe8059f 1719
5d7836c4
JS
1720 // Increment by 1 because we're apply the style one _after_ the split point
1721 long splitPoint = childRange.GetEnd();
1722 if (splitPoint != newPara->GetRange().GetEnd())
1723 splitPoint ++;
7fe8059f 1724
5d7836c4
JS
1725 // Find last object
1726 if (splitPoint == newPara->GetRange().GetEnd() || splitPoint == (newPara->GetRange().GetEnd() - 1))
1727 lastObject = newPara->GetChildren().GetLast()->GetData();
1728 else
1729 // lastObject is set as a side-effect of splitting. It's
1730 // returned as the object before the new object.
1731 (void) newPara->SplitAt(splitPoint, & lastObject);
7fe8059f 1732
5d7836c4
JS
1733 wxASSERT(firstObject != NULL);
1734 wxASSERT(lastObject != NULL);
7fe8059f 1735
5d7836c4
JS
1736 if (!firstObject || !lastObject)
1737 continue;
7fe8059f 1738
5d7836c4
JS
1739 wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
1740 wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
7fe8059f 1741
4c9847e1
MW
1742 wxASSERT(firstNode);
1743 wxASSERT(lastNode);
7fe8059f 1744
5d7836c4 1745 wxRichTextObjectList::compatibility_iterator node2 = firstNode;
7fe8059f 1746
5d7836c4
JS
1747 while (node2)
1748 {
1749 wxRichTextObject* child = node2->GetData();
7fe8059f 1750
523d2f14
JS
1751 if (resetExistingStyle)
1752 child->GetAttributes() = characterAttributes;
1753 else
59509217 1754 {
523d2f14
JS
1755 if (applyMinimal)
1756 {
1757 // Only apply attributes that will make a difference to the combined
1758 // style as seen on the display
1759 wxRichTextAttr combinedAttr(newPara->GetCombinedAttributes(child->GetAttributes()));
1760 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes, & combinedAttr);
1761 }
1762 else
1763 wxRichTextApplyStyle(child->GetAttributes(), characterAttributes);
59509217 1764 }
59509217 1765
5d7836c4
JS
1766 if (node2 == lastNode)
1767 break;
7fe8059f 1768
5d7836c4
JS
1769 node2 = node2->GetNext();
1770 }
1771 }
1772 }
1773 }
1774
1775 node = node->GetNext();
1776 }
1777
1778 // Do action, or delay it until end of batch.
1779 if (haveControl && withUndo)
1780 GetRichTextCtrl()->GetBuffer().SubmitAction(action);
1781
1782 return true;
1783}
1784
1785/// Set text attributes
59509217 1786bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxTextAttrEx& style, int flags)
5d7836c4
JS
1787{
1788 wxRichTextAttr richStyle = style;
59509217 1789 return SetStyle(range, richStyle, flags);
5d7836c4
JS
1790}
1791
1792/// Get the text attributes for this position.
3966a9f4 1793bool wxRichTextParagraphLayoutBox::GetStyle(long position, wxTextAttrEx& style)
5d7836c4 1794{
fe5aa22c
JS
1795 return DoGetStyle(position, style, true);
1796}
e191ee87 1797
fe5aa22c
JS
1798/// Get the text attributes for this position.
1799bool wxRichTextParagraphLayoutBox::GetStyle(long position, wxRichTextAttr& style)
1800{
1801 wxTextAttrEx textAttrEx(style);
1802 if (GetStyle(position, textAttrEx))
1803 {
1804 style = textAttrEx;
1805 return true;
1806 }
5d7836c4 1807 else
fe5aa22c
JS
1808 return false;
1809}
e191ee87 1810
fe5aa22c
JS
1811/// Get the content (uncombined) attributes for this position.
1812bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position, wxTextAttrEx& style)
1813{
1814 return DoGetStyle(position, style, false);
1815}
1816
1817bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position, wxRichTextAttr& style)
1818{
1819 wxTextAttrEx textAttrEx(style);
1820 if (GetUncombinedStyle(position, textAttrEx))
5d7836c4 1821 {
fe5aa22c 1822 style = textAttrEx;
5d7836c4
JS
1823 return true;
1824 }
1825 else
1826 return false;
1827}
1828
fe5aa22c
JS
1829/// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
1830/// context attributes.
1831bool wxRichTextParagraphLayoutBox::DoGetStyle(long position, wxTextAttrEx& style, bool combineStyles)
5d7836c4 1832{
4e09ebe8 1833 wxRichTextObject* obj wxDUMMY_INITIALIZE(NULL);
e191ee87 1834
5d7836c4 1835 if (style.IsParagraphStyle())
fe5aa22c 1836 {
5d7836c4 1837 obj = GetParagraphAtPosition(position);
fe5aa22c
JS
1838 if (obj)
1839 {
1840#if wxRICHTEXT_USE_DYNAMIC_STYLES
1841 if (combineStyles)
1842 {
1843 // Start with the base style
1844 style = GetAttributes();
e191ee87 1845
fe5aa22c
JS
1846 // Apply the paragraph style
1847 wxRichTextApplyStyle(style, obj->GetAttributes());
1848 }
1849 else
1850 style = obj->GetAttributes();
1851#else
1852 style = obj->GetAttributes();
1853#endif
1854 return true;
1855 }
5d7836c4
JS
1856 }
1857 else
fe5aa22c
JS
1858 {
1859 obj = GetLeafObjectAtPosition(position);
1860 if (obj)
1861 {
1862#if wxRICHTEXT_USE_DYNAMIC_STYLES
1863 if (combineStyles)
1864 {
1865 wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph);
1866 style = para ? para->GetCombinedAttributes(obj->GetAttributes()) : obj->GetAttributes();
1867 }
1868 else
1869 style = obj->GetAttributes();
1870#else
1871 style = obj->GetAttributes();
1872#endif
1873 return true;
1874 }
fe5aa22c
JS
1875 }
1876 return false;
5d7836c4
JS
1877}
1878
59509217
JS
1879static bool wxHasStyle(long flags, long style)
1880{
1881 return (flags & style) != 0;
1882}
1883
1884/// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
1885/// content.
42688aea 1886bool wxRichTextParagraphLayoutBox::CollectStyle(wxTextAttrEx& currentStyle, const wxTextAttrEx& style, long& multipleStyleAttributes, int& multipleTextEffectAttributes)
59509217
JS
1887{
1888 if (style.HasFont())
1889 {
1890 if (style.HasSize() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_FONT_SIZE))
1891 {
1892 if (currentStyle.GetFont().Ok() && currentStyle.HasSize())
1893 {
1894 if (currentStyle.GetFont().GetPointSize() != style.GetFont().GetPointSize())
1895 {
1896 // Clash of style - mark as such
1897 multipleStyleAttributes |= wxTEXT_ATTR_FONT_SIZE;
1898 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_FONT_SIZE);
1899 }
1900 }
1901 else
1902 {
1903 if (!currentStyle.GetFont().Ok())
1904 wxSetFontPreservingStyles(currentStyle, *wxNORMAL_FONT);
1905 wxFont font(currentStyle.GetFont());
1906 font.SetPointSize(style.GetFont().GetPointSize());
1907
1908 wxSetFontPreservingStyles(currentStyle, font);
1909 currentStyle.SetFlags(currentStyle.GetFlags() | wxTEXT_ATTR_FONT_SIZE);
1910 }
1911 }
1912
1913 if (style.HasItalic() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_FONT_ITALIC))
1914 {
1915 if (currentStyle.GetFont().Ok() && currentStyle.HasItalic())
1916 {
1917 if (currentStyle.GetFont().GetStyle() != style.GetFont().GetStyle())
1918 {
1919 // Clash of style - mark as such
1920 multipleStyleAttributes |= wxTEXT_ATTR_FONT_ITALIC;
1921 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_FONT_ITALIC);
1922 }
1923 }
1924 else
1925 {
1926 if (!currentStyle.GetFont().Ok())
1927 wxSetFontPreservingStyles(currentStyle, *wxNORMAL_FONT);
1928 wxFont font(currentStyle.GetFont());
1929 font.SetStyle(style.GetFont().GetStyle());
1930 wxSetFontPreservingStyles(currentStyle, font);
1931 currentStyle.SetFlags(currentStyle.GetFlags() | wxTEXT_ATTR_FONT_ITALIC);
1932 }
1933 }
1934
1935 if (style.HasWeight() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_FONT_WEIGHT))
1936 {
1937 if (currentStyle.GetFont().Ok() && currentStyle.HasWeight())
1938 {
1939 if (currentStyle.GetFont().GetWeight() != style.GetFont().GetWeight())
1940 {
1941 // Clash of style - mark as such
1942 multipleStyleAttributes |= wxTEXT_ATTR_FONT_WEIGHT;
1943 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_FONT_WEIGHT);
1944 }
1945 }
1946 else
1947 {
1948 if (!currentStyle.GetFont().Ok())
1949 wxSetFontPreservingStyles(currentStyle, *wxNORMAL_FONT);
1950 wxFont font(currentStyle.GetFont());
1951 font.SetWeight(style.GetFont().GetWeight());
1952 wxSetFontPreservingStyles(currentStyle, font);
1953 currentStyle.SetFlags(currentStyle.GetFlags() | wxTEXT_ATTR_FONT_WEIGHT);
1954 }
1955 }
1956
1957 if (style.HasFaceName() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_FONT_FACE))
1958 {
1959 if (currentStyle.GetFont().Ok() && currentStyle.HasFaceName())
1960 {
1961 wxString faceName1(currentStyle.GetFont().GetFaceName());
1962 wxString faceName2(style.GetFont().GetFaceName());
1963
1964 if (faceName1 != faceName2)
1965 {
1966 // Clash of style - mark as such
1967 multipleStyleAttributes |= wxTEXT_ATTR_FONT_FACE;
1968 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_FONT_FACE);
1969 }
1970 }
1971 else
1972 {
1973 if (!currentStyle.GetFont().Ok())
1974 wxSetFontPreservingStyles(currentStyle, *wxNORMAL_FONT);
1975 wxFont font(currentStyle.GetFont());
1976 font.SetFaceName(style.GetFont().GetFaceName());
1977 wxSetFontPreservingStyles(currentStyle, font);
1978 currentStyle.SetFlags(currentStyle.GetFlags() | wxTEXT_ATTR_FONT_FACE);
1979 }
1980 }
1981
1982 if (style.HasUnderlined() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_FONT_UNDERLINE))
1983 {
1984 if (currentStyle.GetFont().Ok() && currentStyle.HasUnderlined())
1985 {
1986 if (currentStyle.GetFont().GetUnderlined() != style.GetFont().GetUnderlined())
1987 {
1988 // Clash of style - mark as such
1989 multipleStyleAttributes |= wxTEXT_ATTR_FONT_UNDERLINE;
1990 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_FONT_UNDERLINE);
1991 }
1992 }
1993 else
1994 {
1995 if (!currentStyle.GetFont().Ok())
1996 wxSetFontPreservingStyles(currentStyle, *wxNORMAL_FONT);
1997 wxFont font(currentStyle.GetFont());
1998 font.SetUnderlined(style.GetFont().GetUnderlined());
1999 wxSetFontPreservingStyles(currentStyle, font);
2000 currentStyle.SetFlags(currentStyle.GetFlags() | wxTEXT_ATTR_FONT_UNDERLINE);
2001 }
2002 }
2003 }
2004
2005 if (style.HasTextColour() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_TEXT_COLOUR))
2006 {
2007 if (currentStyle.HasTextColour())
2008 {
2009 if (currentStyle.GetTextColour() != style.GetTextColour())
2010 {
2011 // Clash of style - mark as such
2012 multipleStyleAttributes |= wxTEXT_ATTR_TEXT_COLOUR;
2013 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_TEXT_COLOUR);
2014 }
2015 }
2016 else
2017 currentStyle.SetTextColour(style.GetTextColour());
2018 }
2019
2020 if (style.HasBackgroundColour() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_BACKGROUND_COLOUR))
2021 {
2022 if (currentStyle.HasBackgroundColour())
2023 {
2024 if (currentStyle.GetBackgroundColour() != style.GetBackgroundColour())
2025 {
2026 // Clash of style - mark as such
2027 multipleStyleAttributes |= wxTEXT_ATTR_BACKGROUND_COLOUR;
2028 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR);
2029 }
2030 }
2031 else
2032 currentStyle.SetBackgroundColour(style.GetBackgroundColour());
2033 }
2034
2035 if (style.HasAlignment() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_ALIGNMENT))
2036 {
2037 if (currentStyle.HasAlignment())
2038 {
2039 if (currentStyle.GetAlignment() != style.GetAlignment())
2040 {
2041 // Clash of style - mark as such
2042 multipleStyleAttributes |= wxTEXT_ATTR_ALIGNMENT;
2043 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_ALIGNMENT);
2044 }
2045 }
2046 else
2047 currentStyle.SetAlignment(style.GetAlignment());
2048 }
2049
2050 if (style.HasTabs() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_TABS))
2051 {
2052 if (currentStyle.HasTabs())
2053 {
2054 if (!wxRichTextTabsEq(currentStyle.GetTabs(), style.GetTabs()))
2055 {
2056 // Clash of style - mark as such
2057 multipleStyleAttributes |= wxTEXT_ATTR_TABS;
2058 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_TABS);
2059 }
2060 }
2061 else
2062 currentStyle.SetTabs(style.GetTabs());
2063 }
2064
2065 if (style.HasLeftIndent() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_LEFT_INDENT))
2066 {
2067 if (currentStyle.HasLeftIndent())
2068 {
2069 if (currentStyle.GetLeftIndent() != style.GetLeftIndent() || currentStyle.GetLeftSubIndent() != style.GetLeftSubIndent())
2070 {
2071 // Clash of style - mark as such
2072 multipleStyleAttributes |= wxTEXT_ATTR_LEFT_INDENT;
2073 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_LEFT_INDENT);
2074 }
2075 }
2076 else
2077 currentStyle.SetLeftIndent(style.GetLeftIndent(), style.GetLeftSubIndent());
2078 }
2079
2080 if (style.HasRightIndent() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_RIGHT_INDENT))
2081 {
2082 if (currentStyle.HasRightIndent())
2083 {
2084 if (currentStyle.GetRightIndent() != style.GetRightIndent())
2085 {
2086 // Clash of style - mark as such
2087 multipleStyleAttributes |= wxTEXT_ATTR_RIGHT_INDENT;
2088 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_RIGHT_INDENT);
2089 }
2090 }
2091 else
2092 currentStyle.SetRightIndent(style.GetRightIndent());
2093 }
2094
2095 if (style.HasParagraphSpacingAfter() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_PARA_SPACING_AFTER))
2096 {
2097 if (currentStyle.HasParagraphSpacingAfter())
2098 {
42688aea 2099 if (currentStyle.GetParagraphSpacingAfter() != style.GetParagraphSpacingAfter())
59509217
JS
2100 {
2101 // Clash of style - mark as such
2102 multipleStyleAttributes |= wxTEXT_ATTR_PARA_SPACING_AFTER;
2103 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_PARA_SPACING_AFTER);
2104 }
2105 }
2106 else
2107 currentStyle.SetParagraphSpacingAfter(style.GetParagraphSpacingAfter());
2108 }
2109
2110 if (style.HasParagraphSpacingBefore() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_PARA_SPACING_BEFORE))
2111 {
2112 if (currentStyle.HasParagraphSpacingBefore())
2113 {
42688aea 2114 if (currentStyle.GetParagraphSpacingBefore() != style.GetParagraphSpacingBefore())
59509217
JS
2115 {
2116 // Clash of style - mark as such
2117 multipleStyleAttributes |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
2118 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_PARA_SPACING_BEFORE);
2119 }
2120 }
2121 else
2122 currentStyle.SetParagraphSpacingBefore(style.GetParagraphSpacingBefore());
2123 }
2124
2125 if (style.HasLineSpacing() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_LINE_SPACING))
2126 {
2127 if (currentStyle.HasLineSpacing())
2128 {
42688aea 2129 if (currentStyle.GetLineSpacing() != style.GetLineSpacing())
59509217
JS
2130 {
2131 // Clash of style - mark as such
2132 multipleStyleAttributes |= wxTEXT_ATTR_LINE_SPACING;
2133 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_LINE_SPACING);
2134 }
2135 }
2136 else
2137 currentStyle.SetLineSpacing(style.GetLineSpacing());
2138 }
2139
2140 if (style.HasCharacterStyleName() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
2141 {
2142 if (currentStyle.HasCharacterStyleName())
2143 {
42688aea 2144 if (currentStyle.GetCharacterStyleName() != style.GetCharacterStyleName())
59509217
JS
2145 {
2146 // Clash of style - mark as such
2147 multipleStyleAttributes |= wxTEXT_ATTR_CHARACTER_STYLE_NAME;
2148 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_CHARACTER_STYLE_NAME);
2149 }
2150 }
2151 else
2152 currentStyle.SetCharacterStyleName(style.GetCharacterStyleName());
2153 }
2154
2155 if (style.HasParagraphStyleName() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
2156 {
2157 if (currentStyle.HasParagraphStyleName())
2158 {
42688aea 2159 if (currentStyle.GetParagraphStyleName() != style.GetParagraphStyleName())
59509217
JS
2160 {
2161 // Clash of style - mark as such
2162 multipleStyleAttributes |= wxTEXT_ATTR_PARAGRAPH_STYLE_NAME;
2163 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
2164 }
2165 }
2166 else
2167 currentStyle.SetParagraphStyleName(style.GetParagraphStyleName());
2168 }
2169
38f833b1
JS
2170 if (style.HasListStyleName() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_LIST_STYLE_NAME))
2171 {
2172 if (currentStyle.HasListStyleName())
2173 {
42688aea 2174 if (currentStyle.GetListStyleName() != style.GetListStyleName())
38f833b1
JS
2175 {
2176 // Clash of style - mark as such
2177 multipleStyleAttributes |= wxTEXT_ATTR_LIST_STYLE_NAME;
2178 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_LIST_STYLE_NAME);
2179 }
2180 }
2181 else
2182 currentStyle.SetListStyleName(style.GetListStyleName());
2183 }
2184
59509217
JS
2185 if (style.HasBulletStyle() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_BULLET_STYLE))
2186 {
2187 if (currentStyle.HasBulletStyle())
2188 {
42688aea 2189 if (currentStyle.GetBulletStyle() != style.GetBulletStyle())
59509217
JS
2190 {
2191 // Clash of style - mark as such
2192 multipleStyleAttributes |= wxTEXT_ATTR_BULLET_STYLE;
2193 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_BULLET_STYLE);
2194 }
2195 }
2196 else
2197 currentStyle.SetBulletStyle(style.GetBulletStyle());
2198 }
2199
2200 if (style.HasBulletNumber() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_BULLET_NUMBER))
2201 {
2202 if (currentStyle.HasBulletNumber())
2203 {
42688aea 2204 if (currentStyle.GetBulletNumber() != style.GetBulletNumber())
59509217
JS
2205 {
2206 // Clash of style - mark as such
2207 multipleStyleAttributes |= wxTEXT_ATTR_BULLET_NUMBER;
2208 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_BULLET_NUMBER);
2209 }
2210 }
2211 else
2212 currentStyle.SetBulletNumber(style.GetBulletNumber());
2213 }
2214
d2d0adc7 2215 if (style.HasBulletText() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_BULLET_TEXT))
59509217 2216 {
d2d0adc7 2217 if (currentStyle.HasBulletText())
59509217 2218 {
42688aea 2219 if (currentStyle.GetBulletText() != style.GetBulletText())
59509217
JS
2220 {
2221 // Clash of style - mark as such
d2d0adc7
JS
2222 multipleStyleAttributes |= wxTEXT_ATTR_BULLET_TEXT;
2223 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_BULLET_TEXT);
59509217
JS
2224 }
2225 }
2226 else
2227 {
d2d0adc7 2228 currentStyle.SetBulletText(style.GetBulletText());
59509217
JS
2229 currentStyle.SetBulletFont(style.GetBulletFont());
2230 }
2231 }
2232
f089713f
JS
2233 if (style.HasBulletName() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_BULLET_NAME))
2234 {
2235 if (currentStyle.HasBulletName())
2236 {
42688aea 2237 if (currentStyle.GetBulletName() != style.GetBulletName())
f089713f
JS
2238 {
2239 // Clash of style - mark as such
2240 multipleStyleAttributes |= wxTEXT_ATTR_BULLET_NAME;
2241 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_BULLET_NAME);
2242 }
2243 }
2244 else
2245 {
2246 currentStyle.SetBulletName(style.GetBulletName());
2247 }
2248 }
2249
d2d0adc7
JS
2250 if (style.HasURL() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_URL))
2251 {
2252 if (currentStyle.HasURL())
2253 {
42688aea 2254 if (currentStyle.GetURL() != style.GetURL())
d2d0adc7
JS
2255 {
2256 // Clash of style - mark as such
2257 multipleStyleAttributes |= wxTEXT_ATTR_URL;
2258 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_URL);
2259 }
2260 }
2261 else
2262 {
2263 currentStyle.SetURL(style.GetURL());
2264 }
2265 }
2266
42688aea
JS
2267 if (style.HasTextEffects() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_EFFECTS))
2268 {
2269 if (currentStyle.HasTextEffects())
2270 {
2271 // We need to find the bits in the new style that are different:
2272 // just look at those bits that are specified by the new style.
2273
2274 int currentRelevantTextEffects = currentStyle.GetTextEffects() & style.GetTextEffectFlags();
2275 int newRelevantTextEffects = style.GetTextEffects() & style.GetTextEffectFlags();
2276
2277 if (currentRelevantTextEffects != newRelevantTextEffects)
2278 {
2279 // Find the text effects that were different, using XOR
2280 int differentEffects = currentRelevantTextEffects ^ newRelevantTextEffects;
2281
2282 // Clash of style - mark as such
2283 multipleTextEffectAttributes |= differentEffects;
2284 currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~differentEffects);
2285 }
2286 }
2287 else
2288 {
2289 currentStyle.SetTextEffects(style.GetTextEffects());
2290 currentStyle.SetTextEffectFlags(style.GetTextEffectFlags());
2291 }
2292 }
2293
4d6d8bf4
JS
2294 if (style.HasOutlineLevel() && !wxHasStyle(multipleStyleAttributes, wxTEXT_ATTR_OUTLINE_LEVEL))
2295 {
2296 if (currentStyle.HasOutlineLevel())
2297 {
2298 if (currentStyle.GetOutlineLevel() != style.GetOutlineLevel())
2299 {
2300 // Clash of style - mark as such
2301 multipleStyleAttributes |= wxTEXT_ATTR_OUTLINE_LEVEL;
2302 currentStyle.SetFlags(currentStyle.GetFlags() & ~wxTEXT_ATTR_OUTLINE_LEVEL);
2303 }
2304 }
2305 else
2306 currentStyle.SetOutlineLevel(style.GetOutlineLevel());
2307 }
2308
59509217
JS
2309 return true;
2310}
2311
2312/// Get the combined style for a range - if any attribute is different within the range,
2313/// that attribute is not present within the flags.
2314/// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
2315/// nested.
2316bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange& range, wxTextAttrEx& style)
2317{
2318 style = wxTextAttrEx();
2319
2320 // The attributes that aren't valid because of multiple styles within the range
2321 long multipleStyleAttributes = 0;
42688aea
JS
2322 int multipleTextEffectAttributes = 0;
2323
59509217
JS
2324 wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
2325 while (node)
2326 {
2327 wxRichTextParagraph* para = (wxRichTextParagraph*) node->GetData();
2328 if (!(para->GetRange().GetStart() > range.GetEnd() || para->GetRange().GetEnd() < range.GetStart()))
2329 {
2330 if (para->GetChildren().GetCount() == 0)
2331 {
2332 wxTextAttrEx paraStyle = para->GetCombinedAttributes();
2333
42688aea 2334 CollectStyle(style, paraStyle, multipleStyleAttributes, multipleTextEffectAttributes);
59509217
JS
2335 }
2336 else
2337 {
2338 wxRichTextRange paraRange(para->GetRange());
2339 paraRange.LimitTo(range);
2340
2341 // First collect paragraph attributes only
2342 wxTextAttrEx paraStyle = para->GetCombinedAttributes();
2343 paraStyle.SetFlags(paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH);
42688aea 2344 CollectStyle(style, paraStyle, multipleStyleAttributes, multipleTextEffectAttributes);
59509217
JS
2345
2346 wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
2347
2348 while (childNode)
2349 {
2350 wxRichTextObject* child = childNode->GetData();
2351 if (!(child->GetRange().GetStart() > range.GetEnd() || child->GetRange().GetEnd() < range.GetStart()))
2352 {
2353 wxTextAttrEx childStyle = para->GetCombinedAttributes(child->GetAttributes());
2354
2355 // Now collect character attributes only
2356 childStyle.SetFlags(childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER);
2357
42688aea 2358 CollectStyle(style, childStyle, multipleStyleAttributes, multipleTextEffectAttributes);
59509217
JS
2359 }
2360
2361 childNode = childNode->GetNext();
2362 }
2363 }
2364 }
2365 node = node->GetNext();
2366 }
2367 return true;
2368}
2369
5d7836c4
JS
2370/// Set default style
2371bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxTextAttrEx& style)
2372{
fe5aa22c 2373 m_defaultAttributes = style;
5d7836c4
JS
2374 return true;
2375}
2376
2377/// Test if this whole range has character attributes of the specified kind. If any
2378/// of the attributes are different within the range, the test fails. You
2379/// can use this to implement, for example, bold button updating. style must have
2380/// flags indicating which attributes are of interest.
2381bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
2382{
2383 int foundCount = 0;
2384 int matchingCount = 0;
2385
2386 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2387 while (node)
2388 {
2389 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2390 wxASSERT (para != NULL);
2391
2392 if (para)
2393 {
2394 // Stop searching if we're beyond the range of interest
2395 if (para->GetRange().GetStart() > range.GetEnd())
2396 return foundCount == matchingCount;
2397
2398 if (!para->GetRange().IsOutside(range))
2399 {
2400 wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
2401
2402 while (node2)
2403 {
2404 wxRichTextObject* child = node2->GetData();
2405 if (!child->GetRange().IsOutside(range) && child->IsKindOf(CLASSINFO(wxRichTextPlainText)))
2406 {
2407 foundCount ++;
fe5aa22c
JS
2408#if wxRICHTEXT_USE_DYNAMIC_STYLES
2409 wxTextAttrEx textAttr = para->GetCombinedAttributes(child->GetAttributes());
2410#else
2411 const wxTextAttrEx& textAttr = child->GetAttributes();
2412#endif
2413 if (wxTextAttrEqPartial(textAttr, style, style.GetFlags()))
5d7836c4
JS
2414 matchingCount ++;
2415 }
2416
2417 node2 = node2->GetNext();
2418 }
2419 }
2420 }
2421
2422 node = node->GetNext();
2423 }
2424
2425 return foundCount == matchingCount;
2426}
2427
2428bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& range, const wxTextAttrEx& style) const
2429{
2430 wxRichTextAttr richStyle = style;
2431 return HasCharacterAttributes(range, richStyle);
2432}
2433
2434/// Test if this whole range has paragraph attributes of the specified kind. If any
2435/// of the attributes are different within the range, the test fails. You
2436/// can use this to implement, for example, centering button updating. style must have
2437/// flags indicating which attributes are of interest.
2438bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
2439{
2440 int foundCount = 0;
2441 int matchingCount = 0;
2442
2443 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2444 while (node)
2445 {
2446 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2447 wxASSERT (para != NULL);
2448
2449 if (para)
2450 {
2451 // Stop searching if we're beyond the range of interest
2452 if (para->GetRange().GetStart() > range.GetEnd())
2453 return foundCount == matchingCount;
2454
2455 if (!para->GetRange().IsOutside(range))
2456 {
fe5aa22c
JS
2457#if wxRICHTEXT_USE_DYNAMIC_STYLES
2458 wxTextAttrEx textAttr = GetAttributes();
2459 // Apply the paragraph style
2460 wxRichTextApplyStyle(textAttr, para->GetAttributes());
2461
2462#else
2463 const wxTextAttrEx& textAttr = para->GetAttributes();
2464#endif
5d7836c4 2465 foundCount ++;
fe5aa22c 2466 if (wxTextAttrEqPartial(textAttr, style, style.GetFlags()))
5d7836c4
JS
2467 matchingCount ++;
2468 }
2469 }
2470
2471 node = node->GetNext();
2472 }
2473 return foundCount == matchingCount;
2474}
2475
2476bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& range, const wxTextAttrEx& style) const
2477{
2478 wxRichTextAttr richStyle = style;
2479 return HasParagraphAttributes(range, richStyle);
2480}
2481
2482void wxRichTextParagraphLayoutBox::Clear()
2483{
2484 DeleteChildren();
2485}
2486
2487void wxRichTextParagraphLayoutBox::Reset()
2488{
2489 Clear();
2490
7fe8059f 2491 AddParagraph(wxEmptyString);
85d8909b
JS
2492
2493 Invalidate(wxRICHTEXT_ALL);
5d7836c4
JS
2494}
2495
38113684
JS
2496/// Invalidate the buffer. With no argument, invalidates whole buffer.
2497void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange& invalidRange)
2498{
2499 SetDirty(true);
39a1c2f2 2500
1e967276 2501 if (invalidRange == wxRICHTEXT_ALL)
38113684 2502 {
1e967276 2503 m_invalidRange = wxRICHTEXT_ALL;
38113684
JS
2504 return;
2505 }
1e967276
JS
2506
2507 // Already invalidating everything
2508 if (m_invalidRange == wxRICHTEXT_ALL)
2509 return;
39a1c2f2 2510
1e967276 2511 if ((invalidRange.GetStart() < m_invalidRange.GetStart()) || m_invalidRange.GetStart() == -1)
38113684
JS
2512 m_invalidRange.SetStart(invalidRange.GetStart());
2513 if (invalidRange.GetEnd() > m_invalidRange.GetEnd())
2514 m_invalidRange.SetEnd(invalidRange.GetEnd());
2515}
2516
2517/// Get invalid range, rounding to entire paragraphs if argument is true.
2518wxRichTextRange wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs) const
2519{
1e967276 2520 if (m_invalidRange == wxRICHTEXT_ALL || m_invalidRange == wxRICHTEXT_NONE)
38113684 2521 return m_invalidRange;
39a1c2f2 2522
38113684 2523 wxRichTextRange range = m_invalidRange;
39a1c2f2 2524
38113684
JS
2525 if (wholeParagraphs)
2526 {
2527 wxRichTextParagraph* para1 = GetParagraphAtPosition(range.GetStart());
2528 wxRichTextParagraph* para2 = GetParagraphAtPosition(range.GetEnd());
2529 if (para1)
2530 range.SetStart(para1->GetRange().GetStart());
2531 if (para2)
2532 range.SetEnd(para2->GetRange().GetEnd());
2533 }
2534 return range;
2535}
2536
fe5aa22c
JS
2537/// Apply the style sheet to the buffer, for example if the styles have changed.
2538bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet* styleSheet)
2539{
2540 wxASSERT(styleSheet != NULL);
2541 if (!styleSheet)
2542 return false;
2543
2544 int foundCount = 0;
2545
2546 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2547 while (node)
2548 {
2549 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2550 wxASSERT (para != NULL);
2551
2552 if (para)
2553 {
38f833b1
JS
2554 // Combine paragraph and list styles. If there is a list style in the original attributes,
2555 // the current indentation overrides anything else and is used to find the item indentation.
2556 // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
2557 // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
2558 // exception as above).
2559 // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
2560 // So when changing a list style interactively, could retrieve level based on current style, then
2561 // set appropriate indent and apply new style.
41a85215 2562
38f833b1
JS
2563 if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
2564 {
2565 int currentIndent = para->GetAttributes().GetLeftIndent();
2566
2567 wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
2568 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
2569 if (paraDef && !listDef)
2570 {
2571 para->GetAttributes() = paraDef->GetStyle();
2572 foundCount ++;
2573 }
2574 else if (listDef && !paraDef)
2575 {
2576 // Set overall style defined for the list style definition
2577 para->GetAttributes() = listDef->GetStyle();
2578
2579 // Apply the style for this level
2580 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
2581 foundCount ++;
2582 }
2583 else if (listDef && paraDef)
2584 {
2585 // Combines overall list style, style for level, and paragraph style
2586 para->GetAttributes() = listDef->CombineWithParagraphStyle(currentIndent, paraDef->GetStyle());
2587 foundCount ++;
2588 }
2589 }
2590 else if (para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
2591 {
2592 int currentIndent = para->GetAttributes().GetLeftIndent();
2593
2594 wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
2595
41a85215 2596 // Overall list definition style
38f833b1 2597 para->GetAttributes() = listDef->GetStyle();
41a85215 2598
38f833b1
JS
2599 // Style for this level
2600 wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
2601
2602 foundCount ++;
2603 }
2604 else if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && para->GetAttributes().GetListStyleName().IsEmpty())
fe5aa22c
JS
2605 {
2606 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
2607 if (def)
2608 {
2609 para->GetAttributes() = def->GetStyle();
2610 foundCount ++;
2611 }
2612 }
2613 }
2614
2615 node = node->GetNext();
2616 }
2617 return foundCount != 0;
2618}
2619
38f833b1
JS
2620/// Set list style
2621bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
2622{
2623 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
2624 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
2625 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
2626 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
41a85215 2627
38f833b1
JS
2628 // Current number, if numbering
2629 int n = startFrom;
41a85215 2630
38f833b1
JS
2631 wxASSERT (!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
2632
2633 // If we are associated with a control, make undoable; otherwise, apply immediately
2634 // to the data.
2635
2636 bool haveControl = (GetRichTextCtrl() != NULL);
2637
2638 wxRichTextAction* action = NULL;
2639
2640 if (haveControl && withUndo)
2641 {
2642 action = new wxRichTextAction(NULL, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE, & GetRichTextCtrl()->GetBuffer(), GetRichTextCtrl());
2643 action->SetRange(range);
2644 action->SetPosition(GetRichTextCtrl()->GetCaretPosition());
2645 }
2646
2647 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2648 while (node)
2649 {
2650 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2651 wxASSERT (para != NULL);
2652
2653 if (para && para->GetChildCount() > 0)
2654 {
2655 // Stop searching if we're beyond the range of interest
2656 if (para->GetRange().GetStart() > range.GetEnd())
2657 break;
2658
2659 if (!para->GetRange().IsOutside(range))
2660 {
2661 // We'll be using a copy of the paragraph to make style changes,
2662 // not updating the buffer directly.
2663 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
2664
2665 if (haveControl && withUndo)
2666 {
2667 newPara = new wxRichTextParagraph(*para);
2668 action->GetNewParagraphs().AppendChild(newPara);
2669
2670 // Also store the old ones for Undo
2671 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
2672 }
2673 else
2674 newPara = para;
41a85215 2675
38f833b1
JS
2676 if (def)
2677 {
2678 int thisIndent = newPara->GetAttributes().GetLeftIndent();
2679 int thisLevel = specifyLevel ? specifiedLevel : def->FindLevelForIndent(thisIndent);
41a85215 2680
38f833b1
JS
2681 // How is numbering going to work?
2682 // If we are renumbering, or numbering for the first time, we need to keep
2683 // track of the number for each level. But we might be simply applying a different
2684 // list style.
2685 // In Word, applying a style to several paragraphs, even if at different levels,
2686 // reverts the level back to the same one. So we could do the same here.
2687 // Renumbering will need to be done when we promote/demote a paragraph.
2688
2689 // Apply the overall list style, and item style for this level
2690 wxTextAttrEx listStyle(def->GetCombinedStyleForLevel(thisLevel));
2691 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
41a85215 2692
d2d0adc7 2693 // Now we need to do numbering
38f833b1
JS
2694 if (renumber)
2695 {
2696 newPara->GetAttributes().SetBulletNumber(n);
2697 }
41a85215 2698
38f833b1
JS
2699 n ++;
2700 }
2701 else if (!newPara->GetAttributes().GetListStyleName().IsEmpty())
2702 {
2703 // if def is NULL, remove list style, applying any associated paragraph style
2704 // to restore the attributes
2705
2706 newPara->GetAttributes().SetListStyleName(wxEmptyString);
2707 newPara->GetAttributes().SetLeftIndent(0, 0);
d2d0adc7 2708 newPara->GetAttributes().SetBulletText(wxEmptyString);
41a85215 2709
38f833b1 2710 // Eliminate the main list-related attributes
d2d0adc7 2711 newPara->GetAttributes().SetFlags(newPara->GetAttributes().GetFlags() & ~wxTEXT_ATTR_LEFT_INDENT & ~wxTEXT_ATTR_BULLET_STYLE & ~wxTEXT_ATTR_BULLET_NUMBER & ~wxTEXT_ATTR_BULLET_TEXT & wxTEXT_ATTR_LIST_STYLE_NAME);
41a85215 2712
38f833b1
JS
2713 wxRichTextStyleSheet* styleSheet = GetStyleSheet();
2714 if (styleSheet && !newPara->GetAttributes().GetParagraphStyleName().IsEmpty())
2715 {
2716 wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(newPara->GetAttributes().GetParagraphStyleName());
2717 if (def)
2718 {
2719 newPara->GetAttributes() = def->GetStyle();
2720 }
2721 }
2722 }
2723 }
2724 }
2725
2726 node = node->GetNext();
2727 }
2728
2729 // Do action, or delay it until end of batch.
2730 if (haveControl && withUndo)
2731 GetRichTextCtrl()->GetBuffer().SubmitAction(action);
2732
2733 return true;
2734}
2735
2736bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
2737{
2738 if (GetStyleSheet())
2739 {
2740 wxRichTextListStyleDefinition* def = GetStyleSheet()->FindListStyle(defName);
2741 if (def)
2742 return SetListStyle(range, def, flags, startFrom, specifiedLevel);
2743 }
2744 return false;
2745}
2746
2747/// Clear list for given range
2748bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange& range, int flags)
2749{
2750 return SetListStyle(range, NULL, flags);
2751}
2752
2753/// Number/renumber any list elements in the given range
2754bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
2755{
2756 return DoNumberList(range, range, 0, def, flags, startFrom, specifiedLevel);
2757}
2758
2759/// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
2760bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, const wxRichTextRange& promotionRange, int promoteBy,
2761 wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
2762{
2763 bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
2764 // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
3c738608 2765#ifdef __WXDEBUG__
38f833b1 2766 bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
3c738608 2767#endif
38f833b1
JS
2768
2769 bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
41a85215 2770
38f833b1
JS
2771 // Max number of levels
2772 const int maxLevels = 10;
41a85215 2773
38f833b1
JS
2774 // The level we're looking at now
2775 int currentLevel = -1;
41a85215 2776
38f833b1
JS
2777 // The item number for each level
2778 int levels[maxLevels];
2779 int i;
41a85215 2780
38f833b1
JS
2781 // Reset all numbering
2782 for (i = 0; i < maxLevels; i++)
2783 {
2784 if (startFrom != -1)
d2d0adc7 2785 levels[i] = startFrom-1;
38f833b1 2786 else if (renumber) // start again
d2d0adc7 2787 levels[i] = 0;
38f833b1
JS
2788 else
2789 levels[i] = -1; // start from the number we found, if any
2790 }
41a85215 2791
38f833b1
JS
2792 wxASSERT(!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
2793
2794 // If we are associated with a control, make undoable; otherwise, apply immediately
2795 // to the data.
2796
2797 bool haveControl = (GetRichTextCtrl() != NULL);
2798
2799 wxRichTextAction* action = NULL;
2800
2801 if (haveControl && withUndo)
2802 {
2803 action = new wxRichTextAction(NULL, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE, & GetRichTextCtrl()->GetBuffer(), GetRichTextCtrl());
2804 action->SetRange(range);
2805 action->SetPosition(GetRichTextCtrl()->GetCaretPosition());
2806 }
2807
2808 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
2809 while (node)
2810 {
2811 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
2812 wxASSERT (para != NULL);
2813
2814 if (para && para->GetChildCount() > 0)
2815 {
2816 // Stop searching if we're beyond the range of interest
2817 if (para->GetRange().GetStart() > range.GetEnd())
2818 break;
2819
2820 if (!para->GetRange().IsOutside(range))
2821 {
2822 // We'll be using a copy of the paragraph to make style changes,
2823 // not updating the buffer directly.
2824 wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
2825
2826 if (haveControl && withUndo)
2827 {
2828 newPara = new wxRichTextParagraph(*para);
2829 action->GetNewParagraphs().AppendChild(newPara);
2830
2831 // Also store the old ones for Undo
2832 action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
2833 }
2834 else
2835 newPara = para;
41a85215 2836
38f833b1
JS
2837 wxRichTextListStyleDefinition* defToUse = def;
2838 if (!defToUse)
2839 {
2840 wxRichTextStyleSheet* sheet = GetStyleSheet();
41a85215 2841
38f833b1
JS
2842 if (sheet && !newPara->GetAttributes().GetListStyleName().IsEmpty())
2843 defToUse = sheet->FindListStyle(newPara->GetAttributes().GetListStyleName());
2844 }
41a85215 2845
38f833b1
JS
2846 if (defToUse)
2847 {
2848 int thisIndent = newPara->GetAttributes().GetLeftIndent();
2849 int thisLevel = defToUse->FindLevelForIndent(thisIndent);
2850
d2d0adc7
JS
2851 // If we've specified a level to apply to all, change the level.
2852 if (specifiedLevel != -1)
38f833b1 2853 thisLevel = specifiedLevel;
41a85215 2854
38f833b1
JS
2855 // Do promotion if specified
2856 if ((promoteBy != 0) && !para->GetRange().IsOutside(promotionRange))
2857 {
2858 thisLevel = thisLevel - promoteBy;
2859 if (thisLevel < 0)
2860 thisLevel = 0;
2861 if (thisLevel > 9)
2862 thisLevel = 9;
2863 }
41a85215 2864
38f833b1
JS
2865 // Apply the overall list style, and item style for this level
2866 wxTextAttrEx listStyle(defToUse->GetCombinedStyleForLevel(thisLevel));
2867 wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
41a85215 2868
38f833b1 2869 // OK, we've (re)applied the style, now let's get the numbering right.
41a85215 2870
38f833b1
JS
2871 if (currentLevel == -1)
2872 currentLevel = thisLevel;
41a85215 2873
38f833b1
JS
2874 // Same level as before, do nothing except increment level's number afterwards
2875 if (currentLevel == thisLevel)
2876 {
2877 }
2878 // A deeper level: start renumbering all levels after current level
2879 else if (thisLevel > currentLevel)
2880 {
2881 for (i = currentLevel+1; i <= thisLevel; i++)
2882 {
d2d0adc7 2883 levels[i] = 0;
38f833b1
JS
2884 }
2885 currentLevel = thisLevel;
2886 }
2887 else if (thisLevel < currentLevel)
2888 {
2889 currentLevel = thisLevel;
41a85215 2890 }
38f833b1
JS
2891
2892 // Use the current numbering if -1 and we have a bullet number already
2893 if (levels[currentLevel] == -1)
2894 {
2895 if (newPara->GetAttributes().HasBulletNumber())
2896 levels[currentLevel] = newPara->GetAttributes().GetBulletNumber();
2897 else
2898 levels[currentLevel] = 1;
2899 }
d2d0adc7
JS
2900 else
2901 {
2902 levels[currentLevel] ++;
2903 }
41a85215 2904
38f833b1
JS
2905 newPara->GetAttributes().SetBulletNumber(levels[currentLevel]);
2906
d2d0adc7
JS
2907 // Create the bullet text if an outline list
2908 if (listStyle.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
2909 {
2910 wxString text;
2911 for (i = 0; i <= currentLevel; i++)
2912 {
2913 if (!text.IsEmpty())
2914 text += wxT(".");
2915 text += wxString::Format(wxT("%d"), levels[i]);
2916 }
2917 newPara->GetAttributes().SetBulletText(text);
2918 }
38f833b1
JS
2919 }
2920 }
2921 }
2922
2923 node = node->GetNext();
2924 }
2925
2926 // Do action, or delay it until end of batch.
2927 if (haveControl && withUndo)
2928 GetRichTextCtrl()->GetBuffer().SubmitAction(action);
2929
2930 return true;
2931}
2932
2933bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
2934{
2935 if (GetStyleSheet())
2936 {
2937 wxRichTextListStyleDefinition* def = NULL;
2938 if (!defName.IsEmpty())
2939 def = GetStyleSheet()->FindListStyle(defName);
2940 return NumberList(range, def, flags, startFrom, specifiedLevel);
2941 }
2942 return false;
2943}
2944
2945/// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
2946bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel)
2947{
2948 // TODO
2949 // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
2950 // to NumberList with a flag indicating promotion is required within one of the ranges.
2951 // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
2952 // a paragraph that either has no list style, or has one that is different or whose indentation is less.
2953 // We start renumbering from the para after that different para we found. We specify that the numbering of that
2954 // list position will start from 1.
2955 // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
2956 // We can end the renumbering at this point.
41a85215 2957
38f833b1 2958 // For now, only renumber within the promotion range.
41a85215 2959
38f833b1
JS
2960 return DoNumberList(range, range, promoteBy, def, flags, 1, specifiedLevel);
2961}
2962
2963bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel)
2964{
2965 if (GetStyleSheet())
2966 {
2967 wxRichTextListStyleDefinition* def = NULL;
2968 if (!defName.IsEmpty())
2969 def = GetStyleSheet()->FindListStyle(defName);
2970 return PromoteList(promoteBy, range, def, flags, specifiedLevel);
2971 }
2972 return false;
2973}
2974
d2d0adc7
JS
2975/// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
2976/// position of the paragraph that it had to start looking from.
2977bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr) const
2978{
d2d0adc7
JS
2979 if (!previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE)
2980 return false;
2981
2982 wxRichTextStyleSheet* sheet = GetStyleSheet();
2983 if (sheet && !previousParagraph->GetAttributes().GetListStyleName().IsEmpty())
2984 {
2985 wxRichTextListStyleDefinition* def = sheet->FindListStyle(previousParagraph->GetAttributes().GetListStyleName());
2986 if (def)
2987 {
2988 // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
2989 // int thisLevel = def->FindLevelForIndent(thisIndent);
2990
2991 bool isOutline = (previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE) != 0;
2992
2993 attr.SetFlags(previousParagraph->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER|wxTEXT_ATTR_BULLET_TEXT|wxTEXT_ATTR_BULLET_NAME));
2994 if (previousParagraph->GetAttributes().HasBulletName())
2995 attr.SetBulletName(previousParagraph->GetAttributes().GetBulletName());
2996 attr.SetBulletStyle(previousParagraph->GetAttributes().GetBulletStyle());
2997 attr.SetListStyleName(previousParagraph->GetAttributes().GetListStyleName());
2998
2999 int nextNumber = previousParagraph->GetAttributes().GetBulletNumber() + 1;
3000 attr.SetBulletNumber(nextNumber);
3001
3002 if (isOutline)
3003 {
3004 wxString text = previousParagraph->GetAttributes().GetBulletText();
3005 if (!text.IsEmpty())
3006 {
3007 int pos = text.Find(wxT('.'), true);
3008 if (pos != wxNOT_FOUND)
3009 {
3010 text = text.Mid(0, text.Length() - pos - 1);
3011 }
3012 else
3013 text = wxEmptyString;
3014 if (!text.IsEmpty())
3015 text += wxT(".");
3016 text += wxString::Format(wxT("%d"), nextNumber);
3017 attr.SetBulletText(text);
3018 }
3019 }
3020
3021 return true;
3022 }
3023 else
3024 return false;
3025 }
3026 else
3027 return false;
3028}
3029
5d7836c4
JS
3030/*!
3031 * wxRichTextParagraph
3032 * This object represents a single paragraph (or in a straight text editor, a line).
3033 */
3034
3035IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph, wxRichTextBox)
3036
cfa3b256
JS
3037wxArrayInt wxRichTextParagraph::sm_defaultTabs;
3038
5d7836c4
JS
3039wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject* parent, wxTextAttrEx* style):
3040 wxRichTextBox(parent)
3041{
5d7836c4
JS
3042 if (style)
3043 SetAttributes(*style);
3044}
3045
4f32b3cf 3046wxRichTextParagraph::wxRichTextParagraph(const wxString& text, wxRichTextObject* parent, wxTextAttrEx* paraStyle, wxTextAttrEx* charStyle):
5d7836c4
JS
3047 wxRichTextBox(parent)
3048{
4f32b3cf
JS
3049 if (paraStyle)
3050 SetAttributes(*paraStyle);
5d7836c4 3051
4f32b3cf 3052 AppendChild(new wxRichTextPlainText(text, this, charStyle));
5d7836c4
JS
3053}
3054
3055wxRichTextParagraph::~wxRichTextParagraph()
3056{
3057 ClearLines();
3058}
3059
3060/// Draw the item
46e7a90e 3061bool wxRichTextParagraph::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& WXUNUSED(rect), int WXUNUSED(descent), int style)
5d7836c4 3062{
fe5aa22c
JS
3063#if wxRICHTEXT_USE_DYNAMIC_STYLES
3064 wxTextAttrEx attr = GetCombinedAttributes();
3065#else
3066 const wxTextAttrEx& attr = GetAttributes();
3067#endif
3068
5d7836c4 3069 // Draw the bullet, if any
fe5aa22c 3070 if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
5d7836c4 3071 {
fe5aa22c 3072 if (attr.GetLeftSubIndent() != 0)
5d7836c4 3073 {
fe5aa22c 3074 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
fe5aa22c 3075 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
5d7836c4 3076
d2d0adc7
JS
3077 wxTextAttrEx bulletAttr(GetCombinedAttributes());
3078
3079 // Get line height from first line, if any
3080 wxRichTextLine* line = m_cachedLines.GetFirst() ? (wxRichTextLine* ) m_cachedLines.GetFirst()->GetData() : (wxRichTextLine*) NULL;
3081
3082 wxPoint linePos;
3083 int lineHeight wxDUMMY_INITIALIZE(0);
3084 if (line)
5d7836c4 3085 {
d2d0adc7
JS
3086 lineHeight = line->GetSize().y;
3087 linePos = line->GetPosition() + GetPosition();
5d7836c4 3088 }
d2d0adc7 3089 else
f089713f 3090 {
f089713f
JS
3091 wxFont font;
3092 if (bulletAttr.GetFont().Ok())
3093 font = bulletAttr.GetFont();
3094 else
3095 font = (*wxNORMAL_FONT);
3096
3097 dc.SetFont(font);
3098
d2d0adc7
JS
3099 lineHeight = dc.GetCharHeight();
3100 linePos = GetPosition();
3101 linePos.y += spaceBeforePara;
3102 }
f089713f 3103
d2d0adc7 3104 wxRect bulletRect(GetPosition().x + leftIndent, linePos.y, linePos.x - (GetPosition().x + leftIndent), lineHeight);
f089713f 3105
d2d0adc7
JS
3106 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP)
3107 {
3108 if (wxRichTextBuffer::GetRenderer())
3109 wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc, bulletAttr, bulletRect);
3110 }
3111 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD)
3112 {
3113 if (wxRichTextBuffer::GetRenderer())
3114 wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc, bulletAttr, bulletRect);
f089713f 3115 }
5d7836c4
JS
3116 else
3117 {
3118 wxString bulletText = GetBulletText();
d2d0adc7
JS
3119
3120 if (!bulletText.empty() && wxRichTextBuffer::GetRenderer())
3121 wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc, bulletAttr, bulletRect, bulletText);
5d7836c4
JS
3122 }
3123 }
3124 }
7fe8059f 3125
5d7836c4
JS
3126 // Draw the range for each line, one object at a time.
3127
3128 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
3129 while (node)
3130 {
3131 wxRichTextLine* line = node->GetData();
1e967276 3132 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4
JS
3133
3134 int maxDescent = line->GetDescent();
3135
3136 // Lines are specified relative to the paragraph
3137
3138 wxPoint linePosition = line->GetPosition() + GetPosition();
3139 wxPoint objectPosition = linePosition;
3140
3141 // Loop through objects until we get to the one within range
3142 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
3143 while (node2)
3144 {
3145 wxRichTextObject* child = node2->GetData();
46e7a90e
JS
3146
3147 if (!child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
5d7836c4
JS
3148 {
3149 // Draw this part of the line at the correct position
3150 wxRichTextRange objectRange(child->GetRange());
1e967276 3151 objectRange.LimitTo(lineRange);
5d7836c4
JS
3152
3153 wxSize objectSize;
3154 int descent = 0;
7f0d9d71 3155 child->GetRangeSize(objectRange, objectSize, descent, dc, wxRICHTEXT_UNFORMATTED, objectPosition);
5d7836c4
JS
3156
3157 // Use the child object's width, but the whole line's height
3158 wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
3159 child->Draw(dc, objectRange, selectionRange, childRect, maxDescent, style);
3160
3161 objectPosition.x += objectSize.x;
3162 }
1e967276 3163 else if (child->GetRange().GetStart() > lineRange.GetEnd())
5d7836c4
JS
3164 // Can break out of inner loop now since we've passed this line's range
3165 break;
3166
3167 node2 = node2->GetNext();
3168 }
3169
3170 node = node->GetNext();
7fe8059f 3171 }
5d7836c4
JS
3172
3173 return true;
3174}
3175
3176/// Lay the item out
38113684 3177bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, int style)
5d7836c4 3178{
fe5aa22c
JS
3179#if wxRICHTEXT_USE_DYNAMIC_STYLES
3180 wxTextAttrEx attr = GetCombinedAttributes();
3181#else
3182 const wxTextAttrEx& attr = GetAttributes();
3183#endif
3184
169adfa9
JS
3185 // ClearLines();
3186
5d7836c4 3187 // Increase the size of the paragraph due to spacing
fe5aa22c
JS
3188 int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
3189 int spaceAfterPara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingAfter());
3190 int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
3191 int leftSubIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftSubIndent());
3192 int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
5d7836c4
JS
3193
3194 int lineSpacing = 0;
3195
3196 // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
fe5aa22c 3197 if (attr.GetLineSpacing() > 10 && attr.GetFont().Ok())
5d7836c4 3198 {
fe5aa22c
JS
3199 dc.SetFont(attr.GetFont());
3200 lineSpacing = (ConvertTenthsMMToPixels(dc, dc.GetCharHeight()) * attr.GetLineSpacing())/10;
5d7836c4
JS
3201 }
3202
3203 // Available space for text on each line differs.
3204 int availableTextSpaceFirstLine = rect.GetWidth() - leftIndent - rightIndent;
3205
3206 // Bullets start the text at the same position as subsequent lines
fe5aa22c 3207 if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
5d7836c4
JS
3208 availableTextSpaceFirstLine -= leftSubIndent;
3209
3210 int availableTextSpaceSubsequentLines = rect.GetWidth() - leftIndent - rightIndent - leftSubIndent;
3211
3212 // Start position for each line relative to the paragraph
3213 int startPositionFirstLine = leftIndent;
3214 int startPositionSubsequentLines = leftIndent + leftSubIndent;
3215
3216 // If we have a bullet in this paragraph, the start position for the first line's text
3217 // is actually leftIndent + leftSubIndent.
fe5aa22c 3218 if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
5d7836c4
JS
3219 startPositionFirstLine = startPositionSubsequentLines;
3220
5d7836c4
JS
3221 long lastEndPos = GetRange().GetStart()-1;
3222 long lastCompletedEndPos = lastEndPos;
3223
3224 int currentWidth = 0;
3225 SetPosition(rect.GetPosition());
3226
3227 wxPoint currentPosition(0, spaceBeforePara); // We will calculate lines relative to paragraph
3228 int lineHeight = 0;
3229 int maxWidth = 0;
3230 int maxDescent = 0;
3231
3232 int lineCount = 0;
3233
3234 // Split up lines
3235
3236 // We may need to go back to a previous child, in which case create the new line,
3237 // find the child corresponding to the start position of the string, and
3238 // continue.
3239
3240 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3241 while (node)
3242 {
3243 wxRichTextObject* child = node->GetData();
3244
3245 // If this is e.g. a composite text box, it will need to be laid out itself.
3246 // But if just a text fragment or image, for example, this will
3247 // do nothing. NB: won't we need to set the position after layout?
3248 // since for example if position is dependent on vertical line size, we
3249 // can't tell the position until the size is determined. So possibly introduce
3250 // another layout phase.
3251
38113684 3252 child->Layout(dc, rect, style);
5d7836c4
JS
3253
3254 // Available width depends on whether we're on the first or subsequent lines
3255 int availableSpaceForText = (lineCount == 0 ? availableTextSpaceFirstLine : availableTextSpaceSubsequentLines);
3256
3257 currentPosition.x = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
3258
3259 // We may only be looking at part of a child, if we searched back for wrapping
3260 // and found a suitable point some way into the child. So get the size for the fragment
3261 // if necessary.
3262
3263 wxSize childSize;
3264 int childDescent = 0;
3265 if (lastEndPos == child->GetRange().GetStart() - 1)
3266 {
3267 childSize = child->GetCachedSize();
3268 childDescent = child->GetDescent();
3269 }
3270 else
7f0d9d71 3271 GetRangeSize(wxRichTextRange(lastEndPos+1, child->GetRange().GetEnd()), childSize, childDescent, dc, wxRICHTEXT_UNFORMATTED,rect.GetPosition());
5d7836c4
JS
3272
3273 if (childSize.x + currentWidth > availableSpaceForText)
3274 {
3275 long wrapPosition = 0;
3276
3277 // Find a place to wrap. This may walk back to previous children,
3278 // for example if a word spans several objects.
3279 if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, availableSpaceForText, wrapPosition))
3280 {
3281 // If the function failed, just cut it off at the end of this child.
3282 wrapPosition = child->GetRange().GetEnd();
3283 }
3284
3285 // FindWrapPosition can still return a value that will put us in an endless wrapping loop
3286 if (wrapPosition <= lastCompletedEndPos)
3287 wrapPosition = wxMax(lastCompletedEndPos+1,child->GetRange().GetEnd());
3288
3289 // wxLogDebug(wxT("Split at %ld"), wrapPosition);
7fe8059f 3290
5d7836c4
JS
3291 // Let's find the actual size of the current line now
3292 wxSize actualSize;
3293 wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
3294 GetRangeSize(actualRange, actualSize, childDescent, dc, wxRICHTEXT_UNFORMATTED);
3295 currentWidth = actualSize.x;
3296 lineHeight = wxMax(lineHeight, actualSize.y);
3297 maxDescent = wxMax(childDescent, maxDescent);
7fe8059f 3298
5d7836c4 3299 // Add a new line
1e967276 3300 wxRichTextLine* line = AllocateLine(lineCount);
39a1c2f2 3301
1e967276
JS
3302 // Set relative range so we won't have to change line ranges when paragraphs are moved
3303 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
5d7836c4
JS
3304 line->SetPosition(currentPosition);
3305 line->SetSize(wxSize(currentWidth, lineHeight));
3306 line->SetDescent(maxDescent);
3307
5d7836c4
JS
3308 // Now move down a line. TODO: add margins, spacing
3309 currentPosition.y += lineHeight;
3310 currentPosition.y += lineSpacing;
3311 currentWidth = 0;
3312 maxDescent = 0;
7fe8059f
WS
3313 maxWidth = wxMax(maxWidth, currentWidth);
3314
5d7836c4
JS
3315 lineCount ++;
3316
3317 // TODO: account for zero-length objects, such as fields
3318 wxASSERT(wrapPosition > lastCompletedEndPos);
7fe8059f 3319
5d7836c4
JS
3320 lastEndPos = wrapPosition;
3321 lastCompletedEndPos = lastEndPos;
3322
3323 lineHeight = 0;
3324
3325 // May need to set the node back to a previous one, due to searching back in wrapping
3326 wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition(wrapPosition+1);
3327 if (childAfterWrapPosition)
3328 node = m_children.Find(childAfterWrapPosition);
3329 else
3330 node = node->GetNext();
3331 }
3332 else
3333 {
3334 // We still fit, so don't add a line, and keep going
3335 currentWidth += childSize.x;
3336 lineHeight = wxMax(lineHeight, childSize.y);
3337 maxDescent = wxMax(childDescent, maxDescent);
3338
3339 maxWidth = wxMax(maxWidth, currentWidth);
3340 lastEndPos = child->GetRange().GetEnd();
3341
3342 node = node->GetNext();
3343 }
3344 }
3345
3346 // Add the last line - it's the current pos -> last para pos
3347 // Substract -1 because the last position is always the end-paragraph position.
3348 if (lastCompletedEndPos <= GetRange().GetEnd()-1)
3349 {
3350 currentPosition.x = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
3351
1e967276
JS
3352 wxRichTextLine* line = AllocateLine(lineCount);
3353
3354 wxRichTextRange actualRange(lastCompletedEndPos+1, GetRange().GetEnd()-1);
3355
3356 // Set relative range so we won't have to change line ranges when paragraphs are moved
3357 line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
7fe8059f 3358
5d7836c4
JS
3359 line->SetPosition(currentPosition);
3360
3361 if (lineHeight == 0)
3362 {
fe5aa22c
JS
3363 if (attr.GetFont().Ok())
3364 dc.SetFont(attr.GetFont());
5d7836c4
JS
3365 lineHeight = dc.GetCharHeight();
3366 }
3367 if (maxDescent == 0)
3368 {
3369 int w, h;
3370 dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
3371 }
3372
3373 line->SetSize(wxSize(currentWidth, lineHeight));
3374 line->SetDescent(maxDescent);
3375 currentPosition.y += lineHeight;
3376 currentPosition.y += lineSpacing;
3377 lineCount ++;
5d7836c4
JS
3378 }
3379
1e967276
JS
3380 // Remove remaining unused line objects, if any
3381 ClearUnusedLines(lineCount);
3382
5d7836c4 3383 // Apply styles to wrapped lines
fe5aa22c 3384 ApplyParagraphStyle(attr, rect);
5d7836c4
JS
3385
3386 SetCachedSize(wxSize(maxWidth, currentPosition.y + spaceBeforePara + spaceAfterPara));
3387
3388 m_dirty = false;
3389
3390 return true;
3391}
3392
3393/// Apply paragraph styles, such as centering, to wrapped lines
fe5aa22c 3394void wxRichTextParagraph::ApplyParagraphStyle(const wxTextAttrEx& attr, const wxRect& rect)
5d7836c4 3395{
fe5aa22c 3396 if (!attr.HasAlignment())
5d7836c4
JS
3397 return;
3398
3399 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
3400 while (node)
3401 {
3402 wxRichTextLine* line = node->GetData();
3403
3404 wxPoint pos = line->GetPosition();
3405 wxSize size = line->GetSize();
3406
3407 // centering, right-justification
fe5aa22c 3408 if (attr.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
5d7836c4
JS
3409 {
3410 pos.x = (rect.GetWidth() - size.x)/2 + pos.x;
3411 line->SetPosition(pos);
3412 }
fe5aa22c 3413 else if (attr.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
5d7836c4 3414 {
44219ff0 3415 pos.x = pos.x + rect.GetWidth() - size.x;
5d7836c4
JS
3416 line->SetPosition(pos);
3417 }
3418
3419 node = node->GetNext();
3420 }
3421}
3422
3423/// Insert text at the given position
3424bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
3425{
3426 wxRichTextObject* childToUse = NULL;
09f14108 3427 wxRichTextObjectList::compatibility_iterator nodeToUse = wxRichTextObjectList::compatibility_iterator();
5d7836c4
JS
3428
3429 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3430 while (node)
3431 {
3432 wxRichTextObject* child = node->GetData();
3433 if (child->GetRange().Contains(pos) && child->GetRange().GetLength() > 0)
3434 {
3435 childToUse = child;
3436 nodeToUse = node;
3437 break;
3438 }
3439
3440 node = node->GetNext();
3441 }
3442
3443 if (childToUse)
3444 {
3445 wxRichTextPlainText* textObject = wxDynamicCast(childToUse, wxRichTextPlainText);
3446 if (textObject)
3447 {
3448 int posInString = pos - textObject->GetRange().GetStart();
3449
3450 wxString newText = textObject->GetText().Mid(0, posInString) +
3451 text + textObject->GetText().Mid(posInString);
3452 textObject->SetText(newText);
3453
28f92d74 3454 int textLength = text.length();
5d7836c4
JS
3455
3456 textObject->SetRange(wxRichTextRange(textObject->GetRange().GetStart(),
3457 textObject->GetRange().GetEnd() + textLength));
3458
3459 // Increment the end range of subsequent fragments in this paragraph.
3460 // We'll set the paragraph range itself at a higher level.
3461
3462 wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
3463 while (node)
3464 {
3465 wxRichTextObject* child = node->GetData();
3466 child->SetRange(wxRichTextRange(textObject->GetRange().GetStart() + textLength,
3467 textObject->GetRange().GetEnd() + textLength));
7fe8059f 3468
5d7836c4
JS
3469 node = node->GetNext();
3470 }
3471
3472 return true;
3473 }
3474 else
3475 {
3476 // TODO: if not a text object, insert at closest position, e.g. in front of it
3477 }
3478 }
3479 else
3480 {
3481 // Add at end.
3482 // Don't pass parent initially to suppress auto-setting of parent range.
3483 // We'll do that at a higher level.
3484 wxRichTextPlainText* textObject = new wxRichTextPlainText(text, this);
3485
3486 AppendChild(textObject);
3487 return true;
3488 }
3489
3490 return false;
3491}
3492
3493void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
3494{
3495 wxRichTextBox::Copy(obj);
3496}
3497
3498/// Clear the cached lines
3499void wxRichTextParagraph::ClearLines()
3500{
3501 WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
3502}
3503
3504/// Get/set the object size for the given range. Returns false if the range
3505/// is invalid for this object.
7f0d9d71 3506bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags, wxPoint position) const
5d7836c4
JS
3507{
3508 if (!range.IsWithin(GetRange()))
3509 return false;
3510
3511 if (flags & wxRICHTEXT_UNFORMATTED)
3512 {
3513 // Just use unformatted data, assume no line breaks
3514 // TODO: take into account line breaks
3515
3516 wxSize sz;
3517
3518 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3519 while (node)
3520 {
3521 wxRichTextObject* child = node->GetData();
3522 if (!child->GetRange().IsOutside(range))
3523 {
3524 wxSize childSize;
7fe8059f 3525
5d7836c4
JS
3526 wxRichTextRange rangeToUse = range;
3527 rangeToUse.LimitTo(child->GetRange());
3528 int childDescent = 0;
7fe8059f 3529
7f0d9d71 3530 if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, flags, position))
5d7836c4
JS
3531 {
3532 sz.y = wxMax(sz.y, childSize.y);
3533 sz.x += childSize.x;
3534 descent = wxMax(descent, childDescent);
3535 }
3536 }
3537
3538 node = node->GetNext();
3539 }
3540 size = sz;
3541 }
3542 else
3543 {
3544 // Use formatted data, with line breaks
3545 wxSize sz;
3546
3547 // We're going to loop through each line, and then for each line,
3548 // call GetRangeSize for the fragment that comprises that line.
3549 // Only we have to do that multiple times within the line, because
3550 // the line may be broken into pieces. For now ignore line break commands
3551 // (so we can assume that getting the unformatted size for a fragment
3552 // within a line is the actual size)
3553
3554 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
3555 while (node)
3556 {
3557 wxRichTextLine* line = node->GetData();
1e967276
JS
3558 wxRichTextRange lineRange = line->GetAbsoluteRange();
3559 if (!lineRange.IsOutside(range))
5d7836c4
JS
3560 {
3561 wxSize lineSize;
7fe8059f 3562
5d7836c4
JS
3563 wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
3564 while (node2)
3565 {
3566 wxRichTextObject* child = node2->GetData();
7fe8059f 3567
1e967276 3568 if (!child->GetRange().IsOutside(lineRange))
5d7836c4 3569 {
1e967276 3570 wxRichTextRange rangeToUse = lineRange;
5d7836c4 3571 rangeToUse.LimitTo(child->GetRange());
7fe8059f 3572
5d7836c4
JS
3573 wxSize childSize;
3574 int childDescent = 0;
7f0d9d71 3575 if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, flags, position))
5d7836c4
JS
3576 {
3577 lineSize.y = wxMax(lineSize.y, childSize.y);
3578 lineSize.x += childSize.x;
3579 }
3580 descent = wxMax(descent, childDescent);
3581 }
7fe8059f 3582
5d7836c4
JS
3583 node2 = node2->GetNext();
3584 }
3585
3586 // Increase size by a line (TODO: paragraph spacing)
3587 sz.y += lineSize.y;
3588 sz.x = wxMax(sz.x, lineSize.x);
3589 }
3590 node = node->GetNext();
3591 }
3592 size = sz;
3593 }
3594 return true;
3595}
3596
3597/// Finds the absolute position and row height for the given character position
3598bool wxRichTextParagraph::FindPosition(wxDC& dc, long index, wxPoint& pt, int* height, bool forceLineStart)
3599{
3600 if (index == -1)
3601 {
3602 wxRichTextLine* line = ((wxRichTextParagraphLayoutBox*)GetParent())->GetLineAtPosition(0);
3603 if (line)
3604 *height = line->GetSize().y;
3605 else
3606 *height = dc.GetCharHeight();
3607
3608 // -1 means 'the start of the buffer'.
3609 pt = GetPosition();
3610 if (line)
3611 pt = pt + line->GetPosition();
3612
5d7836c4
JS
3613 return true;
3614 }
3615
3616 // The final position in a paragraph is taken to mean the position
3617 // at the start of the next paragraph.
3618 if (index == GetRange().GetEnd())
3619 {
3620 wxRichTextParagraphLayoutBox* parent = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
3621 wxASSERT( parent != NULL );
3622
3623 // Find the height at the next paragraph, if any
3624 wxRichTextLine* line = parent->GetLineAtPosition(index + 1);
3625 if (line)
3626 {
3627 *height = line->GetSize().y;
3628 pt = line->GetAbsolutePosition();
3629 }
3630 else
3631 {
3632 *height = dc.GetCharHeight();
3633 int indent = ConvertTenthsMMToPixels(dc, m_attributes.GetLeftIndent());
3634 pt = wxPoint(indent, GetCachedSize().y);
3635 }
3636
3637 return true;
3638 }
3639
3640 if (index < GetRange().GetStart() || index > GetRange().GetEnd())
3641 return false;
3642
3643 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
3644 while (node)
3645 {
3646 wxRichTextLine* line = node->GetData();
1e967276
JS
3647 wxRichTextRange lineRange = line->GetAbsoluteRange();
3648 if (index >= lineRange.GetStart() && index <= lineRange.GetEnd())
5d7836c4
JS
3649 {
3650 // If this is the last point in the line, and we're forcing the
3651 // returned value to be the start of the next line, do the required
3652 // thing.
1e967276 3653 if (index == lineRange.GetEnd() && forceLineStart)
5d7836c4
JS
3654 {
3655 if (node->GetNext())
3656 {
3657 wxRichTextLine* nextLine = node->GetNext()->GetData();
3658 *height = nextLine->GetSize().y;
3659 pt = nextLine->GetAbsolutePosition();
3660 return true;
3661 }
3662 }
3663
3664 pt.y = line->GetPosition().y + GetPosition().y;
3665
1e967276 3666 wxRichTextRange r(lineRange.GetStart(), index);
5d7836c4
JS
3667 wxSize rangeSize;
3668 int descent = 0;
3669
3670 // We find the size of the line up to this point,
3671 // then we can add this size to the line start position and
3672 // paragraph start position to find the actual position.
3673
7f0d9d71 3674 if (GetRangeSize(r, rangeSize, descent, dc, wxRICHTEXT_UNFORMATTED, line->GetPosition()+ GetPosition()))
5d7836c4
JS
3675 {
3676 pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
3677 *height = line->GetSize().y;
3678
3679 return true;
3680 }
3681
3682 }
3683
3684 node = node->GetNext();
3685 }
3686
3687 return false;
3688}
3689
3690/// Hit-testing: returns a flag indicating hit test details, plus
3691/// information about position
3692int wxRichTextParagraph::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition)
3693{
3694 wxPoint paraPos = GetPosition();
3695
3696 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
3697 while (node)
3698 {
3699 wxRichTextLine* line = node->GetData();
3700 wxPoint linePos = paraPos + line->GetPosition();
3701 wxSize lineSize = line->GetSize();
1e967276 3702 wxRichTextRange lineRange = line->GetAbsoluteRange();
5d7836c4
JS
3703
3704 if (pt.y >= linePos.y && pt.y <= linePos.y + lineSize.y)
3705 {
3706 if (pt.x < linePos.x)
3707 {
1e967276 3708 textPosition = lineRange.GetStart();
5d7836c4
JS
3709 return wxRICHTEXT_HITTEST_BEFORE;
3710 }
3711 else if (pt.x >= (linePos.x + lineSize.x))
3712 {
1e967276 3713 textPosition = lineRange.GetEnd();
5d7836c4
JS
3714 return wxRICHTEXT_HITTEST_AFTER;
3715 }
3716 else
3717 {
3718 long i;
3719 int lastX = linePos.x;
1e967276 3720 for (i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++)
5d7836c4
JS
3721 {
3722 wxSize childSize;
3723 int descent = 0;
7fe8059f 3724
1e967276 3725 wxRichTextRange rangeToUse(lineRange.GetStart(), i);
7fe8059f 3726
7f0d9d71 3727 GetRangeSize(rangeToUse, childSize, descent, dc, wxRICHTEXT_UNFORMATTED, linePos);
5d7836c4
JS
3728
3729 int nextX = childSize.x + linePos.x;
3730
3731 if (pt.x >= lastX && pt.x <= nextX)
3732 {
3733 textPosition = i;
3734
3735 // So now we know it's between i-1 and i.
3736 // Let's see if we can be more precise about
3737 // which side of the position it's on.
3738
3739 int midPoint = (nextX - lastX)/2 + lastX;
3740 if (pt.x >= midPoint)
3741 return wxRICHTEXT_HITTEST_AFTER;
3742 else
3743 return wxRICHTEXT_HITTEST_BEFORE;
3744 }
3745 else
3746 {
3747 lastX = nextX;
3748 }
3749 }
3750 }
3751 }
7fe8059f 3752
5d7836c4
JS
3753 node = node->GetNext();
3754 }
3755
3756 return wxRICHTEXT_HITTEST_NONE;
3757}
3758
3759/// Split an object at this position if necessary, and return
3760/// the previous object, or NULL if inserting at beginning.
3761wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** previousObject)
3762{
3763 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3764 while (node)
3765 {
3766 wxRichTextObject* child = node->GetData();
3767
3768 if (pos == child->GetRange().GetStart())
3769 {
3770 if (previousObject)
4d551ad5
JS
3771 {
3772 if (node->GetPrevious())
3773 *previousObject = node->GetPrevious()->GetData();
3774 else
3775 *previousObject = NULL;
3776 }
5d7836c4
JS
3777
3778 return child;
3779 }
3780
3781 if (child->GetRange().Contains(pos))
3782 {
3783 // This should create a new object, transferring part of
3784 // the content to the old object and the rest to the new object.
3785 wxRichTextObject* newObject = child->DoSplit(pos);
3786
3787 // If we couldn't split this object, just insert in front of it.
3788 if (!newObject)
3789 {
3790 // Maybe this is an empty string, try the next one
3791 // return child;
3792 }
3793 else
3794 {
3795 // Insert the new object after 'child'
3796 if (node->GetNext())
3797 m_children.Insert(node->GetNext(), newObject);
3798 else
3799 m_children.Append(newObject);
3800 newObject->SetParent(this);
3801
3802 if (previousObject)
3803 *previousObject = child;
3804
3805 return newObject;
3806 }
3807 }
3808
3809 node = node->GetNext();
3810 }
3811 if (previousObject)
3812 *previousObject = NULL;
3813 return NULL;
3814}
3815
3816/// Move content to a list from obj on
3817void wxRichTextParagraph::MoveToList(wxRichTextObject* obj, wxList& list)
3818{
3819 wxRichTextObjectList::compatibility_iterator node = m_children.Find(obj);
3820 while (node)
3821 {
3822 wxRichTextObject* child = node->GetData();
3823 list.Append(child);
3824
3825 wxRichTextObjectList::compatibility_iterator oldNode = node;
3826
3827 node = node->GetNext();
3828
3829 m_children.DeleteNode(oldNode);
3830 }
3831}
3832
3833/// Add content back from list
3834void wxRichTextParagraph::MoveFromList(wxList& list)
3835{
09f14108 3836 for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
3837 {
3838 AppendChild((wxRichTextObject*) node->GetData());
3839 }
3840}
3841
3842/// Calculate range
3843void wxRichTextParagraph::CalculateRange(long start, long& end)
3844{
3845 wxRichTextCompositeObject::CalculateRange(start, end);
3846
3847 // Add one for end of paragraph
3848 end ++;
3849
3850 m_range.SetRange(start, end);
3851}
3852
3853/// Find the object at the given position
3854wxRichTextObject* wxRichTextParagraph::FindObjectAtPosition(long position)
3855{
3856 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3857 while (node)
3858 {
3859 wxRichTextObject* obj = node->GetData();
3860 if (obj->GetRange().Contains(position))
3861 return obj;
7fe8059f 3862
5d7836c4
JS
3863 node = node->GetNext();
3864 }
3865 return NULL;
3866}
3867
3868/// Get the plain text searching from the start or end of the range.
3869/// The resulting string may be shorter than the range given.
3870bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTextRange& range, bool fromStart)
3871{
3872 text = wxEmptyString;
3873
3874 if (fromStart)
3875 {
3876 wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
3877 while (node)
3878 {
3879 wxRichTextObject* obj = node->GetData();
3880 if (!obj->GetRange().IsOutside(range))
3881 {
3882 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
3883 if (textObj)
3884 {
3885 text += textObj->GetTextForRange(range);
3886 }
3887 else
3888 return true;
3889 }
3890
3891 node = node->GetNext();
3892 }
3893 }
3894 else
3895 {
3896 wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
3897 while (node)
3898 {
3899 wxRichTextObject* obj = node->GetData();
3900 if (!obj->GetRange().IsOutside(range))
3901 {
3902 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
3903 if (textObj)
3904 {
3905 text = textObj->GetTextForRange(range) + text;
3906 }
3907 else
3908 return true;
3909 }
3910
3911 node = node->GetPrevious();
3912 }
3913 }
3914
3915 return true;
3916}
3917
3918/// Find a suitable wrap position.
3919bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, int availableSpace, long& wrapPosition)
3920{
3921 // Find the first position where the line exceeds the available space.
3922 wxSize sz;
3923 long i;
3924 long breakPosition = range.GetEnd();
3925 for (i = range.GetStart(); i <= range.GetEnd(); i++)
3926 {
3927 int descent = 0;
3928 GetRangeSize(wxRichTextRange(range.GetStart(), i), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
3929
3930 if (sz.x > availableSpace)
3931 {
3932 breakPosition = i-1;
3933 break;
3934 }
3935 }
3936
3937 // Now we know the last position on the line.
3938 // Let's try to find a word break.
3939
3940 wxString plainText;
3941 if (GetContiguousPlainText(plainText, wxRichTextRange(range.GetStart(), breakPosition), false))
3942 {
3943 int spacePos = plainText.Find(wxT(' '), true);
3944 if (spacePos != wxNOT_FOUND)
3945 {
28f92d74 3946 int positionsFromEndOfString = plainText.length() - spacePos - 1;
5d7836c4
JS
3947 breakPosition = breakPosition - positionsFromEndOfString;
3948 }
3949 }
3950
3951 wrapPosition = breakPosition;
3952
3953 return true;
3954}
3955
3956/// Get the bullet text for this paragraph.
3957wxString wxRichTextParagraph::GetBulletText()
3958{
3959 if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ||
3960 (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP))
3961 return wxEmptyString;
3962
3963 int number = GetAttributes().GetBulletNumber();
3964
3965 wxString text;
d2d0adc7 3966 if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE))
5d7836c4
JS
3967 {
3968 text.Printf(wxT("%d"), number);
3969 }
3970 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER)
3971 {
3972 // TODO: Unicode, and also check if number > 26
3973 text.Printf(wxT("%c"), (wxChar) (number+64));
3974 }
3975 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER)
3976 {
3977 // TODO: Unicode, and also check if number > 26
3978 text.Printf(wxT("%c"), (wxChar) (number+96));
3979 }
3980 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
3981 {
59509217 3982 text = wxRichTextDecimalToRoman(number);
5d7836c4
JS
3983 }
3984 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
3985 {
59509217
JS
3986 text = wxRichTextDecimalToRoman(number);
3987 text.MakeLower();
5d7836c4
JS
3988 }
3989 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
3990 {
d2d0adc7
JS
3991 text = GetAttributes().GetBulletText();
3992 }
3993
3994 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
3995 {
3996 // The outline style relies on the text being computed statically,
3997 // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
3998 // should be stored in the attributes; if not, just use the number for this
3999 // level, as previously computed.
4000 if (!GetAttributes().GetBulletText().IsEmpty())
4001 text = GetAttributes().GetBulletText();
5d7836c4
JS
4002 }
4003
4004 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES)
4005 {
4006 text = wxT("(") + text + wxT(")");
4007 }
d2d0adc7
JS
4008 else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS)
4009 {
4010 text = text + wxT(")");
4011 }
4012
5d7836c4
JS
4013 if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD)
4014 {
4015 text += wxT(".");
4016 }
4017
4018 return text;
4019}
4020
1e967276
JS
4021/// Allocate or reuse a line object
4022wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
4023{
4024 if (pos < (int) m_cachedLines.GetCount())
4025 {
4026 wxRichTextLine* line = m_cachedLines.Item(pos)->GetData();
4027 line->Init(this);
4028 return line;
4029 }
4030 else
4031 {
4032 wxRichTextLine* line = new wxRichTextLine(this);
4033 m_cachedLines.Append(line);
4034 return line;
4035 }
4036}
4037
4038/// Clear remaining unused line objects, if any
4039bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
4040{
4041 int cachedLineCount = m_cachedLines.GetCount();
4042 if ((int) cachedLineCount > lineCount)
4043 {
4044 for (int i = 0; i < (int) (cachedLineCount - lineCount); i ++)
4045 {
4046 wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetLast();
4047 wxRichTextLine* line = node->GetData();
4048 m_cachedLines.Erase(node);
4049 delete line;
4050 }
4051 }
4052 return true;
4053}
4054
fe5aa22c
JS
4055/// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
4056/// retrieve the actual style.
59509217 4057wxTextAttrEx wxRichTextParagraph::GetCombinedAttributes(const wxTextAttrEx& contentStyle) const
fe5aa22c
JS
4058{
4059 wxTextAttrEx attr;
4060 wxRichTextBuffer* buf = wxDynamicCast(GetParent(), wxRichTextBuffer);
4061 if (buf)
4062 {
4063 attr = buf->GetBasicStyle();
4064 wxRichTextApplyStyle(attr, GetAttributes());
4065 }
4066 else
4067 attr = GetAttributes();
4068
4069 wxRichTextApplyStyle(attr, contentStyle);
4070 return attr;
4071}
4072
4073/// Get combined attributes of the base style and paragraph style.
4074wxTextAttrEx wxRichTextParagraph::GetCombinedAttributes() const
4075{
4076 wxTextAttrEx attr;
4077 wxRichTextBuffer* buf = wxDynamicCast(GetParent(), wxRichTextBuffer);
4078 if (buf)
4079 {
4080 attr = buf->GetBasicStyle();
4081 wxRichTextApplyStyle(attr, GetAttributes());
4082 }
4083 else
4084 attr = GetAttributes();
4085
4086 return attr;
4087}
5d7836c4 4088
cfa3b256
JS
4089/// Create default tabstop array
4090void wxRichTextParagraph::InitDefaultTabs()
4091{
4092 // create a default tab list at 10 mm each.
4093 for (int i = 0; i < 20; ++i)
4094 {
4095 sm_defaultTabs.Add(i*100);
4096 }
4097}
4098
4099/// Clear default tabstop array
4100void wxRichTextParagraph::ClearDefaultTabs()
4101{
4102 sm_defaultTabs.Clear();
4103}
4104
4105
5d7836c4
JS
4106/*!
4107 * wxRichTextLine
4108 * This object represents a line in a paragraph, and stores
4109 * offsets from the start of the paragraph representing the
4110 * start and end positions of the line.
4111 */
4112
4113wxRichTextLine::wxRichTextLine(wxRichTextParagraph* parent)
4114{
1e967276 4115 Init(parent);
5d7836c4
JS
4116}
4117
4118/// Initialisation
1e967276 4119void wxRichTextLine::Init(wxRichTextParagraph* parent)
5d7836c4 4120{
1e967276
JS
4121 m_parent = parent;
4122 m_range.SetRange(-1, -1);
4123 m_pos = wxPoint(0, 0);
4124 m_size = wxSize(0, 0);
5d7836c4
JS
4125 m_descent = 0;
4126}
4127
4128/// Copy
4129void wxRichTextLine::Copy(const wxRichTextLine& obj)
4130{
4131 m_range = obj.m_range;
4132}
4133
4134/// Get the absolute object position
4135wxPoint wxRichTextLine::GetAbsolutePosition() const
4136{
4137 return m_parent->GetPosition() + m_pos;
4138}
4139
1e967276
JS
4140/// Get the absolute range
4141wxRichTextRange wxRichTextLine::GetAbsoluteRange() const
4142{
4143 wxRichTextRange range(m_range.GetStart() + m_parent->GetRange().GetStart(), 0);
4144 range.SetEnd(range.GetStart() + m_range.GetLength()-1);
4145 return range;
4146}
4147
5d7836c4
JS
4148/*!
4149 * wxRichTextPlainText
4150 * This object represents a single piece of text.
4151 */
4152
4153IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText, wxRichTextObject)
4154
4155wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* parent, wxTextAttrEx* style):
4156 wxRichTextObject(parent)
4157{
5d7836c4
JS
4158 if (style)
4159 SetAttributes(*style);
4160
4161 m_text = text;
4162}
4163
cfa3b256
JS
4164#define USE_KERNING_FIX 1
4165
5d7836c4
JS
4166/// Draw the item
4167bool wxRichTextPlainText::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int descent, int WXUNUSED(style))
4168{
fe5aa22c
JS
4169#if wxRICHTEXT_USE_DYNAMIC_STYLES
4170 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
4171 wxASSERT (para != NULL);
4172
4173 wxTextAttrEx textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes());
4174#else
4175 wxTextAttrEx textAttr(GetAttributes());
4176#endif
4177
5d7836c4
JS
4178 int offset = GetRange().GetStart();
4179
4180 long len = range.GetLength();
4181 wxString stringChunk = m_text.Mid(range.GetStart() - offset, (size_t) len);
42688aea
JS
4182 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
4183 stringChunk.MakeUpper();
5d7836c4
JS
4184
4185 int charHeight = dc.GetCharHeight();
4186
4187 int x = rect.x;
4188 int y = rect.y + (rect.height - charHeight - (descent - m_descent));
4189
4190 // Test for the optimized situations where all is selected, or none
4191 // is selected.
4192
fe5aa22c
JS
4193 if (textAttr.GetFont().Ok())
4194 dc.SetFont(textAttr.GetFont());
5d7836c4
JS
4195
4196 // (a) All selected.
4197 if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd())
ab14c7aa 4198 {
fe5aa22c 4199 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, true);
5d7836c4
JS
4200 }
4201 // (b) None selected.
4202 else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd())
4203 {
4204 // Draw all unselected
fe5aa22c 4205 DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, false);
5d7836c4
JS
4206 }
4207 else
4208 {
4209 // (c) Part selected, part not
4210 // Let's draw unselected chunk, selected chunk, then unselected chunk.
4211
4212 dc.SetBackgroundMode(wxTRANSPARENT);
7fe8059f 4213
5d7836c4
JS
4214 // 1. Initial unselected chunk, if any, up until start of selection.
4215 if (selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd())
4216 {
4217 int r1 = range.GetStart();
4218 int s1 = selectionRange.GetStart()-1;
4219 int fragmentLen = s1 - r1 + 1;
4220 if (fragmentLen < 0)
4221 wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 - offset), (int)fragmentLen);
4222 wxString stringFragment = m_text.Mid(r1 - offset, fragmentLen);
5d7836c4 4223
fe5aa22c 4224 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
cfa3b256
JS
4225
4226#if USE_KERNING_FIX
4227 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
4228 {
4229 // Compensate for kerning difference
4230 wxString stringFragment2(m_text.Mid(r1 - offset, fragmentLen+1));
4231 wxString stringFragment3(m_text.Mid(r1 - offset + fragmentLen, 1));
41a85215 4232
cfa3b256
JS
4233 wxCoord w1, h1, w2, h2, w3, h3;
4234 dc.GetTextExtent(stringFragment, & w1, & h1);
4235 dc.GetTextExtent(stringFragment2, & w2, & h2);
4236 dc.GetTextExtent(stringFragment3, & w3, & h3);
41a85215 4237
cfa3b256
JS
4238 int kerningDiff = (w1 + w3) - w2;
4239 x = x - kerningDiff;
4240 }
4241#endif
5d7836c4
JS
4242 }
4243
4244 // 2. Selected chunk, if any.
4245 if (selectionRange.GetEnd() >= range.GetStart())
4246 {
4247 int s1 = wxMax(selectionRange.GetStart(), range.GetStart());
4248 int s2 = wxMin(selectionRange.GetEnd(), range.GetEnd());
4249
4250 int fragmentLen = s2 - s1 + 1;
4251 if (fragmentLen < 0)
4252 wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 - offset), (int)fragmentLen);
4253 wxString stringFragment = m_text.Mid(s1 - offset, fragmentLen);
5d7836c4 4254
fe5aa22c 4255 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, true);
cfa3b256
JS
4256
4257#if USE_KERNING_FIX
4258 if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
4259 {
4260 // Compensate for kerning difference
4261 wxString stringFragment2(m_text.Mid(s1 - offset, fragmentLen+1));
4262 wxString stringFragment3(m_text.Mid(s1 - offset + fragmentLen, 1));
41a85215 4263
cfa3b256
JS
4264 wxCoord w1, h1, w2, h2, w3, h3;
4265 dc.GetTextExtent(stringFragment, & w1, & h1);
4266 dc.GetTextExtent(stringFragment2, & w2, & h2);
4267 dc.GetTextExtent(stringFragment3, & w3, & h3);
41a85215 4268
cfa3b256
JS
4269 int kerningDiff = (w1 + w3) - w2;
4270 x = x - kerningDiff;
4271 }
4272#endif
5d7836c4
JS
4273 }
4274
4275 // 3. Remaining unselected chunk, if any
4276 if (selectionRange.GetEnd() < range.GetEnd())
4277 {
4278 int s2 = wxMin(selectionRange.GetEnd()+1, range.GetEnd());
4279 int r2 = range.GetEnd();
4280
4281 int fragmentLen = r2 - s2 + 1;
4282 if (fragmentLen < 0)
4283 wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 - offset), (int)fragmentLen);
4284 wxString stringFragment = m_text.Mid(s2 - offset, fragmentLen);
ab14c7aa 4285
fe5aa22c 4286 DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
7fe8059f 4287 }
5d7836c4
JS
4288 }
4289
4290 return true;
4291}
61399247 4292
fe5aa22c 4293bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxTextAttrEx& attr, const wxRect& rect,wxString& str, wxCoord& x, wxCoord& y, bool selected)
7f0d9d71 4294{
cfa3b256
JS
4295 bool hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
4296
4297 wxArrayInt tabArray;
4298 int tabCount;
4299 if (hasTabs)
ab14c7aa 4300 {
cfa3b256
JS
4301 if (attr.GetTabs().IsEmpty())
4302 tabArray = wxRichTextParagraph::GetDefaultTabs();
4303 else
4304 tabArray = attr.GetTabs();
4305 tabCount = tabArray.GetCount();
4306
4307 for (int i = 0; i < tabCount; ++i)
ab14c7aa 4308 {
cfa3b256
JS
4309 int pos = tabArray[i];
4310 pos = ConvertTenthsMMToPixels(dc, pos);
4311 tabArray[i] = pos;
7f0d9d71
JS
4312 }
4313 }
cfa3b256
JS
4314 else
4315 tabCount = 0;
ab14c7aa 4316
cfa3b256
JS
4317 int nextTabPos = -1;
4318 int tabPos = -1;
7f0d9d71 4319 wxCoord w, h;
ab14c7aa 4320
cfa3b256 4321 if (selected)
ab14c7aa 4322 {
7f0d9d71
JS
4323 dc.SetBrush(*wxBLACK_BRUSH);
4324 dc.SetPen(*wxBLACK_PEN);
4325 dc.SetTextForeground(*wxWHITE);
4326 dc.SetBackgroundMode(wxTRANSPARENT);
4327 }
ab14c7aa
JS
4328 else
4329 {
fe5aa22c 4330 dc.SetTextForeground(attr.GetTextColour());
7f0d9d71
JS
4331 dc.SetBackgroundMode(wxTRANSPARENT);
4332 }
ab14c7aa 4333
cfa3b256 4334 while (hasTabs)
ab14c7aa
JS
4335 {
4336 // the string has a tab
7f0d9d71
JS
4337 // break up the string at the Tab
4338 wxString stringChunk = str.BeforeFirst(wxT('\t'));
4339 str = str.AfterFirst(wxT('\t'));
4340 dc.GetTextExtent(stringChunk, & w, & h);
cfa3b256 4341 tabPos = x + w;
7f0d9d71 4342 bool not_found = true;
cfa3b256 4343 for (int i = 0; i < tabCount && not_found; ++i)
ab14c7aa 4344 {
cfa3b256
JS
4345 nextTabPos = tabArray.Item(i);
4346 if (nextTabPos > tabPos)
ab14c7aa 4347 {
7f0d9d71 4348 not_found = false;
ab14c7aa
JS
4349 if (selected)
4350 {
cfa3b256 4351 w = nextTabPos - x;
7f0d9d71 4352 wxRect selRect(x, rect.y, w, rect.GetHeight());
61399247 4353 dc.DrawRectangle(selRect);
7f0d9d71
JS
4354 }
4355 dc.DrawText(stringChunk, x, y);
42688aea
JS
4356
4357 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
4358 {
4359 wxPen oldPen = dc.GetPen();
4360 dc.SetPen(wxPen(attr.GetTextColour(), 1));
4361 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
4362 dc.SetPen(oldPen);
4363 }
4364
cfa3b256 4365 x = nextTabPos;
7f0d9d71
JS
4366 }
4367 }
cfa3b256 4368 hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
7f0d9d71 4369 }
61399247 4370
cfa3b256 4371 if (!str.IsEmpty())
ab14c7aa 4372 {
cfa3b256
JS
4373 dc.GetTextExtent(str, & w, & h);
4374 if (selected)
4375 {
4376 wxRect selRect(x, rect.y, w, rect.GetHeight());
4377 dc.DrawRectangle(selRect);
4378 }
4379 dc.DrawText(str, x, y);
42688aea
JS
4380
4381 if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
4382 {
4383 wxPen oldPen = dc.GetPen();
4384 dc.SetPen(wxPen(attr.GetTextColour(), 1));
4385 dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
4386 dc.SetPen(oldPen);
4387 }
4388
cfa3b256 4389 x += w;
7f0d9d71 4390 }
7f0d9d71 4391 return true;
5d7836c4 4392
7f0d9d71 4393}
fe5aa22c 4394
5d7836c4 4395/// Lay the item out
38113684 4396bool wxRichTextPlainText::Layout(wxDC& dc, const wxRect& WXUNUSED(rect), int WXUNUSED(style))
5d7836c4 4397{
fe5aa22c
JS
4398#if wxRICHTEXT_USE_DYNAMIC_STYLES
4399 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
4400 wxASSERT (para != NULL);
4401
4402 wxTextAttrEx textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes());
4403#else
4404 wxTextAttrEx textAttr(GetAttributes());
4405#endif
4406
4407 if (textAttr.GetFont().Ok())
4408 dc.SetFont(textAttr.GetFont());
5d7836c4 4409
42688aea
JS
4410 wxString str = m_text;
4411 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
4412 str.MakeUpper();
4413
5d7836c4 4414 wxCoord w, h;
42688aea 4415 dc.GetTextExtent(str, & w, & h, & m_descent);
5d7836c4
JS
4416 m_size = wxSize(w, dc.GetCharHeight());
4417
4418 return true;
4419}
4420
4421/// Copy
4422void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj)
4423{
4424 wxRichTextObject::Copy(obj);
4425
4426 m_text = obj.m_text;
4427}
4428
4429/// Get/set the object size for the given range. Returns false if the range
4430/// is invalid for this object.
7f0d9d71 4431bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int WXUNUSED(flags), wxPoint position) const
5d7836c4
JS
4432{
4433 if (!range.IsWithin(GetRange()))
4434 return false;
4435
fe5aa22c
JS
4436#if wxRICHTEXT_USE_DYNAMIC_STYLES
4437 wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
4438 wxASSERT (para != NULL);
4439
4440 wxTextAttrEx textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes());
4441#else
4442 wxTextAttrEx textAttr(GetAttributes());
4443#endif
4444
5d7836c4
JS
4445 // Always assume unformatted text, since at this level we have no knowledge
4446 // of line breaks - and we don't need it, since we'll calculate size within
4447 // formatted text by doing it in chunks according to the line ranges
4448
fe5aa22c
JS
4449 if (textAttr.GetFont().Ok())
4450 dc.SetFont(textAttr.GetFont());
5d7836c4
JS
4451
4452 int startPos = range.GetStart() - GetRange().GetStart();
4453 long len = range.GetLength();
4454 wxString stringChunk = m_text.Mid(startPos, (size_t) len);
42688aea
JS
4455
4456 if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
4457 stringChunk.MakeUpper();
4458
5d7836c4 4459 wxCoord w, h;
7f0d9d71 4460 int width = 0;
cfa3b256 4461 if (stringChunk.Find(wxT('\t')) != wxNOT_FOUND)
ab14c7aa
JS
4462 {
4463 // the string has a tab
cfa3b256
JS
4464 wxArrayInt tabArray;
4465 if (textAttr.GetTabs().IsEmpty())
4466 tabArray = wxRichTextParagraph::GetDefaultTabs();
4467 else
4468 tabArray = textAttr.GetTabs();
ab14c7aa 4469
cfa3b256 4470 int tabCount = tabArray.GetCount();
41a85215 4471
cfa3b256 4472 for (int i = 0; i < tabCount; ++i)
61399247 4473 {
cfa3b256
JS
4474 int pos = tabArray[i];
4475 pos = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, pos);
4476 tabArray[i] = pos;
7f0d9d71 4477 }
41a85215 4478
cfa3b256 4479 int nextTabPos = -1;
61399247 4480
ab14c7aa
JS
4481 while (stringChunk.Find(wxT('\t')) >= 0)
4482 {
4483 // the string has a tab
7f0d9d71
JS
4484 // break up the string at the Tab
4485 wxString stringFragment = stringChunk.BeforeFirst(wxT('\t'));
4486 stringChunk = stringChunk.AfterFirst(wxT('\t'));
4487 dc.GetTextExtent(stringFragment, & w, & h);
4488 width += w;
cfa3b256
JS
4489 int absoluteWidth = width + position.x;
4490 bool notFound = true;
4491 for (int i = 0; i < tabCount && notFound; ++i)
ab14c7aa 4492 {
cfa3b256
JS
4493 nextTabPos = tabArray.Item(i);
4494 if (nextTabPos > absoluteWidth)
ab14c7aa 4495 {
cfa3b256
JS
4496 notFound = false;
4497 width = nextTabPos - position.x;
7f0d9d71
JS
4498 }
4499 }
4500 }
4501 }
5d7836c4 4502 dc.GetTextExtent(stringChunk, & w, & h, & descent);
7f0d9d71
JS
4503 width += w;
4504 size = wxSize(width, dc.GetCharHeight());
61399247 4505
5d7836c4
JS
4506 return true;
4507}
4508
4509/// Do a split, returning an object containing the second part, and setting
4510/// the first part in 'this'.
4511wxRichTextObject* wxRichTextPlainText::DoSplit(long pos)
4512{
4513 int index = pos - GetRange().GetStart();
28f92d74 4514 if (index < 0 || index >= (int) m_text.length())
5d7836c4
JS
4515 return NULL;
4516
4517 wxString firstPart = m_text.Mid(0, index);
4518 wxString secondPart = m_text.Mid(index);
4519
4520 m_text = firstPart;
4521
4522 wxRichTextPlainText* newObject = new wxRichTextPlainText(secondPart);
4523 newObject->SetAttributes(GetAttributes());
4524
4525 newObject->SetRange(wxRichTextRange(pos, GetRange().GetEnd()));
4526 GetRange().SetEnd(pos-1);
4527
4528 return newObject;
4529}
4530
4531/// Calculate range
4532void wxRichTextPlainText::CalculateRange(long start, long& end)
4533{
28f92d74 4534 end = start + m_text.length() - 1;
5d7836c4
JS
4535 m_range.SetRange(start, end);
4536}
4537
4538/// Delete range
4539bool wxRichTextPlainText::DeleteRange(const wxRichTextRange& range)
4540{
4541 wxRichTextRange r = range;
4542
4543 r.LimitTo(GetRange());
4544
4545 if (r.GetStart() == GetRange().GetStart() && r.GetEnd() == GetRange().GetEnd())
4546 {
4547 m_text.Empty();
4548 return true;
4549 }
4550
4551 long startIndex = r.GetStart() - GetRange().GetStart();
4552 long len = r.GetLength();
4553
4554 m_text = m_text.Mid(0, startIndex) + m_text.Mid(startIndex+len);
4555 return true;
4556}
4557
4558/// Get text for the given range.
4559wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) const
4560{
4561 wxRichTextRange r = range;
4562
4563 r.LimitTo(GetRange());
4564
4565 long startIndex = r.GetStart() - GetRange().GetStart();
4566 long len = r.GetLength();
4567
4568 return m_text.Mid(startIndex, len);
4569}
4570
4571/// Returns true if this object can merge itself with the given one.
4572bool wxRichTextPlainText::CanMerge(wxRichTextObject* object) const
4573{
4574 return object->GetClassInfo() == CLASSINFO(wxRichTextPlainText) &&
7fe8059f 4575 (m_text.empty() || wxTextAttrEq(GetAttributes(), object->GetAttributes()));
5d7836c4
JS
4576}
4577
4578/// Returns true if this object merged itself with the given one.
4579/// The calling code will then delete the given object.
4580bool wxRichTextPlainText::Merge(wxRichTextObject* object)
4581{
4582 wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText);
4583 wxASSERT( textObject != NULL );
4584
4585 if (textObject)
4586 {
4587 m_text += textObject->GetText();
4588 return true;
4589 }
4590 else
4591 return false;
4592}
4593
4594/// Dump to output stream for debugging
4595void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
4596{
4597 wxRichTextObject::Dump(stream);
4598 stream << m_text << wxT("\n");
4599}
4600
4601/*!
4602 * wxRichTextBuffer
4603 * This is a kind of box, used to represent the whole buffer
4604 */
4605
4606IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox)
4607
d2d0adc7
JS
4608wxList wxRichTextBuffer::sm_handlers;
4609wxRichTextRenderer* wxRichTextBuffer::sm_renderer = NULL;
4610int wxRichTextBuffer::sm_bulletRightMargin = 20;
4611float wxRichTextBuffer::sm_bulletProportion = (float) 0.3;
5d7836c4
JS
4612
4613/// Initialisation
4614void wxRichTextBuffer::Init()
4615{
4616 m_commandProcessor = new wxCommandProcessor;
4617 m_styleSheet = NULL;
4618 m_modified = false;
4619 m_batchedCommandDepth = 0;
4620 m_batchedCommand = NULL;
4621 m_suppressUndo = 0;
d2d0adc7 4622 m_handlerFlags = 0;
44219ff0 4623 m_scale = 1.0;
5d7836c4
JS
4624}
4625
4626/// Initialisation
4627wxRichTextBuffer::~wxRichTextBuffer()
4628{
4629 delete m_commandProcessor;
4630 delete m_batchedCommand;
4631
4632 ClearStyleStack();
d2d0adc7 4633 ClearEventHandlers();
5d7836c4
JS
4634}
4635
85d8909b 4636void wxRichTextBuffer::ResetAndClearCommands()
5d7836c4 4637{
85d8909b
JS
4638 Reset();
4639
5d7836c4 4640 GetCommandProcessor()->ClearCommands();
5d7836c4 4641
5d7836c4 4642 Modify(false);
1e967276 4643 Invalidate(wxRICHTEXT_ALL);
5d7836c4
JS
4644}
4645
0ca07313
JS
4646void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj)
4647{
4648 wxRichTextParagraphLayoutBox::Copy(obj);
4649
4650 m_styleSheet = obj.m_styleSheet;
4651 m_modified = obj.m_modified;
4652 m_batchedCommandDepth = obj.m_batchedCommandDepth;
4653 m_batchedCommand = obj.m_batchedCommand;
4654 m_suppressUndo = obj.m_suppressUndo;
4655}
4656
38f833b1
JS
4657/// Push style sheet to top of stack
4658bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet* styleSheet)
4659{
4660 if (m_styleSheet)
4661 styleSheet->InsertSheet(m_styleSheet);
4662
4663 SetStyleSheet(styleSheet);
41a85215 4664
38f833b1
JS
4665 return true;
4666}
4667
4668/// Pop style sheet from top of stack
4669wxRichTextStyleSheet* wxRichTextBuffer::PopStyleSheet()
4670{
4671 if (m_styleSheet)
4672 {
4673 wxRichTextStyleSheet* oldSheet = m_styleSheet;
4674 m_styleSheet = oldSheet->GetNextSheet();
4675 oldSheet->Unlink();
41a85215 4676
38f833b1
JS
4677 return oldSheet;
4678 }
4679 else
4680 return NULL;
4681}
4682
0ca07313
JS
4683/// Submit command to insert paragraphs
4684bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int flags)
4685{
4686 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, this, ctrl, false);
4687
4f32b3cf
JS
4688#if wxRICHTEXT_USE_DYNAMIC_STYLES
4689 wxTextAttrEx attr(GetDefaultStyle());
4690#else
4691 wxTextAttrEx attr(GetBasicStyle());
4692 wxRichTextApplyStyle(attr, GetDefaultStyle());
4693#endif
4694
0ca07313
JS
4695 wxTextAttrEx* p = NULL;
4696 wxTextAttrEx paraAttr;
4697 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
4698 {
4699 paraAttr = GetStyleForNewParagraph(pos);
4700 if (!paraAttr.IsDefault())
4701 p = & paraAttr;
4702 }
4f32b3cf
JS
4703 else
4704 p = & attr;
0ca07313
JS
4705
4706 action->GetNewParagraphs() = paragraphs;
59509217
JS
4707
4708 if (p)
4709 {
4710 wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
4711 while (node)
4712 {
4713 wxRichTextParagraph* obj = (wxRichTextParagraph*) node->GetData();
4714 obj->SetAttributes(*p);
4715 node = node->GetPrevious();
4716 }
4717 }
4718
0ca07313
JS
4719 action->SetPosition(pos);
4720
4721 // Set the range we'll need to delete in Undo
4722 action->SetRange(wxRichTextRange(pos, pos + paragraphs.GetRange().GetEnd() - 1));
4723
4724 SubmitAction(action);
4725
4726 return true;
4727}
4728
5d7836c4 4729/// Submit command to insert the given text
fe5aa22c 4730bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
5d7836c4
JS
4731{
4732 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, this, ctrl, false);
4733
fe5aa22c
JS
4734 wxTextAttrEx* p = NULL;
4735 wxTextAttrEx paraAttr;
4736 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
4737 {
4738 paraAttr = GetStyleForNewParagraph(pos);
4739 if (!paraAttr.IsDefault())
4740 p = & paraAttr;
4741 }
4742
fe5aa22c 4743 action->GetNewParagraphs().AddParagraphs(text, p);
0ca07313
JS
4744
4745 int length = action->GetNewParagraphs().GetRange().GetLength();
4746
4747 if (text.length() > 0 && text.Last() != wxT('\n'))
4748 {
4749 // Don't count the newline when undoing
4750 length --;
5d7836c4 4751 action->GetNewParagraphs().SetPartialParagraph(true);
0ca07313 4752 }
5d7836c4
JS
4753
4754 action->SetPosition(pos);
4755
4756 // Set the range we'll need to delete in Undo
0ca07313 4757 action->SetRange(wxRichTextRange(pos, pos + length - 1));
7fe8059f 4758
5d7836c4 4759 SubmitAction(action);
7fe8059f 4760
5d7836c4
JS
4761 return true;
4762}
4763
4764/// Submit command to insert the given text
fe5aa22c 4765bool wxRichTextBuffer::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl, int flags)
5d7836c4
JS
4766{
4767 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, this, ctrl, false);
4768
fe5aa22c
JS
4769 wxTextAttrEx* p = NULL;
4770 wxTextAttrEx paraAttr;
4771 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
4772 {
4773 paraAttr = GetStyleForNewParagraph(pos);
4774 if (!paraAttr.IsDefault())
4775 p = & paraAttr;
4776 }
4777
4778#if wxRICHTEXT_USE_DYNAMIC_STYLES
4779 wxTextAttrEx attr(GetDefaultStyle());
4780#else
5d7836c4
JS
4781 wxTextAttrEx attr(GetBasicStyle());
4782 wxRichTextApplyStyle(attr, GetDefaultStyle());
fe5aa22c 4783#endif
7fe8059f
WS
4784
4785 wxRichTextParagraph* newPara = new wxRichTextParagraph(wxEmptyString, this, & attr);
5d7836c4
JS
4786 action->GetNewParagraphs().AppendChild(newPara);
4787 action->GetNewParagraphs().UpdateRanges();
4788 action->GetNewParagraphs().SetPartialParagraph(false);
4789 action->SetPosition(pos);
4790
fe5aa22c
JS
4791 if (p)
4792 newPara->SetAttributes(*p);
4793
5d7836c4
JS
4794 // Set the range we'll need to delete in Undo
4795 action->SetRange(wxRichTextRange(pos, pos));
7fe8059f 4796
5d7836c4 4797 SubmitAction(action);
7fe8059f 4798
5d7836c4
JS
4799 return true;
4800}
4801
4802/// Submit command to insert the given image
fe5aa22c 4803bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock, wxRichTextCtrl* ctrl, int flags)
5d7836c4
JS
4804{
4805 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, ctrl, false);
4806
fe5aa22c
JS
4807 wxTextAttrEx* p = NULL;
4808 wxTextAttrEx paraAttr;
4809 if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
4810 {
4811 paraAttr = GetStyleForNewParagraph(pos);
4812 if (!paraAttr.IsDefault())
4813 p = & paraAttr;
4814 }
4815
4816#if wxRICHTEXT_USE_DYNAMIC_STYLES
4817 wxTextAttrEx attr(GetDefaultStyle());
4818#else
5d7836c4
JS
4819 wxTextAttrEx attr(GetBasicStyle());
4820 wxRichTextApplyStyle(attr, GetDefaultStyle());
fe5aa22c 4821#endif
7fe8059f 4822
5d7836c4 4823 wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
fe5aa22c
JS
4824 if (p)
4825 newPara->SetAttributes(*p);
4826
5d7836c4
JS
4827 wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
4828 newPara->AppendChild(imageObject);
4829 action->GetNewParagraphs().AppendChild(newPara);
4830 action->GetNewParagraphs().UpdateRanges();
4831
4832 action->GetNewParagraphs().SetPartialParagraph(true);
4833
4834 action->SetPosition(pos);
4835
4836 // Set the range we'll need to delete in Undo
4837 action->SetRange(wxRichTextRange(pos, pos));
7fe8059f 4838
5d7836c4 4839 SubmitAction(action);
7fe8059f 4840
5d7836c4
JS
4841 return true;
4842}
4843
fe5aa22c
JS
4844/// Get the style that is appropriate for a new paragraph at this position.
4845/// If the previous paragraph has a paragraph style name, look up the next-paragraph
4846/// style.
4847wxRichTextAttr wxRichTextBuffer::GetStyleForNewParagraph(long pos, bool caretPosition) const
4848{
4849 wxRichTextParagraph* para = GetParagraphAtPosition(pos, caretPosition);
4850 if (para)
4851 {
d2d0adc7
JS
4852 wxRichTextAttr attr;
4853 bool foundAttributes = false;
4854
4855 // Look for a matching paragraph style
fe5aa22c
JS
4856 if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && GetStyleSheet())
4857 {
4858 wxRichTextParagraphStyleDefinition* paraDef = GetStyleSheet()->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
d2d0adc7 4859 if (paraDef)
fe5aa22c 4860 {
d2d0adc7
JS
4861 if (!paraDef->GetNextStyle().IsEmpty())
4862 {
4863 wxRichTextParagraphStyleDefinition* nextParaDef = GetStyleSheet()->FindParagraphStyle(paraDef->GetNextStyle());
4864 if (nextParaDef)
4865 {
4866 foundAttributes = true;
4867 attr = nextParaDef->GetStyle();
4868 }
4869 }
4870
4871 // If we didn't find the 'next style', use this style instead.
4872 if (!foundAttributes)
4873 {
4874 foundAttributes = true;
4875 attr = paraDef->GetStyle();
4876 }
fe5aa22c
JS
4877 }
4878 }
d2d0adc7
JS
4879 if (!foundAttributes)
4880 {
4881 attr = para->GetAttributes();
4882 int flags = attr.GetFlags();
fe5aa22c 4883
d2d0adc7
JS
4884 // Eliminate character styles
4885 flags &= ( (~ wxTEXT_ATTR_FONT) |
fe5aa22c
JS
4886 (~ wxTEXT_ATTR_TEXT_COLOUR) |
4887 (~ wxTEXT_ATTR_BACKGROUND_COLOUR) );
d2d0adc7
JS
4888 attr.SetFlags(flags);
4889 }
4890
4891 // Now see if we need to number the paragraph.
4892 if (attr.HasBulletStyle())
4893 {
4894 wxRichTextAttr numberingAttr;
4895 if (FindNextParagraphNumber(para, numberingAttr))
1f71a770 4896 wxRichTextApplyStyle(attr, (const wxRichTextAttr&) numberingAttr);
d2d0adc7 4897 }
fe5aa22c
JS
4898
4899 return attr;
4900 }
4901 else
4902 return wxRichTextAttr();
4903}
4904
5d7836c4
JS
4905/// Submit command to delete this range
4906bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange& range, long initialCaretPosition, long WXUNUSED(newCaretPositon), wxRichTextCtrl* ctrl)
4907{
4908 wxRichTextAction* action = new wxRichTextAction(NULL, _("Delete"), wxRICHTEXT_DELETE, this, ctrl);
7fe8059f 4909
5d7836c4
JS
4910 action->SetPosition(initialCaretPosition);
4911
4912 // Set the range to delete
4913 action->SetRange(range);
7fe8059f 4914
5d7836c4
JS
4915 // Copy the fragment that we'll need to restore in Undo
4916 CopyFragment(range, action->GetOldParagraphs());
4917
4918 // Special case: if there is only one (non-partial) paragraph,
4919 // we must save the *next* paragraph's style, because that
4920 // is the style we must apply when inserting the content back
4921 // when undoing the delete. (This is because we're merging the
4922 // paragraph with the previous paragraph and throwing away
4923 // the style, and we need to restore it.)
4924 if (!action->GetOldParagraphs().GetPartialParagraph() && action->GetOldParagraphs().GetChildCount() == 1)
4925 {
4926 wxRichTextParagraph* lastPara = GetParagraphAtPosition(range.GetStart());
4927 if (lastPara)
4928 {
4929 wxRichTextParagraph* nextPara = GetParagraphAtPosition(range.GetEnd()+1);
4930 if (nextPara)
4931 {
4932 wxRichTextParagraph* para = (wxRichTextParagraph*) action->GetOldParagraphs().GetChild(0);
4933 para->SetAttributes(nextPara->GetAttributes());
4934 }
4935 }
4936 }
4937
4938 SubmitAction(action);
7fe8059f 4939
5d7836c4
JS
4940 return true;
4941}
4942
4943/// Collapse undo/redo commands
4944bool wxRichTextBuffer::BeginBatchUndo(const wxString& cmdName)
4945{
4946 if (m_batchedCommandDepth == 0)
4947 {
4948 wxASSERT(m_batchedCommand == NULL);
4949 if (m_batchedCommand)
4950 {
4951 GetCommandProcessor()->Submit(m_batchedCommand);
4952 }
4953 m_batchedCommand = new wxRichTextCommand(cmdName);
4954 }
4955
7fe8059f 4956 m_batchedCommandDepth ++;
5d7836c4
JS
4957
4958 return true;
4959}
4960
4961/// Collapse undo/redo commands
4962bool wxRichTextBuffer::EndBatchUndo()
4963{
4964 m_batchedCommandDepth --;
4965
4966 wxASSERT(m_batchedCommandDepth >= 0);
4967 wxASSERT(m_batchedCommand != NULL);
4968
4969 if (m_batchedCommandDepth == 0)
4970 {
4971 GetCommandProcessor()->Submit(m_batchedCommand);
4972 m_batchedCommand = NULL;
4973 }
4974
4975 return true;
4976}
4977
4978/// Submit immediately, or delay according to whether collapsing is on
4979bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action)
4980{
4981 if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
4982 m_batchedCommand->AddAction(action);
4983 else
4984 {
4985 wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
4986 cmd->AddAction(action);
4987
4988 // Only store it if we're not suppressing undo.
4989 return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
4990 }
4991
4992 return true;
4993}
4994
4995/// Begin suppressing undo/redo commands.
4996bool wxRichTextBuffer::BeginSuppressUndo()
4997{
7fe8059f 4998 m_suppressUndo ++;
5d7836c4
JS
4999
5000 return true;
5001}
5002
5003/// End suppressing undo/redo commands.
5004bool wxRichTextBuffer::EndSuppressUndo()
5005{
7fe8059f 5006 m_suppressUndo --;
5d7836c4
JS
5007
5008 return true;
5009}
5010
5011/// Begin using a style
5012bool wxRichTextBuffer::BeginStyle(const wxTextAttrEx& style)
5013{
5014 wxTextAttrEx newStyle(GetDefaultStyle());
5015
5016 // Save the old default style
5017 m_attributeStack.Append((wxObject*) new wxTextAttrEx(GetDefaultStyle()));
5018
5019 wxRichTextApplyStyle(newStyle, style);
5020 newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags());
5021
5022 SetDefaultStyle(newStyle);
5023
5024 // wxLogDebug("Default style size = %d", GetDefaultStyle().GetFont().GetPointSize());
5025
5026 return true;
5027}
5028
5029/// End the style
5030bool wxRichTextBuffer::EndStyle()
5031{
63886f6d 5032 if (!m_attributeStack.GetFirst())
5d7836c4
JS
5033 {
5034 wxLogDebug(_("Too many EndStyle calls!"));
5035 return false;
5036 }
5037
09f14108 5038 wxList::compatibility_iterator node = m_attributeStack.GetLast();
5d7836c4 5039 wxTextAttrEx* attr = (wxTextAttrEx*)node->GetData();
9e31a660 5040 m_attributeStack.Erase(node);
5d7836c4
JS
5041
5042 SetDefaultStyle(*attr);
5043
5044 delete attr;
5045 return true;
5046}
5047
5048/// End all styles
5049bool wxRichTextBuffer::EndAllStyles()
5050{
5051 while (m_attributeStack.GetCount() != 0)
5052 EndStyle();
5053 return true;
5054}
5055
5056/// Clear the style stack
5057void wxRichTextBuffer::ClearStyleStack()
5058{
09f14108 5059 for (wxList::compatibility_iterator node = m_attributeStack.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
5060 delete (wxTextAttrEx*) node->GetData();
5061 m_attributeStack.Clear();
5062}
5063
5064/// Begin using bold
5065bool wxRichTextBuffer::BeginBold()
5066{
5067 wxFont font(GetBasicStyle().GetFont());
5068 font.SetWeight(wxBOLD);
5069
5070 wxTextAttrEx attr;
5071 attr.SetFont(font,wxTEXT_ATTR_FONT_WEIGHT);
7fe8059f 5072
5d7836c4
JS
5073 return BeginStyle(attr);
5074}
5075
5076/// Begin using italic
5077bool wxRichTextBuffer::BeginItalic()
5078{
5079 wxFont font(GetBasicStyle().GetFont());
5080 font.SetStyle(wxITALIC);
5081
5082 wxTextAttrEx attr;
5083 attr.SetFont(font, wxTEXT_ATTR_FONT_ITALIC);
7fe8059f 5084
5d7836c4
JS
5085 return BeginStyle(attr);
5086}
5087
5088/// Begin using underline
5089bool wxRichTextBuffer::BeginUnderline()
5090{
5091 wxFont font(GetBasicStyle().GetFont());
5092 font.SetUnderlined(true);
5093
5094 wxTextAttrEx attr;
5095 attr.SetFont(font, wxTEXT_ATTR_FONT_UNDERLINE);
7fe8059f 5096
5d7836c4
JS
5097 return BeginStyle(attr);
5098}
5099
5100/// Begin using point size
5101bool wxRichTextBuffer::BeginFontSize(int pointSize)
5102{
5103 wxFont font(GetBasicStyle().GetFont());
5104 font.SetPointSize(pointSize);
5105
5106 wxTextAttrEx attr;
5107 attr.SetFont(font, wxTEXT_ATTR_FONT_SIZE);
7fe8059f 5108
5d7836c4
JS
5109 return BeginStyle(attr);
5110}
5111
5112/// Begin using this font
5113bool wxRichTextBuffer::BeginFont(const wxFont& font)
5114{
5115 wxTextAttrEx attr;
5116 attr.SetFlags(wxTEXT_ATTR_FONT);
5117 attr.SetFont(font);
7fe8059f 5118
5d7836c4
JS
5119 return BeginStyle(attr);
5120}
5121
5122/// Begin using this colour
5123bool wxRichTextBuffer::BeginTextColour(const wxColour& colour)
5124{
5125 wxTextAttrEx attr;
5126 attr.SetFlags(wxTEXT_ATTR_TEXT_COLOUR);
5127 attr.SetTextColour(colour);
7fe8059f 5128
5d7836c4
JS
5129 return BeginStyle(attr);
5130}
5131
5132/// Begin using alignment
5133bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment)
5134{
5135 wxTextAttrEx attr;
5136 attr.SetFlags(wxTEXT_ATTR_ALIGNMENT);
5137 attr.SetAlignment(alignment);
7fe8059f 5138
5d7836c4
JS
5139 return BeginStyle(attr);
5140}
5141
5142/// Begin left indent
5143bool wxRichTextBuffer::BeginLeftIndent(int leftIndent, int leftSubIndent)
5144{
5145 wxTextAttrEx attr;
5146 attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
5147 attr.SetLeftIndent(leftIndent, leftSubIndent);
7fe8059f 5148
5d7836c4
JS
5149 return BeginStyle(attr);
5150}
5151
5152/// Begin right indent
5153bool wxRichTextBuffer::BeginRightIndent(int rightIndent)
5154{
5155 wxTextAttrEx attr;
5156 attr.SetFlags(wxTEXT_ATTR_RIGHT_INDENT);
5157 attr.SetRightIndent(rightIndent);
7fe8059f 5158
5d7836c4
JS
5159 return BeginStyle(attr);
5160}
5161
5162/// Begin paragraph spacing
5163bool wxRichTextBuffer::BeginParagraphSpacing(int before, int after)
5164{
5165 long flags = 0;
5166 if (before != 0)
5167 flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
5168 if (after != 0)
5169 flags |= wxTEXT_ATTR_PARA_SPACING_AFTER;
5170
5171 wxTextAttrEx attr;
5172 attr.SetFlags(flags);
5173 attr.SetParagraphSpacingBefore(before);
5174 attr.SetParagraphSpacingAfter(after);
7fe8059f 5175
5d7836c4
JS
5176 return BeginStyle(attr);
5177}
5178
5179/// Begin line spacing
5180bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing)
5181{
5182 wxTextAttrEx attr;
5183 attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
5184 attr.SetLineSpacing(lineSpacing);
7fe8059f 5185
5d7836c4
JS
5186 return BeginStyle(attr);
5187}
5188
5189/// Begin numbered bullet
5190bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber, int leftIndent, int leftSubIndent, int bulletStyle)
5191{
5192 wxTextAttrEx attr;
f089713f 5193 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5d7836c4
JS
5194 attr.SetBulletStyle(bulletStyle);
5195 attr.SetBulletNumber(bulletNumber);
5196 attr.SetLeftIndent(leftIndent, leftSubIndent);
7fe8059f 5197
5d7836c4
JS
5198 return BeginStyle(attr);
5199}
5200
5201/// Begin symbol bullet
d2d0adc7 5202bool wxRichTextBuffer::BeginSymbolBullet(const wxString& symbol, int leftIndent, int leftSubIndent, int bulletStyle)
5d7836c4
JS
5203{
5204 wxTextAttrEx attr;
f089713f 5205 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5d7836c4
JS
5206 attr.SetBulletStyle(bulletStyle);
5207 attr.SetLeftIndent(leftIndent, leftSubIndent);
d2d0adc7 5208 attr.SetBulletText(symbol);
7fe8059f 5209
5d7836c4
JS
5210 return BeginStyle(attr);
5211}
5212
f089713f
JS
5213/// Begin standard bullet
5214bool wxRichTextBuffer::BeginStandardBullet(const wxString& bulletName, int leftIndent, int leftSubIndent, int bulletStyle)
5215{
5216 wxTextAttrEx attr;
5217 attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
5218 attr.SetBulletStyle(bulletStyle);
5219 attr.SetLeftIndent(leftIndent, leftSubIndent);
5220 attr.SetBulletName(bulletName);
5221
5222 return BeginStyle(attr);
5223}
5224
5d7836c4
JS
5225/// Begin named character style
5226bool wxRichTextBuffer::BeginCharacterStyle(const wxString& characterStyle)
5227{
5228 if (GetStyleSheet())
5229 {
5230 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
5231 if (def)
5232 {
4d6d8bf4 5233 wxTextAttrEx attr = def->GetStyle();
5d7836c4
JS
5234 return BeginStyle(attr);
5235 }
5236 }
5237 return false;
5238}
5239
5240/// Begin named paragraph style
5241bool wxRichTextBuffer::BeginParagraphStyle(const wxString& paragraphStyle)
5242{
5243 if (GetStyleSheet())
5244 {
5245 wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(paragraphStyle);
5246 if (def)
5247 {
4d6d8bf4 5248 wxTextAttrEx attr = def->GetStyle();
5d7836c4
JS
5249 return BeginStyle(attr);
5250 }
5251 }
5252 return false;
5253}
5254
f089713f
JS
5255/// Begin named list style
5256bool wxRichTextBuffer::BeginListStyle(const wxString& listStyle, int level, int number)
5257{
5258 if (GetStyleSheet())
5259 {
5260 wxRichTextListStyleDefinition* def = GetStyleSheet()->FindListStyle(listStyle);
5261 if (def)
5262 {
5263 wxTextAttrEx attr(def->GetCombinedStyleForLevel(level));
5264
5265 attr.SetBulletNumber(number);
5266
5267 return BeginStyle(attr);
5268 }
5269 }
5270 return false;
5271}
5272
d2d0adc7
JS
5273/// Begin URL
5274bool wxRichTextBuffer::BeginURL(const wxString& url, const wxString& characterStyle)
5275{
5276 wxTextAttrEx attr;
5277
5278 if (!characterStyle.IsEmpty() && GetStyleSheet())
5279 {
5280 wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
5281 if (def)
5282 {
4d6d8bf4 5283 attr = def->GetStyle();
d2d0adc7
JS
5284 }
5285 }
5286 attr.SetURL(url);
5287
5288 return BeginStyle(attr);
5289}
5290
5d7836c4
JS
5291/// Adds a handler to the end
5292void wxRichTextBuffer::AddHandler(wxRichTextFileHandler *handler)
5293{
5294 sm_handlers.Append(handler);
5295}
5296
5297/// Inserts a handler at the front
5298void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler *handler)
5299{
5300 sm_handlers.Insert( handler );
5301}
5302
5303/// Removes a handler
5304bool wxRichTextBuffer::RemoveHandler(const wxString& name)
5305{
5306 wxRichTextFileHandler *handler = FindHandler(name);
5307 if (handler)
5308 {
5309 sm_handlers.DeleteObject(handler);
5310 delete handler;
5311 return true;
5312 }
5313 else
5314 return false;
5315}
5316
5317/// Finds a handler by filename or, if supplied, type
5318wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename, int imageType)
5319{
5320 if (imageType != wxRICHTEXT_TYPE_ANY)
5321 return FindHandler(imageType);
0ca07313 5322 else if (!filename.IsEmpty())
5d7836c4
JS
5323 {
5324 wxString path, file, ext;
5325 wxSplitPath(filename, & path, & file, & ext);
5326 return FindHandler(ext, imageType);
5327 }
0ca07313
JS
5328 else
5329 return NULL;
5d7836c4
JS
5330}
5331
5332
5333/// Finds a handler by name
5334wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& name)
5335{
5336 wxList::compatibility_iterator node = sm_handlers.GetFirst();
5337 while (node)
5338 {
5339 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
5340 if (handler->GetName().Lower() == name.Lower()) return handler;
5341
5342 node = node->GetNext();
5343 }
5344 return NULL;
5345}
5346
5347/// Finds a handler by extension and type
5348wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, int type)
5349{
5350 wxList::compatibility_iterator node = sm_handlers.GetFirst();
5351 while (node)
5352 {
5353 wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
5354 if ( handler->GetExtension().Lower() == extension.Lower() &&
5355 (type == wxRICHTEXT_TYPE_ANY || handler->GetType() == type) )
5356 return handler;
5357 node = node->GetNext();
5358 }
5359 return 0;
5360}
5361
5362/// Finds a handler by type
5363wxRichTextFileHandler* wxRichTextBuffer::FindHandler(int type)
5364{
5365 wxList::compatibility_iterator node = sm_handlers.GetFirst();
5366 while (node)
5367 {
5368 wxRichTextFileHandler *handler = (wxRichTextFileHandler *)node->GetData();
5369 if (handler->GetType() == type) return handler;
5370 node = node->GetNext();
5371 }
5372 return NULL;
5373}
5374
5375void wxRichTextBuffer::InitStandardHandlers()
5376{
5377 if (!FindHandler(wxRICHTEXT_TYPE_TEXT))
5378 AddHandler(new wxRichTextPlainTextHandler);
5379}
5380
5381void wxRichTextBuffer::CleanUpHandlers()
5382{
5383 wxList::compatibility_iterator node = sm_handlers.GetFirst();
5384 while (node)
5385 {
5386 wxRichTextFileHandler* handler = (wxRichTextFileHandler*)node->GetData();
5387 wxList::compatibility_iterator next = node->GetNext();
5388 delete handler;
5389 node = next;
5390 }
5391
5392 sm_handlers.Clear();
5393}
5394
1e967276 5395wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* types)
5d7836c4 5396{
1e967276
JS
5397 if (types)
5398 types->Clear();
5399
5d7836c4
JS
5400 wxString wildcard;
5401
5402 wxList::compatibility_iterator node = GetHandlers().GetFirst();
5403 int count = 0;
5404 while (node)
5405 {
5406 wxRichTextFileHandler* handler = (wxRichTextFileHandler*) node->GetData();
5407 if (handler->IsVisible() && ((save && handler->CanSave()) || !save && handler->CanLoad()))
5408 {
5409 if (combine)
5410 {
5411 if (count > 0)
5412 wildcard += wxT(";");
5413 wildcard += wxT("*.") + handler->GetExtension();
5414 }
5415 else
5416 {
5417 if (count > 0)
5418 wildcard += wxT("|");
5419 wildcard += handler->GetName();
5420 wildcard += wxT(" ");
5421 wildcard += _("files");
5422 wildcard += wxT(" (*.");
5423 wildcard += handler->GetExtension();
5424 wildcard += wxT(")|*.");
5425 wildcard += handler->GetExtension();
1e967276
JS
5426 if (types)
5427 types->Add(handler->GetType());
5d7836c4
JS
5428 }
5429 count ++;
5430 }
5431
5432 node = node->GetNext();
5433 }
5434
5435 if (combine)
5436 wildcard = wxT("(") + wildcard + wxT(")|") + wildcard;
5437 return wildcard;
5438}
5439
5440/// Load a file
5441bool wxRichTextBuffer::LoadFile(const wxString& filename, int type)
5442{
5443 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
5444 if (handler)
1e967276
JS
5445 {
5446 SetDefaultStyle(wxTextAttrEx());
d2d0adc7 5447 handler->SetFlags(GetHandlerFlags());
1e967276
JS
5448 bool success = handler->LoadFile(this, filename);
5449 Invalidate(wxRICHTEXT_ALL);
5450 return success;
5451 }
5d7836c4
JS
5452 else
5453 return false;
5454}
5455
5456/// Save a file
5457bool wxRichTextBuffer::SaveFile(const wxString& filename, int type)
5458{
5459 wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
5460 if (handler)
d2d0adc7
JS
5461 {
5462 handler->SetFlags(GetHandlerFlags());
5d7836c4 5463 return handler->SaveFile(this, filename);
d2d0adc7 5464 }
5d7836c4
JS
5465 else
5466 return false;
5467}
5468
5469/// Load from a stream
5470bool wxRichTextBuffer::LoadFile(wxInputStream& stream, int type)
5471{
5472 wxRichTextFileHandler* handler = FindHandler(type);
5473 if (handler)
1e967276
JS
5474 {
5475 SetDefaultStyle(wxTextAttrEx());
d2d0adc7 5476 handler->SetFlags(GetHandlerFlags());
1e967276
JS
5477 bool success = handler->LoadFile(this, stream);
5478 Invalidate(wxRICHTEXT_ALL);
5479 return success;
5480 }
5d7836c4
JS
5481 else
5482 return false;
5483}
5484
5485/// Save to a stream
5486bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, int type)
5487{
5488 wxRichTextFileHandler* handler = FindHandler(type);
5489 if (handler)
d2d0adc7
JS
5490 {
5491 handler->SetFlags(GetHandlerFlags());
5d7836c4 5492 return handler->SaveFile(this, stream);
d2d0adc7 5493 }
5d7836c4
JS
5494 else
5495 return false;
5496}
5497
5498/// Copy the range to the clipboard
5499bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
5500{
5501 bool success = false;
11ef729d 5502#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
0ca07313 5503
d2142335 5504 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
7fe8059f 5505 {
0ca07313
JS
5506 wxTheClipboard->Clear();
5507
5508 // Add composite object
5509
5510 wxDataObjectComposite* compositeObject = new wxDataObjectComposite();
5511
5512 {
5513 wxString text = GetTextForRange(range);
5514
5515#ifdef __WXMSW__
5516 text = wxTextFile::Translate(text, wxTextFileType_Dos);
5517#endif
5518
5519 compositeObject->Add(new wxTextDataObject(text), false /* not preferred */);
5520 }
5521
5522 // Add rich text buffer data object. This needs the XML handler to be present.
5523
5524 if (FindHandler(wxRICHTEXT_TYPE_XML))
5525 {
5526 wxRichTextBuffer* richTextBuf = new wxRichTextBuffer;
5527 CopyFragment(range, *richTextBuf);
5528
5529 compositeObject->Add(new wxRichTextBufferDataObject(richTextBuf), true /* preferred */);
5530 }
5531
5532 if (wxTheClipboard->SetData(compositeObject))
5533 success = true;
5534
5d7836c4
JS
5535 wxTheClipboard->Close();
5536 }
0ca07313 5537
39a1c2f2
WS
5538#else
5539 wxUnusedVar(range);
5540#endif
5d7836c4
JS
5541 return success;
5542}
5543
5544/// Paste the clipboard content to the buffer
5545bool wxRichTextBuffer::PasteFromClipboard(long position)
5546{
5547 bool success = false;
11ef729d 5548#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
5d7836c4
JS
5549 if (CanPasteFromClipboard())
5550 {
5551 if (wxTheClipboard->Open())
5552 {
0ca07313
JS
5553 if (wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
5554 {
5555 wxRichTextBufferDataObject data;
5556 wxTheClipboard->GetData(data);
5557 wxRichTextBuffer* richTextBuffer = data.GetRichTextBuffer();
5558 if (richTextBuffer)
5559 {
5560 InsertParagraphsWithUndo(position+1, *richTextBuffer, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
5561 delete richTextBuffer;
5562 }
5563 }
5564 else if (wxTheClipboard->IsSupported(wxDF_TEXT) || wxTheClipboard->IsSupported(wxDF_UNICODETEXT))
5d7836c4
JS
5565 {
5566 wxTextDataObject data;
5567 wxTheClipboard->GetData(data);
5568 wxString text(data.GetText());
d5363f57 5569 text.Replace(_T("\r\n"), _T("\n"));
5d7836c4
JS
5570
5571 InsertTextWithUndo(position+1, text, GetRichTextCtrl());
7fe8059f 5572
5d7836c4
JS
5573 success = true;
5574 }
5575 else if (wxTheClipboard->IsSupported(wxDF_BITMAP))
5576 {
5577 wxBitmapDataObject data;
5578 wxTheClipboard->GetData(data);
5579 wxBitmap bitmap(data.GetBitmap());
5580 wxImage image(bitmap.ConvertToImage());
5581
5582 wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, GetRichTextCtrl(), false);
7fe8059f 5583
5d7836c4
JS
5584 action->GetNewParagraphs().AddImage(image);
5585
5586 if (action->GetNewParagraphs().GetChildCount() == 1)
5587 action->GetNewParagraphs().SetPartialParagraph(true);
7fe8059f 5588
5d7836c4 5589 action->SetPosition(position);
7fe8059f 5590
5d7836c4
JS
5591 // Set the range we'll need to delete in Undo
5592 action->SetRange(wxRichTextRange(position, position));
7fe8059f 5593
5d7836c4
JS
5594 SubmitAction(action);
5595
5596 success = true;
5597 }
5598 wxTheClipboard->Close();
5599 }
5600 }
39a1c2f2
WS
5601#else
5602 wxUnusedVar(position);
5603#endif
5d7836c4
JS
5604 return success;
5605}
5606
5607/// Can we paste from the clipboard?
5608bool wxRichTextBuffer::CanPasteFromClipboard() const
5609{
7fe8059f 5610 bool canPaste = false;
11ef729d 5611#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
d2142335 5612 if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
5d7836c4 5613 {
0ca07313
JS
5614 if (wxTheClipboard->IsSupported(wxDF_TEXT) || wxTheClipboard->IsSupported(wxDF_UNICODETEXT) ||
5615 wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
5616 wxTheClipboard->IsSupported(wxDF_BITMAP))
5d7836c4 5617 {
7fe8059f 5618 canPaste = true;
5d7836c4
JS
5619 }
5620 wxTheClipboard->Close();
5621 }
39a1c2f2 5622#endif
5d7836c4
JS
5623 return canPaste;
5624}
5625
5626/// Dumps contents of buffer for debugging purposes
5627void wxRichTextBuffer::Dump()
5628{
5629 wxString text;
5630 {
5631 wxStringOutputStream stream(& text);
5632 wxTextOutputStream textStream(stream);
5633 Dump(textStream);
5634 }
5635
5636 wxLogDebug(text);
5637}
5638
d2d0adc7
JS
5639/// Add an event handler
5640bool wxRichTextBuffer::AddEventHandler(wxEvtHandler* handler)
5641{
5642 m_eventHandlers.Append(handler);
5643 return true;
5644}
5645
5646/// Remove an event handler
5647bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler* handler, bool deleteHandler)
5648{
5649 wxList::compatibility_iterator node = m_eventHandlers.Find(handler);
5650 if (node)
5651 {
5652 m_eventHandlers.Erase(node);
5653 if (deleteHandler)
5654 delete handler;
5655
5656 return true;
5657 }
5658 else
5659 return false;
5660}
5661
5662/// Clear event handlers
5663void wxRichTextBuffer::ClearEventHandlers()
5664{
5665 m_eventHandlers.Clear();
5666}
5667
5668/// Send event to event handlers. If sendToAll is true, will send to all event handlers,
5669/// otherwise will stop at the first successful one.
5670bool wxRichTextBuffer::SendEvent(wxEvent& event, bool sendToAll)
5671{
5672 bool success = false;
5673 for (wxList::compatibility_iterator node = m_eventHandlers.GetFirst(); node; node = node->GetNext())
5674 {
5675 wxEvtHandler* handler = (wxEvtHandler*) node->GetData();
5676 if (handler->ProcessEvent(event))
5677 {
5678 success = true;
5679 if (!sendToAll)
5680 return true;
5681 }
5682 }
5683 return success;
5684}
5685
5686/// Set style sheet and notify of the change
5687bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet* sheet)
5688{
5689 wxRichTextStyleSheet* oldSheet = GetStyleSheet();
5690
5691 wxWindowID id = wxID_ANY;
5692 if (GetRichTextCtrl())
5693 id = GetRichTextCtrl()->GetId();
5694
5695 wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING, id);
5696 event.SetEventObject(GetRichTextCtrl());
5697 event.SetOldStyleSheet(oldSheet);
5698 event.SetNewStyleSheet(sheet);
5699 event.Allow();
5700
5701 if (SendEvent(event) && !event.IsAllowed())
5702 {
5703 if (sheet != oldSheet)
5704 delete sheet;
5705
5706 return false;
5707 }
5708
5709 if (oldSheet && oldSheet != sheet)
5710 delete oldSheet;
5711
5712 SetStyleSheet(sheet);
5713
5714 event.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED);
5715 event.SetOldStyleSheet(NULL);
5716 event.Allow();
5717
5718 return SendEvent(event);
5719}
5720
5721/// Set renderer, deleting old one
5722void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer)
5723{
5724 if (sm_renderer)
5725 delete sm_renderer;
5726 sm_renderer = renderer;
5727}
5728
44219ff0 5729bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxTextAttrEx& bulletAttr, const wxRect& rect)
d2d0adc7
JS
5730{
5731 if (bulletAttr.GetTextColour().Ok())
5732 {
5733 dc.SetPen(wxPen(bulletAttr.GetTextColour()));
5734 dc.SetBrush(wxBrush(bulletAttr.GetTextColour()));
5735 }
5736 else
5737 {
5738 dc.SetPen(*wxBLACK_PEN);
5739 dc.SetBrush(*wxBLACK_BRUSH);
5740 }
5741
5742 wxFont font;
5743 if (bulletAttr.GetFont().Ok())
5744 font = bulletAttr.GetFont();
5745 else
5746 font = (*wxNORMAL_FONT);
5747
5748 dc.SetFont(font);
5749
5750 int charHeight = dc.GetCharHeight();
5751
5752 int bulletWidth = (int) (((float) charHeight) * wxRichTextBuffer::GetBulletProportion());
5753 int bulletHeight = bulletWidth;
5754
5755 int x = rect.x;
5756
5757 // Calculate the top position of the character (as opposed to the whole line height)
5758 int y = rect.y + (rect.height - charHeight);
5759
5760 // Calculate where the bullet should be positioned
5761 y = y + (charHeight+1)/2 - (bulletHeight+1)/2;
5762
5763 // The margin between a bullet and text.
44219ff0 5764 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
d2d0adc7
JS
5765
5766 if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
5767 x = rect.x + rect.width - bulletWidth - margin;
5768 else if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
5769 x = x + (rect.width)/2 - bulletWidth/2;
5770
5771 if (bulletAttr.GetBulletName() == wxT("standard/square"))
5772 {
5773 dc.DrawRectangle(x, y, bulletWidth, bulletHeight);
5774 }
5775 else if (bulletAttr.GetBulletName() == wxT("standard/diamond"))
5776 {
5777 wxPoint pts[5];
5778 pts[0].x = x; pts[0].y = y + bulletHeight/2;
5779 pts[1].x = x + bulletWidth/2; pts[1].y = y;
5780 pts[2].x = x + bulletWidth; pts[2].y = y + bulletHeight/2;
5781 pts[3].x = x + bulletWidth/2; pts[3].y = y + bulletHeight;
5782
5783 dc.DrawPolygon(4, pts);
5784 }
5785 else if (bulletAttr.GetBulletName() == wxT("standard/triangle"))
5786 {
5787 wxPoint pts[3];
5788 pts[0].x = x; pts[0].y = y;
5789 pts[1].x = x + bulletWidth; pts[1].y = y + bulletHeight/2;
5790 pts[2].x = x; pts[2].y = y + bulletHeight;
5791
5792 dc.DrawPolygon(3, pts);
5793 }
5794 else // "standard/circle", and catch-all
5795 {
5796 dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
5797 }
5798
5799 return true;
5800}
5801
44219ff0 5802bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxTextAttrEx& attr, const wxRect& rect, const wxString& text)
d2d0adc7
JS
5803{
5804 if (!text.empty())
5805 {
5806 wxFont font;
5807 if ((attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL) && !attr.GetBulletFont().IsEmpty() && attr.GetFont().Ok())
5808 {
5809 font = (*wxTheFontList->FindOrCreateFont(attr.GetFont().GetPointSize(), attr.GetFont().GetFamily(),
5810 attr.GetFont().GetStyle(), attr.GetFont().GetWeight(), attr.GetFont().GetUnderlined(),
5811 attr.GetBulletFont()));
5812 }
5813 else if (attr.GetFont().Ok())
5814 font = attr.GetFont();
5815 else
5816 font = (*wxNORMAL_FONT);
5817
5818 dc.SetFont(font);
5819
5820 if (attr.GetTextColour().Ok())
5821 dc.SetTextForeground(attr.GetTextColour());
5822
5823 dc.SetBackgroundMode(wxTRANSPARENT);
5824
5825 int charHeight = dc.GetCharHeight();
5826 wxCoord tw, th;
5827 dc.GetTextExtent(text, & tw, & th);
5828
5829 int x = rect.x;
5830
5831 // Calculate the top position of the character (as opposed to the whole line height)
5832 int y = rect.y + (rect.height - charHeight);
5833
5834 // The margin between a bullet and text.
44219ff0 5835 int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
d2d0adc7
JS
5836
5837 if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
5838 x = (rect.x + rect.width) - tw - margin;
5839 else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
5840 x = x + (rect.width)/2 - tw/2;
5841
5842 dc.DrawText(text, x, y);
5843
5844 return true;
5845 }
5846 else
5847 return false;
5848}
5849
5850bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& WXUNUSED(dc), const wxTextAttrEx& WXUNUSED(attr), const wxRect& WXUNUSED(rect))
5851{
5852 // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
5853 // with the buffer. The store will allow retrieval from memory, disk or other means.
5854 return false;
5855}
5856
5857/// Enumerate the standard bullet names currently supported
5858bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString& bulletNames)
5859{
5860 bulletNames.Add(wxT("standard/circle"));
5861 bulletNames.Add(wxT("standard/square"));
5862 bulletNames.Add(wxT("standard/diamond"));
5863 bulletNames.Add(wxT("standard/triangle"));
5864
5865 return true;
5866}
5d7836c4
JS
5867
5868/*
5869 * Module to initialise and clean up handlers
5870 */
5871
5872class wxRichTextModule: public wxModule
5873{
5874DECLARE_DYNAMIC_CLASS(wxRichTextModule)
5875public:
5876 wxRichTextModule() {}
cfa3b256
JS
5877 bool OnInit()
5878 {
d2d0adc7 5879 wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer);
cfa3b256
JS
5880 wxRichTextBuffer::InitStandardHandlers();
5881 wxRichTextParagraph::InitDefaultTabs();
5882 return true;
5883 };
5884 void OnExit()
5885 {
5886 wxRichTextBuffer::CleanUpHandlers();
5887 wxRichTextDecimalToRoman(-1);
5888 wxRichTextParagraph::ClearDefaultTabs();
dadd4f55 5889 wxRichTextCtrl::ClearAvailableFontNames();
d2d0adc7 5890 wxRichTextBuffer::SetRenderer(NULL);
cfa3b256 5891 };
5d7836c4
JS
5892};
5893
5894IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule)
5895
5896
f1d6804f
RD
5897// If the richtext lib is dynamically loaded after the app has already started
5898// (such as from wxPython) then the built-in module system will not init this
5899// module. Provide this function to do it manually.
5900void wxRichTextModuleInit()
5901{
5902 wxModule* module = new wxRichTextModule;
5903 module->Init();
5904 wxModule::RegisterModule(module);
5905}
5906
5907
5d7836c4
JS
5908/*!
5909 * Commands for undo/redo
5910 *
5911 */
5912
5913wxRichTextCommand::wxRichTextCommand(const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
7fe8059f 5914 wxRichTextCtrl* ctrl, bool ignoreFirstTime): wxCommand(true, name)
5d7836c4
JS
5915{
5916 /* wxRichTextAction* action = */ new wxRichTextAction(this, name, id, buffer, ctrl, ignoreFirstTime);
5917}
5918
7fe8059f 5919wxRichTextCommand::wxRichTextCommand(const wxString& name): wxCommand(true, name)
5d7836c4
JS
5920{
5921}
5922
5923wxRichTextCommand::~wxRichTextCommand()
5924{
5925 ClearActions();
5926}
5927
5928void wxRichTextCommand::AddAction(wxRichTextAction* action)
5929{
5930 if (!m_actions.Member(action))
5931 m_actions.Append(action);
5932}
5933
5934bool wxRichTextCommand::Do()
5935{
09f14108 5936 for (wxList::compatibility_iterator node = m_actions.GetFirst(); node; node = node->GetNext())
5d7836c4
JS
5937 {
5938 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
5939 action->Do();
5940 }
5941
5942 return true;
5943}
5944
5945bool wxRichTextCommand::Undo()
5946{
09f14108 5947 for (wxList::compatibility_iterator node = m_actions.GetLast(); node; node = node->GetPrevious())
5d7836c4
JS
5948 {
5949 wxRichTextAction* action = (wxRichTextAction*) node->GetData();
5950 action->Undo();
5951 }
5952
5953 return true;
5954}
5955
5956void wxRichTextCommand::ClearActions()
5957{
5958 WX_CLEAR_LIST(wxList, m_actions);
5959}
5960
5961/*!
5962 * Individual action
5963 *
5964 */
5965
5966wxRichTextAction::wxRichTextAction(wxRichTextCommand* cmd, const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
5967 wxRichTextCtrl* ctrl, bool ignoreFirstTime)
5968{
5969 m_buffer = buffer;
5970 m_ignoreThis = ignoreFirstTime;
5971 m_cmdId = id;
5972 m_position = -1;
5973 m_ctrl = ctrl;
5974 m_name = name;
5975 m_newParagraphs.SetDefaultStyle(buffer->GetDefaultStyle());
5976 m_newParagraphs.SetBasicStyle(buffer->GetBasicStyle());
5977 if (cmd)
5978 cmd->AddAction(this);
5979}
5980
5981wxRichTextAction::~wxRichTextAction()
5982{
5983}
5984
5985bool wxRichTextAction::Do()
5986{
5987 m_buffer->Modify(true);
5988
5989 switch (m_cmdId)
5990 {
5991 case wxRICHTEXT_INSERT:
5992 {
ea160b2e
JS
5993 // Store a list of line start character and y positions so we can figure out which area
5994 // we need to refresh
5995 wxArrayInt optimizationLineCharPositions;
5996 wxArrayInt optimizationLineYPositions;
5997
5998#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
5999 // NOTE: we're assuming that the buffer is laid out correctly at this point.
6000 // If we had several actions, which only invalidate and leave layout until the
6001 // paint handler is called, then this might not be true. So we may need to switch
6002 // optimisation on only when we're simply adding text and not simultaneously
6003 // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
6004 // first, but of course this means we'll be doing it twice.
6005 if (!m_buffer->GetDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
6006 {
6007 wxSize clientSize = m_ctrl->GetClientSize();
6008 wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint();
6009 int lastY = firstVisiblePt.y + clientSize.y;
6010
6011 wxRichTextParagraph* para = m_buffer->GetParagraphAtPosition(GetPosition());
6012 wxRichTextObjectList::compatibility_iterator node = m_buffer->GetChildren().Find(para);
6013 while (node)
6014 {
6015 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
6016 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
6017 while (node2)
6018 {
6019 wxRichTextLine* line = node2->GetData();
6020 wxPoint pt = line->GetAbsolutePosition();
6021 wxRichTextRange range = line->GetAbsoluteRange();
6022
6023 if (pt.y > lastY)
6024 {
27b5dab3
JS
6025 node2 = wxRichTextLineList::compatibility_iterator();
6026 node = wxRichTextObjectList::compatibility_iterator();
ea160b2e
JS
6027 }
6028 else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
6029 {
6030 optimizationLineCharPositions.Add(range.GetStart());
6031 optimizationLineYPositions.Add(pt.y);
6032 }
6033
6034 if (node2)
6035 node2 = node2->GetNext();
6036 }
6037
6038 if (node)
6039 node = node->GetNext();
6040 }
6041 }
6042#endif
6043
5d7836c4
JS
6044 m_buffer->InsertFragment(GetPosition(), m_newParagraphs);
6045 m_buffer->UpdateRanges();
1e967276 6046 m_buffer->Invalidate(GetRange());
5d7836c4 6047
0ca07313
JS
6048 long newCaretPosition = GetPosition() + m_newParagraphs.GetRange().GetLength();
6049
6050 // Character position to caret position
6051 newCaretPosition --;
6052
6053 // Don't take into account the last newline
5d7836c4
JS
6054 if (m_newParagraphs.GetPartialParagraph())
6055 newCaretPosition --;
6056
0ca07313 6057 newCaretPosition = wxMin(newCaretPosition, (m_buffer->GetRange().GetEnd()-1));
ea160b2e 6058
0ca07313 6059
ea160b2e
JS
6060 if (optimizationLineCharPositions.GetCount() > 0)
6061 UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions);
6062 else
6063 UpdateAppearance(newCaretPosition, true /* send update event */);
5d7836c4
JS
6064
6065 break;
6066 }
6067 case wxRICHTEXT_DELETE:
6068 {
6069 m_buffer->DeleteRange(GetRange());
6070 m_buffer->UpdateRanges();
1e967276 6071 m_buffer->Invalidate(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
5d7836c4
JS
6072
6073 UpdateAppearance(GetRange().GetStart()-1, true /* send update event */);
6074
6075 break;
6076 }
6077 case wxRICHTEXT_CHANGE_STYLE:
6078 {
6079 ApplyParagraphs(GetNewParagraphs());
1e967276 6080 m_buffer->Invalidate(GetRange());
5d7836c4
JS
6081
6082 UpdateAppearance(GetPosition());
6083
6084 break;
6085 }
6086 default:
6087 break;
6088 }
6089
6090 return true;
6091}
6092
6093bool wxRichTextAction::Undo()
6094{
6095 m_buffer->Modify(true);
6096
6097 switch (m_cmdId)
6098 {
6099 case wxRICHTEXT_INSERT:
6100 {
6101 m_buffer->DeleteRange(GetRange());
6102 m_buffer->UpdateRanges();
1e967276 6103 m_buffer->Invalidate(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
5d7836c4
JS
6104
6105 long newCaretPosition = GetPosition() - 1;
6106 // if (m_newParagraphs.GetPartialParagraph())
6107 // newCaretPosition --;
ea160b2e 6108
5d7836c4
JS
6109 UpdateAppearance(newCaretPosition, true /* send update event */);
6110
6111 break;
6112 }
6113 case wxRICHTEXT_DELETE:
6114 {
6115 m_buffer->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
6116 m_buffer->UpdateRanges();
1e967276 6117 m_buffer->Invalidate(GetRange());
5d7836c4
JS
6118
6119 UpdateAppearance(GetPosition(), true /* send update event */);
6120
6121 break;
6122 }
6123 case wxRICHTEXT_CHANGE_STYLE:
6124 {
6125 ApplyParagraphs(GetOldParagraphs());
1e967276 6126 m_buffer->Invalidate(GetRange());
5d7836c4
JS
6127
6128 UpdateAppearance(GetPosition());
6129
6130 break;
6131 }
6132 default:
6133 break;
6134 }
6135
6136 return true;
6137}
6138
6139/// Update the control appearance
ea160b2e 6140void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions)
5d7836c4
JS
6141{
6142 if (m_ctrl)
6143 {
6144 m_ctrl->SetCaretPosition(caretPosition);
6145 if (!m_ctrl->IsFrozen())
6146 {
2f36e8dc 6147 m_ctrl->LayoutContent();
5d7836c4 6148 m_ctrl->PositionCaret();
ea160b2e
JS
6149
6150#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
6151 // Find refresh rectangle if we are in a position to optimise refresh
6152 if (m_cmdId == wxRICHTEXT_INSERT && optimizationLineCharPositions && optimizationLineCharPositions->GetCount() > 0)
6153 {
6154 size_t i;
6155
6156 wxSize clientSize = m_ctrl->GetClientSize();
6157 wxPoint firstVisiblePt = m_ctrl->GetFirstVisiblePoint();
6158
6159 // Start/end positions
6160 int firstY = 0;
6161 int lastY = firstVisiblePt.y + clientSize.y;
6162
6163 bool foundStart = false;
6164 bool foundEnd = false;
6165
6166 // position offset - how many characters were inserted
6167 int positionOffset = GetRange().GetLength();
6168
6169 // find the first line which is being drawn at the same position as it was
6170 // before. Since we're talking about a simple insertion, we can assume
6171 // that the rest of the window does not need to be redrawn.
6172
6173 wxRichTextParagraph* para = m_buffer->GetParagraphAtPosition(GetPosition());
6174 wxRichTextObjectList::compatibility_iterator node = m_buffer->GetChildren().Find(para);
6175 while (node)
6176 {
6177 wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
6178 wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
6179 while (node2)
6180 {
6181 wxRichTextLine* line = node2->GetData();
6182 wxPoint pt = line->GetAbsolutePosition();
6183 wxRichTextRange range = line->GetAbsoluteRange();
6184
6185 // we want to find the first line that is in the same position
6186 // as before. This will mean we're at the end of the changed text.
6187
6188 if (pt.y > lastY) // going past the end of the window, no more info
6189 {
27b5dab3
JS
6190 node2 = wxRichTextLineList::compatibility_iterator();
6191 node = wxRichTextObjectList::compatibility_iterator();
ea160b2e
JS
6192 }
6193 else
6194 {
6195 if (!foundStart)
6196 {
6197 firstY = pt.y - firstVisiblePt.y;
6198 foundStart = true;
6199 }
6200
6201 // search for this line being at the same position as before
6202 for (i = 0; i < optimizationLineCharPositions->GetCount(); i++)
6203 {
6204 if (((*optimizationLineCharPositions)[i] + positionOffset == range.GetStart()) &&
6205 ((*optimizationLineYPositions)[i] == pt.y))
6206 {
6207 // Stop, we're now the same as we were
6208 foundEnd = true;
6209 lastY = pt.y - firstVisiblePt.y;
6210
27b5dab3
JS
6211 node2 = wxRichTextLineList::compatibility_iterator();
6212 node = wxRichTextObjectList::compatibility_iterator();
6213
ea160b2e
JS
6214 break;
6215 }
6216 }
6217 }
6218
6219 if (node2)
6220 node2 = node2->GetNext();
6221 }
6222
6223 if (node)
6224 node = node->GetNext();
6225 }
6226
6227 if (!foundStart)
6228 firstY = firstVisiblePt.y;
6229 if (!foundEnd)
6230 lastY = firstVisiblePt.y + clientSize.y;
6231
6232 wxRect rect(firstVisiblePt.x, firstY, firstVisiblePt.x + clientSize.x, lastY - firstY);
6233 m_ctrl->RefreshRect(rect);
6234
6235 // TODO: we need to make sure that lines are only drawn if in the update region. The rect
6236 // passed to Draw is currently used in different ways (to pass the position the content should
6237 // be drawn at as well as the relevant region).
6238 }
6239 else
6240#endif
6241 m_ctrl->Refresh(false);
5d7836c4
JS
6242
6243 if (sendUpdateEvent)
343ae70d 6244 m_ctrl->SendTextUpdatedEvent();
5d7836c4 6245 }
7fe8059f 6246 }
5d7836c4
JS
6247}
6248
6249/// Replace the buffer paragraphs with the new ones.
0ca07313 6250void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment)
5d7836c4
JS
6251{
6252 wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst();
6253 while (node)
6254 {
6255 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
6256 wxASSERT (para != NULL);
6257
6258 // We'll replace the existing paragraph by finding the paragraph at this position,
6259 // delete its node data, and setting a copy as the new node data.
6260 // TODO: make more efficient by simply swapping old and new paragraph objects.
6261
6262 wxRichTextParagraph* existingPara = m_buffer->GetParagraphAtPosition(para->GetRange().GetStart());
6263 if (existingPara)
6264 {
6265 wxRichTextObjectList::compatibility_iterator bufferParaNode = m_buffer->GetChildren().Find(existingPara);
6266 if (bufferParaNode)
6267 {
6268 wxRichTextParagraph* newPara = new wxRichTextParagraph(*para);
6269 newPara->SetParent(m_buffer);
6270
6271 bufferParaNode->SetData(newPara);
6272
6273 delete existingPara;
6274 }
6275 }
6276
6277 node = node->GetNext();
6278 }
6279}
6280
6281
6282/*!
6283 * wxRichTextRange
6284 * This stores beginning and end positions for a range of data.
6285 */
6286
6287/// Limit this range to be within 'range'
6288bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
6289{
6290 if (m_start < range.m_start)
6291 m_start = range.m_start;
6292
6293 if (m_end > range.m_end)
6294 m_end = range.m_end;
6295
6296 return true;
6297}
6298
6299/*!
6300 * wxRichTextImage implementation
6301 * This object represents an image.
6302 */
6303
6304IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
6305
4f32b3cf 6306wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxTextAttrEx* charStyle):
5d7836c4
JS
6307 wxRichTextObject(parent)
6308{
6309 m_image = image;
4f32b3cf
JS
6310 if (charStyle)
6311 SetAttributes(*charStyle);
5d7836c4
JS
6312}
6313
4f32b3cf 6314wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxTextAttrEx* charStyle):
5d7836c4
JS
6315 wxRichTextObject(parent)
6316{
6317 m_imageBlock = imageBlock;
6318 m_imageBlock.Load(m_image);
4f32b3cf
JS
6319 if (charStyle)
6320 SetAttributes(*charStyle);
5d7836c4
JS
6321}
6322
6323/// Load wxImage from the block
6324bool wxRichTextImage::LoadFromBlock()
6325{
6326 m_imageBlock.Load(m_image);
6327 return m_imageBlock.Ok();
6328}
6329
6330/// Make block from the wxImage
6331bool wxRichTextImage::MakeBlock()
6332{
6333 if (m_imageBlock.GetImageType() == wxBITMAP_TYPE_ANY || m_imageBlock.GetImageType() == -1)
6334 m_imageBlock.SetImageType(wxBITMAP_TYPE_PNG);
6335
6336 m_imageBlock.MakeImageBlock(m_image, m_imageBlock.GetImageType());
6337 return m_imageBlock.Ok();
6338}
6339
6340
6341/// Draw the item
6342bool wxRichTextImage::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
6343{
6344 if (!m_image.Ok() && m_imageBlock.Ok())
6345 LoadFromBlock();
6346
6347 if (!m_image.Ok())
6348 return false;
6349
6350 if (m_image.Ok() && !m_bitmap.Ok())
6351 m_bitmap = wxBitmap(m_image);
6352
6353 int y = rect.y + (rect.height - m_image.GetHeight());
6354
6355 if (m_bitmap.Ok())
6356 dc.DrawBitmap(m_bitmap, rect.x, y, true);
6357
6358 if (selectionRange.Contains(range.GetStart()))
6359 {
6360 dc.SetBrush(*wxBLACK_BRUSH);
6361 dc.SetPen(*wxBLACK_PEN);
6362 dc.SetLogicalFunction(wxINVERT);
6363 dc.DrawRectangle(rect);
6364 dc.SetLogicalFunction(wxCOPY);
6365 }
6366
6367 return true;
6368}
6369
6370/// Lay the item out
38113684 6371bool wxRichTextImage::Layout(wxDC& WXUNUSED(dc), const wxRect& rect, int WXUNUSED(style))
5d7836c4
JS
6372{
6373 if (!m_image.Ok())
6374 LoadFromBlock();
6375
6376 if (m_image.Ok())
6377 {
6378 SetCachedSize(wxSize(m_image.GetWidth(), m_image.GetHeight()));
6379 SetPosition(rect.GetPosition());
6380 }
6381
6382 return true;
6383}
6384
6385/// Get/set the object size for the given range. Returns false if the range
6386/// is invalid for this object.
7f0d9d71 6387bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& WXUNUSED(dc), int WXUNUSED(flags), wxPoint WXUNUSED(position)) const
5d7836c4
JS
6388{
6389 if (!range.IsWithin(GetRange()))
6390 return false;
6391
6392 if (!m_image.Ok())
6393 return false;
6394
6395 size.x = m_image.GetWidth();
6396 size.y = m_image.GetHeight();
6397
6398 return true;
6399}
6400
6401/// Copy
6402void wxRichTextImage::Copy(const wxRichTextImage& obj)
6403{
59509217
JS
6404 wxRichTextObject::Copy(obj);
6405
5d7836c4
JS
6406 m_image = obj.m_image;
6407 m_imageBlock = obj.m_imageBlock;
6408}
6409
6410/*!
6411 * Utilities
6412 *
6413 */
6414
6415/// Compare two attribute objects
6416bool wxTextAttrEq(const wxTextAttrEx& attr1, const wxTextAttrEx& attr2)
6417{
38f833b1 6418 return (attr1 == attr2);
5d7836c4
JS
6419}
6420
6421bool wxTextAttrEq(const wxTextAttrEx& attr1, const wxRichTextAttr& attr2)
6422{
6423 return (
6424 attr1.GetTextColour() == attr2.GetTextColour() &&
6425 attr1.GetBackgroundColour() == attr2.GetBackgroundColour() &&
6426 attr1.GetFont().GetPointSize() == attr2.GetFontSize() &&
6427 attr1.GetFont().GetStyle() == attr2.GetFontStyle() &&
6428 attr1.GetFont().GetWeight() == attr2.GetFontWeight() &&
6429 attr1.GetFont().GetFaceName() == attr2.GetFontFaceName() &&
6430 attr1.GetFont().GetUnderlined() == attr2.GetFontUnderlined() &&
42688aea
JS
6431 attr1.GetTextEffects() == attr2.GetTextEffects() &&
6432 attr1.GetTextEffectFlags() == attr2.GetTextEffectFlags() &&
5d7836c4
JS
6433 attr1.GetAlignment() == attr2.GetAlignment() &&
6434 attr1.GetLeftIndent() == attr2.GetLeftIndent() &&
6435 attr1.GetRightIndent() == attr2.GetRightIndent() &&
6436 attr1.GetLeftSubIndent() == attr2.GetLeftSubIndent() &&
59509217 6437 wxRichTextTabsEq(attr1.GetTabs(), attr2.GetTabs()) &&
5d7836c4
JS
6438 attr1.GetLineSpacing() == attr2.GetLineSpacing() &&
6439 attr1.GetParagraphSpacingAfter() == attr2.GetParagraphSpacingAfter() &&
6440 attr1.GetParagraphSpacingBefore() == attr2.GetParagraphSpacingBefore() &&
6441 attr1.GetBulletStyle() == attr2.GetBulletStyle() &&
6442 attr1.GetBulletNumber() == attr2.GetBulletNumber() &&
d2d0adc7 6443 attr1.GetBulletText() == attr2.GetBulletText() &&
f089713f 6444 attr1.GetBulletName() == attr2.GetBulletName() &&
59509217 6445 attr1.GetBulletFont() == attr2.GetBulletFont() &&
4d6d8bf4 6446 attr1.GetOutlineLevel() == attr2.GetOutlineLevel() &&
5d7836c4 6447 attr1.GetCharacterStyleName() == attr2.GetCharacterStyleName() &&
38f833b1 6448 attr1.GetParagraphStyleName() == attr2.GetParagraphStyleName() &&
ea160b2e
JS
6449 attr1.GetListStyleName() == attr2.GetListStyleName() &&
6450 attr1.HasPageBreak() == attr2.HasPageBreak());
5d7836c4
JS
6451}
6452
6453/// Compare two attribute objects, but take into account the flags
6454/// specifying attributes of interest.
6455bool wxTextAttrEqPartial(const wxTextAttrEx& attr1, const wxTextAttrEx& attr2, int flags)
6456{
6457 if ((flags & wxTEXT_ATTR_TEXT_COLOUR) && attr1.GetTextColour() != attr2.GetTextColour())
6458 return false;
6459
6460 if ((flags & wxTEXT_ATTR_BACKGROUND_COLOUR) && attr1.GetBackgroundColour() != attr2.GetBackgroundColour())
6461 return false;
6462
6463 if ((flags & wxTEXT_ATTR_FONT_FACE) && attr1.GetFont().Ok() && attr2.GetFont().Ok() &&
6464 attr1.GetFont().GetFaceName() != attr2.GetFont().GetFaceName())
6465 return false;
6466
6467 if ((flags & wxTEXT_ATTR_FONT_SIZE) && attr1.GetFont().Ok() && attr2.GetFont().Ok() &&
6468 attr1.GetFont().GetPointSize() != attr2.GetFont().GetPointSize())
6469 return false;
6470
6471 if ((flags & wxTEXT_ATTR_FONT_WEIGHT) && attr1.GetFont().Ok() && attr2.GetFont().Ok() &&
6472 attr1.GetFont().GetWeight() != attr2.GetFont().GetWeight())
6473 return false;
6474
6475 if ((flags & wxTEXT_ATTR_FONT_ITALIC) && attr1.GetFont().Ok() && attr2.GetFont().Ok() &&
6476 attr1.GetFont().GetStyle() != attr2.GetFont().GetStyle())
6477 return false;
6478
6479 if ((flags & wxTEXT_ATTR_FONT_UNDERLINE) && attr1.GetFont().Ok() && attr2.GetFont().Ok() &&
6480 attr1.GetFont().GetUnderlined() != attr2.GetFont().GetUnderlined())
6481 return false;
6482
6483 if ((flags & wxTEXT_ATTR_ALIGNMENT) && attr1.GetAlignment() != attr2.GetAlignment())
6484 return false;
6485
6486 if ((flags & wxTEXT_ATTR_LEFT_INDENT) &&
6487 ((attr1.GetLeftIndent() != attr2.GetLeftIndent()) || (attr1.GetLeftSubIndent() != attr2.GetLeftSubIndent())))
6488 return false;
6489
6490 if ((flags & wxTEXT_ATTR_RIGHT_INDENT) &&
6491 (attr1.GetRightIndent() != attr2.GetRightIndent()))
6492 return false;
6493
4d551ad5 6494 if ((flags & wxTEXT_ATTR_PARA_SPACING_AFTER) &&
5d7836c4
JS
6495 (attr1.GetParagraphSpacingAfter() != attr2.GetParagraphSpacingAfter()))
6496 return false;
6497
4d551ad5 6498 if ((flags & wxTEXT_ATTR_PARA_SPACING_BEFORE) &&
5d7836c4
JS
6499 (attr1.GetParagraphSpacingBefore() != attr2.GetParagraphSpacingBefore()))
6500 return false;
6501
4d551ad5 6502 if ((flags & wxTEXT_ATTR_LINE_SPACING) &&
5d7836c4
JS
6503 (attr1.GetLineSpacing() != attr2.GetLineSpacing()))
6504 return false;
6505
4d551ad5 6506 if ((flags & wxTEXT_ATTR_CHARACTER_STYLE_NAME) &&
5d7836c4
JS
6507 (attr1.GetCharacterStyleName() != attr2.GetCharacterStyleName()))
6508 return false;
6509
4d551ad5 6510 if ((flags & wxTEXT_ATTR_PARAGRAPH_STYLE_NAME) &&
5d7836c4
JS
6511 (attr1.GetParagraphStyleName() != attr2.GetParagraphStyleName()))
6512 return false;
6513
38f833b1
JS
6514 if ((flags & wxTEXT_ATTR_LIST_STYLE_NAME) &&
6515 (attr1.GetListStyleName() != attr2.GetListStyleName()))
6516 return false;
6517
5d7836c4
JS
6518 if ((flags & wxTEXT_ATTR_BULLET_STYLE) &&
6519 (attr1.GetBulletStyle() != attr2.GetBulletStyle()))
6520 return false;
6521
6522 if ((flags & wxTEXT_ATTR_BULLET_NUMBER) &&
6523 (attr1.GetBulletNumber() != attr2.GetBulletNumber()))
6524 return false;
6525
d2d0adc7
JS
6526 if ((flags & wxTEXT_ATTR_BULLET_TEXT) &&
6527 (attr1.GetBulletText() != attr2.GetBulletText()) &&
f089713f 6528 (attr1.GetBulletFont() != attr2.GetBulletFont()))
5d7836c4
JS
6529 return false;
6530
f089713f
JS
6531 if ((flags & wxTEXT_ATTR_BULLET_NAME) &&
6532 (attr1.GetBulletName() != attr2.GetBulletName()))
59509217
JS
6533 return false;
6534
5d7836c4 6535 if ((flags & wxTEXT_ATTR_TABS) &&
59509217 6536 !wxRichTextTabsEq(attr1.GetTabs(), attr2.GetTabs()))
5d7836c4 6537 return false;
5d7836c4 6538
ea160b2e
JS
6539 if ((flags & wxTEXT_ATTR_PAGE_BREAK) &&
6540 (attr1.HasPageBreak() != attr2.HasPageBreak()))
6541 return false;
6542
42688aea
JS
6543 if (flags & wxTEXT_ATTR_EFFECTS)
6544 {
6545 if (attr1.HasTextEffects() != attr2.HasTextEffects())
6546 return false;
6547 if (!wxRichTextBitlistsEqPartial(attr1.GetTextEffects(), attr2.GetTextEffects(), attr2.GetTextEffectFlags()))
6548 return false;
6549 }
6550
4d6d8bf4
JS
6551 if ((flags & wxTEXT_ATTR_OUTLINE_LEVEL) &&
6552 (attr1.GetOutlineLevel() != attr2.GetOutlineLevel()))
6553 return false;
6554
5d7836c4
JS
6555 return true;
6556}
6557
6558bool wxTextAttrEqPartial(const wxTextAttrEx& attr1, const wxRichTextAttr& attr2, int flags)
6559{
6560 if ((flags & wxTEXT_ATTR_TEXT_COLOUR) && attr1.GetTextColour() != attr2.GetTextColour())
6561 return false;
6562
6563 if ((flags & wxTEXT_ATTR_BACKGROUND_COLOUR) && attr1.GetBackgroundColour() != attr2.GetBackgroundColour())
6564 return false;
6565
6566 if ((flags & (wxTEXT_ATTR_FONT)) && !attr1.GetFont().Ok())
6567 return false;
6568
6569 if ((flags & wxTEXT_ATTR_FONT_FACE) && attr1.GetFont().Ok() &&
6570 attr1.GetFont().GetFaceName() != attr2.GetFontFaceName())
6571 return false;
6572
6573 if ((flags & wxTEXT_ATTR_FONT_SIZE) && attr1.GetFont().Ok() &&
6574 attr1.GetFont().GetPointSize() != attr2.GetFontSize())
6575 return false;
6576
6577 if ((flags & wxTEXT_ATTR_FONT_WEIGHT) && attr1.GetFont().Ok() &&
6578 attr1.GetFont().GetWeight() != attr2.GetFontWeight())
6579 return false;
6580
6581 if ((flags & wxTEXT_ATTR_FONT_ITALIC) && attr1.GetFont().Ok() &&
6582 attr1.GetFont().GetStyle() != attr2.GetFontStyle())
6583 return false;
6584
6585 if ((flags & wxTEXT_ATTR_FONT_UNDERLINE) && attr1.GetFont().Ok() &&
6586 attr1.GetFont().GetUnderlined() != attr2.GetFontUnderlined())
6587 return false;
6588
6589 if ((flags & wxTEXT_ATTR_ALIGNMENT) && attr1.GetAlignment() != attr2.GetAlignment())
6590 return false;
6591
6592 if ((flags & wxTEXT_ATTR_LEFT_INDENT) &&
6593 ((attr1.GetLeftIndent() != attr2.GetLeftIndent()) || (attr1.GetLeftSubIndent() != attr2.GetLeftSubIndent())))
6594 return false;
6595
6596 if ((flags & wxTEXT_ATTR_RIGHT_INDENT) &&
6597 (attr1.GetRightIndent() != attr2.GetRightIndent()))
6598 return false;
6599
4d551ad5 6600 if ((flags & wxTEXT_ATTR_PARA_SPACING_AFTER) &&
5d7836c4
JS
6601 (attr1.GetParagraphSpacingAfter() != attr2.GetParagraphSpacingAfter()))
6602 return false;
6603
4d551ad5 6604 if ((flags & wxTEXT_ATTR_PARA_SPACING_BEFORE) &&
5d7836c4
JS
6605 (attr1.GetParagraphSpacingBefore() != attr2.GetParagraphSpacingBefore()))
6606 return false;
6607
4d551ad5 6608 if ((flags & wxTEXT_ATTR_LINE_SPACING) &&
5d7836c4
JS
6609 (attr1.GetLineSpacing() != attr2.GetLineSpacing()))
6610 return false;
6611
4d551ad5 6612 if ((flags & wxTEXT_ATTR_CHARACTER_STYLE_NAME) &&
5d7836c4
JS
6613 (attr1.GetCharacterStyleName() != attr2.GetCharacterStyleName()))
6614 return false;
6615
4d551ad5 6616 if ((flags & wxTEXT_ATTR_PARAGRAPH_STYLE_NAME) &&
5d7836c4
JS
6617 (attr1.GetParagraphStyleName() != attr2.GetParagraphStyleName()))
6618 return false;
6619
38f833b1
JS
6620 if ((flags & wxTEXT_ATTR_LIST_STYLE_NAME) &&
6621 (attr1.GetListStyleName() != attr2.GetListStyleName()))
6622 return false;
6623
5d7836c4
JS
6624 if ((flags & wxTEXT_ATTR_BULLET_STYLE) &&
6625 (attr1.GetBulletStyle() != attr2.GetBulletStyle()))
6626 return false;
6627
6628 if ((flags & wxTEXT_ATTR_BULLET_NUMBER) &&
6629 (attr1.GetBulletNumber() != attr2.GetBulletNumber()))
6630 return false;
6631
d2d0adc7
JS
6632 if ((flags & wxTEXT_ATTR_BULLET_TEXT) &&
6633 (attr1.GetBulletText() != attr2.GetBulletText()) &&
f089713f 6634 (attr1.GetBulletFont() != attr2.GetBulletFont()))
5d7836c4
JS
6635 return false;
6636
f089713f
JS
6637 if ((flags & wxTEXT_ATTR_BULLET_NAME) &&
6638 (attr1.GetBulletName() != attr2.GetBulletName()))
59509217
JS
6639 return false;
6640
5d7836c4 6641 if ((flags & wxTEXT_ATTR_TABS) &&
59509217 6642 !wxRichTextTabsEq(attr1.GetTabs(), attr2.GetTabs()))
5d7836c4 6643 return false;
5d7836c4 6644
ea160b2e
JS
6645 if ((flags & wxTEXT_ATTR_PAGE_BREAK) &&
6646 (attr1.HasPageBreak() != attr2.HasPageBreak()))
6647 return false;
6648
42688aea
JS
6649 if (flags & wxTEXT_ATTR_EFFECTS)
6650 {
6651 if (attr1.HasTextEffects() != attr2.HasTextEffects())
6652 return false;
6653 if (!wxRichTextBitlistsEqPartial(attr1.GetTextEffects(), attr2.GetTextEffects(), attr2.GetTextEffectFlags()))
6654 return false;
6655 }
6656
4d6d8bf4
JS
6657 if ((flags & wxTEXT_ATTR_OUTLINE_LEVEL) &&
6658 (attr1.GetOutlineLevel() != attr2.GetOutlineLevel()))
6659 return false;
6660
5d7836c4
JS
6661 return true;
6662}
6663
59509217
JS
6664/// Compare tabs
6665bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2)
6666{
6667 if (tabs1.GetCount() != tabs2.GetCount())
6668 return false;
6669
6670 size_t i;
6671 for (i = 0; i < tabs1.GetCount(); i++)
6672 {
6673 if (tabs1[i] != tabs2[i])
6674 return false;
6675 }
6676 return true;
6677}
6678
5d7836c4
JS
6679/// Apply one style to another
6680bool wxRichTextApplyStyle(wxTextAttrEx& destStyle, const wxTextAttrEx& style)
6681{
6682 // Whole font
6683 if (style.GetFont().Ok() && ((style.GetFlags() & (wxTEXT_ATTR_FONT)) == (wxTEXT_ATTR_FONT)))
6684 destStyle.SetFont(style.GetFont());
6685 else if (style.GetFont().Ok())
6686 {
6687 wxFont font = destStyle.GetFont();
6688
6689 if (style.GetFlags() & wxTEXT_ATTR_FONT_FACE)
fe5aa22c
JS
6690 {
6691 destStyle.SetFlags(destStyle.GetFlags() | wxTEXT_ATTR_FONT_FACE);
5d7836c4 6692 font.SetFaceName(style.GetFont().GetFaceName());
fe5aa22c 6693 }
5d7836c4
JS
6694
6695 if (style.GetFlags() & wxTEXT_ATTR_FONT_SIZE)
fe5aa22c
JS
6696 {
6697 destStyle.SetFlags(destStyle.GetFlags() | wxTEXT_ATTR_FONT_SIZE);
5d7836c4 6698 font.SetPointSize(style.GetFont().GetPointSize());
fe5aa22c 6699 }
5d7836c4
JS
6700
6701 if (style.GetFlags() & wxTEXT_ATTR_FONT_ITALIC)
fe5aa22c
JS
6702 {
6703 destStyle.SetFlags(destStyle.GetFlags() | wxTEXT_ATTR_FONT_ITALIC);
5d7836c4 6704 font.SetStyle(style.GetFont().GetStyle());
fe5aa22c 6705 }
5d7836c4
JS
6706
6707 if (style.GetFlags() & wxTEXT_ATTR_FONT_WEIGHT)
fe5aa22c
JS
6708 {
6709 destStyle.SetFlags(destStyle.GetFlags() | wxTEXT_ATTR_FONT_WEIGHT);
5d7836c4 6710 font.SetWeight(style.GetFont().GetWeight());
fe5aa22c 6711 }
5d7836c4
JS
6712
6713 if (style.GetFlags() & wxTEXT_ATTR_FONT_UNDERLINE)
fe5aa22c
JS
6714 {
6715 destStyle.SetFlags(destStyle.GetFlags() | wxTEXT_ATTR_FONT_UNDERLINE);
5d7836c4 6716 font.SetUnderlined(style.GetFont().GetUnderlined());
fe5aa22c 6717 }
5d7836c4
JS
6718
6719 if (font != destStyle.GetFont())
fe5aa22c
JS
6720 {
6721 int oldFlags = destStyle.GetFlags();
6722
5d7836c4 6723 destStyle.SetFont(font);
fe5aa22c
JS
6724
6725 destStyle.SetFlags(oldFlags);
6726 }
5d7836c4
JS
6727 }
6728
6729 if ( style.GetTextColour().Ok() && style.HasTextColour())
6730 destStyle.SetTextColour(style.GetTextColour());
6731
6732 if ( style.GetBackgroundColour().Ok() && style.HasBackgroundColour())
6733 destStyle.SetBackgroundColour(style.GetBackgroundColour());
6734
6735 if (style.HasAlignment())
6736 destStyle.SetAlignment(style.GetAlignment());
6737
6738 if (style.HasTabs())
6739 destStyle.SetTabs(style.GetTabs());
6740
6741 if (style.HasLeftIndent())
6742 destStyle.SetLeftIndent(style.GetLeftIndent(), style.GetLeftSubIndent());
6743
6744 if (style.HasRightIndent())
6745 destStyle.SetRightIndent(style.GetRightIndent());
6746
6747 if (style.HasParagraphSpacingAfter())
6748 destStyle.SetParagraphSpacingAfter(style.GetParagraphSpacingAfter());
6749
6750 if (style.HasParagraphSpacingBefore())
6751 destStyle.SetParagraphSpacingBefore(style.GetParagraphSpacingBefore());
6752
6753 if (style.HasLineSpacing())
6754 destStyle.SetLineSpacing(style.GetLineSpacing());
6755
6756 if (style.HasCharacterStyleName())
6757 destStyle.SetCharacterStyleName(style.GetCharacterStyleName());
6758
6759 if (style.HasParagraphStyleName())
6760 destStyle.SetParagraphStyleName(style.GetParagraphStyleName());
6761
38f833b1
JS
6762 if (style.HasListStyleName())
6763 destStyle.SetListStyleName(style.GetListStyleName());
6764
5d7836c4 6765 if (style.HasBulletStyle())
5d7836c4 6766 destStyle.SetBulletStyle(style.GetBulletStyle());
f089713f 6767
d2d0adc7 6768 if (style.HasBulletText())
f089713f 6769 {
d2d0adc7 6770 destStyle.SetBulletText(style.GetBulletText());
59509217 6771 destStyle.SetBulletFont(style.GetBulletFont());
5d7836c4
JS
6772 }
6773
f089713f
JS
6774 if (style.HasBulletName())
6775 destStyle.SetBulletName(style.GetBulletName());
6776
5d7836c4
JS
6777 if (style.HasBulletNumber())
6778 destStyle.SetBulletNumber(style.GetBulletNumber());
6779
d2d0adc7
JS
6780 if (style.HasURL())
6781 destStyle.SetURL(style.GetURL());
6782
ea160b2e
JS
6783 if (style.HasPageBreak())
6784 destStyle.SetPageBreak();
6785
42688aea
JS
6786 if (style.HasTextEffects())
6787 {
6788 int destBits = destStyle.GetTextEffects();
6789 int destFlags = destStyle.GetTextEffectFlags();
6790
6791 int srcBits = style.GetTextEffects();
6792 int srcFlags = style.GetTextEffectFlags();
6793
6794 wxRichTextCombineBitlists(destBits, srcBits, destFlags, srcFlags);
6795
6796 destStyle.SetTextEffects(destBits);
6797 destStyle.SetTextEffectFlags(destFlags);
6798 }
6799
4d6d8bf4
JS
6800 if (style.HasOutlineLevel())
6801 destStyle.SetOutlineLevel(style.GetOutlineLevel());
6802
5d7836c4
JS
6803 return true;
6804}
6805
6806bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxTextAttrEx& style)
6807{
4d6d8bf4 6808 wxTextAttrEx destStyle2 = destStyle;
5d7836c4
JS
6809 wxRichTextApplyStyle(destStyle2, style);
6810 destStyle = destStyle2;
6811 return true;
6812}
6813
1f71a770
JS
6814bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
6815{
6816 wxTextAttrEx attr(destStyle);
6817 wxRichTextApplyStyle(attr, style, compareWith);
6818 destStyle = attr;
6819 return true;
6820}
6821
59509217 6822bool wxRichTextApplyStyle(wxTextAttrEx& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
5d7836c4 6823{
5d7836c4
JS
6824 // Whole font. Avoiding setting individual attributes if possible, since
6825 // it recreates the font each time.
59509217 6826 if (((style.GetFlags() & (wxTEXT_ATTR_FONT)) == (wxTEXT_ATTR_FONT)) && !compareWith)
5d7836c4
JS
6827 {
6828 destStyle.SetFont(wxFont(style.GetFontSize(), destStyle.GetFont().Ok() ? destStyle.GetFont().GetFamily() : wxDEFAULT,
6829 style.GetFontStyle(), style.GetFontWeight(), style.GetFontUnderlined(), style.GetFontFaceName()));
6830 }
6831 else if (style.GetFlags() & (wxTEXT_ATTR_FONT))
6832 {
6833 wxFont font = destStyle.GetFont();
6834
6835 if (style.GetFlags() & wxTEXT_ATTR_FONT_FACE)
fe5aa22c 6836 {
59509217
JS
6837 if (compareWith && compareWith->HasFaceName() && compareWith->GetFontFaceName() == style.GetFontFaceName())
6838 {
6839 // The same as currently displayed, so don't set
6840 }
6841 else
6842 {
6843 destStyle.SetFlags(destStyle.GetFlags() | wxTEXT_ATTR_FONT_FACE);
6844 font.SetFaceName(style.GetFontFaceName());
6845 }
fe5aa22c 6846 }
7fe8059f 6847
5d7836c4 6848 if (style.GetFlags() & wxTEXT_ATTR_FONT_SIZE)
fe5aa22c 6849 {
59509217
JS
6850 if (compareWith && compareWith->HasSize() && compareWith->GetFontSize() == style.GetFontSize())
6851 {
6852 // The same as currently displayed, so don't set
6853 }
6854 else
6855 {
6856 destStyle.SetFlags(destStyle.GetFlags() | wxTEXT_ATTR_FONT_SIZE);
6857 font.SetPointSize(style.GetFontSize());
6858 }
fe5aa22c 6859 }
7fe8059f 6860
5d7836c4 6861 if (style.GetFlags() & wxTEXT_ATTR_FONT_ITALIC)
fe5aa22c 6862 {
59509217
JS
6863 if (compareWith && compareWith->HasItalic() && compareWith->GetFontStyle() == style.GetFontStyle())
6864 {
6865 // The same as currently displayed, so don't set
6866 }
6867 else
6868 {
6869 destStyle.SetFlags(destStyle.GetFlags() | wxTEXT_ATTR_FONT_ITALIC);
6870 font.SetStyle(style.GetFontStyle());
6871 }
fe5aa22c 6872 }
7fe8059f 6873
5d7836c4 6874 if (style.GetFlags() & wxTEXT_ATTR_FONT_WEIGHT)
fe5aa22c 6875 {
59509217
JS
6876 if (compareWith && compareWith->HasWeight() && compareWith->GetFontWeight() == style.GetFontWeight())
6877 {
6878 // The same as currently displayed, so don't set
6879 }
6880 else
6881 {
6882 destStyle.SetFlags(destStyle.GetFlags() | wxTEXT_ATTR_FONT_WEIGHT);
6883 font.SetWeight(style.GetFontWeight());
6884 }
fe5aa22c 6885 }
7fe8059f 6886
5d7836c4 6887 if (style.GetFlags() & wxTEXT_ATTR_FONT_UNDERLINE)
fe5aa22c 6888 {
59509217
JS
6889 if (compareWith && compareWith->HasUnderlined() && compareWith->GetFontUnderlined() == style.GetFontUnderlined())
6890 {
6891 // The same as currently displayed, so don't set
6892 }
6893 else
6894 {
6895 destStyle.SetFlags(destStyle.GetFlags() | wxTEXT_ATTR_FONT_UNDERLINE);
6896 font.SetUnderlined(style.GetFontUnderlined());
6897 }
fe5aa22c 6898 }
5d7836c4
JS
6899
6900 if (font != destStyle.GetFont())
fe5aa22c
JS
6901 {
6902 int oldFlags = destStyle.GetFlags();
6903
5d7836c4 6904 destStyle.SetFont(font);
fe5aa22c
JS
6905
6906 destStyle.SetFlags(oldFlags);
6907 }
5d7836c4
JS
6908 }
6909
59509217
JS
6910 if (style.GetTextColour().Ok() && style.HasTextColour())
6911 {
6912 if (!(compareWith && compareWith->HasTextColour() && compareWith->GetTextColour() == style.GetTextColour()))
6913 destStyle.SetTextColour(style.GetTextColour());
6914 }
5d7836c4 6915
59509217
JS
6916 if (style.GetBackgroundColour().Ok() && style.HasBackgroundColour())
6917 {
6918 if (!(compareWith && compareWith->HasBackgroundColour() && compareWith->GetBackgroundColour() == style.GetBackgroundColour()))
6919 destStyle.SetBackgroundColour(style.GetBackgroundColour());
6920 }
5d7836c4
JS
6921
6922 if (style.HasAlignment())
59509217
JS
6923 {
6924 if (!(compareWith && compareWith->HasAlignment() && compareWith->GetAlignment() == style.GetAlignment()))
6925 destStyle.SetAlignment(style.GetAlignment());
6926 }
5d7836c4
JS
6927
6928 if (style.HasTabs())
59509217
JS
6929 {
6930 if (!(compareWith && compareWith->HasTabs() && wxRichTextTabsEq(compareWith->GetTabs(), style.GetTabs())))
6931 destStyle.SetTabs(style.GetTabs());
6932 }
5d7836c4
JS
6933
6934 if (style.HasLeftIndent())
59509217
JS
6935 {
6936 if (!(compareWith && compareWith->HasLeftIndent() && compareWith->GetLeftIndent() == style.GetLeftIndent()
6937 && compareWith->GetLeftSubIndent() == style.GetLeftSubIndent()))
6938 destStyle.SetLeftIndent(style.GetLeftIndent(), style.GetLeftSubIndent());
6939 }
5d7836c4
JS
6940
6941 if (style.HasRightIndent())
59509217
JS
6942 {
6943 if (!(compareWith && compareWith->HasRightIndent() && compareWith->GetRightIndent() == style.GetRightIndent()))
6944 destStyle.SetRightIndent(style.GetRightIndent());
6945 }
5d7836c4
JS
6946
6947 if (style.HasParagraphSpacingAfter())
59509217
JS
6948 {
6949 if (!(compareWith && compareWith->HasParagraphSpacingAfter() && compareWith->GetParagraphSpacingAfter() == style.GetParagraphSpacingAfter()))
6950 destStyle.SetParagraphSpacingAfter(style.GetParagraphSpacingAfter());
6951 }
5d7836c4
JS
6952
6953 if (style.HasParagraphSpacingBefore())
59509217
JS
6954 {
6955 if (!(compareWith && compareWith->HasParagraphSpacingBefore() && compareWith->GetParagraphSpacingBefore() == style.GetParagraphSpacingBefore()))
6956 destStyle.SetParagraphSpacingBefore(style.GetParagraphSpacingBefore());
6957 }
5d7836c4
JS
6958
6959 if (style.HasLineSpacing())
59509217
JS
6960 {
6961 if (!(compareWith && compareWith->HasLineSpacing() && compareWith->GetLineSpacing() == style.GetLineSpacing()))
6962 destStyle.SetLineSpacing(style.GetLineSpacing());
6963 }
5d7836c4
JS
6964
6965 if (style.HasCharacterStyleName())
59509217
JS
6966 {
6967 if (!(compareWith && compareWith->HasCharacterStyleName() && compareWith->GetCharacterStyleName() == style.GetCharacterStyleName()))
6968 destStyle.SetCharacterStyleName(style.GetCharacterStyleName());
6969 }
5d7836c4
JS
6970
6971 if (style.HasParagraphStyleName())
59509217
JS
6972 {
6973 if (!(compareWith && compareWith->HasParagraphStyleName() && compareWith->GetParagraphStyleName() == style.GetParagraphStyleName()))
6974 destStyle.SetParagraphStyleName(style.GetParagraphStyleName());
6975 }
5d7836c4 6976
38f833b1
JS
6977 if (style.HasListStyleName())
6978 {
6979 if (!(compareWith && compareWith->HasListStyleName() && compareWith->GetListStyleName() == style.GetListStyleName()))
6980 destStyle.SetListStyleName(style.GetListStyleName());
6981 }
6982
5d7836c4
JS
6983 if (style.HasBulletStyle())
6984 {
59509217
JS
6985 if (!(compareWith && compareWith->HasBulletStyle() && compareWith->GetBulletStyle() == style.GetBulletStyle()))
6986 destStyle.SetBulletStyle(style.GetBulletStyle());
6987 }
6988
d2d0adc7 6989 if (style.HasBulletText())
59509217 6990 {
d2d0adc7 6991 if (!(compareWith && compareWith->HasBulletText() && compareWith->GetBulletText() == style.GetBulletText()))
59509217 6992 {
d2d0adc7 6993 destStyle.SetBulletText(style.GetBulletText());
59509217
JS
6994 destStyle.SetBulletFont(style.GetBulletFont());
6995 }
5d7836c4
JS
6996 }
6997
6998 if (style.HasBulletNumber())
59509217
JS
6999 {
7000 if (!(compareWith && compareWith->HasBulletNumber() && compareWith->GetBulletNumber() == style.GetBulletNumber()))
7001 destStyle.SetBulletNumber(style.GetBulletNumber());
7002 }
5d7836c4 7003
f089713f
JS
7004 if (style.HasBulletName())
7005 {
7006 if (!(compareWith && compareWith->HasBulletName() && compareWith->GetBulletName() == style.GetBulletName()))
7007 destStyle.SetBulletName(style.GetBulletName());
7008 }
7009
d2d0adc7
JS
7010 if (style.HasURL())
7011 {
7012 if (!(compareWith && compareWith->HasURL() && compareWith->GetURL() == style.GetURL()))
7013 destStyle.SetURL(style.GetURL());
7014 }
7015
ea160b2e
JS
7016 if (style.HasPageBreak())
7017 {
7018 if (!(compareWith && compareWith->HasPageBreak()))
7019 destStyle.SetPageBreak();
7020 }
7021
42688aea
JS
7022 if (style.HasTextEffects())
7023 {
7024 if (!(compareWith && compareWith->HasTextEffects() && compareWith->GetTextEffects() == style.GetTextEffects()))
7025 {
7026 int destBits = destStyle.GetTextEffects();
7027 int destFlags = destStyle.GetTextEffectFlags();
7028
7029 int srcBits = style.GetTextEffects();
7030 int srcFlags = style.GetTextEffectFlags();
7031
7032 wxRichTextCombineBitlists(destBits, srcBits, destFlags, srcFlags);
7033
7034 destStyle.SetTextEffects(destBits);
7035 destStyle.SetTextEffectFlags(destFlags);
7036 }
7037 }
7038
4d6d8bf4
JS
7039 if (style.HasOutlineLevel())
7040 {
7041 if (!(compareWith && compareWith->HasOutlineLevel() && compareWith->GetOutlineLevel() == style.GetOutlineLevel()))
7042 destStyle.SetOutlineLevel(style.GetOutlineLevel());
7043 }
7044
5d7836c4
JS
7045 return true;
7046}
7047
42688aea
JS
7048/// Combine two bitlists, specifying the bits of interest with separate flags.
7049bool wxRichTextCombineBitlists(int& valueA, int valueB, int& flagsA, int flagsB)
7050{
7051 // We want to apply B's bits to A, taking into account each's flags which indicate which bits
7052 // are to be taken into account. A zero in B's bits should reset that bit in A but only if B's flags
7053 // indicate it.
7054
7055 // First, reset the 0 bits from B. We make a mask so we're only dealing with B's zero
7056 // bits at this point, ignoring any 1 bits in B or 0 bits in B that are not relevant.
7057 int valueA2 = ~(~valueB & flagsB) & valueA;
7058
7059 // Now combine the 1 bits.
7060 int valueA3 = (valueB & flagsB) | valueA2;
7061
7062 valueA = valueA3;
7063 flagsA = (flagsA | flagsB);
7064
7065 return true;
7066}
7067
7068/// Compare two bitlists
7069bool wxRichTextBitlistsEqPartial(int valueA, int valueB, int flags)
7070{
7071 int relevantBitsA = valueA & flags;
7072 int relevantBitsB = valueB & flags;
7073 return (relevantBitsA != relevantBitsB);
7074}
7075
4f32b3cf
JS
7076/// Split into paragraph and character styles
7077bool wxRichTextSplitParaCharStyles(const wxTextAttrEx& style, wxTextAttrEx& parStyle, wxTextAttrEx& charStyle)
7078{
7079 wxTextAttrEx defaultCharStyle1(style);
7080 wxTextAttrEx defaultParaStyle1(style);
7081 defaultCharStyle1.SetFlags(defaultCharStyle1.GetFlags()&wxTEXT_ATTR_CHARACTER);
7082 defaultParaStyle1.SetFlags(defaultParaStyle1.GetFlags()&wxTEXT_ATTR_PARAGRAPH);
7083
7084 wxRichTextApplyStyle(charStyle, defaultCharStyle1);
7085 wxRichTextApplyStyle(parStyle, defaultParaStyle1);
7086
7087 return true;
7088}
7089
59509217
JS
7090void wxSetFontPreservingStyles(wxTextAttr& attr, const wxFont& font)
7091{
7092 long flags = attr.GetFlags();
7093 attr.SetFont(font);
7094 attr.SetFlags(flags);
7095}
7096
7097/// Convert a decimal to Roman numerals
7098wxString wxRichTextDecimalToRoman(long n)
7099{
7100 static wxArrayInt decimalNumbers;
7101 static wxArrayString romanNumbers;
7102
7103 // Clean up arrays
7104 if (n == -1)
7105 {
7106 decimalNumbers.Clear();
7107 romanNumbers.Clear();
7108 return wxEmptyString;
7109 }
7110
7111 if (decimalNumbers.GetCount() == 0)
7112 {
7113 #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
7114
7115 wxRichTextAddDecRom(1000, wxT("M"));
7116 wxRichTextAddDecRom(900, wxT("CM"));
7117 wxRichTextAddDecRom(500, wxT("D"));
7118 wxRichTextAddDecRom(400, wxT("CD"));
7119 wxRichTextAddDecRom(100, wxT("C"));
7120 wxRichTextAddDecRom(90, wxT("XC"));
7121 wxRichTextAddDecRom(50, wxT("L"));
7122 wxRichTextAddDecRom(40, wxT("XL"));
7123 wxRichTextAddDecRom(10, wxT("X"));
7124 wxRichTextAddDecRom(9, wxT("IX"));
7125 wxRichTextAddDecRom(5, wxT("V"));
7126 wxRichTextAddDecRom(4, wxT("IV"));
7127 wxRichTextAddDecRom(1, wxT("I"));
7128 }
7129
7130 int i = 0;
7131 wxString roman;
7132
7133 while (n > 0 && i < 13)
7134 {
7135 if (n >= decimalNumbers[i])
7136 {
7137 n -= decimalNumbers[i];
7138 roman += romanNumbers[i];
7139 }
7140 else
7141 {
7142 i ++;
7143 }
7144 }
7145 if (roman.IsEmpty())
7146 roman = wxT("0");
7147 return roman;
7148}
7149
5d7836c4
JS
7150/*!
7151 * wxRichTextAttr stores attributes without a wxFont object, so is a much more
7152 * efficient way to query styles.
7153 */
7154
7155// ctors
7156wxRichTextAttr::wxRichTextAttr(const wxColour& colText,
7157 const wxColour& colBack,
7158 wxTextAttrAlignment alignment): m_textAlignment(alignment), m_colText(colText), m_colBack(colBack)
7159{
7160 Init();
7161
7162 if (m_colText.Ok()) m_flags |= wxTEXT_ATTR_TEXT_COLOUR;
7163 if (m_colBack.Ok()) m_flags |= wxTEXT_ATTR_BACKGROUND_COLOUR;
7164 if (alignment != wxTEXT_ALIGNMENT_DEFAULT)
7165 m_flags |= wxTEXT_ATTR_ALIGNMENT;
7166}
7167
7168wxRichTextAttr::wxRichTextAttr(const wxTextAttrEx& attr)
7169{
7170 Init();
7171
7172 (*this) = attr;
7173}
7174
d2d0adc7
JS
7175wxRichTextAttr::wxRichTextAttr(const wxRichTextAttr& attr)
7176{
7177 Copy(attr);
7178}
7179
5d7836c4
JS
7180// operations
7181void wxRichTextAttr::Init()
7182{
7183 m_textAlignment = wxTEXT_ALIGNMENT_DEFAULT;
7184 m_flags = 0;
7185 m_leftIndent = 0;
7186 m_leftSubIndent = 0;
7187 m_rightIndent = 0;
7188
7189 m_fontSize = 12;
7190 m_fontStyle = wxNORMAL;
7191 m_fontWeight = wxNORMAL;
7192 m_fontUnderlined = false;
7193
7194 m_paragraphSpacingAfter = 0;
7195 m_paragraphSpacingBefore = 0;
7196 m_lineSpacing = 0;
7197 m_bulletStyle = wxTEXT_ATTR_BULLET_STYLE_NONE;
42688aea
JS
7198 m_textEffects = wxTEXT_ATTR_EFFECT_NONE;
7199 m_textEffectFlags = wxTEXT_ATTR_EFFECT_NONE;
4d6d8bf4 7200 m_outlineLevel = 0;
5d7836c4 7201 m_bulletNumber = 0;
5d7836c4
JS
7202}
7203
d2d0adc7
JS
7204// Copy
7205void wxRichTextAttr::Copy(const wxRichTextAttr& attr)
5d7836c4
JS
7206{
7207 m_colText = attr.m_colText;
7208 m_colBack = attr.m_colBack;
7209 m_textAlignment = attr.m_textAlignment;
7210 m_leftIndent = attr.m_leftIndent;
7211 m_leftSubIndent = attr.m_leftSubIndent;
7212 m_rightIndent = attr.m_rightIndent;
7213 m_tabs = attr.m_tabs;
7214 m_flags = attr.m_flags;
7215
7216 m_fontSize = attr.m_fontSize;
7217 m_fontStyle = attr.m_fontStyle;
7218 m_fontWeight = attr.m_fontWeight;
7219 m_fontUnderlined = attr.m_fontUnderlined;
7220 m_fontFaceName = attr.m_fontFaceName;
42688aea
JS
7221 m_textEffects = attr.m_textEffects;
7222 m_textEffectFlags = attr.m_textEffectFlags;
5d7836c4
JS
7223
7224 m_paragraphSpacingAfter = attr.m_paragraphSpacingAfter;
7225 m_paragraphSpacingBefore = attr.m_paragraphSpacingBefore;
7226 m_lineSpacing = attr.m_lineSpacing;
7227 m_characterStyleName = attr.m_characterStyleName;
7228 m_paragraphStyleName = attr.m_paragraphStyleName;
38f833b1 7229 m_listStyleName = attr.m_listStyleName;
5d7836c4
JS
7230 m_bulletStyle = attr.m_bulletStyle;
7231 m_bulletNumber = attr.m_bulletNumber;
d2d0adc7 7232 m_bulletText = attr.m_bulletText;
59509217 7233 m_bulletFont = attr.m_bulletFont;
f089713f 7234 m_bulletName = attr.m_bulletName;
4d6d8bf4 7235 m_outlineLevel = attr.m_outlineLevel;
d2d0adc7
JS
7236
7237 m_urlTarget = attr.m_urlTarget;
7238}
7239
7240// operators
7241void wxRichTextAttr::operator= (const wxRichTextAttr& attr)
7242{
7243 Copy(attr);
5d7836c4
JS
7244}
7245
7246// operators
7247void wxRichTextAttr::operator= (const wxTextAttrEx& attr)
7248{
42688aea
JS
7249 m_flags = attr.GetFlags();
7250
5d7836c4
JS
7251 m_colText = attr.GetTextColour();
7252 m_colBack = attr.GetBackgroundColour();
7253 m_textAlignment = attr.GetAlignment();
7254 m_leftIndent = attr.GetLeftIndent();
7255 m_leftSubIndent = attr.GetLeftSubIndent();
7256 m_rightIndent = attr.GetRightIndent();
7257 m_tabs = attr.GetTabs();
42688aea
JS
7258 m_textEffects = attr.GetTextEffects();
7259 m_textEffectFlags = attr.GetTextEffectFlags();
5d7836c4
JS
7260
7261 m_paragraphSpacingAfter = attr.GetParagraphSpacingAfter();
7262 m_paragraphSpacingBefore = attr.GetParagraphSpacingBefore();
7263 m_lineSpacing = attr.GetLineSpacing();
7264 m_characterStyleName = attr.GetCharacterStyleName();
7265 m_paragraphStyleName = attr.GetParagraphStyleName();
38f833b1 7266 m_listStyleName = attr.GetListStyleName();
59509217
JS
7267 m_bulletStyle = attr.GetBulletStyle();
7268 m_bulletNumber = attr.GetBulletNumber();
d2d0adc7 7269 m_bulletText = attr.GetBulletText();
f089713f 7270 m_bulletName = attr.GetBulletName();
59509217 7271 m_bulletFont = attr.GetBulletFont();
4d6d8bf4 7272 m_outlineLevel = attr.GetOutlineLevel();
5d7836c4 7273
d2d0adc7
JS
7274 m_urlTarget = attr.GetURL();
7275
5d7836c4
JS
7276 if (attr.GetFont().Ok())
7277 GetFontAttributes(attr.GetFont());
7278}
7279
7280// Making a wxTextAttrEx object.
7281wxRichTextAttr::operator wxTextAttrEx () const
7282{
7283 wxTextAttrEx attr;
4d6d8bf4
JS
7284 attr.SetTextColour(GetTextColour());
7285 attr.SetBackgroundColour(GetBackgroundColour());
7286 attr.SetAlignment(GetAlignment());
7287 attr.SetTabs(GetTabs());
7288 attr.SetLeftIndent(GetLeftIndent(), GetLeftSubIndent());
7289 attr.SetRightIndent(GetRightIndent());
7290 attr.SetFont(CreateFont());
7291
7292 attr.SetParagraphSpacingAfter(m_paragraphSpacingAfter);
7293 attr.SetParagraphSpacingBefore(m_paragraphSpacingBefore);
7294 attr.SetLineSpacing(m_lineSpacing);
7295 attr.SetBulletStyle(m_bulletStyle);
7296 attr.SetBulletNumber(m_bulletNumber);
7297 attr.SetBulletText(m_bulletText);
7298 attr.SetBulletName(m_bulletName);
7299 attr.SetBulletFont(m_bulletFont);
7300 attr.SetCharacterStyleName(m_characterStyleName);
7301 attr.SetParagraphStyleName(m_paragraphStyleName);
7302 attr.SetListStyleName(m_listStyleName);
7303 attr.SetTextEffects(m_textEffects);
7304 attr.SetTextEffectFlags(m_textEffectFlags);
7305 attr.SetOutlineLevel(m_outlineLevel);
7306
7307 attr.SetURL(m_urlTarget);
7308
7309 attr.SetFlags(GetFlags()); // Important: set after SetFont and others, since they set flags
5d7836c4
JS
7310 return attr;
7311}
7312
fe5aa22c
JS
7313// Equality test
7314bool wxRichTextAttr::operator== (const wxRichTextAttr& attr) const
7315{
7316 return GetFlags() == attr.GetFlags() &&
7317
7318 GetTextColour() == attr.GetTextColour() &&
7319 GetBackgroundColour() == attr.GetBackgroundColour() &&
7320
7321 GetAlignment() == attr.GetAlignment() &&
7322 GetLeftIndent() == attr.GetLeftIndent() &&
7323 GetLeftSubIndent() == attr.GetLeftSubIndent() &&
7324 GetRightIndent() == attr.GetRightIndent() &&
59509217 7325 wxRichTextTabsEq(GetTabs(), attr.GetTabs()) &&
fe5aa22c
JS
7326
7327 GetParagraphSpacingAfter() == attr.GetParagraphSpacingAfter() &&
7328 GetParagraphSpacingBefore() == attr.GetParagraphSpacingBefore() &&
7329 GetLineSpacing() == attr.GetLineSpacing() &&
7330 GetCharacterStyleName() == attr.GetCharacterStyleName() &&
7331 GetParagraphStyleName() == attr.GetParagraphStyleName() &&
38f833b1 7332 GetListStyleName() == attr.GetListStyleName() &&
fe5aa22c 7333
59509217 7334 GetBulletStyle() == attr.GetBulletStyle() &&
d2d0adc7 7335 GetBulletText() == attr.GetBulletText() &&
59509217
JS
7336 GetBulletNumber() == attr.GetBulletNumber() &&
7337 GetBulletFont() == attr.GetBulletFont() &&
f089713f 7338 GetBulletName() == attr.GetBulletName() &&
59509217 7339
42688aea
JS
7340 GetTextEffects() == attr.GetTextEffects() &&
7341 GetTextEffectFlags() == attr.GetTextEffectFlags() &&
7342
4d6d8bf4 7343 GetOutlineLevel() == attr.GetOutlineLevel() &&
d2d0adc7 7344
4d6d8bf4
JS
7345 GetFontSize() == attr.GetFontSize() &&
7346 GetFontStyle() == attr.GetFontStyle() &&
7347 GetFontWeight() == attr.GetFontWeight() &&
7348 GetFontUnderlined() == attr.GetFontUnderlined() &&
7349 GetFontFaceName() == attr.GetFontFaceName() &&
7350
7351 GetURL() == attr.GetURL();
5d7836c4
JS
7352}
7353
7354// Create font from font attributes.
7355wxFont wxRichTextAttr::CreateFont() const
7356{
7357 wxFont font(m_fontSize, wxDEFAULT, m_fontStyle, m_fontWeight, m_fontUnderlined, m_fontFaceName);
ff2baa25
JS
7358#ifdef __WXMAC__
7359 font.SetNoAntiAliasing(true);
7360#endif
5d7836c4
JS
7361 return font;
7362}
7363
7364// Get attributes from font.
7365bool wxRichTextAttr::GetFontAttributes(const wxFont& font)
7366{
7367 if (!font.Ok())
7368 return false;
7369
7370 m_fontSize = font.GetPointSize();
7371 m_fontStyle = font.GetStyle();
7372 m_fontWeight = font.GetWeight();
7373 m_fontUnderlined = font.GetUnderlined();
7374 m_fontFaceName = font.GetFaceName();
7375
7376 return true;
7377}
7378
05b4fddf
JS
7379wxRichTextAttr wxRichTextAttr::Combine(const wxRichTextAttr& attr,
7380 const wxRichTextAttr& attrDef,
7381 const wxTextCtrlBase *text)
61399247 7382{
05b4fddf
JS
7383 wxColour colFg = attr.GetTextColour();
7384 if ( !colFg.Ok() )
7385 {
7386 colFg = attrDef.GetTextColour();
7387
7388 if ( text && !colFg.Ok() )
7389 colFg = text->GetForegroundColour();
7390 }
7391
7392 wxColour colBg = attr.GetBackgroundColour();
7393 if ( !colBg.Ok() )
7394 {
7395 colBg = attrDef.GetBackgroundColour();
7396
7397 if ( text && !colBg.Ok() )
7398 colBg = text->GetBackgroundColour();
7399 }
7400
7401 wxRichTextAttr newAttr(colFg, colBg);
7402
7403 if (attr.HasWeight())
7404 newAttr.SetFontWeight(attr.GetFontWeight());
61399247 7405
05b4fddf
JS
7406 if (attr.HasSize())
7407 newAttr.SetFontSize(attr.GetFontSize());
61399247 7408
05b4fddf
JS
7409 if (attr.HasItalic())
7410 newAttr.SetFontStyle(attr.GetFontStyle());
61399247 7411
05b4fddf
JS
7412 if (attr.HasUnderlined())
7413 newAttr.SetFontUnderlined(attr.GetFontUnderlined());
61399247 7414
05b4fddf
JS
7415 if (attr.HasFaceName())
7416 newAttr.SetFontFaceName(attr.GetFontFaceName());
61399247 7417
05b4fddf
JS
7418 if (attr.HasAlignment())
7419 newAttr.SetAlignment(attr.GetAlignment());
7420 else if (attrDef.HasAlignment())
7421 newAttr.SetAlignment(attrDef.GetAlignment());
7422
7423 if (attr.HasTabs())
7424 newAttr.SetTabs(attr.GetTabs());
7425 else if (attrDef.HasTabs())
7426 newAttr.SetTabs(attrDef.GetTabs());
7427
7428 if (attr.HasLeftIndent())
7429 newAttr.SetLeftIndent(attr.GetLeftIndent(), attr.GetLeftSubIndent());
7430 else if (attrDef.HasLeftIndent())
7431 newAttr.SetLeftIndent(attrDef.GetLeftIndent(), attr.GetLeftSubIndent());
7432
7433 if (attr.HasRightIndent())
7434 newAttr.SetRightIndent(attr.GetRightIndent());
7435 else if (attrDef.HasRightIndent())
7436 newAttr.SetRightIndent(attrDef.GetRightIndent());
61399247 7437
05b4fddf 7438 // NEW ATTRIBUTES
61399247 7439
05b4fddf
JS
7440 if (attr.HasParagraphSpacingAfter())
7441 newAttr.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter());
61399247 7442
05b4fddf
JS
7443 if (attr.HasParagraphSpacingBefore())
7444 newAttr.SetParagraphSpacingBefore(attr.GetParagraphSpacingBefore());
7445
7446 if (attr.HasLineSpacing())
7447 newAttr.SetLineSpacing(attr.GetLineSpacing());
61399247 7448
05b4fddf
JS
7449 if (attr.HasCharacterStyleName())
7450 newAttr.SetCharacterStyleName(attr.GetCharacterStyleName());
61399247 7451
05b4fddf
JS
7452 if (attr.HasParagraphStyleName())
7453 newAttr.SetParagraphStyleName(attr.GetParagraphStyleName());
61399247 7454
38f833b1
JS
7455 if (attr.HasListStyleName())
7456 newAttr.SetListStyleName(attr.GetListStyleName());
7457
05b4fddf
JS
7458 if (attr.HasBulletStyle())
7459 newAttr.SetBulletStyle(attr.GetBulletStyle());
7460
7461 if (attr.HasBulletNumber())
7462 newAttr.SetBulletNumber(attr.GetBulletNumber());
61399247 7463
f089713f
JS
7464 if (attr.HasBulletName())
7465 newAttr.SetBulletName(attr.GetBulletName());
7466
d2d0adc7 7467 if (attr.HasBulletText())
59509217 7468 {
d2d0adc7 7469 newAttr.SetBulletText(attr.GetBulletText());
59509217
JS
7470 newAttr.SetBulletFont(attr.GetBulletFont());
7471 }
61399247 7472
d2d0adc7
JS
7473 if (attr.HasURL())
7474 newAttr.SetURL(attr.GetURL());
7475
ea160b2e
JS
7476 if (attr.HasPageBreak())
7477 newAttr.SetPageBreak();
7478
42688aea
JS
7479 if (attr.HasTextEffects())
7480 {
7481 newAttr.SetTextEffects(attr.GetTextEffects());
7482 newAttr.SetTextEffectFlags(attr.GetTextEffectFlags());
7483 }
7484
4d6d8bf4
JS
7485 if (attr.HasOutlineLevel())
7486 newAttr.SetOutlineLevel(attr.GetOutlineLevel());
7487
05b4fddf
JS
7488 return newAttr;
7489}
7490
5d7836c4
JS
7491/*!
7492 * wxTextAttrEx is an extended version of wxTextAttr with more paragraph attributes.
7493 */
7494
6a817343 7495wxTextAttrEx::wxTextAttrEx(const wxTextAttrEx& attr): wxTextAttr()
5d7836c4 7496{
d2d0adc7 7497 Copy(attr);
5d7836c4
JS
7498}
7499
7500// Initialise this object.
7501void wxTextAttrEx::Init()
7502{
7503 m_paragraphSpacingAfter = 0;
7504 m_paragraphSpacingBefore = 0;
7505 m_lineSpacing = 0;
7506 m_bulletStyle = wxTEXT_ATTR_BULLET_STYLE_NONE;
42688aea
JS
7507 m_textEffects = wxTEXT_ATTR_EFFECT_NONE;
7508 m_textEffectFlags = wxTEXT_ATTR_EFFECT_NONE;
5d7836c4 7509 m_bulletNumber = 0;
4d6d8bf4 7510 m_outlineLevel = 0;
5d7836c4
JS
7511}
7512
d2d0adc7
JS
7513// Copy
7514void wxTextAttrEx::Copy(const wxTextAttrEx& attr)
5d7836c4
JS
7515{
7516 wxTextAttr::operator= (attr);
7517
7518 m_paragraphSpacingAfter = attr.m_paragraphSpacingAfter;
7519 m_paragraphSpacingBefore = attr.m_paragraphSpacingBefore;
7520 m_lineSpacing = attr.m_lineSpacing;
7521 m_characterStyleName = attr.m_characterStyleName;
7522 m_paragraphStyleName = attr.m_paragraphStyleName;
38f833b1 7523 m_listStyleName = attr.m_listStyleName;
5d7836c4
JS
7524 m_bulletStyle = attr.m_bulletStyle;
7525 m_bulletNumber = attr.m_bulletNumber;
d2d0adc7 7526 m_bulletText = attr.m_bulletText;
59509217 7527 m_bulletFont = attr.m_bulletFont;
f089713f 7528 m_bulletName = attr.m_bulletName;
d2d0adc7 7529 m_urlTarget = attr.m_urlTarget;
42688aea
JS
7530 m_textEffects = attr.m_textEffects;
7531 m_textEffectFlags = attr.m_textEffectFlags;
4d6d8bf4 7532 m_outlineLevel = attr.m_outlineLevel;
d2d0adc7
JS
7533}
7534
7535// Assignment from a wxTextAttrEx object
7536void wxTextAttrEx::operator= (const wxTextAttrEx& attr)
7537{
7538 Copy(attr);
5d7836c4
JS
7539}
7540
7541// Assignment from a wxTextAttr object.
7542void wxTextAttrEx::operator= (const wxTextAttr& attr)
7543{
7544 wxTextAttr::operator= (attr);
7545}
7546
38f833b1
JS
7547// Equality test
7548bool wxTextAttrEx::operator== (const wxTextAttrEx& attr) const
7549{
7550 return (
4d6d8bf4 7551 GetFlags() == attr.GetFlags() &&
38f833b1
JS
7552 GetTextColour() == attr.GetTextColour() &&
7553 GetBackgroundColour() == attr.GetBackgroundColour() &&
7554 GetFont() == attr.GetFont() &&
42688aea 7555 GetTextEffects() == attr.GetTextEffects() &&
4d6d8bf4 7556 GetTextEffectFlags() == attr.GetTextEffectFlags() &&
38f833b1
JS
7557 GetAlignment() == attr.GetAlignment() &&
7558 GetLeftIndent() == attr.GetLeftIndent() &&
7559 GetRightIndent() == attr.GetRightIndent() &&
7560 GetLeftSubIndent() == attr.GetLeftSubIndent() &&
7561 wxRichTextTabsEq(GetTabs(), attr.GetTabs()) &&
7562 GetLineSpacing() == attr.GetLineSpacing() &&
7563 GetParagraphSpacingAfter() == attr.GetParagraphSpacingAfter() &&
7564 GetParagraphSpacingBefore() == attr.GetParagraphSpacingBefore() &&
7565 GetBulletStyle() == attr.GetBulletStyle() &&
7566 GetBulletNumber() == attr.GetBulletNumber() &&
d2d0adc7 7567 GetBulletText() == attr.GetBulletText() &&
f089713f 7568 GetBulletName() == attr.GetBulletName() &&
38f833b1
JS
7569 GetBulletFont() == attr.GetBulletFont() &&
7570 GetCharacterStyleName() == attr.GetCharacterStyleName() &&
7571 GetParagraphStyleName() == attr.GetParagraphStyleName() &&
d2d0adc7 7572 GetListStyleName() == attr.GetListStyleName() &&
4d6d8bf4 7573 GetOutlineLevel() == attr.GetOutlineLevel() &&
d2d0adc7 7574 GetURL() == attr.GetURL());
38f833b1
JS
7575}
7576
05b4fddf
JS
7577wxTextAttrEx wxTextAttrEx::CombineEx(const wxTextAttrEx& attr,
7578 const wxTextAttrEx& attrDef,
7579 const wxTextCtrlBase *text)
61399247 7580{
05b4fddf
JS
7581 wxTextAttrEx newAttr;
7582
7583 // If attr specifies the complete font, just use that font, overriding all
7584 // default font attributes.
7585 if ((attr.GetFlags() & wxTEXT_ATTR_FONT) == wxTEXT_ATTR_FONT)
7586 newAttr.SetFont(attr.GetFont());
7587 else
7588 {
7589 // First find the basic, default font
7590 long flags = 0;
7591
7592 wxFont font;
7593 if (attrDef.HasFont())
7594 {
7595 flags = (attrDef.GetFlags() & wxTEXT_ATTR_FONT);
7596 font = attrDef.GetFont();
7597 }
7598 else
7599 {
7600 if (text)
7601 font = text->GetFont();
7602
7603 // We leave flags at 0 because no font attributes have been specified yet
7604 }
7605 if (!font.Ok())
7606 font = *wxNORMAL_FONT;
7607
7608 // Otherwise, if there are font attributes in attr, apply them
59509217 7609 if (attr.GetFlags() & wxTEXT_ATTR_FONT)
05b4fddf
JS
7610 {
7611 if (attr.HasSize())
7612 {
7613 flags |= wxTEXT_ATTR_FONT_SIZE;
7614 font.SetPointSize(attr.GetFont().GetPointSize());
7615 }
7616 if (attr.HasItalic())
7617 {
7618 flags |= wxTEXT_ATTR_FONT_ITALIC;;
7619 font.SetStyle(attr.GetFont().GetStyle());
7620 }
7621 if (attr.HasWeight())
7622 {
7623 flags |= wxTEXT_ATTR_FONT_WEIGHT;
7624 font.SetWeight(attr.GetFont().GetWeight());
7625 }
7626 if (attr.HasFaceName())
7627 {
7628 flags |= wxTEXT_ATTR_FONT_FACE;
7629 font.SetFaceName(attr.GetFont().GetFaceName());
7630 }
7631 if (attr.HasUnderlined())
7632 {
7633 flags |= wxTEXT_ATTR_FONT_UNDERLINE;
7634 font.SetUnderlined(attr.GetFont().GetUnderlined());
7635 }
7636 newAttr.SetFont(font);
7637 newAttr.SetFlags(newAttr.GetFlags()|flags);
7638 }
7639 }
7640
7641 // TODO: should really check we are specifying these in the flags,
7642 // before setting them, as per above; or we will set them willy-nilly.
7643 // However, we should also check whether this is the intention
7644 // as per wxTextAttr::Combine, i.e. always to have valid colours
7645 // in the style.
7646 wxColour colFg = attr.GetTextColour();
7647 if ( !colFg.Ok() )
7648 {
7649 colFg = attrDef.GetTextColour();
7650
7651 if ( text && !colFg.Ok() )
7652 colFg = text->GetForegroundColour();
7653 }
7654
7655 wxColour colBg = attr.GetBackgroundColour();
7656 if ( !colBg.Ok() )
7657 {
7658 colBg = attrDef.GetBackgroundColour();
7659
7660 if ( text && !colBg.Ok() )
7661 colBg = text->GetBackgroundColour();
7662 }
7663
7664 newAttr.SetTextColour(colFg);
7665 newAttr.SetBackgroundColour(colBg);
7666
7667 if (attr.HasAlignment())
7668 newAttr.SetAlignment(attr.GetAlignment());
7669 else if (attrDef.HasAlignment())
7670 newAttr.SetAlignment(attrDef.GetAlignment());
7671
7672 if (attr.HasTabs())
7673 newAttr.SetTabs(attr.GetTabs());
7674 else if (attrDef.HasTabs())
7675 newAttr.SetTabs(attrDef.GetTabs());
7676
7677 if (attr.HasLeftIndent())
7678 newAttr.SetLeftIndent(attr.GetLeftIndent(), attr.GetLeftSubIndent());
7679 else if (attrDef.HasLeftIndent())
7680 newAttr.SetLeftIndent(attrDef.GetLeftIndent(), attr.GetLeftSubIndent());
7681
7682 if (attr.HasRightIndent())
7683 newAttr.SetRightIndent(attr.GetRightIndent());
7684 else if (attrDef.HasRightIndent())
7685 newAttr.SetRightIndent(attrDef.GetRightIndent());
61399247 7686
05b4fddf 7687 // NEW ATTRIBUTES
61399247 7688
05b4fddf
JS
7689 if (attr.HasParagraphSpacingAfter())
7690 newAttr.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter());
61399247 7691
05b4fddf
JS
7692 if (attr.HasParagraphSpacingBefore())
7693 newAttr.SetParagraphSpacingBefore(attr.GetParagraphSpacingBefore());
7694
7695 if (attr.HasLineSpacing())
7696 newAttr.SetLineSpacing(attr.GetLineSpacing());
61399247 7697
05b4fddf
JS
7698 if (attr.HasCharacterStyleName())
7699 newAttr.SetCharacterStyleName(attr.GetCharacterStyleName());
61399247 7700
05b4fddf
JS
7701 if (attr.HasParagraphStyleName())
7702 newAttr.SetParagraphStyleName(attr.GetParagraphStyleName());
61399247 7703
38f833b1
JS
7704 if (attr.HasListStyleName())
7705 newAttr.SetListStyleName(attr.GetListStyleName());
7706
05b4fddf
JS
7707 if (attr.HasBulletStyle())
7708 newAttr.SetBulletStyle(attr.GetBulletStyle());
7709
7710 if (attr.HasBulletNumber())
7711 newAttr.SetBulletNumber(attr.GetBulletNumber());
61399247 7712
f089713f
JS
7713 if (attr.HasBulletName())
7714 newAttr.SetBulletName(attr.GetBulletName());
7715
d2d0adc7 7716 if (attr.HasBulletText())
59509217 7717 {
d2d0adc7 7718 newAttr.SetBulletText(attr.GetBulletText());
59509217
JS
7719 newAttr.SetBulletFont(attr.GetBulletFont());
7720 }
61399247 7721
d2d0adc7
JS
7722 if (attr.HasURL())
7723 newAttr.SetURL(attr.GetURL());
7724
42688aea
JS
7725 if (attr.HasTextEffects())
7726 {
7727 newAttr.SetTextEffects(attr.GetTextEffects());
7728 newAttr.SetTextEffectFlags(attr.GetTextEffectFlags());
7729 }
7730
4d6d8bf4
JS
7731 if (attr.HasOutlineLevel())
7732 newAttr.SetOutlineLevel(attr.GetOutlineLevel());
7733
05b4fddf
JS
7734 return newAttr;
7735}
7736
7737
5d7836c4
JS
7738/*!
7739 * wxRichTextFileHandler
7740 * Base class for file handlers
7741 */
7742
7743IMPLEMENT_CLASS(wxRichTextFileHandler, wxObject)
7744
7fe8059f 7745#if wxUSE_STREAMS
5d7836c4
JS
7746bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer *buffer, const wxString& filename)
7747{
7748 wxFFileInputStream stream(filename);
7749 if (stream.Ok())
7750 return LoadFile(buffer, stream);
caae22fb
WS
7751
7752 return false;
5d7836c4
JS
7753}
7754
7755bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer *buffer, const wxString& filename)
7756{
7757 wxFFileOutputStream stream(filename);
7758 if (stream.Ok())
7759 return SaveFile(buffer, stream);
caae22fb
WS
7760
7761 return false;
5d7836c4 7762}
7fe8059f 7763#endif // wxUSE_STREAMS
5d7836c4
JS
7764
7765/// Can we handle this filename (if using files)? By default, checks the extension.
7766bool wxRichTextFileHandler::CanHandle(const wxString& filename) const
7767{
7768 wxString path, file, ext;
7769 wxSplitPath(filename, & path, & file, & ext);
7770
7771 return (ext.Lower() == GetExtension());
7772}
7773
7774/*!
7775 * wxRichTextTextHandler
7776 * Plain text handler
7777 */
7778
7779IMPLEMENT_CLASS(wxRichTextPlainTextHandler, wxRichTextFileHandler)
7780
7781#if wxUSE_STREAMS
7fe8059f 7782bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
5d7836c4
JS
7783{
7784 if (!stream.IsOk())
7785 return false;
7786
7787 wxString str;
1e967276 7788 int lastCh = 0;
5d7836c4
JS
7789
7790 while (!stream.Eof())
7791 {
e191ee87 7792 int ch = stream.GetC();
5d7836c4 7793
169adfa9
JS
7794 if (!stream.Eof())
7795 {
7796 if (ch == 10 && lastCh != 13)
7797 str += wxT('\n');
39a1c2f2 7798
169adfa9
JS
7799 if (ch > 0 && ch != 10)
7800 str += wxChar(ch);
39a1c2f2 7801
169adfa9
JS
7802 lastCh = ch;
7803 }
5d7836c4
JS
7804 }
7805
42688aea 7806 buffer->ResetAndClearCommands();
5d7836c4
JS
7807 buffer->Clear();
7808 buffer->AddParagraphs(str);
7809 buffer->UpdateRanges();
7810
7811 return true;
5d7836c4
JS
7812}
7813
7fe8059f 7814bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
5d7836c4
JS
7815{
7816 if (!stream.IsOk())
7817 return false;
7818
7819 wxString text = buffer->GetText();
7820 wxCharBuffer buf = text.ToAscii();
7821
28f92d74 7822 stream.Write((const char*) buf, text.length());
5d7836c4
JS
7823 return true;
7824}
7fe8059f 7825#endif // wxUSE_STREAMS
5d7836c4
JS
7826
7827/*
7828 * Stores information about an image, in binary in-memory form
7829 */
7830
7831wxRichTextImageBlock::wxRichTextImageBlock()
7832{
7833 Init();
7834}
7835
7836wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock& block):wxObject()
7837{
7838 Init();
7839 Copy(block);
7840}
7841
7842wxRichTextImageBlock::~wxRichTextImageBlock()
7843{
7844 if (m_data)
7845 {
7846 delete[] m_data;
7847 m_data = NULL;
7848 }
7849}
7850
7851void wxRichTextImageBlock::Init()
7852{
7853 m_data = NULL;
7854 m_dataSize = 0;
7855 m_imageType = -1;
7856}
7857
7858void wxRichTextImageBlock::Clear()
7859{
b01ca8b6 7860 delete[] m_data;
5d7836c4
JS
7861 m_data = NULL;
7862 m_dataSize = 0;
7863 m_imageType = -1;
7864}
7865
7866
7867// Load the original image into a memory block.
7868// If the image is not a JPEG, we must convert it into a JPEG
7869// to conserve space.
7870// If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
7871// load the image a 2nd time.
7872
7873bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, int imageType, wxImage& image, bool convertToJPEG)
7874{
7875 m_imageType = imageType;
7876
7877 wxString filenameToRead(filename);
7fe8059f 7878 bool removeFile = false;
5d7836c4
JS
7879
7880 if (imageType == -1)
7fe8059f 7881 return false; // Could not determine image type
5d7836c4
JS
7882
7883 if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
7884 {
7885 wxString tempFile;
7886 bool success = wxGetTempFileName(_("image"), tempFile) ;
7887
7888 wxASSERT(success);
7889
7890 wxUnusedVar(success);
7891
7892 image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
7893 filenameToRead = tempFile;
7fe8059f 7894 removeFile = true;
5d7836c4
JS
7895
7896 m_imageType = wxBITMAP_TYPE_JPEG;
7897 }
7898 wxFile file;
7899 if (!file.Open(filenameToRead))
7fe8059f 7900 return false;
5d7836c4
JS
7901
7902 m_dataSize = (size_t) file.Length();
7903 file.Close();
7904
7905 if (m_data)
7906 delete[] m_data;
7907 m_data = ReadBlock(filenameToRead, m_dataSize);
7908
7909 if (removeFile)
7910 wxRemoveFile(filenameToRead);
7911
7912 return (m_data != NULL);
7913}
7914
7915// Make an image block from the wxImage in the given
7916// format.
7917bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, int imageType, int quality)
7918{
7919 m_imageType = imageType;
7920 image.SetOption(wxT("quality"), quality);
7921
7922 if (imageType == -1)
7fe8059f 7923 return false; // Could not determine image type
5d7836c4
JS
7924
7925 wxString tempFile;
7926 bool success = wxGetTempFileName(_("image"), tempFile) ;
7fe8059f 7927
5d7836c4
JS
7928 wxASSERT(success);
7929 wxUnusedVar(success);
7fe8059f 7930
5d7836c4
JS
7931 if (!image.SaveFile(tempFile, m_imageType))
7932 {
7933 if (wxFileExists(tempFile))
7934 wxRemoveFile(tempFile);
7fe8059f 7935 return false;
5d7836c4
JS
7936 }
7937
7938 wxFile file;
7939 if (!file.Open(tempFile))
7fe8059f 7940 return false;
5d7836c4
JS
7941
7942 m_dataSize = (size_t) file.Length();
7943 file.Close();
7944
7945 if (m_data)
7946 delete[] m_data;
7947 m_data = ReadBlock(tempFile, m_dataSize);
7948
7949 wxRemoveFile(tempFile);
7950
7951 return (m_data != NULL);
7952}
7953
7954
7955// Write to a file
7956bool wxRichTextImageBlock::Write(const wxString& filename)
7957{
7958 return WriteBlock(filename, m_data, m_dataSize);
7959}
7960
7961void wxRichTextImageBlock::Copy(const wxRichTextImageBlock& block)
7962{
7963 m_imageType = block.m_imageType;
7964 if (m_data)
7965 {
7966 delete[] m_data;
7967 m_data = NULL;
7968 }
7969 m_dataSize = block.m_dataSize;
7970 if (m_dataSize == 0)
7971 return;
7972
7973 m_data = new unsigned char[m_dataSize];
7974 unsigned int i;
7975 for (i = 0; i < m_dataSize; i++)
7976 m_data[i] = block.m_data[i];
7977}
7978
7979//// Operators
7980void wxRichTextImageBlock::operator=(const wxRichTextImageBlock& block)
7981{
7982 Copy(block);
7983}
7984
7985// Load a wxImage from the block
7986bool wxRichTextImageBlock::Load(wxImage& image)
7987{
7988 if (!m_data)
7fe8059f 7989 return false;
5d7836c4
JS
7990
7991 // Read in the image.
0ca07313 7992#if wxUSE_STREAMS
5d7836c4
JS
7993 wxMemoryInputStream mstream(m_data, m_dataSize);
7994 bool success = image.LoadFile(mstream, GetImageType());
7995#else
7996 wxString tempFile;
7997 bool success = wxGetTempFileName(_("image"), tempFile) ;
7998 wxASSERT(success);
7999
8000 if (!WriteBlock(tempFile, m_data, m_dataSize))
8001 {
7fe8059f 8002 return false;
5d7836c4
JS
8003 }
8004 success = image.LoadFile(tempFile, GetImageType());
8005 wxRemoveFile(tempFile);
8006#endif
8007
8008 return success;
8009}
8010
8011// Write data in hex to a stream
8012bool wxRichTextImageBlock::WriteHex(wxOutputStream& stream)
8013{
8014 wxString hex;
8015 int i;
8016 for (i = 0; i < (int) m_dataSize; i++)
8017 {
8018 hex = wxDecToHex(m_data[i]);
8019 wxCharBuffer buf = hex.ToAscii();
7fe8059f 8020
28f92d74 8021 stream.Write((const char*) buf, hex.length());
5d7836c4
JS
8022 }
8023
8024 return true;
8025}
8026
8027// Read data in hex from a stream
8028bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, int imageType)
8029{
8030 int dataSize = length/2;
8031
8032 if (m_data)
8033 delete[] m_data;
8034
8035 wxString str(wxT(" "));
8036 m_data = new unsigned char[dataSize];
8037 int i;
8038 for (i = 0; i < dataSize; i ++)
8039 {
8040 str[0] = stream.GetC();
8041 str[1] = stream.GetC();
8042
7fe8059f 8043 m_data[i] = (unsigned char)wxHexToDec(str);
5d7836c4
JS
8044 }
8045
8046 m_dataSize = dataSize;
8047 m_imageType = imageType;
8048
8049 return true;
8050}
8051
5d7836c4
JS
8052// Allocate and read from stream as a block of memory
8053unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream& stream, size_t size)
8054{
8055 unsigned char* block = new unsigned char[size];
8056 if (!block)
8057 return NULL;
8058
8059 stream.Read(block, size);
8060
8061 return block;
8062}
8063
8064unsigned char* wxRichTextImageBlock::ReadBlock(const wxString& filename, size_t size)
8065{
8066 wxFileInputStream stream(filename);
8067 if (!stream.Ok())
8068 return NULL;
8069
8070 return ReadBlock(stream, size);
8071}
8072
8073// Write memory block to stream
8074bool wxRichTextImageBlock::WriteBlock(wxOutputStream& stream, unsigned char* block, size_t size)
8075{
8076 stream.Write((void*) block, size);
8077 return stream.IsOk();
8078
8079}
8080
8081// Write memory block to file
8082bool wxRichTextImageBlock::WriteBlock(const wxString& filename, unsigned char* block, size_t size)
8083{
8084 wxFileOutputStream outStream(filename);
8085 if (!outStream.Ok())
7fe8059f 8086 return false;
5d7836c4
JS
8087
8088 return WriteBlock(outStream, block, size);
8089}
8090
d2d0adc7
JS
8091// Gets the extension for the block's type
8092wxString wxRichTextImageBlock::GetExtension() const
8093{
8094 wxImageHandler* handler = wxImage::FindHandler(GetImageType());
8095 if (handler)
8096 return handler->GetExtension();
8097 else
8098 return wxEmptyString;
8099}
8100
0ca07313
JS
8101#if wxUSE_DATAOBJ
8102
8103/*!
8104 * The data object for a wxRichTextBuffer
8105 */
8106
8107const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxShape");
8108
8109wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer)
8110{
8111 m_richTextBuffer = richTextBuffer;
8112
8113 // this string should uniquely identify our format, but is otherwise
8114 // arbitrary
8115 m_formatRichTextBuffer.SetId(GetRichTextBufferFormatId());
8116
8117 SetFormat(m_formatRichTextBuffer);
8118}
8119
8120wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
8121{
8122 delete m_richTextBuffer;
8123}
8124
8125// after a call to this function, the richTextBuffer is owned by the caller and it
8126// is responsible for deleting it!
8127wxRichTextBuffer* wxRichTextBufferDataObject::GetRichTextBuffer()
8128{
8129 wxRichTextBuffer* richTextBuffer = m_richTextBuffer;
8130 m_richTextBuffer = NULL;
8131
8132 return richTextBuffer;
8133}
8134
8135wxDataFormat wxRichTextBufferDataObject::GetPreferredFormat(Direction WXUNUSED(dir)) const
8136{
8137 return m_formatRichTextBuffer;
8138}
8139
8140size_t wxRichTextBufferDataObject::GetDataSize() const
8141{
8142 if (!m_richTextBuffer)
8143 return 0;
8144
8145 wxString bufXML;
8146
8147 {
8148 wxStringOutputStream stream(& bufXML);
8149 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
8150 {
8151 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
8152 return 0;
8153 }
8154 }
8155
8156#if wxUSE_UNICODE
8157 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
8158 return strlen(buffer) + 1;
8159#else
8160 return bufXML.Length()+1;
8161#endif
8162}
8163
8164bool wxRichTextBufferDataObject::GetDataHere(void *pBuf) const
8165{
8166 if (!pBuf || !m_richTextBuffer)
8167 return false;
8168
8169 wxString bufXML;
8170
8171 {
8172 wxStringOutputStream stream(& bufXML);
8173 if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
8174 {
8175 wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
8176 return 0;
8177 }
8178 }
8179
8180#if wxUSE_UNICODE
8181 wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
8182 size_t len = strlen(buffer);
8183 memcpy((char*) pBuf, (const char*) buffer, len);
8184 ((char*) pBuf)[len] = 0;
8185#else
8186 size_t len = bufXML.Length();
8187 memcpy((char*) pBuf, (const char*) bufXML.c_str(), len);
8188 ((char*) pBuf)[len] = 0;
8189#endif
8190
8191 return true;
8192}
8193
8194bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len), const void *buf)
8195{
8196 delete m_richTextBuffer;
8197 m_richTextBuffer = NULL;
8198
8199 wxString bufXML((const char*) buf, wxConvUTF8);
8200
8201 m_richTextBuffer = new wxRichTextBuffer;
8202
8203 wxStringInputStream stream(bufXML);
8204 if (!m_richTextBuffer->LoadFile(stream, wxRICHTEXT_TYPE_XML))
8205 {
8206 wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
8207
8208 delete m_richTextBuffer;
8209 m_richTextBuffer = NULL;
8210
8211 return false;
8212 }
8213 return true;
8214}
8215
8216#endif
8217 // wxUSE_DATAOBJ
8218
5d7836c4
JS
8219#endif
8220 // wxUSE_RICHTEXT