~brian-sidebotham/wxwidgets-cmake/wxpython-2.9.4

« back to all changes in this revision

Viewing changes to src/richtext/richtextbuffer.cpp

  • Committer: Brian Sidebotham
  • Date: 2013-08-03 14:30:08 UTC
  • Revision ID: brian.sidebotham@gmail.com-20130803143008-c7806tkych1tp6fc
Initial import into Bazaar

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/////////////////////////////////////////////////////////////////////////////
 
2
// Name:        src/richtext/richtextbuffer.cpp
 
3
// Purpose:     Buffer for wxRichTextCtrl
 
4
// Author:      Julian Smart
 
5
// Modified by:
 
6
// Created:     2005-09-30
 
7
// RCS-ID:      $Id: richtextbuffer.cpp 71828 2012-06-21 19:12:04Z JS $
 
8
// Copyright:   (c) Julian Smart
 
9
// Licence:     wxWindows licence
 
10
/////////////////////////////////////////////////////////////////////////////
 
11
 
 
12
// For compilers that support precompilation, includes "wx.h".
 
13
#include "wx/wxprec.h"
 
14
 
 
15
#ifdef __BORLANDC__
 
16
    #pragma hdrstop
 
17
#endif
 
18
 
 
19
#if wxUSE_RICHTEXT
 
20
 
 
21
#include "wx/richtext/richtextbuffer.h"
 
22
 
 
23
#ifndef WX_PRECOMP
 
24
    #include "wx/dc.h"
 
25
    #include "wx/intl.h"
 
26
    #include "wx/log.h"
 
27
    #include "wx/dataobj.h"
 
28
    #include "wx/module.h"
 
29
#endif
 
30
 
 
31
#include "wx/settings.h"
 
32
#include "wx/filename.h"
 
33
#include "wx/clipbrd.h"
 
34
#include "wx/wfstream.h"
 
35
#include "wx/mstream.h"
 
36
#include "wx/sstream.h"
 
37
#include "wx/textfile.h"
 
38
#include "wx/hashmap.h"
 
39
#include "wx/dynarray.h"
 
40
 
 
41
#include "wx/richtext/richtextctrl.h"
 
42
#include "wx/richtext/richtextstyles.h"
 
43
#include "wx/richtext/richtextimagedlg.h"
 
44
#include "wx/richtext/richtextsizepage.h"
 
45
#include "wx/richtext/richtextxml.h"
 
46
 
 
47
#include "wx/listimpl.cpp"
 
48
#include "wx/arrimpl.cpp"
 
49
 
 
50
WX_DEFINE_LIST(wxRichTextObjectList)
 
51
WX_DEFINE_LIST(wxRichTextLineList)
 
52
 
 
53
// Switch off if the platform doesn't like it for some reason
 
54
#define wxRICHTEXT_USE_OPTIMIZED_DRAWING 1
 
55
 
 
56
// Use GetPartialTextExtents for platforms that support it natively
 
57
#define wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS 1
 
58
 
 
59
const wxChar wxRichTextLineBreakChar = (wxChar) 29;
 
60
 
 
61
// Helper classes for floating layout
 
62
struct wxRichTextFloatRectMap
 
63
{
 
64
    wxRichTextFloatRectMap(int sY, int eY, int w, wxRichTextObject* obj)
 
65
    {
 
66
        startY = sY;
 
67
        endY = eY;
 
68
        width = w;
 
69
        anchor = obj;
 
70
    }
 
71
 
 
72
    int startY, endY;
 
73
    int width;
 
74
    wxRichTextObject* anchor;
 
75
};
 
76
 
 
77
WX_DEFINE_SORTED_ARRAY(wxRichTextFloatRectMap*, wxRichTextFloatRectMapArray);
 
78
 
 
79
int wxRichTextFloatRectMapCmp(wxRichTextFloatRectMap* r1, wxRichTextFloatRectMap* r2)
 
80
{
 
81
    return r1->startY - r2->startY;
 
82
}
 
83
 
 
84
class wxRichTextFloatCollector
 
85
{
 
86
public:
 
87
    wxRichTextFloatCollector(const wxRect& availableRect);
 
88
    ~wxRichTextFloatCollector();
 
89
 
 
90
    // Collect the floating objects info in the given paragraph
 
91
    void CollectFloat(wxRichTextParagraph* para);
 
92
    void CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating);
 
93
 
 
94
    // Return the last paragraph we collected
 
95
    wxRichTextParagraph* LastParagraph();
 
96
 
 
97
    // Given the start y position and the height of the line,
 
98
    // find out how wide the line can be
 
99
    wxRect GetAvailableRect(int startY, int endY);
 
100
 
 
101
    // Given a floating box, find its fit position
 
102
    int GetFitPosition(int direction, int start, int height) const;
 
103
    int GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const;
 
104
 
 
105
    // Find the last y position
 
106
    int GetLastRectBottom();
 
107
 
 
108
    // Draw the floats inside a rect
 
109
    void Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
 
110
 
 
111
    // HitTest the floats
 
112
    int HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
 
113
 
 
114
    // Get floating object count
 
115
    int GetFloatingObjectCount() const { return m_left.GetCount() + m_right.GetCount(); }
 
116
 
 
117
    // Get floating objects
 
118
    bool GetFloatingObjects(wxRichTextObjectList& objects) const;
 
119
 
 
120
    // Delete a float
 
121
    bool DeleteFloat(wxRichTextObject* obj);
 
122
 
 
123
    // Do we have this float already?
 
124
    bool HasFloat(wxRichTextObject* obj);
 
125
 
 
126
    bool HasFloats() const { return m_left.GetCount() >0 || m_right.GetCount() > 0; }
 
127
 
 
128
    static int SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point);
 
129
 
 
130
    static int GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY);
 
131
 
 
132
    static void FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array);
 
133
 
 
134
    static void DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style);
 
135
 
 
136
    static int HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags);
 
137
 
 
138
private:
 
139
    wxRichTextFloatRectMapArray m_left;
 
140
    wxRichTextFloatRectMapArray m_right;
 
141
    //int m_width;
 
142
    wxRect               m_availableRect;
 
143
    wxRichTextParagraph* m_para;
 
144
};
 
145
 
 
146
// Delete a float
 
147
bool wxRichTextFloatCollector::DeleteFloat(wxRichTextObject* obj)
 
148
{
 
149
    size_t i;
 
150
    for (i = 0; i < m_left.GetCount(); i++)
 
151
    {
 
152
        if (m_left[i]->anchor == obj)
 
153
        {
 
154
            m_left.RemoveAt(i);
 
155
            return true;
 
156
        }
 
157
    }
 
158
    for (i = 0; i < m_right.GetCount(); i++)
 
159
    {
 
160
        if (m_right[i]->anchor == obj)
 
161
        {
 
162
            m_right.RemoveAt(i);
 
163
            return true;
 
164
        }
 
165
    }
 
166
    return false;
 
167
}
 
168
 
 
169
// Do we have this float already?
 
170
bool wxRichTextFloatCollector::HasFloat(wxRichTextObject* obj)
 
171
{
 
172
    size_t i;
 
173
    for (i = 0; i < m_left.GetCount(); i++)
 
174
    {
 
175
        if (m_left[i]->anchor == obj)
 
176
        {
 
177
            return true;
 
178
        }
 
179
    }
 
180
    for (i = 0; i < m_right.GetCount(); i++)
 
181
    {
 
182
        if (m_right[i]->anchor == obj)
 
183
        {
 
184
            return true;
 
185
        }
 
186
    }
 
187
    return false;
 
188
}
 
189
 
 
190
// Get floating objects
 
191
bool wxRichTextFloatCollector::GetFloatingObjects(wxRichTextObjectList& objects) const
 
192
{
 
193
    size_t i;
 
194
    for (i = 0; i < m_left.GetCount(); i++)
 
195
        objects.Append(m_left[i]->anchor);
 
196
    for (i = 0; i < m_right.GetCount(); i++)
 
197
        objects.Append(m_right[i]->anchor);
 
198
    return true;
 
199
}
 
200
 
 
201
 
 
202
/*
 
203
 * Binary search helper function
 
204
 * The argument point is the Y coordinate, and this fuction
 
205
 * always return the floating rect that contain this coordinate
 
206
 * or under this coordinate.
 
207
 */
 
208
int wxRichTextFloatCollector::SearchAdjacentRect(const wxRichTextFloatRectMapArray& array, int point)
 
209
{
 
210
    int end = array.GetCount() - 1;
 
211
    int start = 0;
 
212
    int ret = 0;
 
213
 
 
214
    wxASSERT(end >= 0);
 
215
 
 
216
    while (true)
 
217
    {
 
218
        if (start > end)
 
219
        {
 
220
            break;
 
221
        }
 
222
 
 
223
        int mid = (start + end) / 2;
 
224
        if (array[mid]->startY <= point && array[mid]->endY >= point)
 
225
            return mid;
 
226
        else if (array[mid]->startY > point)
 
227
        {
 
228
            end = mid - 1;
 
229
            ret = mid;
 
230
        }
 
231
        else if (array[mid]->endY < point)
 
232
        {
 
233
            start = mid + 1;
 
234
            ret = start;
 
235
        }
 
236
    }
 
237
 
 
238
    return ret;
 
239
}
 
240
 
 
241
int wxRichTextFloatCollector::GetWidthFromFloatRect(const wxRichTextFloatRectMapArray& array, int index, int startY, int endY)
 
242
{
 
243
    int ret = 0;
 
244
    int len = array.GetCount();
 
245
 
 
246
    wxASSERT(index >= 0 && index < len);
 
247
 
 
248
    if (array[index]->startY < startY && array[index]->endY > startY)
 
249
        ret = ret < array[index]->width ? array[index]->width : ret;
 
250
    while (index < len && array[index]->startY <= endY)
 
251
    {
 
252
        ret = ret < array[index]->width ? array[index]->width : ret;
 
253
        index++;
 
254
    }
 
255
 
 
256
    return ret;
 
257
}
 
258
 
 
259
wxRichTextFloatCollector::wxRichTextFloatCollector(const wxRect& rect) : m_left(wxRichTextFloatRectMapCmp), m_right(wxRichTextFloatRectMapCmp)
 
260
{
 
261
    m_availableRect = rect;
 
262
    m_para = NULL;
 
263
}
 
264
 
 
265
void wxRichTextFloatCollector::FreeFloatRectMapArray(wxRichTextFloatRectMapArray& array)
 
266
{
 
267
    int len = array.GetCount();
 
268
    for (int i = 0; i < len; i++)
 
269
        delete array[i];
 
270
}
 
271
 
 
272
wxRichTextFloatCollector::~wxRichTextFloatCollector()
 
273
{
 
274
    FreeFloatRectMapArray(m_left);
 
275
    FreeFloatRectMapArray(m_right);
 
276
}
 
277
 
 
278
int wxRichTextFloatCollector::GetFitPosition(const wxRichTextFloatRectMapArray& array, int start, int height) const
 
279
{
 
280
    if (array.GetCount() == 0)
 
281
        return start;
 
282
 
 
283
    int i = SearchAdjacentRect(array, start);
 
284
    int last = start;
 
285
    while (i < (int) array.GetCount())
 
286
    {
 
287
        if (array[i]->startY - last >= height)
 
288
            return last + 1;
 
289
        last = array[i]->endY;
 
290
        i++;
 
291
    }
 
292
 
 
293
    return last + 1;
 
294
}
 
295
 
 
296
int wxRichTextFloatCollector::GetFitPosition(int direction, int start, int height) const
 
297
{
 
298
    if (direction == wxTEXT_BOX_ATTR_FLOAT_LEFT)
 
299
        return GetFitPosition(m_left, start, height);
 
300
    else if (direction == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
 
301
        return GetFitPosition(m_right, start, height);
 
302
    else
 
303
    {
 
304
        wxASSERT("Never should be here");
 
305
        return start;
 
306
    }
 
307
}
 
308
 
 
309
// Adds a floating image to the float collector.
 
310
// The actual positioning is done by wxRichTextParagraph::LayoutFloat.
 
311
void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para, wxRichTextObject* floating)
 
312
{
 
313
    int direction = floating->GetFloatDirection();
 
314
 
 
315
    wxPoint pos = floating->GetPosition();
 
316
    wxSize size = floating->GetCachedSize();
 
317
    wxRichTextFloatRectMap *map = new wxRichTextFloatRectMap(pos.y, pos.y + size.y, size.x, floating);
 
318
    switch (direction)
 
319
    {
 
320
        case wxTEXT_BOX_ATTR_FLOAT_NONE:
 
321
            delete map;
 
322
            break;
 
323
        case wxTEXT_BOX_ATTR_FLOAT_LEFT:
 
324
            // Just a not-enough simple assertion
 
325
            wxASSERT (m_left.Index(map) == wxNOT_FOUND);
 
326
            m_left.Add(map);
 
327
            break;
 
328
        case wxTEXT_BOX_ATTR_FLOAT_RIGHT:
 
329
            wxASSERT (m_right.Index(map) == wxNOT_FOUND);
 
330
            m_right.Add(map);
 
331
            break;
 
332
        default:
 
333
            delete map;
 
334
            wxASSERT("Unrecognised float attribute.");
 
335
    }
 
336
 
 
337
    m_para = para;
 
338
}
 
339
 
 
340
void wxRichTextFloatCollector::CollectFloat(wxRichTextParagraph* para)
 
341
{
 
342
    wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
 
343
    while (node)
 
344
    {
 
345
        wxRichTextObject* floating = node->GetData();
 
346
 
 
347
        if (floating->IsFloating())
 
348
        {
 
349
            CollectFloat(para, floating);
 
350
        }
 
351
 
 
352
        node = node->GetNext();
 
353
    }
 
354
 
 
355
    m_para = para;
 
356
}
 
357
 
 
358
wxRichTextParagraph* wxRichTextFloatCollector::LastParagraph()
 
359
{
 
360
    return m_para;
 
361
}
 
362
 
 
363
wxRect wxRichTextFloatCollector::GetAvailableRect(int startY, int endY)
 
364
{
 
365
    int widthLeft = 0, widthRight = 0;
 
366
    if (m_left.GetCount() != 0)
 
367
    {
 
368
        int i = SearchAdjacentRect(m_left, startY);
 
369
        if (i < (int) m_left.GetCount())
 
370
            widthLeft = GetWidthFromFloatRect(m_left, i, startY, endY);
 
371
    }
 
372
    if (m_right.GetCount() != 0)
 
373
    {
 
374
        int j = SearchAdjacentRect(m_right, startY);
 
375
        if (j < (int) m_right.GetCount())
 
376
            widthRight = GetWidthFromFloatRect(m_right, j, startY, endY);
 
377
    }
 
378
 
 
379
    // TODO: actually we want to use the actual image positions to find the
 
380
    // available remaining space, since the image might not be right up against
 
381
    // the left or right edge of the container.
 
382
    return wxRect(widthLeft + m_availableRect.x, 0, m_availableRect.width - widthLeft - widthRight, 0);
 
383
}
 
384
 
 
385
int wxRichTextFloatCollector::GetLastRectBottom()
 
386
{
 
387
    int ret = 0;
 
388
    int len = m_left.GetCount();
 
389
    if (len) {
 
390
        ret = ret > m_left[len-1]->endY ? ret : m_left[len-1]->endY;
 
391
    }
 
392
    len = m_right.GetCount();
 
393
    if (len) {
 
394
        ret = ret > m_right[len-1]->endY ? ret : m_right[len-1]->endY;
 
395
    }
 
396
 
 
397
    return ret;
 
398
}
 
399
 
 
400
void wxRichTextFloatCollector::DrawFloat(const wxRichTextFloatRectMapArray& array, wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
 
401
{
 
402
    int start = rect.y;
 
403
    int end = rect.y + rect.height;
 
404
    int i, j;
 
405
    i = SearchAdjacentRect(array, start);
 
406
    if (i < 0 || i >= (int) array.GetCount())
 
407
        return;
 
408
    j = SearchAdjacentRect(array, end);
 
409
    if (j < 0 || j >= (int) array.GetCount())
 
410
        j = array.GetCount() - 1;
 
411
    while (i <= j)
 
412
    {
 
413
        wxRichTextObject* obj = array[i]->anchor;
 
414
        wxRichTextRange r = obj->GetRange();
 
415
        obj->Draw(dc, context, r, selection, wxRect(obj->GetPosition(), obj->GetCachedSize()), descent, style);
 
416
        i++;
 
417
    }
 
418
}
 
419
 
 
420
void wxRichTextFloatCollector::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
 
421
{
 
422
    if (m_left.GetCount() > 0)
 
423
        DrawFloat(m_left, dc, context, range, selection, rect, descent, style);
 
424
    if (m_right.GetCount() > 0)
 
425
        DrawFloat(m_right, dc, context, range, selection, rect, descent, style);
 
426
}
 
427
 
 
428
int wxRichTextFloatCollector::HitTestFloat(const wxRichTextFloatRectMapArray& array, wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int WXUNUSED(flags))
 
429
{
 
430
    int i;
 
431
    if (array.GetCount() == 0)
 
432
        return wxRICHTEXT_HITTEST_NONE;
 
433
    i = SearchAdjacentRect(array, pt.y);
 
434
    if (i < 0 || i >= (int) array.GetCount())
 
435
        return wxRICHTEXT_HITTEST_NONE;
 
436
    if (!array[i]->anchor->IsShown())
 
437
        return wxRICHTEXT_HITTEST_NONE;
 
438
 
 
439
    wxPoint point = array[i]->anchor->GetPosition();
 
440
    wxSize size = array[i]->anchor->GetCachedSize();
 
441
    if (point.x <= pt.x && point.x + size.x >= pt.x
 
442
        && point.y <= pt.y && point.y + size.y >= pt.y)
 
443
    {
 
444
        textPosition = array[i]->anchor->GetRange().GetStart();
 
445
        * obj = array[i]->anchor;
 
446
        if (pt.x > (pt.x + pt.x + size.x) / 2)
 
447
            return wxRICHTEXT_HITTEST_BEFORE;
 
448
        else
 
449
            return wxRICHTEXT_HITTEST_AFTER;
 
450
    }
 
451
 
 
452
    return wxRICHTEXT_HITTEST_NONE;
 
453
}
 
454
 
 
455
int wxRichTextFloatCollector::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, int flags)
 
456
{
 
457
    int ret = HitTestFloat(m_left, dc, context, pt, textPosition, obj, flags);
 
458
    if (ret == wxRICHTEXT_HITTEST_NONE)
 
459
    {
 
460
        ret = HitTestFloat(m_right, dc, context, pt, textPosition, obj, flags);
 
461
    }
 
462
    return ret;
 
463
}
 
464
 
 
465
// Helpers for efficiency
 
466
inline void wxCheckSetFont(wxDC& dc, const wxFont& font)
 
467
{
 
468
    dc.SetFont(font);
 
469
}
 
470
 
 
471
inline void wxCheckSetPen(wxDC& dc, const wxPen& pen)
 
472
{
 
473
    const wxPen& pen1 = dc.GetPen();
 
474
    if (pen1.IsOk() && pen.IsOk())
 
475
    {
 
476
        if (pen1.GetWidth() == pen.GetWidth() &&
 
477
            pen1.GetStyle() == pen.GetStyle() &&
 
478
            pen1.GetColour() == pen.GetColour())
 
479
            return;
 
480
    }
 
481
    dc.SetPen(pen);
 
482
}
 
483
 
 
484
inline void wxCheckSetBrush(wxDC& dc, const wxBrush& brush)
 
485
{
 
486
    const wxBrush& brush1 = dc.GetBrush();
 
487
    if (brush1.IsOk() && brush.IsOk())
 
488
    {
 
489
        if (brush1.GetStyle() == brush.GetStyle() &&
 
490
            brush1.GetColour() == brush.GetColour())
 
491
            return;
 
492
    }
 
493
    dc.SetBrush(brush);
 
494
}
 
495
 
 
496
/*!
 
497
 * wxRichTextObject
 
498
 * This is the base for drawable objects.
 
499
 */
 
500
 
 
501
IMPLEMENT_CLASS(wxRichTextObject, wxObject)
 
502
 
 
503
wxRichTextObject::wxRichTextObject(wxRichTextObject* parent)
 
504
{
 
505
    m_refCount = 1;
 
506
    m_parent = parent;
 
507
    m_descent = 0;
 
508
    m_show = true;
 
509
}
 
510
 
 
511
wxRichTextObject::~wxRichTextObject()
 
512
{
 
513
}
 
514
 
 
515
void wxRichTextObject::Dereference()
 
516
{
 
517
    m_refCount --;
 
518
    if (m_refCount <= 0)
 
519
        delete this;
 
520
}
 
521
 
 
522
/// Copy
 
523
void wxRichTextObject::Copy(const wxRichTextObject& obj)
 
524
{
 
525
    m_size = obj.m_size;
 
526
    m_maxSize = obj.m_maxSize;
 
527
    m_minSize = obj.m_minSize;
 
528
    m_pos = obj.m_pos;
 
529
    m_range = obj.m_range;
 
530
    m_ownRange = obj.m_ownRange;
 
531
    m_attributes = obj.m_attributes;
 
532
    m_properties = obj.m_properties;
 
533
    m_descent = obj.m_descent;
 
534
    m_show = obj.m_show;
 
535
}
 
536
 
 
537
// Get/set the top-level container of this object.
 
538
wxRichTextParagraphLayoutBox* wxRichTextObject::GetContainer() const
 
539
{
 
540
    const wxRichTextObject* p = this;
 
541
    while (p)
 
542
    {
 
543
        if (p->IsTopLevel())
 
544
        {
 
545
            return wxDynamicCast(p, wxRichTextParagraphLayoutBox);
 
546
        }
 
547
        p = p->GetParent();
 
548
    }
 
549
    return NULL;
 
550
}
 
551
 
 
552
void wxRichTextObject::SetMargins(int margin)
 
553
{
 
554
    SetMargins(margin, margin, margin, margin);
 
555
}
 
556
 
 
557
void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin, int bottomMargin)
 
558
{
 
559
    GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().SetValue(leftMargin, wxTEXT_ATTR_UNITS_PIXELS);
 
560
    GetAttributes().GetTextBoxAttr().GetMargins().GetRight().SetValue(rightMargin, wxTEXT_ATTR_UNITS_PIXELS);
 
561
    GetAttributes().GetTextBoxAttr().GetMargins().GetTop().SetValue(topMargin, wxTEXT_ATTR_UNITS_PIXELS);
 
562
    GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().SetValue(bottomMargin, wxTEXT_ATTR_UNITS_PIXELS);
 
563
}
 
564
 
 
565
int wxRichTextObject::GetLeftMargin() const
 
566
{
 
567
    return GetAttributes().GetTextBoxAttr().GetMargins().GetLeft().GetValue();
 
568
}
 
569
 
 
570
int wxRichTextObject::GetRightMargin() const
 
571
{
 
572
    return GetAttributes().GetTextBoxAttr().GetMargins().GetRight().GetValue();
 
573
}
 
574
 
 
575
int wxRichTextObject::GetTopMargin() const
 
576
{
 
577
    return GetAttributes().GetTextBoxAttr().GetMargins().GetTop().GetValue();
 
578
}
 
579
 
 
580
int wxRichTextObject::GetBottomMargin() const
 
581
{
 
582
    return GetAttributes().GetTextBoxAttr().GetMargins().GetBottom().GetValue();
 
583
}
 
584
 
 
585
// Calculate the available content space in the given rectangle, given the
 
586
// margins, border and padding specified in the object's attributes.
 
587
wxRect wxRichTextObject::GetAvailableContentArea(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& outerRect) const
 
588
{
 
589
    wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
 
590
    marginRect = outerRect;
 
591
    wxRichTextAttr attr(GetAttributes());
 
592
    context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
 
593
    GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
 
594
    return contentRect;
 
595
}
 
596
 
 
597
// Invalidate the buffer. With no argument, invalidates whole buffer.
 
598
void wxRichTextObject::Invalidate(const wxRichTextRange& invalidRange)
 
599
{
 
600
    if (invalidRange != wxRICHTEXT_NONE)
 
601
    {
 
602
        // If this is a floating object, size may not be recalculated
 
603
        // after floats have been collected in an early stage of Layout.
 
604
        // So avoid resetting the cache for floating objects during layout.
 
605
        if (!IsFloating())
 
606
            SetCachedSize(wxDefaultSize);
 
607
        SetMaxSize(wxDefaultSize);
 
608
        SetMinSize(wxDefaultSize);
 
609
    }
 
610
}
 
611
 
 
612
// Convert units in tenths of a millimetre to device units
 
613
int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units) const
 
614
{
 
615
    // Unscale
 
616
    double scale = 1.0;
 
617
    if (GetBuffer())
 
618
        scale = GetBuffer()->GetScale() / GetBuffer()->GetDimensionScale();
 
619
    int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units, scale);
 
620
 
 
621
    return p;
 
622
}
 
623
 
 
624
// Convert units in tenths of a millimetre to device units
 
625
int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units, double scale)
 
626
{
 
627
    // There are ppi pixels in 254.1 "1/10 mm"
 
628
 
 
629
    double pixels = ((double) units * (double)ppi) / 254.1;
 
630
    if (scale != 1.0)
 
631
        pixels /= scale;
 
632
 
 
633
    // If the result is very small, make it at least one pixel in size.
 
634
    if (pixels == 0 && units > 0)
 
635
        pixels = 1;
 
636
 
 
637
    return (int) pixels;
 
638
}
 
639
 
 
640
// Convert units in pixels to tenths of a millimetre
 
641
int wxRichTextObject::ConvertPixelsToTenthsMM(wxDC& dc, int pixels) const
 
642
{
 
643
    int p = pixels;
 
644
    double scale = 1.0;
 
645
    if (GetBuffer())
 
646
        scale = GetBuffer()->GetScale();
 
647
 
 
648
    return ConvertPixelsToTenthsMM(dc.GetPPI().x, p, scale);
 
649
}
 
650
 
 
651
int wxRichTextObject::ConvertPixelsToTenthsMM(int ppi, int pixels, double scale)
 
652
{
 
653
    // There are ppi pixels in 254.1 "1/10 mm"
 
654
 
 
655
    double p = double(pixels);
 
656
 
 
657
    if (scale != 1.0)
 
658
        p *= scale;
 
659
 
 
660
    int units = int( p * 254.1 / (double) ppi );
 
661
    return units;
 
662
}
 
663
 
 
664
// Draw the borders and background for the given rectangle and attributes.
 
665
// Width and height are taken to be the outer margin size, not the content.
 
666
bool wxRichTextObject::DrawBoxAttributes(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, const wxRect& boxRect, int flags)
 
667
{
 
668
    // Assume boxRect is the area around the content
 
669
    wxRect marginRect = boxRect;
 
670
    wxRect contentRect, borderRect, paddingRect, outlineRect;
 
671
 
 
672
    GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
 
673
 
 
674
    // Margin is transparent. Draw background from margin.
 
675
    if (attr.HasBackgroundColour() || (flags & wxRICHTEXT_DRAW_SELECTED))
 
676
    {
 
677
        wxColour colour;
 
678
        if (flags & wxRICHTEXT_DRAW_SELECTED)
 
679
        {
 
680
            // TODO: get selection colour from control?
 
681
            colour = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
 
682
        }
 
683
        else
 
684
            colour = attr.GetBackgroundColour();
 
685
 
 
686
        wxPen pen(colour);
 
687
        wxBrush brush(colour);
 
688
 
 
689
        dc.SetPen(pen);
 
690
        dc.SetBrush(brush);
 
691
        dc.DrawRectangle(borderRect);
 
692
    }
 
693
 
 
694
    if (flags & wxRICHTEXT_DRAW_GUIDELINES)
 
695
    {
 
696
        wxRichTextAttr editBorderAttr = attr;
 
697
        // TODO: make guideline colour configurable
 
698
        editBorderAttr.GetTextBoxAttr().GetBorder().SetColour(*wxLIGHT_GREY);
 
699
        editBorderAttr.GetTextBoxAttr().GetBorder().SetWidth(1, wxTEXT_ATTR_UNITS_PIXELS);
 
700
        editBorderAttr.GetTextBoxAttr().GetBorder().SetStyle(wxTEXT_BOX_ATTR_BORDER_SOLID);
 
701
 
 
702
        DrawBorder(dc, buffer, editBorderAttr.GetTextBoxAttr().GetBorder(), borderRect, flags);
 
703
    }
 
704
 
 
705
    if (attr.GetTextBoxAttr().GetBorder().IsValid())
 
706
        DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetBorder(), borderRect);
 
707
 
 
708
    if (attr.GetTextBoxAttr().GetOutline().IsValid())
 
709
        DrawBorder(dc, buffer, attr.GetTextBoxAttr().GetOutline(), outlineRect);
 
710
 
 
711
    return true;
 
712
}
 
713
 
 
714
// Draw a border
 
715
bool wxRichTextObject::DrawBorder(wxDC& dc, wxRichTextBuffer* buffer, const wxTextAttrBorders& attr, const wxRect& rect, int WXUNUSED(flags))
 
716
{
 
717
    int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
 
718
    wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
 
719
 
 
720
    if (attr.GetLeft().IsValid() && attr.GetLeft().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
 
721
    {
 
722
        borderLeft = converter.GetPixels(attr.GetLeft().GetWidth());
 
723
        wxColour col(attr.GetLeft().GetColour());
 
724
 
 
725
        // If pen width is > 1, resorts to a solid rectangle.
 
726
        if (borderLeft == 1)
 
727
        {
 
728
            int penStyle = wxSOLID;
 
729
            if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
 
730
                penStyle = wxDOT;
 
731
            else if (attr.GetLeft().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
 
732
                penStyle = wxLONG_DASH;
 
733
            wxPen pen(col, 1, penStyle);
 
734
            dc.SetPen(pen);
 
735
            dc.DrawLine(rect.x, rect.y, rect.x, rect.y + rect.height);
 
736
 
 
737
        }
 
738
        else if (borderLeft > 1)
 
739
        {
 
740
            wxPen pen(col);
 
741
            wxBrush brush(col);
 
742
            dc.SetPen(pen);
 
743
            dc.SetBrush(brush);
 
744
            dc.DrawRectangle(rect.x, rect.y, borderLeft, rect.height);
 
745
        }
 
746
    }
 
747
 
 
748
    if (attr.GetRight().IsValid() && attr.GetRight().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
 
749
    {
 
750
        borderRight = converter.GetPixels(attr.GetRight().GetWidth());
 
751
 
 
752
        wxColour col(attr.GetRight().GetColour());
 
753
 
 
754
        // If pen width is > 1, resorts to a solid rectangle.
 
755
        if (borderRight == 1)
 
756
        {
 
757
            int penStyle = wxSOLID;
 
758
            if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
 
759
                penStyle = wxDOT;
 
760
            else if (attr.GetRight().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
 
761
                penStyle = wxLONG_DASH;
 
762
            wxPen pen(col, 1, penStyle);
 
763
            dc.SetPen(pen);
 
764
            dc.DrawLine(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height + 1);
 
765
 
 
766
        }
 
767
        else if (borderRight > 1)
 
768
        {
 
769
            wxPen pen(col);
 
770
            wxBrush brush(col);
 
771
            dc.SetPen(pen);
 
772
            dc.SetBrush(brush);
 
773
            dc.DrawRectangle(rect.x + rect.width - borderRight, rect.y, borderRight, rect.height);
 
774
        }
 
775
    }
 
776
 
 
777
    if (attr.GetTop().IsValid() && attr.GetTop().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
 
778
    {
 
779
        borderTop = converter.GetPixels(attr.GetTop().GetWidth());
 
780
 
 
781
        wxColour col(attr.GetTop().GetColour());
 
782
 
 
783
        // If pen width is > 1, resorts to a solid rectangle.
 
784
        if (borderTop == 1)
 
785
        {
 
786
            int penStyle = wxSOLID;
 
787
            if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
 
788
                penStyle = wxDOT;
 
789
            else if (attr.GetTop().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
 
790
                penStyle = wxLONG_DASH;
 
791
            wxPen pen(col, 1, penStyle);
 
792
            dc.SetPen(pen);
 
793
            dc.DrawLine(rect.x, rect.y, rect.x + rect.width, rect.y);
 
794
 
 
795
        }
 
796
        else if (borderTop > 1)
 
797
        {
 
798
            wxPen pen(col);
 
799
            wxBrush brush(col);
 
800
            dc.SetPen(pen);
 
801
            dc.SetBrush(brush);
 
802
            dc.DrawRectangle(rect.x, rect.y, rect.width, borderTop);
 
803
        }
 
804
    }
 
805
 
 
806
    if (attr.GetBottom().IsValid() && attr.GetBottom().GetStyle() != wxTEXT_BOX_ATTR_BORDER_NONE)
 
807
    {
 
808
        borderBottom = converter.GetPixels(attr.GetBottom().GetWidth());
 
809
        wxColour col(attr.GetTop().GetColour());
 
810
 
 
811
        // If pen width is > 1, resorts to a solid rectangle.
 
812
        if (borderBottom == 1)
 
813
        {
 
814
            int penStyle = wxSOLID;
 
815
            if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DOTTED)
 
816
                penStyle = wxDOT;
 
817
            else if (attr.GetBottom().GetStyle() == wxTEXT_BOX_ATTR_BORDER_DASHED)
 
818
                penStyle = wxLONG_DASH;
 
819
            wxPen pen(col, 1, penStyle);
 
820
            dc.SetPen(pen);
 
821
            dc.DrawLine(rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height);
 
822
 
 
823
        }
 
824
        else if (borderBottom > 1)
 
825
        {
 
826
            wxPen pen(col);
 
827
            wxBrush brush(col);
 
828
            dc.SetPen(pen);
 
829
            dc.SetBrush(brush);
 
830
            dc.DrawRectangle(rect.x, rect.y + rect.height - borderBottom, rect.width, borderBottom);
 
831
        }
 
832
    }
 
833
 
 
834
    return true;
 
835
}
 
836
 
 
837
// Get the various rectangles of the box model in pixels. You can either specify contentRect (inner)
 
838
// or marginRect (outer), and the other must be the default rectangle (no width or height).
 
839
// Note that the outline doesn't affect the position of the rectangle, it's drawn in whatever space
 
840
// is available.
 
841
//
 
842
// | Margin | Border | Padding | CONTENT | Padding | Border | Margin |
 
843
 
 
844
bool wxRichTextObject::GetBoxRects(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, wxRect& marginRect, wxRect& borderRect, wxRect& contentRect, wxRect& paddingRect, wxRect& outlineRect)
 
845
{
 
846
    int borderLeft = 0, borderRight = 0, borderTop = 0, borderBottom = 0;
 
847
    int outlineLeft = 0, outlineRight = 0, outlineTop = 0, outlineBottom = 0;
 
848
    int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
 
849
    int marginLeft = 0, marginRight = 0, marginTop = 0, marginBottom = 0;
 
850
 
 
851
    wxTextAttrDimensionConverter converter(dc, buffer ? buffer->GetScale() : 1.0);
 
852
 
 
853
    if (attr.GetTextBoxAttr().GetMargins().GetLeft().IsValid())
 
854
        marginLeft = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetLeft());
 
855
    if (attr.GetTextBoxAttr().GetMargins().GetRight().IsValid())
 
856
        marginRight = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetRight());
 
857
    if (attr.GetTextBoxAttr().GetMargins().GetTop().IsValid())
 
858
        marginTop = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetTop());
 
859
    if (attr.GetTextBoxAttr().GetMargins().GetBottom().IsValid())
 
860
        marginBottom = converter.GetPixels(attr.GetTextBoxAttr().GetMargins().GetBottom());
 
861
 
 
862
    if (attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth().IsValid())
 
863
        borderLeft = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetLeft().GetWidth());
 
864
    if (attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth().IsValid())
 
865
        borderRight = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetRight().GetWidth());
 
866
    if (attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth().IsValid())
 
867
        borderTop = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetTop().GetWidth());
 
868
    if (attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth().IsValid())
 
869
        borderBottom = converter.GetPixels(attr.GetTextBoxAttr().GetBorder().GetBottom().GetWidth());
 
870
 
 
871
    if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
 
872
        paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
 
873
    if (attr.GetTextBoxAttr().GetPadding().GetRight().IsValid())
 
874
        paddingRight = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetRight());
 
875
    if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
 
876
        paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
 
877
    if (attr.GetTextBoxAttr().GetPadding().GetBottom().IsValid())
 
878
        paddingBottom = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetBottom());
 
879
 
 
880
    if (attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth().IsValid())
 
881
        outlineLeft = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetLeft().GetWidth());
 
882
    if (attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth().IsValid())
 
883
        outlineRight = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetRight().GetWidth());
 
884
    if (attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth().IsValid())
 
885
        outlineTop = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetTop().GetWidth());
 
886
    if (attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth().IsValid())
 
887
        outlineBottom = converter.GetPixels(attr.GetTextBoxAttr().GetOutline().GetBottom().GetWidth());
 
888
 
 
889
    int leftTotal = marginLeft + borderLeft + paddingLeft;
 
890
    int rightTotal = marginRight + borderRight + paddingRight;
 
891
    int topTotal = marginTop + borderTop + paddingTop;
 
892
    int bottomTotal = marginBottom + borderBottom + paddingBottom;
 
893
 
 
894
    if (marginRect != wxRect())
 
895
    {
 
896
        contentRect.x = marginRect.x + leftTotal;
 
897
        contentRect.y = marginRect.y + topTotal;
 
898
        contentRect.width = marginRect.width - (leftTotal + rightTotal);
 
899
        contentRect.height = marginRect.height - (topTotal + bottomTotal);
 
900
    }
 
901
    else
 
902
    {
 
903
        marginRect.x = contentRect.x - leftTotal;
 
904
        marginRect.y = contentRect.y - topTotal;
 
905
        marginRect.width = contentRect.width + (leftTotal + rightTotal);
 
906
        marginRect.height = contentRect.height + (topTotal + bottomTotal);
 
907
    }
 
908
 
 
909
    borderRect.x = marginRect.x + marginLeft;
 
910
    borderRect.y = marginRect.y + marginTop;
 
911
    borderRect.width = marginRect.width - (marginLeft + marginRight);
 
912
    borderRect.height = marginRect.height - (marginTop + marginBottom);
 
913
 
 
914
    paddingRect.x = marginRect.x + marginLeft + borderLeft;
 
915
    paddingRect.y = marginRect.y + marginTop + borderTop;
 
916
    paddingRect.width = marginRect.width - (marginLeft + marginRight + borderLeft + borderRight);
 
917
    paddingRect.height = marginRect.height - (marginTop + marginBottom + borderTop + borderBottom);
 
918
 
 
919
    // The outline is outside the margin and doesn't influence the overall box position or content size.
 
920
    outlineRect.x = marginRect.x - outlineLeft;
 
921
    outlineRect.y = marginRect.y - outlineTop;
 
922
    outlineRect.width = marginRect.width + (outlineLeft + outlineRight);
 
923
    outlineRect.height = marginRect.height + (outlineTop + outlineBottom);
 
924
 
 
925
    return true;
 
926
}
 
927
 
 
928
// Get the total margin for the object in pixels, taking into account margin, padding and border size
 
929
bool wxRichTextObject::GetTotalMargin(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& attr, int& leftMargin, int& rightMargin,
 
930
        int& topMargin, int& bottomMargin)
 
931
{
 
932
    // Assume boxRect is the area around the content
 
933
    wxRect contentRect, marginRect, borderRect, paddingRect, outlineRect;
 
934
    marginRect = wxRect(0, 0, 1000, 1000);
 
935
 
 
936
    GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
 
937
 
 
938
    leftMargin = contentRect.GetLeft() - marginRect.GetLeft();
 
939
    rightMargin = marginRect.GetRight() - contentRect.GetRight();
 
940
    topMargin = contentRect.GetTop() - marginRect.GetTop();
 
941
    bottomMargin = marginRect.GetBottom() - contentRect.GetBottom();
 
942
 
 
943
    return true;
 
944
}
 
945
 
 
946
// Returns the rectangle which the child has available to it given restrictions specified in the
 
947
// child attribute, e.g. 50% width of the parent, 400 pixels, x position 20% of the parent, etc.
 
948
// availableContainerSpace might be a parent that the cell has to compute its width relative to.
 
949
// E.g. a cell that's 50% of its parent.
 
950
wxRect wxRichTextObject::AdjustAvailableSpace(wxDC& dc, wxRichTextBuffer* buffer, const wxRichTextAttr& WXUNUSED(parentAttr), const wxRichTextAttr& childAttr, const wxRect& availableParentSpace, const wxRect& availableContainerSpace)
 
951
{
 
952
    wxRect rect = availableParentSpace;
 
953
    double scale = 1.0;
 
954
    if (buffer)
 
955
        scale = buffer->GetScale();
 
956
 
 
957
    wxTextAttrDimensionConverter converter(dc, scale, availableContainerSpace.GetSize());
 
958
 
 
959
    if (childAttr.GetTextBoxAttr().GetWidth().IsValid())
 
960
        rect.width = converter.GetPixels(childAttr.GetTextBoxAttr().GetWidth());
 
961
 
 
962
    if (childAttr.GetTextBoxAttr().GetHeight().IsValid())
 
963
        rect.height = converter.GetPixels(childAttr.GetTextBoxAttr().GetHeight());
 
964
 
 
965
    // Can specify either left or right for the position (we're assuming we can't
 
966
    // set the left and right edges to effectively set the size. Would we want to do that?)
 
967
    if (childAttr.GetTextBoxAttr().GetPosition().GetLeft().IsValid())
 
968
    {
 
969
        rect.x = rect.x + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetLeft());
 
970
    }
 
971
    else if (childAttr.GetTextBoxAttr().GetPosition().GetRight().IsValid())
 
972
    {
 
973
        int x = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetRight());
 
974
        if (childAttr.GetTextBoxAttr().GetPosition().GetRight().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
 
975
            rect.x = availableContainerSpace.x + availableContainerSpace.width - rect.width;
 
976
        else
 
977
            rect.x += x;
 
978
    }
 
979
 
 
980
    if (childAttr.GetTextBoxAttr().GetPosition().GetTop().IsValid())
 
981
    {
 
982
        rect.y = rect.y + converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetTop());
 
983
    }
 
984
    else if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().IsValid())
 
985
    {
 
986
        int y = converter.GetPixels(childAttr.GetTextBoxAttr().GetPosition().GetBottom());
 
987
        if (childAttr.GetTextBoxAttr().GetPosition().GetBottom().GetPosition() == wxTEXT_BOX_ATTR_POSITION_RELATIVE)
 
988
            rect.y = availableContainerSpace.y + availableContainerSpace.height - rect.height;
 
989
        else
 
990
            rect.y += y;
 
991
    }
 
992
 
 
993
    if (rect.GetWidth() > availableParentSpace.GetWidth())
 
994
        rect.SetWidth(availableParentSpace.GetWidth());
 
995
 
 
996
    return rect;
 
997
}
 
998
 
 
999
// Dump to output stream for debugging
 
1000
void wxRichTextObject::Dump(wxTextOutputStream& stream)
 
1001
{
 
1002
    stream << GetClassInfo()->GetClassName() << wxT("\n");
 
1003
    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");
 
1004
    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");
 
1005
}
 
1006
 
 
1007
// Gets the containing buffer
 
1008
wxRichTextBuffer* wxRichTextObject::GetBuffer() const
 
1009
{
 
1010
    const wxRichTextObject* obj = this;
 
1011
    while (obj && !wxDynamicCast(obj, wxRichTextBuffer))
 
1012
        obj = obj->GetParent();
 
1013
    return wxDynamicCast(obj, wxRichTextBuffer);
 
1014
}
 
1015
 
 
1016
// Get the absolute object position, by traversing up the child/parent hierarchy
 
1017
wxPoint wxRichTextObject::GetAbsolutePosition() const
 
1018
{
 
1019
    wxPoint pt = GetPosition();
 
1020
 
 
1021
    wxRichTextObject* p = GetParent();
 
1022
    while (p)
 
1023
    {
 
1024
        pt = pt + p->GetPosition();
 
1025
        p = p->GetParent();
 
1026
    }
 
1027
 
 
1028
    return pt;
 
1029
}
 
1030
 
 
1031
// Hit-testing: returns a flag indicating hit test details, plus
 
1032
// information about position
 
1033
int wxRichTextObject::HitTest(wxDC& WXUNUSED(dc), wxRichTextDrawingContext& WXUNUSED(context), const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int WXUNUSED(flags))
 
1034
{
 
1035
    if (!IsShown())
 
1036
        return wxRICHTEXT_HITTEST_NONE;
 
1037
 
 
1038
    wxRect rect = GetRect();
 
1039
    if (pt.x >= rect.x && pt.x < rect.x + rect.width &&
 
1040
        pt.y >= rect.y && pt.y < rect.y + rect.height)
 
1041
    {
 
1042
        *obj = this;
 
1043
        *contextObj = GetParentContainer();
 
1044
        textPosition = GetRange().GetStart();
 
1045
        return wxRICHTEXT_HITTEST_ON;
 
1046
    }
 
1047
    else
 
1048
        return wxRICHTEXT_HITTEST_NONE;
 
1049
}
 
1050
 
 
1051
// Lays out the object first with a given amount of space, and then if no width was specified in attr,
 
1052
// lays out the object again using the maximum ('best') size
 
1053
bool wxRichTextObject::LayoutToBestSize(wxDC& dc, wxRichTextDrawingContext& context, wxRichTextBuffer* buffer,
 
1054
    const wxRichTextAttr& parentAttr, const wxRichTextAttr& attr,
 
1055
    const wxRect& availableParentSpace, const wxRect& availableContainerSpace,
 
1056
    int style)
 
1057
{
 
1058
    wxRect availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, attr, availableParentSpace, availableContainerSpace);
 
1059
    wxRect originalAvailableRect = availableChildRect;
 
1060
    Layout(dc, context, availableChildRect, availableContainerSpace, style);
 
1061
 
 
1062
    wxSize maxSize = GetMaxSize();
 
1063
 
 
1064
    // Don't ignore if maxSize.x is zero, since we need to redo the paragraph's lines
 
1065
    // on this basis
 
1066
    if (!attr.GetTextBoxAttr().GetWidth().IsValid() && maxSize.x < availableChildRect.width)
 
1067
    {
 
1068
        // Redo the layout with a fixed, minimum size this time.
 
1069
        Invalidate(wxRICHTEXT_ALL);
 
1070
        wxRichTextAttr newAttr(attr);
 
1071
        newAttr.GetTextBoxAttr().GetWidth().SetValue(maxSize.x, wxTEXT_ATTR_UNITS_PIXELS);
 
1072
        newAttr.GetTextBoxAttr().GetWidth().SetPosition(wxTEXT_BOX_ATTR_POSITION_ABSOLUTE);
 
1073
 
 
1074
        availableChildRect = AdjustAvailableSpace(dc, buffer, parentAttr, newAttr, availableParentSpace, availableContainerSpace);
 
1075
 
 
1076
        // If a paragraph, align the whole paragraph.
 
1077
        // Problem with this: if we're limited by a floating object, a line may be centered
 
1078
        // w.r.t. the smaller resulting box rather than the actual available width.
 
1079
        if (attr.HasAlignment() && !GetContainer()->GetFloatCollector()->HasFloats()) // FIXME: aligning whole paragraph not compatible with floating objects
 
1080
        {
 
1081
            // centering, right-justification
 
1082
            if (attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
 
1083
            {
 
1084
                availableChildRect.x = (originalAvailableRect.GetWidth() - availableChildRect.GetWidth())/2 + availableChildRect.x;
 
1085
            }
 
1086
            else if (attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
 
1087
            {
 
1088
                availableChildRect.x = availableChildRect.x + originalAvailableRect.GetWidth() - availableChildRect.GetWidth();
 
1089
            }
 
1090
        }
 
1091
 
 
1092
        Layout(dc, context, availableChildRect, availableContainerSpace, style);
 
1093
    }
 
1094
 
 
1095
    /*
 
1096
     __________________
 
1097
    |   ____________   |
 
1098
    |  |            |  |
 
1099
 
 
1100
 
 
1101
    */
 
1102
 
 
1103
    return true;
 
1104
}
 
1105
 
 
1106
// Move the object recursively, by adding the offset from old to new
 
1107
void wxRichTextObject::Move(const wxPoint& pt)
 
1108
{
 
1109
    SetPosition(pt);
 
1110
}
 
1111
 
 
1112
 
 
1113
/*!
 
1114
 * wxRichTextCompositeObject
 
1115
 * This is the base for drawable objects.
 
1116
 */
 
1117
 
 
1118
IMPLEMENT_CLASS(wxRichTextCompositeObject, wxRichTextObject)
 
1119
 
 
1120
wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject* parent):
 
1121
    wxRichTextObject(parent)
 
1122
{
 
1123
}
 
1124
 
 
1125
wxRichTextCompositeObject::~wxRichTextCompositeObject()
 
1126
{
 
1127
    DeleteChildren();
 
1128
}
 
1129
 
 
1130
/// Get the nth child
 
1131
wxRichTextObject* wxRichTextCompositeObject::GetChild(size_t n) const
 
1132
{
 
1133
    wxASSERT ( n < m_children.GetCount() );
 
1134
 
 
1135
    return m_children.Item(n)->GetData();
 
1136
}
 
1137
 
 
1138
/// Append a child, returning the position
 
1139
size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject* child)
 
1140
{
 
1141
    m_children.Append(child);
 
1142
    child->SetParent(this);
 
1143
    return m_children.GetCount() - 1;
 
1144
}
 
1145
 
 
1146
/// Insert the child in front of the given object, or at the beginning
 
1147
bool wxRichTextCompositeObject::InsertChild(wxRichTextObject* child, wxRichTextObject* inFrontOf)
 
1148
{
 
1149
    if (inFrontOf)
 
1150
    {
 
1151
        wxRichTextObjectList::compatibility_iterator node = m_children.Find(inFrontOf);
 
1152
        m_children.Insert(node, child);
 
1153
    }
 
1154
    else
 
1155
        m_children.Insert(child);
 
1156
    child->SetParent(this);
 
1157
 
 
1158
    return true;
 
1159
}
 
1160
 
 
1161
/// Delete the child
 
1162
bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject* child, bool deleteChild)
 
1163
{
 
1164
    wxRichTextObjectList::compatibility_iterator node = m_children.Find(child);
 
1165
    if (node)
 
1166
    {
 
1167
        wxRichTextObject* obj = node->GetData();
 
1168
        m_children.Erase(node);
 
1169
        if (deleteChild)
 
1170
            delete obj;
 
1171
 
 
1172
        return true;
 
1173
    }
 
1174
    return false;
 
1175
}
 
1176
 
 
1177
/// Delete all children
 
1178
bool wxRichTextCompositeObject::DeleteChildren()
 
1179
{
 
1180
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
1181
    while (node)
 
1182
    {
 
1183
        wxRichTextObjectList::compatibility_iterator oldNode = node;
 
1184
 
 
1185
        wxRichTextObject* child = node->GetData();
 
1186
        child->Dereference(); // Only delete if reference count is zero
 
1187
 
 
1188
        node = node->GetNext();
 
1189
        m_children.Erase(oldNode);
 
1190
    }
 
1191
 
 
1192
    return true;
 
1193
}
 
1194
 
 
1195
/// Get the child count
 
1196
size_t wxRichTextCompositeObject::GetChildCount() const
 
1197
{
 
1198
    return m_children.GetCount();
 
1199
}
 
1200
 
 
1201
/// Copy
 
1202
void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject& obj)
 
1203
{
 
1204
    wxRichTextObject::Copy(obj);
 
1205
 
 
1206
    DeleteChildren();
 
1207
 
 
1208
    wxRichTextObjectList::compatibility_iterator node = obj.m_children.GetFirst();
 
1209
    while (node)
 
1210
    {
 
1211
        wxRichTextObject* child = node->GetData();
 
1212
        wxRichTextObject* newChild = child->Clone();
 
1213
        newChild->SetParent(this);
 
1214
        m_children.Append(newChild);
 
1215
 
 
1216
        node = node->GetNext();
 
1217
    }
 
1218
}
 
1219
 
 
1220
/// Hit-testing: returns a flag indicating hit test details, plus
 
1221
/// information about position
 
1222
int wxRichTextCompositeObject::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
 
1223
{
 
1224
    if (!IsShown())
 
1225
        return wxRICHTEXT_HITTEST_NONE;
 
1226
 
 
1227
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
1228
    while (node)
 
1229
    {
 
1230
        wxRichTextObject* child = node->GetData();
 
1231
 
 
1232
        if (child->IsShown() && child->IsTopLevel() && (flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS))
 
1233
        {
 
1234
            // Just check if we hit the overall object
 
1235
            int ret = child->wxRichTextObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
 
1236
            if (ret != wxRICHTEXT_HITTEST_NONE)
 
1237
                return ret;
 
1238
        }
 
1239
        else if (child->IsShown())
 
1240
        {
 
1241
            int ret = child->HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
 
1242
            if (ret != wxRICHTEXT_HITTEST_NONE)
 
1243
                return ret;
 
1244
        }
 
1245
 
 
1246
        node = node->GetNext();
 
1247
    }
 
1248
 
 
1249
    return wxRICHTEXT_HITTEST_NONE;
 
1250
}
 
1251
 
 
1252
/// Finds the absolute position and row height for the given character position
 
1253
bool wxRichTextCompositeObject::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
 
1254
{
 
1255
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
1256
    while (node)
 
1257
    {
 
1258
        wxRichTextObject* child = node->GetData();
 
1259
 
 
1260
        // Don't recurse if the child is a top-level object,
 
1261
        // such as a text box, because the character position will no longer
 
1262
        // apply. By definition, a top-level object has its own range of
 
1263
        // character positions.
 
1264
        if (!child->IsTopLevel() && child->FindPosition(dc, context, index, pt, height, forceLineStart))
 
1265
            return true;
 
1266
 
 
1267
        node = node->GetNext();
 
1268
    }
 
1269
 
 
1270
    return false;
 
1271
}
 
1272
 
 
1273
/// Calculate range
 
1274
void wxRichTextCompositeObject::CalculateRange(long start, long& end)
 
1275
{
 
1276
    long current = start;
 
1277
    long lastEnd = current;
 
1278
 
 
1279
    if (IsTopLevel())
 
1280
    {
 
1281
        current = 0;
 
1282
        lastEnd = 0;
 
1283
    }
 
1284
 
 
1285
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
1286
    while (node)
 
1287
    {
 
1288
        wxRichTextObject* child = node->GetData();
 
1289
        long childEnd = 0;
 
1290
 
 
1291
        child->CalculateRange(current, childEnd);
 
1292
        lastEnd = childEnd;
 
1293
 
 
1294
        current = childEnd + 1;
 
1295
 
 
1296
        node = node->GetNext();
 
1297
    }
 
1298
 
 
1299
    if (IsTopLevel())
 
1300
    {
 
1301
        // A top-level object always has a range of size 1,
 
1302
        // because its children don't count at this level.
 
1303
        end = start;
 
1304
        m_range.SetRange(start, start);
 
1305
 
 
1306
        // An object with no children has zero length
 
1307
        if (m_children.GetCount() == 0)
 
1308
            lastEnd --;
 
1309
        m_ownRange.SetRange(0, lastEnd);
 
1310
    }
 
1311
    else
 
1312
    {
 
1313
        end = lastEnd;
 
1314
 
 
1315
        // An object with no children has zero length
 
1316
        if (m_children.GetCount() == 0)
 
1317
            end --;
 
1318
 
 
1319
        m_range.SetRange(start, end);
 
1320
    }
 
1321
}
 
1322
 
 
1323
/// Delete range from layout.
 
1324
bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange& range)
 
1325
{
 
1326
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
1327
 
 
1328
    while (node)
 
1329
    {
 
1330
        wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
 
1331
        wxRichTextObjectList::compatibility_iterator next = node->GetNext();
 
1332
 
 
1333
        // Delete the range in each paragraph
 
1334
 
 
1335
        // When a chunk has been deleted, internally the content does not
 
1336
        // now match the ranges.
 
1337
        // However, so long as deletion is not done on the same object twice this is OK.
 
1338
        // If you may delete content from the same object twice, recalculate
 
1339
        // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
 
1340
        // adjust the range you're deleting accordingly.
 
1341
 
 
1342
        if (!obj->GetRange().IsOutside(range))
 
1343
        {
 
1344
            // No need to delete within a top-level object; just removing this object will do fine
 
1345
            if (!obj->IsTopLevel())
 
1346
                obj->DeleteRange(range);
 
1347
 
 
1348
            // Delete an empty object, or paragraph within this range.
 
1349
            if (obj->IsEmpty() ||
 
1350
                (range.GetStart() <= obj->GetRange().GetStart() && range.GetEnd() >= obj->GetRange().GetEnd()))
 
1351
            {
 
1352
                // An empty paragraph has length 1, so won't be deleted unless the
 
1353
                // whole range is deleted.
 
1354
                RemoveChild(obj, true);
 
1355
            }
 
1356
        }
 
1357
 
 
1358
        node = next;
 
1359
    }
 
1360
 
 
1361
    return true;
 
1362
}
 
1363
 
 
1364
/// Get any text in this object for the given range
 
1365
wxString wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange& range) const
 
1366
{
 
1367
    wxString text;
 
1368
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
1369
    while (node)
 
1370
    {
 
1371
        wxRichTextObject* child = node->GetData();
 
1372
        wxRichTextRange childRange = range;
 
1373
        if (!child->GetRange().IsOutside(range))
 
1374
        {
 
1375
            childRange.LimitTo(child->GetRange());
 
1376
 
 
1377
            wxString childText = child->GetTextForRange(childRange);
 
1378
 
 
1379
            text += childText;
 
1380
        }
 
1381
        node = node->GetNext();
 
1382
    }
 
1383
 
 
1384
    return text;
 
1385
}
 
1386
 
 
1387
/// Get the child object at the given character position
 
1388
wxRichTextObject* wxRichTextCompositeObject::GetChildAtPosition(long pos) const
 
1389
{
 
1390
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
1391
    while (node)
 
1392
    {
 
1393
        wxRichTextObject* child = node->GetData();
 
1394
        if (child->GetRange().GetStart() == pos)
 
1395
            return child;
 
1396
        node = node->GetNext();
 
1397
    }
 
1398
    return NULL;
 
1399
}
 
1400
 
 
1401
/// Recursively merge all pieces that can be merged.
 
1402
bool wxRichTextCompositeObject::Defragment(const wxRichTextRange& range)
 
1403
{
 
1404
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
1405
    while (node)
 
1406
    {
 
1407
        wxRichTextObject* child = node->GetData();
 
1408
        if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
 
1409
        {
 
1410
            wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject);
 
1411
            if (composite)
 
1412
                composite->Defragment();
 
1413
 
 
1414
            if (node->GetNext())
 
1415
            {
 
1416
                wxRichTextObject* nextChild = node->GetNext()->GetData();
 
1417
                if (child->CanMerge(nextChild) && child->Merge(nextChild))
 
1418
                {
 
1419
                    nextChild->Dereference();
 
1420
                    m_children.Erase(node->GetNext());
 
1421
 
 
1422
                    // Don't set node -- we'll see if we can merge again with the next
 
1423
                    // child.
 
1424
                }
 
1425
                else
 
1426
                    node = node->GetNext();
 
1427
            }
 
1428
            else
 
1429
                node = node->GetNext();
 
1430
        }
 
1431
        else
 
1432
            node = node->GetNext();
 
1433
    }
 
1434
 
 
1435
    // Delete any remaining empty objects, but leave at least one empty object per composite object.
 
1436
    if (GetChildCount() > 1)
 
1437
    {
 
1438
        node = m_children.GetFirst();
 
1439
        while (node)
 
1440
        {
 
1441
            wxRichTextObjectList::compatibility_iterator next = node->GetNext();
 
1442
            wxRichTextObject* child = node->GetData();
 
1443
            if (range == wxRICHTEXT_ALL || !child->GetRange().IsOutside(range))
 
1444
            {
 
1445
                if (child->IsEmpty())
 
1446
                {
 
1447
                    child->Dereference();
 
1448
                    m_children.Erase(node);
 
1449
                }
 
1450
                node = next;
 
1451
            }
 
1452
            else
 
1453
                node = node->GetNext();
 
1454
        }
 
1455
    }
 
1456
 
 
1457
    return true;
 
1458
}
 
1459
 
 
1460
/// Dump to output stream for debugging
 
1461
void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
 
1462
{
 
1463
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
1464
    while (node)
 
1465
    {
 
1466
        wxRichTextObject* child = node->GetData();
 
1467
        child->Dump(stream);
 
1468
        node = node->GetNext();
 
1469
    }
 
1470
}
 
1471
 
 
1472
/// Get/set the object size for the given range. Returns false if the range
 
1473
/// is invalid for this object.
 
1474
bool wxRichTextCompositeObject::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
 
1475
{
 
1476
    if (!range.IsWithin(GetRange()))
 
1477
        return false;
 
1478
 
 
1479
    wxSize sz;
 
1480
 
 
1481
    wxArrayInt childExtents;
 
1482
    wxArrayInt* p;
 
1483
    if (partialExtents)
 
1484
        p = & childExtents;
 
1485
    else
 
1486
        p = NULL;
 
1487
 
 
1488
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
1489
    while (node)
 
1490
    {
 
1491
        wxRichTextObject* child = node->GetData();
 
1492
        if (!child->GetRange().IsOutside(range))
 
1493
        {
 
1494
            // Floating objects have a zero size within the paragraph.
 
1495
            if (child->IsFloating())
 
1496
            {
 
1497
                if (partialExtents)
 
1498
                {
 
1499
                    int lastSize;
 
1500
                    if (partialExtents->GetCount() > 0)
 
1501
                        lastSize = (*partialExtents)[partialExtents->GetCount()-1];
 
1502
                    else
 
1503
                        lastSize = 0;
 
1504
 
 
1505
                    partialExtents->Add(0 /* zero size */ + lastSize);
 
1506
                }
 
1507
            }
 
1508
            else
 
1509
            {
 
1510
                wxSize childSize;
 
1511
 
 
1512
                wxRichTextRange rangeToUse = range;
 
1513
                rangeToUse.LimitTo(child->GetRange());
 
1514
                if (child->IsTopLevel())
 
1515
                    rangeToUse = child->GetOwnRange();
 
1516
 
 
1517
                int childDescent = 0;
 
1518
 
 
1519
                // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we're already cached the size,
 
1520
                // but it's only going to be used after caching has taken place.
 
1521
                if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
 
1522
                {
 
1523
                    childDescent = child->GetDescent();
 
1524
                    childSize = child->GetCachedSize();
 
1525
 
 
1526
                    sz.y = wxMax(sz.y, childSize.y);
 
1527
                    sz.x += childSize.x;
 
1528
                    descent = wxMax(descent, childDescent);
 
1529
                }
 
1530
                else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), p))
 
1531
                {
 
1532
                    sz.y = wxMax(sz.y, childSize.y);
 
1533
                    sz.x += childSize.x;
 
1534
                    descent = wxMax(descent, childDescent);
 
1535
 
 
1536
                    if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange() || child->IsTopLevel()))
 
1537
                    {
 
1538
                        child->SetCachedSize(childSize);
 
1539
                        child->SetDescent(childDescent);
 
1540
                    }
 
1541
 
 
1542
                    if (partialExtents)
 
1543
                    {
 
1544
                        int lastSize;
 
1545
                        if (partialExtents->GetCount() > 0)
 
1546
                            lastSize = (*partialExtents)[partialExtents->GetCount()-1];
 
1547
                        else
 
1548
                            lastSize = 0;
 
1549
 
 
1550
                        size_t i;
 
1551
                        for (i = 0; i < childExtents.GetCount(); i++)
 
1552
                        {
 
1553
                            partialExtents->Add(childExtents[i] + lastSize);
 
1554
                        }
 
1555
                    }
 
1556
                }
 
1557
            }
 
1558
 
 
1559
            if (p)
 
1560
                p->Clear();
 
1561
        }
 
1562
 
 
1563
        node = node->GetNext();
 
1564
    }
 
1565
    size = sz;
 
1566
    return true;
 
1567
}
 
1568
 
 
1569
// Invalidate the buffer. With no argument, invalidates whole buffer.
 
1570
void wxRichTextCompositeObject::Invalidate(const wxRichTextRange& invalidRange)
 
1571
{
 
1572
    wxRichTextObject::Invalidate(invalidRange);
 
1573
 
 
1574
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
1575
    while (node)
 
1576
    {
 
1577
        wxRichTextObject* child = node->GetData();
 
1578
        if (invalidRange != wxRICHTEXT_ALL && invalidRange != wxRICHTEXT_NONE && child->GetRange().IsOutside(invalidRange))
 
1579
        {
 
1580
            // Skip
 
1581
        }
 
1582
        else if (child->IsTopLevel())
 
1583
        {
 
1584
            if (invalidRange == wxRICHTEXT_NONE)
 
1585
                child->Invalidate(wxRICHTEXT_NONE);
 
1586
            else
 
1587
                child->Invalidate(wxRICHTEXT_ALL); // All children must be invalidated if within parent range
 
1588
        }
 
1589
        else
 
1590
            child->Invalidate(invalidRange);
 
1591
        node = node->GetNext();
 
1592
    }
 
1593
}
 
1594
 
 
1595
// Move the object recursively, by adding the offset from old to new
 
1596
void wxRichTextCompositeObject::Move(const wxPoint& pt)
 
1597
{
 
1598
    wxPoint oldPos = GetPosition();
 
1599
    SetPosition(pt);
 
1600
    wxPoint offset = pt - oldPos;
 
1601
 
 
1602
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
1603
    while (node)
 
1604
    {
 
1605
        wxRichTextObject* child = node->GetData();
 
1606
        wxPoint childPos = child->GetPosition() + offset;
 
1607
        child->Move(childPos);
 
1608
        node = node->GetNext();
 
1609
    }
 
1610
}
 
1611
 
 
1612
 
 
1613
/*!
 
1614
 * wxRichTextParagraphLayoutBox
 
1615
 * This box knows how to lay out paragraphs.
 
1616
 */
 
1617
 
 
1618
IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextCompositeObject)
 
1619
 
 
1620
wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject* parent):
 
1621
    wxRichTextCompositeObject(parent)
 
1622
{
 
1623
    Init();
 
1624
}
 
1625
 
 
1626
wxRichTextParagraphLayoutBox::~wxRichTextParagraphLayoutBox()
 
1627
{
 
1628
    if (m_floatCollector)
 
1629
    {
 
1630
        delete m_floatCollector;
 
1631
        m_floatCollector = NULL;
 
1632
    }
 
1633
}
 
1634
 
 
1635
/// Initialize the object.
 
1636
void wxRichTextParagraphLayoutBox::Init()
 
1637
{
 
1638
    m_ctrl = NULL;
 
1639
 
 
1640
    // For now, assume is the only box and has no initial size.
 
1641
    m_range = wxRichTextRange(0, -1);
 
1642
    m_ownRange = wxRichTextRange(0, -1);
 
1643
 
 
1644
    m_invalidRange = wxRICHTEXT_ALL;
 
1645
 
 
1646
    m_partialParagraph = false;
 
1647
    m_floatCollector = NULL;
 
1648
}
 
1649
 
 
1650
void wxRichTextParagraphLayoutBox::Clear()
 
1651
{
 
1652
    DeleteChildren();
 
1653
 
 
1654
    if (m_floatCollector)
 
1655
        delete m_floatCollector;
 
1656
    m_floatCollector = NULL;
 
1657
    m_partialParagraph = false;
 
1658
}
 
1659
 
 
1660
/// Copy
 
1661
void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
 
1662
{
 
1663
    Clear();
 
1664
 
 
1665
    wxRichTextCompositeObject::Copy(obj);
 
1666
 
 
1667
    m_partialParagraph = obj.m_partialParagraph;
 
1668
    m_defaultAttributes = obj.m_defaultAttributes;
 
1669
}
 
1670
 
 
1671
// Gather information about floating objects; only gather floats for those paragraphs that
 
1672
// will not be formatted again due to optimization, after which floats will be gathered per-paragraph
 
1673
// during layout.
 
1674
bool wxRichTextParagraphLayoutBox::UpdateFloatingObjects(const wxRect& availableRect, wxRichTextObject* untilObj)
 
1675
{
 
1676
    if (m_floatCollector != NULL)
 
1677
        delete m_floatCollector;
 
1678
    m_floatCollector = new wxRichTextFloatCollector(availableRect);
 
1679
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
1680
    // Only gather floats up to the point we'll start formatting paragraphs.
 
1681
    while (untilObj && node && node->GetData() != untilObj)
 
1682
    {
 
1683
        wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
 
1684
        wxASSERT (child != NULL);
 
1685
        if (child)
 
1686
            m_floatCollector->CollectFloat(child);
 
1687
        node = node->GetNext();
 
1688
    }
 
1689
 
 
1690
    return true;
 
1691
}
 
1692
 
 
1693
// Returns the style sheet associated with the overall buffer.
 
1694
wxRichTextStyleSheet* wxRichTextParagraphLayoutBox::GetStyleSheet() const
 
1695
{
 
1696
    return GetBuffer() ? GetBuffer()->GetStyleSheet() : (wxRichTextStyleSheet*) NULL;
 
1697
}
 
1698
 
 
1699
// Get the number of floating objects at this level
 
1700
int wxRichTextParagraphLayoutBox::GetFloatingObjectCount() const
 
1701
{
 
1702
    if (m_floatCollector)
 
1703
        return m_floatCollector->GetFloatingObjectCount();
 
1704
    else
 
1705
        return 0;
 
1706
}
 
1707
 
 
1708
// Get a list of floating objects
 
1709
bool wxRichTextParagraphLayoutBox::GetFloatingObjects(wxRichTextObjectList& objects) const
 
1710
{
 
1711
    if (m_floatCollector)
 
1712
    {
 
1713
        return m_floatCollector->GetFloatingObjects(objects);
 
1714
    }
 
1715
    else
 
1716
        return false;
 
1717
}
 
1718
 
 
1719
// Calculate ranges
 
1720
void wxRichTextParagraphLayoutBox::UpdateRanges()
 
1721
{
 
1722
    long start = 0;
 
1723
    if (GetParent())
 
1724
        start = GetRange().GetStart();
 
1725
    long end;
 
1726
    CalculateRange(start, end);
 
1727
}
 
1728
 
 
1729
// HitTest
 
1730
int wxRichTextParagraphLayoutBox::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
 
1731
{
 
1732
    if (!IsShown())
 
1733
        return wxRICHTEXT_HITTEST_NONE;
 
1734
 
 
1735
    int ret = wxRICHTEXT_HITTEST_NONE;
 
1736
    if (m_floatCollector && (flags & wxRICHTEXT_HITTEST_NO_FLOATING_OBJECTS) == 0)
 
1737
        ret = m_floatCollector->HitTest(dc, context, pt, textPosition, obj, flags);
 
1738
 
 
1739
    if (ret == wxRICHTEXT_HITTEST_NONE)
 
1740
        return wxRichTextCompositeObject::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
 
1741
    else
 
1742
    {
 
1743
        *contextObj = this;
 
1744
        return ret;
 
1745
    }
 
1746
}
 
1747
 
 
1748
/// Draw the floating objects
 
1749
void wxRichTextParagraphLayoutBox::DrawFloats(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
 
1750
{
 
1751
    if (m_floatCollector)
 
1752
        m_floatCollector->Draw(dc, context, range, selection, rect, descent, style);
 
1753
}
 
1754
 
 
1755
void wxRichTextParagraphLayoutBox::MoveAnchoredObjectToParagraph(wxRichTextParagraph* from, wxRichTextParagraph* to, wxRichTextObject* obj)
 
1756
{
 
1757
    if (from == to)
 
1758
        return;
 
1759
 
 
1760
    from->RemoveChild(obj);
 
1761
    to->AppendChild(obj);
 
1762
}
 
1763
 
 
1764
/// Draw the item
 
1765
bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
 
1766
{
 
1767
    if (!IsShown())
 
1768
        return true;
 
1769
 
 
1770
    wxRect thisRect(GetPosition(), GetCachedSize());
 
1771
 
 
1772
    wxRichTextAttr attr(GetAttributes());
 
1773
    context.ApplyVirtualAttributes(attr, this);
 
1774
 
 
1775
    int flags = style;
 
1776
    if (selection.IsValid() && GetParentContainer() != this && selection.WithinSelection(GetRange().GetStart(), GetParentContainer()))
 
1777
        flags |= wxRICHTEXT_DRAW_SELECTED;
 
1778
 
 
1779
    // Don't draw guidelines if at top level
 
1780
    int theseFlags = flags;
 
1781
    if (!GetParent())
 
1782
        theseFlags &= ~wxRICHTEXT_DRAW_GUIDELINES;
 
1783
    DrawBoxAttributes(dc, GetBuffer(), attr, thisRect, theseFlags);
 
1784
 
 
1785
    DrawFloats(dc, context, range, selection, rect, descent, style);
 
1786
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
1787
    while (node)
 
1788
    {
 
1789
        wxRichTextObject* child = node->GetData();
 
1790
 
 
1791
        if (child && !child->GetRange().IsOutside(range))
 
1792
        {
 
1793
            wxRect childRect(child->GetPosition(), child->GetCachedSize());
 
1794
            wxRichTextRange childRange = range;
 
1795
            if (child->IsTopLevel())
 
1796
            {
 
1797
                childRange = child->GetOwnRange();
 
1798
            }
 
1799
 
 
1800
            if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetTop() > rect.GetBottom())
 
1801
            {
 
1802
                // Stop drawing
 
1803
                break;
 
1804
            }
 
1805
            else if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetBottom() < rect.GetTop())
 
1806
            {
 
1807
                // Skip
 
1808
            }
 
1809
            else
 
1810
                child->Draw(dc, context, childRange, selection, rect, descent, style);
 
1811
        }
 
1812
 
 
1813
        node = node->GetNext();
 
1814
    }
 
1815
    return true;
 
1816
}
 
1817
 
 
1818
/// Lay the item out
 
1819
bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
 
1820
{
 
1821
    SetPosition(rect.GetPosition());
 
1822
 
 
1823
    if (!IsShown())
 
1824
        return true;
 
1825
 
 
1826
    wxRect availableSpace;
 
1827
    bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
 
1828
 
 
1829
    wxRichTextAttr attr(GetAttributes());
 
1830
    context.ApplyVirtualAttributes(attr, this);
 
1831
 
 
1832
    // If only laying out a specific area, the passed rect has a different meaning:
 
1833
    // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize,
 
1834
    // so that during a size, only the visible part will be relaid out, or
 
1835
    // it would take too long causing flicker. As an approximation, we assume that
 
1836
    // everything up to the start of the visible area is laid out correctly.
 
1837
    if (formatRect)
 
1838
    {
 
1839
        wxRect rect2(0, 0, rect.width, rect.height);
 
1840
        availableSpace = GetAvailableContentArea(dc, context, rect2);
 
1841
 
 
1842
        // Invalidate the part of the buffer from the first visible line
 
1843
        // to the end. If other parts of the buffer are currently invalid,
 
1844
        // then they too will be taken into account if they are above
 
1845
        // the visible point.
 
1846
        long startPos = 0;
 
1847
        wxRichTextLine* line = GetLineAtYPosition(rect.y);
 
1848
        if (line)
 
1849
            startPos = line->GetAbsoluteRange().GetStart();
 
1850
 
 
1851
        Invalidate(wxRichTextRange(startPos, GetOwnRange().GetEnd()));
 
1852
    }
 
1853
    else
 
1854
    {
 
1855
        availableSpace = GetAvailableContentArea(dc, context, rect);
 
1856
    }
 
1857
 
 
1858
    // Fix the width if we're at the top level
 
1859
    if (!GetParent())
 
1860
        attr.GetTextBoxAttr().GetWidth().SetValue(rect.GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
 
1861
 
 
1862
    int leftMargin, rightMargin, topMargin, bottomMargin;
 
1863
    wxRichTextObject::GetTotalMargin(dc, GetBuffer(), attr, leftMargin, rightMargin,
 
1864
            topMargin, bottomMargin);
 
1865
 
 
1866
    int maxWidth = 0;
 
1867
    int maxHeight = 0;
 
1868
 
 
1869
    // The maximum paragraph maximum width, so we can set the overall maximum width for this object
 
1870
    int maxMaxWidth = 0;
 
1871
 
 
1872
    // The maximum paragraph minimum width, so we can set the overall minimum width for this object
 
1873
    int maxMinWidth = 0;
 
1874
 
 
1875
    // If we have vertical alignment, we must recalculate everything.
 
1876
    bool hasVerticalAlignment = (attr.GetTextBoxAttr().HasVerticalAlignment() &&
 
1877
        (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP));
 
1878
 
 
1879
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
1880
 
 
1881
    bool layoutAll = true;
 
1882
 
 
1883
    // Get invalid range, rounding to paragraph start/end.
 
1884
    wxRichTextRange invalidRange = GetInvalidRange(true);
 
1885
 
 
1886
    if (invalidRange == wxRICHTEXT_NONE && !formatRect)
 
1887
        return true;
 
1888
 
 
1889
    if (invalidRange == wxRICHTEXT_ALL || hasVerticalAlignment)
 
1890
        layoutAll = true;
 
1891
    else    // If we know what range is affected, start laying out from that point on.
 
1892
        if (invalidRange.GetStart() >= GetOwnRange().GetStart())
 
1893
    {
 
1894
        wxRichTextParagraph* firstParagraph = GetParagraphAtPosition(invalidRange.GetStart());
 
1895
        if (firstParagraph)
 
1896
        {
 
1897
            wxRichTextObjectList::compatibility_iterator firstNode = m_children.Find(firstParagraph);
 
1898
            wxRichTextObjectList::compatibility_iterator previousNode;
 
1899
            if ( firstNode )
 
1900
                previousNode = firstNode->GetPrevious();
 
1901
            if (firstNode)
 
1902
            {
 
1903
                if (previousNode)
 
1904
                {
 
1905
                    wxRichTextParagraph* previousParagraph = wxDynamicCast(previousNode->GetData(), wxRichTextParagraph);
 
1906
                    availableSpace.y = previousParagraph->GetPosition().y + previousParagraph->GetCachedSize().y;
 
1907
                }
 
1908
 
 
1909
                // Now we're going to start iterating from the first affected paragraph.
 
1910
                node = firstNode;
 
1911
 
 
1912
                layoutAll = false;
 
1913
            }
 
1914
        }
 
1915
    }
 
1916
 
 
1917
    // Gather information about only those floating objects that will not be formatted,
 
1918
    // after which floats will be gathered per-paragraph during layout.
 
1919
    UpdateFloatingObjects(availableSpace, node ? node->GetData() : (wxRichTextObject*) NULL);
 
1920
 
 
1921
    // A way to force speedy rest-of-buffer layout (the 'else' below)
 
1922
    bool forceQuickLayout = false;
 
1923
 
 
1924
    // First get the size of the paragraphs we won't be laying out
 
1925
    wxRichTextObjectList::compatibility_iterator n = m_children.GetFirst();
 
1926
    while (n && n != node)
 
1927
    {
 
1928
        wxRichTextParagraph* child = wxDynamicCast(n->GetData(), wxRichTextParagraph);
 
1929
        if (child)
 
1930
        {
 
1931
            maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
 
1932
            maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
 
1933
            maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
 
1934
        }
 
1935
        n = n->GetNext();
 
1936
    }
 
1937
 
 
1938
    while (node)
 
1939
    {
 
1940
        // Assume this box only contains paragraphs
 
1941
 
 
1942
        wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
 
1943
        // Unsure if this is needed
 
1944
        // wxCHECK_MSG( child, false, wxT("Unknown object in layout") );
 
1945
 
 
1946
        if (child && child->IsShown())
 
1947
        {
 
1948
            // TODO: what if the child hasn't been laid out (e.g. involved in Undo) but still has 'old' lines
 
1949
            if ( !forceQuickLayout &&
 
1950
                    (layoutAll ||
 
1951
                        child->GetLines().IsEmpty() ||
 
1952
                            !child->GetRange().IsOutside(invalidRange)) )
 
1953
            {
 
1954
                // Lays out the object first with a given amount of space, and then if no width was specified in attr,
 
1955
                // lays out the object again using the minimum size
 
1956
                child->LayoutToBestSize(dc, context, GetBuffer(),
 
1957
                        attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
 
1958
 
 
1959
                // Layout must set the cached size
 
1960
                availableSpace.y += child->GetCachedSize().y;
 
1961
                maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
 
1962
                maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
 
1963
                maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
 
1964
 
 
1965
                // If we're just formatting the visible part of the buffer,
 
1966
                // and we're now past the bottom of the window, and we don't have any
 
1967
                // floating objects (since they may cause wrapping to change for the rest of the
 
1968
                // the buffer), start quick layout.
 
1969
                if (!hasVerticalAlignment && formatRect && child->GetPosition().y > rect.GetBottom() && GetFloatingObjectCount() == 0)
 
1970
                    forceQuickLayout = true;
 
1971
            }
 
1972
            else
 
1973
            {
 
1974
                // We're outside the immediately affected range, so now let's just
 
1975
                // move everything up or down. This assumes that all the children have previously
 
1976
                // been laid out and have wrapped line lists associated with them.
 
1977
                // TODO: check all paragraphs before the affected range.
 
1978
 
 
1979
                int inc = availableSpace.y - child->GetPosition().y;
 
1980
 
 
1981
                while (node)
 
1982
                {
 
1983
                    wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
 
1984
                    if (child)
 
1985
                    {
 
1986
                        if (child->GetLines().GetCount() == 0)
 
1987
                        {
 
1988
                            // Lays out the object first with a given amount of space, and then if no width was specified in attr,
 
1989
                            // lays out the object again using the minimum size
 
1990
                            child->LayoutToBestSize(dc, context, GetBuffer(),
 
1991
                                        attr, child->GetAttributes(), availableSpace, rect, style&~wxRICHTEXT_LAYOUT_SPECIFIED_RECT);
 
1992
 
 
1993
                            //child->Layout(dc, availableChildRect, style);
 
1994
                        }
 
1995
                        else
 
1996
                            child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + inc));
 
1997
 
 
1998
                        availableSpace.y += child->GetCachedSize().y;
 
1999
                        maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
 
2000
                        maxMinWidth = wxMax(maxMinWidth, child->GetMinSize().x);
 
2001
                        maxMaxWidth = wxMax(maxMaxWidth, child->GetMaxSize().x);
 
2002
                    }
 
2003
 
 
2004
                    node = node->GetNext();
 
2005
                }
 
2006
                break;
 
2007
            }
 
2008
        }
 
2009
 
 
2010
        node = node->GetNext();
 
2011
    }
 
2012
 
 
2013
    node = m_children.GetLast();
 
2014
    if (node && node->GetData()->IsShown())
 
2015
    {
 
2016
        wxRichTextObject* child = node->GetData();
 
2017
        maxHeight = child->GetPosition().y - (GetPosition().y + topMargin) + child->GetCachedSize().y;
 
2018
    }
 
2019
    else
 
2020
        maxHeight = 0; // topMargin + bottomMargin;
 
2021
 
 
2022
    // Check the bottom edge of any floating object
 
2023
    if (GetFloatCollector() && GetFloatCollector()->HasFloats())
 
2024
    {
 
2025
        int bottom = GetFloatCollector()->GetLastRectBottom();
 
2026
        if (bottom > maxHeight)
 
2027
            maxHeight = bottom;
 
2028
    }
 
2029
 
 
2030
    if (attr.GetTextBoxAttr().GetSize().GetWidth().IsValid())
 
2031
    {
 
2032
        wxRect r = AdjustAvailableSpace(dc, GetBuffer(), wxRichTextAttr() /* not used */, attr, parentRect, parentRect);
 
2033
        int w = r.GetWidth();
 
2034
 
 
2035
        // Convert external to content rect
 
2036
        w = w - leftMargin - rightMargin;
 
2037
        maxWidth = wxMax(maxWidth, w);
 
2038
        maxMaxWidth = wxMax(maxMaxWidth, w);
 
2039
    }
 
2040
    else
 
2041
    {
 
2042
        // TODO: Make sure the layout box's position reflects
 
2043
        // the position of the children, but without
 
2044
        // breaking layout of a box within a paragraph.
 
2045
    }
 
2046
 
 
2047
    // TODO: (also in para layout) should set the
 
2048
    // object's size to an absolute one if specified,
 
2049
    // but if not specified, calculate it from content.
 
2050
 
 
2051
    // We need to add back the margins etc.
 
2052
    {
 
2053
        wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
 
2054
        contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, maxHeight));
 
2055
        GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
 
2056
        SetCachedSize(marginRect.GetSize());
 
2057
    }
 
2058
 
 
2059
    // The maximum size is the greatest of all maximum widths for all paragraphs.
 
2060
    {
 
2061
        wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
 
2062
        contentRect = wxRect(wxPoint(0, 0), wxSize(maxMaxWidth, maxHeight)); // Actually max height is a lie, we can't know it
 
2063
        GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
 
2064
        SetMaxSize(marginRect.GetSize());
 
2065
    }
 
2066
 
 
2067
    // The minimum size is the greatest of all minimum widths for all paragraphs.
 
2068
    {
 
2069
        wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
 
2070
        contentRect = wxRect(wxPoint(0, 0), wxSize(maxMinWidth, maxHeight)); // Actually max height is a lie, we can't know it
 
2071
        GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
 
2072
        SetMinSize(marginRect.GetSize());
 
2073
    }
 
2074
 
 
2075
    if (attr.GetTextBoxAttr().HasVerticalAlignment() &&
 
2076
        (attr.GetTextBoxAttr().GetVerticalAlignment() > wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_TOP))
 
2077
    {
 
2078
        int yOffset = 0;
 
2079
        int leftOverSpace = availableSpace.height - topMargin - bottomMargin - maxHeight;
 
2080
        if (leftOverSpace > 0)
 
2081
        {
 
2082
            if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_CENTRE)
 
2083
            {
 
2084
                yOffset = (leftOverSpace/2);
 
2085
            }
 
2086
            else if (attr.GetTextBoxAttr().GetVerticalAlignment() == wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_BOTTOM)
 
2087
            {
 
2088
                yOffset = leftOverSpace;
 
2089
            }
 
2090
        }
 
2091
 
 
2092
        // Move all the children to vertically align the content
 
2093
        // This doesn't take into account floating objects, unfortunately.
 
2094
        if (yOffset != 0)
 
2095
        {
 
2096
            wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
2097
            while (node)
 
2098
            {
 
2099
                wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
 
2100
                if (child)
 
2101
                    child->Move(wxPoint(child->GetPosition().x, child->GetPosition().y + yOffset));
 
2102
 
 
2103
                node = node->GetNext();
 
2104
            }
 
2105
        }
 
2106
    }
 
2107
 
 
2108
    m_invalidRange = wxRICHTEXT_NONE;
 
2109
 
 
2110
    return true;
 
2111
}
 
2112
 
 
2113
/// Get/set the size for the given range.
 
2114
bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* WXUNUSED(partialExtents)) const
 
2115
{
 
2116
    wxSize sz;
 
2117
 
 
2118
    wxRichTextObjectList::compatibility_iterator startPara = wxRichTextObjectList::compatibility_iterator();
 
2119
    wxRichTextObjectList::compatibility_iterator endPara = wxRichTextObjectList::compatibility_iterator();
 
2120
 
 
2121
    // First find the first paragraph whose starting position is within the range.
 
2122
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
2123
    while (node)
 
2124
    {
 
2125
        // child is a paragraph
 
2126
        wxRichTextObject* child = node->GetData();
 
2127
        const wxRichTextRange& r = child->GetRange();
 
2128
 
 
2129
        if (r.GetStart() <= range.GetStart() && r.GetEnd() >= range.GetStart())
 
2130
        {
 
2131
            startPara = node;
 
2132
            break;
 
2133
        }
 
2134
 
 
2135
        node = node->GetNext();
 
2136
    }
 
2137
 
 
2138
    // Next find the last paragraph containing part of the range
 
2139
    node = m_children.GetFirst();
 
2140
    while (node)
 
2141
    {
 
2142
        // child is a paragraph
 
2143
        wxRichTextObject* child = node->GetData();
 
2144
        const wxRichTextRange& r = child->GetRange();
 
2145
 
 
2146
        if (r.GetStart() <= range.GetEnd() && r.GetEnd() >= range.GetEnd())
 
2147
        {
 
2148
            endPara = node;
 
2149
            break;
 
2150
        }
 
2151
 
 
2152
        node = node->GetNext();
 
2153
    }
 
2154
 
 
2155
    if (!startPara || !endPara)
 
2156
        return false;
 
2157
 
 
2158
    // Now we can add up the sizes
 
2159
    for (node = startPara; node ; node = node->GetNext())
 
2160
    {
 
2161
        // child is a paragraph
 
2162
        wxRichTextObject* child = node->GetData();
 
2163
        const wxRichTextRange& childRange = child->GetRange();
 
2164
        wxRichTextRange rangeToFind = range;
 
2165
        rangeToFind.LimitTo(childRange);
 
2166
 
 
2167
        if (child->IsTopLevel())
 
2168
            rangeToFind = child->GetOwnRange();
 
2169
 
 
2170
        wxSize childSize;
 
2171
 
 
2172
        int childDescent = 0;
 
2173
        child->GetRangeSize(rangeToFind, childSize, childDescent, dc, context, flags, position);
 
2174
 
 
2175
        descent = wxMax(childDescent, descent);
 
2176
 
 
2177
        sz.x = wxMax(sz.x, childSize.x);
 
2178
        sz.y += childSize.y;
 
2179
 
 
2180
        if (node == endPara)
 
2181
            break;
 
2182
    }
 
2183
 
 
2184
    size = sz;
 
2185
 
 
2186
    return true;
 
2187
}
 
2188
 
 
2189
/// Get the paragraph at the given position
 
2190
wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos, bool caretPosition) const
 
2191
{
 
2192
    if (caretPosition)
 
2193
        pos ++;
 
2194
 
 
2195
    // First find the first paragraph whose starting position is within the range.
 
2196
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
2197
    while (node)
 
2198
    {
 
2199
        // child is a paragraph
 
2200
        wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
 
2201
        // wxASSERT (child != NULL);
 
2202
 
 
2203
        if (child)
 
2204
        {
 
2205
            // Return first child in buffer if position is -1
 
2206
            // if (pos == -1)
 
2207
            //    return child;
 
2208
 
 
2209
            if (child->GetRange().Contains(pos))
 
2210
                return child;
 
2211
        }
 
2212
 
 
2213
        node = node->GetNext();
 
2214
    }
 
2215
    return NULL;
 
2216
}
 
2217
 
 
2218
/// Get the line at the given position
 
2219
wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos, bool caretPosition) const
 
2220
{
 
2221
    if (caretPosition)
 
2222
        pos ++;
 
2223
 
 
2224
    // First find the first paragraph whose starting position is within the range.
 
2225
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
2226
    while (node)
 
2227
    {
 
2228
        wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
 
2229
        if (obj->GetRange().Contains(pos))
 
2230
        {
 
2231
            // child is a paragraph
 
2232
            wxRichTextParagraph* child = wxDynamicCast(obj, wxRichTextParagraph);
 
2233
            // wxASSERT (child != NULL);
 
2234
 
 
2235
            if (child)
 
2236
            {
 
2237
                wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
 
2238
                while (node2)
 
2239
                {
 
2240
                    wxRichTextLine* line = node2->GetData();
 
2241
 
 
2242
                    wxRichTextRange range = line->GetAbsoluteRange();
 
2243
 
 
2244
                    if (range.Contains(pos) ||
 
2245
 
 
2246
                        // If the position is end-of-paragraph, then return the last line of
 
2247
                        // of the paragraph.
 
2248
                        ((range.GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd())))
 
2249
                        return line;
 
2250
 
 
2251
                    node2 = node2->GetNext();
 
2252
                }
 
2253
            }
 
2254
        }
 
2255
 
 
2256
        node = node->GetNext();
 
2257
    }
 
2258
 
 
2259
    int lineCount = GetLineCount();
 
2260
    if (lineCount > 0)
 
2261
        return GetLineForVisibleLineNumber(lineCount-1);
 
2262
    else
 
2263
        return NULL;
 
2264
}
 
2265
 
 
2266
/// Get the line at the given y pixel position, or the last line.
 
2267
wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y) const
 
2268
{
 
2269
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
2270
    while (node)
 
2271
    {
 
2272
        wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
 
2273
        // wxASSERT (child != NULL);
 
2274
 
 
2275
        if (child)
 
2276
        {
 
2277
            wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
 
2278
            while (node2)
 
2279
            {
 
2280
                wxRichTextLine* line = node2->GetData();
 
2281
 
 
2282
                wxRect rect(line->GetRect());
 
2283
 
 
2284
                if (y <= rect.GetBottom())
 
2285
                    return line;
 
2286
 
 
2287
                node2 = node2->GetNext();
 
2288
            }
 
2289
        }
 
2290
 
 
2291
        node = node->GetNext();
 
2292
    }
 
2293
 
 
2294
    // Return last line
 
2295
    int lineCount = GetLineCount();
 
2296
    if (lineCount > 0)
 
2297
        return GetLineForVisibleLineNumber(lineCount-1);
 
2298
    else
 
2299
        return NULL;
 
2300
}
 
2301
 
 
2302
/// Get the number of visible lines
 
2303
int wxRichTextParagraphLayoutBox::GetLineCount() const
 
2304
{
 
2305
    int count = 0;
 
2306
 
 
2307
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
2308
    while (node)
 
2309
    {
 
2310
        wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
 
2311
        // wxASSERT (child != NULL);
 
2312
 
 
2313
        if (child)
 
2314
            count += child->GetLines().GetCount();
 
2315
 
 
2316
        node = node->GetNext();
 
2317
    }
 
2318
    return count;
 
2319
}
 
2320
 
 
2321
 
 
2322
/// Get the paragraph for a given line
 
2323
wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine* line) const
 
2324
{
 
2325
    return GetParagraphAtPosition(line->GetAbsoluteRange().GetStart());
 
2326
}
 
2327
 
 
2328
/// Get the line size at the given position
 
2329
wxSize wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos, bool caretPosition) const
 
2330
{
 
2331
    wxRichTextLine* line = GetLineAtPosition(pos, caretPosition);
 
2332
    if (line)
 
2333
    {
 
2334
        return line->GetSize();
 
2335
    }
 
2336
    else
 
2337
        return wxSize(0, 0);
 
2338
}
 
2339
 
 
2340
 
 
2341
/// Convenience function to add a paragraph of text
 
2342
wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph(const wxString& text, wxRichTextAttr* paraStyle)
 
2343
{
 
2344
    // Don't use the base style, just the default style, and the base style will
 
2345
    // be combined at display time.
 
2346
    // Divide into paragraph and character styles.
 
2347
 
 
2348
    wxRichTextAttr defaultCharStyle;
 
2349
    wxRichTextAttr defaultParaStyle;
 
2350
 
 
2351
    // If the default style is a named paragraph style, don't apply any character formatting
 
2352
    // to the initial text string.
 
2353
    if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
 
2354
    {
 
2355
        wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
 
2356
        if (def)
 
2357
            defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
 
2358
    }
 
2359
    else
 
2360
        wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
 
2361
 
 
2362
    wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
 
2363
    wxRichTextAttr* cStyle = & defaultCharStyle;
 
2364
 
 
2365
    wxRichTextParagraph* para = new wxRichTextParagraph(text, this, pStyle, cStyle);
 
2366
    para->GetAttributes().GetTextBoxAttr().Reset();
 
2367
 
 
2368
    AppendChild(para);
 
2369
 
 
2370
    UpdateRanges();
 
2371
 
 
2372
    return para->GetRange();
 
2373
}
 
2374
 
 
2375
/// Adds multiple paragraphs, based on newlines.
 
2376
wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text, wxRichTextAttr* paraStyle)
 
2377
{
 
2378
    // Don't use the base style, just the default style, and the base style will
 
2379
    // be combined at display time.
 
2380
    // Divide into paragraph and character styles.
 
2381
 
 
2382
    wxRichTextAttr defaultCharStyle;
 
2383
    wxRichTextAttr defaultParaStyle;
 
2384
 
 
2385
    // If the default style is a named paragraph style, don't apply any character formatting
 
2386
    // to the initial text string.
 
2387
    if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
 
2388
    {
 
2389
        wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
 
2390
        if (def)
 
2391
            defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
 
2392
    }
 
2393
    else
 
2394
        wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
 
2395
 
 
2396
    wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
 
2397
    wxRichTextAttr* cStyle = & defaultCharStyle;
 
2398
 
 
2399
    wxRichTextParagraph* firstPara = NULL;
 
2400
    wxRichTextParagraph* lastPara = NULL;
 
2401
 
 
2402
    wxRichTextRange range(-1, -1);
 
2403
 
 
2404
    size_t i = 0;
 
2405
    size_t len = text.length();
 
2406
    wxString line;
 
2407
    wxRichTextParagraph* para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
 
2408
    para->GetAttributes().GetTextBoxAttr().Reset();
 
2409
 
 
2410
    AppendChild(para);
 
2411
 
 
2412
    firstPara = para;
 
2413
    lastPara = para;
 
2414
 
 
2415
    while (i < len)
 
2416
    {
 
2417
        wxChar ch = text[i];
 
2418
        if (ch == wxT('\n') || ch == wxT('\r'))
 
2419
        {
 
2420
            if (i != (len-1))
 
2421
            {
 
2422
                wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
 
2423
                plainText->SetText(line);
 
2424
 
 
2425
                para = new wxRichTextParagraph(wxEmptyString, this, pStyle, cStyle);
 
2426
                para->GetAttributes().GetTextBoxAttr().Reset();
 
2427
 
 
2428
                AppendChild(para);
 
2429
 
 
2430
                lastPara = para;
 
2431
                line = wxEmptyString;
 
2432
            }
 
2433
        }
 
2434
        else
 
2435
            line += ch;
 
2436
 
 
2437
        i ++;
 
2438
    }
 
2439
 
 
2440
    if (!line.empty())
 
2441
    {
 
2442
        wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData();
 
2443
        plainText->SetText(line);
 
2444
    }
 
2445
 
 
2446
    UpdateRanges();
 
2447
 
 
2448
    return wxRichTextRange(firstPara->GetRange().GetStart(), lastPara->GetRange().GetEnd());
 
2449
}
 
2450
 
 
2451
/// Convenience function to add an image
 
2452
wxRichTextRange wxRichTextParagraphLayoutBox::AddImage(const wxImage& image, wxRichTextAttr* paraStyle)
 
2453
{
 
2454
    // Don't use the base style, just the default style, and the base style will
 
2455
    // be combined at display time.
 
2456
    // Divide into paragraph and character styles.
 
2457
 
 
2458
    wxRichTextAttr defaultCharStyle;
 
2459
    wxRichTextAttr defaultParaStyle;
 
2460
 
 
2461
    // If the default style is a named paragraph style, don't apply any character formatting
 
2462
    // to the initial text string.
 
2463
    if (GetDefaultStyle().HasParagraphStyleName() && GetStyleSheet())
 
2464
    {
 
2465
        wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(GetDefaultStyle().GetParagraphStyleName());
 
2466
        if (def)
 
2467
            defaultParaStyle = def->GetStyleMergedWithBase(GetStyleSheet());
 
2468
    }
 
2469
    else
 
2470
        wxRichTextSplitParaCharStyles(GetDefaultStyle(), defaultParaStyle, defaultCharStyle);
 
2471
 
 
2472
    wxRichTextAttr* pStyle = paraStyle ? paraStyle : (wxRichTextAttr*) & defaultParaStyle;
 
2473
    wxRichTextAttr* cStyle = & defaultCharStyle;
 
2474
 
 
2475
    wxRichTextParagraph* para = new wxRichTextParagraph(this, pStyle);
 
2476
    para->GetAttributes().GetTextBoxAttr().Reset();
 
2477
    AppendChild(para);
 
2478
    para->AppendChild(new wxRichTextImage(image, this, cStyle));
 
2479
 
 
2480
    UpdateRanges();
 
2481
 
 
2482
    return para->GetRange();
 
2483
}
 
2484
 
 
2485
 
 
2486
/// Insert fragment into this box at the given position. If partialParagraph is true,
 
2487
/// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
 
2488
/// marker.
 
2489
 
 
2490
bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextParagraphLayoutBox& fragment)
 
2491
{
 
2492
    // First, find the first paragraph whose starting position is within the range.
 
2493
    wxRichTextParagraph* para = GetParagraphAtPosition(position);
 
2494
    if (para)
 
2495
    {
 
2496
        wxRichTextAttr originalAttr = para->GetAttributes();
 
2497
 
 
2498
        wxRichTextObjectList::compatibility_iterator node = m_children.Find(para);
 
2499
 
 
2500
        // Now split at this position, returning the object to insert the new
 
2501
        // ones in front of.
 
2502
        wxRichTextObject* nextObject = para->SplitAt(position);
 
2503
 
 
2504
        // Special case: partial paragraph, just one paragraph. Might be a small amount of
 
2505
        // text, for example, so let's optimize.
 
2506
 
 
2507
        if (fragment.GetPartialParagraph() && fragment.GetChildren().GetCount() == 1)
 
2508
        {
 
2509
            // Add the first para to this para...
 
2510
            wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
 
2511
            if (!firstParaNode)
 
2512
                return false;
 
2513
 
 
2514
            // Iterate through the fragment paragraph inserting the content into this paragraph.
 
2515
            wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
 
2516
            wxASSERT (firstPara != NULL);
 
2517
 
 
2518
            wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
 
2519
            while (objectNode)
 
2520
            {
 
2521
                wxRichTextObject* newObj = objectNode->GetData()->Clone();
 
2522
 
 
2523
                if (!nextObject)
 
2524
                {
 
2525
                    // Append
 
2526
                    para->AppendChild(newObj);
 
2527
                }
 
2528
                else
 
2529
                {
 
2530
                    // Insert before nextObject
 
2531
                    para->InsertChild(newObj, nextObject);
 
2532
                }
 
2533
 
 
2534
                objectNode = objectNode->GetNext();
 
2535
            }
 
2536
 
 
2537
            return true;
 
2538
        }
 
2539
        else
 
2540
        {
 
2541
            // Procedure for inserting a fragment consisting of a number of
 
2542
            // paragraphs:
 
2543
            //
 
2544
            // 1. Remove and save the content that's after the insertion point, for adding
 
2545
            //    back once we've added the fragment.
 
2546
            // 2. Add the content from the first fragment paragraph to the current
 
2547
            //    paragraph.
 
2548
            // 3. Add remaining fragment paragraphs after the current paragraph.
 
2549
            // 4. Add back the saved content from the first paragraph. If partialParagraph
 
2550
            //    is true, add it to the last paragraph added and not a new one.
 
2551
 
 
2552
            // 1. Remove and save objects after split point.
 
2553
            wxList savedObjects;
 
2554
            if (nextObject)
 
2555
                para->MoveToList(nextObject, savedObjects);
 
2556
 
 
2557
            // 2. Add the content from the 1st fragment paragraph.
 
2558
            wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
 
2559
            if (!firstParaNode)
 
2560
                return false;
 
2561
 
 
2562
            wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
 
2563
            wxASSERT(firstPara != NULL);
 
2564
 
 
2565
            if (!(fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE))
 
2566
                para->SetAttributes(firstPara->GetAttributes());
 
2567
 
 
2568
            // Save empty paragraph attributes for appending later
 
2569
            // These are character attributes deliberately set for a new paragraph. Without this,
 
2570
            // we couldn't pass default attributes when appending a new paragraph.
 
2571
            wxRichTextAttr emptyParagraphAttributes;
 
2572
 
 
2573
            wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
 
2574
 
 
2575
            if (objectNode && firstPara->GetChildren().GetCount() == 1 && objectNode->GetData()->IsEmpty())
 
2576
                emptyParagraphAttributes = objectNode->GetData()->GetAttributes();
 
2577
 
 
2578
            while (objectNode)
 
2579
            {
 
2580
                wxRichTextObject* newObj = objectNode->GetData()->Clone();
 
2581
 
 
2582
                // Append
 
2583
                para->AppendChild(newObj);
 
2584
 
 
2585
                objectNode = objectNode->GetNext();
 
2586
            }
 
2587
 
 
2588
            // 3. Add remaining fragment paragraphs after the current paragraph.
 
2589
            wxRichTextObjectList::compatibility_iterator nextParagraphNode = node->GetNext();
 
2590
            wxRichTextObject* nextParagraph = NULL;
 
2591
            if (nextParagraphNode)
 
2592
                nextParagraph = nextParagraphNode->GetData();
 
2593
 
 
2594
            wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst()->GetNext();
 
2595
            wxRichTextParagraph* finalPara = para;
 
2596
 
 
2597
            bool needExtraPara = (!i || !fragment.GetPartialParagraph());
 
2598
 
 
2599
            // If there was only one paragraph, we need to insert a new one.
 
2600
            while (i)
 
2601
            {
 
2602
                wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
 
2603
                wxASSERT( para != NULL );
 
2604
 
 
2605
                finalPara = (wxRichTextParagraph*) para->Clone();
 
2606
 
 
2607
                if (nextParagraph)
 
2608
                    InsertChild(finalPara, nextParagraph);
 
2609
                else
 
2610
                    AppendChild(finalPara);
 
2611
 
 
2612
                i = i->GetNext();
 
2613
            }
 
2614
 
 
2615
            // If there was only one paragraph, or we have full paragraphs in our fragment,
 
2616
            // we need to insert a new one.
 
2617
            if (needExtraPara)
 
2618
            {
 
2619
                finalPara = new wxRichTextParagraph;
 
2620
 
 
2621
                if (nextParagraph)
 
2622
                    InsertChild(finalPara, nextParagraph);
 
2623
                else
 
2624
                    AppendChild(finalPara);
 
2625
            }
 
2626
 
 
2627
            // 4. Add back the remaining content.
 
2628
            if (finalPara)
 
2629
            {
 
2630
                if (nextObject)
 
2631
                    finalPara->MoveFromList(savedObjects);
 
2632
 
 
2633
                // Ensure there's at least one object
 
2634
                if (finalPara->GetChildCount() == 0)
 
2635
                {
 
2636
                    wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
 
2637
                    text->SetAttributes(emptyParagraphAttributes);
 
2638
 
 
2639
                    finalPara->AppendChild(text);
 
2640
                }
 
2641
            }
 
2642
 
 
2643
            if ((fragment.GetAttributes().GetFlags() & wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE) && firstPara)
 
2644
                finalPara->SetAttributes(firstPara->GetAttributes());
 
2645
            else if (finalPara && finalPara != para)
 
2646
                finalPara->SetAttributes(originalAttr);
 
2647
 
 
2648
            return true;
 
2649
        }
 
2650
    }
 
2651
    else
 
2652
    {
 
2653
        // Append
 
2654
        wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst();
 
2655
        while (i)
 
2656
        {
 
2657
            wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
 
2658
            wxASSERT( para != NULL );
 
2659
 
 
2660
            AppendChild(para->Clone());
 
2661
 
 
2662
            i = i->GetNext();
 
2663
        }
 
2664
 
 
2665
        return true;
 
2666
    }
 
2667
}
 
2668
 
 
2669
/// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
 
2670
/// If there was an incomplete paragraph at the end, partialParagraph is set to true.
 
2671
bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange& range, wxRichTextParagraphLayoutBox& fragment)
 
2672
{
 
2673
    wxRichTextObjectList::compatibility_iterator i = GetChildren().GetFirst();
 
2674
    while (i)
 
2675
    {
 
2676
        wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
 
2677
        wxASSERT( para != NULL );
 
2678
 
 
2679
        if (!para->GetRange().IsOutside(range))
 
2680
        {
 
2681
            fragment.AppendChild(para->Clone());
 
2682
        }
 
2683
        i = i->GetNext();
 
2684
    }
 
2685
 
 
2686
    // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
 
2687
    if (!fragment.IsEmpty())
 
2688
    {
 
2689
        wxRichTextParagraph* firstPara = wxDynamicCast(fragment.GetChildren().GetFirst()->GetData(), wxRichTextParagraph);
 
2690
        wxASSERT( firstPara != NULL );
 
2691
 
 
2692
        wxRichTextParagraph* lastPara = wxDynamicCast(fragment.GetChildren().GetLast()->GetData(), wxRichTextParagraph);
 
2693
        wxASSERT( lastPara != NULL );
 
2694
 
 
2695
        if (!firstPara || !lastPara)
 
2696
            return false;
 
2697
 
 
2698
        bool isFragment = (range.GetEnd() < lastPara->GetRange().GetEnd());
 
2699
 
 
2700
        long firstPos = firstPara->GetRange().GetStart();
 
2701
 
 
2702
        // Adjust for renumbering from zero
 
2703
        wxRichTextRange topTailRange(range.GetStart() - firstPos, range.GetEnd() - firstPos);
 
2704
 
 
2705
        long end;
 
2706
        fragment.CalculateRange(0, end);
 
2707
 
 
2708
        // Chop off the start of the paragraph
 
2709
        if (topTailRange.GetStart() > 0)
 
2710
        {
 
2711
            wxRichTextRange r(0, topTailRange.GetStart()-1);
 
2712
            firstPara->DeleteRange(r);
 
2713
 
 
2714
            // Make sure the numbering is correct
 
2715
            fragment.CalculateRange(0, end);
 
2716
 
 
2717
            // Now, we've deleted some positions, so adjust the range
 
2718
            // accordingly.
 
2719
            topTailRange.SetStart(range.GetLength());
 
2720
            topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
 
2721
        }
 
2722
        else
 
2723
        {
 
2724
            topTailRange.SetStart(range.GetLength());
 
2725
            topTailRange.SetEnd(fragment.GetOwnRange().GetEnd());
 
2726
        }
 
2727
 
 
2728
        if (topTailRange.GetStart() < lastPara->GetRange().GetEnd())
 
2729
        {
 
2730
            lastPara->DeleteRange(topTailRange);
 
2731
 
 
2732
            // Make sure the numbering is correct
 
2733
            long end;
 
2734
            fragment.CalculateRange(0, end);
 
2735
 
 
2736
            // We only have part of a paragraph at the end
 
2737
            fragment.SetPartialParagraph(true);
 
2738
        }
 
2739
        else
 
2740
        {
 
2741
            // We have a partial paragraph (don't save last new paragraph marker)
 
2742
            // or complete paragraph
 
2743
            fragment.SetPartialParagraph(isFragment);
 
2744
        }
 
2745
    }
 
2746
 
 
2747
    return true;
 
2748
}
 
2749
 
 
2750
/// Given a position, get the number of the visible line (potentially many to a paragraph),
 
2751
/// starting from zero at the start of the buffer.
 
2752
long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos, bool caretPosition, bool startOfLine) const
 
2753
{
 
2754
    if (caretPosition)
 
2755
        pos ++;
 
2756
 
 
2757
    int lineCount = 0;
 
2758
 
 
2759
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
2760
    while (node)
 
2761
    {
 
2762
        wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
 
2763
        // wxASSERT( child != NULL );
 
2764
 
 
2765
        if (child)
 
2766
        {
 
2767
            if (child->GetRange().Contains(pos))
 
2768
            {
 
2769
                wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
 
2770
                while (node2)
 
2771
                {
 
2772
                    wxRichTextLine* line = node2->GetData();
 
2773
                    wxRichTextRange lineRange = line->GetAbsoluteRange();
 
2774
 
 
2775
                    if (lineRange.Contains(pos) || pos == lineRange.GetStart())
 
2776
                    {
 
2777
                        // If the caret is displayed at the end of the previous wrapped line,
 
2778
                        // we want to return the line it's _displayed_ at (not the actual line
 
2779
                        // containing the position).
 
2780
                        if (lineRange.GetStart() == pos && !startOfLine && child->GetRange().GetStart() != pos)
 
2781
                            return lineCount - 1;
 
2782
                        else
 
2783
                            return lineCount;
 
2784
                    }
 
2785
 
 
2786
                    lineCount ++;
 
2787
 
 
2788
                    node2 = node2->GetNext();
 
2789
                }
 
2790
                // If we didn't find it in the lines, it must be
 
2791
                // the last position of the paragraph. So return the last line.
 
2792
                return lineCount-1;
 
2793
            }
 
2794
            else
 
2795
                lineCount += child->GetLines().GetCount();
 
2796
        }
 
2797
 
 
2798
        node = node->GetNext();
 
2799
    }
 
2800
 
 
2801
    // Not found
 
2802
    return -1;
 
2803
}
 
2804
 
 
2805
/// Given a line number, get the corresponding wxRichTextLine object.
 
2806
wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber) const
 
2807
{
 
2808
    int lineCount = 0;
 
2809
 
 
2810
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
2811
    while (node)
 
2812
    {
 
2813
        wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
 
2814
        // wxASSERT(child != NULL);
 
2815
 
 
2816
        if (child)
 
2817
        {
 
2818
            if (lineNumber < (int) (child->GetLines().GetCount() + lineCount))
 
2819
            {
 
2820
                wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
 
2821
                while (node2)
 
2822
                {
 
2823
                    wxRichTextLine* line = node2->GetData();
 
2824
 
 
2825
                    if (lineCount == lineNumber)
 
2826
                        return line;
 
2827
 
 
2828
                    lineCount ++;
 
2829
 
 
2830
                    node2 = node2->GetNext();
 
2831
                }
 
2832
            }
 
2833
            else
 
2834
                lineCount += child->GetLines().GetCount();
 
2835
        }
 
2836
 
 
2837
        node = node->GetNext();
 
2838
    }
 
2839
 
 
2840
    // Didn't find it
 
2841
    return NULL;
 
2842
}
 
2843
 
 
2844
/// Delete range from layout.
 
2845
bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange& range)
 
2846
{
 
2847
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
2848
 
 
2849
    wxRichTextParagraph* firstPara = NULL;
 
2850
    while (node)
 
2851
    {
 
2852
        wxRichTextParagraph* obj = wxDynamicCast(node->GetData(), wxRichTextParagraph);
 
2853
        // wxASSERT (obj != NULL);
 
2854
 
 
2855
        wxRichTextObjectList::compatibility_iterator next = node->GetNext();
 
2856
 
 
2857
        if (obj)
 
2858
        {
 
2859
            // Delete the range in each paragraph
 
2860
 
 
2861
            if (!obj->GetRange().IsOutside(range))
 
2862
            {
 
2863
                // Deletes the content of this object within the given range
 
2864
                obj->DeleteRange(range);
 
2865
 
 
2866
                wxRichTextRange thisRange = obj->GetRange();
 
2867
                wxRichTextAttr thisAttr = obj->GetAttributes();
 
2868
 
 
2869
                // If the whole paragraph is within the range to delete,
 
2870
                // delete the whole thing.
 
2871
                if (range.GetStart() <= thisRange.GetStart() && range.GetEnd() >= thisRange.GetEnd())
 
2872
                {
 
2873
                    // Delete the whole object
 
2874
                    RemoveChild(obj, true);
 
2875
                    obj = NULL;
 
2876
                }
 
2877
                else if (!firstPara)
 
2878
                    firstPara = obj;
 
2879
 
 
2880
                // If the range includes the paragraph end, we need to join this
 
2881
                // and the next paragraph.
 
2882
                if (range.GetEnd() <= thisRange.GetEnd())
 
2883
                {
 
2884
                    // We need to move the objects from the next paragraph
 
2885
                    // to this paragraph
 
2886
 
 
2887
                    wxRichTextParagraph* nextParagraph = NULL;
 
2888
                    if ((range.GetEnd() < thisRange.GetEnd()) && obj)
 
2889
                        nextParagraph = obj;
 
2890
                    else
 
2891
                    {
 
2892
                        // We're ending at the end of the paragraph, so merge the _next_ paragraph.
 
2893
                        if (next)
 
2894
                            nextParagraph = wxDynamicCast(next->GetData(), wxRichTextParagraph);
 
2895
                    }
 
2896
 
 
2897
                    bool applyFinalParagraphStyle = firstPara && nextParagraph && nextParagraph != firstPara;
 
2898
 
 
2899
                    wxRichTextAttr nextParaAttr;
 
2900
                    if (applyFinalParagraphStyle)
 
2901
                    {
 
2902
                        // Special case when deleting the end of a paragraph - use _this_ paragraph's style,
 
2903
                        // not the next one.
 
2904
                        if (range.GetStart() == range.GetEnd() && range.GetStart() == thisRange.GetEnd())
 
2905
                            nextParaAttr = thisAttr;
 
2906
                        else
 
2907
                            nextParaAttr = nextParagraph->GetAttributes();
 
2908
                    }
 
2909
 
 
2910
                    if (firstPara && nextParagraph && firstPara != nextParagraph)
 
2911
                    {
 
2912
                        // Move the objects to the previous para
 
2913
                        wxRichTextObjectList::compatibility_iterator node1 = nextParagraph->GetChildren().GetFirst();
 
2914
 
 
2915
                        while (node1)
 
2916
                        {
 
2917
                            wxRichTextObject* obj1 = node1->GetData();
 
2918
 
 
2919
                            firstPara->AppendChild(obj1);
 
2920
 
 
2921
                            wxRichTextObjectList::compatibility_iterator next1 = node1->GetNext();
 
2922
                            nextParagraph->GetChildren().Erase(node1);
 
2923
 
 
2924
                            node1 = next1;
 
2925
                        }
 
2926
 
 
2927
                        // Delete the paragraph
 
2928
                        RemoveChild(nextParagraph, true);
 
2929
                    }
 
2930
 
 
2931
                    // Avoid empty paragraphs
 
2932
                    if (firstPara && firstPara->GetChildren().GetCount() == 0)
 
2933
                    {
 
2934
                        wxRichTextPlainText* text = new wxRichTextPlainText(wxEmptyString);
 
2935
                        firstPara->AppendChild(text);
 
2936
                    }
 
2937
 
 
2938
                    if (applyFinalParagraphStyle)
 
2939
                        firstPara->SetAttributes(nextParaAttr);
 
2940
 
 
2941
                    return true;
 
2942
                }
 
2943
            }
 
2944
        }
 
2945
 
 
2946
        node = next;
 
2947
    }
 
2948
 
 
2949
    return true;
 
2950
}
 
2951
 
 
2952
/// Get any text in this object for the given range
 
2953
wxString wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange& range) const
 
2954
{
 
2955
    int lineCount = 0;
 
2956
    wxString text;
 
2957
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
2958
    while (node)
 
2959
    {
 
2960
        wxRichTextObject* child = node->GetData();
 
2961
        if (!child->GetRange().IsOutside(range))
 
2962
        {
 
2963
            wxRichTextRange childRange = range;
 
2964
            childRange.LimitTo(child->GetRange());
 
2965
 
 
2966
            wxString childText = child->GetTextForRange(childRange);
 
2967
 
 
2968
            text += childText;
 
2969
 
 
2970
            if ((childRange.GetEnd() == child->GetRange().GetEnd()) && node->GetNext())
 
2971
                text += wxT("\n");
 
2972
 
 
2973
            lineCount ++;
 
2974
        }
 
2975
        node = node->GetNext();
 
2976
    }
 
2977
 
 
2978
    return text;
 
2979
}
 
2980
 
 
2981
/// Get all the text
 
2982
wxString wxRichTextParagraphLayoutBox::GetText() const
 
2983
{
 
2984
    return GetTextForRange(GetOwnRange());
 
2985
}
 
2986
 
 
2987
/// Get the paragraph by number
 
2988
wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber) const
 
2989
{
 
2990
    if ((size_t) paragraphNumber >= GetChildCount())
 
2991
        return NULL;
 
2992
 
 
2993
    return (wxRichTextParagraph*) GetChild((size_t) paragraphNumber);
 
2994
}
 
2995
 
 
2996
/// Get the length of the paragraph
 
2997
int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber) const
 
2998
{
 
2999
    wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
 
3000
    if (para)
 
3001
        return para->GetRange().GetLength() - 1; // don't include newline
 
3002
    else
 
3003
        return 0;
 
3004
}
 
3005
 
 
3006
/// Get the text of the paragraph
 
3007
wxString wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber) const
 
3008
{
 
3009
    wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
 
3010
    if (para)
 
3011
        return para->GetTextForRange(para->GetRange());
 
3012
    else
 
3013
        return wxEmptyString;
 
3014
}
 
3015
 
 
3016
/// Convert zero-based line column and paragraph number to a position.
 
3017
long wxRichTextParagraphLayoutBox::XYToPosition(long x, long y) const
 
3018
{
 
3019
    wxRichTextParagraph* para = GetParagraphAtLine(y);
 
3020
    if (para)
 
3021
    {
 
3022
        return para->GetRange().GetStart() + x;
 
3023
    }
 
3024
    else
 
3025
        return -1;
 
3026
}
 
3027
 
 
3028
/// Convert zero-based position to line column and paragraph number
 
3029
bool wxRichTextParagraphLayoutBox::PositionToXY(long pos, long* x, long* y) const
 
3030
{
 
3031
    wxRichTextParagraph* para = GetParagraphAtPosition(pos);
 
3032
    if (para)
 
3033
    {
 
3034
        int count = 0;
 
3035
        wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
3036
        while (node)
 
3037
        {
 
3038
            wxRichTextObject* child = node->GetData();
 
3039
            if (child == para)
 
3040
                break;
 
3041
            count ++;
 
3042
            node = node->GetNext();
 
3043
        }
 
3044
 
 
3045
        *y = count;
 
3046
        *x = pos - para->GetRange().GetStart();
 
3047
 
 
3048
        return true;
 
3049
    }
 
3050
    else
 
3051
        return false;
 
3052
}
 
3053
 
 
3054
/// Get the leaf object in a paragraph at this position.
 
3055
/// Given a line number, get the corresponding wxRichTextLine object.
 
3056
wxRichTextObject* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position) const
 
3057
{
 
3058
    wxRichTextParagraph* para = GetParagraphAtPosition(position);
 
3059
    if (para)
 
3060
    {
 
3061
        wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
 
3062
 
 
3063
        while (node)
 
3064
        {
 
3065
            wxRichTextObject* child = node->GetData();
 
3066
            if (child->GetRange().Contains(position))
 
3067
                return child;
 
3068
 
 
3069
            node = node->GetNext();
 
3070
        }
 
3071
        if (position == para->GetRange().GetEnd() && para->GetChildCount() > 0)
 
3072
            return para->GetChildren().GetLast()->GetData();
 
3073
    }
 
3074
    return NULL;
 
3075
}
 
3076
 
 
3077
/// Set character or paragraph text attributes: apply character styles only to immediate text nodes
 
3078
bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, int flags)
 
3079
{
 
3080
    bool characterStyle = false;
 
3081
    bool paragraphStyle = false;
 
3082
 
 
3083
    if (style.IsCharacterStyle())
 
3084
        characterStyle = true;
 
3085
    if (style.IsParagraphStyle())
 
3086
        paragraphStyle = true;
 
3087
 
 
3088
    wxRichTextBuffer* buffer = GetBuffer();
 
3089
 
 
3090
    bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
 
3091
    bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
 
3092
    bool parasOnly = ((flags & wxRICHTEXT_SETSTYLE_PARAGRAPHS_ONLY) != 0);
 
3093
    bool charactersOnly = ((flags & wxRICHTEXT_SETSTYLE_CHARACTERS_ONLY) != 0);
 
3094
    bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
 
3095
    bool removeStyle = ((flags & wxRICHTEXT_SETSTYLE_REMOVE) != 0);
 
3096
 
 
3097
    // Apply paragraph style first, if any
 
3098
    wxRichTextAttr wholeStyle(style);
 
3099
 
 
3100
    if (!removeStyle && wholeStyle.HasParagraphStyleName() && buffer->GetStyleSheet())
 
3101
    {
 
3102
        wxRichTextParagraphStyleDefinition* def = buffer->GetStyleSheet()->FindParagraphStyle(wholeStyle.GetParagraphStyleName());
 
3103
        if (def)
 
3104
            wxRichTextApplyStyle(wholeStyle, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
 
3105
    }
 
3106
 
 
3107
    // Limit the attributes to be set to the content to only character attributes.
 
3108
    wxRichTextAttr characterAttributes(wholeStyle);
 
3109
    characterAttributes.SetFlags(characterAttributes.GetFlags() & (wxTEXT_ATTR_CHARACTER));
 
3110
 
 
3111
    if (!removeStyle && characterAttributes.HasCharacterStyleName() && buffer->GetStyleSheet())
 
3112
    {
 
3113
        wxRichTextCharacterStyleDefinition* def = buffer->GetStyleSheet()->FindCharacterStyle(characterAttributes.GetCharacterStyleName());
 
3114
        if (def)
 
3115
            wxRichTextApplyStyle(characterAttributes, def->GetStyleMergedWithBase(buffer->GetStyleSheet()));
 
3116
    }
 
3117
 
 
3118
    // If we are associated with a control, make undoable; otherwise, apply immediately
 
3119
    // to the data.
 
3120
 
 
3121
    bool haveControl = (buffer->GetRichTextCtrl() != NULL);
 
3122
 
 
3123
    wxRichTextAction* action = NULL;
 
3124
 
 
3125
    if (haveControl && withUndo)
 
3126
    {
 
3127
        action = new wxRichTextAction(NULL, _("Change Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
 
3128
        action->SetRange(range);
 
3129
        action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
 
3130
    }
 
3131
 
 
3132
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
3133
    while (node)
 
3134
    {
 
3135
        wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
 
3136
        // wxASSERT (para != NULL);
 
3137
 
 
3138
        if (para && para->GetChildCount() > 0)
 
3139
        {
 
3140
            // Stop searching if we're beyond the range of interest
 
3141
            if (para->GetRange().GetStart() > range.GetEnd())
 
3142
                break;
 
3143
 
 
3144
            if (!para->GetRange().IsOutside(range))
 
3145
            {
 
3146
                // We'll be using a copy of the paragraph to make style changes,
 
3147
                // not updating the buffer directly.
 
3148
                wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
 
3149
 
 
3150
                if (haveControl && withUndo)
 
3151
                {
 
3152
                    newPara = new wxRichTextParagraph(*para);
 
3153
                    action->GetNewParagraphs().AppendChild(newPara);
 
3154
 
 
3155
                    // Also store the old ones for Undo
 
3156
                    action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
 
3157
                }
 
3158
                else
 
3159
                    newPara = para;
 
3160
 
 
3161
                // If we're specifying paragraphs only, then we really mean character formatting
 
3162
                // to be included in the paragraph style
 
3163
                if ((paragraphStyle || parasOnly) && !charactersOnly)
 
3164
                {
 
3165
                    if (removeStyle)
 
3166
                    {
 
3167
                        // Removes the given style from the paragraph
 
3168
                        wxRichTextRemoveStyle(newPara->GetAttributes(), style);
 
3169
                    }
 
3170
                    else if (resetExistingStyle)
 
3171
                        newPara->GetAttributes() = wholeStyle;
 
3172
                    else
 
3173
                    {
 
3174
                        if (applyMinimal)
 
3175
                        {
 
3176
                            // Only apply attributes that will make a difference to the combined
 
3177
                            // style as seen on the display
 
3178
                            wxRichTextAttr combinedAttr(para->GetCombinedAttributes(true));
 
3179
                            wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle, & combinedAttr);
 
3180
                        }
 
3181
                        else
 
3182
                            wxRichTextApplyStyle(newPara->GetAttributes(), wholeStyle);
 
3183
                    }
 
3184
                }
 
3185
 
 
3186
                // When applying paragraph styles dynamically, don't change the text objects' attributes
 
3187
                // since they will computed as needed. Only apply the character styling if it's _only_
 
3188
                // character styling. This policy is subject to change and might be put under user control.
 
3189
 
 
3190
                // Hm. we might well be applying a mix of paragraph and character styles, in which
 
3191
                // case we _do_ want to apply character styles regardless of what para styles are set.
 
3192
                // But if we're applying a paragraph style, which has some character attributes, but
 
3193
                // we only want the paragraphs to hold this character style, then we _don't_ want to
 
3194
                // apply the character style. So we need to be able to choose.
 
3195
 
 
3196
                if (!parasOnly && (characterStyle|charactersOnly) && range.GetStart() != newPara->GetRange().GetEnd())
 
3197
                {
 
3198
                    wxRichTextRange childRange(range);
 
3199
                    childRange.LimitTo(newPara->GetRange());
 
3200
 
 
3201
                    // Find the starting position and if necessary split it so
 
3202
                    // we can start applying a different style.
 
3203
                    // TODO: check that the style actually changes or is different
 
3204
                    // from style outside of range
 
3205
                    wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
 
3206
                    wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
 
3207
 
 
3208
                    if (childRange.GetStart() == newPara->GetRange().GetStart())
 
3209
                        firstObject = newPara->GetChildren().GetFirst()->GetData();
 
3210
                    else
 
3211
                        firstObject = newPara->SplitAt(range.GetStart());
 
3212
 
 
3213
                    // Increment by 1 because we're apply the style one _after_ the split point
 
3214
                    long splitPoint = childRange.GetEnd();
 
3215
                    if (splitPoint != newPara->GetRange().GetEnd())
 
3216
                        splitPoint ++;
 
3217
 
 
3218
                    // Find last object
 
3219
                    if (splitPoint == newPara->GetRange().GetEnd())
 
3220
                        lastObject = newPara->GetChildren().GetLast()->GetData();
 
3221
                    else
 
3222
                        // lastObject is set as a side-effect of splitting. It's
 
3223
                        // returned as the object before the new object.
 
3224
                        (void) newPara->SplitAt(splitPoint, & lastObject);
 
3225
 
 
3226
                    wxASSERT(firstObject != NULL);
 
3227
                    wxASSERT(lastObject != NULL);
 
3228
 
 
3229
                    if (!firstObject || !lastObject)
 
3230
                        continue;
 
3231
 
 
3232
                    wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
 
3233
                    wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
 
3234
 
 
3235
                    wxASSERT(firstNode);
 
3236
                    wxASSERT(lastNode);
 
3237
 
 
3238
                    wxRichTextObjectList::compatibility_iterator node2 = firstNode;
 
3239
 
 
3240
                    while (node2)
 
3241
                    {
 
3242
                        wxRichTextObject* child = node2->GetData();
 
3243
 
 
3244
                        if (removeStyle)
 
3245
                        {
 
3246
                            // Removes the given style from the paragraph
 
3247
                            wxRichTextRemoveStyle(child->GetAttributes(), style);
 
3248
                        }
 
3249
                        else if (resetExistingStyle)
 
3250
                            child->GetAttributes() = characterAttributes;
 
3251
                        else
 
3252
                        {
 
3253
                            if (applyMinimal)
 
3254
                            {
 
3255
                                // Only apply attributes that will make a difference to the combined
 
3256
                                // style as seen on the display
 
3257
                                wxRichTextAttr combinedAttr(newPara->GetCombinedAttributes(child->GetAttributes(), true));
 
3258
                                wxRichTextApplyStyle(child->GetAttributes(), characterAttributes, & combinedAttr);
 
3259
                            }
 
3260
                            else
 
3261
                                wxRichTextApplyStyle(child->GetAttributes(), characterAttributes);
 
3262
                        }
 
3263
 
 
3264
                        if (node2 == lastNode)
 
3265
                            break;
 
3266
 
 
3267
                        node2 = node2->GetNext();
 
3268
                    }
 
3269
                }
 
3270
            }
 
3271
        }
 
3272
 
 
3273
        node = node->GetNext();
 
3274
    }
 
3275
 
 
3276
    // Do action, or delay it until end of batch.
 
3277
    if (haveControl && withUndo)
 
3278
        buffer->SubmitAction(action);
 
3279
 
 
3280
    return true;
 
3281
}
 
3282
 
 
3283
// Just change the attributes for this single object.
 
3284
void wxRichTextParagraphLayoutBox::SetStyle(wxRichTextObject* obj, const wxRichTextAttr& textAttr, int flags)
 
3285
{
 
3286
    wxRichTextBuffer* buffer = GetBuffer();
 
3287
    bool withUndo = flags & wxRICHTEXT_SETSTYLE_WITH_UNDO;
 
3288
    bool resetExistingStyle = ((flags & wxRICHTEXT_SETSTYLE_RESET) != 0);
 
3289
    bool haveControl = (buffer->GetRichTextCtrl() != NULL);
 
3290
 
 
3291
    wxRichTextAction *action = NULL;
 
3292
    wxRichTextAttr newAttr = obj->GetAttributes();
 
3293
    if (resetExistingStyle)
 
3294
        newAttr = textAttr;
 
3295
    else
 
3296
        newAttr.Apply(textAttr);
 
3297
 
 
3298
    if (haveControl && withUndo)
 
3299
    {
 
3300
        action = new wxRichTextAction(NULL, _("Change Object Style"), wxRICHTEXT_CHANGE_ATTRIBUTES, buffer, obj->GetContainer(), buffer->GetRichTextCtrl());
 
3301
        action->SetRange(obj->GetRange().FromInternal());
 
3302
        action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
 
3303
        action->MakeObject(obj);
 
3304
 
 
3305
        action->GetAttributes() = newAttr;
 
3306
    }
 
3307
    else
 
3308
        obj->GetAttributes() = newAttr;
 
3309
 
 
3310
    if (haveControl && withUndo)
 
3311
        buffer->SubmitAction(action);
 
3312
}
 
3313
 
 
3314
/// Get the text attributes for this position.
 
3315
bool wxRichTextParagraphLayoutBox::GetStyle(long position, wxRichTextAttr& style)
 
3316
{
 
3317
    return DoGetStyle(position, style, true);
 
3318
}
 
3319
 
 
3320
bool wxRichTextParagraphLayoutBox::GetUncombinedStyle(long position, wxRichTextAttr& style)
 
3321
{
 
3322
    return DoGetStyle(position, style, false);
 
3323
}
 
3324
 
 
3325
/// Implementation helper for GetStyle. If combineStyles is true, combine base, paragraph and
 
3326
/// context attributes.
 
3327
bool wxRichTextParagraphLayoutBox::DoGetStyle(long position, wxRichTextAttr& style, bool combineStyles)
 
3328
{
 
3329
    wxRichTextObject* obj wxDUMMY_INITIALIZE(NULL);
 
3330
 
 
3331
    if (style.IsParagraphStyle())
 
3332
    {
 
3333
        obj = GetParagraphAtPosition(position);
 
3334
        if (obj)
 
3335
        {
 
3336
            if (combineStyles)
 
3337
            {
 
3338
                // Start with the base style
 
3339
                style = GetAttributes();
 
3340
                style.GetTextBoxAttr().Reset();
 
3341
 
 
3342
                // Apply the paragraph style
 
3343
                wxRichTextApplyStyle(style, obj->GetAttributes());
 
3344
            }
 
3345
            else
 
3346
                style = obj->GetAttributes();
 
3347
 
 
3348
            return true;
 
3349
        }
 
3350
    }
 
3351
    else
 
3352
    {
 
3353
        obj = GetLeafObjectAtPosition(position);
 
3354
        if (obj)
 
3355
        {
 
3356
            if (combineStyles)
 
3357
            {
 
3358
                wxRichTextParagraph* para = wxDynamicCast(obj->GetParent(), wxRichTextParagraph);
 
3359
                style = para ? para->GetCombinedAttributes(obj->GetAttributes()) : obj->GetAttributes();
 
3360
            }
 
3361
            else
 
3362
                style = obj->GetAttributes();
 
3363
 
 
3364
            return true;
 
3365
        }
 
3366
    }
 
3367
    return false;
 
3368
}
 
3369
 
 
3370
static bool wxHasStyle(long flags, long style)
 
3371
{
 
3372
    return (flags & style) != 0;
 
3373
}
 
3374
 
 
3375
/// Combines 'style' with 'currentStyle' for the purpose of summarising the attributes of a range of
 
3376
/// content.
 
3377
bool wxRichTextParagraphLayoutBox::CollectStyle(wxRichTextAttr& currentStyle, const wxRichTextAttr& style, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
 
3378
{
 
3379
    currentStyle.CollectCommonAttributes(style, clashingAttr, absentAttr);
 
3380
 
 
3381
    return true;
 
3382
}
 
3383
 
 
3384
/// Get the combined style for a range - if any attribute is different within the range,
 
3385
/// that attribute is not present within the flags.
 
3386
/// *** Note that this is not recursive, and so assumes that content inside a paragraph is not itself
 
3387
/// nested.
 
3388
bool wxRichTextParagraphLayoutBox::GetStyleForRange(const wxRichTextRange& range, wxRichTextAttr& style)
 
3389
{
 
3390
    style = wxRichTextAttr();
 
3391
 
 
3392
    wxRichTextAttr clashingAttr;
 
3393
    wxRichTextAttr absentAttrPara, absentAttrChar;
 
3394
 
 
3395
    wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
 
3396
    while (node)
 
3397
    {
 
3398
        wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
 
3399
        if (para && !(para->GetRange().GetStart() > range.GetEnd() || para->GetRange().GetEnd() < range.GetStart()))
 
3400
        {
 
3401
            if (para->GetChildren().GetCount() == 0)
 
3402
            {
 
3403
                wxRichTextAttr paraStyle = para->GetCombinedAttributes(true /* use box attributes */);
 
3404
 
 
3405
                CollectStyle(style, paraStyle, clashingAttr, absentAttrPara);
 
3406
            }
 
3407
            else
 
3408
            {
 
3409
                wxRichTextRange paraRange(para->GetRange());
 
3410
                paraRange.LimitTo(range);
 
3411
 
 
3412
                // First collect paragraph attributes only
 
3413
                wxRichTextAttr paraStyle = para->GetCombinedAttributes();
 
3414
                paraStyle.SetFlags(paraStyle.GetFlags() & wxTEXT_ATTR_PARAGRAPH);
 
3415
                CollectStyle(style, paraStyle, clashingAttr, absentAttrPara);
 
3416
 
 
3417
                wxRichTextObjectList::compatibility_iterator childNode = para->GetChildren().GetFirst();
 
3418
 
 
3419
                while (childNode)
 
3420
                {
 
3421
                    wxRichTextObject* child = childNode->GetData();
 
3422
                    if (!(child->GetRange().GetStart() > range.GetEnd() || child->GetRange().GetEnd() < range.GetStart()))
 
3423
                    {
 
3424
                        wxRichTextAttr childStyle = para->GetCombinedAttributes(child->GetAttributes(), true /* include box attributes */);
 
3425
 
 
3426
                        // Now collect character attributes only
 
3427
                        childStyle.SetFlags(childStyle.GetFlags() & wxTEXT_ATTR_CHARACTER);
 
3428
 
 
3429
                        CollectStyle(style, childStyle, clashingAttr, absentAttrChar);
 
3430
                    }
 
3431
 
 
3432
                    childNode = childNode->GetNext();
 
3433
                }
 
3434
            }
 
3435
        }
 
3436
        node = node->GetNext();
 
3437
    }
 
3438
    return true;
 
3439
}
 
3440
 
 
3441
/// Set default style
 
3442
bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxRichTextAttr& style)
 
3443
{
 
3444
    m_defaultAttributes = style;
 
3445
    return true;
 
3446
}
 
3447
 
 
3448
/// Test if this whole range has character attributes of the specified kind. If any
 
3449
/// of the attributes are different within the range, the test fails. You
 
3450
/// can use this to implement, for example, bold button updating. style must have
 
3451
/// flags indicating which attributes are of interest.
 
3452
bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
 
3453
{
 
3454
    int foundCount = 0;
 
3455
    int matchingCount = 0;
 
3456
 
 
3457
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
3458
    while (node)
 
3459
    {
 
3460
        wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
 
3461
        // wxASSERT (para != NULL);
 
3462
 
 
3463
        if (para)
 
3464
        {
 
3465
            // Stop searching if we're beyond the range of interest
 
3466
            if (para->GetRange().GetStart() > range.GetEnd())
 
3467
                return foundCount == matchingCount && foundCount != 0;
 
3468
 
 
3469
            if (!para->GetRange().IsOutside(range))
 
3470
            {
 
3471
                wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
 
3472
 
 
3473
                while (node2)
 
3474
                {
 
3475
                    wxRichTextObject* child = node2->GetData();
 
3476
                    // Allow for empty string if no buffer
 
3477
                    wxRichTextRange childRange = child->GetRange();
 
3478
                    if (childRange.GetLength() == 0 && GetRange().GetLength() == 1)
 
3479
                        childRange.SetEnd(childRange.GetEnd()+1);
 
3480
 
 
3481
                    if (!childRange.IsOutside(range) && wxDynamicCast(child, wxRichTextPlainText))
 
3482
                    {
 
3483
                        foundCount ++;
 
3484
                        wxRichTextAttr textAttr = para->GetCombinedAttributes(child->GetAttributes());
 
3485
 
 
3486
                        if (textAttr.EqPartial(style, false /* strong test - attributes must be valid in both objects */))
 
3487
                            matchingCount ++;
 
3488
                    }
 
3489
 
 
3490
                    node2 = node2->GetNext();
 
3491
                }
 
3492
            }
 
3493
        }
 
3494
 
 
3495
        node = node->GetNext();
 
3496
    }
 
3497
 
 
3498
    return foundCount == matchingCount && foundCount != 0;
 
3499
}
 
3500
 
 
3501
/// Test if this whole range has paragraph attributes of the specified kind. If any
 
3502
/// of the attributes are different within the range, the test fails. You
 
3503
/// can use this to implement, for example, centering button updating. style must have
 
3504
/// flags indicating which attributes are of interest.
 
3505
bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
 
3506
{
 
3507
    int foundCount = 0;
 
3508
    int matchingCount = 0;
 
3509
 
 
3510
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
3511
    while (node)
 
3512
    {
 
3513
        wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
 
3514
        // wxASSERT (para != NULL);
 
3515
 
 
3516
        if (para)
 
3517
        {
 
3518
            // Stop searching if we're beyond the range of interest
 
3519
            if (para->GetRange().GetStart() > range.GetEnd())
 
3520
                return foundCount == matchingCount && foundCount != 0;
 
3521
 
 
3522
            if (!para->GetRange().IsOutside(range))
 
3523
            {
 
3524
                wxRichTextAttr textAttr = GetAttributes();
 
3525
                // Apply the paragraph style
 
3526
                wxRichTextApplyStyle(textAttr, para->GetAttributes());
 
3527
 
 
3528
                foundCount ++;
 
3529
                if (textAttr.EqPartial(style, false /* strong test */))
 
3530
                    matchingCount ++;
 
3531
            }
 
3532
        }
 
3533
 
 
3534
        node = node->GetNext();
 
3535
    }
 
3536
    return foundCount == matchingCount && foundCount != 0;
 
3537
}
 
3538
 
 
3539
void wxRichTextParagraphLayoutBox::PrepareContent(wxRichTextParagraphLayoutBox& container)
 
3540
{
 
3541
    wxRichTextBuffer* buffer = GetBuffer();
 
3542
    if (buffer && buffer->GetRichTextCtrl())
 
3543
        buffer->GetRichTextCtrl()->PrepareContent(container);
 
3544
}
 
3545
 
 
3546
/// Set character or paragraph properties
 
3547
bool wxRichTextParagraphLayoutBox::SetProperties(const wxRichTextRange& range, const wxRichTextProperties& properties, int flags)
 
3548
{
 
3549
    wxRichTextBuffer* buffer = GetBuffer();
 
3550
 
 
3551
    bool withUndo = ((flags & wxRICHTEXT_SETPROPERTIES_WITH_UNDO) != 0);
 
3552
    bool parasOnly = ((flags & wxRICHTEXT_SETPROPERTIES_PARAGRAPHS_ONLY) != 0);
 
3553
    bool charactersOnly = ((flags & wxRICHTEXT_SETPROPERTIES_CHARACTERS_ONLY) != 0);
 
3554
    bool resetExistingProperties = ((flags & wxRICHTEXT_SETPROPERTIES_RESET) != 0);
 
3555
    bool removeProperties = ((flags & wxRICHTEXT_SETPROPERTIES_REMOVE) != 0);
 
3556
 
 
3557
    // If we are associated with a control, make undoable; otherwise, apply immediately
 
3558
    // to the data.
 
3559
 
 
3560
    bool haveControl = (buffer->GetRichTextCtrl() != NULL);
 
3561
 
 
3562
    wxRichTextAction* action = NULL;
 
3563
 
 
3564
    if (haveControl && withUndo)
 
3565
    {
 
3566
        action = new wxRichTextAction(NULL, _("Change Properties"), wxRICHTEXT_CHANGE_PROPERTIES, buffer, this, buffer->GetRichTextCtrl());
 
3567
        action->SetRange(range);
 
3568
        action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
 
3569
    }
 
3570
 
 
3571
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
3572
    while (node)
 
3573
    {
 
3574
        wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
 
3575
        // wxASSERT (para != NULL);
 
3576
 
 
3577
        if (para && para->GetChildCount() > 0)
 
3578
        {
 
3579
            // Stop searching if we're beyond the range of interest
 
3580
            if (para->GetRange().GetStart() > range.GetEnd())
 
3581
                break;
 
3582
 
 
3583
            if (!para->GetRange().IsOutside(range))
 
3584
            {
 
3585
                // We'll be using a copy of the paragraph to make style changes,
 
3586
                // not updating the buffer directly.
 
3587
                wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
 
3588
 
 
3589
                if (haveControl && withUndo)
 
3590
                {
 
3591
                    newPara = new wxRichTextParagraph(*para);
 
3592
                    action->GetNewParagraphs().AppendChild(newPara);
 
3593
 
 
3594
                    // Also store the old ones for Undo
 
3595
                    action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
 
3596
                }
 
3597
                else
 
3598
                    newPara = para;
 
3599
 
 
3600
                if (parasOnly)
 
3601
                {
 
3602
                    if (removeProperties)
 
3603
                    {
 
3604
                        // Removes the given style from the paragraph
 
3605
                        // TODO
 
3606
                        newPara->GetProperties().RemoveProperties(properties);
 
3607
                    }
 
3608
                    else if (resetExistingProperties)
 
3609
                        newPara->GetProperties() = properties;
 
3610
                    else
 
3611
                        newPara->GetProperties().MergeProperties(properties);
 
3612
                }
 
3613
 
 
3614
                // When applying paragraph styles dynamically, don't change the text objects' attributes
 
3615
                // since they will computed as needed. Only apply the character styling if it's _only_
 
3616
                // character styling. This policy is subject to change and might be put under user control.
 
3617
 
 
3618
                // Hm. we might well be applying a mix of paragraph and character styles, in which
 
3619
                // case we _do_ want to apply character styles regardless of what para styles are set.
 
3620
                // But if we're applying a paragraph style, which has some character attributes, but
 
3621
                // we only want the paragraphs to hold this character style, then we _don't_ want to
 
3622
                // apply the character style. So we need to be able to choose.
 
3623
 
 
3624
                if (!parasOnly && charactersOnly && range.GetStart() != newPara->GetRange().GetEnd())
 
3625
                {
 
3626
                    wxRichTextRange childRange(range);
 
3627
                    childRange.LimitTo(newPara->GetRange());
 
3628
 
 
3629
                    // Find the starting position and if necessary split it so
 
3630
                    // we can start applying different properties.
 
3631
                    // TODO: check that the properties actually change or are different
 
3632
                    // from properties outside of range
 
3633
                    wxRichTextObject* firstObject wxDUMMY_INITIALIZE(NULL);
 
3634
                    wxRichTextObject* lastObject wxDUMMY_INITIALIZE(NULL);
 
3635
 
 
3636
                    if (childRange.GetStart() == newPara->GetRange().GetStart())
 
3637
                        firstObject = newPara->GetChildren().GetFirst()->GetData();
 
3638
                    else
 
3639
                        firstObject = newPara->SplitAt(range.GetStart());
 
3640
 
 
3641
                    // Increment by 1 because we're apply the style one _after_ the split point
 
3642
                    long splitPoint = childRange.GetEnd();
 
3643
                    if (splitPoint != newPara->GetRange().GetEnd())
 
3644
                        splitPoint ++;
 
3645
 
 
3646
                    // Find last object
 
3647
                    if (splitPoint == newPara->GetRange().GetEnd())
 
3648
                        lastObject = newPara->GetChildren().GetLast()->GetData();
 
3649
                    else
 
3650
                        // lastObject is set as a side-effect of splitting. It's
 
3651
                        // returned as the object before the new object.
 
3652
                        (void) newPara->SplitAt(splitPoint, & lastObject);
 
3653
 
 
3654
                    wxASSERT(firstObject != NULL);
 
3655
                    wxASSERT(lastObject != NULL);
 
3656
 
 
3657
                    if (!firstObject || !lastObject)
 
3658
                        continue;
 
3659
 
 
3660
                    wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
 
3661
                    wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
 
3662
 
 
3663
                    wxASSERT(firstNode);
 
3664
                    wxASSERT(lastNode);
 
3665
 
 
3666
                    wxRichTextObjectList::compatibility_iterator node2 = firstNode;
 
3667
 
 
3668
                    while (node2)
 
3669
                    {
 
3670
                        wxRichTextObject* child = node2->GetData();
 
3671
 
 
3672
                        if (removeProperties)
 
3673
                        {
 
3674
                            // Removes the given properties from the paragraph
 
3675
                            child->GetProperties().RemoveProperties(properties);
 
3676
                        }
 
3677
                        else if (resetExistingProperties)
 
3678
                            child->GetProperties() = properties;
 
3679
                        else
 
3680
                        {
 
3681
                            child->GetProperties().MergeProperties(properties);
 
3682
                        }
 
3683
 
 
3684
                        if (node2 == lastNode)
 
3685
                            break;
 
3686
 
 
3687
                        node2 = node2->GetNext();
 
3688
                    }
 
3689
                }
 
3690
            }
 
3691
        }
 
3692
 
 
3693
        node = node->GetNext();
 
3694
    }
 
3695
 
 
3696
    // Do action, or delay it until end of batch.
 
3697
    if (haveControl && withUndo)
 
3698
        buffer->SubmitAction(action);
 
3699
 
 
3700
    return true;
 
3701
}
 
3702
 
 
3703
void wxRichTextParagraphLayoutBox::Reset()
 
3704
{
 
3705
    Clear();
 
3706
 
 
3707
    wxRichTextBuffer* buffer = GetBuffer();
 
3708
    if (buffer && buffer->GetRichTextCtrl())
 
3709
    {
 
3710
        wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_BUFFER_RESET, buffer->GetRichTextCtrl()->GetId());
 
3711
        event.SetEventObject(buffer->GetRichTextCtrl());
 
3712
        event.SetContainer(this);
 
3713
 
 
3714
        buffer->SendEvent(event, true);
 
3715
    }
 
3716
 
 
3717
    AddParagraph(wxEmptyString);
 
3718
 
 
3719
    PrepareContent(*this);
 
3720
 
 
3721
    InvalidateHierarchy(wxRICHTEXT_ALL);
 
3722
}
 
3723
 
 
3724
/// Invalidate the buffer. With no argument, invalidates whole buffer.
 
3725
void wxRichTextParagraphLayoutBox::Invalidate(const wxRichTextRange& invalidRange)
 
3726
{
 
3727
    wxRichTextCompositeObject::Invalidate(invalidRange);
 
3728
 
 
3729
    DoInvalidate(invalidRange);
 
3730
}
 
3731
 
 
3732
// Do the (in)validation for this object only
 
3733
void wxRichTextParagraphLayoutBox::DoInvalidate(const wxRichTextRange& invalidRange)
 
3734
{
 
3735
    if (invalidRange == wxRICHTEXT_ALL)
 
3736
    {
 
3737
        m_invalidRange = wxRICHTEXT_ALL;
 
3738
    }
 
3739
    // Already invalidating everything
 
3740
    else if (m_invalidRange == wxRICHTEXT_ALL)
 
3741
    {
 
3742
    }
 
3743
    else
 
3744
    {
 
3745
        if ((invalidRange.GetStart() < m_invalidRange.GetStart()) || m_invalidRange.GetStart() == -1)
 
3746
            m_invalidRange.SetStart(invalidRange.GetStart());
 
3747
        if (invalidRange.GetEnd() > m_invalidRange.GetEnd())
 
3748
            m_invalidRange.SetEnd(invalidRange.GetEnd());
 
3749
    }
 
3750
}
 
3751
 
 
3752
// Do the (in)validation both up and down the hierarchy
 
3753
void wxRichTextParagraphLayoutBox::InvalidateHierarchy(const wxRichTextRange& invalidRange)
 
3754
{
 
3755
    Invalidate(invalidRange);
 
3756
 
 
3757
    if (invalidRange != wxRICHTEXT_NONE)
 
3758
    {
 
3759
        // Now go up the hierarchy
 
3760
        wxRichTextObject* thisObj = this;
 
3761
        wxRichTextObject* p = GetParent();
 
3762
        while (p)
 
3763
        {
 
3764
            wxRichTextParagraphLayoutBox* l = wxDynamicCast(p, wxRichTextParagraphLayoutBox);
 
3765
            if (l)
 
3766
                l->DoInvalidate(thisObj->GetRange());
 
3767
 
 
3768
            thisObj = p;
 
3769
            p = p->GetParent();
 
3770
        }
 
3771
    }
 
3772
}
 
3773
 
 
3774
/// Get invalid range, rounding to entire paragraphs if argument is true.
 
3775
wxRichTextRange wxRichTextParagraphLayoutBox::GetInvalidRange(bool wholeParagraphs) const
 
3776
{
 
3777
    if (m_invalidRange == wxRICHTEXT_ALL || m_invalidRange == wxRICHTEXT_NONE)
 
3778
        return m_invalidRange;
 
3779
 
 
3780
    wxRichTextRange range = m_invalidRange;
 
3781
 
 
3782
    if (wholeParagraphs)
 
3783
    {
 
3784
        wxRichTextParagraph* para1 = GetParagraphAtPosition(range.GetStart());
 
3785
        if (para1)
 
3786
            range.SetStart(para1->GetRange().GetStart());
 
3787
        // floating layout make all child should be relayout
 
3788
        range.SetEnd(GetOwnRange().GetEnd());
 
3789
    }
 
3790
    return range;
 
3791
}
 
3792
 
 
3793
/// Apply the style sheet to the buffer, for example if the styles have changed.
 
3794
bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet* styleSheet)
 
3795
{
 
3796
    wxASSERT(styleSheet != NULL);
 
3797
    if (!styleSheet)
 
3798
        return false;
 
3799
 
 
3800
    int foundCount = 0;
 
3801
 
 
3802
    wxRichTextAttr attr(GetBasicStyle());
 
3803
    if (GetBasicStyle().HasParagraphStyleName())
 
3804
    {
 
3805
        wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(GetBasicStyle().GetParagraphStyleName());
 
3806
        if (paraDef)
 
3807
        {
 
3808
            attr.Apply(paraDef->GetStyleMergedWithBase(styleSheet));
 
3809
            SetBasicStyle(attr);
 
3810
            foundCount ++;
 
3811
        }
 
3812
    }
 
3813
 
 
3814
    if (GetBasicStyle().HasCharacterStyleName())
 
3815
    {
 
3816
        wxRichTextCharacterStyleDefinition* charDef = styleSheet->FindCharacterStyle(GetBasicStyle().GetCharacterStyleName());
 
3817
        if (charDef)
 
3818
        {
 
3819
            attr.Apply(charDef->GetStyleMergedWithBase(styleSheet));
 
3820
            SetBasicStyle(attr);
 
3821
            foundCount ++;
 
3822
        }
 
3823
    }
 
3824
 
 
3825
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
3826
    while (node)
 
3827
    {
 
3828
        wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
 
3829
        // wxASSERT (para != NULL);
 
3830
 
 
3831
        if (para)
 
3832
        {
 
3833
            // Combine paragraph and list styles. If there is a list style in the original attributes,
 
3834
            // the current indentation overrides anything else and is used to find the item indentation.
 
3835
            // Also, for applying paragraph styles, consider having 2 modes: (1) we merge with what we have,
 
3836
            // thereby taking into account all user changes, (2) reset the style completely (except for indentation/list
 
3837
            // exception as above).
 
3838
            // Problem: when changing from one list style to another, there's a danger that the level info will get lost.
 
3839
            // So when changing a list style interactively, could retrieve level based on current style, then
 
3840
            // set appropriate indent and apply new style.
 
3841
 
 
3842
            int outline = -1;
 
3843
            int num = -1;
 
3844
            if (para->GetAttributes().HasOutlineLevel())
 
3845
                outline = para->GetAttributes().GetOutlineLevel();
 
3846
            if (para->GetAttributes().HasBulletNumber())
 
3847
                num = para->GetAttributes().GetBulletNumber();
 
3848
 
 
3849
            if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
 
3850
            {
 
3851
                int currentIndent = para->GetAttributes().GetLeftIndent();
 
3852
 
 
3853
                wxRichTextParagraphStyleDefinition* paraDef = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
 
3854
                wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
 
3855
                if (paraDef && !listDef)
 
3856
                {
 
3857
                    para->GetAttributes() = paraDef->GetStyleMergedWithBase(styleSheet);
 
3858
                    foundCount ++;
 
3859
                }
 
3860
                else if (listDef && !paraDef)
 
3861
                {
 
3862
                    // Set overall style defined for the list style definition
 
3863
                    para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
 
3864
 
 
3865
                    // Apply the style for this level
 
3866
                    wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
 
3867
                    foundCount ++;
 
3868
                }
 
3869
                else if (listDef && paraDef)
 
3870
                {
 
3871
                    // Combines overall list style, style for level, and paragraph style
 
3872
                    para->GetAttributes() = listDef->CombineWithParagraphStyle(currentIndent, paraDef->GetStyleMergedWithBase(styleSheet));
 
3873
                    foundCount ++;
 
3874
                }
 
3875
            }
 
3876
            else if (para->GetAttributes().GetParagraphStyleName().IsEmpty() && !para->GetAttributes().GetListStyleName().IsEmpty())
 
3877
            {
 
3878
                int currentIndent = para->GetAttributes().GetLeftIndent();
 
3879
 
 
3880
                wxRichTextListStyleDefinition* listDef = styleSheet->FindListStyle(para->GetAttributes().GetListStyleName());
 
3881
 
 
3882
                // Overall list definition style
 
3883
                para->GetAttributes() = listDef->GetStyleMergedWithBase(styleSheet);
 
3884
 
 
3885
                // Style for this level
 
3886
                wxRichTextApplyStyle(para->GetAttributes(), * listDef->GetLevelAttributes(listDef->FindLevelForIndent(currentIndent)));
 
3887
 
 
3888
                foundCount ++;
 
3889
            }
 
3890
            else if (!para->GetAttributes().GetParagraphStyleName().IsEmpty() && para->GetAttributes().GetListStyleName().IsEmpty())
 
3891
            {
 
3892
                wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
 
3893
                if (def)
 
3894
                {
 
3895
                    para->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
 
3896
                    foundCount ++;
 
3897
                }
 
3898
            }
 
3899
 
 
3900
            if (outline != -1)
 
3901
                para->GetAttributes().SetOutlineLevel(outline);
 
3902
            if (num != -1)
 
3903
                para->GetAttributes().SetBulletNumber(num);
 
3904
        }
 
3905
 
 
3906
        node = node->GetNext();
 
3907
    }
 
3908
    return foundCount != 0;
 
3909
}
 
3910
 
 
3911
/// Set list style
 
3912
bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
 
3913
{
 
3914
    wxRichTextBuffer* buffer = GetBuffer();
 
3915
    wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
 
3916
 
 
3917
    bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
 
3918
    // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
 
3919
    bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
 
3920
    bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
 
3921
 
 
3922
    // Current number, if numbering
 
3923
    int n = startFrom;
 
3924
 
 
3925
    wxASSERT (!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
 
3926
 
 
3927
    // If we are associated with a control, make undoable; otherwise, apply immediately
 
3928
    // to the data.
 
3929
 
 
3930
    bool haveControl = (buffer->GetRichTextCtrl() != NULL);
 
3931
 
 
3932
    wxRichTextAction* action = NULL;
 
3933
 
 
3934
    if (haveControl && withUndo)
 
3935
    {
 
3936
        action = new wxRichTextAction(NULL, _("Change List Style"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
 
3937
        action->SetRange(range);
 
3938
        action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
 
3939
    }
 
3940
 
 
3941
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
3942
    while (node)
 
3943
    {
 
3944
        wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
 
3945
        // wxASSERT (para != NULL);
 
3946
 
 
3947
        if (para && para->GetChildCount() > 0)
 
3948
        {
 
3949
            // Stop searching if we're beyond the range of interest
 
3950
            if (para->GetRange().GetStart() > range.GetEnd())
 
3951
                break;
 
3952
 
 
3953
            if (!para->GetRange().IsOutside(range))
 
3954
            {
 
3955
                // We'll be using a copy of the paragraph to make style changes,
 
3956
                // not updating the buffer directly.
 
3957
                wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
 
3958
 
 
3959
                if (haveControl && withUndo)
 
3960
                {
 
3961
                    newPara = new wxRichTextParagraph(*para);
 
3962
                    action->GetNewParagraphs().AppendChild(newPara);
 
3963
 
 
3964
                    // Also store the old ones for Undo
 
3965
                    action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
 
3966
                }
 
3967
                else
 
3968
                    newPara = para;
 
3969
 
 
3970
                if (def)
 
3971
                {
 
3972
                    int thisIndent = newPara->GetAttributes().GetLeftIndent();
 
3973
                    int thisLevel = specifyLevel ? specifiedLevel : def->FindLevelForIndent(thisIndent);
 
3974
 
 
3975
                    // How is numbering going to work?
 
3976
                    // If we are renumbering, or numbering for the first time, we need to keep
 
3977
                    // track of the number for each level. But we might be simply applying a different
 
3978
                    // list style.
 
3979
                    // In Word, applying a style to several paragraphs, even if at different levels,
 
3980
                    // reverts the level back to the same one. So we could do the same here.
 
3981
                    // Renumbering will need to be done when we promote/demote a paragraph.
 
3982
 
 
3983
                    // Apply the overall list style, and item style for this level
 
3984
                    wxRichTextAttr listStyle(def->GetCombinedStyleForLevel(thisLevel, styleSheet));
 
3985
                    wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
 
3986
 
 
3987
                    // Now we need to do numbering
 
3988
                    if (renumber)
 
3989
                    {
 
3990
                        newPara->GetAttributes().SetBulletNumber(n);
 
3991
                    }
 
3992
 
 
3993
                    n ++;
 
3994
                }
 
3995
                else if (!newPara->GetAttributes().GetListStyleName().IsEmpty())
 
3996
                {
 
3997
                    // if def is NULL, remove list style, applying any associated paragraph style
 
3998
                    // to restore the attributes
 
3999
 
 
4000
                    newPara->GetAttributes().SetListStyleName(wxEmptyString);
 
4001
                    newPara->GetAttributes().SetLeftIndent(0, 0);
 
4002
                    newPara->GetAttributes().SetBulletText(wxEmptyString);
 
4003
 
 
4004
                    // Eliminate the main list-related attributes
 
4005
                    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);
 
4006
 
 
4007
                    if (styleSheet && !newPara->GetAttributes().GetParagraphStyleName().IsEmpty())
 
4008
                    {
 
4009
                        wxRichTextParagraphStyleDefinition* def = styleSheet->FindParagraphStyle(newPara->GetAttributes().GetParagraphStyleName());
 
4010
                        if (def)
 
4011
                        {
 
4012
                            newPara->GetAttributes() = def->GetStyleMergedWithBase(styleSheet);
 
4013
                        }
 
4014
                    }
 
4015
                }
 
4016
            }
 
4017
        }
 
4018
 
 
4019
        node = node->GetNext();
 
4020
    }
 
4021
 
 
4022
    // Do action, or delay it until end of batch.
 
4023
    if (haveControl && withUndo)
 
4024
        buffer->SubmitAction(action);
 
4025
 
 
4026
    return true;
 
4027
}
 
4028
 
 
4029
bool wxRichTextParagraphLayoutBox::SetListStyle(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
 
4030
{
 
4031
    wxRichTextBuffer* buffer = GetBuffer();
 
4032
    if (buffer && buffer->GetStyleSheet())
 
4033
    {
 
4034
        wxRichTextListStyleDefinition* def = buffer->GetStyleSheet()->FindListStyle(defName);
 
4035
        if (def)
 
4036
            return SetListStyle(range, def, flags, startFrom, specifiedLevel);
 
4037
    }
 
4038
    return false;
 
4039
}
 
4040
 
 
4041
/// Clear list for given range
 
4042
bool wxRichTextParagraphLayoutBox::ClearListStyle(const wxRichTextRange& range, int flags)
 
4043
{
 
4044
    return SetListStyle(range, NULL, flags);
 
4045
}
 
4046
 
 
4047
/// Number/renumber any list elements in the given range
 
4048
bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
 
4049
{
 
4050
    return DoNumberList(range, range, 0, def, flags, startFrom, specifiedLevel);
 
4051
}
 
4052
 
 
4053
/// Number/renumber any list elements in the given range. Also do promotion or demotion of items, if specified
 
4054
bool wxRichTextParagraphLayoutBox::DoNumberList(const wxRichTextRange& range, const wxRichTextRange& promotionRange, int promoteBy,
 
4055
                                                wxRichTextListStyleDefinition* def, int flags, int startFrom, int specifiedLevel)
 
4056
{
 
4057
    wxRichTextBuffer* buffer = GetBuffer();
 
4058
    wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
 
4059
 
 
4060
    bool withUndo = ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
 
4061
    // bool applyMinimal = ((flags & wxRICHTEXT_SETSTYLE_OPTIMIZE) != 0);
 
4062
#if wxDEBUG_LEVEL
 
4063
    bool specifyLevel = ((flags & wxRICHTEXT_SETSTYLE_SPECIFY_LEVEL) != 0);
 
4064
#endif
 
4065
 
 
4066
    bool renumber = ((flags & wxRICHTEXT_SETSTYLE_RENUMBER) != 0);
 
4067
 
 
4068
    // Max number of levels
 
4069
    const int maxLevels = 10;
 
4070
 
 
4071
    // The level we're looking at now
 
4072
    int currentLevel = -1;
 
4073
 
 
4074
    // The item number for each level
 
4075
    int levels[maxLevels];
 
4076
    int i;
 
4077
 
 
4078
    // Reset all numbering
 
4079
    for (i = 0; i < maxLevels; i++)
 
4080
    {
 
4081
        if (startFrom != -1)
 
4082
            levels[i] = startFrom-1;
 
4083
        else if (renumber) // start again
 
4084
            levels[i] = 0;
 
4085
        else
 
4086
            levels[i] = -1; // start from the number we found, if any
 
4087
    }
 
4088
 
 
4089
#if wxDEBUG_LEVEL
 
4090
    wxASSERT(!specifyLevel || (specifyLevel && (specifiedLevel >= 0)));
 
4091
#endif
 
4092
 
 
4093
    // If we are associated with a control, make undoable; otherwise, apply immediately
 
4094
    // to the data.
 
4095
 
 
4096
    bool haveControl = (buffer->GetRichTextCtrl() != NULL);
 
4097
 
 
4098
    wxRichTextAction* action = NULL;
 
4099
 
 
4100
    if (haveControl && withUndo)
 
4101
    {
 
4102
        action = new wxRichTextAction(NULL, _("Renumber List"), wxRICHTEXT_CHANGE_STYLE, buffer, this, buffer->GetRichTextCtrl());
 
4103
        action->SetRange(range);
 
4104
        action->SetPosition(buffer->GetRichTextCtrl()->GetCaretPosition());
 
4105
    }
 
4106
 
 
4107
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
4108
    while (node)
 
4109
    {
 
4110
        wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
 
4111
        // wxASSERT (para != NULL);
 
4112
 
 
4113
        if (para && para->GetChildCount() > 0)
 
4114
        {
 
4115
            // Stop searching if we're beyond the range of interest
 
4116
            if (para->GetRange().GetStart() > range.GetEnd())
 
4117
                break;
 
4118
 
 
4119
            if (!para->GetRange().IsOutside(range))
 
4120
            {
 
4121
                // We'll be using a copy of the paragraph to make style changes,
 
4122
                // not updating the buffer directly.
 
4123
                wxRichTextParagraph* newPara wxDUMMY_INITIALIZE(NULL);
 
4124
 
 
4125
                if (haveControl && withUndo)
 
4126
                {
 
4127
                    newPara = new wxRichTextParagraph(*para);
 
4128
                    action->GetNewParagraphs().AppendChild(newPara);
 
4129
 
 
4130
                    // Also store the old ones for Undo
 
4131
                    action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
 
4132
                }
 
4133
                else
 
4134
                    newPara = para;
 
4135
 
 
4136
                wxRichTextListStyleDefinition* defToUse = def;
 
4137
                if (!defToUse)
 
4138
                {
 
4139
                    if (styleSheet && !newPara->GetAttributes().GetListStyleName().IsEmpty())
 
4140
                        defToUse = styleSheet->FindListStyle(newPara->GetAttributes().GetListStyleName());
 
4141
                }
 
4142
 
 
4143
                if (defToUse)
 
4144
                {
 
4145
                    int thisIndent = newPara->GetAttributes().GetLeftIndent();
 
4146
                    int thisLevel = defToUse->FindLevelForIndent(thisIndent);
 
4147
 
 
4148
                    // If we've specified a level to apply to all, change the level.
 
4149
                    if (specifiedLevel != -1)
 
4150
                        thisLevel = specifiedLevel;
 
4151
 
 
4152
                    // Do promotion if specified
 
4153
                    if ((promoteBy != 0) && !para->GetRange().IsOutside(promotionRange))
 
4154
                    {
 
4155
                        thisLevel = thisLevel - promoteBy;
 
4156
                        if (thisLevel < 0)
 
4157
                            thisLevel = 0;
 
4158
                        if (thisLevel > 9)
 
4159
                            thisLevel = 9;
 
4160
                    }
 
4161
 
 
4162
                    // Apply the overall list style, and item style for this level
 
4163
                    wxRichTextAttr listStyle(defToUse->GetCombinedStyleForLevel(thisLevel, styleSheet));
 
4164
                    wxRichTextApplyStyle(newPara->GetAttributes(), listStyle);
 
4165
 
 
4166
                    // OK, we've (re)applied the style, now let's get the numbering right.
 
4167
 
 
4168
                    if (currentLevel == -1)
 
4169
                        currentLevel = thisLevel;
 
4170
 
 
4171
                    // Same level as before, do nothing except increment level's number afterwards
 
4172
                    if (currentLevel == thisLevel)
 
4173
                    {
 
4174
                    }
 
4175
                    // A deeper level: start renumbering all levels after current level
 
4176
                    else if (thisLevel > currentLevel)
 
4177
                    {
 
4178
                        for (i = currentLevel+1; i <= thisLevel; i++)
 
4179
                        {
 
4180
                            levels[i] = 0;
 
4181
                        }
 
4182
                        currentLevel = thisLevel;
 
4183
                    }
 
4184
                    else if (thisLevel < currentLevel)
 
4185
                    {
 
4186
                        currentLevel = thisLevel;
 
4187
                    }
 
4188
 
 
4189
                    // Use the current numbering if -1 and we have a bullet number already
 
4190
                    if (levels[currentLevel] == -1)
 
4191
                    {
 
4192
                        if (newPara->GetAttributes().HasBulletNumber())
 
4193
                            levels[currentLevel] = newPara->GetAttributes().GetBulletNumber();
 
4194
                        else
 
4195
                            levels[currentLevel] = 1;
 
4196
                    }
 
4197
                    else
 
4198
                    {
 
4199
                        levels[currentLevel] ++;
 
4200
                    }
 
4201
 
 
4202
                    newPara->GetAttributes().SetBulletNumber(levels[currentLevel]);
 
4203
 
 
4204
                    // Create the bullet text if an outline list
 
4205
                    if (listStyle.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
 
4206
                    {
 
4207
                        wxString text;
 
4208
                        for (i = 0; i <= currentLevel; i++)
 
4209
                        {
 
4210
                            if (!text.IsEmpty())
 
4211
                                text += wxT(".");
 
4212
                            text += wxString::Format(wxT("%d"), levels[i]);
 
4213
                        }
 
4214
                        newPara->GetAttributes().SetBulletText(text);
 
4215
                    }
 
4216
                }
 
4217
            }
 
4218
        }
 
4219
 
 
4220
        node = node->GetNext();
 
4221
    }
 
4222
 
 
4223
    // Do action, or delay it until end of batch.
 
4224
    if (haveControl && withUndo)
 
4225
        buffer->SubmitAction(action);
 
4226
 
 
4227
    return true;
 
4228
}
 
4229
 
 
4230
bool wxRichTextParagraphLayoutBox::NumberList(const wxRichTextRange& range, const wxString& defName, int flags, int startFrom, int specifiedLevel)
 
4231
{
 
4232
    wxRichTextBuffer* buffer = GetBuffer();
 
4233
    if (buffer->GetStyleSheet())
 
4234
    {
 
4235
        wxRichTextListStyleDefinition* def = NULL;
 
4236
        if (!defName.IsEmpty())
 
4237
            def = buffer->GetStyleSheet()->FindListStyle(defName);
 
4238
        return NumberList(range, def, flags, startFrom, specifiedLevel);
 
4239
    }
 
4240
    return false;
 
4241
}
 
4242
 
 
4243
/// Promote the list items within the given range. promoteBy can be a positive or negative number, e.g. 1 or -1
 
4244
bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, wxRichTextListStyleDefinition* def, int flags, int specifiedLevel)
 
4245
{
 
4246
    // TODO
 
4247
    // One strategy is to first work out the range within which renumbering must occur. Then could pass these two ranges
 
4248
    // to NumberList with a flag indicating promotion is required within one of the ranges.
 
4249
    // Find first and last paragraphs in range. Then for first, calculate new indentation and look back until we find
 
4250
    // a paragraph that either has no list style, or has one that is different or whose indentation is less.
 
4251
    // We start renumbering from the para after that different para we found. We specify that the numbering of that
 
4252
    // list position will start from 1.
 
4253
    // Similarly, we look after the last para in the promote range for an indentation that is less (or no list style).
 
4254
    // We can end the renumbering at this point.
 
4255
 
 
4256
    // For now, only renumber within the promotion range.
 
4257
 
 
4258
    return DoNumberList(range, range, promoteBy, def, flags, 1, specifiedLevel);
 
4259
}
 
4260
 
 
4261
bool wxRichTextParagraphLayoutBox::PromoteList(int promoteBy, const wxRichTextRange& range, const wxString& defName, int flags, int specifiedLevel)
 
4262
{
 
4263
    wxRichTextBuffer* buffer = GetBuffer();
 
4264
    if (buffer->GetStyleSheet())
 
4265
    {
 
4266
        wxRichTextListStyleDefinition* def = NULL;
 
4267
        if (!defName.IsEmpty())
 
4268
            def = buffer->GetStyleSheet()->FindListStyle(defName);
 
4269
        return PromoteList(promoteBy, range, def, flags, specifiedLevel);
 
4270
    }
 
4271
    return false;
 
4272
}
 
4273
 
 
4274
/// Fills in the attributes for numbering a paragraph after previousParagraph. It also finds the
 
4275
/// position of the paragraph that it had to start looking from.
 
4276
bool wxRichTextParagraphLayoutBox::FindNextParagraphNumber(wxRichTextParagraph* previousParagraph, wxRichTextAttr& attr) const
 
4277
{
 
4278
    if (!previousParagraph->GetAttributes().HasFlag(wxTEXT_ATTR_BULLET_STYLE) || previousParagraph->GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE)
 
4279
        return false;
 
4280
 
 
4281
    wxRichTextBuffer* buffer = GetBuffer();
 
4282
    wxRichTextStyleSheet* styleSheet = buffer->GetStyleSheet();
 
4283
    if (styleSheet && !previousParagraph->GetAttributes().GetListStyleName().IsEmpty())
 
4284
    {
 
4285
        wxRichTextListStyleDefinition* def = styleSheet->FindListStyle(previousParagraph->GetAttributes().GetListStyleName());
 
4286
        if (def)
 
4287
        {
 
4288
            // int thisIndent = previousParagraph->GetAttributes().GetLeftIndent();
 
4289
            // int thisLevel = def->FindLevelForIndent(thisIndent);
 
4290
 
 
4291
            bool isOutline = (previousParagraph->GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE) != 0;
 
4292
 
 
4293
            attr.SetFlags(previousParagraph->GetAttributes().GetFlags() & (wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER|wxTEXT_ATTR_BULLET_TEXT|wxTEXT_ATTR_BULLET_NAME));
 
4294
            if (previousParagraph->GetAttributes().HasBulletName())
 
4295
                attr.SetBulletName(previousParagraph->GetAttributes().GetBulletName());
 
4296
            attr.SetBulletStyle(previousParagraph->GetAttributes().GetBulletStyle());
 
4297
            attr.SetListStyleName(previousParagraph->GetAttributes().GetListStyleName());
 
4298
 
 
4299
            int nextNumber = previousParagraph->GetAttributes().GetBulletNumber() + 1;
 
4300
            attr.SetBulletNumber(nextNumber);
 
4301
 
 
4302
            if (isOutline)
 
4303
            {
 
4304
                wxString text = previousParagraph->GetAttributes().GetBulletText();
 
4305
                if (!text.IsEmpty())
 
4306
                {
 
4307
                    int pos = text.Find(wxT('.'), true);
 
4308
                    if (pos != wxNOT_FOUND)
 
4309
                    {
 
4310
                        text = text.Mid(0, text.Length() - pos - 1);
 
4311
                    }
 
4312
                    else
 
4313
                        text = wxEmptyString;
 
4314
                    if (!text.IsEmpty())
 
4315
                        text += wxT(".");
 
4316
                    text += wxString::Format(wxT("%d"), nextNumber);
 
4317
                    attr.SetBulletText(text);
 
4318
                }
 
4319
            }
 
4320
 
 
4321
            return true;
 
4322
        }
 
4323
        else
 
4324
            return false;
 
4325
    }
 
4326
    else
 
4327
        return false;
 
4328
}
 
4329
 
 
4330
/*!
 
4331
 * wxRichTextParagraph
 
4332
 * This object represents a single paragraph (or in a straight text editor, a line).
 
4333
 */
 
4334
 
 
4335
IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph, wxRichTextCompositeObject)
 
4336
 
 
4337
wxArrayInt wxRichTextParagraph::sm_defaultTabs;
 
4338
 
 
4339
wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject* parent, wxRichTextAttr* style):
 
4340
    wxRichTextCompositeObject(parent)
 
4341
{
 
4342
    if (style)
 
4343
        SetAttributes(*style);
 
4344
}
 
4345
 
 
4346
wxRichTextParagraph::wxRichTextParagraph(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* paraStyle, wxRichTextAttr* charStyle):
 
4347
    wxRichTextCompositeObject(parent)
 
4348
{
 
4349
    if (paraStyle)
 
4350
        SetAttributes(*paraStyle);
 
4351
 
 
4352
    AppendChild(new wxRichTextPlainText(text, this, charStyle));
 
4353
}
 
4354
 
 
4355
wxRichTextParagraph::~wxRichTextParagraph()
 
4356
{
 
4357
    ClearLines();
 
4358
}
 
4359
 
 
4360
/// Draw the item
 
4361
bool wxRichTextParagraph::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int style)
 
4362
{
 
4363
    if (!IsShown())
 
4364
        return true;
 
4365
 
 
4366
    // Currently we don't merge these attributes with the parent, but we
 
4367
    // should consider whether we should (e.g. if we set a border colour
 
4368
    // for all paragraphs). But generally box attributes are likely to be
 
4369
    // different for different objects.
 
4370
    wxRect paraRect = GetRect();
 
4371
    wxRichTextAttr attr = GetCombinedAttributes();
 
4372
    context.ApplyVirtualAttributes(attr, this);
 
4373
 
 
4374
    DrawBoxAttributes(dc, GetBuffer(), attr, paraRect);
 
4375
 
 
4376
    // Draw the bullet, if any
 
4377
    if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
 
4378
    {
 
4379
        if (attr.GetLeftSubIndent() != 0)
 
4380
        {
 
4381
            int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
 
4382
            int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
 
4383
 
 
4384
            wxRichTextAttr bulletAttr(attr);
 
4385
 
 
4386
            // Combine with the font of the first piece of content, if one is specified
 
4387
            if (GetChildren().GetCount() > 0)
 
4388
            {
 
4389
                wxRichTextObject* firstObj = (wxRichTextObject*) GetChildren().GetFirst()->GetData();
 
4390
                if (!firstObj->IsFloatable() && firstObj->GetAttributes().HasFont())
 
4391
                {
 
4392
                    wxRichTextApplyStyle(bulletAttr, firstObj->GetAttributes());
 
4393
                }
 
4394
            }
 
4395
 
 
4396
            // Get line height from first line, if any
 
4397
            wxRichTextLine* line = m_cachedLines.GetFirst() ? (wxRichTextLine* ) m_cachedLines.GetFirst()->GetData() : NULL;
 
4398
 
 
4399
            wxPoint linePos;
 
4400
            int lineHeight wxDUMMY_INITIALIZE(0);
 
4401
            if (line)
 
4402
            {
 
4403
                lineHeight = line->GetSize().y;
 
4404
                linePos = line->GetPosition() + GetPosition();
 
4405
            }
 
4406
            else
 
4407
            {
 
4408
                wxFont font;
 
4409
                if (bulletAttr.HasFont() && GetBuffer())
 
4410
                    font = GetBuffer()->GetFontTable().FindFont(bulletAttr);
 
4411
                else
 
4412
                    font = (*wxNORMAL_FONT);
 
4413
 
 
4414
                wxCheckSetFont(dc, font);
 
4415
 
 
4416
                lineHeight = dc.GetCharHeight();
 
4417
                linePos = GetPosition();
 
4418
                linePos.y += spaceBeforePara;
 
4419
            }
 
4420
 
 
4421
            wxRect bulletRect(GetPosition().x + leftIndent, linePos.y, linePos.x - (GetPosition().x + leftIndent), lineHeight);
 
4422
 
 
4423
            if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP)
 
4424
            {
 
4425
                if (wxRichTextBuffer::GetRenderer())
 
4426
                    wxRichTextBuffer::GetRenderer()->DrawBitmapBullet(this, dc, bulletAttr, bulletRect);
 
4427
            }
 
4428
            else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_STANDARD)
 
4429
            {
 
4430
                if (wxRichTextBuffer::GetRenderer())
 
4431
                    wxRichTextBuffer::GetRenderer()->DrawStandardBullet(this, dc, bulletAttr, bulletRect);
 
4432
            }
 
4433
            else
 
4434
            {
 
4435
                wxString bulletText = GetBulletText();
 
4436
 
 
4437
                if (!bulletText.empty() && wxRichTextBuffer::GetRenderer())
 
4438
                    wxRichTextBuffer::GetRenderer()->DrawTextBullet(this, dc, bulletAttr, bulletRect, bulletText);
 
4439
            }
 
4440
        }
 
4441
    }
 
4442
 
 
4443
    // Draw the range for each line, one object at a time.
 
4444
 
 
4445
    wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
 
4446
    while (node)
 
4447
    {
 
4448
        wxRichTextLine* line = node->GetData();
 
4449
        wxRichTextRange lineRange = line->GetAbsoluteRange();
 
4450
 
 
4451
        // Lines are specified relative to the paragraph
 
4452
 
 
4453
        wxPoint linePosition = line->GetPosition() + GetPosition();
 
4454
 
 
4455
        // Don't draw if off the screen
 
4456
        if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) != 0) || ((linePosition.y + line->GetSize().y) >= rect.y && linePosition.y <= rect.y + rect.height))
 
4457
        {
 
4458
            wxPoint objectPosition = linePosition;
 
4459
            int maxDescent = line->GetDescent();
 
4460
 
 
4461
            // Loop through objects until we get to the one within range
 
4462
            wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
 
4463
 
 
4464
            int i = 0;
 
4465
            while (node2)
 
4466
            {
 
4467
                wxRichTextObject* child = node2->GetData();
 
4468
 
 
4469
                if (!child->IsFloating() && child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange) && !lineRange.IsOutside(range))
 
4470
                {
 
4471
                    // Draw this part of the line at the correct position
 
4472
                    wxRichTextRange objectRange(child->GetRange());
 
4473
                    objectRange.LimitTo(lineRange);
 
4474
 
 
4475
                    wxSize objectSize;
 
4476
                    if (child->IsTopLevel())
 
4477
                    {
 
4478
                        objectSize = child->GetCachedSize();
 
4479
                        objectRange = child->GetOwnRange();
 
4480
                    }
 
4481
                    else
 
4482
                    {
 
4483
#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING && wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
 
4484
                        if (i < (int) line->GetObjectSizes().GetCount())
 
4485
                        {
 
4486
                            objectSize.x = line->GetObjectSizes()[(size_t) i];
 
4487
                        }
 
4488
                        else
 
4489
#endif
 
4490
                        {
 
4491
                            int descent = 0;
 
4492
                            child->GetRangeSize(objectRange, objectSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, objectPosition);
 
4493
                        }
 
4494
                    }
 
4495
 
 
4496
                    // Use the child object's width, but the whole line's height
 
4497
                    wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
 
4498
                    child->Draw(dc, context, objectRange, selection, childRect, maxDescent, style);
 
4499
 
 
4500
                    objectPosition.x += objectSize.x;
 
4501
                    i ++;
 
4502
                }
 
4503
                else if (child->GetRange().GetStart() > lineRange.GetEnd())
 
4504
                    // Can break out of inner loop now since we've passed this line's range
 
4505
                    break;
 
4506
 
 
4507
                node2 = node2->GetNext();
 
4508
            }
 
4509
        }
 
4510
 
 
4511
        node = node->GetNext();
 
4512
    }
 
4513
 
 
4514
    return true;
 
4515
}
 
4516
 
 
4517
// Get the range width using partial extents calculated for the whole paragraph.
 
4518
static int wxRichTextGetRangeWidth(const wxRichTextParagraph& para, const wxRichTextRange& range, const wxArrayInt& partialExtents)
 
4519
{
 
4520
    wxASSERT(partialExtents.GetCount() >= (size_t) range.GetLength());
 
4521
 
 
4522
    if (partialExtents.GetCount() < (size_t) range.GetLength())
 
4523
        return 0;
 
4524
 
 
4525
    int leftMostPos = 0;
 
4526
    if (range.GetStart() - para.GetRange().GetStart() > 0)
 
4527
        leftMostPos = partialExtents[range.GetStart() - para.GetRange().GetStart() - 1];
 
4528
 
 
4529
    int rightMostPos = partialExtents[range.GetEnd() - para.GetRange().GetStart()];
 
4530
 
 
4531
    int w = rightMostPos - leftMostPos;
 
4532
 
 
4533
    return w;
 
4534
}
 
4535
 
 
4536
/// Lay the item out
 
4537
bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
 
4538
{
 
4539
    // Deal with floating objects firstly before the normal layout
 
4540
    wxRichTextBuffer* buffer = GetBuffer();
 
4541
    wxASSERT(buffer);
 
4542
    wxRichTextFloatCollector* collector = GetContainer()->GetFloatCollector();
 
4543
    wxASSERT(collector);
 
4544
    LayoutFloat(dc, context, rect, style, collector);
 
4545
 
 
4546
    wxRichTextAttr attr = GetCombinedAttributes();
 
4547
    context.ApplyVirtualAttributes(attr, this);
 
4548
 
 
4549
    // ClearLines();
 
4550
 
 
4551
    // Increase the size of the paragraph due to spacing
 
4552
    int spaceBeforePara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingBefore());
 
4553
    int spaceAfterPara = ConvertTenthsMMToPixels(dc, attr.GetParagraphSpacingAfter());
 
4554
    int leftIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftIndent());
 
4555
    int leftSubIndent = ConvertTenthsMMToPixels(dc, attr.GetLeftSubIndent());
 
4556
    int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
 
4557
 
 
4558
    int lineSpacing = 0;
 
4559
 
 
4560
    // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
 
4561
    if (attr.HasLineSpacing() && attr.GetLineSpacing() > 0 && attr.GetFont().IsOk())
 
4562
    {
 
4563
        wxCheckSetFont(dc, attr.GetFont());
 
4564
        lineSpacing = (int) (double(dc.GetCharHeight()) * (double(attr.GetLineSpacing())/10.0 - 1.0));
 
4565
    }
 
4566
 
 
4567
    // Start position for each line relative to the paragraph
 
4568
    int startPositionFirstLine = leftIndent;
 
4569
    int startPositionSubsequentLines = leftIndent + leftSubIndent;
 
4570
 
 
4571
    // If we have a bullet in this paragraph, the start position for the first line's text
 
4572
    // is actually leftIndent + leftSubIndent.
 
4573
    if (attr.GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
 
4574
        startPositionFirstLine = startPositionSubsequentLines;
 
4575
 
 
4576
    long lastEndPos = GetRange().GetStart()-1;
 
4577
    long lastCompletedEndPos = lastEndPos;
 
4578
 
 
4579
    int currentWidth = 0;
 
4580
    SetPosition(rect.GetPosition());
 
4581
 
 
4582
    wxPoint currentPosition(0, spaceBeforePara); // We will calculate lines relative to paragraph
 
4583
    int lineHeight = 0;
 
4584
    int maxWidth = 0;
 
4585
    int maxHeight = currentPosition.y;
 
4586
    int maxAscent = 0;
 
4587
    int maxDescent = 0;
 
4588
    int lineCount = 0;
 
4589
    int lineAscent = 0;
 
4590
    int lineDescent = 0;
 
4591
 
 
4592
    wxRichTextObjectList::compatibility_iterator node;
 
4593
 
 
4594
#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
 
4595
    wxUnusedVar(style);
 
4596
    wxArrayInt partialExtents;
 
4597
 
 
4598
    wxSize paraSize;
 
4599
    int paraDescent = 0;
 
4600
 
 
4601
    // This calculates the partial text extents
 
4602
    GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, rect.GetPosition(), & partialExtents);
 
4603
#else
 
4604
    node = m_children.GetFirst();
 
4605
    while (node)
 
4606
    {
 
4607
        wxRichTextObject* child = node->GetData();
 
4608
 
 
4609
        //child->SetCachedSize(wxDefaultSize);
 
4610
        child->Layout(dc, context, rect, style);
 
4611
 
 
4612
        node = node->GetNext();
 
4613
    }
 
4614
#endif
 
4615
 
 
4616
    // Split up lines
 
4617
 
 
4618
    // We may need to go back to a previous child, in which case create the new line,
 
4619
    // find the child corresponding to the start position of the string, and
 
4620
    // continue.
 
4621
 
 
4622
    wxRect availableRect;
 
4623
 
 
4624
    node = m_children.GetFirst();
 
4625
    while (node)
 
4626
    {
 
4627
        wxRichTextObject* child = node->GetData();
 
4628
 
 
4629
        // If floating, ignore. We already laid out floats.
 
4630
        // Also ignore if empty object, except if we haven't got any
 
4631
        // size yet.
 
4632
        if (child->IsFloating() || !child->IsShown() ||
 
4633
            (child->GetRange().GetLength() == 0 && maxHeight > spaceBeforePara)
 
4634
            )
 
4635
        {
 
4636
            node = node->GetNext();
 
4637
            continue;
 
4638
        }
 
4639
 
 
4640
        // If this is e.g. a composite text box, it will need to be laid out itself.
 
4641
        // But if just a text fragment or image, for example, this will
 
4642
        // do nothing. NB: won't we need to set the position after layout?
 
4643
        // since for example if position is dependent on vertical line size, we
 
4644
        // can't tell the position until the size is determined. So possibly introduce
 
4645
        // another layout phase.
 
4646
 
 
4647
        // We may only be looking at part of a child, if we searched back for wrapping
 
4648
        // and found a suitable point some way into the child. So get the size for the fragment
 
4649
        // if necessary.
 
4650
 
 
4651
        long nextBreakPos = GetFirstLineBreakPosition(lastEndPos+1);
 
4652
        long lastPosToUse = child->GetRange().GetEnd();
 
4653
        bool lineBreakInThisObject = (nextBreakPos > -1 && nextBreakPos <= child->GetRange().GetEnd());
 
4654
 
 
4655
        if (lineBreakInThisObject)
 
4656
            lastPosToUse = nextBreakPos;
 
4657
 
 
4658
        wxSize childSize;
 
4659
        int childDescent = 0;
 
4660
 
 
4661
        int startOffset = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
 
4662
        availableRect = wxRect(rect.x + startOffset, rect.y + currentPosition.y,
 
4663
                                     rect.width - startOffset - rightIndent, rect.height);
 
4664
 
 
4665
        if (child->IsTopLevel())
 
4666
        {
 
4667
            wxSize oldSize = child->GetCachedSize();
 
4668
 
 
4669
            child->Invalidate(wxRICHTEXT_ALL);
 
4670
            child->SetPosition(wxPoint(0, 0));
 
4671
 
 
4672
            // Lays out the object first with a given amount of space, and then if no width was specified in attr,
 
4673
            // lays out the object again using the minimum size
 
4674
            // The position will be determined by its location in its line,
 
4675
            // and not by the child's actual position.
 
4676
            child->LayoutToBestSize(dc, context, buffer,
 
4677
                    attr, child->GetAttributes(), availableRect, parentRect, style);
 
4678
 
 
4679
            if (oldSize != child->GetCachedSize())
 
4680
            {
 
4681
                partialExtents.Clear();
 
4682
 
 
4683
                // Recalculate the partial text extents since the child object changed size
 
4684
                GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
 
4685
            }
 
4686
        }
 
4687
 
 
4688
        // Problem: we need to layout composites here for which we need the available width,
 
4689
        // but we can't get the available width without using the float collector which
 
4690
        // needs to know the object height.
 
4691
 
 
4692
        if ((nextBreakPos == -1) && (lastEndPos == child->GetRange().GetStart() - 1)) // i.e. we want to get the whole thing
 
4693
        {
 
4694
            childSize = child->GetCachedSize();
 
4695
            childDescent = child->GetDescent();
 
4696
        }
 
4697
        else
 
4698
        {
 
4699
#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
 
4700
            // Get height only, then the width using the partial extents
 
4701
            GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
 
4702
            childSize.x = wxRichTextGetRangeWidth(*this, wxRichTextRange(lastEndPos+1, lastPosToUse), partialExtents);
 
4703
#else
 
4704
            GetRangeSize(wxRichTextRange(lastEndPos+1, lastPosToUse), childSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED, rect.GetPosition());
 
4705
#endif
 
4706
        }
 
4707
 
 
4708
        bool doLoop = true;
 
4709
        int loopIterations = 0;
 
4710
 
 
4711
        // If there are nested objects that need to lay themselves out, we have to do this in a
 
4712
        // loop because the height of the object may well depend on the available width.
 
4713
        // And because of floating object positioning, the available width depends on the
 
4714
        // height of the object and whether it will clash with the floating objects.
 
4715
        // So, we see whether the available width changes due to the presence of floating images.
 
4716
        // If it does, then we'll use the new restricted width to find the object height again.
 
4717
        // If this causes another restriction in the available width, we'll try again, until
 
4718
        // either we lose patience or the available width settles down.
 
4719
        do
 
4720
        {
 
4721
            loopIterations ++;
 
4722
 
 
4723
            wxRect oldAvailableRect = availableRect;
 
4724
 
 
4725
            // Available width depends on the floating objects and the line height.
 
4726
            // Note: the floating objects may be placed vertically along the two sides of
 
4727
            // buffer, so we may have different available line widths with different
 
4728
            // [startY, endY]. So, we can't determine how wide the available
 
4729
            // space is until we know the exact line height.
 
4730
            if (childDescent == 0)
 
4731
            {
 
4732
                lineHeight = wxMax(lineHeight, childSize.y);
 
4733
                lineDescent = maxDescent;
 
4734
                lineAscent = maxAscent;
 
4735
            }
 
4736
            else
 
4737
            {
 
4738
                lineDescent = wxMax(childDescent, maxDescent);
 
4739
                lineAscent = wxMax(childSize.y-childDescent, maxAscent);
 
4740
            }
 
4741
            lineHeight = wxMax(lineHeight, (lineDescent + lineAscent));
 
4742
            wxRect floatAvailableRect = collector->GetAvailableRect(rect.y + currentPosition.y, rect.y + currentPosition.y + lineHeight);
 
4743
 
 
4744
            // Adjust availableRect to the space that is available when taking floating objects into account.
 
4745
 
 
4746
            if (floatAvailableRect.x + startOffset > availableRect.x)
 
4747
            {
 
4748
                int newX = floatAvailableRect.x + startOffset;
 
4749
                int newW = availableRect.width - (newX - availableRect.x);
 
4750
                availableRect.x = newX;
 
4751
                availableRect.width = newW;
 
4752
            }
 
4753
 
 
4754
            if (floatAvailableRect.width < availableRect.width)
 
4755
                availableRect.width = floatAvailableRect.width;
 
4756
 
 
4757
            currentPosition.x = availableRect.x - rect.x;
 
4758
 
 
4759
            if (child->IsTopLevel() && loopIterations <= 20)
 
4760
            {
 
4761
                if (availableRect != oldAvailableRect)
 
4762
                {
 
4763
                    wxSize oldSize = child->GetCachedSize();
 
4764
 
 
4765
                    // Lays out the object first with a given amount of space, and then if no width was specified in attr,
 
4766
                    // lays out the object again using the minimum size
 
4767
                    child->Invalidate(wxRICHTEXT_ALL);
 
4768
                    child->LayoutToBestSize(dc, context, buffer,
 
4769
                                attr, child->GetAttributes(), availableRect, parentRect, style);
 
4770
                    childSize = child->GetCachedSize();
 
4771
                    childDescent = child->GetDescent();
 
4772
 
 
4773
                    if (oldSize != child->GetCachedSize())
 
4774
                    {
 
4775
                        partialExtents.Clear();
 
4776
 
 
4777
                        // Recalculate the partial text extents since the child object changed size
 
4778
                        GetRangeSize(GetRange(), paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_CACHE_SIZE, wxPoint(0,0), & partialExtents);
 
4779
                    }
 
4780
 
 
4781
                    // Go around the loop finding the available rect for the given floating objects
 
4782
                }
 
4783
                else
 
4784
                    doLoop = false;
 
4785
            }
 
4786
            else
 
4787
                doLoop = false;
 
4788
        }
 
4789
        while (doLoop);
 
4790
 
 
4791
        if (child->IsTopLevel())
 
4792
        {
 
4793
            // We can move it to the correct position at this point
 
4794
            // TODO: probably need to add margin
 
4795
            child->Move(GetPosition() + wxPoint(currentWidth + (wxMax(leftIndent, leftIndent + leftSubIndent)), currentPosition.y));
 
4796
        }
 
4797
 
 
4798
        // Cases:
 
4799
        // 1) There was a line break BEFORE the natural break
 
4800
        // 2) There was a line break AFTER the natural break
 
4801
        // 3) It's the last line
 
4802
        // 4) The child still fits (carry on) - 'else' clause
 
4803
 
 
4804
        if ((lineBreakInThisObject && (childSize.x + currentWidth <= availableRect.width))
 
4805
            ||
 
4806
            (childSize.x + currentWidth > availableRect.width)
 
4807
            ||
 
4808
            ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext())
 
4809
 
 
4810
            )
 
4811
        {
 
4812
            long wrapPosition = 0;
 
4813
            if ((childSize.x + currentWidth <= availableRect.width) && !node->GetNext() && !lineBreakInThisObject)
 
4814
                wrapPosition = child->GetRange().GetEnd();
 
4815
            else
 
4816
 
 
4817
            // Find a place to wrap. This may walk back to previous children,
 
4818
            // for example if a word spans several objects.
 
4819
            // Note: one object must contains only one wxTextAtrr, so the line height will not
 
4820
            //       change inside one object. Thus, we can pass the remain line width to the
 
4821
            //       FindWrapPosition function.
 
4822
            if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, context, availableRect.width, wrapPosition, & partialExtents))
 
4823
            {
 
4824
                // If the function failed, just cut it off at the end of this child.
 
4825
                wrapPosition = child->GetRange().GetEnd();
 
4826
            }
 
4827
 
 
4828
            // FindWrapPosition can still return a value that will put us in an endless wrapping loop
 
4829
            if (wrapPosition <= lastCompletedEndPos)
 
4830
                wrapPosition = wxMax(lastCompletedEndPos+1,child->GetRange().GetEnd());
 
4831
 
 
4832
            // Line end position shouldn't be the same as the end, or greater.
 
4833
            if (wrapPosition >= GetRange().GetEnd())
 
4834
                wrapPosition = wxMax(0, GetRange().GetEnd()-1);
 
4835
 
 
4836
            // wxLogDebug(wxT("Split at %ld"), wrapPosition);
 
4837
 
 
4838
            // Let's find the actual size of the current line now
 
4839
            wxSize actualSize;
 
4840
            wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
 
4841
 
 
4842
            childDescent = 0;
 
4843
 
 
4844
#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
 
4845
            if (!child->IsEmpty())
 
4846
            {
 
4847
                // Get height only, then the width using the partial extents
 
4848
                GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED|wxRICHTEXT_HEIGHT_ONLY);
 
4849
                actualSize.x = wxRichTextGetRangeWidth(*this, actualRange, partialExtents);
 
4850
            }
 
4851
            else
 
4852
#endif
 
4853
                GetRangeSize(actualRange, actualSize, childDescent, dc, context, wxRICHTEXT_UNFORMATTED);
 
4854
 
 
4855
            currentWidth = actualSize.x;
 
4856
 
 
4857
            // The descent for the whole line at this point, is the correct max descent
 
4858
            maxDescent = childDescent;
 
4859
            // Maximum ascent
 
4860
            maxAscent = actualSize.y-childDescent;
 
4861
 
 
4862
            // lineHeight is given by the height for the whole line, since it will
 
4863
            // take into account ascend/descend.
 
4864
            lineHeight = actualSize.y;
 
4865
 
 
4866
            if (lineHeight == 0 && buffer)
 
4867
            {
 
4868
                wxFont font(buffer->GetFontTable().FindFont(attr));
 
4869
                wxCheckSetFont(dc, font);
 
4870
                lineHeight = dc.GetCharHeight();
 
4871
            }
 
4872
 
 
4873
            if (maxDescent == 0)
 
4874
            {
 
4875
                int w, h;
 
4876
                dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
 
4877
            }
 
4878
 
 
4879
            // Add a new line
 
4880
            wxRichTextLine* line = AllocateLine(lineCount);
 
4881
 
 
4882
            // Set relative range so we won't have to change line ranges when paragraphs are moved
 
4883
            line->SetRange(wxRichTextRange(actualRange.GetStart() - GetRange().GetStart(), actualRange.GetEnd() - GetRange().GetStart()));
 
4884
            line->SetPosition(currentPosition);
 
4885
            line->SetSize(wxSize(currentWidth, lineHeight));
 
4886
            line->SetDescent(maxDescent);
 
4887
 
 
4888
            maxHeight = currentPosition.y + lineHeight;
 
4889
 
 
4890
            // Now move down a line. TODO: add margins, spacing
 
4891
            currentPosition.y += lineHeight;
 
4892
            currentPosition.y += lineSpacing;
 
4893
            maxDescent = 0;
 
4894
            maxAscent = 0;
 
4895
            maxWidth = wxMax(maxWidth, currentWidth+startOffset);
 
4896
            currentWidth = 0;
 
4897
 
 
4898
            lineCount ++;
 
4899
 
 
4900
            // TODO: account for zero-length objects
 
4901
            // wxASSERT(wrapPosition > lastCompletedEndPos);
 
4902
 
 
4903
            lastEndPos = wrapPosition;
 
4904
            lastCompletedEndPos = lastEndPos;
 
4905
 
 
4906
            lineHeight = 0;
 
4907
 
 
4908
            if (wrapPosition < GetRange().GetEnd()-1)
 
4909
            {
 
4910
                // May need to set the node back to a previous one, due to searching back in wrapping
 
4911
                wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition(wrapPosition+1);
 
4912
                if (childAfterWrapPosition)
 
4913
                    node = m_children.Find(childAfterWrapPosition);
 
4914
                else
 
4915
                    node = node->GetNext();
 
4916
            }
 
4917
            else
 
4918
                node = node->GetNext();
 
4919
 
 
4920
            // Apply paragraph styles such as alignment to the wrapped line
 
4921
            ApplyParagraphStyle(line, attr, availableRect, dc);
 
4922
        }
 
4923
        else
 
4924
        {
 
4925
            // We still fit, so don't add a line, and keep going
 
4926
            currentWidth += childSize.x;
 
4927
 
 
4928
            if (childDescent == 0)
 
4929
            {
 
4930
                // An object with a zero descend value wants to take up the whole
 
4931
                // height regardless of baseline
 
4932
                lineHeight = wxMax(lineHeight, childSize.y);
 
4933
            }
 
4934
            else
 
4935
            {
 
4936
                maxDescent = wxMax(childDescent, maxDescent);
 
4937
                maxAscent = wxMax(childSize.y-childDescent, maxAscent);
 
4938
            }
 
4939
 
 
4940
            lineHeight = wxMax(lineHeight, (maxDescent + maxAscent));
 
4941
 
 
4942
            maxWidth = wxMax(maxWidth, currentWidth+startOffset);
 
4943
            lastEndPos = child->GetRange().GetEnd();
 
4944
 
 
4945
            node = node->GetNext();
 
4946
        }
 
4947
    }
 
4948
 
 
4949
    //wxASSERT(!(lastCompletedEndPos != -1 && lastCompletedEndPos < GetRange().GetEnd()-1));
 
4950
 
 
4951
    // Remove remaining unused line objects, if any
 
4952
    ClearUnusedLines(lineCount);
 
4953
 
 
4954
    // We need to add back the margins etc.
 
4955
    {
 
4956
        wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
 
4957
        contentRect = wxRect(wxPoint(0, 0), wxSize(maxWidth, currentPosition.y + spaceAfterPara));
 
4958
        GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
 
4959
        SetCachedSize(marginRect.GetSize());
 
4960
    }
 
4961
 
 
4962
    // The maximum size is the length of the paragraph stretched out into a line.
 
4963
    // So if there were a single word, or an image, or a fixed-size text box, the object could be shrunk around
 
4964
    // this size. TODO: take into account line breaks.
 
4965
    {
 
4966
        wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
 
4967
        contentRect = wxRect(wxPoint(0, 0), wxSize(paraSize.x + wxMax(leftIndent, leftIndent + leftSubIndent) + rightIndent, currentPosition.y + spaceAfterPara));
 
4968
        GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
 
4969
        SetMaxSize(marginRect.GetSize());
 
4970
    }
 
4971
 
 
4972
    // Find the greatest minimum size. Currently we only look at non-text objects,
 
4973
    // which isn't ideal but it would be slow to find the maximum word width to
 
4974
    // use as the minimum.
 
4975
    {
 
4976
        int minWidth = 0;
 
4977
        node = m_children.GetFirst();
 
4978
        while (node)
 
4979
        {
 
4980
            wxRichTextObject* child = node->GetData();
 
4981
 
 
4982
            // If floating, ignore. We already laid out floats.
 
4983
            // Also ignore if empty object, except if we haven't got any
 
4984
            // size yet.
 
4985
            if (!child->IsFloating() && child->GetRange().GetLength() != 0 && !wxDynamicCast(child, wxRichTextPlainText))
 
4986
            {
 
4987
                if (child->GetCachedSize().x > minWidth)
 
4988
                    minWidth = child->GetMinSize().x;
 
4989
            }
 
4990
            node = node->GetNext();
 
4991
        }
 
4992
 
 
4993
        wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
 
4994
        contentRect = wxRect(wxPoint(0, 0), wxSize(minWidth, currentPosition.y + spaceAfterPara));
 
4995
        GetBoxRects(dc, buffer, attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
 
4996
        SetMinSize(marginRect.GetSize());
 
4997
    }
 
4998
 
 
4999
#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
 
5000
#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
 
5001
    // Use the text extents to calculate the size of each fragment in each line
 
5002
    wxRichTextLineList::compatibility_iterator lineNode = m_cachedLines.GetFirst();
 
5003
    while (lineNode)
 
5004
    {
 
5005
        wxRichTextLine* line = lineNode->GetData();
 
5006
        wxRichTextRange lineRange = line->GetAbsoluteRange();
 
5007
 
 
5008
        // Loop through objects until we get to the one within range
 
5009
        wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
 
5010
 
 
5011
        while (node2)
 
5012
        {
 
5013
            wxRichTextObject* child = node2->GetData();
 
5014
 
 
5015
            if (child->GetRange().GetLength() > 0 && !child->GetRange().IsOutside(lineRange))
 
5016
            {
 
5017
                wxRichTextRange rangeToUse = lineRange;
 
5018
                rangeToUse.LimitTo(child->GetRange());
 
5019
 
 
5020
                // Find the size of the child from the text extents, and store in an array
 
5021
                // for drawing later
 
5022
                int left = 0;
 
5023
                if (rangeToUse.GetStart() > GetRange().GetStart())
 
5024
                    left = partialExtents[(rangeToUse.GetStart()-1) - GetRange().GetStart()];
 
5025
                int right = partialExtents[rangeToUse.GetEnd() - GetRange().GetStart()];
 
5026
                int sz = right - left;
 
5027
                line->GetObjectSizes().Add(sz);
 
5028
            }
 
5029
            else if (child->GetRange().GetStart() > lineRange.GetEnd())
 
5030
                // Can break out of inner loop now since we've passed this line's range
 
5031
                break;
 
5032
 
 
5033
            node2 = node2->GetNext();
 
5034
        }
 
5035
 
 
5036
        lineNode = lineNode->GetNext();
 
5037
    }
 
5038
#endif
 
5039
#endif
 
5040
 
 
5041
    return true;
 
5042
}
 
5043
 
 
5044
/// Apply paragraph styles, such as centering, to wrapped lines
 
5045
/// TODO: take into account box attributes, possibly
 
5046
void wxRichTextParagraph::ApplyParagraphStyle(wxRichTextLine* line, const wxRichTextAttr& attr, const wxRect& rect, wxDC& dc)
 
5047
{
 
5048
    if (!attr.HasAlignment())
 
5049
        return;
 
5050
 
 
5051
    wxPoint pos = line->GetPosition();
 
5052
    wxPoint originalPos = pos;
 
5053
    wxSize size = line->GetSize();
 
5054
 
 
5055
    // centering, right-justification
 
5056
    if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
 
5057
    {
 
5058
        int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
 
5059
        pos.x = (rect.GetWidth() - rightIndent - size.x)/2 + pos.x;
 
5060
        line->SetPosition(pos);
 
5061
    }
 
5062
    else if (attr.HasAlignment() && attr.GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
 
5063
    {
 
5064
        int rightIndent = ConvertTenthsMMToPixels(dc, attr.GetRightIndent());
 
5065
        pos.x = pos.x + rect.GetWidth() - size.x - rightIndent;
 
5066
        line->SetPosition(pos);
 
5067
    }
 
5068
 
 
5069
    if (pos != originalPos)
 
5070
    {
 
5071
        wxPoint inc = pos - originalPos;
 
5072
 
 
5073
        wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
5074
 
 
5075
        while (node)
 
5076
        {
 
5077
            wxRichTextObject* child = node->GetData();
 
5078
            if (child->IsTopLevel() && !child->GetRange().IsOutside(line->GetAbsoluteRange()))
 
5079
                child->Move(child->GetPosition() + inc);
 
5080
 
 
5081
            node = node->GetNext();
 
5082
        }
 
5083
    }
 
5084
}
 
5085
 
 
5086
/// Insert text at the given position
 
5087
bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
 
5088
{
 
5089
    wxRichTextObject* childToUse = NULL;
 
5090
    wxRichTextObjectList::compatibility_iterator nodeToUse = wxRichTextObjectList::compatibility_iterator();
 
5091
 
 
5092
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
5093
    while (node)
 
5094
    {
 
5095
        wxRichTextObject* child = node->GetData();
 
5096
        if (child->GetRange().Contains(pos) && child->GetRange().GetLength() > 0)
 
5097
        {
 
5098
            childToUse = child;
 
5099
            nodeToUse = node;
 
5100
            break;
 
5101
        }
 
5102
 
 
5103
        node = node->GetNext();
 
5104
    }
 
5105
 
 
5106
    if (childToUse)
 
5107
    {
 
5108
        wxRichTextPlainText* textObject = wxDynamicCast(childToUse, wxRichTextPlainText);
 
5109
        if (textObject)
 
5110
        {
 
5111
            int posInString = pos - textObject->GetRange().GetStart();
 
5112
 
 
5113
            wxString newText = textObject->GetText().Mid(0, posInString) +
 
5114
                               text + textObject->GetText().Mid(posInString);
 
5115
            textObject->SetText(newText);
 
5116
 
 
5117
            int textLength = text.length();
 
5118
 
 
5119
            textObject->SetRange(wxRichTextRange(textObject->GetRange().GetStart(),
 
5120
                                                 textObject->GetRange().GetEnd() + textLength));
 
5121
 
 
5122
            // Increment the end range of subsequent fragments in this paragraph.
 
5123
            // We'll set the paragraph range itself at a higher level.
 
5124
 
 
5125
            wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
 
5126
            while (node)
 
5127
            {
 
5128
                wxRichTextObject* child = node->GetData();
 
5129
                child->SetRange(wxRichTextRange(textObject->GetRange().GetStart() + textLength,
 
5130
                                                 textObject->GetRange().GetEnd() + textLength));
 
5131
 
 
5132
                node = node->GetNext();
 
5133
            }
 
5134
 
 
5135
            return true;
 
5136
        }
 
5137
        else
 
5138
        {
 
5139
            // TODO: if not a text object, insert at closest position, e.g. in front of it
 
5140
        }
 
5141
    }
 
5142
    else
 
5143
    {
 
5144
        // Add at end.
 
5145
        // Don't pass parent initially to suppress auto-setting of parent range.
 
5146
        // We'll do that at a higher level.
 
5147
        wxRichTextPlainText* textObject = new wxRichTextPlainText(text, this);
 
5148
 
 
5149
        AppendChild(textObject);
 
5150
        return true;
 
5151
    }
 
5152
 
 
5153
    return false;
 
5154
}
 
5155
 
 
5156
void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
 
5157
{
 
5158
    wxRichTextCompositeObject::Copy(obj);
 
5159
}
 
5160
 
 
5161
/// Clear the cached lines
 
5162
void wxRichTextParagraph::ClearLines()
 
5163
{
 
5164
    WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
 
5165
}
 
5166
 
 
5167
/// Get/set the object size for the given range. Returns false if the range
 
5168
/// is invalid for this object.
 
5169
bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
 
5170
{
 
5171
    if (!range.IsWithin(GetRange()))
 
5172
        return false;
 
5173
 
 
5174
    if (flags & wxRICHTEXT_UNFORMATTED)
 
5175
    {
 
5176
        // Just use unformatted data, assume no line breaks
 
5177
        wxSize sz;
 
5178
 
 
5179
        wxArrayInt childExtents;
 
5180
        wxArrayInt* p;
 
5181
        if (partialExtents)
 
5182
            p = & childExtents;
 
5183
        else
 
5184
            p = NULL;
 
5185
 
 
5186
        int maxDescent = 0;
 
5187
        int maxAscent = 0;
 
5188
        int maxLineHeight = 0;
 
5189
 
 
5190
        wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
5191
        while (node)
 
5192
        {
 
5193
            wxRichTextObject* child = node->GetData();
 
5194
            if (!child->GetRange().IsOutside(range))
 
5195
            {
 
5196
                // Floating objects have a zero size within the paragraph.
 
5197
                if (child->IsFloating())
 
5198
                {
 
5199
                    if (partialExtents)
 
5200
                    {
 
5201
                        int lastSize;
 
5202
                        if (partialExtents->GetCount() > 0)
 
5203
                            lastSize = (*partialExtents)[partialExtents->GetCount()-1];
 
5204
                        else
 
5205
                            lastSize = 0;
 
5206
 
 
5207
                        partialExtents->Add(0 /* zero size */ + lastSize);
 
5208
                    }
 
5209
                }
 
5210
                else
 
5211
                {
 
5212
                    wxSize childSize;
 
5213
 
 
5214
                    wxRichTextRange rangeToUse = range;
 
5215
                    rangeToUse.LimitTo(child->GetRange());
 
5216
                    int childDescent = 0;
 
5217
 
 
5218
                    // At present wxRICHTEXT_HEIGHT_ONLY is only fast if we've already cached the size,
 
5219
                    // but it's only going to be used after caching has taken place.
 
5220
                    if ((flags & wxRICHTEXT_HEIGHT_ONLY) && child->GetCachedSize().y != 0)
 
5221
                    {
 
5222
                        childDescent = child->GetDescent();
 
5223
                        childSize = child->GetCachedSize();
 
5224
 
 
5225
                        if (childDescent == 0)
 
5226
                        {
 
5227
                            maxLineHeight = wxMax(maxLineHeight, childSize.y);
 
5228
                        }
 
5229
                        else
 
5230
                        {
 
5231
                            maxDescent = wxMax(maxDescent, childDescent);
 
5232
                            maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
 
5233
                        }
 
5234
 
 
5235
                        maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
 
5236
 
 
5237
                        sz.y = wxMax(sz.y, maxLineHeight);
 
5238
                        sz.x += childSize.x;
 
5239
                        descent = maxDescent;
 
5240
                    }
 
5241
                    else if (child->IsTopLevel())
 
5242
                    {
 
5243
                        childDescent = child->GetDescent();
 
5244
                        childSize = child->GetCachedSize();
 
5245
 
 
5246
                        if (childDescent == 0)
 
5247
                        {
 
5248
                            maxLineHeight = wxMax(maxLineHeight, childSize.y);
 
5249
                        }
 
5250
                        else
 
5251
                        {
 
5252
                            maxDescent = wxMax(maxDescent, childDescent);
 
5253
                            maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
 
5254
                        }
 
5255
 
 
5256
                        maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
 
5257
 
 
5258
                        sz.y = wxMax(sz.y, maxLineHeight);
 
5259
                        sz.x += childSize.x;
 
5260
                        descent = maxDescent;
 
5261
 
 
5262
                        // FIXME: this won't change the original values.
 
5263
                        // Should we be calling GetRangeSize above instead of using cached values?
 
5264
#if 0
 
5265
                        if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
 
5266
                        {
 
5267
                            child->SetCachedSize(childSize);
 
5268
                            child->SetDescent(childDescent);
 
5269
                        }
 
5270
#endif
 
5271
 
 
5272
                        if (partialExtents)
 
5273
                        {
 
5274
                            int lastSize;
 
5275
                            if (partialExtents->GetCount() > 0)
 
5276
                                lastSize = (*partialExtents)[partialExtents->GetCount()-1];
 
5277
                            else
 
5278
                                lastSize = 0;
 
5279
 
 
5280
                            partialExtents->Add(childSize.x + lastSize);
 
5281
                        }
 
5282
                    }
 
5283
                    else if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y), p))
 
5284
                    {
 
5285
                        if (childDescent == 0)
 
5286
                        {
 
5287
                            maxLineHeight = wxMax(maxLineHeight, childSize.y);
 
5288
                        }
 
5289
                        else
 
5290
                        {
 
5291
                            maxDescent = wxMax(maxDescent, childDescent);
 
5292
                            maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
 
5293
                        }
 
5294
 
 
5295
                        maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
 
5296
 
 
5297
                        sz.y = wxMax(sz.y, maxLineHeight);
 
5298
                        sz.x += childSize.x;
 
5299
                        descent = maxDescent;
 
5300
 
 
5301
                        if ((flags & wxRICHTEXT_CACHE_SIZE) && (rangeToUse == child->GetRange()))
 
5302
                        {
 
5303
                            child->SetCachedSize(childSize);
 
5304
                            child->SetDescent(childDescent);
 
5305
                        }
 
5306
 
 
5307
                        if (partialExtents)
 
5308
                        {
 
5309
                            int lastSize;
 
5310
                            if (partialExtents->GetCount() > 0)
 
5311
                                lastSize = (*partialExtents)[partialExtents->GetCount()-1];
 
5312
                            else
 
5313
                                lastSize = 0;
 
5314
 
 
5315
                            size_t i;
 
5316
                            for (i = 0; i < childExtents.GetCount(); i++)
 
5317
                            {
 
5318
                                partialExtents->Add(childExtents[i] + lastSize);
 
5319
                            }
 
5320
                        }
 
5321
                    }
 
5322
                }
 
5323
 
 
5324
                if (p)
 
5325
                    p->Clear();
 
5326
            }
 
5327
 
 
5328
            node = node->GetNext();
 
5329
        }
 
5330
        size = sz;
 
5331
    }
 
5332
    else
 
5333
    {
 
5334
        // Use formatted data, with line breaks
 
5335
        wxSize sz;
 
5336
 
 
5337
        // We're going to loop through each line, and then for each line,
 
5338
        // call GetRangeSize for the fragment that comprises that line.
 
5339
        // Only we have to do that multiple times within the line, because
 
5340
        // the line may be broken into pieces. For now ignore line break commands
 
5341
        // (so we can assume that getting the unformatted size for a fragment
 
5342
        // within a line is the actual size)
 
5343
 
 
5344
        wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
 
5345
        while (node)
 
5346
        {
 
5347
            wxRichTextLine* line = node->GetData();
 
5348
            wxRichTextRange lineRange = line->GetAbsoluteRange();
 
5349
            if (!lineRange.IsOutside(range))
 
5350
            {
 
5351
                int maxDescent = 0;
 
5352
                int maxAscent = 0;
 
5353
                int maxLineHeight = 0;
 
5354
                int maxLineWidth = 0;
 
5355
 
 
5356
                wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
 
5357
                while (node2)
 
5358
                {
 
5359
                    wxRichTextObject* child = node2->GetData();
 
5360
 
 
5361
                    if (!child->IsFloating() && !child->GetRange().IsOutside(lineRange))
 
5362
                    {
 
5363
                        wxRichTextRange rangeToUse = lineRange;
 
5364
                        rangeToUse.LimitTo(child->GetRange());
 
5365
                        if (child->IsTopLevel())
 
5366
                            rangeToUse = child->GetOwnRange();
 
5367
 
 
5368
                        wxSize childSize;
 
5369
                        int childDescent = 0;
 
5370
                        if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, context, flags, wxPoint(position.x + sz.x, position.y)))
 
5371
                        {
 
5372
                            if (childDescent == 0)
 
5373
                            {
 
5374
                                // Assume that if descent is zero, this child can occupy the full line height
 
5375
                                // and does not need space for the line's maximum descent. So we influence
 
5376
                                // the overall max line height only.
 
5377
                                maxLineHeight = wxMax(maxLineHeight, childSize.y);
 
5378
                            }
 
5379
                            else
 
5380
                            {
 
5381
                                maxAscent = wxMax(maxAscent, (childSize.y - childDescent));
 
5382
                                maxDescent = wxMax(maxAscent, childDescent);
 
5383
                            }
 
5384
                            maxLineHeight = wxMax(maxLineHeight, (maxAscent + maxDescent));
 
5385
                            maxLineWidth += childSize.x;
 
5386
                        }
 
5387
                    }
 
5388
 
 
5389
                    node2 = node2->GetNext();
 
5390
                }
 
5391
 
 
5392
                descent = wxMax(descent, maxDescent);
 
5393
 
 
5394
                // Increase size by a line (TODO: paragraph spacing)
 
5395
                sz.y += maxLineHeight;
 
5396
                sz.x = wxMax(sz.x, maxLineWidth);
 
5397
            }
 
5398
            node = node->GetNext();
 
5399
        }
 
5400
        size = sz;
 
5401
    }
 
5402
    return true;
 
5403
}
 
5404
 
 
5405
/// Finds the absolute position and row height for the given character position
 
5406
bool wxRichTextParagraph::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
 
5407
{
 
5408
    if (index == -1)
 
5409
    {
 
5410
        wxRichTextLine* line = ((wxRichTextParagraphLayoutBox*)GetParent())->GetLineAtPosition(0);
 
5411
        if (line)
 
5412
            *height = line->GetSize().y;
 
5413
        else
 
5414
            *height = dc.GetCharHeight();
 
5415
 
 
5416
        // -1 means 'the start of the buffer'.
 
5417
        pt = GetPosition();
 
5418
        if (line)
 
5419
            pt = pt + line->GetPosition();
 
5420
 
 
5421
        return true;
 
5422
    }
 
5423
 
 
5424
    // The final position in a paragraph is taken to mean the position
 
5425
    // at the start of the next paragraph.
 
5426
    if (index == GetRange().GetEnd())
 
5427
    {
 
5428
        wxRichTextParagraphLayoutBox* parent = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
 
5429
        wxASSERT( parent != NULL );
 
5430
 
 
5431
        // Find the height at the next paragraph, if any
 
5432
        wxRichTextLine* line = parent->GetLineAtPosition(index + 1);
 
5433
        if (line)
 
5434
        {
 
5435
            *height = line->GetSize().y;
 
5436
            pt = line->GetAbsolutePosition();
 
5437
        }
 
5438
        else
 
5439
        {
 
5440
            *height = dc.GetCharHeight();
 
5441
            int indent = ConvertTenthsMMToPixels(dc, m_attributes.GetLeftIndent());
 
5442
            pt = wxPoint(indent, GetCachedSize().y);
 
5443
        }
 
5444
 
 
5445
        return true;
 
5446
    }
 
5447
 
 
5448
    if (index < GetRange().GetStart() || index > GetRange().GetEnd())
 
5449
        return false;
 
5450
 
 
5451
    wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
 
5452
    while (node)
 
5453
    {
 
5454
        wxRichTextLine* line = node->GetData();
 
5455
        wxRichTextRange lineRange = line->GetAbsoluteRange();
 
5456
        if (index >= lineRange.GetStart() && index <= lineRange.GetEnd())
 
5457
        {
 
5458
            // If this is the last point in the line, and we're forcing the
 
5459
            // returned value to be the start of the next line, do the required
 
5460
            // thing.
 
5461
            if (index == lineRange.GetEnd() && forceLineStart)
 
5462
            {
 
5463
                if (node->GetNext())
 
5464
                {
 
5465
                    wxRichTextLine* nextLine = node->GetNext()->GetData();
 
5466
                    *height = nextLine->GetSize().y;
 
5467
                    pt = nextLine->GetAbsolutePosition();
 
5468
                    return true;
 
5469
                }
 
5470
            }
 
5471
 
 
5472
            pt.y = line->GetPosition().y + GetPosition().y;
 
5473
 
 
5474
            wxRichTextRange r(lineRange.GetStart(), index);
 
5475
            wxSize rangeSize;
 
5476
            int descent = 0;
 
5477
 
 
5478
            // We find the size of the line up to this point,
 
5479
            // then we can add this size to the line start position and
 
5480
            // paragraph start position to find the actual position.
 
5481
 
 
5482
            if (GetRangeSize(r, rangeSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, line->GetPosition()+ GetPosition()))
 
5483
            {
 
5484
                pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
 
5485
                *height = line->GetSize().y;
 
5486
 
 
5487
                return true;
 
5488
            }
 
5489
 
 
5490
        }
 
5491
 
 
5492
        node = node->GetNext();
 
5493
    }
 
5494
 
 
5495
    return false;
 
5496
}
 
5497
 
 
5498
/// Hit-testing: returns a flag indicating hit test details, plus
 
5499
/// information about position
 
5500
int wxRichTextParagraph::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
 
5501
{
 
5502
    if (!IsShown())
 
5503
        return wxRICHTEXT_HITTEST_NONE;
 
5504
 
 
5505
    // If we're in the top-level container, then we can return
 
5506
    // a suitable hit test code even if the point is outside the container area,
 
5507
    // so that we can position the caret sensibly even if we don't
 
5508
    // click on valid content. If we're not at the top-level, and the point
 
5509
    // is not within this paragraph object, then we don't want to stop more
 
5510
    // precise hit-testing from working prematurely, so return immediately.
 
5511
    // NEW STRATEGY: use the parent boundary to test whether we're in the
 
5512
    // right region, not the paragraph, since the paragraph may be positioned
 
5513
    // some way in from where the user clicks.
 
5514
    {
 
5515
        long tmpPos;
 
5516
        wxRichTextObject* tempObj, *tempContextObj;
 
5517
        if (GetParent() && GetParent()->wxRichTextObject::HitTest(dc, context, pt, tmpPos, & tempObj, & tempContextObj, flags) == wxRICHTEXT_HITTEST_NONE)
 
5518
            return wxRICHTEXT_HITTEST_NONE;
 
5519
    }
 
5520
 
 
5521
    wxRichTextObjectList::compatibility_iterator objNode = m_children.GetFirst();
 
5522
    while (objNode)
 
5523
    {
 
5524
        wxRichTextObject* child = objNode->GetData();
 
5525
        // Don't recurse if we have wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS,
 
5526
        // and also, if this seems composite but actually is marked as atomic,
 
5527
        // don't recurse.
 
5528
        if (child->IsTopLevel() && ((flags & wxRICHTEXT_HITTEST_NO_NESTED_OBJECTS) == 0) &&
 
5529
            (! (((flags & wxRICHTEXT_HITTEST_HONOUR_ATOMIC) != 0) && child->IsAtomic())))
 
5530
        {
 
5531
            {
 
5532
                int hitTest = child->HitTest(dc, context, pt, textPosition, obj, contextObj);
 
5533
                if (hitTest != wxRICHTEXT_HITTEST_NONE)
 
5534
                    return hitTest;
 
5535
            }
 
5536
        }
 
5537
 
 
5538
        objNode = objNode->GetNext();
 
5539
    }
 
5540
 
 
5541
    wxPoint paraPos = GetPosition();
 
5542
 
 
5543
    wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
 
5544
    while (node)
 
5545
    {
 
5546
        wxRichTextLine* line = node->GetData();
 
5547
        wxPoint linePos = paraPos + line->GetPosition();
 
5548
        wxSize lineSize = line->GetSize();
 
5549
        wxRichTextRange lineRange = line->GetAbsoluteRange();
 
5550
 
 
5551
        if (pt.y <= linePos.y + lineSize.y)
 
5552
        {
 
5553
            if (pt.x < linePos.x)
 
5554
            {
 
5555
                textPosition = lineRange.GetStart();
 
5556
                *obj = FindObjectAtPosition(textPosition);
 
5557
                *contextObj = GetContainer();
 
5558
                return wxRICHTEXT_HITTEST_BEFORE|wxRICHTEXT_HITTEST_OUTSIDE;
 
5559
            }
 
5560
            else if (pt.x >= (linePos.x + lineSize.x))
 
5561
            {
 
5562
                textPosition = lineRange.GetEnd();
 
5563
                *obj = FindObjectAtPosition(textPosition);
 
5564
                *contextObj = GetContainer();
 
5565
                return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
 
5566
            }
 
5567
            else
 
5568
            {
 
5569
#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
 
5570
                wxArrayInt partialExtents;
 
5571
 
 
5572
                wxSize paraSize;
 
5573
                int paraDescent;
 
5574
 
 
5575
                // This calculates the partial text extents
 
5576
                GetRangeSize(lineRange, paraSize, paraDescent, dc, context, wxRICHTEXT_UNFORMATTED, linePos, & partialExtents);
 
5577
 
 
5578
                int lastX = linePos.x;
 
5579
                size_t i;
 
5580
                for (i = 0; i < partialExtents.GetCount(); i++)
 
5581
                {
 
5582
                    int nextX = partialExtents[i] + linePos.x;
 
5583
 
 
5584
                    if (pt.x >= lastX && pt.x <= nextX)
 
5585
                    {
 
5586
                        textPosition = i + lineRange.GetStart(); // minus 1?
 
5587
 
 
5588
                        *obj = FindObjectAtPosition(textPosition);
 
5589
                        *contextObj = GetContainer();
 
5590
 
 
5591
                        // So now we know it's between i-1 and i.
 
5592
                        // Let's see if we can be more precise about
 
5593
                        // which side of the position it's on.
 
5594
 
 
5595
                        int midPoint = (nextX + lastX)/2;
 
5596
                        if (pt.x >= midPoint)
 
5597
                            return wxRICHTEXT_HITTEST_AFTER;
 
5598
                        else
 
5599
                            return wxRICHTEXT_HITTEST_BEFORE;
 
5600
                    }
 
5601
 
 
5602
                    lastX = nextX;
 
5603
                }
 
5604
#else
 
5605
                long i;
 
5606
                int lastX = linePos.x;
 
5607
                for (i = lineRange.GetStart(); i <= lineRange.GetEnd(); i++)
 
5608
                {
 
5609
                    wxSize childSize;
 
5610
                    int descent = 0;
 
5611
 
 
5612
                    wxRichTextRange rangeToUse(lineRange.GetStart(), i);
 
5613
 
 
5614
                    GetRangeSize(rangeToUse, childSize, descent, dc, context, wxRICHTEXT_UNFORMATTED, linePos);
 
5615
 
 
5616
                    int nextX = childSize.x + linePos.x;
 
5617
 
 
5618
                    if (pt.x >= lastX && pt.x <= nextX)
 
5619
                    {
 
5620
                        textPosition = i;
 
5621
 
 
5622
                        *obj = FindObjectAtPosition(textPosition);
 
5623
                        *contextObj = GetContainer();
 
5624
 
 
5625
                        // So now we know it's between i-1 and i.
 
5626
                        // Let's see if we can be more precise about
 
5627
                        // which side of the position it's on.
 
5628
 
 
5629
                        int midPoint = (nextX + lastX)/2;
 
5630
                        if (pt.x >= midPoint)
 
5631
                            return wxRICHTEXT_HITTEST_AFTER;
 
5632
                        else
 
5633
                            return wxRICHTEXT_HITTEST_BEFORE;
 
5634
                    }
 
5635
                    else
 
5636
                    {
 
5637
                        lastX = nextX;
 
5638
                    }
 
5639
                }
 
5640
#endif
 
5641
            }
 
5642
        }
 
5643
 
 
5644
        node = node->GetNext();
 
5645
    }
 
5646
 
 
5647
    return wxRICHTEXT_HITTEST_NONE;
 
5648
}
 
5649
 
 
5650
/// Split an object at this position if necessary, and return
 
5651
/// the previous object, or NULL if inserting at beginning.
 
5652
wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** previousObject)
 
5653
{
 
5654
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
5655
    while (node)
 
5656
    {
 
5657
        wxRichTextObject* child = node->GetData();
 
5658
 
 
5659
        if (pos == child->GetRange().GetStart())
 
5660
        {
 
5661
            if (previousObject)
 
5662
            {
 
5663
                if (node->GetPrevious())
 
5664
                    *previousObject = node->GetPrevious()->GetData();
 
5665
                else
 
5666
                    *previousObject = NULL;
 
5667
            }
 
5668
 
 
5669
            return child;
 
5670
        }
 
5671
 
 
5672
        if (child->GetRange().Contains(pos))
 
5673
        {
 
5674
            // This should create a new object, transferring part of
 
5675
            // the content to the old object and the rest to the new object.
 
5676
            wxRichTextObject* newObject = child->DoSplit(pos);
 
5677
 
 
5678
            // If we couldn't split this object, just insert in front of it.
 
5679
            if (!newObject)
 
5680
            {
 
5681
                // Maybe this is an empty string, try the next one
 
5682
                // return child;
 
5683
            }
 
5684
            else
 
5685
            {
 
5686
                // Insert the new object after 'child'
 
5687
                if (node->GetNext())
 
5688
                    m_children.Insert(node->GetNext(), newObject);
 
5689
                else
 
5690
                    m_children.Append(newObject);
 
5691
                newObject->SetParent(this);
 
5692
 
 
5693
                if (previousObject)
 
5694
                    *previousObject = child;
 
5695
 
 
5696
                return newObject;
 
5697
            }
 
5698
        }
 
5699
 
 
5700
        node = node->GetNext();
 
5701
    }
 
5702
    if (previousObject)
 
5703
        *previousObject = NULL;
 
5704
    return NULL;
 
5705
}
 
5706
 
 
5707
/// Move content to a list from obj on
 
5708
void wxRichTextParagraph::MoveToList(wxRichTextObject* obj, wxList& list)
 
5709
{
 
5710
    wxRichTextObjectList::compatibility_iterator node = m_children.Find(obj);
 
5711
    while (node)
 
5712
    {
 
5713
        wxRichTextObject* child = node->GetData();
 
5714
        list.Append(child);
 
5715
 
 
5716
        wxRichTextObjectList::compatibility_iterator oldNode = node;
 
5717
 
 
5718
        node = node->GetNext();
 
5719
 
 
5720
        m_children.DeleteNode(oldNode);
 
5721
    }
 
5722
}
 
5723
 
 
5724
/// Add content back from list
 
5725
void wxRichTextParagraph::MoveFromList(wxList& list)
 
5726
{
 
5727
    for (wxList::compatibility_iterator node = list.GetFirst(); node; node = node->GetNext())
 
5728
    {
 
5729
        AppendChild((wxRichTextObject*) node->GetData());
 
5730
    }
 
5731
}
 
5732
 
 
5733
/// Calculate range
 
5734
void wxRichTextParagraph::CalculateRange(long start, long& end)
 
5735
{
 
5736
    wxRichTextCompositeObject::CalculateRange(start, end);
 
5737
 
 
5738
    // Add one for end of paragraph
 
5739
    end ++;
 
5740
 
 
5741
    m_range.SetRange(start, end);
 
5742
}
 
5743
 
 
5744
/// Find the object at the given position
 
5745
wxRichTextObject* wxRichTextParagraph::FindObjectAtPosition(long position)
 
5746
{
 
5747
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
5748
    while (node)
 
5749
    {
 
5750
        wxRichTextObject* obj = node->GetData();
 
5751
        if (obj->GetRange().Contains(position) ||
 
5752
            obj->GetRange().GetStart() == position ||
 
5753
            obj->GetRange().GetEnd() == position)
 
5754
            return obj;
 
5755
 
 
5756
        node = node->GetNext();
 
5757
    }
 
5758
    return NULL;
 
5759
}
 
5760
 
 
5761
/// Get the plain text searching from the start or end of the range.
 
5762
/// The resulting string may be shorter than the range given.
 
5763
bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTextRange& range, bool fromStart)
 
5764
{
 
5765
    text = wxEmptyString;
 
5766
 
 
5767
    if (fromStart)
 
5768
    {
 
5769
        wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
5770
        while (node)
 
5771
        {
 
5772
            wxRichTextObject* obj = node->GetData();
 
5773
            if (!obj->GetRange().IsOutside(range))
 
5774
            {
 
5775
                wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
 
5776
                if (textObj)
 
5777
                {
 
5778
                    text += textObj->GetTextForRange(range);
 
5779
                }
 
5780
                else
 
5781
                {
 
5782
                    text += wxT(" ");
 
5783
                }
 
5784
            }
 
5785
 
 
5786
            node = node->GetNext();
 
5787
        }
 
5788
    }
 
5789
    else
 
5790
    {
 
5791
        wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
 
5792
        while (node)
 
5793
        {
 
5794
            wxRichTextObject* obj = node->GetData();
 
5795
            if (!obj->GetRange().IsOutside(range))
 
5796
            {
 
5797
                wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
 
5798
                if (textObj)
 
5799
                {
 
5800
                    text = textObj->GetTextForRange(range) + text;
 
5801
                }
 
5802
                else
 
5803
                {
 
5804
                    text = wxT(" ") + text;
 
5805
                }
 
5806
            }
 
5807
 
 
5808
            node = node->GetPrevious();
 
5809
        }
 
5810
    }
 
5811
 
 
5812
    return true;
 
5813
}
 
5814
 
 
5815
/// Find a suitable wrap position.
 
5816
bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, wxRichTextDrawingContext& context, int availableSpace, long& wrapPosition, wxArrayInt* partialExtents)
 
5817
{
 
5818
    if (range.GetLength() <= 0)
 
5819
        return false;
 
5820
 
 
5821
    // Find the first position where the line exceeds the available space.
 
5822
    wxSize sz;
 
5823
    long breakPosition = range.GetEnd();
 
5824
 
 
5825
#if wxRICHTEXT_USE_PARTIAL_TEXT_EXTENTS
 
5826
    if (partialExtents && partialExtents->GetCount() >= (size_t) (GetRange().GetLength()-1)) // the final position in a paragraph is the newline
 
5827
    {
 
5828
        int widthBefore;
 
5829
 
 
5830
        if (range.GetStart() > GetRange().GetStart())
 
5831
            widthBefore = (*partialExtents)[range.GetStart() - GetRange().GetStart() - 1];
 
5832
        else
 
5833
            widthBefore = 0;
 
5834
 
 
5835
        size_t i;
 
5836
        for (i = (size_t) range.GetStart(); i <= (size_t) range.GetEnd(); i++)
 
5837
        {
 
5838
            int widthFromStartOfThisRange = (*partialExtents)[i - GetRange().GetStart()] - widthBefore;
 
5839
 
 
5840
            if (widthFromStartOfThisRange > availableSpace)
 
5841
            {
 
5842
                breakPosition = i-1;
 
5843
                break;
 
5844
            }
 
5845
        }
 
5846
    }
 
5847
    else
 
5848
#endif
 
5849
    {
 
5850
        // Binary chop for speed
 
5851
        long minPos = range.GetStart();
 
5852
        long maxPos = range.GetEnd();
 
5853
        while (true)
 
5854
        {
 
5855
            if (minPos == maxPos)
 
5856
            {
 
5857
                int descent = 0;
 
5858
                GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
 
5859
 
 
5860
                if (sz.x > availableSpace)
 
5861
                    breakPosition = minPos - 1;
 
5862
                break;
 
5863
            }
 
5864
            else if ((maxPos - minPos) == 1)
 
5865
            {
 
5866
                int descent = 0;
 
5867
                GetRangeSize(wxRichTextRange(range.GetStart(), minPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
 
5868
 
 
5869
                if (sz.x > availableSpace)
 
5870
                    breakPosition = minPos - 1;
 
5871
                else
 
5872
                {
 
5873
                    GetRangeSize(wxRichTextRange(range.GetStart(), maxPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
 
5874
                    if (sz.x > availableSpace)
 
5875
                        breakPosition = maxPos-1;
 
5876
                }
 
5877
                break;
 
5878
            }
 
5879
            else
 
5880
            {
 
5881
                long nextPos = minPos + ((maxPos - minPos) / 2);
 
5882
 
 
5883
                int descent = 0;
 
5884
                GetRangeSize(wxRichTextRange(range.GetStart(), nextPos), sz, descent, dc, context, wxRICHTEXT_UNFORMATTED);
 
5885
 
 
5886
                if (sz.x > availableSpace)
 
5887
                {
 
5888
                    maxPos = nextPos;
 
5889
                }
 
5890
                else
 
5891
                {
 
5892
                    minPos = nextPos;
 
5893
                }
 
5894
            }
 
5895
        }
 
5896
    }
 
5897
 
 
5898
    // Now we know the last position on the line.
 
5899
    // Let's try to find a word break.
 
5900
 
 
5901
    wxString plainText;
 
5902
    if (GetContiguousPlainText(plainText, wxRichTextRange(range.GetStart(), breakPosition), false))
 
5903
    {
 
5904
        int newLinePos = plainText.Find(wxRichTextLineBreakChar);
 
5905
        if (newLinePos != wxNOT_FOUND)
 
5906
        {
 
5907
            breakPosition = wxMax(0, range.GetStart() + newLinePos);
 
5908
        }
 
5909
        else
 
5910
        {
 
5911
            int spacePos = plainText.Find(wxT(' '), true);
 
5912
            int tabPos = plainText.Find(wxT('\t'), true);
 
5913
            int pos = wxMax(spacePos, tabPos);
 
5914
            if (pos != wxNOT_FOUND)
 
5915
            {
 
5916
                int positionsFromEndOfString = plainText.length() - pos - 1;
 
5917
                breakPosition = breakPosition - positionsFromEndOfString;
 
5918
            }
 
5919
        }
 
5920
    }
 
5921
 
 
5922
    wrapPosition = breakPosition;
 
5923
 
 
5924
    return true;
 
5925
}
 
5926
 
 
5927
/// Get the bullet text for this paragraph.
 
5928
wxString wxRichTextParagraph::GetBulletText()
 
5929
{
 
5930
    if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ||
 
5931
        (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP))
 
5932
        return wxEmptyString;
 
5933
 
 
5934
    int number = GetAttributes().GetBulletNumber();
 
5935
 
 
5936
    wxString text;
 
5937
    if ((GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC) || (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE))
 
5938
    {
 
5939
        text.Printf(wxT("%d"), number);
 
5940
    }
 
5941
    else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER)
 
5942
    {
 
5943
        // TODO: Unicode, and also check if number > 26
 
5944
        text.Printf(wxT("%c"), (wxChar) (number+64));
 
5945
    }
 
5946
    else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER)
 
5947
    {
 
5948
        // TODO: Unicode, and also check if number > 26
 
5949
        text.Printf(wxT("%c"), (wxChar) (number+96));
 
5950
    }
 
5951
    else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
 
5952
    {
 
5953
        text = wxRichTextDecimalToRoman(number);
 
5954
    }
 
5955
    else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
 
5956
    {
 
5957
        text = wxRichTextDecimalToRoman(number);
 
5958
        text.MakeLower();
 
5959
    }
 
5960
    else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
 
5961
    {
 
5962
        text = GetAttributes().GetBulletText();
 
5963
    }
 
5964
 
 
5965
    if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_OUTLINE)
 
5966
    {
 
5967
        // The outline style relies on the text being computed statically,
 
5968
        // since it depends on other levels points (e.g. 1.2.1.1). So normally the bullet text
 
5969
        // should be stored in the attributes; if not, just use the number for this
 
5970
        // level, as previously computed.
 
5971
        if (!GetAttributes().GetBulletText().IsEmpty())
 
5972
            text = GetAttributes().GetBulletText();
 
5973
    }
 
5974
 
 
5975
    if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES)
 
5976
    {
 
5977
        text = wxT("(") + text + wxT(")");
 
5978
    }
 
5979
    else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_RIGHT_PARENTHESIS)
 
5980
    {
 
5981
        text = text + wxT(")");
 
5982
    }
 
5983
 
 
5984
    if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD)
 
5985
    {
 
5986
        text += wxT(".");
 
5987
    }
 
5988
 
 
5989
    return text;
 
5990
}
 
5991
 
 
5992
/// Allocate or reuse a line object
 
5993
wxRichTextLine* wxRichTextParagraph::AllocateLine(int pos)
 
5994
{
 
5995
    if (pos < (int) m_cachedLines.GetCount())
 
5996
    {
 
5997
        wxRichTextLine* line = m_cachedLines.Item(pos)->GetData();
 
5998
        line->Init(this);
 
5999
        return line;
 
6000
    }
 
6001
    else
 
6002
    {
 
6003
        wxRichTextLine* line = new wxRichTextLine(this);
 
6004
        m_cachedLines.Append(line);
 
6005
        return line;
 
6006
    }
 
6007
}
 
6008
 
 
6009
/// Clear remaining unused line objects, if any
 
6010
bool wxRichTextParagraph::ClearUnusedLines(int lineCount)
 
6011
{
 
6012
    int cachedLineCount = m_cachedLines.GetCount();
 
6013
    if ((int) cachedLineCount > lineCount)
 
6014
    {
 
6015
        for (int i = 0; i < (int) (cachedLineCount - lineCount); i ++)
 
6016
        {
 
6017
            wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetLast();
 
6018
            wxRichTextLine* line = node->GetData();
 
6019
            m_cachedLines.Erase(node);
 
6020
            delete line;
 
6021
        }
 
6022
    }
 
6023
    return true;
 
6024
}
 
6025
 
 
6026
/// Get combined attributes of the base style, paragraph style and character style. We use this to dynamically
 
6027
/// retrieve the actual style.
 
6028
wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(const wxRichTextAttr& contentStyle, bool includingBoxAttr) const
 
6029
{
 
6030
    wxRichTextAttr attr;
 
6031
    wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
 
6032
    if (buf)
 
6033
    {
 
6034
        attr = buf->GetBasicStyle();
 
6035
        if (!includingBoxAttr)
 
6036
        {
 
6037
            attr.GetTextBoxAttr().Reset();
 
6038
            // The background colour will be painted by the container, and we don't
 
6039
            // want to unnecessarily overwrite the background when we're drawing text
 
6040
            // because this may erase the guideline (which appears just under the text
 
6041
            // if there's no padding).
 
6042
            attr.SetFlags(attr.GetFlags() & ~wxTEXT_ATTR_BACKGROUND_COLOUR);
 
6043
        }
 
6044
        wxRichTextApplyStyle(attr, GetAttributes());
 
6045
    }
 
6046
    else
 
6047
        attr = GetAttributes();
 
6048
 
 
6049
    wxRichTextApplyStyle(attr, contentStyle);
 
6050
    return attr;
 
6051
}
 
6052
 
 
6053
/// Get combined attributes of the base style and paragraph style.
 
6054
wxRichTextAttr wxRichTextParagraph::GetCombinedAttributes(bool includingBoxAttr) const
 
6055
{
 
6056
    wxRichTextAttr attr;
 
6057
    wxRichTextParagraphLayoutBox* buf = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
 
6058
    if (buf)
 
6059
    {
 
6060
        attr = buf->GetBasicStyle();
 
6061
        if (!includingBoxAttr)
 
6062
            attr.GetTextBoxAttr().Reset();
 
6063
        wxRichTextApplyStyle(attr, GetAttributes());
 
6064
    }
 
6065
    else
 
6066
        attr = GetAttributes();
 
6067
 
 
6068
    return attr;
 
6069
}
 
6070
 
 
6071
// Create default tabstop array
 
6072
void wxRichTextParagraph::InitDefaultTabs()
 
6073
{
 
6074
    // create a default tab list at 10 mm each.
 
6075
    for (int i = 0; i < 20; ++i)
 
6076
    {
 
6077
        sm_defaultTabs.Add(i*100);
 
6078
    }
 
6079
}
 
6080
 
 
6081
// Clear default tabstop array
 
6082
void wxRichTextParagraph::ClearDefaultTabs()
 
6083
{
 
6084
    sm_defaultTabs.Clear();
 
6085
}
 
6086
 
 
6087
void wxRichTextParagraph::LayoutFloat(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, int style, wxRichTextFloatCollector* floatCollector)
 
6088
{
 
6089
    wxRichTextObjectList::compatibility_iterator node = GetChildren().GetFirst();
 
6090
    while (node)
 
6091
    {
 
6092
        wxRichTextObject* anchored = node->GetData();
 
6093
        if (anchored && anchored->IsFloating() && !floatCollector->HasFloat(anchored))
 
6094
        {
 
6095
            wxSize size;
 
6096
            int descent, x = 0;
 
6097
            anchored->GetRangeSize(anchored->GetRange(), size, descent, dc, context, style);
 
6098
 
 
6099
            int offsetY = 0;
 
6100
            if (anchored->GetAttributes().GetTextBoxAttr().GetTop().IsValid())
 
6101
            {
 
6102
                offsetY = anchored->GetAttributes().GetTextBoxAttr().GetTop().GetValue();
 
6103
                if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
 
6104
                {
 
6105
                    offsetY = ConvertTenthsMMToPixels(dc, offsetY);
 
6106
                }
 
6107
            }
 
6108
 
 
6109
            int pos = floatCollector->GetFitPosition(anchored->GetAttributes().GetTextBoxAttr().GetFloatMode(), rect.y + offsetY, size.y);
 
6110
 
 
6111
            /* Update the offset */
 
6112
            int newOffsetY = pos - rect.y;
 
6113
            if (newOffsetY != offsetY)
 
6114
            {
 
6115
                if (anchored->GetAttributes().GetTextBoxAttr().GetTop().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
 
6116
                    newOffsetY = ConvertPixelsToTenthsMM(dc, newOffsetY);
 
6117
                anchored->GetAttributes().GetTextBoxAttr().GetTop().SetValue(newOffsetY);
 
6118
            }
 
6119
 
 
6120
            if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_LEFT)
 
6121
                x = rect.x;
 
6122
            else if (anchored->GetAttributes().GetTextBoxAttr().GetFloatMode() == wxTEXT_BOX_ATTR_FLOAT_RIGHT)
 
6123
                x = rect.x + rect.width - size.x;
 
6124
 
 
6125
            anchored->SetPosition(wxPoint(x, pos));
 
6126
            anchored->SetCachedSize(size);
 
6127
            floatCollector->CollectFloat(this, anchored);
 
6128
        }
 
6129
 
 
6130
        node = node->GetNext();
 
6131
    }
 
6132
}
 
6133
 
 
6134
// Get the first position from pos that has a line break character.
 
6135
long wxRichTextParagraph::GetFirstLineBreakPosition(long pos)
 
6136
{
 
6137
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
6138
    while (node)
 
6139
    {
 
6140
        wxRichTextObject* obj = node->GetData();
 
6141
        if (pos >= obj->GetRange().GetStart() && pos <= obj->GetRange().GetEnd())
 
6142
        {
 
6143
            wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
 
6144
            if (textObj)
 
6145
            {
 
6146
                long breakPos = textObj->GetFirstLineBreakPosition(pos);
 
6147
                if (breakPos > -1)
 
6148
                    return breakPos;
 
6149
            }
 
6150
        }
 
6151
        node = node->GetNext();
 
6152
    }
 
6153
    return -1;
 
6154
}
 
6155
 
 
6156
/*!
 
6157
 * wxRichTextLine
 
6158
 * This object represents a line in a paragraph, and stores
 
6159
 * offsets from the start of the paragraph representing the
 
6160
 * start and end positions of the line.
 
6161
 */
 
6162
 
 
6163
wxRichTextLine::wxRichTextLine(wxRichTextParagraph* parent)
 
6164
{
 
6165
    Init(parent);
 
6166
}
 
6167
 
 
6168
/// Initialisation
 
6169
void wxRichTextLine::Init(wxRichTextParagraph* parent)
 
6170
{
 
6171
    m_parent = parent;
 
6172
    m_range.SetRange(-1, -1);
 
6173
    m_pos = wxPoint(0, 0);
 
6174
    m_size = wxSize(0, 0);
 
6175
    m_descent = 0;
 
6176
#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
 
6177
    m_objectSizes.Clear();
 
6178
#endif
 
6179
}
 
6180
 
 
6181
/// Copy
 
6182
void wxRichTextLine::Copy(const wxRichTextLine& obj)
 
6183
{
 
6184
    m_range = obj.m_range;
 
6185
#if wxRICHTEXT_USE_OPTIMIZED_LINE_DRAWING
 
6186
    m_objectSizes = obj.m_objectSizes;
 
6187
#endif
 
6188
}
 
6189
 
 
6190
/// Get the absolute object position
 
6191
wxPoint wxRichTextLine::GetAbsolutePosition() const
 
6192
{
 
6193
    return m_parent->GetPosition() + m_pos;
 
6194
}
 
6195
 
 
6196
/// Get the absolute range
 
6197
wxRichTextRange wxRichTextLine::GetAbsoluteRange() const
 
6198
{
 
6199
    wxRichTextRange range(m_range.GetStart() + m_parent->GetRange().GetStart(), 0);
 
6200
    range.SetEnd(range.GetStart() + m_range.GetLength()-1);
 
6201
    return range;
 
6202
}
 
6203
 
 
6204
/*!
 
6205
 * wxRichTextPlainText
 
6206
 * This object represents a single piece of text.
 
6207
 */
 
6208
 
 
6209
IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText, wxRichTextObject)
 
6210
 
 
6211
wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* parent, wxRichTextAttr* style):
 
6212
    wxRichTextObject(parent)
 
6213
{
 
6214
    if (style)
 
6215
        SetAttributes(*style);
 
6216
 
 
6217
    m_text = text;
 
6218
}
 
6219
 
 
6220
#define USE_KERNING_FIX 1
 
6221
 
 
6222
// If insufficient tabs are defined, this is the tab width used
 
6223
#define WIDTH_FOR_DEFAULT_TABS 50
 
6224
 
 
6225
/// Draw the item
 
6226
bool wxRichTextPlainText::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
 
6227
{
 
6228
    wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
 
6229
    wxASSERT (para != NULL);
 
6230
 
 
6231
    wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes(), false /* no box attributes */) : GetAttributes());
 
6232
    context.ApplyVirtualAttributes(textAttr, this);
 
6233
 
 
6234
    // Let's make the assumption for now that for content in a paragraph, including
 
6235
    // text, we never have a discontinuous selection. So we only deal with a
 
6236
    // single range.
 
6237
    wxRichTextRange selectionRange;
 
6238
    if (selection.IsValid())
 
6239
    {
 
6240
        wxRichTextRangeArray selectionRanges = selection.GetSelectionForObject(this);
 
6241
        if (selectionRanges.GetCount() > 0)
 
6242
            selectionRange = selectionRanges[0];
 
6243
        else
 
6244
            selectionRange = wxRICHTEXT_NO_SELECTION;
 
6245
    }
 
6246
    else
 
6247
        selectionRange = wxRICHTEXT_NO_SELECTION;
 
6248
 
 
6249
    int offset = GetRange().GetStart();
 
6250
 
 
6251
    // Replace line break characters with spaces
 
6252
    wxString str = m_text;
 
6253
    wxString toRemove = wxRichTextLineBreakChar;
 
6254
    str.Replace(toRemove, wxT(" "));
 
6255
    if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
 
6256
        str.MakeUpper();
 
6257
 
 
6258
    long len = range.GetLength();
 
6259
    wxString stringChunk = str.Mid(range.GetStart() - offset, (size_t) len);
 
6260
 
 
6261
    // Test for the optimized situations where all is selected, or none
 
6262
    // is selected.
 
6263
 
 
6264
    wxFont textFont(GetBuffer()->GetFontTable().FindFont(textAttr));
 
6265
    wxCheckSetFont(dc, textFont);
 
6266
    int charHeight = dc.GetCharHeight();
 
6267
 
 
6268
    int x, y;
 
6269
    if ( textFont.IsOk() )
 
6270
    {
 
6271
        if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT) )
 
6272
        {
 
6273
            if (textFont.IsUsingSizeInPixels())
 
6274
            {
 
6275
                double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
 
6276
                textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
 
6277
                x = rect.x;
 
6278
                y = rect.y;
 
6279
            }
 
6280
            else
 
6281
            {
 
6282
                double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
 
6283
                textFont.SetPointSize(static_cast<int>(size));
 
6284
                x = rect.x;
 
6285
                y = rect.y;
 
6286
            }
 
6287
            wxCheckSetFont(dc, textFont);
 
6288
        }
 
6289
        else if ( textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) )
 
6290
        {
 
6291
            if (textFont.IsUsingSizeInPixels())
 
6292
            {
 
6293
                double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
 
6294
                textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
 
6295
                x = rect.x;
 
6296
                int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
 
6297
                y = rect.y + (rect.height - sub_height + (descent - m_descent));
 
6298
            }
 
6299
            else
 
6300
            {
 
6301
                double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
 
6302
                textFont.SetPointSize(static_cast<int>(size));
 
6303
                x = rect.x;
 
6304
                int sub_height = static_cast<int>(static_cast<double>(charHeight) / wxSCRIPT_MUL_FACTOR);
 
6305
                y = rect.y + (rect.height - sub_height + (descent - m_descent));
 
6306
            }
 
6307
            wxCheckSetFont(dc, textFont);
 
6308
        }
 
6309
        else
 
6310
        {
 
6311
            x = rect.x;
 
6312
            y = rect.y + (rect.height - charHeight - (descent - m_descent));
 
6313
        }
 
6314
    }
 
6315
    else
 
6316
    {
 
6317
        x = rect.x;
 
6318
        y = rect.y + (rect.height - charHeight - (descent - m_descent));
 
6319
    }
 
6320
 
 
6321
    // TODO: new selection code
 
6322
 
 
6323
    // (a) All selected.
 
6324
    if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd())
 
6325
    {
 
6326
        DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, true);
 
6327
    }
 
6328
    // (b) None selected.
 
6329
    else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd())
 
6330
    {
 
6331
        // Draw all unselected
 
6332
        DrawTabbedString(dc, textAttr, rect, stringChunk, x, y, false);
 
6333
    }
 
6334
    else
 
6335
    {
 
6336
        // (c) Part selected, part not
 
6337
        // Let's draw unselected chunk, selected chunk, then unselected chunk.
 
6338
 
 
6339
        dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
 
6340
 
 
6341
        // 1. Initial unselected chunk, if any, up until start of selection.
 
6342
        if (selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd())
 
6343
        {
 
6344
            int r1 = range.GetStart();
 
6345
            int s1 = selectionRange.GetStart()-1;
 
6346
            int fragmentLen = s1 - r1 + 1;
 
6347
            if (fragmentLen < 0)
 
6348
            {
 
6349
                wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 - offset), (int)fragmentLen);
 
6350
            }
 
6351
            wxString stringFragment = str.Mid(r1 - offset, fragmentLen);
 
6352
 
 
6353
            DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
 
6354
 
 
6355
#if USE_KERNING_FIX
 
6356
            if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
 
6357
            {
 
6358
                // Compensate for kerning difference
 
6359
                wxString stringFragment2(str.Mid(r1 - offset, fragmentLen+1));
 
6360
                wxString stringFragment3(str.Mid(r1 - offset + fragmentLen, 1));
 
6361
 
 
6362
                wxCoord w1, h1, w2, h2, w3, h3;
 
6363
                dc.GetTextExtent(stringFragment,  & w1, & h1);
 
6364
                dc.GetTextExtent(stringFragment2, & w2, & h2);
 
6365
                dc.GetTextExtent(stringFragment3, & w3, & h3);
 
6366
 
 
6367
                int kerningDiff = (w1 + w3) - w2;
 
6368
                x = x - kerningDiff;
 
6369
            }
 
6370
#endif
 
6371
        }
 
6372
 
 
6373
        // 2. Selected chunk, if any.
 
6374
        if (selectionRange.GetEnd() >= range.GetStart())
 
6375
        {
 
6376
            int s1 = wxMax(selectionRange.GetStart(), range.GetStart());
 
6377
            int s2 = wxMin(selectionRange.GetEnd(), range.GetEnd());
 
6378
 
 
6379
            int fragmentLen = s2 - s1 + 1;
 
6380
            if (fragmentLen < 0)
 
6381
            {
 
6382
                wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 - offset), (int)fragmentLen);
 
6383
            }
 
6384
            wxString stringFragment = str.Mid(s1 - offset, fragmentLen);
 
6385
 
 
6386
            DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, true);
 
6387
 
 
6388
#if USE_KERNING_FIX
 
6389
            if (stringChunk.Find(wxT("\t")) == wxNOT_FOUND)
 
6390
            {
 
6391
                // Compensate for kerning difference
 
6392
                wxString stringFragment2(str.Mid(s1 - offset, fragmentLen+1));
 
6393
                wxString stringFragment3(str.Mid(s1 - offset + fragmentLen, 1));
 
6394
 
 
6395
                wxCoord w1, h1, w2, h2, w3, h3;
 
6396
                dc.GetTextExtent(stringFragment,  & w1, & h1);
 
6397
                dc.GetTextExtent(stringFragment2, & w2, & h2);
 
6398
                dc.GetTextExtent(stringFragment3, & w3, & h3);
 
6399
 
 
6400
                int kerningDiff = (w1 + w3) - w2;
 
6401
                x = x - kerningDiff;
 
6402
            }
 
6403
#endif
 
6404
        }
 
6405
 
 
6406
        // 3. Remaining unselected chunk, if any
 
6407
        if (selectionRange.GetEnd() < range.GetEnd())
 
6408
        {
 
6409
            int s2 = wxMin(selectionRange.GetEnd()+1, range.GetEnd());
 
6410
            int r2 = range.GetEnd();
 
6411
 
 
6412
            int fragmentLen = r2 - s2 + 1;
 
6413
            if (fragmentLen < 0)
 
6414
            {
 
6415
                wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 - offset), (int)fragmentLen);
 
6416
            }
 
6417
            wxString stringFragment = str.Mid(s2 - offset, fragmentLen);
 
6418
 
 
6419
            DrawTabbedString(dc, textAttr, rect, stringFragment, x, y, false);
 
6420
        }
 
6421
    }
 
6422
 
 
6423
    return true;
 
6424
}
 
6425
 
 
6426
bool wxRichTextPlainText::DrawTabbedString(wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect,wxString& str, wxCoord& x, wxCoord& y, bool selected)
 
6427
{
 
6428
    bool hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
 
6429
 
 
6430
    wxArrayInt tabArray;
 
6431
    int tabCount;
 
6432
    if (hasTabs)
 
6433
    {
 
6434
        if (attr.GetTabs().IsEmpty())
 
6435
            tabArray = wxRichTextParagraph::GetDefaultTabs();
 
6436
        else
 
6437
            tabArray = attr.GetTabs();
 
6438
        tabCount = tabArray.GetCount();
 
6439
 
 
6440
        for (int i = 0; i < tabCount; ++i)
 
6441
        {
 
6442
            int pos = tabArray[i];
 
6443
            pos = ConvertTenthsMMToPixels(dc, pos);
 
6444
            tabArray[i] = pos;
 
6445
        }
 
6446
    }
 
6447
    else
 
6448
        tabCount = 0;
 
6449
 
 
6450
    int nextTabPos = -1;
 
6451
    int tabPos = -1;
 
6452
    wxCoord w, h;
 
6453
 
 
6454
    if (selected)
 
6455
    {
 
6456
        wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
 
6457
        wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
 
6458
 
 
6459
        wxCheckSetBrush(dc, wxBrush(highlightColour));
 
6460
        wxCheckSetPen(dc, wxPen(highlightColour));
 
6461
        dc.SetTextForeground(highlightTextColour);
 
6462
        dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
 
6463
    }
 
6464
    else
 
6465
    {
 
6466
        dc.SetTextForeground(attr.GetTextColour());
 
6467
 
 
6468
        if (attr.HasFlag(wxTEXT_ATTR_BACKGROUND_COLOUR) && attr.GetBackgroundColour().IsOk())
 
6469
        {
 
6470
            dc.SetBackgroundMode(wxBRUSHSTYLE_SOLID);
 
6471
            dc.SetTextBackground(attr.GetBackgroundColour());
 
6472
        }
 
6473
        else
 
6474
            dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
 
6475
    }
 
6476
 
 
6477
    wxCoord x_orig = GetParent()->GetPosition().x;
 
6478
    while (hasTabs)
 
6479
    {
 
6480
        // the string has a tab
 
6481
        // break up the string at the Tab
 
6482
        wxString stringChunk = str.BeforeFirst(wxT('\t'));
 
6483
        str = str.AfterFirst(wxT('\t'));
 
6484
        dc.GetTextExtent(stringChunk, & w, & h);
 
6485
        tabPos = x + w;
 
6486
        bool not_found = true;
 
6487
        for (int i = 0; i < tabCount && not_found; ++i)
 
6488
        {
 
6489
            nextTabPos = tabArray.Item(i) + x_orig;
 
6490
 
 
6491
            // Find the next tab position.
 
6492
            // Even if we're at the end of the tab array, we must still draw the chunk.
 
6493
 
 
6494
            if (nextTabPos > tabPos || (i == (tabCount - 1)))
 
6495
            {
 
6496
                if (nextTabPos <= tabPos)
 
6497
                {
 
6498
                    int defaultTabWidth = ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
 
6499
                    nextTabPos = tabPos + defaultTabWidth;
 
6500
                }
 
6501
 
 
6502
                not_found = false;
 
6503
                if (selected)
 
6504
                {
 
6505
                    w = nextTabPos - x;
 
6506
                    wxRect selRect(x, rect.y, w, rect.GetHeight());
 
6507
                    dc.DrawRectangle(selRect);
 
6508
                }
 
6509
                dc.DrawText(stringChunk, x, y);
 
6510
 
 
6511
                if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
 
6512
                {
 
6513
                    wxPen oldPen = dc.GetPen();
 
6514
                    wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
 
6515
                    dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
 
6516
                    wxCheckSetPen(dc, oldPen);
 
6517
                }
 
6518
 
 
6519
                x = nextTabPos;
 
6520
            }
 
6521
        }
 
6522
        hasTabs = (str.Find(wxT('\t')) != wxNOT_FOUND);
 
6523
    }
 
6524
 
 
6525
    if (!str.IsEmpty())
 
6526
    {
 
6527
        dc.GetTextExtent(str, & w, & h);
 
6528
        if (selected)
 
6529
        {
 
6530
            wxRect selRect(x, rect.y, w, rect.GetHeight());
 
6531
            dc.DrawRectangle(selRect);
 
6532
        }
 
6533
        dc.DrawText(str, x, y);
 
6534
 
 
6535
        if (attr.HasTextEffects() && (attr.GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH))
 
6536
        {
 
6537
            wxPen oldPen = dc.GetPen();
 
6538
            wxCheckSetPen(dc, wxPen(attr.GetTextColour(), 1));
 
6539
            dc.DrawLine(x, (int) (y+(h/2)+0.5), x+w, (int) (y+(h/2)+0.5));
 
6540
            wxCheckSetPen(dc, oldPen);
 
6541
        }
 
6542
 
 
6543
        x += w;
 
6544
    }
 
6545
 
 
6546
    return true;
 
6547
}
 
6548
 
 
6549
/// Lay the item out
 
6550
bool wxRichTextPlainText::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
 
6551
{
 
6552
    // Only lay out if we haven't already cached the size
 
6553
    if (m_size.x == -1)
 
6554
        GetRangeSize(GetRange(), m_size, m_descent, dc, context, 0, wxPoint(0, 0));
 
6555
    m_maxSize = m_size;
 
6556
    // Eventually we want to have a reasonable estimate of minimum size.
 
6557
    m_minSize = wxSize(0, 0);
 
6558
    return true;
 
6559
}
 
6560
 
 
6561
/// Copy
 
6562
void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj)
 
6563
{
 
6564
    wxRichTextObject::Copy(obj);
 
6565
 
 
6566
    m_text = obj.m_text;
 
6567
}
 
6568
 
 
6569
/// Get/set the object size for the given range. Returns false if the range
 
6570
/// is invalid for this object.
 
6571
bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), wxPoint position, wxArrayInt* partialExtents) const
 
6572
{
 
6573
    if (!range.IsWithin(GetRange()))
 
6574
        return false;
 
6575
 
 
6576
    wxRichTextParagraph* para = wxDynamicCast(GetParent(), wxRichTextParagraph);
 
6577
    wxASSERT (para != NULL);
 
6578
 
 
6579
    int relativeX = position.x - GetParent()->GetPosition().x;
 
6580
 
 
6581
    wxRichTextAttr textAttr(para ? para->GetCombinedAttributes(GetAttributes()) : GetAttributes());
 
6582
    context.ApplyVirtualAttributes(textAttr, (wxRichTextObject*) this);
 
6583
 
 
6584
    // Always assume unformatted text, since at this level we have no knowledge
 
6585
    // of line breaks - and we don't need it, since we'll calculate size within
 
6586
    // formatted text by doing it in chunks according to the line ranges
 
6587
 
 
6588
    bool bScript(false);
 
6589
    wxFont font(GetBuffer()->GetFontTable().FindFont(textAttr));
 
6590
    if (font.IsOk())
 
6591
    {
 
6592
        if ( textAttr.HasTextEffects() && ( (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT)
 
6593
            || (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT) ) )
 
6594
        {
 
6595
            wxFont textFont = font;
 
6596
            if (textFont.IsUsingSizeInPixels())
 
6597
            {
 
6598
                double size = static_cast<double>(textFont.GetPixelSize().y) / wxSCRIPT_MUL_FACTOR;
 
6599
                textFont.SetPixelSize(wxSize(0, static_cast<int>(size)));
 
6600
            }
 
6601
            else
 
6602
            {
 
6603
                double size = static_cast<double>(textFont.GetPointSize()) / wxSCRIPT_MUL_FACTOR;
 
6604
                textFont.SetPointSize(static_cast<int>(size));
 
6605
            }
 
6606
            wxCheckSetFont(dc, textFont);
 
6607
            bScript = true;
 
6608
        }
 
6609
        else
 
6610
        {
 
6611
            wxCheckSetFont(dc, font);
 
6612
        }
 
6613
    }
 
6614
 
 
6615
    bool haveDescent = false;
 
6616
    int startPos = range.GetStart() - GetRange().GetStart();
 
6617
    long len = range.GetLength();
 
6618
 
 
6619
    wxString str(m_text);
 
6620
    wxString toReplace = wxRichTextLineBreakChar;
 
6621
    str.Replace(toReplace, wxT(" "));
 
6622
 
 
6623
    wxString stringChunk = str.Mid(startPos, (size_t) len);
 
6624
 
 
6625
    if (textAttr.HasTextEffects() && (textAttr.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS))
 
6626
        stringChunk.MakeUpper();
 
6627
 
 
6628
    wxCoord w, h;
 
6629
    int width = 0;
 
6630
    if (stringChunk.Find(wxT('\t')) != wxNOT_FOUND)
 
6631
    {
 
6632
        // the string has a tab
 
6633
        wxArrayInt tabArray;
 
6634
        if (textAttr.GetTabs().IsEmpty())
 
6635
            tabArray = wxRichTextParagraph::GetDefaultTabs();
 
6636
        else
 
6637
            tabArray = textAttr.GetTabs();
 
6638
 
 
6639
        int tabCount = tabArray.GetCount();
 
6640
 
 
6641
        for (int i = 0; i < tabCount; ++i)
 
6642
        {
 
6643
            int pos = tabArray[i];
 
6644
            pos = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, pos);
 
6645
            tabArray[i] = pos;
 
6646
        }
 
6647
 
 
6648
        int nextTabPos = -1;
 
6649
 
 
6650
        while (stringChunk.Find(wxT('\t')) >= 0)
 
6651
        {
 
6652
            int absoluteWidth = 0;
 
6653
 
 
6654
            // the string has a tab
 
6655
            // break up the string at the Tab
 
6656
            wxString stringFragment = stringChunk.BeforeFirst(wxT('\t'));
 
6657
            stringChunk = stringChunk.AfterFirst(wxT('\t'));
 
6658
 
 
6659
            if (partialExtents)
 
6660
            {
 
6661
                int oldWidth;
 
6662
                if (partialExtents->GetCount() > 0)
 
6663
                    oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
 
6664
                else
 
6665
                    oldWidth = 0;
 
6666
 
 
6667
                // Add these partial extents
 
6668
                wxArrayInt p;
 
6669
                dc.GetPartialTextExtents(stringFragment, p);
 
6670
                size_t j;
 
6671
                for (j = 0; j < p.GetCount(); j++)
 
6672
                    partialExtents->Add(oldWidth + p[j]);
 
6673
 
 
6674
                if (partialExtents->GetCount() > 0)
 
6675
                    absoluteWidth = (*partialExtents)[(*partialExtents).GetCount()-1] + relativeX;
 
6676
                else
 
6677
                    absoluteWidth = relativeX;
 
6678
            }
 
6679
            else
 
6680
            {
 
6681
                dc.GetTextExtent(stringFragment, & w, & h);
 
6682
                width += w;
 
6683
                absoluteWidth = width + relativeX;
 
6684
                haveDescent = true;
 
6685
            }
 
6686
 
 
6687
            bool notFound = true;
 
6688
            for (int i = 0; i < tabCount && notFound; ++i)
 
6689
            {
 
6690
                nextTabPos = tabArray.Item(i);
 
6691
 
 
6692
                // Find the next tab position.
 
6693
                // Even if we're at the end of the tab array, we must still process the chunk.
 
6694
 
 
6695
                if (nextTabPos > absoluteWidth || (i == (tabCount - 1)))
 
6696
                {
 
6697
                    if (nextTabPos <= absoluteWidth)
 
6698
                    {
 
6699
                        int defaultTabWidth = ((wxRichTextPlainText*) this)->ConvertTenthsMMToPixels(dc, WIDTH_FOR_DEFAULT_TABS);
 
6700
                        nextTabPos = absoluteWidth + defaultTabWidth;
 
6701
                    }
 
6702
 
 
6703
                    notFound = false;
 
6704
                    width = nextTabPos - relativeX;
 
6705
 
 
6706
                    if (partialExtents)
 
6707
                        partialExtents->Add(width);
 
6708
                }
 
6709
            }
 
6710
        }
 
6711
    }
 
6712
 
 
6713
    if (!stringChunk.IsEmpty())
 
6714
    {
 
6715
        if (partialExtents)
 
6716
        {
 
6717
            int oldWidth;
 
6718
            if (partialExtents->GetCount() > 0)
 
6719
                oldWidth = (*partialExtents)[partialExtents->GetCount()-1];
 
6720
            else
 
6721
                oldWidth = 0;
 
6722
 
 
6723
            // Add these partial extents
 
6724
            wxArrayInt p;
 
6725
            dc.GetPartialTextExtents(stringChunk, p);
 
6726
            size_t j;
 
6727
            for (j = 0; j < p.GetCount(); j++)
 
6728
                partialExtents->Add(oldWidth + p[j]);
 
6729
        }
 
6730
        else
 
6731
        {
 
6732
            dc.GetTextExtent(stringChunk, & w, & h, & descent);
 
6733
            width += w;
 
6734
            haveDescent = true;
 
6735
        }
 
6736
    }
 
6737
 
 
6738
    if (partialExtents)
 
6739
    {
 
6740
        int charHeight = dc.GetCharHeight();
 
6741
        if ((*partialExtents).GetCount() > 0)
 
6742
            w = (*partialExtents)[partialExtents->GetCount()-1];
 
6743
        else
 
6744
            w = 0;
 
6745
        size = wxSize(w, charHeight);
 
6746
    }
 
6747
    else
 
6748
    {
 
6749
        size = wxSize(width, dc.GetCharHeight());
 
6750
    }
 
6751
 
 
6752
    if (!haveDescent)
 
6753
        dc.GetTextExtent(wxT("X"), & w, & h, & descent);
 
6754
 
 
6755
    if ( bScript )
 
6756
        dc.SetFont(font);
 
6757
 
 
6758
    return true;
 
6759
}
 
6760
 
 
6761
/// Do a split, returning an object containing the second part, and setting
 
6762
/// the first part in 'this'.
 
6763
wxRichTextObject* wxRichTextPlainText::DoSplit(long pos)
 
6764
{
 
6765
    long index = pos - GetRange().GetStart();
 
6766
 
 
6767
    if (index < 0 || index >= (int) m_text.length())
 
6768
        return NULL;
 
6769
 
 
6770
    wxString firstPart = m_text.Mid(0, index);
 
6771
    wxString secondPart = m_text.Mid(index);
 
6772
 
 
6773
    m_text = firstPart;
 
6774
 
 
6775
    wxRichTextPlainText* newObject = new wxRichTextPlainText(secondPart);
 
6776
    newObject->SetAttributes(GetAttributes());
 
6777
    newObject->SetProperties(GetProperties());
 
6778
 
 
6779
    newObject->SetRange(wxRichTextRange(pos, GetRange().GetEnd()));
 
6780
    GetRange().SetEnd(pos-1);
 
6781
 
 
6782
    return newObject;
 
6783
}
 
6784
 
 
6785
/// Calculate range
 
6786
void wxRichTextPlainText::CalculateRange(long start, long& end)
 
6787
{
 
6788
    end = start + m_text.length() - 1;
 
6789
    m_range.SetRange(start, end);
 
6790
}
 
6791
 
 
6792
/// Delete range
 
6793
bool wxRichTextPlainText::DeleteRange(const wxRichTextRange& range)
 
6794
{
 
6795
    wxRichTextRange r = range;
 
6796
 
 
6797
    r.LimitTo(GetRange());
 
6798
 
 
6799
    if (r.GetStart() == GetRange().GetStart() && r.GetEnd() == GetRange().GetEnd())
 
6800
    {
 
6801
        m_text.Empty();
 
6802
        return true;
 
6803
    }
 
6804
 
 
6805
    long startIndex = r.GetStart() - GetRange().GetStart();
 
6806
    long len = r.GetLength();
 
6807
 
 
6808
    m_text = m_text.Mid(0, startIndex) + m_text.Mid(startIndex+len);
 
6809
    return true;
 
6810
}
 
6811
 
 
6812
/// Get text for the given range.
 
6813
wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) const
 
6814
{
 
6815
    wxRichTextRange r = range;
 
6816
 
 
6817
    r.LimitTo(GetRange());
 
6818
 
 
6819
    long startIndex = r.GetStart() - GetRange().GetStart();
 
6820
    long len = r.GetLength();
 
6821
 
 
6822
    return m_text.Mid(startIndex, len);
 
6823
}
 
6824
 
 
6825
/// Returns true if this object can merge itself with the given one.
 
6826
bool wxRichTextPlainText::CanMerge(wxRichTextObject* object) const
 
6827
{
 
6828
    return object->GetClassInfo() == wxCLASSINFO(wxRichTextPlainText) &&
 
6829
        (m_text.empty() || (wxTextAttrEq(GetAttributes(), object->GetAttributes()) && m_properties == object->GetProperties()));
 
6830
}
 
6831
 
 
6832
/// Returns true if this object merged itself with the given one.
 
6833
/// The calling code will then delete the given object.
 
6834
bool wxRichTextPlainText::Merge(wxRichTextObject* object)
 
6835
{
 
6836
    wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText);
 
6837
    wxASSERT( textObject != NULL );
 
6838
 
 
6839
    if (textObject)
 
6840
    {
 
6841
        m_text += textObject->GetText();
 
6842
        wxRichTextApplyStyle(m_attributes, textObject->GetAttributes());
 
6843
        return true;
 
6844
    }
 
6845
    else
 
6846
        return false;
 
6847
}
 
6848
 
 
6849
/// Dump to output stream for debugging
 
6850
void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
 
6851
{
 
6852
    wxRichTextObject::Dump(stream);
 
6853
    stream << m_text << wxT("\n");
 
6854
}
 
6855
 
 
6856
/// Get the first position from pos that has a line break character.
 
6857
long wxRichTextPlainText::GetFirstLineBreakPosition(long pos)
 
6858
{
 
6859
    int i;
 
6860
    int len = m_text.length();
 
6861
    int startPos = pos - m_range.GetStart();
 
6862
    for (i = startPos; i < len; i++)
 
6863
    {
 
6864
        wxChar ch = m_text[i];
 
6865
        if (ch == wxRichTextLineBreakChar)
 
6866
        {
 
6867
            return i + m_range.GetStart();
 
6868
        }
 
6869
    }
 
6870
    return -1;
 
6871
}
 
6872
 
 
6873
/*!
 
6874
 * wxRichTextBuffer
 
6875
 * This is a kind of box, used to represent the whole buffer
 
6876
 */
 
6877
 
 
6878
IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox)
 
6879
 
 
6880
wxList                      wxRichTextBuffer::sm_handlers;
 
6881
wxList                      wxRichTextBuffer::sm_drawingHandlers;
 
6882
wxRichTextFieldTypeHashMap  wxRichTextBuffer::sm_fieldTypes;
 
6883
wxRichTextRenderer*         wxRichTextBuffer::sm_renderer = NULL;
 
6884
int                         wxRichTextBuffer::sm_bulletRightMargin = 20;
 
6885
float                       wxRichTextBuffer::sm_bulletProportion = (float) 0.3;
 
6886
 
 
6887
/// Initialisation
 
6888
void wxRichTextBuffer::Init()
 
6889
{
 
6890
    m_commandProcessor = new wxCommandProcessor;
 
6891
    m_styleSheet = NULL;
 
6892
    m_modified = false;
 
6893
    m_batchedCommandDepth = 0;
 
6894
    m_batchedCommand = NULL;
 
6895
    m_suppressUndo = 0;
 
6896
    m_handlerFlags = 0;
 
6897
    m_scale = 1.0;
 
6898
    m_dimensionScale = 1.0;
 
6899
    m_fontScale = 1.0;
 
6900
    SetMargins(4);
 
6901
}
 
6902
 
 
6903
/// Initialisation
 
6904
wxRichTextBuffer::~wxRichTextBuffer()
 
6905
{
 
6906
    delete m_commandProcessor;
 
6907
    delete m_batchedCommand;
 
6908
 
 
6909
    ClearStyleStack();
 
6910
    ClearEventHandlers();
 
6911
}
 
6912
 
 
6913
void wxRichTextBuffer::ResetAndClearCommands()
 
6914
{
 
6915
    Reset();
 
6916
 
 
6917
    GetCommandProcessor()->ClearCommands();
 
6918
 
 
6919
    Modify(false);
 
6920
    Invalidate(wxRICHTEXT_ALL);
 
6921
}
 
6922
 
 
6923
void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj)
 
6924
{
 
6925
    wxRichTextParagraphLayoutBox::Copy(obj);
 
6926
 
 
6927
    m_styleSheet = obj.m_styleSheet;
 
6928
    m_modified = obj.m_modified;
 
6929
    m_batchedCommandDepth = 0;
 
6930
    if (m_batchedCommand)
 
6931
        delete m_batchedCommand;
 
6932
    m_batchedCommand = NULL;
 
6933
    m_suppressUndo = obj.m_suppressUndo;
 
6934
    m_invalidRange = obj.m_invalidRange;
 
6935
    m_dimensionScale = obj.m_dimensionScale;
 
6936
    m_fontScale = obj.m_fontScale;
 
6937
}
 
6938
 
 
6939
/// Push style sheet to top of stack
 
6940
bool wxRichTextBuffer::PushStyleSheet(wxRichTextStyleSheet* styleSheet)
 
6941
{
 
6942
    if (m_styleSheet)
 
6943
        styleSheet->InsertSheet(m_styleSheet);
 
6944
 
 
6945
    SetStyleSheet(styleSheet);
 
6946
 
 
6947
    return true;
 
6948
}
 
6949
 
 
6950
/// Pop style sheet from top of stack
 
6951
wxRichTextStyleSheet* wxRichTextBuffer::PopStyleSheet()
 
6952
{
 
6953
    if (m_styleSheet)
 
6954
    {
 
6955
        wxRichTextStyleSheet* oldSheet = m_styleSheet;
 
6956
        m_styleSheet = oldSheet->GetNextSheet();
 
6957
        oldSheet->Unlink();
 
6958
 
 
6959
        return oldSheet;
 
6960
    }
 
6961
    else
 
6962
        return NULL;
 
6963
}
 
6964
 
 
6965
/// Submit command to insert paragraphs
 
6966
bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int flags)
 
6967
{
 
6968
    return ctrl->GetFocusObject()->InsertParagraphsWithUndo(this, pos, paragraphs, ctrl, flags);
 
6969
}
 
6970
 
 
6971
/// Submit command to insert paragraphs
 
6972
bool wxRichTextParagraphLayoutBox::InsertParagraphsWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int WXUNUSED(flags))
 
6973
{
 
6974
    wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
 
6975
 
 
6976
    action->GetNewParagraphs() = paragraphs;
 
6977
 
 
6978
    action->SetPosition(pos);
 
6979
 
 
6980
    wxRichTextRange range = wxRichTextRange(pos, pos + paragraphs.GetOwnRange().GetEnd() - 1);
 
6981
    if (!paragraphs.GetPartialParagraph())
 
6982
        range.SetEnd(range.GetEnd()+1);
 
6983
 
 
6984
    // Set the range we'll need to delete in Undo
 
6985
    action->SetRange(range);
 
6986
 
 
6987
    buffer->SubmitAction(action);
 
6988
 
 
6989
    return true;
 
6990
}
 
6991
 
 
6992
/// Submit command to insert the given text
 
6993
bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
 
6994
{
 
6995
    return ctrl->GetFocusObject()->InsertTextWithUndo(this, pos, text, ctrl, flags);
 
6996
}
 
6997
 
 
6998
/// Submit command to insert the given text
 
6999
bool wxRichTextParagraphLayoutBox::InsertTextWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags)
 
7000
{
 
7001
    wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
 
7002
 
 
7003
    wxRichTextAttr* p = NULL;
 
7004
    wxRichTextAttr paraAttr;
 
7005
    if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
 
7006
    {
 
7007
        // Get appropriate paragraph style
 
7008
        paraAttr = GetStyleForNewParagraph(buffer, pos, false, false);
 
7009
        if (!paraAttr.IsDefault())
 
7010
            p = & paraAttr;
 
7011
    }
 
7012
 
 
7013
    action->GetNewParagraphs().AddParagraphs(text, p);
 
7014
 
 
7015
    int length = action->GetNewParagraphs().GetOwnRange().GetLength();
 
7016
 
 
7017
    if (!text.empty() && text.Last() != wxT('\n'))
 
7018
    {
 
7019
        // Don't count the newline when undoing
 
7020
        length --;
 
7021
        action->GetNewParagraphs().SetPartialParagraph(true);
 
7022
    }
 
7023
    else if (!text.empty() && text.Last() == wxT('\n'))
 
7024
        length --;
 
7025
 
 
7026
    action->SetPosition(pos);
 
7027
 
 
7028
    // Set the range we'll need to delete in Undo
 
7029
    action->SetRange(wxRichTextRange(pos, pos + length - 1));
 
7030
 
 
7031
    buffer->SubmitAction(action);
 
7032
 
 
7033
    return true;
 
7034
}
 
7035
 
 
7036
/// Submit command to insert the given text
 
7037
bool wxRichTextBuffer::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl, int flags)
 
7038
{
 
7039
    return ctrl->GetFocusObject()->InsertNewlineWithUndo(this, pos, ctrl, flags);
 
7040
}
 
7041
 
 
7042
/// Submit command to insert the given text
 
7043
bool wxRichTextParagraphLayoutBox::InsertNewlineWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextCtrl* ctrl, int flags)
 
7044
{
 
7045
    wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
 
7046
 
 
7047
    wxRichTextAttr* p = NULL;
 
7048
    wxRichTextAttr paraAttr;
 
7049
    if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
 
7050
    {
 
7051
        paraAttr = GetStyleForNewParagraph(buffer, pos, false, true /* look for next paragraph style */);
 
7052
        if (!paraAttr.IsDefault())
 
7053
            p = & paraAttr;
 
7054
    }
 
7055
 
 
7056
    wxRichTextAttr attr(buffer->GetDefaultStyle());
 
7057
    // Don't include box attributes such as margins
 
7058
    attr.GetTextBoxAttr().Reset();
 
7059
 
 
7060
    wxRichTextParagraph* newPara = new wxRichTextParagraph(wxEmptyString, this, & attr);
 
7061
    action->GetNewParagraphs().AppendChild(newPara);
 
7062
    action->GetNewParagraphs().UpdateRanges();
 
7063
    action->GetNewParagraphs().SetPartialParagraph(false);
 
7064
    wxRichTextParagraph* para = GetParagraphAtPosition(pos, false);
 
7065
    long pos1 = pos;
 
7066
 
 
7067
    if (p)
 
7068
        newPara->SetAttributes(*p);
 
7069
 
 
7070
    if (flags & wxRICHTEXT_INSERT_INTERACTIVE)
 
7071
    {
 
7072
        if (para && para->GetRange().GetEnd() == pos)
 
7073
            pos1 ++;
 
7074
 
 
7075
        // Now see if we need to number the paragraph.
 
7076
        if (newPara->GetAttributes().HasBulletNumber())
 
7077
        {
 
7078
            wxRichTextAttr numberingAttr;
 
7079
            if (FindNextParagraphNumber(para, numberingAttr))
 
7080
                wxRichTextApplyStyle(newPara->GetAttributes(), (const wxRichTextAttr&) numberingAttr);
 
7081
        }
 
7082
    }
 
7083
 
 
7084
    action->SetPosition(pos);
 
7085
 
 
7086
    // Use the default character style
 
7087
    if (!buffer->GetDefaultStyle().IsDefault() && newPara->GetChildren().GetFirst())
 
7088
    {
 
7089
        // Check whether the default style merely reflects the paragraph/basic style,
 
7090
        // in which case don't apply it.
 
7091
        wxRichTextAttr defaultStyle(buffer->GetDefaultStyle());
 
7092
        defaultStyle.GetTextBoxAttr().Reset();
 
7093
        wxRichTextAttr toApply;
 
7094
        if (para)
 
7095
        {
 
7096
            wxRichTextAttr combinedAttr = para->GetCombinedAttributes(true /* include box attributes */);
 
7097
            wxRichTextAttr newAttr;
 
7098
            // This filters out attributes that are accounted for by the current
 
7099
            // paragraph/basic style
 
7100
            wxRichTextApplyStyle(toApply, defaultStyle, & combinedAttr);
 
7101
        }
 
7102
        else
 
7103
            toApply = defaultStyle;
 
7104
 
 
7105
        if (!toApply.IsDefault())
 
7106
            newPara->GetChildren().GetFirst()->GetData()->SetAttributes(toApply);
 
7107
    }
 
7108
 
 
7109
    // Set the range we'll need to delete in Undo
 
7110
    action->SetRange(wxRichTextRange(pos1, pos1));
 
7111
 
 
7112
    buffer->SubmitAction(action);
 
7113
 
 
7114
    return true;
 
7115
}
 
7116
 
 
7117
/// Submit command to insert the given image
 
7118
bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock, wxRichTextCtrl* ctrl, int flags,
 
7119
                                            const wxRichTextAttr& textAttr)
 
7120
{
 
7121
    return ctrl->GetFocusObject()->InsertImageWithUndo(this, pos, imageBlock, ctrl, flags, textAttr);
 
7122
}
 
7123
 
 
7124
/// Submit command to insert the given image
 
7125
bool wxRichTextParagraphLayoutBox::InsertImageWithUndo(wxRichTextBuffer* buffer, long pos, const wxRichTextImageBlock& imageBlock,
 
7126
                                                        wxRichTextCtrl* ctrl, int flags,
 
7127
                                                        const wxRichTextAttr& textAttr)
 
7128
{
 
7129
    wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
 
7130
 
 
7131
    wxRichTextAttr* p = NULL;
 
7132
    wxRichTextAttr paraAttr;
 
7133
    if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
 
7134
    {
 
7135
        paraAttr = GetStyleForNewParagraph(buffer, pos);
 
7136
        if (!paraAttr.IsDefault())
 
7137
            p = & paraAttr;
 
7138
    }
 
7139
 
 
7140
    wxRichTextAttr attr(buffer->GetDefaultStyle());
 
7141
 
 
7142
    // Don't include box attributes such as margins
 
7143
    attr.GetTextBoxAttr().Reset();
 
7144
 
 
7145
    wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
 
7146
    if (p)
 
7147
        newPara->SetAttributes(*p);
 
7148
 
 
7149
    wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
 
7150
    newPara->AppendChild(imageObject);
 
7151
    imageObject->SetAttributes(textAttr);
 
7152
    action->GetNewParagraphs().AppendChild(newPara);
 
7153
    action->GetNewParagraphs().UpdateRanges();
 
7154
 
 
7155
    action->GetNewParagraphs().SetPartialParagraph(true);
 
7156
 
 
7157
    action->SetPosition(pos);
 
7158
 
 
7159
    // Set the range we'll need to delete in Undo
 
7160
    action->SetRange(wxRichTextRange(pos, pos));
 
7161
 
 
7162
    buffer->SubmitAction(action);
 
7163
 
 
7164
    return true;
 
7165
}
 
7166
 
 
7167
// Insert an object with no change of it
 
7168
wxRichTextObject* wxRichTextBuffer::InsertObjectWithUndo(long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
 
7169
{
 
7170
    return ctrl->GetFocusObject()->InsertObjectWithUndo(this, pos, object, ctrl, flags);
 
7171
}
 
7172
 
 
7173
// Insert an object with no change of it
 
7174
wxRichTextObject* wxRichTextParagraphLayoutBox::InsertObjectWithUndo(wxRichTextBuffer* buffer, long pos, wxRichTextObject *object, wxRichTextCtrl* ctrl, int flags)
 
7175
{
 
7176
    wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Object"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
 
7177
 
 
7178
    wxRichTextAttr* p = NULL;
 
7179
    wxRichTextAttr paraAttr;
 
7180
    if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
 
7181
    {
 
7182
        paraAttr = GetStyleForNewParagraph(buffer, pos);
 
7183
        if (!paraAttr.IsDefault())
 
7184
            p = & paraAttr;
 
7185
    }
 
7186
 
 
7187
    wxRichTextAttr attr(buffer->GetDefaultStyle());
 
7188
 
 
7189
    // Don't include box attributes such as margins
 
7190
    attr.GetTextBoxAttr().Reset();
 
7191
 
 
7192
    wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
 
7193
    if (p)
 
7194
        newPara->SetAttributes(*p);
 
7195
 
 
7196
    newPara->AppendChild(object);
 
7197
    action->GetNewParagraphs().AppendChild(newPara);
 
7198
    action->GetNewParagraphs().UpdateRanges();
 
7199
 
 
7200
    action->GetNewParagraphs().SetPartialParagraph(true);
 
7201
 
 
7202
    action->SetPosition(pos);
 
7203
 
 
7204
    // Set the range we'll need to delete in Undo
 
7205
    action->SetRange(wxRichTextRange(pos, pos));
 
7206
 
 
7207
    buffer->SubmitAction(action);
 
7208
 
 
7209
    wxRichTextObject* obj = GetLeafObjectAtPosition(pos);
 
7210
    return obj;
 
7211
}
 
7212
 
 
7213
wxRichTextField* wxRichTextParagraphLayoutBox::InsertFieldWithUndo(wxRichTextBuffer* buffer, long pos, const wxString& fieldType,
 
7214
                                                        const wxRichTextProperties& properties,
 
7215
                                                        wxRichTextCtrl* ctrl, int flags,
 
7216
                                                        const wxRichTextAttr& textAttr)
 
7217
{
 
7218
    wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Field"), wxRICHTEXT_INSERT, buffer, this, ctrl, false);
 
7219
 
 
7220
    wxRichTextAttr* p = NULL;
 
7221
    wxRichTextAttr paraAttr;
 
7222
    if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE)
 
7223
    {
 
7224
        paraAttr = GetStyleForNewParagraph(buffer, pos);
 
7225
        if (!paraAttr.IsDefault())
 
7226
            p = & paraAttr;
 
7227
    }
 
7228
 
 
7229
    wxRichTextAttr attr(buffer->GetDefaultStyle());
 
7230
 
 
7231
    // Don't include box attributes such as margins
 
7232
    attr.GetTextBoxAttr().Reset();
 
7233
 
 
7234
    wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
 
7235
    if (p)
 
7236
        newPara->SetAttributes(*p);
 
7237
 
 
7238
    wxRichTextField* fieldObject = new wxRichTextField();
 
7239
    fieldObject->wxRichTextObject::SetProperties(properties);
 
7240
    fieldObject->SetFieldType(fieldType);
 
7241
    fieldObject->SetAttributes(textAttr);
 
7242
    newPara->AppendChild(fieldObject);
 
7243
    action->GetNewParagraphs().AppendChild(newPara);
 
7244
    action->GetNewParagraphs().UpdateRanges();
 
7245
    action->GetNewParagraphs().SetPartialParagraph(true);
 
7246
    action->SetPosition(pos);
 
7247
 
 
7248
    // Set the range we'll need to delete in Undo
 
7249
    action->SetRange(wxRichTextRange(pos, pos));
 
7250
 
 
7251
    buffer->SubmitAction(action);
 
7252
 
 
7253
    wxRichTextField* obj = wxDynamicCast(GetLeafObjectAtPosition(pos), wxRichTextField);
 
7254
    return obj;
 
7255
}
 
7256
 
 
7257
/// Get the style that is appropriate for a new paragraph at this position.
 
7258
/// If the previous paragraph has a paragraph style name, look up the next-paragraph
 
7259
/// style.
 
7260
wxRichTextAttr wxRichTextParagraphLayoutBox::GetStyleForNewParagraph(wxRichTextBuffer* buffer, long pos, bool caretPosition, bool lookUpNewParaStyle) const
 
7261
{
 
7262
    wxRichTextParagraph* para = GetParagraphAtPosition(pos, caretPosition);
 
7263
    if (para)
 
7264
    {
 
7265
        wxRichTextAttr attr;
 
7266
        bool foundAttributes = false;
 
7267
 
 
7268
        // Look for a matching paragraph style
 
7269
        if (lookUpNewParaStyle && !para->GetAttributes().GetParagraphStyleName().IsEmpty() && buffer->GetStyleSheet())
 
7270
        {
 
7271
            wxRichTextParagraphStyleDefinition* paraDef = buffer->GetStyleSheet()->FindParagraphStyle(para->GetAttributes().GetParagraphStyleName());
 
7272
            if (paraDef)
 
7273
            {
 
7274
                // If we're not at the end of the paragraph, then we apply THIS style, and not the designated next style.
 
7275
                if (para->GetRange().GetEnd() == pos && !paraDef->GetNextStyle().IsEmpty())
 
7276
                {
 
7277
                    wxRichTextParagraphStyleDefinition* nextParaDef = buffer->GetStyleSheet()->FindParagraphStyle(paraDef->GetNextStyle());
 
7278
                    if (nextParaDef)
 
7279
                    {
 
7280
                        foundAttributes = true;
 
7281
                        attr = nextParaDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
 
7282
                    }
 
7283
                }
 
7284
 
 
7285
                // If we didn't find the 'next style', use this style instead.
 
7286
                if (!foundAttributes)
 
7287
                {
 
7288
                    foundAttributes = true;
 
7289
                    attr = paraDef->GetStyleMergedWithBase(buffer->GetStyleSheet());
 
7290
                }
 
7291
            }
 
7292
        }
 
7293
 
 
7294
        // Also apply list style if present
 
7295
        if (lookUpNewParaStyle && !para->GetAttributes().GetListStyleName().IsEmpty() && buffer->GetStyleSheet())
 
7296
        {
 
7297
            wxRichTextListStyleDefinition* listDef = buffer->GetStyleSheet()->FindListStyle(para->GetAttributes().GetListStyleName());
 
7298
            if (listDef)
 
7299
            {
 
7300
                int thisIndent = para->GetAttributes().GetLeftIndent();
 
7301
                int thisLevel = para->GetAttributes().HasOutlineLevel() ? para->GetAttributes().GetOutlineLevel() : listDef->FindLevelForIndent(thisIndent);
 
7302
 
 
7303
                // Apply the overall list style, and item style for this level
 
7304
                wxRichTextAttr listStyle(listDef->GetCombinedStyleForLevel(thisLevel, buffer->GetStyleSheet()));
 
7305
                wxRichTextApplyStyle(attr, listStyle);
 
7306
                attr.SetOutlineLevel(thisLevel);
 
7307
                if (para->GetAttributes().HasBulletNumber())
 
7308
                    attr.SetBulletNumber(para->GetAttributes().GetBulletNumber());
 
7309
            }
 
7310
        }
 
7311
 
 
7312
        if (!foundAttributes)
 
7313
        {
 
7314
            attr = para->GetAttributes();
 
7315
            int flags = attr.GetFlags();
 
7316
 
 
7317
            // Eliminate character styles
 
7318
            flags &= ( (~ wxTEXT_ATTR_FONT) |
 
7319
                    (~ wxTEXT_ATTR_TEXT_COLOUR) |
 
7320
                    (~ wxTEXT_ATTR_BACKGROUND_COLOUR) );
 
7321
            attr.SetFlags(flags);
 
7322
        }
 
7323
 
 
7324
        return attr;
 
7325
    }
 
7326
    else
 
7327
        return wxRichTextAttr();
 
7328
}
 
7329
 
 
7330
/// Submit command to delete this range
 
7331
bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl)
 
7332
{
 
7333
    return ctrl->GetFocusObject()->DeleteRangeWithUndo(range, ctrl, this);
 
7334
}
 
7335
 
 
7336
/// Submit command to delete this range
 
7337
bool wxRichTextParagraphLayoutBox::DeleteRangeWithUndo(const wxRichTextRange& range, wxRichTextCtrl* ctrl, wxRichTextBuffer* buffer)
 
7338
{
 
7339
    wxRichTextAction* action = new wxRichTextAction(NULL, _("Delete"), wxRICHTEXT_DELETE, buffer, this, ctrl);
 
7340
 
 
7341
    action->SetPosition(ctrl->GetCaretPosition());
 
7342
 
 
7343
    // Set the range to delete
 
7344
    action->SetRange(range);
 
7345
 
 
7346
    // Copy the fragment that we'll need to restore in Undo
 
7347
    CopyFragment(range, action->GetOldParagraphs());
 
7348
 
 
7349
    // See if we're deleting a paragraph marker, in which case we need to
 
7350
    // make a note not to copy the attributes from the 2nd paragraph to the 1st.
 
7351
    if (range.GetStart() == range.GetEnd())
 
7352
    {
 
7353
        wxRichTextParagraph* para = GetParagraphAtPosition(range.GetStart());
 
7354
        if (para && para->GetRange().GetEnd() == range.GetEnd())
 
7355
        {
 
7356
            wxRichTextParagraph* nextPara = GetParagraphAtPosition(range.GetStart()+1);
 
7357
            if (nextPara && nextPara != para)
 
7358
            {
 
7359
                action->GetOldParagraphs().GetChildren().GetFirst()->GetData()->SetAttributes(nextPara->GetAttributes());
 
7360
                action->GetOldParagraphs().GetAttributes().SetFlags(action->GetOldParagraphs().GetAttributes().GetFlags() | wxTEXT_ATTR_KEEP_FIRST_PARA_STYLE);
 
7361
            }
 
7362
        }
 
7363
    }
 
7364
 
 
7365
    buffer->SubmitAction(action);
 
7366
 
 
7367
    return true;
 
7368
}
 
7369
 
 
7370
/// Collapse undo/redo commands
 
7371
bool wxRichTextBuffer::BeginBatchUndo(const wxString& cmdName)
 
7372
{
 
7373
    if (m_batchedCommandDepth == 0)
 
7374
    {
 
7375
        wxASSERT(m_batchedCommand == NULL);
 
7376
        if (m_batchedCommand)
 
7377
        {
 
7378
            GetCommandProcessor()->Store(m_batchedCommand);
 
7379
        }
 
7380
        m_batchedCommand = new wxRichTextCommand(cmdName);
 
7381
    }
 
7382
 
 
7383
    m_batchedCommandDepth ++;
 
7384
 
 
7385
    return true;
 
7386
}
 
7387
 
 
7388
/// Collapse undo/redo commands
 
7389
bool wxRichTextBuffer::EndBatchUndo()
 
7390
{
 
7391
    m_batchedCommandDepth --;
 
7392
 
 
7393
    wxASSERT(m_batchedCommandDepth >= 0);
 
7394
    wxASSERT(m_batchedCommand != NULL);
 
7395
 
 
7396
    if (m_batchedCommandDepth == 0)
 
7397
    {
 
7398
        GetCommandProcessor()->Store(m_batchedCommand);
 
7399
        m_batchedCommand = NULL;
 
7400
    }
 
7401
 
 
7402
    return true;
 
7403
}
 
7404
 
 
7405
/// Submit immediately, or delay according to whether collapsing is on
 
7406
bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action)
 
7407
{
 
7408
    if (action && !action->GetNewParagraphs().IsEmpty())
 
7409
        PrepareContent(action->GetNewParagraphs());
 
7410
 
 
7411
    if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
 
7412
    {
 
7413
        wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
 
7414
        cmd->AddAction(action);
 
7415
        cmd->Do();
 
7416
        cmd->GetActions().Clear();
 
7417
        delete cmd;
 
7418
 
 
7419
        m_batchedCommand->AddAction(action);
 
7420
    }
 
7421
    else
 
7422
    {
 
7423
        wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
 
7424
        cmd->AddAction(action);
 
7425
 
 
7426
        // Only store it if we're not suppressing undo.
 
7427
        return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
 
7428
    }
 
7429
 
 
7430
    return true;
 
7431
}
 
7432
 
 
7433
/// Begin suppressing undo/redo commands.
 
7434
bool wxRichTextBuffer::BeginSuppressUndo()
 
7435
{
 
7436
    m_suppressUndo ++;
 
7437
 
 
7438
    return true;
 
7439
}
 
7440
 
 
7441
/// End suppressing undo/redo commands.
 
7442
bool wxRichTextBuffer::EndSuppressUndo()
 
7443
{
 
7444
    m_suppressUndo --;
 
7445
 
 
7446
    return true;
 
7447
}
 
7448
 
 
7449
/// Begin using a style
 
7450
bool wxRichTextBuffer::BeginStyle(const wxRichTextAttr& style)
 
7451
{
 
7452
    wxRichTextAttr newStyle(GetDefaultStyle());
 
7453
    newStyle.GetTextBoxAttr().Reset();
 
7454
 
 
7455
    // Save the old default style
 
7456
    m_attributeStack.Append((wxObject*) new wxRichTextAttr(newStyle));
 
7457
 
 
7458
    wxRichTextApplyStyle(newStyle, style);
 
7459
    newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags());
 
7460
 
 
7461
    SetDefaultStyle(newStyle);
 
7462
 
 
7463
    return true;
 
7464
}
 
7465
 
 
7466
/// End the style
 
7467
bool wxRichTextBuffer::EndStyle()
 
7468
{
 
7469
    if (!m_attributeStack.GetFirst())
 
7470
    {
 
7471
        wxLogDebug(_("Too many EndStyle calls!"));
 
7472
        return false;
 
7473
    }
 
7474
 
 
7475
    wxList::compatibility_iterator node = m_attributeStack.GetLast();
 
7476
    wxRichTextAttr* attr = (wxRichTextAttr*)node->GetData();
 
7477
    m_attributeStack.Erase(node);
 
7478
 
 
7479
    SetDefaultStyle(*attr);
 
7480
 
 
7481
    delete attr;
 
7482
    return true;
 
7483
}
 
7484
 
 
7485
/// End all styles
 
7486
bool wxRichTextBuffer::EndAllStyles()
 
7487
{
 
7488
    while (m_attributeStack.GetCount() != 0)
 
7489
        EndStyle();
 
7490
    return true;
 
7491
}
 
7492
 
 
7493
/// Clear the style stack
 
7494
void wxRichTextBuffer::ClearStyleStack()
 
7495
{
 
7496
    for (wxList::compatibility_iterator node = m_attributeStack.GetFirst(); node; node = node->GetNext())
 
7497
        delete (wxRichTextAttr*) node->GetData();
 
7498
    m_attributeStack.Clear();
 
7499
}
 
7500
 
 
7501
/// Begin using bold
 
7502
bool wxRichTextBuffer::BeginBold()
 
7503
{
 
7504
    wxRichTextAttr attr;
 
7505
    attr.SetFontWeight(wxFONTWEIGHT_BOLD);
 
7506
 
 
7507
    return BeginStyle(attr);
 
7508
}
 
7509
 
 
7510
/// Begin using italic
 
7511
bool wxRichTextBuffer::BeginItalic()
 
7512
{
 
7513
    wxRichTextAttr attr;
 
7514
    attr.SetFontStyle(wxFONTSTYLE_ITALIC);
 
7515
 
 
7516
    return BeginStyle(attr);
 
7517
}
 
7518
 
 
7519
/// Begin using underline
 
7520
bool wxRichTextBuffer::BeginUnderline()
 
7521
{
 
7522
    wxRichTextAttr attr;
 
7523
    attr.SetFontUnderlined(true);
 
7524
 
 
7525
    return BeginStyle(attr);
 
7526
}
 
7527
 
 
7528
/// Begin using point size
 
7529
bool wxRichTextBuffer::BeginFontSize(int pointSize)
 
7530
{
 
7531
    wxRichTextAttr attr;
 
7532
    attr.SetFontSize(pointSize);
 
7533
 
 
7534
    return BeginStyle(attr);
 
7535
}
 
7536
 
 
7537
/// Begin using this font
 
7538
bool wxRichTextBuffer::BeginFont(const wxFont& font)
 
7539
{
 
7540
    wxRichTextAttr attr;
 
7541
    attr.SetFont(font);
 
7542
 
 
7543
    return BeginStyle(attr);
 
7544
}
 
7545
 
 
7546
/// Begin using this colour
 
7547
bool wxRichTextBuffer::BeginTextColour(const wxColour& colour)
 
7548
{
 
7549
    wxRichTextAttr attr;
 
7550
    attr.SetFlags(wxTEXT_ATTR_TEXT_COLOUR);
 
7551
    attr.SetTextColour(colour);
 
7552
 
 
7553
    return BeginStyle(attr);
 
7554
}
 
7555
 
 
7556
/// Begin using alignment
 
7557
bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment)
 
7558
{
 
7559
    wxRichTextAttr attr;
 
7560
    attr.SetFlags(wxTEXT_ATTR_ALIGNMENT);
 
7561
    attr.SetAlignment(alignment);
 
7562
 
 
7563
    return BeginStyle(attr);
 
7564
}
 
7565
 
 
7566
/// Begin left indent
 
7567
bool wxRichTextBuffer::BeginLeftIndent(int leftIndent, int leftSubIndent)
 
7568
{
 
7569
    wxRichTextAttr attr;
 
7570
    attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
 
7571
    attr.SetLeftIndent(leftIndent, leftSubIndent);
 
7572
 
 
7573
    return BeginStyle(attr);
 
7574
}
 
7575
 
 
7576
/// Begin right indent
 
7577
bool wxRichTextBuffer::BeginRightIndent(int rightIndent)
 
7578
{
 
7579
    wxRichTextAttr attr;
 
7580
    attr.SetFlags(wxTEXT_ATTR_RIGHT_INDENT);
 
7581
    attr.SetRightIndent(rightIndent);
 
7582
 
 
7583
    return BeginStyle(attr);
 
7584
}
 
7585
 
 
7586
/// Begin paragraph spacing
 
7587
bool wxRichTextBuffer::BeginParagraphSpacing(int before, int after)
 
7588
{
 
7589
    long flags = 0;
 
7590
    if (before != 0)
 
7591
        flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
 
7592
    if (after != 0)
 
7593
        flags |= wxTEXT_ATTR_PARA_SPACING_AFTER;
 
7594
 
 
7595
    wxRichTextAttr attr;
 
7596
    attr.SetFlags(flags);
 
7597
    attr.SetParagraphSpacingBefore(before);
 
7598
    attr.SetParagraphSpacingAfter(after);
 
7599
 
 
7600
    return BeginStyle(attr);
 
7601
}
 
7602
 
 
7603
/// Begin line spacing
 
7604
bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing)
 
7605
{
 
7606
    wxRichTextAttr attr;
 
7607
    attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
 
7608
    attr.SetLineSpacing(lineSpacing);
 
7609
 
 
7610
    return BeginStyle(attr);
 
7611
}
 
7612
 
 
7613
/// Begin numbered bullet
 
7614
bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber, int leftIndent, int leftSubIndent, int bulletStyle)
 
7615
{
 
7616
    wxRichTextAttr attr;
 
7617
    attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
 
7618
    attr.SetBulletStyle(bulletStyle);
 
7619
    attr.SetBulletNumber(bulletNumber);
 
7620
    attr.SetLeftIndent(leftIndent, leftSubIndent);
 
7621
 
 
7622
    return BeginStyle(attr);
 
7623
}
 
7624
 
 
7625
/// Begin symbol bullet
 
7626
bool wxRichTextBuffer::BeginSymbolBullet(const wxString& symbol, int leftIndent, int leftSubIndent, int bulletStyle)
 
7627
{
 
7628
    wxRichTextAttr attr;
 
7629
    attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
 
7630
    attr.SetBulletStyle(bulletStyle);
 
7631
    attr.SetLeftIndent(leftIndent, leftSubIndent);
 
7632
    attr.SetBulletText(symbol);
 
7633
 
 
7634
    return BeginStyle(attr);
 
7635
}
 
7636
 
 
7637
/// Begin standard bullet
 
7638
bool wxRichTextBuffer::BeginStandardBullet(const wxString& bulletName, int leftIndent, int leftSubIndent, int bulletStyle)
 
7639
{
 
7640
    wxRichTextAttr attr;
 
7641
    attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_LEFT_INDENT);
 
7642
    attr.SetBulletStyle(bulletStyle);
 
7643
    attr.SetLeftIndent(leftIndent, leftSubIndent);
 
7644
    attr.SetBulletName(bulletName);
 
7645
 
 
7646
    return BeginStyle(attr);
 
7647
}
 
7648
 
 
7649
/// Begin named character style
 
7650
bool wxRichTextBuffer::BeginCharacterStyle(const wxString& characterStyle)
 
7651
{
 
7652
    if (GetStyleSheet())
 
7653
    {
 
7654
        wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
 
7655
        if (def)
 
7656
        {
 
7657
            wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
 
7658
            return BeginStyle(attr);
 
7659
        }
 
7660
    }
 
7661
    return false;
 
7662
}
 
7663
 
 
7664
/// Begin named paragraph style
 
7665
bool wxRichTextBuffer::BeginParagraphStyle(const wxString& paragraphStyle)
 
7666
{
 
7667
    if (GetStyleSheet())
 
7668
    {
 
7669
        wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(paragraphStyle);
 
7670
        if (def)
 
7671
        {
 
7672
            wxRichTextAttr attr = def->GetStyleMergedWithBase(GetStyleSheet());
 
7673
            return BeginStyle(attr);
 
7674
        }
 
7675
    }
 
7676
    return false;
 
7677
}
 
7678
 
 
7679
/// Begin named list style
 
7680
bool wxRichTextBuffer::BeginListStyle(const wxString& listStyle, int level, int number)
 
7681
{
 
7682
    if (GetStyleSheet())
 
7683
    {
 
7684
        wxRichTextListStyleDefinition* def = GetStyleSheet()->FindListStyle(listStyle);
 
7685
        if (def)
 
7686
        {
 
7687
            wxRichTextAttr attr(def->GetCombinedStyleForLevel(level));
 
7688
 
 
7689
            attr.SetBulletNumber(number);
 
7690
 
 
7691
            return BeginStyle(attr);
 
7692
        }
 
7693
    }
 
7694
    return false;
 
7695
}
 
7696
 
 
7697
/// Begin URL
 
7698
bool wxRichTextBuffer::BeginURL(const wxString& url, const wxString& characterStyle)
 
7699
{
 
7700
    wxRichTextAttr attr;
 
7701
 
 
7702
    if (!characterStyle.IsEmpty() && GetStyleSheet())
 
7703
    {
 
7704
        wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
 
7705
        if (def)
 
7706
        {
 
7707
            attr = def->GetStyleMergedWithBase(GetStyleSheet());
 
7708
        }
 
7709
    }
 
7710
    attr.SetURL(url);
 
7711
 
 
7712
    return BeginStyle(attr);
 
7713
}
 
7714
 
 
7715
/// Adds a handler to the end
 
7716
void wxRichTextBuffer::AddHandler(wxRichTextFileHandler *handler)
 
7717
{
 
7718
    sm_handlers.Append(handler);
 
7719
}
 
7720
 
 
7721
/// Inserts a handler at the front
 
7722
void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler *handler)
 
7723
{
 
7724
    sm_handlers.Insert( handler );
 
7725
}
 
7726
 
 
7727
/// Removes a handler
 
7728
bool wxRichTextBuffer::RemoveHandler(const wxString& name)
 
7729
{
 
7730
    wxRichTextFileHandler *handler = FindHandler(name);
 
7731
    if (handler)
 
7732
    {
 
7733
        sm_handlers.DeleteObject(handler);
 
7734
        delete handler;
 
7735
        return true;
 
7736
    }
 
7737
    else
 
7738
        return false;
 
7739
}
 
7740
 
 
7741
/// Finds a handler by filename or, if supplied, type
 
7742
wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename,
 
7743
                                                                   wxRichTextFileType imageType)
 
7744
{
 
7745
    if (imageType != wxRICHTEXT_TYPE_ANY)
 
7746
        return FindHandler(imageType);
 
7747
    else if (!filename.IsEmpty())
 
7748
    {
 
7749
        wxString path, file, ext;
 
7750
        wxFileName::SplitPath(filename, & path, & file, & ext);
 
7751
        return FindHandler(ext, imageType);
 
7752
    }
 
7753
    else
 
7754
        return NULL;
 
7755
}
 
7756
 
 
7757
 
 
7758
/// Finds a handler by name
 
7759
wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& name)
 
7760
{
 
7761
    wxList::compatibility_iterator node = sm_handlers.GetFirst();
 
7762
    while (node)
 
7763
    {
 
7764
        wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
 
7765
        if (handler->GetName().Lower() == name.Lower()) return handler;
 
7766
 
 
7767
        node = node->GetNext();
 
7768
    }
 
7769
    return NULL;
 
7770
}
 
7771
 
 
7772
/// Finds a handler by extension and type
 
7773
wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, wxRichTextFileType type)
 
7774
{
 
7775
    wxList::compatibility_iterator node = sm_handlers.GetFirst();
 
7776
    while (node)
 
7777
    {
 
7778
        wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
 
7779
        if ( handler->GetExtension().Lower() == extension.Lower() &&
 
7780
            (type == wxRICHTEXT_TYPE_ANY || handler->GetType() == type) )
 
7781
            return handler;
 
7782
        node = node->GetNext();
 
7783
    }
 
7784
    return 0;
 
7785
}
 
7786
 
 
7787
/// Finds a handler by type
 
7788
wxRichTextFileHandler* wxRichTextBuffer::FindHandler(wxRichTextFileType type)
 
7789
{
 
7790
    wxList::compatibility_iterator node = sm_handlers.GetFirst();
 
7791
    while (node)
 
7792
    {
 
7793
        wxRichTextFileHandler *handler = (wxRichTextFileHandler *)node->GetData();
 
7794
        if (handler->GetType() == type) return handler;
 
7795
        node = node->GetNext();
 
7796
    }
 
7797
    return NULL;
 
7798
}
 
7799
 
 
7800
void wxRichTextBuffer::InitStandardHandlers()
 
7801
{
 
7802
    if (!FindHandler(wxRICHTEXT_TYPE_TEXT))
 
7803
        AddHandler(new wxRichTextPlainTextHandler);
 
7804
}
 
7805
 
 
7806
void wxRichTextBuffer::CleanUpHandlers()
 
7807
{
 
7808
    wxList::compatibility_iterator node = sm_handlers.GetFirst();
 
7809
    while (node)
 
7810
    {
 
7811
        wxRichTextFileHandler* handler = (wxRichTextFileHandler*)node->GetData();
 
7812
        wxList::compatibility_iterator next = node->GetNext();
 
7813
        delete handler;
 
7814
        node = next;
 
7815
    }
 
7816
 
 
7817
    sm_handlers.Clear();
 
7818
}
 
7819
 
 
7820
wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save, wxArrayInt* types)
 
7821
{
 
7822
    if (types)
 
7823
        types->Clear();
 
7824
 
 
7825
    wxString wildcard;
 
7826
 
 
7827
    wxList::compatibility_iterator node = GetHandlers().GetFirst();
 
7828
    int count = 0;
 
7829
    while (node)
 
7830
    {
 
7831
        wxRichTextFileHandler* handler = (wxRichTextFileHandler*) node->GetData();
 
7832
        if (handler->IsVisible() && ((save && handler->CanSave()) || (!save && handler->CanLoad())))
 
7833
        {
 
7834
            if (combine)
 
7835
            {
 
7836
                if (count > 0)
 
7837
                    wildcard += wxT(";");
 
7838
                wildcard += wxT("*.") + handler->GetExtension();
 
7839
            }
 
7840
            else
 
7841
            {
 
7842
                if (count > 0)
 
7843
                    wildcard += wxT("|");
 
7844
                wildcard += handler->GetName();
 
7845
                wildcard += wxT(" ");
 
7846
                wildcard += _("files");
 
7847
                wildcard += wxT(" (*.");
 
7848
                wildcard += handler->GetExtension();
 
7849
                wildcard += wxT(")|*.");
 
7850
                wildcard += handler->GetExtension();
 
7851
                if (types)
 
7852
                    types->Add(handler->GetType());
 
7853
            }
 
7854
            count ++;
 
7855
        }
 
7856
 
 
7857
        node = node->GetNext();
 
7858
    }
 
7859
 
 
7860
    if (combine)
 
7861
        wildcard = wxT("(") + wildcard + wxT(")|") + wildcard;
 
7862
    return wildcard;
 
7863
}
 
7864
 
 
7865
/// Load a file
 
7866
bool wxRichTextBuffer::LoadFile(const wxString& filename, wxRichTextFileType type)
 
7867
{
 
7868
    wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
 
7869
    if (handler)
 
7870
    {
 
7871
        SetDefaultStyle(wxRichTextAttr());
 
7872
        handler->SetFlags(GetHandlerFlags());
 
7873
        bool success = handler->LoadFile(this, filename);
 
7874
        Invalidate(wxRICHTEXT_ALL);
 
7875
        return success;
 
7876
    }
 
7877
    else
 
7878
        return false;
 
7879
}
 
7880
 
 
7881
/// Save a file
 
7882
bool wxRichTextBuffer::SaveFile(const wxString& filename, wxRichTextFileType type)
 
7883
{
 
7884
    wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
 
7885
    if (handler)
 
7886
    {
 
7887
        handler->SetFlags(GetHandlerFlags());
 
7888
        return handler->SaveFile(this, filename);
 
7889
    }
 
7890
    else
 
7891
        return false;
 
7892
}
 
7893
 
 
7894
/// Load from a stream
 
7895
bool wxRichTextBuffer::LoadFile(wxInputStream& stream, wxRichTextFileType type)
 
7896
{
 
7897
    wxRichTextFileHandler* handler = FindHandler(type);
 
7898
    if (handler)
 
7899
    {
 
7900
        SetDefaultStyle(wxRichTextAttr());
 
7901
        handler->SetFlags(GetHandlerFlags());
 
7902
        bool success = handler->LoadFile(this, stream);
 
7903
        Invalidate(wxRICHTEXT_ALL);
 
7904
        return success;
 
7905
    }
 
7906
    else
 
7907
        return false;
 
7908
}
 
7909
 
 
7910
/// Save to a stream
 
7911
bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, wxRichTextFileType type)
 
7912
{
 
7913
    wxRichTextFileHandler* handler = FindHandler(type);
 
7914
    if (handler)
 
7915
    {
 
7916
        handler->SetFlags(GetHandlerFlags());
 
7917
        return handler->SaveFile(this, stream);
 
7918
    }
 
7919
    else
 
7920
        return false;
 
7921
}
 
7922
 
 
7923
/// Copy the range to the clipboard
 
7924
bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
 
7925
{
 
7926
    bool success = false;
 
7927
    wxRichTextParagraphLayoutBox* container = this;
 
7928
    if (GetRichTextCtrl())
 
7929
        container = GetRichTextCtrl()->GetFocusObject();
 
7930
 
 
7931
#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
 
7932
 
 
7933
    if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
 
7934
    {
 
7935
        wxTheClipboard->Clear();
 
7936
 
 
7937
        // Add composite object
 
7938
 
 
7939
        wxDataObjectComposite* compositeObject = new wxDataObjectComposite();
 
7940
 
 
7941
        {
 
7942
            wxString text = container->GetTextForRange(range);
 
7943
 
 
7944
#ifdef __WXMSW__
 
7945
            text = wxTextFile::Translate(text, wxTextFileType_Dos);
 
7946
#endif
 
7947
 
 
7948
            compositeObject->Add(new wxTextDataObject(text), false /* not preferred */);
 
7949
        }
 
7950
 
 
7951
        // Add rich text buffer data object. This needs the XML handler to be present.
 
7952
 
 
7953
        if (FindHandler(wxRICHTEXT_TYPE_XML))
 
7954
        {
 
7955
            wxRichTextBuffer* richTextBuf = new wxRichTextBuffer;
 
7956
            container->CopyFragment(range, *richTextBuf);
 
7957
 
 
7958
            compositeObject->Add(new wxRichTextBufferDataObject(richTextBuf), true /* preferred */);
 
7959
        }
 
7960
 
 
7961
        if (wxTheClipboard->SetData(compositeObject))
 
7962
            success = true;
 
7963
 
 
7964
        wxTheClipboard->Close();
 
7965
    }
 
7966
 
 
7967
#else
 
7968
    wxUnusedVar(range);
 
7969
#endif
 
7970
    return success;
 
7971
}
 
7972
 
 
7973
/// Paste the clipboard content to the buffer
 
7974
bool wxRichTextBuffer::PasteFromClipboard(long position)
 
7975
{
 
7976
    bool success = false;
 
7977
    wxRichTextParagraphLayoutBox* container = this;
 
7978
    if (GetRichTextCtrl())
 
7979
        container = GetRichTextCtrl()->GetFocusObject();
 
7980
 
 
7981
#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
 
7982
    if (CanPasteFromClipboard())
 
7983
    {
 
7984
        if (wxTheClipboard->Open())
 
7985
        {
 
7986
            if (wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())))
 
7987
            {
 
7988
                wxRichTextBufferDataObject data;
 
7989
                wxTheClipboard->GetData(data);
 
7990
                wxRichTextBuffer* richTextBuffer = data.GetRichTextBuffer();
 
7991
                if (richTextBuffer)
 
7992
                {
 
7993
                    container->InsertParagraphsWithUndo(this, position+1, *richTextBuffer, GetRichTextCtrl(), 0);
 
7994
                    if (GetRichTextCtrl())
 
7995
                        GetRichTextCtrl()->ShowPosition(position + richTextBuffer->GetOwnRange().GetEnd());
 
7996
                    delete richTextBuffer;
 
7997
                }
 
7998
            }
 
7999
            else if (wxTheClipboard->IsSupported(wxDF_TEXT)
 
8000
 #if wxUSE_UNICODE
 
8001
                     || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
 
8002
 #endif
 
8003
                    )
 
8004
            {
 
8005
                wxTextDataObject data;
 
8006
                wxTheClipboard->GetData(data);
 
8007
                wxString text(data.GetText());
 
8008
#ifdef __WXMSW__
 
8009
                wxString text2;
 
8010
                text2.Alloc(text.Length()+1);
 
8011
                size_t i;
 
8012
                for (i = 0; i < text.Length(); i++)
 
8013
                {
 
8014
                    wxChar ch = text[i];
 
8015
                    if (ch != wxT('\r'))
 
8016
                        text2 += ch;
 
8017
                }
 
8018
#else
 
8019
                wxString text2 = text;
 
8020
#endif
 
8021
                container->InsertTextWithUndo(this, position+1, text2, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE);
 
8022
 
 
8023
                if (GetRichTextCtrl())
 
8024
                    GetRichTextCtrl()->ShowPosition(position + text2.Length());
 
8025
 
 
8026
                success = true;
 
8027
            }
 
8028
            else if (wxTheClipboard->IsSupported(wxDF_BITMAP))
 
8029
            {
 
8030
                wxBitmapDataObject data;
 
8031
                wxTheClipboard->GetData(data);
 
8032
                wxBitmap bitmap(data.GetBitmap());
 
8033
                wxImage image(bitmap.ConvertToImage());
 
8034
 
 
8035
                wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, container, GetRichTextCtrl(), false);
 
8036
 
 
8037
                action->GetNewParagraphs().AddImage(image);
 
8038
 
 
8039
                if (action->GetNewParagraphs().GetChildCount() == 1)
 
8040
                    action->GetNewParagraphs().SetPartialParagraph(true);
 
8041
 
 
8042
                action->SetPosition(position+1);
 
8043
 
 
8044
                // Set the range we'll need to delete in Undo
 
8045
                action->SetRange(wxRichTextRange(position+1, position+1));
 
8046
 
 
8047
                SubmitAction(action);
 
8048
 
 
8049
                success = true;
 
8050
            }
 
8051
            wxTheClipboard->Close();
 
8052
        }
 
8053
    }
 
8054
#else
 
8055
    wxUnusedVar(position);
 
8056
#endif
 
8057
    return success;
 
8058
}
 
8059
 
 
8060
/// Can we paste from the clipboard?
 
8061
bool wxRichTextBuffer::CanPasteFromClipboard() const
 
8062
{
 
8063
    bool canPaste = false;
 
8064
#if wxUSE_CLIPBOARD && wxUSE_DATAOBJ
 
8065
    if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open())
 
8066
    {
 
8067
        if (wxTheClipboard->IsSupported(wxDF_TEXT)
 
8068
#if wxUSE_UNICODE
 
8069
            || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)
 
8070
#endif
 
8071
            || wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) ||
 
8072
            wxTheClipboard->IsSupported(wxDF_BITMAP))
 
8073
        {
 
8074
            canPaste = true;
 
8075
        }
 
8076
        wxTheClipboard->Close();
 
8077
    }
 
8078
#endif
 
8079
    return canPaste;
 
8080
}
 
8081
 
 
8082
/// Dumps contents of buffer for debugging purposes
 
8083
void wxRichTextBuffer::Dump()
 
8084
{
 
8085
    wxString text;
 
8086
    {
 
8087
        wxStringOutputStream stream(& text);
 
8088
        wxTextOutputStream textStream(stream);
 
8089
        Dump(textStream);
 
8090
    }
 
8091
 
 
8092
    wxLogDebug(text);
 
8093
}
 
8094
 
 
8095
/// Add an event handler
 
8096
bool wxRichTextBuffer::AddEventHandler(wxEvtHandler* handler)
 
8097
{
 
8098
    m_eventHandlers.Append(handler);
 
8099
    return true;
 
8100
}
 
8101
 
 
8102
/// Remove an event handler
 
8103
bool wxRichTextBuffer::RemoveEventHandler(wxEvtHandler* handler, bool deleteHandler)
 
8104
{
 
8105
    wxList::compatibility_iterator node = m_eventHandlers.Find(handler);
 
8106
    if (node)
 
8107
    {
 
8108
        m_eventHandlers.Erase(node);
 
8109
        if (deleteHandler)
 
8110
            delete handler;
 
8111
 
 
8112
        return true;
 
8113
    }
 
8114
    else
 
8115
        return false;
 
8116
}
 
8117
 
 
8118
/// Clear event handlers
 
8119
void wxRichTextBuffer::ClearEventHandlers()
 
8120
{
 
8121
    m_eventHandlers.Clear();
 
8122
}
 
8123
 
 
8124
/// Send event to event handlers. If sendToAll is true, will send to all event handlers,
 
8125
/// otherwise will stop at the first successful one.
 
8126
bool wxRichTextBuffer::SendEvent(wxEvent& event, bool sendToAll)
 
8127
{
 
8128
    bool success = false;
 
8129
    for (wxList::compatibility_iterator node = m_eventHandlers.GetFirst(); node; node = node->GetNext())
 
8130
    {
 
8131
        wxEvtHandler* handler = (wxEvtHandler*) node->GetData();
 
8132
        if (handler->ProcessEvent(event))
 
8133
        {
 
8134
            success = true;
 
8135
            if (!sendToAll)
 
8136
                return true;
 
8137
        }
 
8138
    }
 
8139
    return success;
 
8140
}
 
8141
 
 
8142
/// Set style sheet and notify of the change
 
8143
bool wxRichTextBuffer::SetStyleSheetAndNotify(wxRichTextStyleSheet* sheet)
 
8144
{
 
8145
    wxRichTextStyleSheet* oldSheet = GetStyleSheet();
 
8146
 
 
8147
    wxWindowID winid = wxID_ANY;
 
8148
    if (GetRichTextCtrl())
 
8149
        winid = GetRichTextCtrl()->GetId();
 
8150
 
 
8151
    wxRichTextEvent event(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACING, winid);
 
8152
    event.SetEventObject(GetRichTextCtrl());
 
8153
    event.SetContainer(GetRichTextCtrl()->GetFocusObject());
 
8154
    event.SetOldStyleSheet(oldSheet);
 
8155
    event.SetNewStyleSheet(sheet);
 
8156
    event.Allow();
 
8157
 
 
8158
    if (SendEvent(event) && !event.IsAllowed())
 
8159
    {
 
8160
        if (sheet != oldSheet)
 
8161
            delete sheet;
 
8162
 
 
8163
        return false;
 
8164
    }
 
8165
 
 
8166
    if (oldSheet && oldSheet != sheet)
 
8167
        delete oldSheet;
 
8168
 
 
8169
    SetStyleSheet(sheet);
 
8170
 
 
8171
    event.SetEventType(wxEVT_COMMAND_RICHTEXT_STYLESHEET_REPLACED);
 
8172
    event.SetOldStyleSheet(NULL);
 
8173
    event.Allow();
 
8174
 
 
8175
    return SendEvent(event);
 
8176
}
 
8177
 
 
8178
/// Set renderer, deleting old one
 
8179
void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer)
 
8180
{
 
8181
    if (sm_renderer)
 
8182
        delete sm_renderer;
 
8183
    sm_renderer = renderer;
 
8184
}
 
8185
 
 
8186
/// Hit-testing: returns a flag indicating hit test details, plus
 
8187
/// information about position
 
8188
int wxRichTextBuffer::HitTest(wxDC& dc, wxRichTextDrawingContext& context, const wxPoint& pt, long& textPosition, wxRichTextObject** obj, wxRichTextObject** contextObj, int flags)
 
8189
{
 
8190
    int ret = wxRichTextParagraphLayoutBox::HitTest(dc, context, pt, textPosition, obj, contextObj, flags);
 
8191
    if (ret != wxRICHTEXT_HITTEST_NONE)
 
8192
    {
 
8193
        return ret;
 
8194
    }
 
8195
    else
 
8196
    {
 
8197
        textPosition = m_ownRange.GetEnd()-1;
 
8198
        *obj = this;
 
8199
        *contextObj = this;
 
8200
        return wxRICHTEXT_HITTEST_AFTER|wxRICHTEXT_HITTEST_OUTSIDE;
 
8201
    }
 
8202
}
 
8203
 
 
8204
void wxRichTextBuffer::SetFontScale(double fontScale)
 
8205
{
 
8206
    m_fontScale = fontScale;
 
8207
    m_fontTable.SetFontScale(fontScale);
 
8208
}
 
8209
 
 
8210
void wxRichTextBuffer::SetDimensionScale(double dimScale)
 
8211
{
 
8212
    m_dimensionScale = dimScale;
 
8213
}
 
8214
 
 
8215
bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& bulletAttr, const wxRect& rect)
 
8216
{
 
8217
    if (bulletAttr.GetTextColour().IsOk())
 
8218
    {
 
8219
        wxCheckSetPen(dc, wxPen(bulletAttr.GetTextColour()));
 
8220
        wxCheckSetBrush(dc, wxBrush(bulletAttr.GetTextColour()));
 
8221
    }
 
8222
    else
 
8223
    {
 
8224
        wxCheckSetPen(dc, *wxBLACK_PEN);
 
8225
        wxCheckSetBrush(dc, *wxBLACK_BRUSH);
 
8226
    }
 
8227
 
 
8228
    wxFont font;
 
8229
    if (bulletAttr.HasFont())
 
8230
    {
 
8231
        font = paragraph->GetBuffer()->GetFontTable().FindFont(bulletAttr);
 
8232
    }
 
8233
    else
 
8234
        font = (*wxNORMAL_FONT);
 
8235
 
 
8236
    wxCheckSetFont(dc, font);
 
8237
 
 
8238
    int charHeight = dc.GetCharHeight();
 
8239
 
 
8240
    int bulletWidth = (int) (((float) charHeight) * wxRichTextBuffer::GetBulletProportion());
 
8241
    int bulletHeight = bulletWidth;
 
8242
 
 
8243
    int x = rect.x;
 
8244
 
 
8245
    // Calculate the top position of the character (as opposed to the whole line height)
 
8246
    int y = rect.y + (rect.height - charHeight);
 
8247
 
 
8248
    // Calculate where the bullet should be positioned
 
8249
    y = y + (charHeight+1)/2 - (bulletHeight+1)/2;
 
8250
 
 
8251
    // The margin between a bullet and text.
 
8252
    int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
 
8253
 
 
8254
    if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
 
8255
        x = rect.x + rect.width - bulletWidth - margin;
 
8256
    else if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
 
8257
        x = x + (rect.width)/2 - bulletWidth/2;
 
8258
 
 
8259
    if (bulletAttr.GetBulletName() == wxT("standard/square"))
 
8260
    {
 
8261
        dc.DrawRectangle(x, y, bulletWidth, bulletHeight);
 
8262
    }
 
8263
    else if (bulletAttr.GetBulletName() == wxT("standard/diamond"))
 
8264
    {
 
8265
        wxPoint pts[5];
 
8266
        pts[0].x = x;                   pts[0].y = y + bulletHeight/2;
 
8267
        pts[1].x = x + bulletWidth/2;   pts[1].y = y;
 
8268
        pts[2].x = x + bulletWidth;     pts[2].y = y + bulletHeight/2;
 
8269
        pts[3].x = x + bulletWidth/2;   pts[3].y = y + bulletHeight;
 
8270
 
 
8271
        dc.DrawPolygon(4, pts);
 
8272
    }
 
8273
    else if (bulletAttr.GetBulletName() == wxT("standard/triangle"))
 
8274
    {
 
8275
        wxPoint pts[3];
 
8276
        pts[0].x = x;                   pts[0].y = y;
 
8277
        pts[1].x = x + bulletWidth;     pts[1].y = y + bulletHeight/2;
 
8278
        pts[2].x = x;                   pts[2].y = y + bulletHeight;
 
8279
 
 
8280
        dc.DrawPolygon(3, pts);
 
8281
    }
 
8282
    else if (bulletAttr.GetBulletName() == wxT("standard/circle-outline"))
 
8283
    {
 
8284
        wxCheckSetBrush(dc, *wxTRANSPARENT_BRUSH);
 
8285
        dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
 
8286
    }
 
8287
    else // "standard/circle", and catch-all
 
8288
    {
 
8289
        dc.DrawEllipse(x, y, bulletWidth, bulletHeight);
 
8290
    }
 
8291
 
 
8292
    return true;
 
8293
}
 
8294
 
 
8295
bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxRichTextAttr& attr, const wxRect& rect, const wxString& text)
 
8296
{
 
8297
    if (!text.empty())
 
8298
    {
 
8299
        wxFont font;
 
8300
        if ((attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL) && !attr.GetBulletFont().IsEmpty() && attr.HasFont())
 
8301
        {
 
8302
            wxRichTextAttr fontAttr;
 
8303
            if (attr.HasFontPixelSize())
 
8304
                fontAttr.SetFontPixelSize(attr.GetFontSize());
 
8305
            else
 
8306
                fontAttr.SetFontPointSize(attr.GetFontSize());
 
8307
            fontAttr.SetFontStyle(attr.GetFontStyle());
 
8308
            fontAttr.SetFontWeight(attr.GetFontWeight());
 
8309
            fontAttr.SetFontUnderlined(attr.GetFontUnderlined());
 
8310
            fontAttr.SetFontFaceName(attr.GetBulletFont());
 
8311
            font = paragraph->GetBuffer()->GetFontTable().FindFont(fontAttr);
 
8312
        }
 
8313
        else if (attr.HasFont())
 
8314
            font = paragraph->GetBuffer()->GetFontTable().FindFont(attr);
 
8315
        else
 
8316
            font = (*wxNORMAL_FONT);
 
8317
 
 
8318
        wxCheckSetFont(dc, font);
 
8319
 
 
8320
        if (attr.GetTextColour().IsOk())
 
8321
            dc.SetTextForeground(attr.GetTextColour());
 
8322
 
 
8323
        dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
 
8324
 
 
8325
        int charHeight = dc.GetCharHeight();
 
8326
        wxCoord tw, th;
 
8327
        dc.GetTextExtent(text, & tw, & th);
 
8328
 
 
8329
        int x = rect.x;
 
8330
 
 
8331
        // Calculate the top position of the character (as opposed to the whole line height)
 
8332
        int y = rect.y + (rect.height - charHeight);
 
8333
 
 
8334
        // The margin between a bullet and text.
 
8335
        int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin());
 
8336
 
 
8337
        if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT)
 
8338
            x = (rect.x + rect.width) - tw - margin;
 
8339
        else if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_CENTRE)
 
8340
            x = x + (rect.width)/2 - tw/2;
 
8341
 
 
8342
        dc.DrawText(text, x, y);
 
8343
 
 
8344
        return true;
 
8345
    }
 
8346
    else
 
8347
        return false;
 
8348
}
 
8349
 
 
8350
bool wxRichTextStdRenderer::DrawBitmapBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& WXUNUSED(dc), const wxRichTextAttr& WXUNUSED(attr), const wxRect& WXUNUSED(rect))
 
8351
{
 
8352
    // Currently unimplemented. The intention is to store bitmaps by name in a media store associated
 
8353
    // with the buffer. The store will allow retrieval from memory, disk or other means.
 
8354
    return false;
 
8355
}
 
8356
 
 
8357
/// Enumerate the standard bullet names currently supported
 
8358
bool wxRichTextStdRenderer::EnumerateStandardBulletNames(wxArrayString& bulletNames)
 
8359
{
 
8360
    bulletNames.Add(wxTRANSLATE("standard/circle"));
 
8361
    bulletNames.Add(wxTRANSLATE("standard/circle-outline"));
 
8362
    bulletNames.Add(wxTRANSLATE("standard/square"));
 
8363
    bulletNames.Add(wxTRANSLATE("standard/diamond"));
 
8364
    bulletNames.Add(wxTRANSLATE("standard/triangle"));
 
8365
 
 
8366
    return true;
 
8367
}
 
8368
 
 
8369
/*!
 
8370
 * wxRichTextBox
 
8371
 */
 
8372
 
 
8373
IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextParagraphLayoutBox)
 
8374
 
 
8375
wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
 
8376
    wxRichTextParagraphLayoutBox(parent)
 
8377
{
 
8378
}
 
8379
 
 
8380
/// Draw the item
 
8381
bool wxRichTextBox::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
 
8382
{
 
8383
    if (!IsShown())
 
8384
        return true;
 
8385
 
 
8386
    // TODO: if the active object in the control, draw an indication.
 
8387
    // We need to add the concept of active object, and not just focus object,
 
8388
    // so we can apply commands (properties, delete, ...) to objects such as text boxes and images.
 
8389
    // Ultimately we would like to be able to interactively resize an active object
 
8390
    // using drag handles.
 
8391
    return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
 
8392
}
 
8393
 
 
8394
/// Copy
 
8395
void wxRichTextBox::Copy(const wxRichTextBox& obj)
 
8396
{
 
8397
    wxRichTextParagraphLayoutBox::Copy(obj);
 
8398
}
 
8399
 
 
8400
// Edit properties via a GUI
 
8401
bool wxRichTextBox::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
 
8402
{
 
8403
    wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Box Properties"));
 
8404
    boxDlg.SetAttributes(GetAttributes());
 
8405
 
 
8406
    if (boxDlg.ShowModal() == wxID_OK)
 
8407
    {
 
8408
        // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
 
8409
        // indeterminate in the object.
 
8410
        boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
 
8411
        return true;
 
8412
    }
 
8413
    else
 
8414
        return false;
 
8415
}
 
8416
 
 
8417
/*!
 
8418
 * wxRichTextField
 
8419
 */
 
8420
 
 
8421
IMPLEMENT_DYNAMIC_CLASS(wxRichTextField, wxRichTextParagraphLayoutBox)
 
8422
 
 
8423
wxRichTextField::wxRichTextField(const wxString& fieldType, wxRichTextObject* parent):
 
8424
    wxRichTextParagraphLayoutBox(parent)
 
8425
{
 
8426
    SetFieldType(fieldType);
 
8427
}
 
8428
 
 
8429
/// Draw the item
 
8430
bool wxRichTextField::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
 
8431
{
 
8432
    if (!IsShown())
 
8433
        return true;
 
8434
 
 
8435
    wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
 
8436
    if (fieldType && fieldType->Draw(this, dc, context, range, selection, rect, descent, style))
 
8437
        return true;
 
8438
 
 
8439
    // Fallback; but don't draw guidelines.
 
8440
    style &= ~wxRICHTEXT_DRAW_GUIDELINES;
 
8441
    return wxRichTextParagraphLayoutBox::Draw(dc, context, range, selection, rect, descent, style);
 
8442
}
 
8443
 
 
8444
bool wxRichTextField::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int style)
 
8445
{
 
8446
    wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
 
8447
    if (fieldType && fieldType->Layout(this, dc, context, rect, parentRect, style))
 
8448
        return true;
 
8449
 
 
8450
    // Fallback
 
8451
    return wxRichTextParagraphLayoutBox::Layout(dc, context, rect, parentRect, style);
 
8452
}
 
8453
 
 
8454
bool wxRichTextField::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
 
8455
{
 
8456
    wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
 
8457
    if (fieldType)
 
8458
        return fieldType->GetRangeSize((wxRichTextField*) this, range, size, descent, dc, context, flags, position, partialExtents);
 
8459
 
 
8460
    return wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position, partialExtents);
 
8461
}
 
8462
 
 
8463
/// Calculate range
 
8464
void wxRichTextField::CalculateRange(long start, long& end)
 
8465
{
 
8466
    if (IsTopLevel())
 
8467
        wxRichTextParagraphLayoutBox::CalculateRange(start, end);
 
8468
    else
 
8469
        wxRichTextObject::CalculateRange(start, end);
 
8470
}
 
8471
 
 
8472
/// Copy
 
8473
void wxRichTextField::Copy(const wxRichTextField& obj)
 
8474
{
 
8475
    wxRichTextParagraphLayoutBox::Copy(obj);
 
8476
 
 
8477
    UpdateField(GetBuffer());
 
8478
}
 
8479
 
 
8480
// Edit properties via a GUI
 
8481
bool wxRichTextField::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
 
8482
{
 
8483
    wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
 
8484
    if (fieldType)
 
8485
        return fieldType->EditProperties(this, parent, buffer);
 
8486
 
 
8487
    return false;
 
8488
}
 
8489
 
 
8490
bool wxRichTextField::CanEditProperties() const
 
8491
{
 
8492
    wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
 
8493
    if (fieldType)
 
8494
        return fieldType->CanEditProperties((wxRichTextField*) this);
 
8495
 
 
8496
    return false;
 
8497
}
 
8498
 
 
8499
wxString wxRichTextField::GetPropertiesMenuLabel() const
 
8500
{
 
8501
    wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
 
8502
    if (fieldType)
 
8503
        return fieldType->GetPropertiesMenuLabel((wxRichTextField*) this);
 
8504
 
 
8505
    return wxEmptyString;
 
8506
}
 
8507
 
 
8508
bool wxRichTextField::UpdateField(wxRichTextBuffer* buffer)
 
8509
{
 
8510
    wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
 
8511
    if (fieldType)
 
8512
        return fieldType->UpdateField(buffer, (wxRichTextField*) this);
 
8513
 
 
8514
    return false;
 
8515
}
 
8516
 
 
8517
bool wxRichTextField::IsTopLevel() const
 
8518
{
 
8519
    wxRichTextFieldType* fieldType = wxRichTextBuffer::FindFieldType(GetFieldType());
 
8520
    if (fieldType)
 
8521
        return fieldType->IsTopLevel((wxRichTextField*) this);
 
8522
 
 
8523
    return true;
 
8524
}
 
8525
 
 
8526
IMPLEMENT_CLASS(wxRichTextFieldType, wxObject)
 
8527
 
 
8528
IMPLEMENT_CLASS(wxRichTextFieldTypeStandard, wxRichTextFieldType)
 
8529
 
 
8530
wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxString& label, int displayStyle)
 
8531
{
 
8532
    Init();
 
8533
 
 
8534
    SetName(name);
 
8535
    SetLabel(label);
 
8536
    SetDisplayStyle(displayStyle);
 
8537
}
 
8538
 
 
8539
wxRichTextFieldTypeStandard::wxRichTextFieldTypeStandard(const wxString& name, const wxBitmap& bitmap, int displayStyle)
 
8540
{
 
8541
    Init();
 
8542
 
 
8543
    SetName(name);
 
8544
    SetBitmap(bitmap);
 
8545
    SetDisplayStyle(displayStyle);
 
8546
}
 
8547
 
 
8548
void wxRichTextFieldTypeStandard::Init()
 
8549
{
 
8550
    m_displayStyle = wxRICHTEXT_FIELD_STYLE_RECTANGLE;
 
8551
    m_font = wxFont(6, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
 
8552
    m_textColour = *wxWHITE;
 
8553
    m_borderColour = *wxBLACK;
 
8554
    m_backgroundColour = *wxBLACK;
 
8555
    m_verticalPadding = 1;
 
8556
    m_horizontalPadding = 3;
 
8557
    m_horizontalMargin = 2;
 
8558
    m_verticalMargin = 0;
 
8559
}
 
8560
 
 
8561
void wxRichTextFieldTypeStandard::Copy(const wxRichTextFieldTypeStandard& field)
 
8562
{
 
8563
    wxRichTextFieldType::Copy(field);
 
8564
 
 
8565
    m_label = field.m_label;
 
8566
    m_displayStyle = field.m_displayStyle;
 
8567
    m_font = field.m_font;
 
8568
    m_textColour = field.m_textColour;
 
8569
    m_borderColour = field.m_borderColour;
 
8570
    m_backgroundColour = field.m_backgroundColour;
 
8571
    m_verticalPadding = field.m_verticalPadding;
 
8572
    m_horizontalPadding = field.m_horizontalPadding;
 
8573
    m_horizontalMargin = field.m_horizontalMargin;
 
8574
    m_bitmap = field.m_bitmap;
 
8575
}
 
8576
 
 
8577
bool wxRichTextFieldTypeStandard::Draw(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int descent, int WXUNUSED(style))
 
8578
{
 
8579
    if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
 
8580
        return false; // USe default composite drawing
 
8581
    else // if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE || m_displayStyle == wxRICHTEXT_FIELD_STYLE_NOBORDER)
 
8582
    {
 
8583
        int borderSize = 1;
 
8584
 
 
8585
        wxPen borderPen(m_borderColour, 1, wxSOLID);
 
8586
        wxBrush backgroundBrush(m_backgroundColour);
 
8587
        wxColour textColour(m_textColour);
 
8588
 
 
8589
        if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
 
8590
        {
 
8591
            wxColour highlightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
 
8592
            wxColour highlightTextColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
 
8593
 
 
8594
            borderPen = wxPen(highlightTextColour, 1, wxSOLID);
 
8595
            backgroundBrush = wxBrush(highlightColour);
 
8596
 
 
8597
            wxCheckSetBrush(dc, backgroundBrush);
 
8598
            wxCheckSetPen(dc, wxPen(highlightColour, 1, wxSOLID));
 
8599
            dc.DrawRectangle(rect);
 
8600
        }
 
8601
 
 
8602
        if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
 
8603
            borderSize = 0;
 
8604
 
 
8605
        // objectRect is the area where the content is drawn, after margins around it have been taken into account
 
8606
        wxRect objectRect = wxRect(wxPoint(rect.x + m_horizontalMargin, rect.y + wxMax(0, rect.height - descent - obj->GetCachedSize().y)),
 
8607
            wxSize(obj->GetCachedSize().x - 2*m_horizontalMargin - borderSize, obj->GetCachedSize().y));
 
8608
 
 
8609
        // clientArea is where the text is actually written
 
8610
        wxRect clientArea = objectRect;
 
8611
 
 
8612
        if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_RECTANGLE)
 
8613
        {
 
8614
            dc.SetPen(borderPen);
 
8615
            dc.SetBrush(backgroundBrush);
 
8616
            dc.DrawRoundedRectangle(objectRect, 4.0);
 
8617
        }
 
8618
        else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG)
 
8619
        {
 
8620
            int arrowLength = objectRect.height/2;
 
8621
            clientArea.width -= (arrowLength - m_horizontalPadding);
 
8622
 
 
8623
            wxPoint pts[5];
 
8624
            pts[0].x = objectRect.x; pts[0].y = objectRect.y;
 
8625
            pts[1].x = objectRect.x + objectRect.width - arrowLength; pts[1].y = objectRect.y;
 
8626
            pts[2].x = objectRect.x + objectRect.width; pts[2].y = objectRect.y + (objectRect.height/2);
 
8627
            pts[3].x = objectRect.x + objectRect.width - arrowLength; pts[3].y = objectRect.y + objectRect.height;
 
8628
            pts[4].x = objectRect.x; pts[4].y = objectRect.y + objectRect.height;
 
8629
            dc.SetPen(borderPen);
 
8630
            dc.SetBrush(backgroundBrush);
 
8631
            dc.DrawPolygon(5, pts);
 
8632
        }
 
8633
        else if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
 
8634
        {
 
8635
            int arrowLength = objectRect.height/2;
 
8636
            clientArea.width -= (arrowLength - m_horizontalPadding);
 
8637
            clientArea.x += (arrowLength - m_horizontalPadding);
 
8638
 
 
8639
            wxPoint pts[5];
 
8640
            pts[0].x = objectRect.x + objectRect.width; pts[0].y = objectRect.y;
 
8641
            pts[1].x = objectRect.x + arrowLength; pts[1].y = objectRect.y;
 
8642
            pts[2].x = objectRect.x; pts[2].y = objectRect.y + (objectRect.height/2);
 
8643
            pts[3].x = objectRect.x + arrowLength; pts[3].y = objectRect.y + objectRect.height;
 
8644
            pts[4].x = objectRect.x + objectRect.width; pts[4].y = objectRect.y + objectRect.height;
 
8645
            dc.SetPen(borderPen);
 
8646
            dc.SetBrush(backgroundBrush);
 
8647
            dc.DrawPolygon(5, pts);
 
8648
        }
 
8649
 
 
8650
        if (m_bitmap.IsOk())
 
8651
        {
 
8652
            int x = clientArea.x + (clientArea.width - m_bitmap.GetWidth())/2;
 
8653
            int y = clientArea.y + m_verticalPadding;
 
8654
            dc.DrawBitmap(m_bitmap, x, y, true);
 
8655
 
 
8656
            if (selection.WithinSelection(obj->GetRange().GetStart(), obj))
 
8657
            {
 
8658
                wxCheckSetBrush(dc, *wxBLACK_BRUSH);
 
8659
                wxCheckSetPen(dc, *wxBLACK_PEN);
 
8660
                dc.SetLogicalFunction(wxINVERT);
 
8661
                dc.DrawRectangle(wxRect(x, y, m_bitmap.GetWidth(), m_bitmap.GetHeight()));
 
8662
                dc.SetLogicalFunction(wxCOPY);
 
8663
            }
 
8664
        }
 
8665
        else
 
8666
        {
 
8667
            wxString label(m_label);
 
8668
            if (label.IsEmpty())
 
8669
                label = wxT("??");
 
8670
            int w, h, maxDescent;
 
8671
            dc.SetFont(m_font);
 
8672
            dc.GetTextExtent(m_label, & w, &h, & maxDescent);
 
8673
            dc.SetTextForeground(textColour);
 
8674
 
 
8675
            int x = clientArea.x + (clientArea.width - w)/2;
 
8676
            int y = clientArea.y + (clientArea.height - (h - maxDescent))/2;
 
8677
            dc.DrawText(m_label, x, y);
 
8678
        }
 
8679
    }
 
8680
 
 
8681
    return true;
 
8682
}
 
8683
 
 
8684
bool wxRichTextFieldTypeStandard::Layout(wxRichTextField* obj, wxDC& dc, wxRichTextDrawingContext& context, const wxRect& WXUNUSED(rect), const wxRect& WXUNUSED(parentRect), int style)
 
8685
{
 
8686
    if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_COMPOSITE)
 
8687
        return false; // USe default composite layout
 
8688
 
 
8689
    wxSize size = GetSize(obj, dc, context, style);
 
8690
    obj->SetCachedSize(size);
 
8691
    obj->SetMinSize(size);
 
8692
    obj->SetMaxSize(size);
 
8693
    return true;
 
8694
}
 
8695
 
 
8696
bool wxRichTextFieldTypeStandard::GetRangeSize(wxRichTextField* obj, const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
 
8697
{
 
8698
    if (IsTopLevel(obj))
 
8699
        return obj->wxRichTextParagraphLayoutBox::GetRangeSize(range, size, descent, dc, context, flags, position);
 
8700
    else
 
8701
    {
 
8702
        wxSize sz = GetSize(obj, dc, context, 0);
 
8703
        if (partialExtents)
 
8704
        {
 
8705
            int lastSize;
 
8706
            if (partialExtents->GetCount() > 0)
 
8707
                lastSize = (*partialExtents)[partialExtents->GetCount()-1];
 
8708
            else
 
8709
                lastSize = 0;
 
8710
            partialExtents->Add(lastSize + sz.x);
 
8711
        }
 
8712
        size = sz;
 
8713
       return true;
 
8714
    }
 
8715
}
 
8716
 
 
8717
wxSize wxRichTextFieldTypeStandard::GetSize(wxRichTextField* WXUNUSED(obj), wxDC& dc, wxRichTextDrawingContext& WXUNUSED(context), int WXUNUSED(style)) const
 
8718
{
 
8719
    int borderSize = 1;
 
8720
    int w = 0, h = 0, maxDescent = 0;
 
8721
 
 
8722
    wxSize sz;
 
8723
    if (m_bitmap.IsOk())
 
8724
    {
 
8725
        w = m_bitmap.GetWidth();
 
8726
        h = m_bitmap.GetHeight();
 
8727
 
 
8728
        sz = wxSize(w + m_horizontalMargin*2, h + m_verticalMargin*2);
 
8729
    }
 
8730
    else
 
8731
    {
 
8732
        wxString label(m_label);
 
8733
        if (label.IsEmpty())
 
8734
            label = wxT("??");
 
8735
        dc.SetFont(m_font);
 
8736
        dc.GetTextExtent(label, & w, &h, & maxDescent);
 
8737
 
 
8738
        sz = wxSize(w + m_horizontalPadding*2 + m_horizontalMargin*2, h + m_verticalPadding *2 + m_verticalMargin*2);
 
8739
    }
 
8740
 
 
8741
    if (m_displayStyle != wxRICHTEXT_FIELD_STYLE_NO_BORDER)
 
8742
    {
 
8743
        sz.x += borderSize*2;
 
8744
        sz.y += borderSize*2;
 
8745
    }
 
8746
 
 
8747
    if (m_displayStyle == wxRICHTEXT_FIELD_STYLE_START_TAG || m_displayStyle == wxRICHTEXT_FIELD_STYLE_END_TAG)
 
8748
    {
 
8749
        // Add space for the arrow
 
8750
        sz.x += (sz.y/2 - m_horizontalPadding);
 
8751
    }
 
8752
 
 
8753
    return sz;
 
8754
}
 
8755
 
 
8756
IMPLEMENT_DYNAMIC_CLASS(wxRichTextCell, wxRichTextBox)
 
8757
 
 
8758
wxRichTextCell::wxRichTextCell(wxRichTextObject* parent):
 
8759
    wxRichTextBox(parent)
 
8760
{
 
8761
}
 
8762
 
 
8763
/// Draw the item
 
8764
bool wxRichTextCell::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
 
8765
{
 
8766
    return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
 
8767
}
 
8768
 
 
8769
/// Copy
 
8770
void wxRichTextCell::Copy(const wxRichTextCell& obj)
 
8771
{
 
8772
    wxRichTextBox::Copy(obj);
 
8773
}
 
8774
 
 
8775
// Edit properties via a GUI
 
8776
bool wxRichTextCell::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
 
8777
{
 
8778
    // We need to gather common attributes for all selected cells.
 
8779
 
 
8780
    wxRichTextTable* table = wxDynamicCast(GetParent(), wxRichTextTable);
 
8781
    bool multipleCells = false;
 
8782
    wxRichTextAttr attr;
 
8783
 
 
8784
    if (table && buffer && buffer->GetRichTextCtrl() && buffer->GetRichTextCtrl()->GetSelection().IsValid() &&
 
8785
        buffer->GetRichTextCtrl()->GetSelection().GetContainer() == GetParent())
 
8786
    {
 
8787
        wxRichTextAttr clashingAttr, absentAttr;
 
8788
        const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
 
8789
        size_t i;
 
8790
        int selectedCellCount = 0;
 
8791
        for (i = 0; i < sel.GetCount(); i++)
 
8792
        {
 
8793
            const wxRichTextRange& range = sel[i];
 
8794
            wxRichTextCell* cell = table->GetCell(range.GetStart());
 
8795
            if (cell)
 
8796
            {
 
8797
                wxRichTextAttr cellStyle = cell->GetAttributes();
 
8798
 
 
8799
                CollectStyle(attr, cellStyle, clashingAttr, absentAttr);
 
8800
 
 
8801
                selectedCellCount ++;
 
8802
            }
 
8803
        }
 
8804
        multipleCells = selectedCellCount > 1;
 
8805
    }
 
8806
    else
 
8807
    {
 
8808
        attr = GetAttributes();
 
8809
    }
 
8810
 
 
8811
    wxString caption;
 
8812
    if (multipleCells)
 
8813
        caption = _("Multiple Cell Properties");
 
8814
    else
 
8815
        caption = _("Cell Properties");
 
8816
 
 
8817
    wxRichTextObjectPropertiesDialog cellDlg(this, wxGetTopLevelParent(parent), wxID_ANY, caption);
 
8818
    cellDlg.SetAttributes(attr);
 
8819
 
 
8820
    wxRichTextSizePage* sizePage = wxDynamicCast(cellDlg.FindPage(wxCLASSINFO(wxRichTextSizePage)), wxRichTextSizePage);
 
8821
    if (sizePage)
 
8822
    {
 
8823
        // We don't want position and floating controls for a cell.
 
8824
        sizePage->ShowPositionControls(false);
 
8825
        sizePage->ShowFloatingControls(false);
 
8826
    }
 
8827
 
 
8828
    if (cellDlg.ShowModal() == wxID_OK)
 
8829
    {
 
8830
        if (multipleCells)
 
8831
        {
 
8832
            const wxRichTextSelection& sel = buffer->GetRichTextCtrl()->GetSelection();
 
8833
            // Apply the style; we interpret indeterminate attributes as 'don't touch this attribute'
 
8834
            // since it may represent clashing attributes across multiple objects.
 
8835
            table->SetCellStyle(sel, attr);
 
8836
        }
 
8837
        else
 
8838
            // For a single object, indeterminate attributes set by the user should be reflected in the
 
8839
            // actual object style, so pass the wxRICHTEXT_SETSTYLE_RESET flag to assign
 
8840
            // the style directly instead of applying (which ignores indeterminate attributes,
 
8841
            // leaving them as they were).
 
8842
            cellDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
 
8843
        return true;
 
8844
    }
 
8845
    else
 
8846
        return false;
 
8847
}
 
8848
 
 
8849
WX_DEFINE_OBJARRAY(wxRichTextObjectPtrArrayArray)
 
8850
 
 
8851
IMPLEMENT_DYNAMIC_CLASS(wxRichTextTable, wxRichTextBox)
 
8852
 
 
8853
wxRichTextTable::wxRichTextTable(wxRichTextObject* parent): wxRichTextBox(parent)
 
8854
{
 
8855
    m_rowCount = 0;
 
8856
    m_colCount = 0;
 
8857
}
 
8858
 
 
8859
// Draws the object.
 
8860
bool wxRichTextTable::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& range, const wxRichTextSelection& selection, const wxRect& rect, int descent, int style)
 
8861
{
 
8862
    return wxRichTextBox::Draw(dc, context, range, selection, rect, descent, style);
 
8863
}
 
8864
 
 
8865
WX_DECLARE_OBJARRAY(wxRect, wxRichTextRectArray);
 
8866
WX_DEFINE_OBJARRAY(wxRichTextRectArray);
 
8867
 
 
8868
// Lays the object out. rect is the space available for layout. Often it will
 
8869
// be the specified overall space for this object, if trying to constrain
 
8870
// layout to a particular size, or it could be the total space available in the
 
8871
// parent. rect is the overall size, so we must subtract margins and padding.
 
8872
// to get the actual available space.
 
8873
bool wxRichTextTable::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int style)
 
8874
{
 
8875
    SetPosition(rect.GetPosition());
 
8876
 
 
8877
    // TODO: the meaty bit. Calculate sizes of all cells and rows. Try to use
 
8878
    // minimum size if within alloted size, then divide up remaining size
 
8879
    // between rows/cols.
 
8880
 
 
8881
    double scale = 1.0;
 
8882
    wxRichTextBuffer* buffer = GetBuffer();
 
8883
    if (buffer) scale = buffer->GetScale();
 
8884
 
 
8885
    wxRect availableSpace = GetAvailableContentArea(dc, context, rect);
 
8886
    wxTextAttrDimensionConverter converter(dc, scale, availableSpace.GetSize());
 
8887
 
 
8888
    wxRichTextAttr attr(GetAttributes());
 
8889
    context.ApplyVirtualAttributes(attr, this);
 
8890
 
 
8891
    // If we have no fixed table size, and assuming we're not pushed for
 
8892
    // space, then we don't have to try to stretch the table to fit the contents.
 
8893
    bool stretchToFitTableWidth = false;
 
8894
 
 
8895
    int tableWidth = rect.width;
 
8896
    if (attr.GetTextBoxAttr().GetWidth().IsValid())
 
8897
    {
 
8898
        tableWidth = converter.GetPixels(attr.GetTextBoxAttr().GetWidth());
 
8899
 
 
8900
        // Fixed table width, so we do want to stretch columns out if necessary.
 
8901
        stretchToFitTableWidth = true;
 
8902
 
 
8903
        // Shouldn't be able to exceed the size passed to this function
 
8904
        tableWidth = wxMin(rect.width, tableWidth);
 
8905
    }
 
8906
 
 
8907
    // Get internal padding
 
8908
    int paddingLeft = 0, paddingTop = 0;
 
8909
    if (attr.GetTextBoxAttr().GetPadding().GetLeft().IsValid())
 
8910
        paddingLeft = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetLeft());
 
8911
    if (attr.GetTextBoxAttr().GetPadding().GetTop().IsValid())
 
8912
        paddingTop = converter.GetPixels(attr.GetTextBoxAttr().GetPadding().GetTop());
 
8913
 
 
8914
    // Assume that left and top padding are also used for inter-cell padding.
 
8915
    int paddingX = paddingLeft;
 
8916
    int paddingY = paddingTop;
 
8917
 
 
8918
    int totalLeftMargin = 0, totalRightMargin = 0, totalTopMargin = 0, totalBottomMargin = 0;
 
8919
    GetTotalMargin(dc, buffer, attr, totalLeftMargin, totalRightMargin, totalTopMargin, totalBottomMargin);
 
8920
 
 
8921
    // Internal table width - the area for content
 
8922
    int internalTableWidth = tableWidth - totalLeftMargin - totalRightMargin;
 
8923
 
 
8924
    int rowCount = m_cells.GetCount();
 
8925
    if (m_colCount == 0 || rowCount == 0)
 
8926
    {
 
8927
        wxRect overallRect(rect.x, rect.y, totalLeftMargin + totalRightMargin, totalTopMargin + totalBottomMargin);
 
8928
        SetCachedSize(overallRect.GetSize());
 
8929
 
 
8930
        // Zero content size
 
8931
        SetMinSize(overallRect.GetSize());
 
8932
        SetMaxSize(GetMinSize());
 
8933
        return true;
 
8934
    }
 
8935
 
 
8936
    // The final calculated widths
 
8937
    wxArrayInt colWidths;
 
8938
    colWidths.Add(0, m_colCount);
 
8939
 
 
8940
    wxArrayInt absoluteColWidths;
 
8941
    absoluteColWidths.Add(0, m_colCount);
 
8942
 
 
8943
    wxArrayInt percentageColWidths;
 
8944
    percentageColWidths.Add(0, m_colCount);
 
8945
    // wxArrayInt percentageColWidthsSpanning(m_colCount);
 
8946
    // These are only relevant when the first column contains spanning information.
 
8947
    // wxArrayInt columnSpans(m_colCount); // Each contains 1 for non-spanning cell, > 1 for spanning cell.
 
8948
    wxArrayInt maxColWidths;
 
8949
    maxColWidths.Add(0, m_colCount);
 
8950
    wxArrayInt minColWidths;
 
8951
    minColWidths.Add(0, m_colCount);
 
8952
 
 
8953
    wxSize tableSize(tableWidth, 0);
 
8954
 
 
8955
    int i, j, k;
 
8956
 
 
8957
    for (i = 0; i < m_colCount; i++)
 
8958
    {
 
8959
        absoluteColWidths[i] = 0;
 
8960
        // absoluteColWidthsSpanning[i] = 0;
 
8961
        percentageColWidths[i] = -1;
 
8962
        // percentageColWidthsSpanning[i] = -1;
 
8963
        colWidths[i] = 0;
 
8964
        maxColWidths[i] = 0;
 
8965
        minColWidths[i] = 0;
 
8966
        // columnSpans[i] = 1;
 
8967
    }
 
8968
 
 
8969
    // (0) Determine which cells are visible according to spans
 
8970
    //   1  2   3  4   5
 
8971
    //  __________________
 
8972
    // |  |   |      |    | 1
 
8973
    // |------|      |----|
 
8974
    // |------|      |    | 2
 
8975
    // |------|      |    | 3
 
8976
    // |------------------|
 
8977
    // |__________________| 4
 
8978
 
 
8979
    // To calculate cell visibility:
 
8980
    // First find all spanning cells. Build an array of span records with start x, y and end x, y.
 
8981
    // Then for each cell, test whether we're within one of those cells, and unless we're at the start of
 
8982
    // that cell, hide the cell.
 
8983
 
 
8984
    // We can also use this array to match the size of spanning cells to the grid. Or just do
 
8985
    // this when we iterate through all cells.
 
8986
 
 
8987
    // 0.1: add spanning cells to an array
 
8988
    wxRichTextRectArray rectArray;
 
8989
    for (j = 0; j < m_rowCount; j++)
 
8990
    {
 
8991
        for (i = 0; i < m_colCount; i++)
 
8992
        {
 
8993
            wxRichTextBox* cell = GetCell(j, i);
 
8994
            int colSpan = 1, rowSpan = 1;
 
8995
            if (cell->GetProperties().HasProperty(wxT("colspan")))
 
8996
                colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
 
8997
            if (cell->GetProperties().HasProperty(wxT("rowspan")))
 
8998
                rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
 
8999
            if (colSpan > 1 || rowSpan > 1)
 
9000
            {
 
9001
                rectArray.Add(wxRect(i, j, colSpan, rowSpan));
 
9002
            }
 
9003
        }
 
9004
    }
 
9005
    // 0.2: find which cells are subsumed by a spanning cell
 
9006
    for (j = 0; j < m_rowCount; j++)
 
9007
    {
 
9008
        for (i = 0; i < m_colCount; i++)
 
9009
        {
 
9010
            wxRichTextBox* cell = GetCell(j, i);
 
9011
            if (rectArray.GetCount() == 0)
 
9012
            {
 
9013
                cell->Show(true);
 
9014
            }
 
9015
            else
 
9016
            {
 
9017
                int colSpan = 1, rowSpan = 1;
 
9018
                if (cell->GetProperties().HasProperty(wxT("colspan")))
 
9019
                    colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
 
9020
                if (cell->GetProperties().HasProperty(wxT("rowspan")))
 
9021
                    rowSpan = cell->GetProperties().GetPropertyLong(wxT("rowspan"));
 
9022
                if (colSpan > 1 || rowSpan > 1)
 
9023
                {
 
9024
                    // Assume all spanning cells are shown
 
9025
                    cell->Show(true);
 
9026
                }
 
9027
                else
 
9028
                {
 
9029
                    bool shown = true;
 
9030
                    for (k = 0; k < (int) rectArray.GetCount(); k++)
 
9031
                    {
 
9032
                        if (rectArray[k].Contains(wxPoint(i, j)))
 
9033
                        {
 
9034
                            shown = false;
 
9035
                            break;
 
9036
                        }
 
9037
                    }
 
9038
                    cell->Show(shown);
 
9039
                }
 
9040
            }
 
9041
        }
 
9042
    }
 
9043
 
 
9044
    // TODO: find the first spanned cell in each row that spans the most columns and doesn't
 
9045
    // overlap with a spanned cell starting at a previous column position.
 
9046
    // This means we need to keep an array of rects so we can check. However
 
9047
    // it does also mean that some spans simply may not be taken into account
 
9048
    // where there are different spans happening on different rows. In these cases,
 
9049
    // they will simply be as wide as their constituent columns.
 
9050
 
 
9051
    // (1) Do an initial layout for all cells to get minimum and maximum size, and get
 
9052
    // the absolute or percentage width of each column.
 
9053
 
 
9054
    for (j = 0; j < m_rowCount; j++)
 
9055
    {
 
9056
        // First get the overall margins so we can calculate percentage widths based on
 
9057
        // the available content space for all cells on the row
 
9058
 
 
9059
        int overallRowContentMargin = 0;
 
9060
        int visibleCellCount = 0;
 
9061
 
 
9062
        for (i = 0; i < m_colCount; i++)
 
9063
        {
 
9064
            wxRichTextBox* cell = GetCell(j, i);
 
9065
            if (cell->IsShown())
 
9066
            {
 
9067
                int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
 
9068
                GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
 
9069
 
 
9070
                overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
 
9071
                visibleCellCount ++;
 
9072
            }
 
9073
        }
 
9074
 
 
9075
        // Add in inter-cell padding
 
9076
        overallRowContentMargin += ((visibleCellCount-1) * paddingX);
 
9077
 
 
9078
        int rowContentWidth = internalTableWidth - overallRowContentMargin;
 
9079
        wxSize rowTableSize(rowContentWidth, 0);
 
9080
        wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
 
9081
 
 
9082
        for (i = 0; i < m_colCount; i++)
 
9083
        {
 
9084
            wxRichTextBox* cell = GetCell(j, i);
 
9085
            if (cell->IsShown())
 
9086
            {
 
9087
                int colSpan = 1;
 
9088
                if (cell->GetProperties().HasProperty(wxT("colspan")))
 
9089
                    colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
 
9090
 
 
9091
                // Lay out cell to find min/max widths
 
9092
                cell->Invalidate(wxRICHTEXT_ALL);
 
9093
                cell->Layout(dc, context, availableSpace, availableSpace, style);
 
9094
 
 
9095
                if (colSpan == 1)
 
9096
                {
 
9097
                    int absoluteCellWidth = -1;
 
9098
                    int percentageCellWidth = -1;
 
9099
 
 
9100
                    // I think we need to calculate percentages from the internal table size,
 
9101
                    // minus the padding between cells which we'll need to calculate from the
 
9102
                    // (number of VISIBLE cells - 1)*paddingX. Then percentages that add up to 100%
 
9103
                    // will add up to 100%. In CSS, the width specifies the cell's content rect width,
 
9104
                    // so if we want to conform to that we'll need to add in the overall cell margins.
 
9105
                    // However, this will make it difficult to specify percentages that add up to
 
9106
                    // 100% and still fit within the table width.
 
9107
                    // Let's say two cells have 50% width. They have 10 pixels of overall margin each.
 
9108
                    // The table content rect is 500 pixels and the inter-cell padding is 20 pixels.
 
9109
                    // If we're using internal content size for the width, we would calculate the
 
9110
                    // the overall cell width for n cells as:
 
9111
                    // (500 - 20*(n-1) - overallCellMargin1 - overallCellMargin2 - ...) * percentage / 100
 
9112
                    // + thisOverallCellMargin
 
9113
                    // = 500 - 20 - 10 - 10) * 0.5 + 10 = 240 pixels overall cell width.
 
9114
                    // Adding this back, we get 240 + 240 + 20 = 500 pixels.
 
9115
 
 
9116
                    if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
 
9117
                    {
 
9118
                        int w = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
 
9119
                        if (cell->GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
 
9120
                        {
 
9121
                            percentageCellWidth = w;
 
9122
                        }
 
9123
                        else
 
9124
                        {
 
9125
                            absoluteCellWidth = w;
 
9126
                        }
 
9127
                        // Override absolute width with minimum width if necessary
 
9128
                        if (cell->GetMinSize().x > 0 && absoluteCellWidth !=1 && cell->GetMinSize().x > absoluteCellWidth)
 
9129
                            absoluteCellWidth = cell->GetMinSize().x;
 
9130
                    }
 
9131
 
 
9132
                    if (absoluteCellWidth != -1)
 
9133
                    {
 
9134
                        if (absoluteCellWidth > absoluteColWidths[i])
 
9135
                            absoluteColWidths[i] = absoluteCellWidth;
 
9136
                    }
 
9137
 
 
9138
                    if (percentageCellWidth != -1)
 
9139
                    {
 
9140
                        if (percentageCellWidth > percentageColWidths[i])
 
9141
                            percentageColWidths[i] = percentageCellWidth;
 
9142
                    }
 
9143
 
 
9144
                    if (colSpan == 1 && cell->GetMinSize().x && cell->GetMinSize().x > minColWidths[i])
 
9145
                        minColWidths[i] = cell->GetMinSize().x;
 
9146
                    if (colSpan == 1 && cell->GetMaxSize().x && cell->GetMaxSize().x > maxColWidths[i])
 
9147
                        maxColWidths[i] = cell->GetMaxSize().x;
 
9148
                }
 
9149
            }
 
9150
        }
 
9151
    }
 
9152
 
 
9153
    // (2) Allocate initial column widths from minimum widths, absolute values and proportions
 
9154
    // TODO: simply merge this into (1).
 
9155
    for (i = 0; i < m_colCount; i++)
 
9156
    {
 
9157
        if (absoluteColWidths[i] > 0)
 
9158
        {
 
9159
            colWidths[i] = absoluteColWidths[i];
 
9160
        }
 
9161
        else if (percentageColWidths[i] > 0)
 
9162
        {
 
9163
            colWidths[i] = percentageColWidths[i];
 
9164
 
 
9165
            // This is rubbish - we calculated the absolute widths from percentages, so
 
9166
            // we can't do it again here.
 
9167
            //colWidths[i] = (int) (double(percentageColWidths[i]) * double(tableWidth) / 100.0 + 0.5);
 
9168
        }
 
9169
    }
 
9170
 
 
9171
    // (3) Process absolute or proportional widths of spanning columns,
 
9172
    // now that we know what our fixed column widths are going to be.
 
9173
    // Spanned cells will try to adjust columns so the span will fit.
 
9174
    // Even existing fixed column widths can be expanded if necessary.
 
9175
    // Actually, currently fixed columns widths aren't adjusted; instead,
 
9176
    // the algorithm favours earlier rows and adjusts unspecified column widths
 
9177
    // the first time only. After that, we can't know whether the column has been
 
9178
    // specified explicitly or not. (We could make a note if necessary.)
 
9179
    for (j = 0; j < m_rowCount; j++)
 
9180
    {
 
9181
        // First get the overall margins so we can calculate percentage widths based on
 
9182
        // the available content space for all cells on the row
 
9183
 
 
9184
        int overallRowContentMargin = 0;
 
9185
        int visibleCellCount = 0;
 
9186
 
 
9187
        for (i = 0; i < m_colCount; i++)
 
9188
        {
 
9189
            wxRichTextBox* cell = GetCell(j, i);
 
9190
            if (cell->IsShown())
 
9191
            {
 
9192
                int cellTotalLeftMargin = 0, cellTotalRightMargin = 0, cellTotalTopMargin = 0, cellTotalBottomMargin = 0;
 
9193
                GetTotalMargin(dc, buffer, cell->GetAttributes(), cellTotalLeftMargin, cellTotalRightMargin, cellTotalTopMargin, cellTotalBottomMargin);
 
9194
 
 
9195
                overallRowContentMargin += (cellTotalLeftMargin + cellTotalRightMargin);
 
9196
                visibleCellCount ++;
 
9197
            }
 
9198
        }
 
9199
 
 
9200
        // Add in inter-cell padding
 
9201
        overallRowContentMargin += ((visibleCellCount-1) * paddingX);
 
9202
 
 
9203
        int rowContentWidth = internalTableWidth - overallRowContentMargin;
 
9204
        wxSize rowTableSize(rowContentWidth, 0);
 
9205
        wxTextAttrDimensionConverter converter(dc, scale, rowTableSize);
 
9206
 
 
9207
        for (i = 0; i < m_colCount; i++)
 
9208
        {
 
9209
            wxRichTextBox* cell = GetCell(j, i);
 
9210
            if (cell->IsShown())
 
9211
            {
 
9212
                int colSpan = 1;
 
9213
                if (cell->GetProperties().HasProperty(wxT("colspan")))
 
9214
                    colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
 
9215
 
 
9216
                if (colSpan > 1)
 
9217
                {
 
9218
                    int spans = wxMin(colSpan, m_colCount - i);
 
9219
                    int cellWidth = 0;
 
9220
                    if (spans > 0)
 
9221
                    {
 
9222
                        if (cell->GetAttributes().GetTextBoxAttr().GetWidth().IsValid())
 
9223
                        {
 
9224
                            cellWidth = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetWidth());
 
9225
                            // Override absolute width with minimum width if necessary
 
9226
                            if (cell->GetMinSize().x > 0 && cellWidth !=1 && cell->GetMinSize().x > cellWidth)
 
9227
                                cellWidth = cell->GetMinSize().x;
 
9228
                        }
 
9229
                        else
 
9230
                        {
 
9231
                            // Do we want to do this? It's the only chance we get to
 
9232
                            // use the cell's min/max sizes, so we need to work out
 
9233
                            // how we're going to balance the unspecified spanning cell
 
9234
                            // width with the possibility more-constrained constituent cell widths.
 
9235
                            // Say there's a tiny bitmap giving it a max width of 10 pixels. We
 
9236
                            // don't want to constraint all the spanned columns to fit into this cell.
 
9237
                            // OK, let's say that if any of the constituent columns don't fit,
 
9238
                            // then we simply stop constraining the columns; instead, we'll just fit the spanning
 
9239
                            // cells to the columns later.
 
9240
                            cellWidth = cell->GetMinSize().x;
 
9241
                            if (cell->GetMaxSize().x > cellWidth)
 
9242
                                cellWidth = cell->GetMaxSize().x;
 
9243
                        }
 
9244
 
 
9245
                        // Subtract the padding between cells
 
9246
                        int spanningWidth = cellWidth;
 
9247
                        spanningWidth -= paddingX * (spans-1);
 
9248
 
 
9249
                        if (spanningWidth > 0)
 
9250
                        {
 
9251
                            // Now share the spanning width between columns within that span
 
9252
                            // TODO: take into account min widths of columns within the span
 
9253
                            int spanningWidthLeft = spanningWidth;
 
9254
                            int stretchColCount = 0;
 
9255
                            for (k = i; k < (i+spans); k++)
 
9256
                            {
 
9257
                                if (colWidths[k] > 0) // absolute or proportional width has been specified
 
9258
                                    spanningWidthLeft -= colWidths[k];
 
9259
                                else
 
9260
                                    stretchColCount ++;
 
9261
                            }
 
9262
                            // Now divide what's left between the remaining columns
 
9263
                            int colShare = 0;
 
9264
                            if (stretchColCount > 0)
 
9265
                                colShare = spanningWidthLeft / stretchColCount;
 
9266
                            int colShareRemainder = spanningWidthLeft - (colShare * stretchColCount);
 
9267
 
 
9268
                            // If fixed-width columns are currently too big, then we'll later
 
9269
                            // stretch the spanned cell to fit.
 
9270
 
 
9271
                            if (spanningWidthLeft > 0)
 
9272
                            {
 
9273
                                for (k = i; k < (i+spans); k++)
 
9274
                                {
 
9275
                                    if (colWidths[k] <= 0) // absolute or proportional width has not been specified
 
9276
                                    {
 
9277
                                        int newWidth = colShare;
 
9278
                                        if (k == (i+spans-1))
 
9279
                                            newWidth += colShareRemainder; // ensure all pixels are filled
 
9280
                                        colWidths[k] = newWidth;
 
9281
                                    }
 
9282
                                }
 
9283
                            }
 
9284
                        }
 
9285
                    }
 
9286
                }
 
9287
            }
 
9288
        }
 
9289
    }
 
9290
 
 
9291
    // (4) Next, share any remaining space out between columns that have not yet been calculated.
 
9292
    // TODO: take into account min widths of columns within the span
 
9293
    int tableWidthMinusPadding = internalTableWidth - (m_colCount-1)*paddingX;
 
9294
    int widthLeft = tableWidthMinusPadding;
 
9295
    int stretchColCount = 0;
 
9296
    for (i = 0; i < m_colCount; i++)
 
9297
    {
 
9298
        // TODO: we need to take into account min widths.
 
9299
        // Subtract min width from width left, then
 
9300
        // add the colShare to the min width
 
9301
        if (colWidths[i] > 0) // absolute or proportional width has been specified
 
9302
            widthLeft -= colWidths[i];
 
9303
        else
 
9304
        {
 
9305
            if (minColWidths[i] > 0)
 
9306
                widthLeft -= minColWidths[i];
 
9307
 
 
9308
            stretchColCount ++;
 
9309
        }
 
9310
    }
 
9311
 
 
9312
    // Now divide what's left between the remaining columns
 
9313
    int colShare = 0;
 
9314
    if (stretchColCount > 0)
 
9315
        colShare = widthLeft / stretchColCount;
 
9316
    int colShareRemainder = widthLeft - (colShare * stretchColCount);
 
9317
 
 
9318
    // Check we don't have enough space, in which case shrink all columns, overriding
 
9319
    // any absolute/proportional widths
 
9320
    // TODO: actually we would like to divide up the shrinkage according to size.
 
9321
    // How do we calculate the proportions that will achieve this?
 
9322
    // Could first choose an arbitrary value for stretching cells, and then calculate
 
9323
    // factors to multiply each width by.
 
9324
    // TODO: want to record this fact and pass to an iteration that tries e.g. min widths
 
9325
    if (widthLeft < 0 || (stretchToFitTableWidth && (stretchColCount == 0)))
 
9326
    {
 
9327
        colShare = tableWidthMinusPadding / m_colCount;
 
9328
        colShareRemainder = tableWidthMinusPadding - (colShare * m_colCount);
 
9329
        for (i = 0; i < m_colCount; i++)
 
9330
        {
 
9331
            colWidths[i] = 0;
 
9332
            minColWidths[i] = 0;
 
9333
        }
 
9334
    }
 
9335
 
 
9336
    // We have to adjust the columns if either we need to shrink the
 
9337
    // table to fit the parent/table width, or we explicitly set the
 
9338
    // table width and need to stretch out the table.
 
9339
    if (widthLeft < 0 || stretchToFitTableWidth)
 
9340
    {
 
9341
        for (i = 0; i < m_colCount; i++)
 
9342
        {
 
9343
            if (colWidths[i] <= 0) // absolute or proportional width has not been specified
 
9344
            {
 
9345
                if (minColWidths[i] > 0)
 
9346
                    colWidths[i] = minColWidths[i] + colShare;
 
9347
                else
 
9348
                    colWidths[i] = colShare;
 
9349
                if (i == (m_colCount-1))
 
9350
                    colWidths[i] += colShareRemainder; // ensure all pixels are filled
 
9351
            }
 
9352
        }
 
9353
    }
 
9354
 
 
9355
    // TODO: if spanned cells have no specified or max width, make them the
 
9356
    // as big as the columns they span. Do this for all spanned cells in all
 
9357
    // rows, of course. Size any spanned cells left over at the end - even if they
 
9358
    // have width > 0, make sure they're limited to the appropriate column edge.
 
9359
 
 
9360
 
 
9361
/*
 
9362
    Sort out confusion between content width
 
9363
    and overall width later. For now, assume we specify overall width.
 
9364
 
 
9365
    So, now we've laid out the table to fit into the given space
 
9366
    and have used specified widths and minimum widths.
 
9367
 
 
9368
    Now we need to consider how we will try to take maximum width into account.
 
9369
 
 
9370
*/
 
9371
 
 
9372
    // (??) TODO: take max width into account
 
9373
 
 
9374
    // (6) Lay out all cells again with the current values
 
9375
 
 
9376
    int maxRight = 0;
 
9377
    int y = availableSpace.y;
 
9378
    for (j = 0; j < m_rowCount; j++)
 
9379
    {
 
9380
        int x = availableSpace.x; // TODO: take into account centering etc.
 
9381
        int maxCellHeight = 0;
 
9382
        int maxSpecifiedCellHeight = 0;
 
9383
 
 
9384
        wxArrayInt actualWidths;
 
9385
        actualWidths.Add(0, m_colCount);
 
9386
 
 
9387
        wxTextAttrDimensionConverter converter(dc, scale);
 
9388
        for (i = 0; i < m_colCount; i++)
 
9389
        {
 
9390
            wxRichTextCell* cell = GetCell(j, i);
 
9391
            if (cell->IsShown())
 
9392
            {
 
9393
                // Get max specified cell height
 
9394
                // Don't handle percentages for height
 
9395
                if (cell->GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && cell->GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() != wxTEXT_ATTR_UNITS_PERCENTAGE)
 
9396
                {
 
9397
                    int h = converter.GetPixels(cell->GetAttributes().GetTextBoxAttr().GetHeight());
 
9398
                    if (h > maxSpecifiedCellHeight)
 
9399
                        maxSpecifiedCellHeight = h;
 
9400
                }
 
9401
 
 
9402
                if (colWidths[i] > 0) // absolute or proportional width has been specified
 
9403
                {
 
9404
                    int colSpan = 1;
 
9405
                    if (cell->GetProperties().HasProperty(wxT("colspan")))
 
9406
                        colSpan = cell->GetProperties().GetPropertyLong(wxT("colspan"));
 
9407
 
 
9408
                    wxRect availableCellSpace;
 
9409
 
 
9410
                    // TODO: take into acount spans
 
9411
                    if (colSpan > 1)
 
9412
                    {
 
9413
                        // Calculate the size of this spanning cell from its constituent columns
 
9414
                        int xx = x;
 
9415
                        int spans = wxMin(colSpan, m_colCount - i);
 
9416
                        for (k = i; k < spans; k++)
 
9417
                        {
 
9418
                            if (k != i)
 
9419
                                xx += paddingX;
 
9420
                            xx += colWidths[k];
 
9421
                        }
 
9422
                        availableCellSpace = wxRect(x, y, xx, -1);
 
9423
                    }
 
9424
                    else
 
9425
                        availableCellSpace = wxRect(x, y, colWidths[i], -1);
 
9426
 
 
9427
                    // Store actual width so we can force cell to be the appropriate width on the final loop
 
9428
                    actualWidths[i] = availableCellSpace.GetWidth();
 
9429
 
 
9430
                    // Lay out cell
 
9431
                    cell->Invalidate(wxRICHTEXT_ALL);
 
9432
                    cell->Layout(dc, context, availableCellSpace, availableSpace, style);
 
9433
 
 
9434
                    // TODO: use GetCachedSize().x to compute 'natural' size
 
9435
 
 
9436
                    x += (availableCellSpace.GetWidth() + paddingX);
 
9437
                    if (cell->GetCachedSize().y > maxCellHeight)
 
9438
                        maxCellHeight = cell->GetCachedSize().y;
 
9439
                }
 
9440
            }
 
9441
        }
 
9442
 
 
9443
        maxCellHeight = wxMax(maxCellHeight, maxSpecifiedCellHeight);
 
9444
 
 
9445
        for (i = 0; i < m_colCount; i++)
 
9446
        {
 
9447
            wxRichTextCell* cell = GetCell(j, i);
 
9448
            if (cell->IsShown())
 
9449
            {
 
9450
                wxRect availableCellSpace = wxRect(cell->GetPosition(), wxSize(actualWidths[i], maxCellHeight));
 
9451
                // Lay out cell with new height
 
9452
                cell->Invalidate(wxRICHTEXT_ALL);
 
9453
                cell->Layout(dc, context, availableCellSpace, availableSpace, style);
 
9454
 
 
9455
                // Make sure the cell size really is the appropriate size,
 
9456
                // not the calculated box size
 
9457
                cell->SetCachedSize(wxSize(actualWidths[i], maxCellHeight));
 
9458
 
 
9459
                maxRight = wxMax(maxRight, cell->GetPosition().x + cell->GetCachedSize().x);
 
9460
            }
 
9461
        }
 
9462
 
 
9463
        y += maxCellHeight;
 
9464
        if (j < (m_rowCount-1))
 
9465
            y += paddingY;
 
9466
    }
 
9467
 
 
9468
    // We need to add back the margins etc.
 
9469
    {
 
9470
        wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
 
9471
        contentRect = wxRect(wxPoint(0, 0), wxSize(maxRight - availableSpace.x, y - availableSpace.y));
 
9472
        GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
 
9473
        SetCachedSize(marginRect.GetSize());
 
9474
    }
 
9475
 
 
9476
    // TODO: calculate max size
 
9477
    {
 
9478
        SetMaxSize(GetCachedSize());
 
9479
    }
 
9480
 
 
9481
    // TODO: calculate min size
 
9482
    {
 
9483
        SetMinSize(GetCachedSize());
 
9484
    }
 
9485
 
 
9486
    // TODO: currently we use either a fixed table width or the parent's size.
 
9487
    // We also want to be able to calculate the table width from its content,
 
9488
    // whether using fixed column widths or cell content min/max width.
 
9489
    // Probably need a boolean flag to say whether we need to stretch cells
 
9490
    // to fit the table width, or to simply use min/max cell widths. The
 
9491
    // trouble with this is that if cell widths are not specified, they
 
9492
    // will be tiny; we could use arbitrary defaults but this seems unsatisfactory.
 
9493
    // Anyway, ignoring that problem, we probably need to factor layout into a function
 
9494
    // that can can calculate the maximum unconstrained layout in case table size is
 
9495
    // not specified. Then LayoutToBestSize() can choose to use either parent size to
 
9496
    // constrain Layout(), or the previously-calculated max size to constraint layout.
 
9497
 
 
9498
    return true;
 
9499
}
 
9500
 
 
9501
// Finds the absolute position and row height for the given character position
 
9502
bool wxRichTextTable::FindPosition(wxDC& dc, wxRichTextDrawingContext& context, long index, wxPoint& pt, int* height, bool forceLineStart)
 
9503
{
 
9504
    wxRichTextCell* child = GetCell(index+1);
 
9505
    if (child)
 
9506
    {
 
9507
        // Find the position at the start of the child cell, since the table doesn't
 
9508
        // have any caret position of its own.
 
9509
        return child->FindPosition(dc, context, -1, pt, height, forceLineStart);
 
9510
    }
 
9511
    else
 
9512
        return false;
 
9513
}
 
9514
 
 
9515
// Get the cell at the given character position (in the range of the table).
 
9516
wxRichTextCell* wxRichTextTable::GetCell(long pos) const
 
9517
{
 
9518
    int row = 0, col = 0;
 
9519
    if (GetCellRowColumnPosition(pos, row, col))
 
9520
    {
 
9521
        return GetCell(row, col);
 
9522
    }
 
9523
    else
 
9524
        return NULL;
 
9525
}
 
9526
 
 
9527
// Get the row/column for a given character position
 
9528
bool wxRichTextTable::GetCellRowColumnPosition(long pos, int& row, int& col) const
 
9529
{
 
9530
    if (m_colCount == 0 || m_rowCount == 0)
 
9531
        return false;
 
9532
 
 
9533
    row = (int) (pos / m_colCount);
 
9534
    col = pos - (row * m_colCount);
 
9535
 
 
9536
    wxASSERT(row < m_rowCount && col < m_colCount);
 
9537
 
 
9538
    if (row < m_rowCount && col < m_colCount)
 
9539
        return true;
 
9540
    else
 
9541
        return false;
 
9542
}
 
9543
 
 
9544
// Calculate range, taking row/cell ordering into account instead of relying
 
9545
// on list ordering.
 
9546
void wxRichTextTable::CalculateRange(long start, long& end)
 
9547
{
 
9548
    long current = start;
 
9549
    long lastEnd = current;
 
9550
 
 
9551
    if (IsTopLevel())
 
9552
    {
 
9553
        current = 0;
 
9554
        lastEnd = 0;
 
9555
    }
 
9556
 
 
9557
    int i, j;
 
9558
    for (i = 0; i < m_rowCount; i++)
 
9559
    {
 
9560
        for (j = 0; j < m_colCount; j++)
 
9561
        {
 
9562
            wxRichTextCell* child = GetCell(i, j);
 
9563
            if (child)
 
9564
            {
 
9565
                long childEnd = 0;
 
9566
 
 
9567
                child->CalculateRange(current, childEnd);
 
9568
 
 
9569
                lastEnd = childEnd;
 
9570
                current = childEnd + 1;
 
9571
            }
 
9572
        }
 
9573
    }
 
9574
 
 
9575
    // A top-level object always has a range of size 1,
 
9576
    // because its children don't count at this level.
 
9577
    end = start;
 
9578
    m_range.SetRange(start, start);
 
9579
 
 
9580
    // An object with no children has zero length
 
9581
    if (m_children.GetCount() == 0)
 
9582
        lastEnd --;
 
9583
    m_ownRange.SetRange(0, lastEnd);
 
9584
}
 
9585
 
 
9586
// Gets the range size.
 
9587
bool wxRichTextTable::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, wxRichTextDrawingContext& context, int flags, wxPoint position, wxArrayInt* partialExtents) const
 
9588
{
 
9589
    return wxRichTextBox::GetRangeSize(range, size, descent, dc, context, flags, position, partialExtents);
 
9590
}
 
9591
 
 
9592
// Deletes content in the given range.
 
9593
bool wxRichTextTable::DeleteRange(const wxRichTextRange& WXUNUSED(range))
 
9594
{
 
9595
    // TODO: implement deletion of cells
 
9596
    return true;
 
9597
}
 
9598
 
 
9599
// Gets any text in this object for the given range.
 
9600
wxString wxRichTextTable::GetTextForRange(const wxRichTextRange& range) const
 
9601
{
 
9602
    return wxRichTextBox::GetTextForRange(range);
 
9603
}
 
9604
 
 
9605
// Copies this object.
 
9606
void wxRichTextTable::Copy(const wxRichTextTable& obj)
 
9607
{
 
9608
    wxRichTextBox::Copy(obj);
 
9609
 
 
9610
    ClearTable();
 
9611
 
 
9612
    m_rowCount = obj.m_rowCount;
 
9613
    m_colCount = obj.m_colCount;
 
9614
 
 
9615
    m_cells.Add(wxRichTextObjectPtrArray(), m_rowCount);
 
9616
 
 
9617
    int i, j;
 
9618
    for (i = 0; i < m_rowCount; i++)
 
9619
    {
 
9620
        wxRichTextObjectPtrArray& colArray = m_cells[i];
 
9621
        for (j = 0; j < m_colCount; j++)
 
9622
        {
 
9623
            wxRichTextCell* cell = wxDynamicCast(obj.GetCell(i, j)->Clone(), wxRichTextCell);
 
9624
            AppendChild(cell);
 
9625
 
 
9626
            colArray.Add(cell);
 
9627
        }
 
9628
    }
 
9629
}
 
9630
 
 
9631
void wxRichTextTable::ClearTable()
 
9632
{
 
9633
    m_cells.Clear();
 
9634
    DeleteChildren();
 
9635
}
 
9636
 
 
9637
bool wxRichTextTable::CreateTable(int rows, int cols)
 
9638
{
 
9639
    ClearTable();
 
9640
 
 
9641
    m_rowCount = rows;
 
9642
    m_colCount = cols;
 
9643
 
 
9644
    m_cells.Add(wxRichTextObjectPtrArray(), rows);
 
9645
 
 
9646
    int i, j;
 
9647
    for (i = 0; i < rows; i++)
 
9648
    {
 
9649
        wxRichTextObjectPtrArray& colArray = m_cells[i];
 
9650
        for (j = 0; j < cols; j++)
 
9651
        {
 
9652
            wxRichTextCell* cell = new wxRichTextCell;
 
9653
            AppendChild(cell);
 
9654
            cell->AddParagraph(wxEmptyString);
 
9655
 
 
9656
            colArray.Add(cell);
 
9657
        }
 
9658
    }
 
9659
 
 
9660
    return true;
 
9661
}
 
9662
 
 
9663
wxRichTextCell* wxRichTextTable::GetCell(int row, int col) const
 
9664
{
 
9665
    wxASSERT(row < m_rowCount);
 
9666
    wxASSERT(col < m_colCount);
 
9667
 
 
9668
    if (row < m_rowCount && col < m_colCount)
 
9669
    {
 
9670
        wxRichTextObjectPtrArray& colArray = m_cells[row];
 
9671
        wxRichTextObject* obj = colArray[col];
 
9672
        return wxDynamicCast(obj, wxRichTextCell);
 
9673
    }
 
9674
    else
 
9675
        return NULL;
 
9676
}
 
9677
 
 
9678
// Returns a selection object specifying the selections between start and end character positions.
 
9679
// For example, a table would deduce what cells (of range length 1) are selected when dragging across the table.
 
9680
wxRichTextSelection wxRichTextTable::GetSelection(long start, long end) const
 
9681
{
 
9682
    wxRichTextSelection selection;
 
9683
    selection.SetContainer((wxRichTextTable*) this);
 
9684
 
 
9685
    if (start > end)
 
9686
    {
 
9687
        long tmp = end;
 
9688
        end = start;
 
9689
        start = tmp;
 
9690
    }
 
9691
 
 
9692
    wxASSERT( start >= 0 && end < (m_colCount * m_rowCount));
 
9693
 
 
9694
    if (end >= (m_colCount * m_rowCount))
 
9695
        return selection;
 
9696
 
 
9697
    // We need to find the rectangle of cells that is described by the rectangle
 
9698
    // with start, end as the diagonal. Make sure we don't add cells that are
 
9699
    // not currenty visible because they are overlapped by spanning cells.
 
9700
/*
 
9701
    --------------------------
 
9702
    | 0  | 1  | 2  | 3  | 4  |
 
9703
    --------------------------
 
9704
    | 5  | 6  | 7  | 8  | 9  |
 
9705
    --------------------------
 
9706
    | 10 | 11 | 12 | 13 | 14 |
 
9707
    --------------------------
 
9708
    | 15 | 16 | 17 | 18 | 19 |
 
9709
    --------------------------
 
9710
 
 
9711
    Let's say we select 6 -> 18.
 
9712
 
 
9713
    Left and right edge cols of rectangle are 1 and 3 inclusive. Find least/greatest to find
 
9714
    which is left and which is right.
 
9715
 
 
9716
    Top and bottom edge rows are 1 and 3 inclusive. Again, find least/greatest to find top and bottom.
 
9717
 
 
9718
    Now go through rows from 1 to 3 and only add cells that are (a) within above column range
 
9719
    and (b) shown.
 
9720
 
 
9721
 
 
9722
*/
 
9723
 
 
9724
    int leftCol = start - m_colCount * int(start/m_colCount);
 
9725
    int rightCol = end - m_colCount * int(end/m_colCount);
 
9726
 
 
9727
    int topRow = int(start/m_colCount);
 
9728
    int bottomRow = int(end/m_colCount);
 
9729
 
 
9730
    if (leftCol > rightCol)
 
9731
    {
 
9732
        int tmp = rightCol;
 
9733
        rightCol = leftCol;
 
9734
        leftCol = tmp;
 
9735
    }
 
9736
 
 
9737
    if (topRow > bottomRow)
 
9738
    {
 
9739
        int tmp = bottomRow;
 
9740
        bottomRow = topRow;
 
9741
        topRow = tmp;
 
9742
    }
 
9743
 
 
9744
    int i, j;
 
9745
    for (i = topRow; i <= bottomRow; i++)
 
9746
    {
 
9747
        for (j = leftCol; j <= rightCol; j++)
 
9748
        {
 
9749
            wxRichTextCell* cell = GetCell(i, j);
 
9750
            if (cell && cell->IsShown())
 
9751
                selection.Add(cell->GetRange());
 
9752
        }
 
9753
    }
 
9754
 
 
9755
    return selection;
 
9756
}
 
9757
 
 
9758
// Sets the attributes for the cells specified by the selection.
 
9759
bool wxRichTextTable::SetCellStyle(const wxRichTextSelection& selection, const wxRichTextAttr& style, int flags)
 
9760
{
 
9761
    if (selection.GetContainer() != this)
 
9762
        return false;
 
9763
 
 
9764
    wxRichTextBuffer* buffer = GetBuffer();
 
9765
    bool haveControl = (buffer && buffer->GetRichTextCtrl() != NULL);
 
9766
    bool withUndo = haveControl && ((flags & wxRICHTEXT_SETSTYLE_WITH_UNDO) != 0);
 
9767
 
 
9768
    if (withUndo)
 
9769
        buffer->BeginBatchUndo(_("Set Cell Style"));
 
9770
 
 
9771
    wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
 
9772
    while (node)
 
9773
    {
 
9774
        wxRichTextCell* cell = wxDynamicCast(node->GetData(), wxRichTextCell);
 
9775
        if (cell && selection.WithinSelection(cell->GetRange().GetStart()))
 
9776
            SetStyle(cell, style, flags);
 
9777
        node = node->GetNext();
 
9778
    }
 
9779
 
 
9780
    // Do action, or delay it until end of batch.
 
9781
    if (withUndo)
 
9782
        buffer->EndBatchUndo();
 
9783
 
 
9784
    return true;
 
9785
}
 
9786
 
 
9787
bool wxRichTextTable::DeleteRows(int startRow, int noRows)
 
9788
{
 
9789
    wxASSERT((startRow + noRows) < m_rowCount);
 
9790
    if ((startRow + noRows) >= m_rowCount)
 
9791
        return false;
 
9792
 
 
9793
    int i, j;
 
9794
    for (i = startRow; i < (startRow+noRows); i++)
 
9795
    {
 
9796
        wxRichTextObjectPtrArray& colArray = m_cells[startRow];
 
9797
        for (j = 0; j < (int) colArray.GetCount(); j++)
 
9798
        {
 
9799
            wxRichTextObject* cell = colArray[j];
 
9800
            RemoveChild(cell, true);
 
9801
        }
 
9802
 
 
9803
        // Keep deleting at the same position, since we move all
 
9804
        // the others up
 
9805
        m_cells.RemoveAt(startRow);
 
9806
    }
 
9807
 
 
9808
    m_rowCount = m_rowCount - noRows;
 
9809
 
 
9810
    return true;
 
9811
}
 
9812
 
 
9813
bool wxRichTextTable::DeleteColumns(int startCol, int noCols)
 
9814
{
 
9815
    wxASSERT((startCol + noCols) < m_colCount);
 
9816
    if ((startCol + noCols) >= m_colCount)
 
9817
        return false;
 
9818
 
 
9819
    bool deleteRows = (noCols == m_colCount);
 
9820
 
 
9821
    int i, j;
 
9822
    for (i = 0; i < m_rowCount; i++)
 
9823
    {
 
9824
        wxRichTextObjectPtrArray& colArray = m_cells[deleteRows ? 0 : i];
 
9825
        for (j = startCol; j < (startCol+noCols); j++)
 
9826
        {
 
9827
            wxRichTextObject* cell = colArray[j];
 
9828
            RemoveChild(cell, true);
 
9829
        }
 
9830
 
 
9831
        if (deleteRows)
 
9832
            m_cells.RemoveAt(0);
 
9833
    }
 
9834
 
 
9835
    if (deleteRows)
 
9836
        m_rowCount = 0;
 
9837
    m_colCount = m_colCount - noCols;
 
9838
 
 
9839
    return true;
 
9840
}
 
9841
 
 
9842
bool wxRichTextTable::AddRows(int startRow, int noRows, const wxRichTextAttr& attr)
 
9843
{
 
9844
    wxASSERT(startRow <= m_rowCount);
 
9845
    if (startRow > m_rowCount)
 
9846
        return false;
 
9847
 
 
9848
    int i, j;
 
9849
    for (i = 0; i < noRows; i++)
 
9850
    {
 
9851
        int idx;
 
9852
        if (startRow == m_rowCount)
 
9853
        {
 
9854
            m_cells.Add(wxRichTextObjectPtrArray());
 
9855
            idx = m_cells.GetCount() - 1;
 
9856
        }
 
9857
        else
 
9858
        {
 
9859
            m_cells.Insert(wxRichTextObjectPtrArray(), startRow+i);
 
9860
            idx = startRow+i;
 
9861
        }
 
9862
 
 
9863
        wxRichTextObjectPtrArray& colArray = m_cells[idx];
 
9864
        for (j = 0; j < m_colCount; j++)
 
9865
        {
 
9866
            wxRichTextCell* cell = new wxRichTextCell;
 
9867
            cell->GetAttributes() = attr;
 
9868
 
 
9869
            AppendChild(cell);
 
9870
            colArray.Add(cell);
 
9871
        }
 
9872
    }
 
9873
 
 
9874
    m_rowCount = m_rowCount + noRows;
 
9875
    return true;
 
9876
}
 
9877
 
 
9878
bool wxRichTextTable::AddColumns(int startCol, int noCols, const wxRichTextAttr& attr)
 
9879
{
 
9880
    wxASSERT(startCol <= m_colCount);
 
9881
    if (startCol > m_colCount)
 
9882
        return false;
 
9883
 
 
9884
    int i, j;
 
9885
    for (i = 0; i < m_rowCount; i++)
 
9886
    {
 
9887
        wxRichTextObjectPtrArray& colArray = m_cells[i];
 
9888
        for (j = 0; j < noCols; j++)
 
9889
        {
 
9890
            wxRichTextCell* cell = new wxRichTextCell;
 
9891
            cell->GetAttributes() = attr;
 
9892
 
 
9893
            AppendChild(cell);
 
9894
 
 
9895
            if (startCol == m_colCount)
 
9896
                colArray.Add(cell);
 
9897
            else
 
9898
                colArray.Insert(cell, startCol+j);
 
9899
        }
 
9900
    }
 
9901
 
 
9902
    m_colCount = m_colCount + noCols;
 
9903
 
 
9904
    return true;
 
9905
}
 
9906
 
 
9907
// Edit properties via a GUI
 
9908
bool wxRichTextTable::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
 
9909
{
 
9910
    wxRichTextObjectPropertiesDialog boxDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Table Properties"));
 
9911
    boxDlg.SetAttributes(GetAttributes());
 
9912
 
 
9913
    if (boxDlg.ShowModal() == wxID_OK)
 
9914
    {
 
9915
        boxDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
 
9916
        return true;
 
9917
    }
 
9918
    else
 
9919
        return false;
 
9920
}
 
9921
 
 
9922
/*
 
9923
 * Module to initialise and clean up handlers
 
9924
 */
 
9925
 
 
9926
class wxRichTextModule: public wxModule
 
9927
{
 
9928
DECLARE_DYNAMIC_CLASS(wxRichTextModule)
 
9929
public:
 
9930
    wxRichTextModule() {}
 
9931
    bool OnInit()
 
9932
    {
 
9933
        wxRichTextBuffer::SetRenderer(new wxRichTextStdRenderer);
 
9934
        wxRichTextBuffer::InitStandardHandlers();
 
9935
        wxRichTextParagraph::InitDefaultTabs();
 
9936
 
 
9937
        wxRichTextXMLHandler::RegisterNodeName(wxT("text"), wxT("wxRichTextPlainText"));
 
9938
        wxRichTextXMLHandler::RegisterNodeName(wxT("symbol"), wxT("wxRichTextPlainText"));
 
9939
        wxRichTextXMLHandler::RegisterNodeName(wxT("image"), wxT("wxRichTextImage"));
 
9940
        wxRichTextXMLHandler::RegisterNodeName(wxT("paragraph"), wxT("wxRichTextParagraph"));
 
9941
        wxRichTextXMLHandler::RegisterNodeName(wxT("paragraphlayout"), wxT("wxRichTextParagraphLayoutBox"));
 
9942
        wxRichTextXMLHandler::RegisterNodeName(wxT("textbox"), wxT("wxRichTextBox"));
 
9943
        wxRichTextXMLHandler::RegisterNodeName(wxT("cell"), wxT("wxRichTextCell"));
 
9944
        wxRichTextXMLHandler::RegisterNodeName(wxT("table"), wxT("wxRichTextTable"));
 
9945
        wxRichTextXMLHandler::RegisterNodeName(wxT("field"), wxT("wxRichTextField"));
 
9946
 
 
9947
        return true;
 
9948
    }
 
9949
    void OnExit()
 
9950
    {
 
9951
        wxRichTextBuffer::CleanUpHandlers();
 
9952
        wxRichTextBuffer::CleanUpDrawingHandlers();
 
9953
        wxRichTextBuffer::CleanUpFieldTypes();
 
9954
        wxRichTextXMLHandler::ClearNodeToClassMap();
 
9955
        wxRichTextDecimalToRoman(-1);
 
9956
        wxRichTextParagraph::ClearDefaultTabs();
 
9957
        wxRichTextCtrl::ClearAvailableFontNames();
 
9958
        wxRichTextBuffer::SetRenderer(NULL);
 
9959
    }
 
9960
};
 
9961
 
 
9962
IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule)
 
9963
 
 
9964
 
 
9965
// If the richtext lib is dynamically loaded after the app has already started
 
9966
// (such as from wxPython) then the built-in module system will not init this
 
9967
// module.  Provide this function to do it manually.
 
9968
void wxRichTextModuleInit()
 
9969
{
 
9970
    wxModule* module = new wxRichTextModule;
 
9971
    module->Init();
 
9972
    wxModule::RegisterModule(module);
 
9973
}
 
9974
 
 
9975
 
 
9976
/*!
 
9977
 * Commands for undo/redo
 
9978
 *
 
9979
 */
 
9980
 
 
9981
wxRichTextCommand::wxRichTextCommand(const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
 
9982
                                     wxRichTextParagraphLayoutBox* container, wxRichTextCtrl* ctrl, bool ignoreFirstTime): wxCommand(true, name)
 
9983
{
 
9984
    /* wxRichTextAction* action = */ new wxRichTextAction(this, name, id, buffer, container, ctrl, ignoreFirstTime);
 
9985
}
 
9986
 
 
9987
wxRichTextCommand::wxRichTextCommand(const wxString& name): wxCommand(true, name)
 
9988
{
 
9989
}
 
9990
 
 
9991
wxRichTextCommand::~wxRichTextCommand()
 
9992
{
 
9993
    ClearActions();
 
9994
}
 
9995
 
 
9996
void wxRichTextCommand::AddAction(wxRichTextAction* action)
 
9997
{
 
9998
    if (!m_actions.Member(action))
 
9999
        m_actions.Append(action);
 
10000
}
 
10001
 
 
10002
bool wxRichTextCommand::Do()
 
10003
{
 
10004
    for (wxList::compatibility_iterator node = m_actions.GetFirst(); node; node = node->GetNext())
 
10005
    {
 
10006
        wxRichTextAction* action = (wxRichTextAction*) node->GetData();
 
10007
        action->Do();
 
10008
    }
 
10009
 
 
10010
    return true;
 
10011
}
 
10012
 
 
10013
bool wxRichTextCommand::Undo()
 
10014
{
 
10015
    for (wxList::compatibility_iterator node = m_actions.GetLast(); node; node = node->GetPrevious())
 
10016
    {
 
10017
        wxRichTextAction* action = (wxRichTextAction*) node->GetData();
 
10018
        action->Undo();
 
10019
    }
 
10020
 
 
10021
    return true;
 
10022
}
 
10023
 
 
10024
void wxRichTextCommand::ClearActions()
 
10025
{
 
10026
    WX_CLEAR_LIST(wxList, m_actions);
 
10027
}
 
10028
 
 
10029
/*!
 
10030
 * Individual action
 
10031
 *
 
10032
 */
 
10033
 
 
10034
wxRichTextAction::wxRichTextAction(wxRichTextCommand* cmd, const wxString& name, wxRichTextCommandId id,
 
10035
                                   wxRichTextBuffer* buffer, wxRichTextParagraphLayoutBox* container,
 
10036
                                   wxRichTextCtrl* ctrl, bool ignoreFirstTime)
 
10037
{
 
10038
    m_buffer = buffer;
 
10039
    m_object = NULL;
 
10040
    m_containerAddress.Create(buffer, container);
 
10041
    m_ignoreThis = ignoreFirstTime;
 
10042
    m_cmdId = id;
 
10043
    m_position = -1;
 
10044
    m_ctrl = ctrl;
 
10045
    m_name = name;
 
10046
    m_newParagraphs.SetDefaultStyle(buffer->GetDefaultStyle());
 
10047
    m_newParagraphs.SetBasicStyle(buffer->GetBasicStyle());
 
10048
    if (cmd)
 
10049
        cmd->AddAction(this);
 
10050
}
 
10051
 
 
10052
wxRichTextAction::~wxRichTextAction()
 
10053
{
 
10054
    if (m_object)
 
10055
        delete m_object;
 
10056
}
 
10057
 
 
10058
// Returns the container that this action refers to, using the container address and top-level buffer.
 
10059
wxRichTextParagraphLayoutBox* wxRichTextAction::GetContainer() const
 
10060
{
 
10061
    wxRichTextParagraphLayoutBox* container = wxDynamicCast(GetContainerAddress().GetObject(m_buffer), wxRichTextParagraphLayoutBox);
 
10062
    return container;
 
10063
}
 
10064
 
 
10065
 
 
10066
void wxRichTextAction::CalculateRefreshOptimizations(wxArrayInt& optimizationLineCharPositions, wxArrayInt& optimizationLineYPositions)
 
10067
{
 
10068
    // Store a list of line start character and y positions so we can figure out which area
 
10069
    // we need to refresh
 
10070
 
 
10071
#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
 
10072
    wxRichTextParagraphLayoutBox* container = GetContainer();
 
10073
    wxASSERT(container != NULL);
 
10074
    if (!container)
 
10075
        return;
 
10076
 
 
10077
    // NOTE: we're assuming that the buffer is laid out correctly at this point.
 
10078
    // If we had several actions, which only invalidate and leave layout until the
 
10079
    // paint handler is called, then this might not be true. So we may need to switch
 
10080
    // optimisation on only when we're simply adding text and not simultaneously
 
10081
    // deleting a selection, for example. Or, we make sure the buffer is laid out correctly
 
10082
    // first, but of course this means we'll be doing it twice.
 
10083
    if (!m_buffer->IsDirty() && m_ctrl) // can only do optimisation if the buffer is already laid out correctly
 
10084
    {
 
10085
        wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
 
10086
        wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
 
10087
        int lastY = firstVisiblePt.y + clientSize.y;
 
10088
 
 
10089
        wxRichTextParagraph* para = container->GetParagraphAtPosition(GetRange().GetStart());
 
10090
        wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
 
10091
        while (node)
 
10092
        {
 
10093
            wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
 
10094
            wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
 
10095
            while (node2)
 
10096
            {
 
10097
                wxRichTextLine* line = node2->GetData();
 
10098
                wxPoint pt = line->GetAbsolutePosition();
 
10099
                wxRichTextRange range = line->GetAbsoluteRange();
 
10100
 
 
10101
                if (pt.y > lastY)
 
10102
                {
 
10103
                    node2 = wxRichTextLineList::compatibility_iterator();
 
10104
                    node = wxRichTextObjectList::compatibility_iterator();
 
10105
                }
 
10106
                else if (range.GetStart() > GetPosition() && pt.y >= firstVisiblePt.y)
 
10107
                {
 
10108
                    optimizationLineCharPositions.Add(range.GetStart());
 
10109
                    optimizationLineYPositions.Add(pt.y);
 
10110
                }
 
10111
 
 
10112
                if (node2)
 
10113
                    node2 = node2->GetNext();
 
10114
            }
 
10115
 
 
10116
            if (node)
 
10117
                node = node->GetNext();
 
10118
        }
 
10119
    }
 
10120
#endif
 
10121
}
 
10122
 
 
10123
bool wxRichTextAction::Do()
 
10124
{
 
10125
    m_buffer->Modify(true);
 
10126
 
 
10127
    wxRichTextParagraphLayoutBox* container = GetContainer();
 
10128
    wxASSERT(container != NULL);
 
10129
    if (!container)
 
10130
        return false;
 
10131
 
 
10132
    switch (m_cmdId)
 
10133
    {
 
10134
    case wxRICHTEXT_INSERT:
 
10135
        {
 
10136
            // Store a list of line start character and y positions so we can figure out which area
 
10137
            // we need to refresh
 
10138
            wxArrayInt optimizationLineCharPositions;
 
10139
            wxArrayInt optimizationLineYPositions;
 
10140
 
 
10141
#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
 
10142
            CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
 
10143
#endif
 
10144
 
 
10145
            container->InsertFragment(GetRange().GetStart(), m_newParagraphs);
 
10146
            container->UpdateRanges();
 
10147
 
 
10148
            // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
 
10149
            // Layout() would stop prematurely at the top level.
 
10150
            container->InvalidateHierarchy(wxRichTextRange(wxMax(0, GetRange().GetStart()-1), GetRange().GetEnd()));
 
10151
 
 
10152
            long newCaretPosition = GetPosition() + m_newParagraphs.GetOwnRange().GetLength();
 
10153
 
 
10154
            // Character position to caret position
 
10155
            newCaretPosition --;
 
10156
 
 
10157
            // Don't take into account the last newline
 
10158
            if (m_newParagraphs.GetPartialParagraph())
 
10159
                newCaretPosition --;
 
10160
            else
 
10161
                if (m_newParagraphs.GetChildren().GetCount() > 1)
 
10162
                {
 
10163
                    wxRichTextObject* p = (wxRichTextObject*) m_newParagraphs.GetChildren().GetLast()->GetData();
 
10164
                    if (p->GetRange().GetLength() == 1)
 
10165
                        newCaretPosition --;
 
10166
                }
 
10167
 
 
10168
            newCaretPosition = wxMin(newCaretPosition, (container->GetOwnRange().GetEnd()-1));
 
10169
 
 
10170
            UpdateAppearance(newCaretPosition, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
 
10171
 
 
10172
            wxRichTextEvent cmdEvent(
 
10173
                wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
 
10174
                m_ctrl ? m_ctrl->GetId() : -1);
 
10175
            cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
 
10176
            cmdEvent.SetRange(GetRange());
 
10177
            cmdEvent.SetPosition(GetRange().GetStart());
 
10178
            cmdEvent.SetContainer(container);
 
10179
 
 
10180
            m_buffer->SendEvent(cmdEvent);
 
10181
 
 
10182
            break;
 
10183
        }
 
10184
    case wxRICHTEXT_DELETE:
 
10185
        {
 
10186
            wxArrayInt optimizationLineCharPositions;
 
10187
            wxArrayInt optimizationLineYPositions;
 
10188
 
 
10189
#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
 
10190
            CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
 
10191
#endif
 
10192
 
 
10193
            container->DeleteRange(GetRange());
 
10194
            container->UpdateRanges();
 
10195
            // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
 
10196
            // Layout() would stop prematurely at the top level.
 
10197
            container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
 
10198
 
 
10199
            long caretPos = GetRange().GetStart()-1;
 
10200
            if (caretPos >= container->GetOwnRange().GetEnd())
 
10201
                caretPos --;
 
10202
 
 
10203
            UpdateAppearance(caretPos, true /* send update event */, & optimizationLineCharPositions, & optimizationLineYPositions, true /* do */);
 
10204
 
 
10205
            wxRichTextEvent cmdEvent(
 
10206
                wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
 
10207
                m_ctrl ? m_ctrl->GetId() : -1);
 
10208
            cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
 
10209
            cmdEvent.SetRange(GetRange());
 
10210
            cmdEvent.SetPosition(GetRange().GetStart());
 
10211
            cmdEvent.SetContainer(container);
 
10212
 
 
10213
            m_buffer->SendEvent(cmdEvent);
 
10214
 
 
10215
            break;
 
10216
        }
 
10217
    case wxRICHTEXT_CHANGE_STYLE:
 
10218
    case wxRICHTEXT_CHANGE_PROPERTIES:
 
10219
        {
 
10220
            ApplyParagraphs(GetNewParagraphs());
 
10221
 
 
10222
            // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
 
10223
            // Layout() would stop prematurely at the top level.
 
10224
            container->InvalidateHierarchy(GetRange());
 
10225
 
 
10226
            UpdateAppearance(GetPosition());
 
10227
 
 
10228
            wxRichTextEvent cmdEvent(
 
10229
                m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
 
10230
                m_ctrl ? m_ctrl->GetId() : -1);
 
10231
            cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
 
10232
            cmdEvent.SetRange(GetRange());
 
10233
            cmdEvent.SetPosition(GetRange().GetStart());
 
10234
            cmdEvent.SetContainer(container);
 
10235
 
 
10236
            m_buffer->SendEvent(cmdEvent);
 
10237
 
 
10238
            break;
 
10239
        }
 
10240
    case wxRICHTEXT_CHANGE_ATTRIBUTES:
 
10241
        {
 
10242
            wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer); // container->GetChildAtPosition(GetRange().GetStart());
 
10243
            if (obj)
 
10244
            {
 
10245
                wxRichTextAttr oldAttr = obj->GetAttributes();
 
10246
                obj->GetAttributes() = m_attributes;
 
10247
                m_attributes = oldAttr;
 
10248
            }
 
10249
 
 
10250
            // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
 
10251
            // Layout() would stop prematurely at the top level.
 
10252
            container->InvalidateHierarchy(GetRange());
 
10253
 
 
10254
            UpdateAppearance(GetPosition());
 
10255
 
 
10256
            wxRichTextEvent cmdEvent(
 
10257
                wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED,
 
10258
                m_ctrl ? m_ctrl->GetId() : -1);
 
10259
            cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
 
10260
            cmdEvent.SetRange(GetRange());
 
10261
            cmdEvent.SetPosition(GetRange().GetStart());
 
10262
            cmdEvent.SetContainer(container);
 
10263
 
 
10264
            m_buffer->SendEvent(cmdEvent);
 
10265
 
 
10266
            break;
 
10267
        }
 
10268
    case wxRICHTEXT_CHANGE_OBJECT:
 
10269
        {
 
10270
            wxRichTextObject* obj = m_objectAddress.GetObject(m_buffer);
 
10271
            // wxRichTextObject* obj = container->GetChildAtPosition(GetRange().GetStart());
 
10272
            if (obj && m_object)
 
10273
            {
 
10274
                wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(obj);
 
10275
                if (node)
 
10276
                {
 
10277
                    wxRichTextObject* obj = node->GetData();
 
10278
                    node->SetData(m_object);
 
10279
                    m_object = obj;
 
10280
                }
 
10281
            }
 
10282
 
 
10283
            // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
 
10284
            // Layout() would stop prematurely at the top level.
 
10285
            container->InvalidateHierarchy(GetRange());
 
10286
 
 
10287
            UpdateAppearance(GetPosition());
 
10288
 
 
10289
            // TODO: send new kind of modification event
 
10290
 
 
10291
            break;
 
10292
        }
 
10293
    default:
 
10294
        break;
 
10295
    }
 
10296
 
 
10297
    return true;
 
10298
}
 
10299
 
 
10300
bool wxRichTextAction::Undo()
 
10301
{
 
10302
    m_buffer->Modify(true);
 
10303
 
 
10304
    wxRichTextParagraphLayoutBox* container = GetContainer();
 
10305
    wxASSERT(container != NULL);
 
10306
    if (!container)
 
10307
        return false;
 
10308
 
 
10309
    switch (m_cmdId)
 
10310
    {
 
10311
    case wxRICHTEXT_INSERT:
 
10312
        {
 
10313
            wxArrayInt optimizationLineCharPositions;
 
10314
            wxArrayInt optimizationLineYPositions;
 
10315
 
 
10316
#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
 
10317
            CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
 
10318
#endif
 
10319
 
 
10320
            container->DeleteRange(GetRange());
 
10321
            container->UpdateRanges();
 
10322
 
 
10323
            // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
 
10324
            // Layout() would stop prematurely at the top level.
 
10325
            container->InvalidateHierarchy(wxRichTextRange(GetRange().GetStart(), GetRange().GetStart()));
 
10326
 
 
10327
            long newCaretPosition = GetPosition() - 1;
 
10328
 
 
10329
            UpdateAppearance(newCaretPosition, true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
 
10330
 
 
10331
            wxRichTextEvent cmdEvent(
 
10332
                wxEVT_COMMAND_RICHTEXT_CONTENT_DELETED,
 
10333
                m_ctrl ? m_ctrl->GetId() : -1);
 
10334
            cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
 
10335
            cmdEvent.SetRange(GetRange());
 
10336
            cmdEvent.SetPosition(GetRange().GetStart());
 
10337
            cmdEvent.SetContainer(container);
 
10338
 
 
10339
            m_buffer->SendEvent(cmdEvent);
 
10340
 
 
10341
            break;
 
10342
        }
 
10343
    case wxRICHTEXT_DELETE:
 
10344
        {
 
10345
            wxArrayInt optimizationLineCharPositions;
 
10346
            wxArrayInt optimizationLineYPositions;
 
10347
 
 
10348
#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
 
10349
            CalculateRefreshOptimizations(optimizationLineCharPositions, optimizationLineYPositions);
 
10350
#endif
 
10351
 
 
10352
            container->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
 
10353
            container->UpdateRanges();
 
10354
 
 
10355
            // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
 
10356
            // Layout() would stop prematurely at the top level.
 
10357
            container->InvalidateHierarchy(GetRange());
 
10358
 
 
10359
            UpdateAppearance(GetPosition(), true, /* send update event */ & optimizationLineCharPositions, & optimizationLineYPositions, false /* undo */);
 
10360
 
 
10361
            wxRichTextEvent cmdEvent(
 
10362
                wxEVT_COMMAND_RICHTEXT_CONTENT_INSERTED,
 
10363
                m_ctrl ? m_ctrl->GetId() : -1);
 
10364
            cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
 
10365
            cmdEvent.SetRange(GetRange());
 
10366
            cmdEvent.SetPosition(GetRange().GetStart());
 
10367
            cmdEvent.SetContainer(container);
 
10368
 
 
10369
            m_buffer->SendEvent(cmdEvent);
 
10370
 
 
10371
            break;
 
10372
        }
 
10373
    case wxRICHTEXT_CHANGE_STYLE:
 
10374
    case wxRICHTEXT_CHANGE_PROPERTIES:
 
10375
        {
 
10376
            ApplyParagraphs(GetOldParagraphs());
 
10377
            // InvalidateHierarchy goes up the hierarchy as well as down, otherwise with a nested object,
 
10378
            // Layout() would stop prematurely at the top level.
 
10379
            container->InvalidateHierarchy(GetRange());
 
10380
 
 
10381
            UpdateAppearance(GetPosition());
 
10382
 
 
10383
            wxRichTextEvent cmdEvent(
 
10384
                m_cmdId == wxRICHTEXT_CHANGE_STYLE ? wxEVT_COMMAND_RICHTEXT_STYLE_CHANGED : wxEVT_COMMAND_RICHTEXT_PROPERTIES_CHANGED,
 
10385
                m_ctrl ? m_ctrl->GetId() : -1);
 
10386
            cmdEvent.SetEventObject(m_ctrl ? (wxObject*) m_ctrl : (wxObject*) m_buffer);
 
10387
            cmdEvent.SetRange(GetRange());
 
10388
            cmdEvent.SetPosition(GetRange().GetStart());
 
10389
            cmdEvent.SetContainer(container);
 
10390
 
 
10391
            m_buffer->SendEvent(cmdEvent);
 
10392
 
 
10393
            break;
 
10394
        }
 
10395
    case wxRICHTEXT_CHANGE_ATTRIBUTES:
 
10396
    case wxRICHTEXT_CHANGE_OBJECT:
 
10397
        {
 
10398
            return Do();
 
10399
        }
 
10400
    default:
 
10401
        break;
 
10402
    }
 
10403
 
 
10404
    return true;
 
10405
}
 
10406
 
 
10407
/// Update the control appearance
 
10408
void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent, wxArrayInt* optimizationLineCharPositions, wxArrayInt* optimizationLineYPositions, bool isDoCmd)
 
10409
{
 
10410
    wxRichTextParagraphLayoutBox* container = GetContainer();
 
10411
    wxASSERT(container != NULL);
 
10412
    if (!container)
 
10413
        return;
 
10414
 
 
10415
    if (m_ctrl)
 
10416
    {
 
10417
        m_ctrl->SetFocusObject(container);
 
10418
        m_ctrl->SetCaretPosition(caretPosition);
 
10419
 
 
10420
        if (!m_ctrl->IsFrozen())
 
10421
        {
 
10422
            wxRect containerRect = container->GetRect();
 
10423
 
 
10424
            m_ctrl->LayoutContent();
 
10425
 
 
10426
            // Refresh everything if there were floating objects or the container changed size
 
10427
            // (we can't yet optimize in these cases, since more complex interaction with other content occurs)
 
10428
            if (container->GetFloatingObjectCount() > 0 || (container->GetParent() && containerRect != container->GetRect()))
 
10429
            {
 
10430
                m_ctrl->Refresh(false);
 
10431
            }
 
10432
            else
 
10433
 
 
10434
#if wxRICHTEXT_USE_OPTIMIZED_DRAWING
 
10435
            // Find refresh rectangle if we are in a position to optimise refresh
 
10436
            if ((m_cmdId == wxRICHTEXT_INSERT || m_cmdId == wxRICHTEXT_DELETE) && optimizationLineCharPositions)
 
10437
            {
 
10438
                size_t i;
 
10439
 
 
10440
                wxSize clientSize = m_ctrl->GetUnscaledSize(m_ctrl->GetClientSize());
 
10441
                wxPoint firstVisiblePt = m_ctrl->GetUnscaledPoint(m_ctrl->GetFirstVisiblePoint());
 
10442
 
 
10443
                // Start/end positions
 
10444
                int firstY = 0;
 
10445
                int lastY = firstVisiblePt.y + clientSize.y;
 
10446
 
 
10447
                bool foundEnd = false;
 
10448
 
 
10449
                // position offset - how many characters were inserted
 
10450
                int positionOffset = GetRange().GetLength();
 
10451
 
 
10452
                // Determine whether this is Do or Undo, and adjust positionOffset accordingly
 
10453
                if ((m_cmdId == wxRICHTEXT_DELETE && isDoCmd) || (m_cmdId == wxRICHTEXT_INSERT && !isDoCmd))
 
10454
                    positionOffset = - positionOffset;
 
10455
 
 
10456
                // find the first line which is being drawn at the same position as it was
 
10457
                // before. Since we're talking about a simple insertion, we can assume
 
10458
                // that the rest of the window does not need to be redrawn.
 
10459
 
 
10460
                wxRichTextParagraph* para = container->GetParagraphAtPosition(GetPosition());
 
10461
                // Since we support floating layout, we should redraw the whole para instead of just
 
10462
                // the first line touching the invalid range.
 
10463
                if (para)
 
10464
                {
 
10465
                    firstY = para->GetPosition().y;
 
10466
                }
 
10467
 
 
10468
                wxRichTextObjectList::compatibility_iterator node = container->GetChildren().Find(para);
 
10469
                while (node)
 
10470
                {
 
10471
                    wxRichTextParagraph* child = (wxRichTextParagraph*) node->GetData();
 
10472
                    wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
 
10473
                    while (node2)
 
10474
                    {
 
10475
                        wxRichTextLine* line = node2->GetData();
 
10476
                        wxPoint pt = line->GetAbsolutePosition();
 
10477
                        wxRichTextRange range = line->GetAbsoluteRange();
 
10478
 
 
10479
                        // we want to find the first line that is in the same position
 
10480
                        // as before. This will mean we're at the end of the changed text.
 
10481
 
 
10482
                        if (pt.y > lastY) // going past the end of the window, no more info
 
10483
                        {
 
10484
                            node2 = wxRichTextLineList::compatibility_iterator();
 
10485
                            node = wxRichTextObjectList::compatibility_iterator();
 
10486
                        }
 
10487
                        // Detect last line in the buffer
 
10488
                        else if (!node2->GetNext() && para->GetRange().Contains(container->GetOwnRange().GetEnd()))
 
10489
                        {
 
10490
                            // If deleting text, make sure we refresh below as well as above
 
10491
                            if (positionOffset >= 0)
 
10492
                            {
 
10493
                                foundEnd = true;
 
10494
                                lastY = pt.y + line->GetSize().y;
 
10495
                            }
 
10496
 
 
10497
                            node2 = wxRichTextLineList::compatibility_iterator();
 
10498
                            node = wxRichTextObjectList::compatibility_iterator();
 
10499
 
 
10500
                            break;
 
10501
                        }
 
10502
                        else
 
10503
                        {
 
10504
                            // search for this line being at the same position as before
 
10505
                            for (i = 0; i < optimizationLineCharPositions->GetCount(); i++)
 
10506
                            {
 
10507
                                if (((*optimizationLineCharPositions)[i] + positionOffset == range.GetStart()) &&
 
10508
                                    ((*optimizationLineYPositions)[i] == pt.y))
 
10509
                                {
 
10510
                                    // Stop, we're now the same as we were
 
10511
                                    foundEnd = true;
 
10512
 
 
10513
                                    lastY = pt.y;
 
10514
 
 
10515
                                    node2 = wxRichTextLineList::compatibility_iterator();
 
10516
                                    node = wxRichTextObjectList::compatibility_iterator();
 
10517
 
 
10518
                                    break;
 
10519
                                }
 
10520
                            }
 
10521
                        }
 
10522
 
 
10523
                        if (node2)
 
10524
                            node2 = node2->GetNext();
 
10525
                    }
 
10526
 
 
10527
                    if (node)
 
10528
                        node = node->GetNext();
 
10529
                }
 
10530
 
 
10531
                firstY = wxMax(firstVisiblePt.y, firstY);
 
10532
                if (!foundEnd)
 
10533
                    lastY = firstVisiblePt.y + clientSize.y;
 
10534
 
 
10535
                // Convert to device coordinates
 
10536
                wxRect rect(m_ctrl->GetPhysicalPoint(m_ctrl->GetScaledPoint(wxPoint(firstVisiblePt.x, firstY))), m_ctrl->GetScaledSize(wxSize(clientSize.x, lastY - firstY)));
 
10537
                m_ctrl->RefreshRect(rect);
 
10538
            }
 
10539
            else
 
10540
#endif
 
10541
                m_ctrl->Refresh(false);
 
10542
 
 
10543
            m_ctrl->PositionCaret();
 
10544
 
 
10545
            // This causes styles to persist when doing programmatic
 
10546
            // content creation except when Freeze/Thaw is used, so
 
10547
            // disable this and check for the consequences.
 
10548
            // m_ctrl->SetDefaultStyleToCursorStyle();
 
10549
 
 
10550
            if (sendUpdateEvent)
 
10551
                wxTextCtrl::SendTextUpdatedEvent(m_ctrl);
 
10552
        }
 
10553
    }
 
10554
}
 
10555
 
 
10556
/// Replace the buffer paragraphs with the new ones.
 
10557
void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment)
 
10558
{
 
10559
    wxRichTextParagraphLayoutBox* container = GetContainer();
 
10560
    wxASSERT(container != NULL);
 
10561
    if (!container)
 
10562
        return;
 
10563
 
 
10564
    wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst();
 
10565
    while (node)
 
10566
    {
 
10567
        wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
 
10568
        wxASSERT (para != NULL);
 
10569
 
 
10570
        // We'll replace the existing paragraph by finding the paragraph at this position,
 
10571
        // delete its node data, and setting a copy as the new node data.
 
10572
        // TODO: make more efficient by simply swapping old and new paragraph objects.
 
10573
 
 
10574
        wxRichTextParagraph* existingPara = container->GetParagraphAtPosition(para->GetRange().GetStart());
 
10575
        if (existingPara)
 
10576
        {
 
10577
            wxRichTextObjectList::compatibility_iterator bufferParaNode = container->GetChildren().Find(existingPara);
 
10578
            if (bufferParaNode)
 
10579
            {
 
10580
                wxRichTextParagraph* newPara = new wxRichTextParagraph(*para);
 
10581
                newPara->SetParent(container);
 
10582
 
 
10583
                bufferParaNode->SetData(newPara);
 
10584
 
 
10585
                delete existingPara;
 
10586
            }
 
10587
        }
 
10588
 
 
10589
        node = node->GetNext();
 
10590
    }
 
10591
}
 
10592
 
 
10593
 
 
10594
/*!
 
10595
 * wxRichTextRange
 
10596
 * This stores beginning and end positions for a range of data.
 
10597
 */
 
10598
 
 
10599
WX_DEFINE_OBJARRAY(wxRichTextRangeArray);
 
10600
 
 
10601
/// Limit this range to be within 'range'
 
10602
bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
 
10603
{
 
10604
    if (m_start < range.m_start)
 
10605
        m_start = range.m_start;
 
10606
 
 
10607
    if (m_end > range.m_end)
 
10608
        m_end = range.m_end;
 
10609
 
 
10610
    return true;
 
10611
}
 
10612
 
 
10613
/*!
 
10614
 * wxRichTextImage implementation
 
10615
 * This object represents an image.
 
10616
 */
 
10617
 
 
10618
IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
 
10619
 
 
10620
wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent, wxRichTextAttr* charStyle):
 
10621
    wxRichTextObject(parent)
 
10622
{
 
10623
    Init();
 
10624
    m_imageBlock.MakeImageBlockDefaultQuality(image, wxBITMAP_TYPE_PNG);
 
10625
    if (charStyle)
 
10626
        SetAttributes(*charStyle);
 
10627
}
 
10628
 
 
10629
wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent, wxRichTextAttr* charStyle):
 
10630
    wxRichTextObject(parent)
 
10631
{
 
10632
    Init();
 
10633
    m_imageBlock = imageBlock;
 
10634
    if (charStyle)
 
10635
        SetAttributes(*charStyle);
 
10636
}
 
10637
 
 
10638
void wxRichTextImage::Init()
 
10639
{
 
10640
    m_originalImageSize = wxSize(-1, -1);
 
10641
}
 
10642
 
 
10643
/// Create a cached image at the required size
 
10644
bool wxRichTextImage::LoadImageCache(wxDC& dc, bool resetCache)
 
10645
{
 
10646
    if (!m_imageBlock.IsOk())
 
10647
        return false;
 
10648
 
 
10649
    // If we have an original image size, use that to compute the cached bitmap size
 
10650
    // instead of loading the image each time. This way we can avoid loading
 
10651
    // the image so long as the new cached bitmap size hasn't changed.
 
10652
 
 
10653
    wxImage image;
 
10654
    if (resetCache || m_originalImageSize == wxSize(-1, -1))
 
10655
    {
 
10656
        m_imageCache = wxNullBitmap;
 
10657
 
 
10658
        m_imageBlock.Load(image);
 
10659
        if (!image.IsOk())
 
10660
            return false;
 
10661
 
 
10662
        m_originalImageSize = wxSize(image.GetWidth(), image.GetHeight());
 
10663
    }
 
10664
 
 
10665
    int width = m_originalImageSize.GetWidth();
 
10666
    int height = m_originalImageSize.GetHeight();
 
10667
 
 
10668
    int parentWidth = 0;
 
10669
    int parentHeight = 0;
 
10670
 
 
10671
    int maxWidth = -1;
 
10672
    int maxHeight = -1;
 
10673
 
 
10674
    wxRichTextBuffer* buffer = GetBuffer();
 
10675
    if (buffer)
 
10676
    {
 
10677
        wxSize sz;
 
10678
        if (buffer->GetRichTextCtrl())
 
10679
        {
 
10680
            // Subtract borders
 
10681
            sz = buffer->GetRichTextCtrl()->GetClientSize();
 
10682
 
 
10683
            wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
 
10684
            marginRect = wxRect(0, 0, sz.x, sz.y);
 
10685
            buffer->GetBoxRects(dc, buffer, buffer->GetAttributes(), marginRect, borderRect, contentRect, paddingRect, outlineRect);
 
10686
 
 
10687
            sz = contentRect.GetSize();
 
10688
 
 
10689
            // Start with a maximum width of the control size, even if not specified by the content,
 
10690
            // to minimize the amount of picture overlapping the right-hand side
 
10691
            maxWidth = sz.x;
 
10692
        }
 
10693
        else
 
10694
            sz = buffer->GetCachedSize();
 
10695
        parentWidth = sz.GetWidth();
 
10696
        parentHeight = sz.GetHeight();
 
10697
    }
 
10698
 
 
10699
    if (GetAttributes().GetTextBoxAttr().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetWidth().GetValue() > 0)
 
10700
    {
 
10701
        if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
 
10702
            width = (int) ((GetAttributes().GetTextBoxAttr().GetWidth().GetValue() * parentWidth)/100.0);
 
10703
        else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
 
10704
            width = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetWidth().GetValue());
 
10705
        else if (GetAttributes().GetTextBoxAttr().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
 
10706
            width = GetAttributes().GetTextBoxAttr().GetWidth().GetValue();
 
10707
    }
 
10708
 
 
10709
    // Limit to max width
 
10710
 
 
10711
    if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() > 0)
 
10712
    {
 
10713
        int mw = -1;
 
10714
 
 
10715
        if (parentWidth > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
 
10716
            mw = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue() * parentWidth)/100.0);
 
10717
        else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
 
10718
            mw = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue());
 
10719
        else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
 
10720
            mw = GetAttributes().GetTextBoxAttr().GetMaxSize().GetWidth().GetValue();
 
10721
 
 
10722
        // If we already have a smaller max width due to the constraints of the control size,
 
10723
        // don't use the larger max width.
 
10724
        if (mw != -1 && ((maxWidth == -1) || (mw < maxWidth)))
 
10725
            maxWidth = mw;
 
10726
    }
 
10727
 
 
10728
    if (maxWidth > 0 && width > maxWidth)
 
10729
        width = maxWidth;
 
10730
 
 
10731
    // Preserve the aspect ratio
 
10732
    if (width != m_originalImageSize.GetWidth())
 
10733
        height = (int) (float(m_originalImageSize.GetHeight()) * (float(width)/float(m_originalImageSize.GetWidth())));
 
10734
 
 
10735
    if (GetAttributes().GetTextBoxAttr().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetHeight().GetValue() > 0)
 
10736
    {
 
10737
        if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
 
10738
            height = (int) ((GetAttributes().GetTextBoxAttr().GetHeight().GetValue() * parentHeight)/100.0);
 
10739
        else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
 
10740
            height = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetHeight().GetValue());
 
10741
        else if (GetAttributes().GetTextBoxAttr().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
 
10742
            height = GetAttributes().GetTextBoxAttr().GetHeight().GetValue();
 
10743
 
 
10744
        // Preserve the aspect ratio
 
10745
        if (height != m_originalImageSize.GetHeight())
 
10746
            width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
 
10747
    }
 
10748
 
 
10749
    // Limit to max height
 
10750
 
 
10751
    if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().IsValid() && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() > 0)
 
10752
    {
 
10753
        if (parentHeight > 0 && GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
 
10754
            maxHeight = (int) ((GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue() * parentHeight)/100.0);
 
10755
        else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
 
10756
            maxHeight = ConvertTenthsMMToPixels(dc, GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue());
 
10757
        else if (GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
 
10758
            maxHeight = GetAttributes().GetTextBoxAttr().GetMaxSize().GetHeight().GetValue();
 
10759
    }
 
10760
 
 
10761
    if (maxHeight > 0 && height > maxHeight)
 
10762
    {
 
10763
        height = maxHeight;
 
10764
 
 
10765
        // Preserve the aspect ratio
 
10766
        if (height != m_originalImageSize.GetHeight())
 
10767
            width = (int) (float(m_originalImageSize.GetWidth()) * (float(height)/float(m_originalImageSize.GetHeight())));
 
10768
    }
 
10769
 
 
10770
    if (m_imageCache.IsOk() && m_imageCache.GetWidth() == width && m_imageCache.GetHeight() == height)
 
10771
    {
 
10772
        // Do nothing, we didn't need to change the image cache
 
10773
    }
 
10774
    else
 
10775
    {
 
10776
        if (!image.IsOk())
 
10777
        {
 
10778
            m_imageBlock.Load(image);
 
10779
            if (!image.IsOk())
 
10780
                return false;
 
10781
        }
 
10782
 
 
10783
        if (image.GetWidth() == width && image.GetHeight() == height)
 
10784
            m_imageCache = wxBitmap(image);
 
10785
        else
 
10786
        {
 
10787
            // If the original width and height is small, e.g. 400 or below,
 
10788
            // scale up and then down to improve image quality. This can make
 
10789
            // a big difference, with not much performance hit.
 
10790
            int upscaleThreshold = 400;
 
10791
            wxImage img;
 
10792
            if (image.GetWidth() <= upscaleThreshold || image.GetHeight() <= upscaleThreshold)
 
10793
            {
 
10794
                img = image.Scale(image.GetWidth()*2, image.GetHeight()*2);
 
10795
                img.Rescale(width, height, wxIMAGE_QUALITY_HIGH);
 
10796
            }
 
10797
            else
 
10798
                img = image.Scale(width, height, wxIMAGE_QUALITY_HIGH);
 
10799
            m_imageCache = wxBitmap(img);
 
10800
        }
 
10801
    }
 
10802
 
 
10803
    return m_imageCache.IsOk();
 
10804
}
 
10805
 
 
10806
/// Draw the item
 
10807
bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wxRichTextRange& WXUNUSED(range), const wxRichTextSelection& selection, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
 
10808
{
 
10809
    if (!IsShown())
 
10810
        return true;
 
10811
 
 
10812
    // Don't need cached size AFAIK
 
10813
    // wxSize size = GetCachedSize();
 
10814
    if (!LoadImageCache(dc))
 
10815
        return false;
 
10816
 
 
10817
    wxRichTextAttr attr(GetAttributes());
 
10818
    context.ApplyVirtualAttributes(attr, this);
 
10819
 
 
10820
    DrawBoxAttributes(dc, GetBuffer(), attr, wxRect(rect.GetPosition(), GetCachedSize()));
 
10821
 
 
10822
    wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
 
10823
    wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
 
10824
    marginRect = rect; // outer rectangle, will calculate contentRect
 
10825
    GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
 
10826
 
 
10827
    dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true);
 
10828
 
 
10829
    if (selection.WithinSelection(GetRange().GetStart(), this))
 
10830
    {
 
10831
        wxCheckSetBrush(dc, *wxBLACK_BRUSH);
 
10832
        wxCheckSetPen(dc, *wxBLACK_PEN);
 
10833
        dc.SetLogicalFunction(wxINVERT);
 
10834
        dc.DrawRectangle(contentRect);
 
10835
        dc.SetLogicalFunction(wxCOPY);
 
10836
    }
 
10837
 
 
10838
    return true;
 
10839
}
 
10840
 
 
10841
/// Lay the item out
 
10842
bool wxRichTextImage::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& WXUNUSED(parentRect), int WXUNUSED(style))
 
10843
{
 
10844
    if (!LoadImageCache(dc))
 
10845
        return false;
 
10846
 
 
10847
    wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
 
10848
    wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
 
10849
    contentRect = wxRect(wxPoint(0,0), imageSize);
 
10850
 
 
10851
    wxRichTextAttr attr(GetAttributes());
 
10852
    context.ApplyVirtualAttributes(attr, this);
 
10853
 
 
10854
    GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
 
10855
 
 
10856
    wxSize overallSize = marginRect.GetSize();
 
10857
 
 
10858
    SetCachedSize(overallSize);
 
10859
    SetMaxSize(overallSize);
 
10860
    SetMinSize(overallSize);
 
10861
    SetPosition(rect.GetPosition());
 
10862
 
 
10863
    return true;
 
10864
}
 
10865
 
 
10866
/// Get/set the object size for the given range. Returns false if the range
 
10867
/// is invalid for this object.
 
10868
bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& dc, wxRichTextDrawingContext& context, int WXUNUSED(flags), wxPoint WXUNUSED(position), wxArrayInt* partialExtents) const
 
10869
{
 
10870
    if (!range.IsWithin(GetRange()))
 
10871
        return false;
 
10872
 
 
10873
    if (!((wxRichTextImage*)this)->LoadImageCache(dc))
 
10874
    {
 
10875
        size.x = 0; size.y = 0;
 
10876
        if (partialExtents)
 
10877
            partialExtents->Add(0);
 
10878
        return false;
 
10879
    }
 
10880
 
 
10881
    wxRichTextAttr attr(GetAttributes());
 
10882
    context.ApplyVirtualAttributes(attr, (wxRichTextObject*) this);
 
10883
 
 
10884
    wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
 
10885
    wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
 
10886
    contentRect = wxRect(wxPoint(0,0), imageSize);
 
10887
    GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
 
10888
 
 
10889
    wxSize overallSize = marginRect.GetSize();
 
10890
 
 
10891
    if (partialExtents)
 
10892
        partialExtents->Add(overallSize.x);
 
10893
 
 
10894
    size = overallSize;
 
10895
 
 
10896
    return true;
 
10897
}
 
10898
 
 
10899
// Get the 'natural' size for an object. For an image, it would be the
 
10900
// image size.
 
10901
wxTextAttrSize wxRichTextImage::GetNaturalSize() const
 
10902
{
 
10903
    wxTextAttrSize size;
 
10904
    if (GetImageCache().IsOk())
 
10905
    {
 
10906
        size.SetWidth(GetImageCache().GetWidth(), wxTEXT_ATTR_UNITS_PIXELS);
 
10907
        size.SetHeight(GetImageCache().GetHeight(), wxTEXT_ATTR_UNITS_PIXELS);
 
10908
    }
 
10909
    return size;
 
10910
}
 
10911
 
 
10912
 
 
10913
/// Copy
 
10914
void wxRichTextImage::Copy(const wxRichTextImage& obj)
 
10915
{
 
10916
    wxRichTextObject::Copy(obj);
 
10917
 
 
10918
    m_imageBlock = obj.m_imageBlock;
 
10919
    m_originalImageSize = obj.m_originalImageSize;
 
10920
}
 
10921
 
 
10922
/// Edit properties via a GUI
 
10923
bool wxRichTextImage::EditProperties(wxWindow* parent, wxRichTextBuffer* buffer)
 
10924
{
 
10925
    wxRichTextObjectPropertiesDialog imageDlg(this, wxGetTopLevelParent(parent), wxID_ANY, _("Picture Properties"));
 
10926
    imageDlg.SetAttributes(GetAttributes());
 
10927
 
 
10928
    if (imageDlg.ShowModal() == wxID_OK)
 
10929
    {
 
10930
        // By passing wxRICHTEXT_SETSTYLE_RESET, indeterminate attributes set by the user will be set as
 
10931
        // indeterminate in the object.
 
10932
        imageDlg.ApplyStyle(buffer->GetRichTextCtrl(), wxRICHTEXT_SETSTYLE_WITH_UNDO|wxRICHTEXT_SETSTYLE_RESET);
 
10933
        return true;
 
10934
    }
 
10935
    else
 
10936
        return false;
 
10937
}
 
10938
 
 
10939
/*!
 
10940
 * Utilities
 
10941
 *
 
10942
 */
 
10943
 
 
10944
/// Compare two attribute objects
 
10945
bool wxTextAttrEq(const wxRichTextAttr& attr1, const wxRichTextAttr& attr2)
 
10946
{
 
10947
    return (attr1 == attr2);
 
10948
}
 
10949
 
 
10950
/// Compare tabs
 
10951
bool wxRichTextTabsEq(const wxArrayInt& tabs1, const wxArrayInt& tabs2)
 
10952
{
 
10953
    if (tabs1.GetCount() != tabs2.GetCount())
 
10954
        return false;
 
10955
 
 
10956
    size_t i;
 
10957
    for (i = 0; i < tabs1.GetCount(); i++)
 
10958
    {
 
10959
        if (tabs1[i] != tabs2[i])
 
10960
            return false;
 
10961
    }
 
10962
    return true;
 
10963
}
 
10964
 
 
10965
bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style, wxRichTextAttr* compareWith)
 
10966
{
 
10967
    return destStyle.Apply(style, compareWith);
 
10968
}
 
10969
 
 
10970
// Remove attributes
 
10971
bool wxRichTextRemoveStyle(wxRichTextAttr& destStyle, const wxRichTextAttr& style)
 
10972
{
 
10973
    return destStyle.RemoveStyle(style);
 
10974
}
 
10975
 
 
10976
/// Combine two bitlists, specifying the bits of interest with separate flags.
 
10977
bool wxRichTextCombineBitlists(int& valueA, int valueB, int& flagsA, int flagsB)
 
10978
{
 
10979
    return wxRichTextAttr::CombineBitlists(valueA, valueB, flagsA, flagsB);
 
10980
}
 
10981
 
 
10982
/// Compare two bitlists
 
10983
bool wxRichTextBitlistsEqPartial(int valueA, int valueB, int flags)
 
10984
{
 
10985
    return wxRichTextAttr::BitlistsEqPartial(valueA, valueB, flags);
 
10986
}
 
10987
 
 
10988
/// Split into paragraph and character styles
 
10989
bool wxRichTextSplitParaCharStyles(const wxRichTextAttr& style, wxRichTextAttr& parStyle, wxRichTextAttr& charStyle)
 
10990
{
 
10991
    return wxRichTextAttr::SplitParaCharStyles(style, parStyle, charStyle);
 
10992
}
 
10993
 
 
10994
/// Convert a decimal to Roman numerals
 
10995
wxString wxRichTextDecimalToRoman(long n)
 
10996
{
 
10997
    static wxArrayInt decimalNumbers;
 
10998
    static wxArrayString romanNumbers;
 
10999
 
 
11000
    // Clean up arrays
 
11001
    if (n == -1)
 
11002
    {
 
11003
        decimalNumbers.Clear();
 
11004
        romanNumbers.Clear();
 
11005
        return wxEmptyString;
 
11006
    }
 
11007
 
 
11008
    if (decimalNumbers.GetCount() == 0)
 
11009
    {
 
11010
        #define wxRichTextAddDecRom(n, r) decimalNumbers.Add(n); romanNumbers.Add(r);
 
11011
 
 
11012
        wxRichTextAddDecRom(1000, wxT("M"));
 
11013
        wxRichTextAddDecRom(900, wxT("CM"));
 
11014
        wxRichTextAddDecRom(500, wxT("D"));
 
11015
        wxRichTextAddDecRom(400, wxT("CD"));
 
11016
        wxRichTextAddDecRom(100, wxT("C"));
 
11017
        wxRichTextAddDecRom(90, wxT("XC"));
 
11018
        wxRichTextAddDecRom(50, wxT("L"));
 
11019
        wxRichTextAddDecRom(40, wxT("XL"));
 
11020
        wxRichTextAddDecRom(10, wxT("X"));
 
11021
        wxRichTextAddDecRom(9, wxT("IX"));
 
11022
        wxRichTextAddDecRom(5, wxT("V"));
 
11023
        wxRichTextAddDecRom(4, wxT("IV"));
 
11024
        wxRichTextAddDecRom(1, wxT("I"));
 
11025
    }
 
11026
 
 
11027
    int i = 0;
 
11028
    wxString roman;
 
11029
 
 
11030
    while (n > 0 && i < 13)
 
11031
    {
 
11032
        if (n >= decimalNumbers[i])
 
11033
        {
 
11034
            n -= decimalNumbers[i];
 
11035
            roman += romanNumbers[i];
 
11036
        }
 
11037
        else
 
11038
        {
 
11039
            i ++;
 
11040
        }
 
11041
    }
 
11042
    if (roman.IsEmpty())
 
11043
        roman = wxT("0");
 
11044
    return roman;
 
11045
}
 
11046
 
 
11047
/*!
 
11048
 * wxRichTextFileHandler
 
11049
 * Base class for file handlers
 
11050
 */
 
11051
 
 
11052
IMPLEMENT_CLASS(wxRichTextFileHandler, wxObject)
 
11053
 
 
11054
#if wxUSE_FFILE && wxUSE_STREAMS
 
11055
bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer *buffer, const wxString& filename)
 
11056
{
 
11057
    wxFFileInputStream stream(filename);
 
11058
    if (stream.IsOk())
 
11059
        return LoadFile(buffer, stream);
 
11060
 
 
11061
    return false;
 
11062
}
 
11063
 
 
11064
bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer *buffer, const wxString& filename)
 
11065
{
 
11066
    wxFFileOutputStream stream(filename);
 
11067
    if (stream.IsOk())
 
11068
        return SaveFile(buffer, stream);
 
11069
 
 
11070
    return false;
 
11071
}
 
11072
#endif // wxUSE_FFILE && wxUSE_STREAMS
 
11073
 
 
11074
/// Can we handle this filename (if using files)? By default, checks the extension.
 
11075
bool wxRichTextFileHandler::CanHandle(const wxString& filename) const
 
11076
{
 
11077
    wxString path, file, ext;
 
11078
    wxFileName::SplitPath(filename, & path, & file, & ext);
 
11079
 
 
11080
    return (ext.Lower() == GetExtension());
 
11081
}
 
11082
 
 
11083
/*!
 
11084
 * wxRichTextTextHandler
 
11085
 * Plain text handler
 
11086
 */
 
11087
 
 
11088
IMPLEMENT_CLASS(wxRichTextPlainTextHandler, wxRichTextFileHandler)
 
11089
 
 
11090
#if wxUSE_STREAMS
 
11091
bool wxRichTextPlainTextHandler::DoLoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
 
11092
{
 
11093
    if (!stream.IsOk())
 
11094
        return false;
 
11095
 
 
11096
    wxString str;
 
11097
    int lastCh = 0;
 
11098
 
 
11099
    while (!stream.Eof())
 
11100
    {
 
11101
        int ch = stream.GetC();
 
11102
 
 
11103
        if (!stream.Eof())
 
11104
        {
 
11105
            if (ch == 10 && lastCh != 13)
 
11106
                str += wxT('\n');
 
11107
 
 
11108
            if (ch > 0 && ch != 10)
 
11109
                str += wxChar(ch);
 
11110
 
 
11111
            lastCh = ch;
 
11112
        }
 
11113
    }
 
11114
 
 
11115
    buffer->ResetAndClearCommands();
 
11116
    buffer->Clear();
 
11117
    buffer->AddParagraphs(str);
 
11118
    buffer->UpdateRanges();
 
11119
 
 
11120
    return true;
 
11121
}
 
11122
 
 
11123
bool wxRichTextPlainTextHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
 
11124
{
 
11125
    if (!stream.IsOk())
 
11126
        return false;
 
11127
 
 
11128
    wxString text = buffer->GetText();
 
11129
 
 
11130
    wxString newLine = wxRichTextLineBreakChar;
 
11131
    text.Replace(newLine, wxT("\n"));
 
11132
 
 
11133
    wxCharBuffer buf = text.ToAscii();
 
11134
 
 
11135
    stream.Write((const char*) buf, text.length());
 
11136
    return true;
 
11137
}
 
11138
#endif // wxUSE_STREAMS
 
11139
 
 
11140
/*
 
11141
 * Stores information about an image, in binary in-memory form
 
11142
 */
 
11143
 
 
11144
wxRichTextImageBlock::wxRichTextImageBlock()
 
11145
{
 
11146
    Init();
 
11147
}
 
11148
 
 
11149
wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock& block):wxObject()
 
11150
{
 
11151
    Init();
 
11152
    Copy(block);
 
11153
}
 
11154
 
 
11155
wxRichTextImageBlock::~wxRichTextImageBlock()
 
11156
{
 
11157
    wxDELETEA(m_data);
 
11158
}
 
11159
 
 
11160
void wxRichTextImageBlock::Init()
 
11161
{
 
11162
    m_data = NULL;
 
11163
    m_dataSize = 0;
 
11164
    m_imageType = wxBITMAP_TYPE_INVALID;
 
11165
}
 
11166
 
 
11167
void wxRichTextImageBlock::Clear()
 
11168
{
 
11169
    wxDELETEA(m_data);
 
11170
    m_dataSize = 0;
 
11171
    m_imageType = wxBITMAP_TYPE_INVALID;
 
11172
}
 
11173
 
 
11174
 
 
11175
// Load the original image into a memory block.
 
11176
// If the image is not a JPEG, we must convert it into a JPEG
 
11177
// to conserve space.
 
11178
// If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
 
11179
// load the image a 2nd time.
 
11180
 
 
11181
bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, wxBitmapType imageType,
 
11182
                                          wxImage& image, bool convertToJPEG)
 
11183
{
 
11184
    m_imageType = imageType;
 
11185
 
 
11186
    wxString filenameToRead(filename);
 
11187
    bool removeFile = false;
 
11188
 
 
11189
    if (imageType == wxBITMAP_TYPE_INVALID)
 
11190
        return false; // Could not determine image type
 
11191
 
 
11192
    if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
 
11193
    {
 
11194
        wxString tempFile =
 
11195
            wxFileName::CreateTempFileName(_("image"));
 
11196
 
 
11197
        wxASSERT(!tempFile.IsEmpty());
 
11198
 
 
11199
        image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
 
11200
        filenameToRead = tempFile;
 
11201
        removeFile = true;
 
11202
 
 
11203
        m_imageType = wxBITMAP_TYPE_JPEG;
 
11204
    }
 
11205
    wxFile file;
 
11206
    if (!file.Open(filenameToRead))
 
11207
        return false;
 
11208
 
 
11209
    m_dataSize = (size_t) file.Length();
 
11210
    file.Close();
 
11211
 
 
11212
    if (m_data)
 
11213
        delete[] m_data;
 
11214
    m_data = ReadBlock(filenameToRead, m_dataSize);
 
11215
 
 
11216
    if (removeFile)
 
11217
        wxRemoveFile(filenameToRead);
 
11218
 
 
11219
    return (m_data != NULL);
 
11220
}
 
11221
 
 
11222
// Make an image block from the wxImage in the given
 
11223
// format.
 
11224
bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, wxBitmapType imageType, int quality)
 
11225
{
 
11226
    image.SetOption(wxT("quality"), quality);
 
11227
 
 
11228
    if (imageType == wxBITMAP_TYPE_INVALID)
 
11229
        return false; // Could not determine image type
 
11230
 
 
11231
    return DoMakeImageBlock(image, imageType);
 
11232
}
 
11233
 
 
11234
// Uses a const wxImage for efficiency, but can't set quality (only relevant for JPEG)
 
11235
bool wxRichTextImageBlock::MakeImageBlockDefaultQuality(const wxImage& image, wxBitmapType imageType)
 
11236
{
 
11237
    if (imageType == wxBITMAP_TYPE_INVALID)
 
11238
        return false; // Could not determine image type
 
11239
 
 
11240
    return DoMakeImageBlock(image, imageType);
 
11241
}
 
11242
 
 
11243
// Makes the image block
 
11244
bool wxRichTextImageBlock::DoMakeImageBlock(const wxImage& image, wxBitmapType imageType)
 
11245
{
 
11246
    wxMemoryOutputStream memStream;
 
11247
    if (!image.SaveFile(memStream, imageType))
 
11248
    {
 
11249
        return false;
 
11250
    }
 
11251
 
 
11252
    unsigned char* block = new unsigned char[memStream.GetSize()];
 
11253
    if (!block)
 
11254
        return false;
 
11255
 
 
11256
    if (m_data)
 
11257
        delete[] m_data;
 
11258
    m_data = block;
 
11259
 
 
11260
    m_imageType = imageType;
 
11261
    m_dataSize = memStream.GetSize();
 
11262
 
 
11263
    memStream.CopyTo(m_data, m_dataSize);
 
11264
 
 
11265
    return (m_data != NULL);
 
11266
}
 
11267
 
 
11268
// Write to a file
 
11269
bool wxRichTextImageBlock::Write(const wxString& filename)
 
11270
{
 
11271
    return WriteBlock(filename, m_data, m_dataSize);
 
11272
}
 
11273
 
 
11274
void wxRichTextImageBlock::Copy(const wxRichTextImageBlock& block)
 
11275
{
 
11276
    m_imageType = block.m_imageType;
 
11277
    wxDELETEA(m_data);
 
11278
    m_dataSize = block.m_dataSize;
 
11279
    if (m_dataSize == 0)
 
11280
        return;
 
11281
 
 
11282
    m_data = new unsigned char[m_dataSize];
 
11283
    unsigned int i;
 
11284
    for (i = 0; i < m_dataSize; i++)
 
11285
        m_data[i] = block.m_data[i];
 
11286
}
 
11287
 
 
11288
//// Operators
 
11289
void wxRichTextImageBlock::operator=(const wxRichTextImageBlock& block)
 
11290
{
 
11291
    Copy(block);
 
11292
}
 
11293
 
 
11294
// Load a wxImage from the block
 
11295
bool wxRichTextImageBlock::Load(wxImage& image)
 
11296
{
 
11297
    if (!m_data)
 
11298
        return false;
 
11299
 
 
11300
    // Read in the image.
 
11301
#if wxUSE_STREAMS
 
11302
    wxMemoryInputStream mstream(m_data, m_dataSize);
 
11303
    bool success = image.LoadFile(mstream, GetImageType());
 
11304
#else
 
11305
    wxString tempFile = wxFileName::CreateTempFileName(_("image"));
 
11306
    wxASSERT(!tempFile.IsEmpty());
 
11307
 
 
11308
    if (!WriteBlock(tempFile, m_data, m_dataSize))
 
11309
    {
 
11310
        return false;
 
11311
    }
 
11312
    success = image.LoadFile(tempFile, GetImageType());
 
11313
    wxRemoveFile(tempFile);
 
11314
#endif
 
11315
 
 
11316
    return success;
 
11317
}
 
11318
 
 
11319
// Write data in hex to a stream
 
11320
bool wxRichTextImageBlock::WriteHex(wxOutputStream& stream)
 
11321
{
 
11322
    if (m_dataSize == 0)
 
11323
        return true;
 
11324
 
 
11325
    int bufSize = 100000;
 
11326
    if (int(2*m_dataSize) < bufSize)
 
11327
        bufSize = 2*m_dataSize;
 
11328
    char* buf = new char[bufSize+1];
 
11329
 
 
11330
    int left = m_dataSize;
 
11331
    int n, i, j;
 
11332
    j = 0;
 
11333
    while (left > 0)
 
11334
    {
 
11335
        if (left*2 > bufSize)
 
11336
        {
 
11337
            n = bufSize; left -= (bufSize/2);
 
11338
        }
 
11339
        else
 
11340
        {
 
11341
            n = left*2; left = 0;
 
11342
        }
 
11343
 
 
11344
        char* b = buf;
 
11345
        for (i = 0; i < (n/2); i++)
 
11346
        {
 
11347
            wxDecToHex(m_data[j], b, b+1);
 
11348
            b += 2; j ++;
 
11349
        }
 
11350
 
 
11351
        buf[n] = 0;
 
11352
        stream.Write((const char*) buf, n);
 
11353
    }
 
11354
    delete[] buf;
 
11355
    return true;
 
11356
}
 
11357
 
 
11358
// Read data in hex from a stream
 
11359
bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, wxBitmapType imageType)
 
11360
{
 
11361
    int dataSize = length/2;
 
11362
 
 
11363
    if (m_data)
 
11364
        delete[] m_data;
 
11365
 
 
11366
    // create a null terminated temporary string:
 
11367
    char str[3];
 
11368
    str[2] = '\0';
 
11369
 
 
11370
    m_data = new unsigned char[dataSize];
 
11371
    int i;
 
11372
    for (i = 0; i < dataSize; i ++)
 
11373
    {
 
11374
        str[0] = (char)stream.GetC();
 
11375
        str[1] = (char)stream.GetC();
 
11376
 
 
11377
        m_data[i] = (unsigned char)wxHexToDec(str);
 
11378
    }
 
11379
 
 
11380
    m_dataSize = dataSize;
 
11381
    m_imageType = imageType;
 
11382
 
 
11383
    return true;
 
11384
}
 
11385
 
 
11386
// Allocate and read from stream as a block of memory
 
11387
unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream& stream, size_t size)
 
11388
{
 
11389
    unsigned char* block = new unsigned char[size];
 
11390
    if (!block)
 
11391
        return NULL;
 
11392
 
 
11393
    stream.Read(block, size);
 
11394
 
 
11395
    return block;
 
11396
}
 
11397
 
 
11398
unsigned char* wxRichTextImageBlock::ReadBlock(const wxString& filename, size_t size)
 
11399
{
 
11400
    wxFileInputStream stream(filename);
 
11401
    if (!stream.IsOk())
 
11402
        return NULL;
 
11403
 
 
11404
    return ReadBlock(stream, size);
 
11405
}
 
11406
 
 
11407
// Write memory block to stream
 
11408
bool wxRichTextImageBlock::WriteBlock(wxOutputStream& stream, unsigned char* block, size_t size)
 
11409
{
 
11410
    stream.Write((void*) block, size);
 
11411
    return stream.IsOk();
 
11412
 
 
11413
}
 
11414
 
 
11415
// Write memory block to file
 
11416
bool wxRichTextImageBlock::WriteBlock(const wxString& filename, unsigned char* block, size_t size)
 
11417
{
 
11418
    wxFileOutputStream outStream(filename);
 
11419
    if (!outStream.IsOk())
 
11420
        return false;
 
11421
 
 
11422
    return WriteBlock(outStream, block, size);
 
11423
}
 
11424
 
 
11425
// Gets the extension for the block's type
 
11426
wxString wxRichTextImageBlock::GetExtension() const
 
11427
{
 
11428
    wxImageHandler* handler = wxImage::FindHandler(GetImageType());
 
11429
    if (handler)
 
11430
        return handler->GetExtension();
 
11431
    else
 
11432
        return wxEmptyString;
 
11433
}
 
11434
 
 
11435
#if wxUSE_DATAOBJ
 
11436
 
 
11437
/*!
 
11438
 * The data object for a wxRichTextBuffer
 
11439
 */
 
11440
 
 
11441
const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxShape");
 
11442
 
 
11443
wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer)
 
11444
{
 
11445
    m_richTextBuffer = richTextBuffer;
 
11446
 
 
11447
    // this string should uniquely identify our format, but is otherwise
 
11448
    // arbitrary
 
11449
    m_formatRichTextBuffer.SetId(GetRichTextBufferFormatId());
 
11450
 
 
11451
    SetFormat(m_formatRichTextBuffer);
 
11452
}
 
11453
 
 
11454
wxRichTextBufferDataObject::~wxRichTextBufferDataObject()
 
11455
{
 
11456
    delete m_richTextBuffer;
 
11457
}
 
11458
 
 
11459
// after a call to this function, the richTextBuffer is owned by the caller and it
 
11460
// is responsible for deleting it!
 
11461
wxRichTextBuffer* wxRichTextBufferDataObject::GetRichTextBuffer()
 
11462
{
 
11463
    wxRichTextBuffer* richTextBuffer = m_richTextBuffer;
 
11464
    m_richTextBuffer = NULL;
 
11465
 
 
11466
    return richTextBuffer;
 
11467
}
 
11468
 
 
11469
wxDataFormat wxRichTextBufferDataObject::GetPreferredFormat(Direction WXUNUSED(dir)) const
 
11470
{
 
11471
    return m_formatRichTextBuffer;
 
11472
}
 
11473
 
 
11474
size_t wxRichTextBufferDataObject::GetDataSize() const
 
11475
{
 
11476
    if (!m_richTextBuffer)
 
11477
        return 0;
 
11478
 
 
11479
    wxString bufXML;
 
11480
 
 
11481
    {
 
11482
        wxStringOutputStream stream(& bufXML);
 
11483
        if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
 
11484
        {
 
11485
            wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
 
11486
            return 0;
 
11487
        }
 
11488
    }
 
11489
 
 
11490
#if wxUSE_UNICODE
 
11491
    wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
 
11492
    return strlen(buffer) + 1;
 
11493
#else
 
11494
    return bufXML.Length()+1;
 
11495
#endif
 
11496
}
 
11497
 
 
11498
bool wxRichTextBufferDataObject::GetDataHere(void *pBuf) const
 
11499
{
 
11500
    if (!pBuf || !m_richTextBuffer)
 
11501
        return false;
 
11502
 
 
11503
    wxString bufXML;
 
11504
 
 
11505
    {
 
11506
        wxStringOutputStream stream(& bufXML);
 
11507
        if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML))
 
11508
        {
 
11509
            wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler."));
 
11510
            return 0;
 
11511
        }
 
11512
    }
 
11513
 
 
11514
#if wxUSE_UNICODE
 
11515
    wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8);
 
11516
    size_t len = strlen(buffer);
 
11517
    memcpy((char*) pBuf, (const char*) buffer, len);
 
11518
    ((char*) pBuf)[len] = 0;
 
11519
#else
 
11520
    size_t len = bufXML.Length();
 
11521
    memcpy((char*) pBuf, (const char*) bufXML.c_str(), len);
 
11522
    ((char*) pBuf)[len] = 0;
 
11523
#endif
 
11524
 
 
11525
    return true;
 
11526
}
 
11527
 
 
11528
bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len), const void *buf)
 
11529
{
 
11530
    wxDELETE(m_richTextBuffer);
 
11531
 
 
11532
    wxString bufXML((const char*) buf, wxConvUTF8);
 
11533
 
 
11534
    m_richTextBuffer = new wxRichTextBuffer;
 
11535
 
 
11536
    wxStringInputStream stream(bufXML);
 
11537
    if (!m_richTextBuffer->LoadFile(stream, wxRICHTEXT_TYPE_XML))
 
11538
    {
 
11539
        wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler."));
 
11540
 
 
11541
        wxDELETE(m_richTextBuffer);
 
11542
 
 
11543
        return false;
 
11544
    }
 
11545
    return true;
 
11546
}
 
11547
 
 
11548
#endif
 
11549
    // wxUSE_DATAOBJ
 
11550
 
 
11551
 
 
11552
/*
 
11553
 * wxRichTextFontTable
 
11554
 * Manages quick access to a pool of fonts for rendering rich text
 
11555
 */
 
11556
 
 
11557
WX_DECLARE_STRING_HASH_MAP_WITH_DECL(wxFont, wxRichTextFontTableHashMap, class WXDLLIMPEXP_RICHTEXT);
 
11558
 
 
11559
class wxRichTextFontTableData: public wxObjectRefData
 
11560
{
 
11561
public:
 
11562
    wxRichTextFontTableData() {}
 
11563
 
 
11564
    wxFont FindFont(const wxRichTextAttr& fontSpec, double fontScale);
 
11565
 
 
11566
    wxRichTextFontTableHashMap  m_hashMap;
 
11567
};
 
11568
 
 
11569
wxFont wxRichTextFontTableData::FindFont(const wxRichTextAttr& fontSpec, double fontScale)
 
11570
{
 
11571
    wxString facename(fontSpec.GetFontFaceName());
 
11572
 
 
11573
    int fontSize = fontSpec.GetFontSize();
 
11574
    if (fontScale != 1.0)
 
11575
        fontSize = (int) ((double(fontSize) * fontScale) + 0.5);
 
11576
 
 
11577
    wxString units;
 
11578
    if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
 
11579
        units = wxT("px");
 
11580
    else
 
11581
        units = wxT("pt");
 
11582
    wxString spec = wxString::Format(wxT("%d-%s-%d-%d-%d-%d-%s-%d"),
 
11583
        fontSize, units.c_str(), fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), (int) fontSpec.GetFontUnderlined(), (int) fontSpec.GetFontStrikethrough(),
 
11584
        facename.c_str(), (int) fontSpec.GetFontEncoding());
 
11585
 
 
11586
    wxRichTextFontTableHashMap::iterator entry = m_hashMap.find(spec);
 
11587
    if ( entry == m_hashMap.end() )
 
11588
    {
 
11589
        if (fontSpec.HasFontPixelSize() && !fontSpec.HasFontPointSize())
 
11590
        {
 
11591
            wxFont font(wxSize(0, fontSize), wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename);
 
11592
            if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
 
11593
                font.SetStrikethrough(true);
 
11594
            m_hashMap[spec] = font;
 
11595
            return font;
 
11596
        }
 
11597
        else
 
11598
        {
 
11599
            wxFont font(fontSize, wxFONTFAMILY_DEFAULT, fontSpec.GetFontStyle(), fontSpec.GetFontWeight(), fontSpec.GetFontUnderlined(), facename.c_str());
 
11600
            if (fontSpec.HasFontStrikethrough() && fontSpec.GetFontStrikethrough())
 
11601
                font.SetStrikethrough(true);
 
11602
 
 
11603
            m_hashMap[spec] = font;
 
11604
            return font;
 
11605
        }
 
11606
    }
 
11607
    else
 
11608
    {
 
11609
        return entry->second;
 
11610
    }
 
11611
}
 
11612
 
 
11613
IMPLEMENT_DYNAMIC_CLASS(wxRichTextFontTable, wxObject)
 
11614
 
 
11615
wxRichTextFontTable::wxRichTextFontTable()
 
11616
{
 
11617
    m_refData = new wxRichTextFontTableData;
 
11618
    m_fontScale = 1.0;
 
11619
}
 
11620
 
 
11621
wxRichTextFontTable::wxRichTextFontTable(const wxRichTextFontTable& table)
 
11622
    : wxObject()
 
11623
{
 
11624
    (*this) = table;
 
11625
}
 
11626
 
 
11627
wxRichTextFontTable::~wxRichTextFontTable()
 
11628
{
 
11629
    UnRef();
 
11630
}
 
11631
 
 
11632
bool wxRichTextFontTable::operator == (const wxRichTextFontTable& table) const
 
11633
{
 
11634
    return (m_refData == table.m_refData);
 
11635
}
 
11636
 
 
11637
void wxRichTextFontTable::operator= (const wxRichTextFontTable& table)
 
11638
{
 
11639
    Ref(table);
 
11640
    m_fontScale = table.m_fontScale;
 
11641
}
 
11642
 
 
11643
wxFont wxRichTextFontTable::FindFont(const wxRichTextAttr& fontSpec)
 
11644
{
 
11645
    wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
 
11646
    if (data)
 
11647
        return data->FindFont(fontSpec, m_fontScale);
 
11648
    else
 
11649
        return wxFont();
 
11650
}
 
11651
 
 
11652
void wxRichTextFontTable::Clear()
 
11653
{
 
11654
    wxRichTextFontTableData* data = (wxRichTextFontTableData*) m_refData;
 
11655
    if (data)
 
11656
        data->m_hashMap.clear();
 
11657
}
 
11658
 
 
11659
void wxRichTextFontTable::SetFontScale(double fontScale)
 
11660
{
 
11661
    if (fontScale != m_fontScale)
 
11662
        Clear();
 
11663
    m_fontScale = fontScale;
 
11664
}
 
11665
 
 
11666
// wxTextBoxAttr
 
11667
 
 
11668
void wxTextBoxAttr::Reset()
 
11669
{
 
11670
    m_flags = 0;
 
11671
    m_floatMode = wxTEXT_BOX_ATTR_FLOAT_NONE;
 
11672
    m_clearMode = wxTEXT_BOX_ATTR_CLEAR_NONE;
 
11673
    m_collapseMode = wxTEXT_BOX_ATTR_COLLAPSE_NONE;
 
11674
    m_verticalAlignment = wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT_NONE;
 
11675
    m_boxStyleName = wxEmptyString;
 
11676
 
 
11677
    m_margins.Reset();
 
11678
    m_padding.Reset();
 
11679
    m_position.Reset();
 
11680
 
 
11681
    m_size.Reset();
 
11682
    m_minSize.Reset();
 
11683
    m_maxSize.Reset();
 
11684
 
 
11685
    m_border.Reset();
 
11686
    m_outline.Reset();
 
11687
}
 
11688
 
 
11689
// Equality test
 
11690
bool wxTextBoxAttr::operator== (const wxTextBoxAttr& attr) const
 
11691
{
 
11692
    return (
 
11693
        m_flags == attr.m_flags &&
 
11694
        m_floatMode == attr.m_floatMode &&
 
11695
        m_clearMode == attr.m_clearMode &&
 
11696
        m_collapseMode == attr.m_collapseMode &&
 
11697
        m_verticalAlignment == attr.m_verticalAlignment &&
 
11698
 
 
11699
        m_margins == attr.m_margins &&
 
11700
        m_padding == attr.m_padding &&
 
11701
        m_position == attr.m_position &&
 
11702
 
 
11703
        m_size == attr.m_size &&
 
11704
        m_minSize == attr.m_minSize &&
 
11705
        m_maxSize == attr.m_maxSize &&
 
11706
 
 
11707
        m_border == attr.m_border &&
 
11708
        m_outline == attr.m_outline &&
 
11709
 
 
11710
        m_boxStyleName == attr.m_boxStyleName
 
11711
        );
 
11712
}
 
11713
 
 
11714
// Partial equality test
 
11715
bool wxTextBoxAttr::EqPartial(const wxTextBoxAttr& attr, bool weakTest) const
 
11716
{
 
11717
    if (!weakTest &&
 
11718
            ((!HasFloatMode() && attr.HasFloatMode()) ||
 
11719
             (!HasClearMode() && attr.HasClearMode()) ||
 
11720
             (!HasCollapseBorders() && attr.HasCollapseBorders()) ||
 
11721
             (!HasVerticalAlignment() && attr.HasVerticalAlignment()) ||
 
11722
             (!HasBoxStyleName() && attr.HasBoxStyleName())))
 
11723
    {
 
11724
        return false;
 
11725
    }
 
11726
    if (attr.HasFloatMode() && HasFloatMode() && (GetFloatMode() != attr.GetFloatMode()))
 
11727
        return false;
 
11728
 
 
11729
    if (attr.HasClearMode() && HasClearMode() && (GetClearMode() != attr.GetClearMode()))
 
11730
        return false;
 
11731
 
 
11732
    if (attr.HasCollapseBorders() && HasCollapseBorders() && (attr.GetCollapseBorders() != GetCollapseBorders()))
 
11733
        return false;
 
11734
 
 
11735
    if (attr.HasVerticalAlignment() && HasVerticalAlignment() && (attr.GetVerticalAlignment() != GetVerticalAlignment()))
 
11736
        return false;
 
11737
 
 
11738
    if (attr.HasBoxStyleName() && HasBoxStyleName() && (attr.GetBoxStyleName() != GetBoxStyleName()))
 
11739
        return false;
 
11740
 
 
11741
    // Position
 
11742
 
 
11743
    if (!m_position.EqPartial(attr.m_position, weakTest))
 
11744
        return false;
 
11745
 
 
11746
    // Size
 
11747
 
 
11748
    if (!m_size.EqPartial(attr.m_size, weakTest))
 
11749
        return false;
 
11750
    if (!m_minSize.EqPartial(attr.m_minSize, weakTest))
 
11751
        return false;
 
11752
    if (!m_maxSize.EqPartial(attr.m_maxSize, weakTest))
 
11753
        return false;
 
11754
 
 
11755
    // Margins
 
11756
 
 
11757
    if (!m_margins.EqPartial(attr.m_margins, weakTest))
 
11758
        return false;
 
11759
 
 
11760
    // Padding
 
11761
 
 
11762
    if (!m_padding.EqPartial(attr.m_padding, weakTest))
 
11763
        return false;
 
11764
 
 
11765
    // Border
 
11766
 
 
11767
    if (!GetBorder().EqPartial(attr.GetBorder(), weakTest))
 
11768
        return false;
 
11769
 
 
11770
    // Outline
 
11771
 
 
11772
    if (!GetOutline().EqPartial(attr.GetOutline(), weakTest))
 
11773
        return false;
 
11774
 
 
11775
    return true;
 
11776
}
 
11777
 
 
11778
// Merges the given attributes. If compareWith
 
11779
// is non-NULL, then it will be used to mask out those attributes that are the same in style
 
11780
// and compareWith, for situations where we don't want to explicitly set inherited attributes.
 
11781
bool wxTextBoxAttr::Apply(const wxTextBoxAttr& attr, const wxTextBoxAttr* compareWith)
 
11782
{
 
11783
    if (attr.HasFloatMode())
 
11784
    {
 
11785
        if (!(compareWith && compareWith->HasFloatMode() && compareWith->GetFloatMode() == attr.GetFloatMode()))
 
11786
            SetFloatMode(attr.GetFloatMode());
 
11787
    }
 
11788
 
 
11789
    if (attr.HasClearMode())
 
11790
    {
 
11791
        if (!(compareWith && compareWith->HasClearMode() && compareWith->GetClearMode() == attr.GetClearMode()))
 
11792
            SetClearMode(attr.GetClearMode());
 
11793
    }
 
11794
 
 
11795
    if (attr.HasCollapseBorders())
 
11796
    {
 
11797
        if (!(compareWith && compareWith->HasCollapseBorders() && compareWith->GetCollapseBorders() == attr.GetCollapseBorders()))
 
11798
            SetCollapseBorders(attr.GetCollapseBorders());
 
11799
    }
 
11800
 
 
11801
    if (attr.HasVerticalAlignment())
 
11802
    {
 
11803
        if (!(compareWith && compareWith->HasVerticalAlignment() && compareWith->GetVerticalAlignment() == attr.GetVerticalAlignment()))
 
11804
            SetVerticalAlignment(attr.GetVerticalAlignment());
 
11805
    }
 
11806
 
 
11807
    if (attr.HasBoxStyleName())
 
11808
    {
 
11809
        if (!(compareWith && compareWith->HasBoxStyleName() && compareWith->GetBoxStyleName() == attr.GetBoxStyleName()))
 
11810
            SetBoxStyleName(attr.GetBoxStyleName());
 
11811
    }
 
11812
 
 
11813
    m_margins.Apply(attr.m_margins, compareWith ? (& attr.m_margins) : (const wxTextAttrDimensions*) NULL);
 
11814
    m_padding.Apply(attr.m_padding, compareWith ? (& attr.m_padding) : (const wxTextAttrDimensions*) NULL);
 
11815
    m_position.Apply(attr.m_position, compareWith ? (& attr.m_position) : (const wxTextAttrDimensions*) NULL);
 
11816
 
 
11817
    m_size.Apply(attr.m_size, compareWith ? (& attr.m_size) : (const wxTextAttrSize*) NULL);
 
11818
    m_minSize.Apply(attr.m_minSize, compareWith ? (& attr.m_minSize) : (const wxTextAttrSize*) NULL);
 
11819
    m_maxSize.Apply(attr.m_maxSize, compareWith ? (& attr.m_maxSize) : (const wxTextAttrSize*) NULL);
 
11820
 
 
11821
    m_border.Apply(attr.m_border, compareWith ? (& attr.m_border) : (const wxTextAttrBorders*) NULL);
 
11822
    m_outline.Apply(attr.m_outline, compareWith ? (& attr.m_outline) : (const wxTextAttrBorders*) NULL);
 
11823
 
 
11824
    return true;
 
11825
}
 
11826
 
 
11827
// Remove specified attributes from this object
 
11828
bool wxTextBoxAttr::RemoveStyle(const wxTextBoxAttr& attr)
 
11829
{
 
11830
    if (attr.HasFloatMode())
 
11831
        RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
 
11832
 
 
11833
    if (attr.HasClearMode())
 
11834
        RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
 
11835
 
 
11836
    if (attr.HasCollapseBorders())
 
11837
        RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
 
11838
 
 
11839
    if (attr.HasVerticalAlignment())
 
11840
        RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
 
11841
 
 
11842
    if (attr.HasBoxStyleName())
 
11843
    {
 
11844
        SetBoxStyleName(wxEmptyString);
 
11845
        RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
 
11846
    }
 
11847
 
 
11848
    m_margins.RemoveStyle(attr.m_margins);
 
11849
    m_padding.RemoveStyle(attr.m_padding);
 
11850
    m_position.RemoveStyle(attr.m_position);
 
11851
 
 
11852
    m_size.RemoveStyle(attr.m_size);
 
11853
    m_minSize.RemoveStyle(attr.m_minSize);
 
11854
    m_maxSize.RemoveStyle(attr.m_maxSize);
 
11855
 
 
11856
    m_border.RemoveStyle(attr.m_border);
 
11857
    m_outline.RemoveStyle(attr.m_outline);
 
11858
 
 
11859
    return true;
 
11860
}
 
11861
 
 
11862
// Collects the attributes that are common to a range of content, building up a note of
 
11863
// which attributes are absent in some objects and which clash in some objects.
 
11864
void wxTextBoxAttr::CollectCommonAttributes(const wxTextBoxAttr& attr, wxTextBoxAttr& clashingAttr, wxTextBoxAttr& absentAttr)
 
11865
{
 
11866
    if (attr.HasFloatMode())
 
11867
    {
 
11868
        if (!clashingAttr.HasFloatMode() && !absentAttr.HasFloatMode())
 
11869
        {
 
11870
            if (HasFloatMode())
 
11871
            {
 
11872
                if (GetFloatMode() != attr.GetFloatMode())
 
11873
                {
 
11874
                    clashingAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
 
11875
                    RemoveFlag(wxTEXT_BOX_ATTR_FLOAT);
 
11876
                }
 
11877
            }
 
11878
            else
 
11879
                SetFloatMode(attr.GetFloatMode());
 
11880
        }
 
11881
    }
 
11882
    else
 
11883
        absentAttr.AddFlag(wxTEXT_BOX_ATTR_FLOAT);
 
11884
 
 
11885
    if (attr.HasClearMode())
 
11886
    {
 
11887
        if (!clashingAttr.HasClearMode() && !absentAttr.HasClearMode())
 
11888
        {
 
11889
            if (HasClearMode())
 
11890
            {
 
11891
                if (GetClearMode() != attr.GetClearMode())
 
11892
                {
 
11893
                    clashingAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
 
11894
                    RemoveFlag(wxTEXT_BOX_ATTR_CLEAR);
 
11895
                }
 
11896
            }
 
11897
            else
 
11898
                SetClearMode(attr.GetClearMode());
 
11899
        }
 
11900
    }
 
11901
    else
 
11902
        absentAttr.AddFlag(wxTEXT_BOX_ATTR_CLEAR);
 
11903
 
 
11904
    if (attr.HasCollapseBorders())
 
11905
    {
 
11906
        if (!clashingAttr.HasCollapseBorders() && !absentAttr.HasCollapseBorders())
 
11907
        {
 
11908
            if (HasCollapseBorders())
 
11909
            {
 
11910
                if (GetCollapseBorders() != attr.GetCollapseBorders())
 
11911
                {
 
11912
                    clashingAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
 
11913
                    RemoveFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
 
11914
                }
 
11915
            }
 
11916
            else
 
11917
                SetCollapseBorders(attr.GetCollapseBorders());
 
11918
        }
 
11919
    }
 
11920
    else
 
11921
        absentAttr.AddFlag(wxTEXT_BOX_ATTR_COLLAPSE_BORDERS);
 
11922
 
 
11923
    if (attr.HasVerticalAlignment())
 
11924
    {
 
11925
        if (!clashingAttr.HasVerticalAlignment() && !absentAttr.HasVerticalAlignment())
 
11926
        {
 
11927
            if (HasVerticalAlignment())
 
11928
            {
 
11929
                if (GetVerticalAlignment() != attr.GetVerticalAlignment())
 
11930
                {
 
11931
                    clashingAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
 
11932
                    RemoveFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
 
11933
                }
 
11934
            }
 
11935
            else
 
11936
                SetVerticalAlignment(attr.GetVerticalAlignment());
 
11937
        }
 
11938
    }
 
11939
    else
 
11940
        absentAttr.AddFlag(wxTEXT_BOX_ATTR_VERTICAL_ALIGNMENT);
 
11941
 
 
11942
    if (attr.HasBoxStyleName())
 
11943
    {
 
11944
        if (!clashingAttr.HasBoxStyleName() && !absentAttr.HasBoxStyleName())
 
11945
        {
 
11946
            if (HasBoxStyleName())
 
11947
            {
 
11948
                if (GetBoxStyleName() != attr.GetBoxStyleName())
 
11949
                {
 
11950
                    clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
 
11951
                    RemoveFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
 
11952
                }
 
11953
            }
 
11954
            else
 
11955
                SetBoxStyleName(attr.GetBoxStyleName());
 
11956
        }
 
11957
    }
 
11958
    else
 
11959
        absentAttr.AddFlag(wxTEXT_BOX_ATTR_BOX_STYLE_NAME);
 
11960
 
 
11961
    m_margins.CollectCommonAttributes(attr.m_margins, clashingAttr.m_margins, absentAttr.m_margins);
 
11962
    m_padding.CollectCommonAttributes(attr.m_padding, clashingAttr.m_padding, absentAttr.m_padding);
 
11963
    m_position.CollectCommonAttributes(attr.m_position, clashingAttr.m_position, absentAttr.m_position);
 
11964
 
 
11965
    m_size.CollectCommonAttributes(attr.m_size, clashingAttr.m_size, absentAttr.m_size);
 
11966
    m_minSize.CollectCommonAttributes(attr.m_minSize, clashingAttr.m_minSize, absentAttr.m_minSize);
 
11967
    m_maxSize.CollectCommonAttributes(attr.m_maxSize, clashingAttr.m_maxSize, absentAttr.m_maxSize);
 
11968
 
 
11969
    m_border.CollectCommonAttributes(attr.m_border, clashingAttr.m_border, absentAttr.m_border);
 
11970
    m_outline.CollectCommonAttributes(attr.m_outline, clashingAttr.m_outline, absentAttr.m_outline);
 
11971
}
 
11972
 
 
11973
bool wxTextBoxAttr::IsDefault() const
 
11974
{
 
11975
    return GetFlags() == 0 && !m_border.IsValid() && !m_outline.IsValid() &&
 
11976
        !m_size.IsValid() && !m_minSize.IsValid() && !m_maxSize.IsValid() &&
 
11977
        !m_position.IsValid() && !m_padding.IsValid() && !m_margins.IsValid();
 
11978
}
 
11979
 
 
11980
// wxRichTextAttr
 
11981
 
 
11982
void wxRichTextAttr::Copy(const wxRichTextAttr& attr)
 
11983
{
 
11984
    wxTextAttr::Copy(attr);
 
11985
 
 
11986
    m_textBoxAttr = attr.m_textBoxAttr;
 
11987
}
 
11988
 
 
11989
bool wxRichTextAttr::operator==(const wxRichTextAttr& attr) const
 
11990
{
 
11991
    if (!(wxTextAttr::operator==(attr)))
 
11992
        return false;
 
11993
 
 
11994
    return (m_textBoxAttr == attr.m_textBoxAttr);
 
11995
}
 
11996
 
 
11997
// Partial equality test
 
11998
bool wxRichTextAttr::EqPartial(const wxRichTextAttr& attr, bool weakTest) const
 
11999
{
 
12000
    if (!(wxTextAttr::EqPartial(attr, weakTest)))
 
12001
        return false;
 
12002
 
 
12003
    return m_textBoxAttr.EqPartial(attr.m_textBoxAttr, weakTest);
 
12004
}
 
12005
 
 
12006
// Merges the given attributes. If compareWith
 
12007
// is non-NULL, then it will be used to mask out those attributes that are the same in style
 
12008
// and compareWith, for situations where we don't want to explicitly set inherited attributes.
 
12009
bool wxRichTextAttr::Apply(const wxRichTextAttr& style, const wxRichTextAttr* compareWith)
 
12010
{
 
12011
    wxTextAttr::Apply(style, compareWith);
 
12012
 
 
12013
    return m_textBoxAttr.Apply(style.m_textBoxAttr, compareWith ? (& compareWith->m_textBoxAttr) : (const wxTextBoxAttr*) NULL);
 
12014
}
 
12015
 
 
12016
// Remove specified attributes from this object
 
12017
bool wxRichTextAttr::RemoveStyle(const wxRichTextAttr& attr)
 
12018
{
 
12019
    wxTextAttr::RemoveStyle(*this, attr);
 
12020
 
 
12021
    return m_textBoxAttr.RemoveStyle(attr.m_textBoxAttr);
 
12022
}
 
12023
 
 
12024
// Collects the attributes that are common to a range of content, building up a note of
 
12025
// which attributes are absent in some objects and which clash in some objects.
 
12026
void wxRichTextAttr::CollectCommonAttributes(const wxRichTextAttr& attr, wxRichTextAttr& clashingAttr, wxRichTextAttr& absentAttr)
 
12027
{
 
12028
    wxTextAttrCollectCommonAttributes(*this, attr, clashingAttr, absentAttr);
 
12029
 
 
12030
    m_textBoxAttr.CollectCommonAttributes(attr.m_textBoxAttr, clashingAttr.m_textBoxAttr, absentAttr.m_textBoxAttr);
 
12031
}
 
12032
 
 
12033
// Partial equality test
 
12034
bool wxTextAttrBorder::EqPartial(const wxTextAttrBorder& border, bool weakTest) const
 
12035
{
 
12036
    if (!weakTest &&
 
12037
        ((!HasStyle() && border.HasStyle()) ||
 
12038
         (!HasColour() && border.HasColour()) ||
 
12039
         (!HasWidth() && border.HasWidth())))
 
12040
    {
 
12041
        return false;
 
12042
    }
 
12043
 
 
12044
    if (border.HasStyle() && HasStyle() && (border.GetStyle() != GetStyle()))
 
12045
        return false;
 
12046
 
 
12047
    if (border.HasColour() && HasColour() && (border.GetColourLong() != GetColourLong()))
 
12048
        return false;
 
12049
 
 
12050
    if (border.HasWidth() && HasWidth() && !(border.GetWidth() == GetWidth()))
 
12051
        return false;
 
12052
 
 
12053
    return true;
 
12054
}
 
12055
 
 
12056
// Apply border to 'this', but not if the same as compareWith
 
12057
bool wxTextAttrBorder::Apply(const wxTextAttrBorder& border, const wxTextAttrBorder* compareWith)
 
12058
{
 
12059
    if (border.HasStyle())
 
12060
    {
 
12061
        if (!(compareWith && (border.GetStyle() == compareWith->GetStyle())))
 
12062
            SetStyle(border.GetStyle());
 
12063
    }
 
12064
    if (border.HasColour())
 
12065
    {
 
12066
        if (!(compareWith && (border.GetColourLong() == compareWith->GetColourLong())))
 
12067
            SetColour(border.GetColourLong());
 
12068
    }
 
12069
    if (border.HasWidth())
 
12070
    {
 
12071
        if (!(compareWith && (border.GetWidth() == compareWith->GetWidth())))
 
12072
            SetWidth(border.GetWidth());
 
12073
    }
 
12074
 
 
12075
    return true;
 
12076
}
 
12077
 
 
12078
// Remove specified attributes from this object
 
12079
bool wxTextAttrBorder::RemoveStyle(const wxTextAttrBorder& attr)
 
12080
{
 
12081
    if (attr.HasStyle() && HasStyle())
 
12082
        SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_STYLE);
 
12083
    if (attr.HasColour() && HasColour())
 
12084
        SetFlags(GetFlags() & ~wxTEXT_BOX_ATTR_BORDER_COLOUR);
 
12085
    if (attr.HasWidth() && HasWidth())
 
12086
        m_borderWidth.Reset();
 
12087
 
 
12088
    return true;
 
12089
}
 
12090
 
 
12091
// Collects the attributes that are common to a range of content, building up a note of
 
12092
// which attributes are absent in some objects and which clash in some objects.
 
12093
void wxTextAttrBorder::CollectCommonAttributes(const wxTextAttrBorder& attr, wxTextAttrBorder& clashingAttr, wxTextAttrBorder& absentAttr)
 
12094
{
 
12095
    if (attr.HasStyle())
 
12096
    {
 
12097
        if (!clashingAttr.HasStyle() && !absentAttr.HasStyle())
 
12098
        {
 
12099
            if (HasStyle())
 
12100
            {
 
12101
                if (GetStyle() != attr.GetStyle())
 
12102
                {
 
12103
                    clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
 
12104
                    RemoveFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
 
12105
                }
 
12106
            }
 
12107
            else
 
12108
                SetStyle(attr.GetStyle());
 
12109
        }
 
12110
    }
 
12111
    else
 
12112
        absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_STYLE);
 
12113
 
 
12114
    if (attr.HasColour())
 
12115
    {
 
12116
        if (!clashingAttr.HasColour() && !absentAttr.HasColour())
 
12117
        {
 
12118
            if (HasColour())
 
12119
            {
 
12120
                if (GetColour() != attr.GetColour())
 
12121
                {
 
12122
                    clashingAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
 
12123
                    RemoveFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
 
12124
                }
 
12125
            }
 
12126
            else
 
12127
                SetColour(attr.GetColourLong());
 
12128
        }
 
12129
    }
 
12130
    else
 
12131
        absentAttr.AddFlag(wxTEXT_BOX_ATTR_BORDER_COLOUR);
 
12132
 
 
12133
    m_borderWidth.CollectCommonAttributes(attr.m_borderWidth, clashingAttr.m_borderWidth, absentAttr.m_borderWidth);
 
12134
}
 
12135
 
 
12136
// Partial equality test
 
12137
bool wxTextAttrBorders::EqPartial(const wxTextAttrBorders& borders, bool weakTest) const
 
12138
{
 
12139
    return m_left.EqPartial(borders.m_left, weakTest) && m_right.EqPartial(borders.m_right, weakTest) &&
 
12140
            m_top.EqPartial(borders.m_top, weakTest) && m_bottom.EqPartial(borders.m_bottom, weakTest);
 
12141
}
 
12142
 
 
12143
// Apply border to 'this', but not if the same as compareWith
 
12144
bool wxTextAttrBorders::Apply(const wxTextAttrBorders& borders, const wxTextAttrBorders* compareWith)
 
12145
{
 
12146
    m_left.Apply(borders.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrBorder*) NULL);
 
12147
    m_right.Apply(borders.m_right, compareWith ? (& compareWith->m_right) : (const wxTextAttrBorder*) NULL);
 
12148
    m_top.Apply(borders.m_top, compareWith ? (& compareWith->m_top) : (const wxTextAttrBorder*) NULL);
 
12149
    m_bottom.Apply(borders.m_bottom, compareWith ? (& compareWith->m_bottom) : (const wxTextAttrBorder*) NULL);
 
12150
    return true;
 
12151
}
 
12152
 
 
12153
// Remove specified attributes from this object
 
12154
bool wxTextAttrBorders::RemoveStyle(const wxTextAttrBorders& attr)
 
12155
{
 
12156
    m_left.RemoveStyle(attr.m_left);
 
12157
    m_right.RemoveStyle(attr.m_right);
 
12158
    m_top.RemoveStyle(attr.m_top);
 
12159
    m_bottom.RemoveStyle(attr.m_bottom);
 
12160
    return true;
 
12161
}
 
12162
 
 
12163
// Collects the attributes that are common to a range of content, building up a note of
 
12164
// which attributes are absent in some objects and which clash in some objects.
 
12165
void wxTextAttrBorders::CollectCommonAttributes(const wxTextAttrBorders& attr, wxTextAttrBorders& clashingAttr, wxTextAttrBorders& absentAttr)
 
12166
{
 
12167
    m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
 
12168
    m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
 
12169
    m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
 
12170
    m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
 
12171
}
 
12172
 
 
12173
// Set style of all borders
 
12174
void wxTextAttrBorders::SetStyle(int style)
 
12175
{
 
12176
    m_left.SetStyle(style);
 
12177
    m_right.SetStyle(style);
 
12178
    m_top.SetStyle(style);
 
12179
    m_bottom.SetStyle(style);
 
12180
}
 
12181
 
 
12182
// Set colour of all borders
 
12183
void wxTextAttrBorders::SetColour(unsigned long colour)
 
12184
{
 
12185
    m_left.SetColour(colour);
 
12186
    m_right.SetColour(colour);
 
12187
    m_top.SetColour(colour);
 
12188
    m_bottom.SetColour(colour);
 
12189
}
 
12190
 
 
12191
void wxTextAttrBorders::SetColour(const wxColour& colour)
 
12192
{
 
12193
    m_left.SetColour(colour);
 
12194
    m_right.SetColour(colour);
 
12195
    m_top.SetColour(colour);
 
12196
    m_bottom.SetColour(colour);
 
12197
}
 
12198
 
 
12199
// Set width of all borders
 
12200
void wxTextAttrBorders::SetWidth(const wxTextAttrDimension& width)
 
12201
{
 
12202
    m_left.SetWidth(width);
 
12203
    m_right.SetWidth(width);
 
12204
    m_top.SetWidth(width);
 
12205
    m_bottom.SetWidth(width);
 
12206
}
 
12207
 
 
12208
// Partial equality test
 
12209
bool wxTextAttrDimension::EqPartial(const wxTextAttrDimension& dim, bool weakTest) const
 
12210
{
 
12211
    if (!weakTest && !IsValid() && dim.IsValid())
 
12212
        return false;
 
12213
 
 
12214
    if (dim.IsValid() && IsValid() && !((*this) == dim))
 
12215
        return false;
 
12216
    else
 
12217
        return true;
 
12218
}
 
12219
 
 
12220
bool wxTextAttrDimension::Apply(const wxTextAttrDimension& dim, const wxTextAttrDimension* compareWith)
 
12221
{
 
12222
    if (dim.IsValid())
 
12223
    {
 
12224
        if (!(compareWith && dim == (*compareWith)))
 
12225
            (*this) = dim;
 
12226
    }
 
12227
 
 
12228
    return true;
 
12229
}
 
12230
 
 
12231
// Collects the attributes that are common to a range of content, building up a note of
 
12232
// which attributes are absent in some objects and which clash in some objects.
 
12233
void wxTextAttrDimension::CollectCommonAttributes(const wxTextAttrDimension& attr, wxTextAttrDimension& clashingAttr, wxTextAttrDimension& absentAttr)
 
12234
{
 
12235
    if (attr.IsValid())
 
12236
    {
 
12237
        if (!clashingAttr.IsValid() && !absentAttr.IsValid())
 
12238
        {
 
12239
            if (IsValid())
 
12240
            {
 
12241
                if (!((*this) == attr))
 
12242
                {
 
12243
                    clashingAttr.SetValid(true);
 
12244
                    SetValid(false);
 
12245
                }
 
12246
            }
 
12247
            else
 
12248
                (*this) = attr;
 
12249
        }
 
12250
    }
 
12251
    else
 
12252
        absentAttr.SetValid(true);
 
12253
}
 
12254
 
 
12255
wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(wxDC& dc, double scale, const wxSize& parentSize)
 
12256
{
 
12257
    m_ppi = dc.GetPPI().x; m_scale = scale; m_parentSize = parentSize;
 
12258
}
 
12259
 
 
12260
wxTextAttrDimensionConverter::wxTextAttrDimensionConverter(int ppi, double scale, const wxSize& parentSize)
 
12261
{
 
12262
    m_ppi = ppi; m_scale = scale; m_parentSize = parentSize;
 
12263
}
 
12264
 
 
12265
int wxTextAttrDimensionConverter::ConvertTenthsMMToPixels(int units) const
 
12266
{
 
12267
    return wxRichTextObject::ConvertTenthsMMToPixels(m_ppi, units, m_scale);
 
12268
}
 
12269
 
 
12270
int wxTextAttrDimensionConverter::ConvertPixelsToTenthsMM(int pixels) const
 
12271
{
 
12272
    return wxRichTextObject::ConvertPixelsToTenthsMM(m_ppi, pixels, m_scale);
 
12273
}
 
12274
 
 
12275
int wxTextAttrDimensionConverter::GetPixels(const wxTextAttrDimension& dim, int direction) const
 
12276
{
 
12277
    if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
 
12278
        return ConvertTenthsMMToPixels(dim.GetValue());
 
12279
    else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
 
12280
        return dim.GetValue();
 
12281
    else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PERCENTAGE)
 
12282
    {
 
12283
        wxASSERT(m_parentSize != wxDefaultSize);
 
12284
        if (direction == wxHORIZONTAL)
 
12285
            return (int) (double(m_parentSize.x) * double(dim.GetValue()) / 100.0);
 
12286
        else
 
12287
            return (int) (double(m_parentSize.y) * double(dim.GetValue()) / 100.0);
 
12288
    }
 
12289
    else
 
12290
    {
 
12291
        wxASSERT(false);
 
12292
        return 0;
 
12293
    }
 
12294
}
 
12295
 
 
12296
int wxTextAttrDimensionConverter::GetTenthsMM(const wxTextAttrDimension& dim) const
 
12297
{
 
12298
    if (dim.GetUnits() == wxTEXT_ATTR_UNITS_TENTHS_MM)
 
12299
        return dim.GetValue();
 
12300
    else if (dim.GetUnits() == wxTEXT_ATTR_UNITS_PIXELS)
 
12301
        return ConvertPixelsToTenthsMM(dim.GetValue());
 
12302
    else
 
12303
    {
 
12304
        wxASSERT(false);
 
12305
        return 0;
 
12306
    }
 
12307
}
 
12308
 
 
12309
// Partial equality test
 
12310
bool wxTextAttrDimensions::EqPartial(const wxTextAttrDimensions& dims, bool weakTest) const
 
12311
{
 
12312
    if (!m_left.EqPartial(dims.m_left, weakTest))
 
12313
        return false;
 
12314
 
 
12315
    if (!m_right.EqPartial(dims.m_right, weakTest))
 
12316
        return false;
 
12317
 
 
12318
    if (!m_top.EqPartial(dims.m_top, weakTest))
 
12319
        return false;
 
12320
 
 
12321
    if (!m_bottom.EqPartial(dims.m_bottom, weakTest))
 
12322
        return false;
 
12323
 
 
12324
    return true;
 
12325
}
 
12326
 
 
12327
// Apply border to 'this', but not if the same as compareWith
 
12328
bool wxTextAttrDimensions::Apply(const wxTextAttrDimensions& dims, const wxTextAttrDimensions* compareWith)
 
12329
{
 
12330
    m_left.Apply(dims.m_left, compareWith ? (& compareWith->m_left) : (const wxTextAttrDimension*) NULL);
 
12331
    m_right.Apply(dims.m_right, compareWith ? (& compareWith->m_right): (const wxTextAttrDimension*) NULL);
 
12332
    m_top.Apply(dims.m_top, compareWith ? (& compareWith->m_top): (const wxTextAttrDimension*) NULL);
 
12333
    m_bottom.Apply(dims.m_bottom, compareWith ? (& compareWith->m_bottom): (const wxTextAttrDimension*) NULL);
 
12334
 
 
12335
    return true;
 
12336
}
 
12337
 
 
12338
// Remove specified attributes from this object
 
12339
bool wxTextAttrDimensions::RemoveStyle(const wxTextAttrDimensions& attr)
 
12340
{
 
12341
    if (attr.m_left.IsValid())
 
12342
        m_left.Reset();
 
12343
    if (attr.m_right.IsValid())
 
12344
        m_right.Reset();
 
12345
    if (attr.m_top.IsValid())
 
12346
        m_top.Reset();
 
12347
    if (attr.m_bottom.IsValid())
 
12348
        m_bottom.Reset();
 
12349
 
 
12350
    return true;
 
12351
}
 
12352
 
 
12353
// Collects the attributes that are common to a range of content, building up a note of
 
12354
// which attributes are absent in some objects and which clash in some objects.
 
12355
void wxTextAttrDimensions::CollectCommonAttributes(const wxTextAttrDimensions& attr, wxTextAttrDimensions& clashingAttr, wxTextAttrDimensions& absentAttr)
 
12356
{
 
12357
    m_left.CollectCommonAttributes(attr.m_left, clashingAttr.m_left, absentAttr.m_left);
 
12358
    m_right.CollectCommonAttributes(attr.m_right, clashingAttr.m_right, absentAttr.m_right);
 
12359
    m_top.CollectCommonAttributes(attr.m_top, clashingAttr.m_top, absentAttr.m_top);
 
12360
    m_bottom.CollectCommonAttributes(attr.m_bottom, clashingAttr.m_bottom, absentAttr.m_bottom);
 
12361
}
 
12362
 
 
12363
// Partial equality test
 
12364
bool wxTextAttrSize::EqPartial(const wxTextAttrSize& size, bool weakTest) const
 
12365
{
 
12366
    if (!m_width.EqPartial(size.m_width, weakTest))
 
12367
        return false;
 
12368
 
 
12369
    if (!m_height.EqPartial(size.m_height, weakTest))
 
12370
        return false;
 
12371
 
 
12372
    return true;
 
12373
}
 
12374
 
 
12375
// Apply border to 'this', but not if the same as compareWith
 
12376
bool wxTextAttrSize::Apply(const wxTextAttrSize& size, const wxTextAttrSize* compareWith)
 
12377
{
 
12378
    m_width.Apply(size.m_width, compareWith ? (& compareWith->m_width) : (const wxTextAttrDimension*) NULL);
 
12379
    m_height.Apply(size.m_height, compareWith ? (& compareWith->m_height): (const wxTextAttrDimension*) NULL);
 
12380
 
 
12381
    return true;
 
12382
}
 
12383
 
 
12384
// Remove specified attributes from this object
 
12385
bool wxTextAttrSize::RemoveStyle(const wxTextAttrSize& attr)
 
12386
{
 
12387
    if (attr.m_width.IsValid())
 
12388
        m_width.Reset();
 
12389
    if (attr.m_height.IsValid())
 
12390
        m_height.Reset();
 
12391
 
 
12392
    return true;
 
12393
}
 
12394
 
 
12395
// Collects the attributes that are common to a range of content, building up a note of
 
12396
// which attributes are absent in some objects and which clash in some objects.
 
12397
void wxTextAttrSize::CollectCommonAttributes(const wxTextAttrSize& attr, wxTextAttrSize& clashingAttr, wxTextAttrSize& absentAttr)
 
12398
{
 
12399
    m_width.CollectCommonAttributes(attr.m_width, clashingAttr.m_width, absentAttr.m_width);
 
12400
    m_height.CollectCommonAttributes(attr.m_height, clashingAttr.m_height, absentAttr.m_height);
 
12401
}
 
12402
 
 
12403
// Collects the attributes that are common to a range of content, building up a note of
 
12404
// which attributes are absent in some objects and which clash in some objects.
 
12405
void wxTextAttrCollectCommonAttributes(wxTextAttr& currentStyle, const wxTextAttr& attr, wxTextAttr& clashingAttr, wxTextAttr& absentAttr)
 
12406
{
 
12407
    absentAttr.SetFlags(absentAttr.GetFlags() | (~attr.GetFlags() & wxTEXT_ATTR_ALL));
 
12408
    absentAttr.SetTextEffectFlags(absentAttr.GetTextEffectFlags() | (~attr.GetTextEffectFlags() & 0xFFFF));
 
12409
 
 
12410
    long forbiddenFlags = clashingAttr.GetFlags()|absentAttr.GetFlags();
 
12411
 
 
12412
    if (attr.HasFont())
 
12413
    {
 
12414
        // If different font size units are being used, this is a clash.
 
12415
        if (((attr.GetFlags() & wxTEXT_ATTR_FONT_SIZE) | (currentStyle.GetFlags() & wxTEXT_ATTR_FONT_SIZE)) == wxTEXT_ATTR_FONT_SIZE)
 
12416
        {
 
12417
            currentStyle.SetFontSize(0);
 
12418
            currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_SIZE);
 
12419
            clashingAttr.AddFlag(wxTEXT_ATTR_FONT_SIZE);
 
12420
        }
 
12421
        else
 
12422
        {
 
12423
            if (attr.HasFontPointSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_POINT_SIZE))
 
12424
            {
 
12425
                if (currentStyle.HasFontPointSize())
 
12426
                {
 
12427
                    if (currentStyle.GetFontSize() != attr.GetFontSize())
 
12428
                    {
 
12429
                        // Clash of attr - mark as such
 
12430
                        clashingAttr.AddFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
 
12431
                        currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_POINT_SIZE);
 
12432
                    }
 
12433
                }
 
12434
                else
 
12435
                    currentStyle.SetFontSize(attr.GetFontSize());
 
12436
            }
 
12437
 
 
12438
            if (attr.HasFontPixelSize() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_PIXEL_SIZE))
 
12439
            {
 
12440
                if (currentStyle.HasFontPixelSize())
 
12441
                {
 
12442
                    if (currentStyle.GetFontSize() != attr.GetFontSize())
 
12443
                    {
 
12444
                        // Clash of attr - mark as such
 
12445
                        clashingAttr.AddFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
 
12446
                        currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_PIXEL_SIZE);
 
12447
                    }
 
12448
                }
 
12449
                else
 
12450
                    currentStyle.SetFontPixelSize(attr.GetFontSize());
 
12451
            }
 
12452
        }
 
12453
 
 
12454
        if (attr.HasFontItalic() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_ITALIC))
 
12455
        {
 
12456
            if (currentStyle.HasFontItalic())
 
12457
            {
 
12458
                if (currentStyle.GetFontStyle() != attr.GetFontStyle())
 
12459
                {
 
12460
                    // Clash of attr - mark as such
 
12461
                    clashingAttr.AddFlag(wxTEXT_ATTR_FONT_ITALIC);
 
12462
                    currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_ITALIC);
 
12463
                }
 
12464
            }
 
12465
            else
 
12466
                currentStyle.SetFontStyle(attr.GetFontStyle());
 
12467
        }
 
12468
 
 
12469
        if (attr.HasFontFamily() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FAMILY))
 
12470
        {
 
12471
            if (currentStyle.HasFontFamily())
 
12472
            {
 
12473
                if (currentStyle.GetFontFamily() != attr.GetFontFamily())
 
12474
                {
 
12475
                    // Clash of attr - mark as such
 
12476
                    clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FAMILY);
 
12477
                    currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FAMILY);
 
12478
                }
 
12479
            }
 
12480
            else
 
12481
                currentStyle.SetFontFamily(attr.GetFontFamily());
 
12482
        }
 
12483
 
 
12484
        if (attr.HasFontWeight() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_WEIGHT))
 
12485
        {
 
12486
            if (currentStyle.HasFontWeight())
 
12487
            {
 
12488
                if (currentStyle.GetFontWeight() != attr.GetFontWeight())
 
12489
                {
 
12490
                    // Clash of attr - mark as such
 
12491
                    clashingAttr.AddFlag(wxTEXT_ATTR_FONT_WEIGHT);
 
12492
                    currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_WEIGHT);
 
12493
                }
 
12494
            }
 
12495
            else
 
12496
                currentStyle.SetFontWeight(attr.GetFontWeight());
 
12497
        }
 
12498
 
 
12499
        if (attr.HasFontFaceName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_FACE))
 
12500
        {
 
12501
            if (currentStyle.HasFontFaceName())
 
12502
            {
 
12503
                wxString faceName1(currentStyle.GetFontFaceName());
 
12504
                wxString faceName2(attr.GetFontFaceName());
 
12505
 
 
12506
                if (faceName1 != faceName2)
 
12507
                {
 
12508
                    // Clash of attr - mark as such
 
12509
                    clashingAttr.AddFlag(wxTEXT_ATTR_FONT_FACE);
 
12510
                    currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_FACE);
 
12511
                }
 
12512
            }
 
12513
            else
 
12514
                currentStyle.SetFontFaceName(attr.GetFontFaceName());
 
12515
        }
 
12516
 
 
12517
        if (attr.HasFontUnderlined() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_UNDERLINE))
 
12518
        {
 
12519
            if (currentStyle.HasFontUnderlined())
 
12520
            {
 
12521
                if (currentStyle.GetFontUnderlined() != attr.GetFontUnderlined())
 
12522
                {
 
12523
                    // Clash of attr - mark as such
 
12524
                    clashingAttr.AddFlag(wxTEXT_ATTR_FONT_UNDERLINE);
 
12525
                    currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_UNDERLINE);
 
12526
                }
 
12527
            }
 
12528
            else
 
12529
                currentStyle.SetFontUnderlined(attr.GetFontUnderlined());
 
12530
        }
 
12531
 
 
12532
        if (attr.HasFontStrikethrough() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_FONT_STRIKETHROUGH))
 
12533
        {
 
12534
            if (currentStyle.HasFontStrikethrough())
 
12535
            {
 
12536
                if (currentStyle.GetFontStrikethrough() != attr.GetFontStrikethrough())
 
12537
                {
 
12538
                    // Clash of attr - mark as such
 
12539
                    clashingAttr.AddFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
 
12540
                    currentStyle.RemoveFlag(wxTEXT_ATTR_FONT_STRIKETHROUGH);
 
12541
                }
 
12542
            }
 
12543
            else
 
12544
                currentStyle.SetFontStrikethrough(attr.GetFontStrikethrough());
 
12545
        }
 
12546
    }
 
12547
 
 
12548
    if (attr.HasTextColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TEXT_COLOUR))
 
12549
    {
 
12550
        if (currentStyle.HasTextColour())
 
12551
        {
 
12552
            if (currentStyle.GetTextColour() != attr.GetTextColour())
 
12553
            {
 
12554
                // Clash of attr - mark as such
 
12555
                clashingAttr.AddFlag(wxTEXT_ATTR_TEXT_COLOUR);
 
12556
                currentStyle.RemoveFlag(wxTEXT_ATTR_TEXT_COLOUR);
 
12557
            }
 
12558
        }
 
12559
        else
 
12560
            currentStyle.SetTextColour(attr.GetTextColour());
 
12561
    }
 
12562
 
 
12563
    if (attr.HasBackgroundColour() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BACKGROUND_COLOUR))
 
12564
    {
 
12565
        if (currentStyle.HasBackgroundColour())
 
12566
        {
 
12567
            if (currentStyle.GetBackgroundColour() != attr.GetBackgroundColour())
 
12568
            {
 
12569
                // Clash of attr - mark as such
 
12570
                clashingAttr.AddFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
 
12571
                currentStyle.RemoveFlag(wxTEXT_ATTR_BACKGROUND_COLOUR);
 
12572
            }
 
12573
        }
 
12574
        else
 
12575
            currentStyle.SetBackgroundColour(attr.GetBackgroundColour());
 
12576
    }
 
12577
 
 
12578
    if (attr.HasAlignment() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_ALIGNMENT))
 
12579
    {
 
12580
        if (currentStyle.HasAlignment())
 
12581
        {
 
12582
            if (currentStyle.GetAlignment() != attr.GetAlignment())
 
12583
            {
 
12584
                // Clash of attr - mark as such
 
12585
                clashingAttr.AddFlag(wxTEXT_ATTR_ALIGNMENT);
 
12586
                currentStyle.RemoveFlag(wxTEXT_ATTR_ALIGNMENT);
 
12587
            }
 
12588
        }
 
12589
        else
 
12590
            currentStyle.SetAlignment(attr.GetAlignment());
 
12591
    }
 
12592
 
 
12593
    if (attr.HasTabs() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_TABS))
 
12594
    {
 
12595
        if (currentStyle.HasTabs())
 
12596
        {
 
12597
            if (!wxRichTextTabsEq(currentStyle.GetTabs(), attr.GetTabs()))
 
12598
            {
 
12599
                // Clash of attr - mark as such
 
12600
                clashingAttr.AddFlag(wxTEXT_ATTR_TABS);
 
12601
                currentStyle.RemoveFlag(wxTEXT_ATTR_TABS);
 
12602
            }
 
12603
        }
 
12604
        else
 
12605
            currentStyle.SetTabs(attr.GetTabs());
 
12606
    }
 
12607
 
 
12608
    if (attr.HasLeftIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LEFT_INDENT))
 
12609
    {
 
12610
        if (currentStyle.HasLeftIndent())
 
12611
        {
 
12612
            if (currentStyle.GetLeftIndent() != attr.GetLeftIndent() || currentStyle.GetLeftSubIndent() != attr.GetLeftSubIndent())
 
12613
            {
 
12614
                // Clash of attr - mark as such
 
12615
                clashingAttr.AddFlag(wxTEXT_ATTR_LEFT_INDENT);
 
12616
                currentStyle.RemoveFlag(wxTEXT_ATTR_LEFT_INDENT);
 
12617
            }
 
12618
        }
 
12619
        else
 
12620
            currentStyle.SetLeftIndent(attr.GetLeftIndent(), attr.GetLeftSubIndent());
 
12621
    }
 
12622
 
 
12623
    if (attr.HasRightIndent() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_RIGHT_INDENT))
 
12624
    {
 
12625
        if (currentStyle.HasRightIndent())
 
12626
        {
 
12627
            if (currentStyle.GetRightIndent() != attr.GetRightIndent())
 
12628
            {
 
12629
                // Clash of attr - mark as such
 
12630
                clashingAttr.AddFlag(wxTEXT_ATTR_RIGHT_INDENT);
 
12631
                currentStyle.RemoveFlag(wxTEXT_ATTR_RIGHT_INDENT);
 
12632
            }
 
12633
        }
 
12634
        else
 
12635
            currentStyle.SetRightIndent(attr.GetRightIndent());
 
12636
    }
 
12637
 
 
12638
    if (attr.HasParagraphSpacingAfter() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_AFTER))
 
12639
    {
 
12640
        if (currentStyle.HasParagraphSpacingAfter())
 
12641
        {
 
12642
            if (currentStyle.GetParagraphSpacingAfter() != attr.GetParagraphSpacingAfter())
 
12643
            {
 
12644
                // Clash of attr - mark as such
 
12645
                clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
 
12646
                currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_AFTER);
 
12647
            }
 
12648
        }
 
12649
        else
 
12650
            currentStyle.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter());
 
12651
    }
 
12652
 
 
12653
    if (attr.HasParagraphSpacingBefore() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARA_SPACING_BEFORE))
 
12654
    {
 
12655
        if (currentStyle.HasParagraphSpacingBefore())
 
12656
        {
 
12657
            if (currentStyle.GetParagraphSpacingBefore() != attr.GetParagraphSpacingBefore())
 
12658
            {
 
12659
                // Clash of attr - mark as such
 
12660
                clashingAttr.AddFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
 
12661
                currentStyle.RemoveFlag(wxTEXT_ATTR_PARA_SPACING_BEFORE);
 
12662
            }
 
12663
        }
 
12664
        else
 
12665
            currentStyle.SetParagraphSpacingBefore(attr.GetParagraphSpacingBefore());
 
12666
    }
 
12667
 
 
12668
    if (attr.HasLineSpacing() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LINE_SPACING))
 
12669
    {
 
12670
        if (currentStyle.HasLineSpacing())
 
12671
        {
 
12672
            if (currentStyle.GetLineSpacing() != attr.GetLineSpacing())
 
12673
            {
 
12674
                // Clash of attr - mark as such
 
12675
                clashingAttr.AddFlag(wxTEXT_ATTR_LINE_SPACING);
 
12676
                currentStyle.RemoveFlag(wxTEXT_ATTR_LINE_SPACING);
 
12677
            }
 
12678
        }
 
12679
        else
 
12680
            currentStyle.SetLineSpacing(attr.GetLineSpacing());
 
12681
    }
 
12682
 
 
12683
    if (attr.HasCharacterStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_CHARACTER_STYLE_NAME))
 
12684
    {
 
12685
        if (currentStyle.HasCharacterStyleName())
 
12686
        {
 
12687
            if (currentStyle.GetCharacterStyleName() != attr.GetCharacterStyleName())
 
12688
            {
 
12689
                // Clash of attr - mark as such
 
12690
                clashingAttr.AddFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
 
12691
                currentStyle.RemoveFlag(wxTEXT_ATTR_CHARACTER_STYLE_NAME);
 
12692
            }
 
12693
        }
 
12694
        else
 
12695
            currentStyle.SetCharacterStyleName(attr.GetCharacterStyleName());
 
12696
    }
 
12697
 
 
12698
    if (attr.HasParagraphStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_PARAGRAPH_STYLE_NAME))
 
12699
    {
 
12700
        if (currentStyle.HasParagraphStyleName())
 
12701
        {
 
12702
            if (currentStyle.GetParagraphStyleName() != attr.GetParagraphStyleName())
 
12703
            {
 
12704
                // Clash of attr - mark as such
 
12705
                clashingAttr.AddFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
 
12706
                currentStyle.RemoveFlag(wxTEXT_ATTR_PARAGRAPH_STYLE_NAME);
 
12707
            }
 
12708
        }
 
12709
        else
 
12710
            currentStyle.SetParagraphStyleName(attr.GetParagraphStyleName());
 
12711
    }
 
12712
 
 
12713
    if (attr.HasListStyleName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_LIST_STYLE_NAME))
 
12714
    {
 
12715
        if (currentStyle.HasListStyleName())
 
12716
        {
 
12717
            if (currentStyle.GetListStyleName() != attr.GetListStyleName())
 
12718
            {
 
12719
                // Clash of attr - mark as such
 
12720
                clashingAttr.AddFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
 
12721
                currentStyle.RemoveFlag(wxTEXT_ATTR_LIST_STYLE_NAME);
 
12722
            }
 
12723
        }
 
12724
        else
 
12725
            currentStyle.SetListStyleName(attr.GetListStyleName());
 
12726
    }
 
12727
 
 
12728
    if (attr.HasBulletStyle() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_STYLE))
 
12729
    {
 
12730
        if (currentStyle.HasBulletStyle())
 
12731
        {
 
12732
            if (currentStyle.GetBulletStyle() != attr.GetBulletStyle())
 
12733
            {
 
12734
                // Clash of attr - mark as such
 
12735
                clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_STYLE);
 
12736
                currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_STYLE);
 
12737
            }
 
12738
        }
 
12739
        else
 
12740
            currentStyle.SetBulletStyle(attr.GetBulletStyle());
 
12741
    }
 
12742
 
 
12743
    if (attr.HasBulletNumber() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NUMBER))
 
12744
    {
 
12745
        if (currentStyle.HasBulletNumber())
 
12746
        {
 
12747
            if (currentStyle.GetBulletNumber() != attr.GetBulletNumber())
 
12748
            {
 
12749
                // Clash of attr - mark as such
 
12750
                clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NUMBER);
 
12751
                currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NUMBER);
 
12752
            }
 
12753
        }
 
12754
        else
 
12755
            currentStyle.SetBulletNumber(attr.GetBulletNumber());
 
12756
    }
 
12757
 
 
12758
    if (attr.HasBulletText() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_TEXT))
 
12759
    {
 
12760
        if (currentStyle.HasBulletText())
 
12761
        {
 
12762
            if (currentStyle.GetBulletText() != attr.GetBulletText())
 
12763
            {
 
12764
                // Clash of attr - mark as such
 
12765
                clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_TEXT);
 
12766
                currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_TEXT);
 
12767
            }
 
12768
        }
 
12769
        else
 
12770
        {
 
12771
            currentStyle.SetBulletText(attr.GetBulletText());
 
12772
            currentStyle.SetBulletFont(attr.GetBulletFont());
 
12773
        }
 
12774
    }
 
12775
 
 
12776
    if (attr.HasBulletName() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_BULLET_NAME))
 
12777
    {
 
12778
        if (currentStyle.HasBulletName())
 
12779
        {
 
12780
            if (currentStyle.GetBulletName() != attr.GetBulletName())
 
12781
            {
 
12782
                // Clash of attr - mark as such
 
12783
                clashingAttr.AddFlag(wxTEXT_ATTR_BULLET_NAME);
 
12784
                currentStyle.RemoveFlag(wxTEXT_ATTR_BULLET_NAME);
 
12785
            }
 
12786
        }
 
12787
        else
 
12788
        {
 
12789
            currentStyle.SetBulletName(attr.GetBulletName());
 
12790
        }
 
12791
    }
 
12792
 
 
12793
    if (attr.HasURL() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_URL))
 
12794
    {
 
12795
        if (currentStyle.HasURL())
 
12796
        {
 
12797
            if (currentStyle.GetURL() != attr.GetURL())
 
12798
            {
 
12799
                // Clash of attr - mark as such
 
12800
                clashingAttr.AddFlag(wxTEXT_ATTR_URL);
 
12801
                currentStyle.RemoveFlag(wxTEXT_ATTR_URL);
 
12802
            }
 
12803
        }
 
12804
        else
 
12805
        {
 
12806
            currentStyle.SetURL(attr.GetURL());
 
12807
        }
 
12808
    }
 
12809
 
 
12810
    if (attr.HasTextEffects() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_EFFECTS))
 
12811
    {
 
12812
        if (currentStyle.HasTextEffects())
 
12813
        {
 
12814
            // We need to find the bits in the new attr that are different:
 
12815
            // just look at those bits that are specified by the new attr.
 
12816
 
 
12817
            // We need to remove the bits and flags that are not common between current attr
 
12818
            // and new attr. In so doing we need to take account of the styles absent from one or more of the
 
12819
            // previous styles.
 
12820
 
 
12821
            int currentRelevantTextEffects = currentStyle.GetTextEffects() & attr.GetTextEffectFlags();
 
12822
            int newRelevantTextEffects = attr.GetTextEffects() & attr.GetTextEffectFlags();
 
12823
 
 
12824
            if (currentRelevantTextEffects != newRelevantTextEffects)
 
12825
            {
 
12826
                // Find the text effects that were different, using XOR
 
12827
                int differentEffects = currentRelevantTextEffects ^ newRelevantTextEffects;
 
12828
 
 
12829
                // Clash of attr - mark as such
 
12830
                clashingAttr.SetTextEffectFlags(clashingAttr.GetTextEffectFlags() | differentEffects);
 
12831
                currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~differentEffects);
 
12832
            }
 
12833
        }
 
12834
        else
 
12835
        {
 
12836
            currentStyle.SetTextEffects(attr.GetTextEffects());
 
12837
            currentStyle.SetTextEffectFlags(attr.GetTextEffectFlags());
 
12838
        }
 
12839
 
 
12840
        // Mask out the flags and values that cannot be common because they were absent in one or more objecrs
 
12841
        // that we've looked at so far
 
12842
        currentStyle.SetTextEffects(currentStyle.GetTextEffects() & ~absentAttr.GetTextEffectFlags());
 
12843
        currentStyle.SetTextEffectFlags(currentStyle.GetTextEffectFlags() & ~absentAttr.GetTextEffectFlags());
 
12844
 
 
12845
        if (currentStyle.GetTextEffectFlags() == 0)
 
12846
            currentStyle.RemoveFlag(wxTEXT_ATTR_EFFECTS);
 
12847
    }
 
12848
 
 
12849
    if (attr.HasOutlineLevel() && !wxHasStyle(forbiddenFlags, wxTEXT_ATTR_OUTLINE_LEVEL))
 
12850
    {
 
12851
        if (currentStyle.HasOutlineLevel())
 
12852
        {
 
12853
            if (currentStyle.GetOutlineLevel() != attr.GetOutlineLevel())
 
12854
            {
 
12855
                // Clash of attr - mark as such
 
12856
                clashingAttr.AddFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
 
12857
                currentStyle.RemoveFlag(wxTEXT_ATTR_OUTLINE_LEVEL);
 
12858
            }
 
12859
        }
 
12860
        else
 
12861
            currentStyle.SetOutlineLevel(attr.GetOutlineLevel());
 
12862
    }
 
12863
}
 
12864
 
 
12865
WX_DEFINE_OBJARRAY(wxRichTextVariantArray);
 
12866
 
 
12867
IMPLEMENT_DYNAMIC_CLASS(wxRichTextProperties, wxObject)
 
12868
 
 
12869
bool wxRichTextProperties::operator==(const wxRichTextProperties& props) const
 
12870
{
 
12871
    if (m_properties.GetCount() != props.GetCount())
 
12872
        return false;
 
12873
 
 
12874
    size_t i;
 
12875
    for (i = 0; i < m_properties.GetCount(); i++)
 
12876
    {
 
12877
        const wxVariant& var1 = m_properties[i];
 
12878
        int idx = props.Find(var1.GetName());
 
12879
        if (idx == -1)
 
12880
            return false;
 
12881
        const wxVariant& var2 = props.m_properties[idx];
 
12882
        if (!(var1 == var2))
 
12883
            return false;
 
12884
    }
 
12885
 
 
12886
    return true;
 
12887
}
 
12888
 
 
12889
wxArrayString wxRichTextProperties::GetPropertyNames() const
 
12890
{
 
12891
    wxArrayString arr;
 
12892
    size_t i;
 
12893
    for (i = 0; i < m_properties.GetCount(); i++)
 
12894
    {
 
12895
        arr.Add(m_properties[i].GetName());
 
12896
    }
 
12897
    return arr;
 
12898
}
 
12899
 
 
12900
int wxRichTextProperties::Find(const wxString& name) const
 
12901
{
 
12902
    size_t i;
 
12903
    for (i = 0; i < m_properties.GetCount(); i++)
 
12904
    {
 
12905
        if (m_properties[i].GetName() == name)
 
12906
            return (int) i;
 
12907
    }
 
12908
    return -1;
 
12909
}
 
12910
 
 
12911
bool wxRichTextProperties::Remove(const wxString& name)
 
12912
{
 
12913
    int idx = Find(name);
 
12914
    if (idx != -1)
 
12915
    {
 
12916
        m_properties.RemoveAt(idx);
 
12917
        return true;
 
12918
    }
 
12919
    else
 
12920
        return false;
 
12921
}
 
12922
 
 
12923
wxVariant* wxRichTextProperties::FindOrCreateProperty(const wxString& name)
 
12924
{
 
12925
    int idx = Find(name);
 
12926
    if (idx == wxNOT_FOUND)
 
12927
        SetProperty(name, wxString());
 
12928
    idx = Find(name);
 
12929
    if (idx != wxNOT_FOUND)
 
12930
    {
 
12931
        return & (*this)[idx];
 
12932
    }
 
12933
    else
 
12934
        return NULL;
 
12935
}
 
12936
 
 
12937
const wxVariant& wxRichTextProperties::GetProperty(const wxString& name) const
 
12938
{
 
12939
    static const wxVariant nullVariant;
 
12940
    int idx = Find(name);
 
12941
    if (idx != -1)
 
12942
        return m_properties[idx];
 
12943
    else
 
12944
        return nullVariant;
 
12945
}
 
12946
 
 
12947
wxString wxRichTextProperties::GetPropertyString(const wxString& name) const
 
12948
{
 
12949
    return GetProperty(name).GetString();
 
12950
}
 
12951
 
 
12952
long wxRichTextProperties::GetPropertyLong(const wxString& name) const
 
12953
{
 
12954
    return GetProperty(name).GetLong();
 
12955
}
 
12956
 
 
12957
bool wxRichTextProperties::GetPropertyBool(const wxString& name) const
 
12958
{
 
12959
    return GetProperty(name).GetBool();
 
12960
}
 
12961
 
 
12962
double wxRichTextProperties::GetPropertyDouble(const wxString& name) const
 
12963
{
 
12964
    return GetProperty(name).GetDouble();
 
12965
}
 
12966
 
 
12967
void wxRichTextProperties::SetProperty(const wxVariant& variant)
 
12968
{
 
12969
    wxASSERT(!variant.GetName().IsEmpty());
 
12970
 
 
12971
    int idx = Find(variant.GetName());
 
12972
 
 
12973
    if (idx == -1)
 
12974
        m_properties.Add(variant);
 
12975
    else
 
12976
        m_properties[idx] = variant;
 
12977
}
 
12978
 
 
12979
void wxRichTextProperties::SetProperty(const wxString& name, const wxVariant& variant)
 
12980
{
 
12981
    int idx = Find(name);
 
12982
    wxVariant var(variant);
 
12983
    var.SetName(name);
 
12984
 
 
12985
    if (idx == -1)
 
12986
        m_properties.Add(var);
 
12987
    else
 
12988
        m_properties[idx] = var;
 
12989
}
 
12990
 
 
12991
void wxRichTextProperties::SetProperty(const wxString& name, const wxString& value)
 
12992
{
 
12993
    SetProperty(name, wxVariant(value, name));
 
12994
}
 
12995
 
 
12996
void wxRichTextProperties::SetProperty(const wxString& name, long value)
 
12997
{
 
12998
    SetProperty(name, wxVariant(value, name));
 
12999
}
 
13000
 
 
13001
void wxRichTextProperties::SetProperty(const wxString& name, double value)
 
13002
{
 
13003
    SetProperty(name, wxVariant(value, name));
 
13004
}
 
13005
 
 
13006
void wxRichTextProperties::SetProperty(const wxString& name, bool value)
 
13007
{
 
13008
    SetProperty(name, wxVariant(value, name));
 
13009
}
 
13010
 
 
13011
void wxRichTextProperties::RemoveProperties(const wxRichTextProperties& properties)
 
13012
{
 
13013
    size_t i;
 
13014
    for (i = 0; i < properties.GetCount(); i++)
 
13015
    {
 
13016
        wxString name = properties.GetProperties()[i].GetName();
 
13017
        if (HasProperty(name))
 
13018
            Remove(name);
 
13019
    }
 
13020
}
 
13021
 
 
13022
void wxRichTextProperties::MergeProperties(const wxRichTextProperties& properties)
 
13023
{
 
13024
    size_t i;
 
13025
    for (i = 0; i < properties.GetCount(); i++)
 
13026
    {
 
13027
        SetProperty(properties.GetProperties()[i]);
 
13028
    }
 
13029
}
 
13030
 
 
13031
wxRichTextObject* wxRichTextObjectAddress::GetObject(wxRichTextParagraphLayoutBox* topLevelContainer) const
 
13032
{
 
13033
    if (m_address.GetCount() == 0)
 
13034
        return topLevelContainer;
 
13035
 
 
13036
    wxRichTextCompositeObject* p = topLevelContainer;
 
13037
    size_t i = 0;
 
13038
    while (p && i < m_address.GetCount())
 
13039
    {
 
13040
        int pos = m_address[i];
 
13041
        wxASSERT(pos >= 0 && pos < (int) p->GetChildren().GetCount());
 
13042
        if (pos < 0 || pos >= (int) p->GetChildren().GetCount())
 
13043
            return NULL;
 
13044
 
 
13045
        wxRichTextObject* p1 = p->GetChild(pos);
 
13046
        if (i == (m_address.GetCount()-1))
 
13047
            return p1;
 
13048
 
 
13049
        p = wxDynamicCast(p1, wxRichTextCompositeObject);
 
13050
        i ++;
 
13051
    }
 
13052
    return NULL;
 
13053
}
 
13054
 
 
13055
bool wxRichTextObjectAddress::Create(wxRichTextParagraphLayoutBox* topLevelContainer, wxRichTextObject* obj)
 
13056
{
 
13057
    m_address.Clear();
 
13058
 
 
13059
    if (topLevelContainer == obj)
 
13060
        return true;
 
13061
 
 
13062
    wxRichTextObject* o = obj;
 
13063
    while (o)
 
13064
    {
 
13065
        wxRichTextCompositeObject* p = wxDynamicCast(o->GetParent(), wxRichTextCompositeObject);
 
13066
        if (!p)
 
13067
            return false;
 
13068
 
 
13069
        int pos = p->GetChildren().IndexOf(o);
 
13070
        if (pos == -1)
 
13071
            return false;
 
13072
 
 
13073
        m_address.Insert(pos, 0);
 
13074
 
 
13075
        if (p == topLevelContainer)
 
13076
            return true;
 
13077
 
 
13078
        o = p;
 
13079
    }
 
13080
    return false;
 
13081
}
 
13082
 
 
13083
// Equality test
 
13084
bool wxRichTextSelection::operator==(const wxRichTextSelection& sel) const
 
13085
{
 
13086
    if (m_container != sel.m_container)
 
13087
        return false;
 
13088
    if (m_ranges.GetCount() != sel.m_ranges.GetCount())
 
13089
        return false;
 
13090
    size_t i;
 
13091
    for (i = 0; i < m_ranges.GetCount(); i++)
 
13092
        if (!(m_ranges[i] == sel.m_ranges[i]))
 
13093
            return false;
 
13094
    return true;
 
13095
}
 
13096
 
 
13097
// Get the selections appropriate to the specified object, if any; returns wxRICHTEXT_NO_SELECTION if none
 
13098
// or none at the level of the object's container.
 
13099
wxRichTextRangeArray wxRichTextSelection::GetSelectionForObject(wxRichTextObject* obj) const
 
13100
{
 
13101
    if (IsValid())
 
13102
    {
 
13103
        wxRichTextParagraphLayoutBox* container = obj->GetParentContainer();
 
13104
 
 
13105
        if (container == m_container)
 
13106
            return m_ranges;
 
13107
 
 
13108
        container = obj->GetContainer();
 
13109
        while (container)
 
13110
        {
 
13111
            if (container->GetParent())
 
13112
            {
 
13113
                // If we found that our object's container is within the range of
 
13114
                // a selection higher up, then assume the whole original object
 
13115
                // is also selected.
 
13116
                wxRichTextParagraphLayoutBox* parentContainer = container->GetParentContainer();
 
13117
                if (parentContainer == m_container)
 
13118
                {
 
13119
                    if (WithinSelection(container->GetRange().GetStart(), m_ranges))
 
13120
                    {
 
13121
                        wxRichTextRangeArray ranges;
 
13122
                        ranges.Add(obj->GetRange());
 
13123
                        return ranges;
 
13124
                    }
 
13125
                }
 
13126
 
 
13127
                container = parentContainer;
 
13128
            }
 
13129
            else
 
13130
            {
 
13131
                container = NULL;
 
13132
                break;
 
13133
            }
 
13134
        }
 
13135
    }
 
13136
    return wxRichTextRangeArray();
 
13137
}
 
13138
 
 
13139
// Is the given position within the selection?
 
13140
bool wxRichTextSelection::WithinSelection(long pos, wxRichTextObject* obj) const
 
13141
{
 
13142
    if (!IsValid())
 
13143
        return false;
 
13144
    else
 
13145
    {
 
13146
        wxRichTextRangeArray selectionRanges = GetSelectionForObject(obj);
 
13147
        return WithinSelection(pos, selectionRanges);
 
13148
    }
 
13149
}
 
13150
 
 
13151
// Is the given position within the selection range?
 
13152
bool wxRichTextSelection::WithinSelection(long pos, const wxRichTextRangeArray& ranges)
 
13153
{
 
13154
    size_t i;
 
13155
    for (i = 0; i < ranges.GetCount(); i++)
 
13156
    {
 
13157
        const wxRichTextRange& range = ranges[i];
 
13158
        if (pos >= range.GetStart() && pos <= range.GetEnd())
 
13159
            return true;
 
13160
    }
 
13161
    return false;
 
13162
}
 
13163
 
 
13164
// Is the given range completely within the selection range?
 
13165
bool wxRichTextSelection::WithinSelection(const wxRichTextRange& range, const wxRichTextRangeArray& ranges)
 
13166
{
 
13167
    size_t i;
 
13168
    for (i = 0; i < ranges.GetCount(); i++)
 
13169
    {
 
13170
        const wxRichTextRange& eachRange = ranges[i];
 
13171
        if (range.IsWithin(eachRange))
 
13172
            return true;
 
13173
    }
 
13174
    return false;
 
13175
}
 
13176
 
 
13177
IMPLEMENT_CLASS(wxRichTextDrawingHandler, wxObject)
 
13178
IMPLEMENT_CLASS(wxRichTextDrawingContext, wxObject)
 
13179
 
 
13180
bool wxRichTextDrawingContext::HasVirtualAttributes(wxRichTextObject* obj) const
 
13181
{
 
13182
    wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
 
13183
    while (node)
 
13184
    {
 
13185
        wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
 
13186
        if (handler->HasVirtualAttributes(obj))
 
13187
            return true;
 
13188
 
 
13189
        node = node->GetNext();
 
13190
    }
 
13191
    return false;
 
13192
}
 
13193
 
 
13194
wxRichTextAttr wxRichTextDrawingContext::GetVirtualAttributes(wxRichTextObject* obj) const
 
13195
{
 
13196
    wxRichTextAttr attr;
 
13197
    // We apply all handlers, so we can may combine several different attributes
 
13198
    wxList::compatibility_iterator node = m_buffer->GetDrawingHandlers().GetFirst();
 
13199
    while (node)
 
13200
    {
 
13201
        wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
 
13202
        if (handler->HasVirtualAttributes(obj))
 
13203
        {
 
13204
            bool success = handler->GetVirtualAttributes(attr, obj);
 
13205
            wxASSERT(success);
 
13206
            wxUnusedVar(success);
 
13207
        }
 
13208
 
 
13209
        node = node->GetNext();
 
13210
    }
 
13211
    return attr;
 
13212
}
 
13213
 
 
13214
bool wxRichTextDrawingContext::ApplyVirtualAttributes(wxRichTextAttr& attr, wxRichTextObject* obj) const
 
13215
{
 
13216
    if (HasVirtualAttributes(obj))
 
13217
    {
 
13218
        wxRichTextAttr a(GetVirtualAttributes(obj));
 
13219
        attr.Apply(a);
 
13220
        return true;
 
13221
    }
 
13222
    else
 
13223
        return false;
 
13224
}
 
13225
 
 
13226
/// Adds a handler to the end
 
13227
void wxRichTextBuffer::AddDrawingHandler(wxRichTextDrawingHandler *handler)
 
13228
{
 
13229
    sm_drawingHandlers.Append(handler);
 
13230
}
 
13231
 
 
13232
/// Inserts a handler at the front
 
13233
void wxRichTextBuffer::InsertDrawingHandler(wxRichTextDrawingHandler *handler)
 
13234
{
 
13235
    sm_drawingHandlers.Insert( handler );
 
13236
}
 
13237
 
 
13238
/// Removes a handler
 
13239
bool wxRichTextBuffer::RemoveDrawingHandler(const wxString& name)
 
13240
{
 
13241
    wxRichTextDrawingHandler *handler = FindDrawingHandler(name);
 
13242
    if (handler)
 
13243
    {
 
13244
        sm_drawingHandlers.DeleteObject(handler);
 
13245
        delete handler;
 
13246
        return true;
 
13247
    }
 
13248
    else
 
13249
        return false;
 
13250
}
 
13251
 
 
13252
wxRichTextDrawingHandler* wxRichTextBuffer::FindDrawingHandler(const wxString& name)
 
13253
{
 
13254
    wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
 
13255
    while (node)
 
13256
    {
 
13257
        wxRichTextDrawingHandler *handler = (wxRichTextDrawingHandler*)node->GetData();
 
13258
        if (handler->GetName().Lower() == name.Lower()) return handler;
 
13259
 
 
13260
        node = node->GetNext();
 
13261
    }
 
13262
    return NULL;
 
13263
}
 
13264
 
 
13265
void wxRichTextBuffer::CleanUpDrawingHandlers()
 
13266
{
 
13267
    wxList::compatibility_iterator node = sm_drawingHandlers.GetFirst();
 
13268
    while (node)
 
13269
    {
 
13270
        wxRichTextDrawingHandler* handler = (wxRichTextDrawingHandler*)node->GetData();
 
13271
        wxList::compatibility_iterator next = node->GetNext();
 
13272
        delete handler;
 
13273
        node = next;
 
13274
    }
 
13275
 
 
13276
    sm_drawingHandlers.Clear();
 
13277
}
 
13278
 
 
13279
void wxRichTextBuffer::AddFieldType(wxRichTextFieldType *fieldType)
 
13280
{
 
13281
    sm_fieldTypes[fieldType->GetName()] = fieldType;
 
13282
}
 
13283
 
 
13284
bool wxRichTextBuffer::RemoveFieldType(const wxString& name)
 
13285
{
 
13286
    wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
 
13287
    if (it == sm_fieldTypes.end())
 
13288
        return false;
 
13289
    else
 
13290
    {
 
13291
        wxRichTextFieldType* fieldType = it->second;
 
13292
        sm_fieldTypes.erase(it);
 
13293
        delete fieldType;
 
13294
        return true;
 
13295
    }
 
13296
}
 
13297
 
 
13298
wxRichTextFieldType *wxRichTextBuffer::FindFieldType(const wxString& name)
 
13299
{
 
13300
    wxRichTextFieldTypeHashMap::iterator it = sm_fieldTypes.find(name);
 
13301
    if (it == sm_fieldTypes.end())
 
13302
        return NULL;
 
13303
    else
 
13304
        return it->second;
 
13305
}
 
13306
 
 
13307
void wxRichTextBuffer::CleanUpFieldTypes()
 
13308
{
 
13309
    wxRichTextFieldTypeHashMap::iterator it;
 
13310
    for( it = sm_fieldTypes.begin(); it != sm_fieldTypes.end(); ++it )
 
13311
    {
 
13312
        wxRichTextFieldType* fieldType = it->second;
 
13313
        delete fieldType;
 
13314
    }
 
13315
 
 
13316
    sm_fieldTypes.clear();
 
13317
}
 
13318
 
 
13319
#endif
 
13320
    // wxUSE_RICHTEXT