~registry/dolphin-emu/triforce

« back to all changes in this revision

Viewing changes to Externals/wxWidgets3/src/generic/treelist.cpp

  • Committer: Sérgio Benjamim
  • Date: 2015-02-13 05:54:40 UTC
  • Revision ID: sergio_br2@yahoo.com.br-20150213055440-ey2rt3sjpy27km78
Dolphin Triforce branch from code.google, commit b957980 (4.0-315).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
///////////////////////////////////////////////////////////////////////////////
 
2
// Name:        src/generic/treelist.cpp
 
3
// Purpose:     Generic wxTreeListCtrl implementation.
 
4
// Author:      Vadim Zeitlin
 
5
// Created:     2011-08-19
 
6
// Copyright:   (c) 2011 Vadim Zeitlin <vadim@wxwidgets.org>
 
7
// Licence:     wxWindows licence
 
8
///////////////////////////////////////////////////////////////////////////////
 
9
 
 
10
// ============================================================================
 
11
// Declarations
 
12
// ============================================================================
 
13
 
 
14
// ----------------------------------------------------------------------------
 
15
// Headers
 
16
// ----------------------------------------------------------------------------
 
17
 
 
18
// for compilers that support precompilation, includes "wx.h".
 
19
#include "wx/wxprec.h"
 
20
 
 
21
#ifdef __BORLANDC__
 
22
    #pragma hdrstop
 
23
#endif
 
24
 
 
25
#if wxUSE_TREELISTCTRL
 
26
 
 
27
#ifndef WX_PRECOMP
 
28
    #include "wx/dc.h"
 
29
#endif // WX_PRECOMP
 
30
 
 
31
#include "wx/treelist.h"
 
32
 
 
33
#include "wx/dataview.h"
 
34
#include "wx/renderer.h"
 
35
#include "wx/scopedarray.h"
 
36
#include "wx/scopedptr.h"
 
37
 
 
38
// ----------------------------------------------------------------------------
 
39
// Constants
 
40
// ----------------------------------------------------------------------------
 
41
 
 
42
const char wxTreeListCtrlNameStr[] = "wxTreeListCtrl";
 
43
 
 
44
const wxTreeListItem wxTLI_FIRST(reinterpret_cast<wxTreeListModelNode*>(-1));
 
45
const wxTreeListItem wxTLI_LAST(reinterpret_cast<wxTreeListModelNode*>(-2));
 
46
 
 
47
// ----------------------------------------------------------------------------
 
48
// wxTreeListModelNode: a node in the internal tree representation.
 
49
// ----------------------------------------------------------------------------
 
50
 
 
51
class wxTreeListModelNode
 
52
{
 
53
public:
 
54
    wxTreeListModelNode(wxTreeListModelNode* parent,
 
55
                        const wxString& text = wxString(),
 
56
                        int imageClosed = wxWithImages::NO_IMAGE,
 
57
                        int imageOpened = wxWithImages::NO_IMAGE,
 
58
                        wxClientData* data = NULL)
 
59
        : m_text(text),
 
60
          m_parent(parent)
 
61
    {
 
62
        m_child =
 
63
        m_next = NULL;
 
64
 
 
65
        m_imageClosed = imageClosed;
 
66
        m_imageOpened = imageOpened;
 
67
 
 
68
        m_checkedState = wxCHK_UNCHECKED;
 
69
 
 
70
        m_data = data;
 
71
 
 
72
        m_columnsTexts = NULL;
 
73
    }
 
74
 
 
75
    // Destroying the node also (recursively) destroys its children.
 
76
    ~wxTreeListModelNode()
 
77
    {
 
78
        for ( wxTreeListModelNode* node = m_child; node; )
 
79
        {
 
80
            wxTreeListModelNode* child = node;
 
81
            node = node->m_next;
 
82
            delete child;
 
83
        }
 
84
 
 
85
        delete m_data;
 
86
 
 
87
        delete [] m_columnsTexts;
 
88
    }
 
89
 
 
90
 
 
91
    // Public fields for the first column text and other simple attributes:
 
92
    // there is no need to have accessors/mutators for those as there is no
 
93
    // encapsulation anyhow, all of those are exposed in our public API.
 
94
    wxString m_text;
 
95
 
 
96
    int m_imageClosed,
 
97
        m_imageOpened;
 
98
 
 
99
    wxCheckBoxState m_checkedState;
 
100
 
 
101
 
 
102
    // Accessors for the fields that are not directly exposed.
 
103
 
 
104
    // Client data is owned by us so delete the old value when setting the new
 
105
    // one.
 
106
    wxClientData* GetClientData() const { return m_data; }
 
107
    void SetClientData(wxClientData* data) { delete m_data; m_data = data; }
 
108
 
 
109
    // Setting or getting the non-first column text. Getting is simple but you
 
110
    // need to call HasColumnsTexts() first as the column data is only
 
111
    // allocated on demand. And when setting the text we require to be given
 
112
    // the total number of columns as we allocate the entire array at once,
 
113
    // this is more efficient than using dynamically-expandable wxVector that
 
114
    // we know won't be needed as the number of columns is usually fixed. But
 
115
    // if it does change, our OnInsertColumn() must be called.
 
116
    //
 
117
    // Notice the presence of -1 everywhere in these methods: this is because
 
118
    // the text for the first column is always stored in m_text and so we don't
 
119
    // store it in m_columnsTexts.
 
120
 
 
121
    bool HasColumnsTexts() const { return m_columnsTexts != NULL; }
 
122
    const wxString& GetColumnText(unsigned col) const
 
123
    {
 
124
        return m_columnsTexts[col - 1];
 
125
    }
 
126
 
 
127
    void SetColumnText(const wxString& text, unsigned col, unsigned numColumns)
 
128
    {
 
129
        if ( !m_columnsTexts )
 
130
            m_columnsTexts = new wxString[numColumns - 1];
 
131
 
 
132
        m_columnsTexts[col - 1] = text;
 
133
    }
 
134
 
 
135
    void OnInsertColumn(unsigned col, unsigned numColumns)
 
136
    {
 
137
        wxASSERT_MSG( col, "Shouldn't be called for the first column" );
 
138
 
 
139
        // Nothing to do if we don't have any text.
 
140
        if ( !m_columnsTexts )
 
141
            return;
 
142
 
 
143
        wxScopedArray<wxString> oldTexts(m_columnsTexts);
 
144
        m_columnsTexts = new wxString[numColumns - 1];
 
145
 
 
146
        // In the loop below n is the index in the new column texts array and m
 
147
        // is the index in the old one.
 
148
        for ( unsigned n = 1, m = 1; n < numColumns - 1; n++, m++ )
 
149
        {
 
150
            if ( n == col )
 
151
            {
 
152
                // Leave the new array text initially empty and just adjust the
 
153
                // index (to compensate for "m++" done by the loop anyhow).
 
154
                m--;
 
155
            }
 
156
            else // Not the newly inserted column.
 
157
            {
 
158
                // Copy the old text value.
 
159
                m_columnsTexts[n - 1] = oldTexts[m - 1];
 
160
            }
 
161
        }
 
162
    }
 
163
 
 
164
    void OnDeleteColumn(unsigned col, unsigned numColumns)
 
165
    {
 
166
        wxASSERT_MSG( col, "Shouldn't be called for the first column" );
 
167
 
 
168
        if ( !m_columnsTexts )
 
169
            return;
 
170
 
 
171
        wxScopedArray<wxString> oldTexts(m_columnsTexts);
 
172
        m_columnsTexts = new wxString[numColumns - 2];
 
173
        for ( unsigned n = 1, m = 1; n < numColumns - 1; n++, m++ )
 
174
        {
 
175
            if ( n == col )
 
176
            {
 
177
                n--;
 
178
            }
 
179
            else // Not the deleted column.
 
180
            {
 
181
                m_columnsTexts[n - 1] = oldTexts[m - 1];
 
182
            }
 
183
        }
 
184
    }
 
185
 
 
186
    void OnClearColumns()
 
187
    {
 
188
        if ( m_columnsTexts )
 
189
        {
 
190
            delete [] m_columnsTexts;
 
191
            m_columnsTexts = NULL;
 
192
        }
 
193
    }
 
194
 
 
195
 
 
196
    // Functions for modifying the tree.
 
197
 
 
198
    // Insert the given item as the first child of this one. The parent pointer
 
199
    // must have been already set correctly at creation and we take ownership
 
200
    // of the pointer and will delete it later.
 
201
    void InsertChild(wxTreeListModelNode* child)
 
202
    {
 
203
        wxASSERT( child->m_parent == this );
 
204
 
 
205
        // Our previous first child becomes the next sibling of the new child.
 
206
        child->m_next = m_child;
 
207
        m_child = child;
 
208
    }
 
209
 
 
210
    // Insert the given item as our next sibling. As above, the item must have
 
211
    // the correct parent pointer and we take ownership of it.
 
212
    void InsertNext(wxTreeListModelNode* next)
 
213
    {
 
214
        wxASSERT( next->m_parent == m_parent );
 
215
 
 
216
        next->m_next = m_next;
 
217
        m_next = next;
 
218
    }
 
219
 
 
220
    // Remove the first child of this item from the tree and delete it.
 
221
    void DeleteChild()
 
222
    {
 
223
        wxTreeListModelNode* const oldChild = m_child;
 
224
        m_child = m_child->m_next;
 
225
        delete oldChild;
 
226
    }
 
227
 
 
228
    // Remove the next sibling of this item from the tree and deletes it.
 
229
    void DeleteNext()
 
230
    {
 
231
        wxTreeListModelNode* const oldNext = m_next;
 
232
        m_next = m_next->m_next;
 
233
        delete oldNext;
 
234
    }
 
235
 
 
236
 
 
237
    // Functions for tree traversal. All of them can return NULL.
 
238
 
 
239
    // Only returns NULL when called on the root item.
 
240
    wxTreeListModelNode* GetParent() const { return m_parent; }
 
241
 
 
242
    // Returns the first child of this item.
 
243
    wxTreeListModelNode* GetChild() const { return m_child; }
 
244
 
 
245
    // Returns the next sibling of this item.
 
246
    wxTreeListModelNode* GetNext() const { return m_next; }
 
247
 
 
248
    // Unlike the previous two functions, this one is not a simple accessor
 
249
    // (hence it's not called "GetSomething") but computes the next node after
 
250
    // this one in tree order.
 
251
    wxTreeListModelNode* NextInTree() const
 
252
    {
 
253
        if ( m_child )
 
254
            return m_child;
 
255
 
 
256
        if ( m_next )
 
257
            return m_next;
 
258
 
 
259
        // Recurse upwards until we find the next sibling.
 
260
        for ( wxTreeListModelNode* node = m_parent; node; node = node->m_parent )
 
261
        {
 
262
            if ( node->m_next )
 
263
                return node->m_next;
 
264
        }
 
265
 
 
266
        return NULL;
 
267
    }
 
268
 
 
269
 
 
270
private:
 
271
    // The (never changing after creation) parent of this node and the possibly
 
272
    // NULL pointers to its first child and next sibling.
 
273
    wxTreeListModelNode* const m_parent;
 
274
    wxTreeListModelNode* m_child;
 
275
    wxTreeListModelNode* m_next;
 
276
 
 
277
    // Client data pointer owned by the control. May be NULL.
 
278
    wxClientData* m_data;
 
279
 
 
280
    // Array of column values for all the columns except the first one. May be
 
281
    // NULL if no values had been set for them.
 
282
    wxString* m_columnsTexts;
 
283
};
 
284
 
 
285
// ----------------------------------------------------------------------------
 
286
// wxTreeListModel: wxDataViewModel implementation used by wxTreeListCtrl.
 
287
// ----------------------------------------------------------------------------
 
288
 
 
289
class wxTreeListModel : public wxDataViewModel
 
290
{
 
291
public:
 
292
    typedef wxTreeListModelNode Node;
 
293
 
 
294
    // Unlike a general wxDataViewModel, this model can only be used with a
 
295
    // single control at once. The main reason for this is that we need to
 
296
    // support different icons for opened and closed items and the item state
 
297
    // is associated with the control, not the model, so our GetValue() is also
 
298
    // bound to it (otherwise, what would it return for an item expanded in one
 
299
    // associated control and collapsed in another one?).
 
300
    wxTreeListModel(wxTreeListCtrl* treelist);
 
301
    virtual ~wxTreeListModel();
 
302
 
 
303
 
 
304
    // Helpers for converting between wxDataViewItem and wxTreeListItem. These
 
305
    // methods simply cast the pointer to/from wxDataViewItem except for the
 
306
    // root node that we handle specially unless explicitly disabled.
 
307
    //
 
308
    // The advantage of using them is that they're greppable and stand out
 
309
    // better, hopefully making the code more clear.
 
310
    Node* FromNonRootDVI(wxDataViewItem dvi) const
 
311
    {
 
312
        return static_cast<Node*>(dvi.GetID());
 
313
    }
 
314
 
 
315
    Node* FromDVI(wxDataViewItem dvi) const
 
316
    {
 
317
        if ( !dvi.IsOk() )
 
318
            return m_root;
 
319
 
 
320
        return FromNonRootDVI(dvi);
 
321
    }
 
322
 
 
323
    wxDataViewItem ToNonRootDVI(Node* node) const
 
324
    {
 
325
        return wxDataViewItem(node);
 
326
    }
 
327
 
 
328
    wxDataViewItem ToDVI(Node* node) const
 
329
    {
 
330
        // Our root item must be represented as NULL at wxDVC level to map to
 
331
        // its own invisible root.
 
332
        if ( !node->GetParent() )
 
333
            return wxDataViewItem();
 
334
 
 
335
        return ToNonRootDVI(node);
 
336
    }
 
337
 
 
338
 
 
339
    // Methods called by wxTreeListCtrl.
 
340
    void InsertColumn(unsigned col);
 
341
    void DeleteColumn(unsigned col);
 
342
    void ClearColumns();
 
343
 
 
344
    Node* InsertItem(Node* parent,
 
345
                     Node* previous,
 
346
                     const wxString& text,
 
347
                     int imageClosed,
 
348
                     int imageOpened,
 
349
                     wxClientData* data);
 
350
    void DeleteItem(Node* item);
 
351
    void DeleteAllItems();
 
352
 
 
353
    Node* GetRootItem() const { return m_root; }
 
354
 
 
355
    const wxString& GetItemText(Node* item, unsigned col) const;
 
356
    void SetItemText(Node* item, unsigned col, const wxString& text);
 
357
    void SetItemImage(Node* item, int closed, int opened);
 
358
    wxClientData* GetItemData(Node* item) const;
 
359
    void SetItemData(Node* item, wxClientData* data);
 
360
 
 
361
    void CheckItem(Node* item, wxCheckBoxState checkedState);
 
362
    void ToggleItem(wxDataViewItem item);
 
363
 
 
364
 
 
365
    // Implement the base class pure virtual methods.
 
366
    virtual unsigned GetColumnCount() const;
 
367
    virtual wxString GetColumnType(unsigned col) const;
 
368
    virtual void GetValue(wxVariant& variant,
 
369
                          const wxDataViewItem& item,
 
370
                          unsigned col) const;
 
371
    virtual bool SetValue(const wxVariant& variant,
 
372
                          const wxDataViewItem& item,
 
373
                          unsigned col);
 
374
    virtual wxDataViewItem GetParent(const wxDataViewItem& item) const;
 
375
    virtual bool IsContainer(const wxDataViewItem& item) const;
 
376
    virtual bool HasContainerColumns(const wxDataViewItem& item) const;
 
377
    virtual unsigned GetChildren(const wxDataViewItem& item,
 
378
                                 wxDataViewItemArray& children) const;
 
379
    virtual bool IsListModel() const { return m_isFlat; }
 
380
    virtual int Compare(const wxDataViewItem& item1,
 
381
                        const wxDataViewItem& item2,
 
382
                        unsigned col,
 
383
                        bool ascending) const;
 
384
 
 
385
private:
 
386
    // The control we're associated with.
 
387
    wxTreeListCtrl* const m_treelist;
 
388
 
 
389
    // The unique invisible root element.
 
390
    Node* const m_root;
 
391
 
 
392
    // Number of columns we maintain.
 
393
    unsigned m_numColumns;
 
394
 
 
395
    // Set to false as soon as we have more than one level, i.e. as soon as any
 
396
    // items with non-root item as parent are added (and currently never reset
 
397
    // after this).
 
398
    bool m_isFlat;
 
399
};
 
400
 
 
401
// ============================================================================
 
402
// wxDataViewCheckIconText[Renderer]: special renderer for our first column.
 
403
// ============================================================================
 
404
 
 
405
// Currently this class is private but it could be extracted and made part of
 
406
// public API later as could be used directly with wxDataViewCtrl as well.
 
407
namespace
 
408
{
 
409
 
 
410
const char* CHECK_ICON_TEXT_TYPE = "wxDataViewCheckIconText";
 
411
 
 
412
// The value used by wxDataViewCheckIconTextRenderer
 
413
class wxDataViewCheckIconText : public wxDataViewIconText
 
414
{
 
415
public:
 
416
    wxDataViewCheckIconText(const wxString& text = wxString(),
 
417
                            const wxIcon& icon = wxNullIcon,
 
418
                            wxCheckBoxState checkedState = wxCHK_UNDETERMINED)
 
419
        : wxDataViewIconText(text, icon),
 
420
          m_checkedState(checkedState)
 
421
    {
 
422
    }
 
423
 
 
424
    wxDataViewCheckIconText(const wxDataViewCheckIconText& other)
 
425
        : wxDataViewIconText(other),
 
426
          m_checkedState(other.m_checkedState)
 
427
    {
 
428
    }
 
429
 
 
430
    bool IsSameAs(const wxDataViewCheckIconText& other) const
 
431
    {
 
432
        return wxDataViewIconText::IsSameAs(other) &&
 
433
                m_checkedState == other.m_checkedState;
 
434
    }
 
435
 
 
436
    // There is no encapsulation anyhow, so just expose this field directly.
 
437
    wxCheckBoxState m_checkedState;
 
438
 
 
439
 
 
440
private:
 
441
    wxDECLARE_DYNAMIC_CLASS(wxDataViewCheckIconText);
 
442
};
 
443
 
 
444
wxIMPLEMENT_DYNAMIC_CLASS(wxDataViewCheckIconText, wxDataViewIconText);
 
445
 
 
446
DECLARE_VARIANT_OBJECT(wxDataViewCheckIconText)
 
447
IMPLEMENT_VARIANT_OBJECT(wxDataViewCheckIconText)
 
448
 
 
449
 
 
450
class wxDataViewCheckIconTextRenderer : public wxDataViewCustomRenderer
 
451
{
 
452
public:
 
453
    wxDataViewCheckIconTextRenderer()
 
454
        : wxDataViewCustomRenderer(CHECK_ICON_TEXT_TYPE,
 
455
                                   wxDATAVIEW_CELL_ACTIVATABLE)
 
456
    {
 
457
    }
 
458
 
 
459
    virtual bool SetValue(const wxVariant& value)
 
460
    {
 
461
        m_value << value;
 
462
        return true;
 
463
    }
 
464
 
 
465
    virtual bool GetValue(wxVariant& WXUNUSED(value)) const
 
466
    {
 
467
        return false;
 
468
    }
 
469
 
 
470
    wxSize GetSize() const
 
471
    {
 
472
        wxSize size = GetCheckSize();
 
473
        size.x += MARGIN_CHECK_ICON;
 
474
 
 
475
        if ( m_value.GetIcon().IsOk() )
 
476
        {
 
477
            const wxSize sizeIcon = m_value.GetIcon().GetSize();
 
478
            if ( sizeIcon.y > size.y )
 
479
                size.y = sizeIcon.y;
 
480
 
 
481
            size.x += sizeIcon.x + MARGIN_ICON_TEXT;
 
482
        }
 
483
 
 
484
        wxString text = m_value.GetText();
 
485
        if ( text.empty() )
 
486
            text = "Dummy";
 
487
 
 
488
        const wxSize sizeText = GetTextExtent(text);
 
489
        if ( sizeText.y > size.y )
 
490
            size.y = sizeText.y;
 
491
 
 
492
        size.x += sizeText.x;
 
493
 
 
494
        return size;
 
495
    }
 
496
 
 
497
    virtual bool Render(wxRect cell, wxDC* dc, int state)
 
498
    {
 
499
        // Draw the checkbox first.
 
500
        int renderFlags = 0;
 
501
        switch ( m_value.m_checkedState )
 
502
        {
 
503
            case wxCHK_UNCHECKED:
 
504
                break;
 
505
 
 
506
            case wxCHK_CHECKED:
 
507
                renderFlags |= wxCONTROL_CHECKED;
 
508
                break;
 
509
 
 
510
            case wxCHK_UNDETERMINED:
 
511
                renderFlags |= wxCONTROL_UNDETERMINED;
 
512
                break;
 
513
        }
 
514
 
 
515
        if ( state & wxDATAVIEW_CELL_PRELIT )
 
516
            renderFlags |= wxCONTROL_CURRENT;
 
517
 
 
518
        const wxSize sizeCheck = GetCheckSize();
 
519
 
 
520
        wxRect rectCheck(cell.GetPosition(), sizeCheck);
 
521
        rectCheck = rectCheck.CentreIn(cell, wxVERTICAL);
 
522
 
 
523
        wxRendererNative::Get().DrawCheckBox
 
524
                                (
 
525
                                    GetView(), *dc, rectCheck, renderFlags
 
526
                                );
 
527
 
 
528
        // Then the icon, if any.
 
529
        int xoffset = sizeCheck.x + MARGIN_CHECK_ICON;
 
530
 
 
531
        const wxIcon& icon = m_value.GetIcon();
 
532
        if ( icon.IsOk() )
 
533
        {
 
534
            const wxSize sizeIcon = icon.GetSize();
 
535
            wxRect rectIcon(cell.GetPosition(), sizeIcon);
 
536
            rectIcon.x += xoffset;
 
537
            rectIcon = rectIcon.CentreIn(cell, wxVERTICAL);
 
538
 
 
539
            dc->DrawIcon(icon, rectIcon.GetPosition());
 
540
 
 
541
            xoffset += sizeIcon.x + MARGIN_ICON_TEXT;
 
542
        }
 
543
 
 
544
        // Finally the text.
 
545
        RenderText(m_value.GetText(), xoffset, cell, dc, state);
 
546
 
 
547
        return true;
 
548
    }
 
549
 
 
550
    // Event handlers toggling the items checkbox if it was clicked.
 
551
    virtual bool ActivateCell(const wxRect& WXUNUSED(cell),
 
552
                              wxDataViewModel *model,
 
553
                              const wxDataViewItem & item,
 
554
                              unsigned int WXUNUSED(col),
 
555
                              const wxMouseEvent *mouseEvent)
 
556
    {
 
557
        if ( mouseEvent )
 
558
        {
 
559
            if ( !wxRect(GetCheckSize()).Contains(mouseEvent->GetPosition()) )
 
560
                return false;
 
561
        }
 
562
 
 
563
        static_cast<wxTreeListModel*>(model)->ToggleItem(item);
 
564
        return true;
 
565
    }
 
566
 
 
567
protected:
 
568
    wxSize GetCheckSize() const
 
569
    {
 
570
        return wxRendererNative::Get().GetCheckBoxSize(GetView());
 
571
    }
 
572
 
 
573
private:
 
574
    // Just some arbitrary constants defining margins, in pixels.
 
575
    enum
 
576
    {
 
577
        MARGIN_CHECK_ICON = 3,
 
578
        MARGIN_ICON_TEXT = 4
 
579
    };
 
580
 
 
581
    wxDataViewCheckIconText m_value;
 
582
};
 
583
 
 
584
} // anonymous namespace
 
585
 
 
586
// ============================================================================
 
587
// wxTreeListModel implementation
 
588
// ============================================================================
 
589
 
 
590
wxTreeListModel::wxTreeListModel(wxTreeListCtrl* treelist)
 
591
    : m_treelist(treelist),
 
592
      m_root(new Node(NULL))
 
593
{
 
594
    m_numColumns = 0;
 
595
    m_isFlat = true;
 
596
}
 
597
 
 
598
wxTreeListModel::~wxTreeListModel()
 
599
{
 
600
    delete m_root;
 
601
}
 
602
 
 
603
void wxTreeListModel::InsertColumn(unsigned col)
 
604
{
 
605
    m_numColumns++;
 
606
 
 
607
    // There is no need to update anything when inserting the first column.
 
608
    if ( m_numColumns == 1 )
 
609
        return;
 
610
 
 
611
    // Update all the items as they may have texts for the old columns.
 
612
    for ( Node* node = m_root->GetChild(); node; node = node->NextInTree() )
 
613
    {
 
614
        node->OnInsertColumn(col, m_numColumns);
 
615
    }
 
616
}
 
617
 
 
618
void wxTreeListModel::DeleteColumn(unsigned col)
 
619
{
 
620
    wxCHECK_RET( col < m_numColumns, "Invalid column index" );
 
621
 
 
622
    // Update all the items to remove the text for the non first columns.
 
623
    if ( col > 0 )
 
624
    {
 
625
        for ( Node* node = m_root->GetChild(); node; node = node->NextInTree() )
 
626
        {
 
627
            node->OnDeleteColumn(col, m_numColumns);
 
628
        }
 
629
    }
 
630
 
 
631
    m_numColumns--;
 
632
}
 
633
 
 
634
void wxTreeListModel::ClearColumns()
 
635
{
 
636
    m_numColumns = 0;
 
637
 
 
638
    for ( Node* node = m_root->GetChild(); node; node = node->NextInTree() )
 
639
    {
 
640
        node->OnClearColumns();
 
641
    }
 
642
}
 
643
 
 
644
wxTreeListModelNode*
 
645
wxTreeListModel::InsertItem(Node* parent,
 
646
                            Node* previous,
 
647
                            const wxString& text,
 
648
                            int imageClosed,
 
649
                            int imageOpened,
 
650
                            wxClientData* data)
 
651
{
 
652
    wxCHECK_MSG( parent, NULL,
 
653
                 "Must have a valid parent (maybe GetRootItem()?)" );
 
654
 
 
655
    wxCHECK_MSG( previous, NULL,
 
656
                 "Must have a valid previous item (maybe wxTLI_FIRST/LAST?)" );
 
657
 
 
658
    if ( m_isFlat && parent != m_root )
 
659
    {
 
660
        // Not flat any more, this is a second level child.
 
661
        m_isFlat = false;
 
662
    }
 
663
 
 
664
    wxScopedPtr<Node>
 
665
        newItem(new Node(parent, text, imageClosed, imageOpened, data));
 
666
 
 
667
    // FIXME-VC6: This compiler refuses to compare "Node* previous" with
 
668
    //            wxTLI_XXX without some help.
 
669
    const wxTreeListItem previousItem(previous);
 
670
 
 
671
    // If we have no children at all, then inserting as last child is the same
 
672
    // as inserting as the first one so check for it here too.
 
673
    if ( previousItem == wxTLI_FIRST ||
 
674
            (previousItem == wxTLI_LAST && !parent->GetChild()) )
 
675
    {
 
676
        parent->InsertChild(newItem.get());
 
677
    }
 
678
    else // Not the first item, find the previous one.
 
679
    {
 
680
        if ( previousItem == wxTLI_LAST )
 
681
        {
 
682
            previous = parent->GetChild();
 
683
 
 
684
            // Find the last child.
 
685
            for ( ;; )
 
686
            {
 
687
                Node* const next = previous->GetNext();
 
688
                if ( !next )
 
689
                    break;
 
690
 
 
691
                previous = next;
 
692
            }
 
693
        }
 
694
        else // We already have the previous item.
 
695
        {
 
696
            // Just check it's under the correct parent.
 
697
            wxCHECK_MSG( previous->GetParent() == parent, NULL,
 
698
                         "Previous item is not under the right parent" );
 
699
        }
 
700
 
 
701
        previous->InsertNext(newItem.get());
 
702
    }
 
703
 
 
704
    ItemAdded(ToDVI(parent), ToDVI(newItem.get()));
 
705
 
 
706
    // The item was successfully inserted in the tree and so will be deleted by
 
707
    // it, we can detach it now.
 
708
    return newItem.release();
 
709
}
 
710
 
 
711
void wxTreeListModel::DeleteItem(Node* item)
 
712
{
 
713
    wxCHECK_RET( item, "Invalid item" );
 
714
 
 
715
    wxCHECK_RET( item != m_root, "Can't delete the root item" );
 
716
 
 
717
    Node* const parent = item->GetParent();
 
718
 
 
719
    ItemDeleted(ToDVI(parent), ToDVI(item));
 
720
 
 
721
    Node* previous = parent->GetChild();
 
722
    if ( previous == item )
 
723
    {
 
724
        parent->DeleteChild();
 
725
    }
 
726
    else // Not the first child of its parent.
 
727
    {
 
728
        // Find the sibling just before it.
 
729
        for ( ;; )
 
730
        {
 
731
            Node* const next = previous->GetNext();
 
732
            if ( next == item )
 
733
                break;
 
734
 
 
735
            wxCHECK_RET( next, "Item not a child of its parent?" );
 
736
 
 
737
            previous = next;
 
738
        }
 
739
 
 
740
        previous->DeleteNext();
 
741
    }
 
742
}
 
743
 
 
744
void wxTreeListModel::DeleteAllItems()
 
745
{
 
746
    while ( m_root->GetChild() )
 
747
    {
 
748
        m_root->DeleteChild();
 
749
    }
 
750
 
 
751
    Cleared();
 
752
}
 
753
 
 
754
const wxString& wxTreeListModel::GetItemText(Node* item, unsigned col) const
 
755
{
 
756
    // Returning root item text here is bogus, it just happens to be an always
 
757
    // empty string we can return reference to.
 
758
    wxCHECK_MSG( item, m_root->m_text, "Invalid item" );
 
759
 
 
760
    // Notice that asking for the text of a column of an item that doesn't have
 
761
    // any column texts is not an error so we simply return an empty string in
 
762
    // this case.
 
763
    return col == 0 ? item->m_text
 
764
                    : item->HasColumnsTexts() ? item->GetColumnText(col)
 
765
                                              : m_root->m_text;
 
766
}
 
767
 
 
768
void wxTreeListModel::SetItemText(Node* item, unsigned col, const wxString& text)
 
769
{
 
770
    wxCHECK_RET( item, "Invalid item" );
 
771
 
 
772
    if ( col == 0 )
 
773
        item->m_text = text;
 
774
    else
 
775
        item->SetColumnText(text, col, m_numColumns);
 
776
 
 
777
    ValueChanged(ToDVI(item), col);
 
778
}
 
779
 
 
780
void wxTreeListModel::SetItemImage(Node* item, int closed, int opened)
 
781
{
 
782
    wxCHECK_RET( item, "Invalid item" );
 
783
 
 
784
    item->m_imageClosed = closed;
 
785
    item->m_imageOpened = opened;
 
786
 
 
787
    ValueChanged(ToDVI(item), 0);
 
788
}
 
789
 
 
790
wxClientData* wxTreeListModel::GetItemData(Node* item) const
 
791
{
 
792
    wxCHECK_MSG( item, NULL, "Invalid item" );
 
793
 
 
794
    return item->GetClientData();
 
795
}
 
796
 
 
797
void wxTreeListModel::SetItemData(Node* item, wxClientData* data)
 
798
{
 
799
    wxCHECK_RET( item, "Invalid item" );
 
800
 
 
801
    item->SetClientData(data);
 
802
}
 
803
 
 
804
void wxTreeListModel::CheckItem(Node* item, wxCheckBoxState checkedState)
 
805
{
 
806
    wxCHECK_RET( item, "Invalid item" );
 
807
 
 
808
    item->m_checkedState = checkedState;
 
809
 
 
810
    ItemChanged(ToDVI(item));
 
811
}
 
812
 
 
813
void wxTreeListModel::ToggleItem(wxDataViewItem dvi)
 
814
{
 
815
    Node* const item = FromDVI(dvi);
 
816
 
 
817
    wxCHECK_RET( item, "Invalid item" );
 
818
 
 
819
    const wxCheckBoxState stateOld = item->m_checkedState;
 
820
 
 
821
    // If the 3rd state is user-settable then the cycle is
 
822
    // unchecked->checked->undetermined.
 
823
    switch ( stateOld )
 
824
    {
 
825
        case wxCHK_CHECKED:
 
826
            item->m_checkedState = m_treelist->HasFlag(wxTL_USER_3STATE)
 
827
                                        ? wxCHK_UNDETERMINED
 
828
                                        : wxCHK_UNCHECKED;
 
829
            break;
 
830
 
 
831
        case wxCHK_UNDETERMINED:
 
832
            // Whether 3rd state is user-settable or not, the next state is
 
833
            // unchecked.
 
834
            item->m_checkedState = wxCHK_UNCHECKED;
 
835
            break;
 
836
 
 
837
        case wxCHK_UNCHECKED:
 
838
            item->m_checkedState = wxCHK_CHECKED;
 
839
            break;
 
840
    }
 
841
 
 
842
    ItemChanged(ToDVI(item));
 
843
 
 
844
    m_treelist->OnItemToggled(item, stateOld);
 
845
}
 
846
 
 
847
unsigned wxTreeListModel::GetColumnCount() const
 
848
{
 
849
    return m_numColumns;
 
850
}
 
851
 
 
852
wxString wxTreeListModel::GetColumnType(unsigned col) const
 
853
{
 
854
    if ( col == 0 )
 
855
    {
 
856
        return m_treelist->HasFlag(wxTL_CHECKBOX)
 
857
                    ? wxS("wxDataViewCheckIconText")
 
858
                    : wxS("wxDataViewIconText");
 
859
    }
 
860
    else // All the other columns contain just text.
 
861
    {
 
862
        return wxS("string");
 
863
    }
 
864
}
 
865
 
 
866
void
 
867
wxTreeListModel::GetValue(wxVariant& variant,
 
868
                          const wxDataViewItem& item,
 
869
                          unsigned col) const
 
870
{
 
871
    Node* const node = FromDVI(item);
 
872
 
 
873
    if ( col == 0 )
 
874
    {
 
875
        // Determine the correct image to use depending on the item state.
 
876
        int image = wxWithImages::NO_IMAGE;
 
877
        if ( m_treelist->IsExpanded(node) )
 
878
            image = node->m_imageOpened;
 
879
 
 
880
        if ( image == wxWithImages::NO_IMAGE )
 
881
            image = node->m_imageClosed;
 
882
 
 
883
        wxIcon icon = m_treelist->GetImage(image);
 
884
 
 
885
        if ( m_treelist->HasFlag(wxTL_CHECKBOX) )
 
886
            variant << wxDataViewCheckIconText(node->m_text, icon,
 
887
                                               node->m_checkedState);
 
888
        else
 
889
            variant << wxDataViewIconText(node->m_text, icon);
 
890
    }
 
891
    else
 
892
    {
 
893
        // Notice that we must still assign wxString to wxVariant to ensure
 
894
        // that it at least has the correct type.
 
895
        wxString text;
 
896
        if ( node->HasColumnsTexts() )
 
897
            text = node->GetColumnText(col);
 
898
 
 
899
        variant = text;
 
900
    }
 
901
}
 
902
 
 
903
bool
 
904
wxTreeListModel::SetValue(const wxVariant& WXUNUSED(variant),
 
905
                          const wxDataViewItem& WXUNUSED(item),
 
906
                          unsigned WXUNUSED(col))
 
907
{
 
908
    // We are not editable currently.
 
909
    return false;
 
910
}
 
911
 
 
912
wxDataViewItem wxTreeListModel::GetParent(const wxDataViewItem& item) const
 
913
{
 
914
    Node* const node = FromDVI(item);
 
915
 
 
916
    return ToDVI(node->GetParent());
 
917
}
 
918
 
 
919
bool wxTreeListModel::IsContainer(const wxDataViewItem& item) const
 
920
{
 
921
    // FIXME: In the generic (and native OS X) versions we implement this
 
922
    //        method normally, i.e. only items with children are containers.
 
923
    //        But for the native GTK version we must pretend that all items are
 
924
    //        containers because otherwise adding children to them later would
 
925
    //        fail because wxGTK code calls IsContainer() too early (when
 
926
    //        adding the item itself) and we can't know whether we're container
 
927
    //        or not by then. Luckily, always returning true doesn't have any
 
928
    //        serious drawbacks for us.
 
929
#ifdef __WXGTK__
 
930
    wxUnusedVar(item);
 
931
 
 
932
    return true;
 
933
#else
 
934
    Node* const node = FromDVI(item);
 
935
 
 
936
    return node->GetChild() != NULL;
 
937
#endif
 
938
}
 
939
 
 
940
bool
 
941
wxTreeListModel::HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const
 
942
{
 
943
    return true;
 
944
}
 
945
 
 
946
unsigned
 
947
wxTreeListModel::GetChildren(const wxDataViewItem& item,
 
948
                             wxDataViewItemArray& children) const
 
949
{
 
950
    Node* const node = FromDVI(item);
 
951
 
 
952
    unsigned numChildren = 0;
 
953
    for ( Node* child = node->GetChild(); child; child = child->GetNext() )
 
954
    {
 
955
        children.push_back(ToDVI(child));
 
956
        numChildren++;
 
957
    }
 
958
 
 
959
    return numChildren;
 
960
}
 
961
 
 
962
int
 
963
wxTreeListModel::Compare(const wxDataViewItem& item1,
 
964
                         const wxDataViewItem& item2,
 
965
                         unsigned col,
 
966
                         bool ascending) const
 
967
{
 
968
    // Compare using default alphabetical order if no custom comparator.
 
969
    wxTreeListItemComparator* const comp = m_treelist->m_comparator;
 
970
    if ( !comp )
 
971
        return wxDataViewModel::Compare(item1, item2, col, ascending);
 
972
 
 
973
    // Forward comparison to the comparator:
 
974
    int result = comp->Compare(m_treelist, col, FromDVI(item1), FromDVI(item2));
 
975
 
 
976
    // And adjust by the sort order if necessary.
 
977
    if ( !ascending )
 
978
        result = -result;
 
979
 
 
980
    return result;
 
981
}
 
982
 
 
983
// ============================================================================
 
984
// wxTreeListCtrl implementation
 
985
// ============================================================================
 
986
 
 
987
BEGIN_EVENT_TABLE(wxTreeListCtrl, wxWindow)
 
988
    EVT_DATAVIEW_SELECTION_CHANGED(wxID_ANY, wxTreeListCtrl::OnSelectionChanged)
 
989
    EVT_DATAVIEW_ITEM_EXPANDING(wxID_ANY, wxTreeListCtrl::OnItemExpanding)
 
990
    EVT_DATAVIEW_ITEM_EXPANDED(wxID_ANY, wxTreeListCtrl::OnItemExpanded)
 
991
    EVT_DATAVIEW_ITEM_ACTIVATED(wxID_ANY, wxTreeListCtrl::OnItemActivated)
 
992
    EVT_DATAVIEW_ITEM_CONTEXT_MENU(wxID_ANY, wxTreeListCtrl::OnItemContextMenu)
 
993
    EVT_DATAVIEW_COLUMN_SORTED(wxID_ANY, wxTreeListCtrl::OnColumnSorted)
 
994
 
 
995
    EVT_SIZE(wxTreeListCtrl::OnSize)
 
996
END_EVENT_TABLE()
 
997
 
 
998
// ----------------------------------------------------------------------------
 
999
// Creation
 
1000
// ----------------------------------------------------------------------------
 
1001
 
 
1002
void wxTreeListCtrl::Init()
 
1003
{
 
1004
    m_view = NULL;
 
1005
    m_model = NULL;
 
1006
    m_comparator = NULL;
 
1007
}
 
1008
 
 
1009
bool wxTreeListCtrl::Create(wxWindow* parent,
 
1010
                            wxWindowID id,
 
1011
                            const wxPoint& pos,
 
1012
                            const wxSize& size,
 
1013
                            long style,
 
1014
                            const wxString& name)
 
1015
{
 
1016
    if ( style & wxTL_USER_3STATE )
 
1017
        style |= wxTL_3STATE;
 
1018
 
 
1019
    if ( style & wxTL_3STATE )
 
1020
        style |= wxTL_CHECKBOX;
 
1021
 
 
1022
    // Create the window itself and wxDataViewCtrl used by it.
 
1023
    if ( !wxWindow::Create(parent, id,
 
1024
                           pos, size,
 
1025
                           style, name) )
 
1026
    {
 
1027
        return false;
 
1028
    }
 
1029
 
 
1030
    m_view = new wxDataViewCtrl;
 
1031
    long styleDataView = HasFlag(wxTL_MULTIPLE) ? wxDV_MULTIPLE
 
1032
                                                : wxDV_SINGLE;
 
1033
    if ( HasFlag(wxTL_NO_HEADER) )
 
1034
        styleDataView |= wxDV_NO_HEADER;
 
1035
 
 
1036
    if ( !m_view->Create(this, wxID_ANY,
 
1037
                         wxPoint(0, 0), GetClientSize(),
 
1038
                         styleDataView) )
 
1039
    {
 
1040
        delete m_view;
 
1041
        m_view = NULL;
 
1042
 
 
1043
        return false;
 
1044
    }
 
1045
 
 
1046
 
 
1047
    // Set up the model for wxDataViewCtrl.
 
1048
    m_model = new wxTreeListModel(this);
 
1049
    m_view->AssociateModel(m_model);
 
1050
 
 
1051
    return true;
 
1052
}
 
1053
 
 
1054
wxTreeListCtrl::~wxTreeListCtrl()
 
1055
{
 
1056
    if ( m_model )
 
1057
        m_model->DecRef();
 
1058
}
 
1059
 
 
1060
wxWindowList wxTreeListCtrl::GetCompositeWindowParts() const
 
1061
{
 
1062
    wxWindowList parts;
 
1063
    parts.push_back(m_view);
 
1064
    return parts;
 
1065
}
 
1066
 
 
1067
// ----------------------------------------------------------------------------
 
1068
// Columns
 
1069
// ----------------------------------------------------------------------------
 
1070
 
 
1071
int
 
1072
wxTreeListCtrl::DoInsertColumn(const wxString& title,
 
1073
                               int pos,
 
1074
                               int width,
 
1075
                               wxAlignment align,
 
1076
                               int flags)
 
1077
{
 
1078
    wxCHECK_MSG( m_view, wxNOT_FOUND, "Must Create() first" );
 
1079
 
 
1080
    const unsigned oldNumColumns = m_view->GetColumnCount();
 
1081
 
 
1082
    if ( pos == wxNOT_FOUND )
 
1083
        pos = oldNumColumns;
 
1084
 
 
1085
    wxDataViewRenderer* renderer;
 
1086
    if ( pos == 0 )
 
1087
    {
 
1088
        // Inserting the first column which is special as it uses a different
 
1089
        // renderer.
 
1090
 
 
1091
        // Also, currently it can be done only once.
 
1092
        wxCHECK_MSG( !oldNumColumns, wxNOT_FOUND,
 
1093
                     "Inserting column at position 0 currently not supported" );
 
1094
 
 
1095
        if ( HasFlag(wxTL_CHECKBOX) )
 
1096
        {
 
1097
            // Use our custom renderer to show the checkbox.
 
1098
            renderer = new wxDataViewCheckIconTextRenderer;
 
1099
        }
 
1100
        else // We still need a special renderer to show the icons.
 
1101
        {
 
1102
            renderer = new wxDataViewIconTextRenderer;
 
1103
        }
 
1104
    }
 
1105
    else // Not the first column.
 
1106
    {
 
1107
        // All the other ones use a simple text renderer.
 
1108
        renderer = new wxDataViewTextRenderer;
 
1109
    }
 
1110
 
 
1111
    wxDataViewColumn*
 
1112
        column = new wxDataViewColumn(title, renderer, pos, width, align, flags);
 
1113
 
 
1114
    m_model->InsertColumn(pos);
 
1115
 
 
1116
    m_view->InsertColumn(pos, column);
 
1117
 
 
1118
    return pos;
 
1119
}
 
1120
 
 
1121
unsigned wxTreeListCtrl::GetColumnCount() const
 
1122
{
 
1123
    return m_view ? m_view->GetColumnCount() : 0u;
 
1124
}
 
1125
 
 
1126
bool wxTreeListCtrl::DeleteColumn(unsigned col)
 
1127
{
 
1128
    wxCHECK_MSG( col < GetColumnCount(), false, "Invalid column index" );
 
1129
 
 
1130
    if ( !m_view->DeleteColumn(m_view->GetColumn(col)) )
 
1131
        return false;
 
1132
 
 
1133
    m_model->DeleteColumn(col);
 
1134
 
 
1135
    return true;
 
1136
}
 
1137
 
 
1138
void wxTreeListCtrl::ClearColumns()
 
1139
{
 
1140
    // Don't assert here, clearing columns of the control before it's created
 
1141
    // can be considered valid (just useless).
 
1142
    if ( !m_model )
 
1143
        return;
 
1144
 
 
1145
    m_view->ClearColumns();
 
1146
 
 
1147
    m_model->ClearColumns();
 
1148
}
 
1149
 
 
1150
void wxTreeListCtrl::SetColumnWidth(unsigned col, int width)
 
1151
{
 
1152
    wxCHECK_RET( col < GetColumnCount(), "Invalid column index" );
 
1153
 
 
1154
    wxDataViewColumn* const column = m_view->GetColumn(col);
 
1155
    wxCHECK_RET( column, "No such column?" );
 
1156
 
 
1157
    column->SetWidth(width);
 
1158
}
 
1159
 
 
1160
int wxTreeListCtrl::GetColumnWidth(unsigned col) const
 
1161
{
 
1162
    wxCHECK_MSG( col < GetColumnCount(), -1, "Invalid column index" );
 
1163
 
 
1164
    wxDataViewColumn* column = m_view->GetColumn(col);
 
1165
    wxCHECK_MSG( column, -1, "No such column?" );
 
1166
 
 
1167
    return column->GetWidth();
 
1168
}
 
1169
 
 
1170
int wxTreeListCtrl::WidthFor(const wxString& text) const
 
1171
{
 
1172
    return GetTextExtent(text).x;
 
1173
}
 
1174
 
 
1175
// ----------------------------------------------------------------------------
 
1176
// Items
 
1177
// ----------------------------------------------------------------------------
 
1178
 
 
1179
wxTreeListItem
 
1180
wxTreeListCtrl::DoInsertItem(wxTreeListItem parent,
 
1181
                             wxTreeListItem previous,
 
1182
                             const wxString& text,
 
1183
                             int imageClosed,
 
1184
                             int imageOpened,
 
1185
                             wxClientData* data)
 
1186
{
 
1187
    wxCHECK_MSG( m_model, wxTreeListItem(), "Must create first" );
 
1188
 
 
1189
    return wxTreeListItem(m_model->InsertItem(parent, previous, text,
 
1190
                                              imageClosed, imageOpened, data));
 
1191
}
 
1192
 
 
1193
void wxTreeListCtrl::DeleteItem(wxTreeListItem item)
 
1194
{
 
1195
    wxCHECK_RET( m_model, "Must create first" );
 
1196
 
 
1197
    m_model->DeleteItem(item);
 
1198
}
 
1199
 
 
1200
void wxTreeListCtrl::DeleteAllItems()
 
1201
{
 
1202
    if ( m_model )
 
1203
        m_model->DeleteAllItems();
 
1204
}
 
1205
 
 
1206
// ----------------------------------------------------------------------------
 
1207
// Tree navigation
 
1208
// ----------------------------------------------------------------------------
 
1209
 
 
1210
// The simple accessors in this section are implemented directly using
 
1211
// wxTreeListModelNode methods, without passing by the model. This is just a
 
1212
// shortcut and avoids us the trouble of defining more trivial methods in
 
1213
// wxTreeListModel.
 
1214
 
 
1215
wxTreeListItem wxTreeListCtrl::GetRootItem() const
 
1216
{
 
1217
    wxCHECK_MSG( m_model, wxTreeListItem(), "Must create first" );
 
1218
 
 
1219
    return m_model->GetRootItem();
 
1220
}
 
1221
 
 
1222
wxTreeListItem wxTreeListCtrl::GetItemParent(wxTreeListItem item) const
 
1223
{
 
1224
    wxCHECK_MSG( item.IsOk(), wxTreeListItem(), "Invalid item" );
 
1225
 
 
1226
    return item->GetParent();
 
1227
}
 
1228
 
 
1229
wxTreeListItem wxTreeListCtrl::GetFirstChild(wxTreeListItem item) const
 
1230
{
 
1231
    wxCHECK_MSG( item.IsOk(), wxTreeListItem(), "Invalid item" );
 
1232
 
 
1233
    return item->GetChild();
 
1234
}
 
1235
 
 
1236
wxTreeListItem
 
1237
wxTreeListCtrl::GetNextSibling(wxTreeListItem item) const
 
1238
{
 
1239
    wxCHECK_MSG( item.IsOk(), wxTreeListItem(), "Invalid item" );
 
1240
 
 
1241
    return item->GetNext();
 
1242
}
 
1243
 
 
1244
wxTreeListItem wxTreeListCtrl::GetNextItem(wxTreeListItem item) const
 
1245
{
 
1246
    wxCHECK_MSG( item.IsOk(), wxTreeListItem(), "Invalid item" );
 
1247
 
 
1248
    return item->NextInTree();
 
1249
}
 
1250
 
 
1251
// ----------------------------------------------------------------------------
 
1252
// Item attributes
 
1253
// ----------------------------------------------------------------------------
 
1254
 
 
1255
const wxString&
 
1256
wxTreeListCtrl::GetItemText(wxTreeListItem item, unsigned col) const
 
1257
{
 
1258
    // We can't use wxCHECK_MSG() here because we don't have any empty string
 
1259
    // reference to return so we use a static variable that exists just for the
 
1260
    // purpose of this check -- and so we put it in its own scope so that it's
 
1261
    // never even created during normal program execution.
 
1262
    if ( !m_model || col >= m_model->GetColumnCount() )
 
1263
    {
 
1264
        static wxString s_empty;
 
1265
 
 
1266
        if ( !m_model )
 
1267
        {
 
1268
            wxFAIL_MSG( "Must create first" );
 
1269
        }
 
1270
        else if ( col >= m_model->GetColumnCount() )
 
1271
        {
 
1272
            wxFAIL_MSG( "Invalid column index" );
 
1273
        }
 
1274
 
 
1275
        return s_empty;
 
1276
    }
 
1277
 
 
1278
    return m_model->GetItemText(item, col);
 
1279
}
 
1280
 
 
1281
void
 
1282
wxTreeListCtrl::SetItemText(wxTreeListItem item,
 
1283
                            unsigned col,
 
1284
                            const wxString& text)
 
1285
{
 
1286
    wxCHECK_RET( m_model, "Must create first" );
 
1287
    wxCHECK_RET( col < m_model->GetColumnCount(), "Invalid column index" );
 
1288
 
 
1289
    m_model->SetItemText(item, col, text);
 
1290
}
 
1291
 
 
1292
void wxTreeListCtrl::SetItemImage(wxTreeListItem item, int closed, int opened)
 
1293
{
 
1294
    wxCHECK_RET( m_model, "Must create first" );
 
1295
 
 
1296
    if ( closed != NO_IMAGE || opened != NO_IMAGE )
 
1297
    {
 
1298
        wxImageList* const imageList = GetImageList();
 
1299
        wxCHECK_RET( imageList, "Can't set images without image list" );
 
1300
 
 
1301
        const int imageCount = imageList->GetImageCount();
 
1302
 
 
1303
        wxCHECK_RET( closed < imageCount, "Invalid image index" );
 
1304
        wxCHECK_RET( opened < imageCount, "Invalid opened image index" );
 
1305
    }
 
1306
 
 
1307
    m_model->SetItemImage(item, closed, opened);
 
1308
}
 
1309
 
 
1310
wxClientData* wxTreeListCtrl::GetItemData(wxTreeListItem item) const
 
1311
{
 
1312
    wxCHECK_MSG( m_model, NULL, "Must create first" );
 
1313
 
 
1314
    return m_model->GetItemData(item);
 
1315
}
 
1316
 
 
1317
void wxTreeListCtrl::SetItemData(wxTreeListItem item, wxClientData* data)
 
1318
{
 
1319
    wxCHECK_RET( m_model, "Must create first" );
 
1320
 
 
1321
    m_model->SetItemData(item, data);
 
1322
}
 
1323
 
 
1324
// ----------------------------------------------------------------------------
 
1325
// Expanding and collapsing
 
1326
// ----------------------------------------------------------------------------
 
1327
 
 
1328
void wxTreeListCtrl::Expand(wxTreeListItem item)
 
1329
{
 
1330
    wxCHECK_RET( m_view, "Must create first" );
 
1331
 
 
1332
    m_view->Expand(m_model->ToDVI(item));
 
1333
}
 
1334
 
 
1335
void wxTreeListCtrl::Collapse(wxTreeListItem item)
 
1336
{
 
1337
    wxCHECK_RET( m_view, "Must create first" );
 
1338
 
 
1339
    m_view->Collapse(m_model->ToDVI(item));
 
1340
}
 
1341
 
 
1342
bool wxTreeListCtrl::IsExpanded(wxTreeListItem item) const
 
1343
{
 
1344
    wxCHECK_MSG( m_view, false, "Must create first" );
 
1345
 
 
1346
    return m_view->IsExpanded(m_model->ToDVI(item));
 
1347
}
 
1348
 
 
1349
// ----------------------------------------------------------------------------
 
1350
// Selection
 
1351
// ----------------------------------------------------------------------------
 
1352
 
 
1353
wxTreeListItem wxTreeListCtrl::GetSelection() const
 
1354
{
 
1355
    wxCHECK_MSG( m_view, wxTreeListItem(), "Must create first" );
 
1356
 
 
1357
    wxCHECK_MSG( !HasFlag(wxTL_MULTIPLE), wxTreeListItem(),
 
1358
                 "Must use GetSelections() with multi-selection controls!" );
 
1359
 
 
1360
    const wxDataViewItem dvi = m_view->GetSelection();
 
1361
 
 
1362
    return m_model->FromNonRootDVI(dvi);
 
1363
}
 
1364
 
 
1365
unsigned wxTreeListCtrl::GetSelections(wxTreeListItems& selections) const
 
1366
{
 
1367
    wxCHECK_MSG( m_view, 0, "Must create first" );
 
1368
 
 
1369
    wxDataViewItemArray selectionsDV;
 
1370
    const unsigned numSelected = m_view->GetSelections(selectionsDV);
 
1371
    selections.resize(numSelected);
 
1372
    for ( unsigned n = 0; n < numSelected; n++ )
 
1373
        selections[n] = m_model->FromNonRootDVI(selectionsDV[n]);
 
1374
 
 
1375
    return numSelected;
 
1376
}
 
1377
 
 
1378
void wxTreeListCtrl::Select(wxTreeListItem item)
 
1379
{
 
1380
    wxCHECK_RET( m_view, "Must create first" );
 
1381
 
 
1382
    m_view->Select(m_model->ToNonRootDVI(item));
 
1383
}
 
1384
 
 
1385
void wxTreeListCtrl::Unselect(wxTreeListItem item)
 
1386
{
 
1387
    wxCHECK_RET( m_view, "Must create first" );
 
1388
 
 
1389
    m_view->Unselect(m_model->ToNonRootDVI(item));
 
1390
}
 
1391
 
 
1392
bool wxTreeListCtrl::IsSelected(wxTreeListItem item) const
 
1393
{
 
1394
    wxCHECK_MSG( m_view, false, "Must create first" );
 
1395
 
 
1396
    return m_view->IsSelected(m_model->ToNonRootDVI(item));
 
1397
}
 
1398
 
 
1399
void wxTreeListCtrl::SelectAll()
 
1400
{
 
1401
    wxCHECK_RET( m_view, "Must create first" );
 
1402
 
 
1403
    m_view->SelectAll();
 
1404
}
 
1405
 
 
1406
void wxTreeListCtrl::UnselectAll()
 
1407
{
 
1408
    wxCHECK_RET( m_view, "Must create first" );
 
1409
 
 
1410
    m_view->UnselectAll();
 
1411
}
 
1412
 
 
1413
// ----------------------------------------------------------------------------
 
1414
// Checkbox handling
 
1415
// ----------------------------------------------------------------------------
 
1416
 
 
1417
void wxTreeListCtrl::CheckItem(wxTreeListItem item, wxCheckBoxState state)
 
1418
{
 
1419
    wxCHECK_RET( m_model, "Must create first" );
 
1420
 
 
1421
    m_model->CheckItem(item, state);
 
1422
}
 
1423
 
 
1424
void
 
1425
wxTreeListCtrl::CheckItemRecursively(wxTreeListItem item, wxCheckBoxState state)
 
1426
{
 
1427
    wxCHECK_RET( m_model, "Must create first" );
 
1428
 
 
1429
    m_model->CheckItem(item, state);
 
1430
 
 
1431
    for ( wxTreeListItem child = GetFirstChild(item);
 
1432
          child.IsOk();
 
1433
          child = GetNextSibling(child) )
 
1434
    {
 
1435
        CheckItemRecursively(child, state);
 
1436
    }
 
1437
}
 
1438
 
 
1439
void wxTreeListCtrl::UpdateItemParentStateRecursively(wxTreeListItem item)
 
1440
{
 
1441
    wxCHECK_RET( item.IsOk(), "Invalid item" );
 
1442
 
 
1443
    wxASSERT_MSG( HasFlag(wxTL_3STATE), "Can only be used with wxTL_3STATE" );
 
1444
 
 
1445
    for ( ;; )
 
1446
    {
 
1447
        wxTreeListItem parent = GetItemParent(item);
 
1448
        if ( parent == GetRootItem() )
 
1449
        {
 
1450
            // There is no checked state associated with the root item.
 
1451
            return;
 
1452
        }
 
1453
 
 
1454
        // Set parent state to the state of this item if all the other children
 
1455
        // have the same state too. Otherwise make it indeterminate.
 
1456
        const wxCheckBoxState stateItem = GetCheckedState(item);
 
1457
        CheckItem(parent, AreAllChildrenInState(parent, stateItem)
 
1458
                            ? stateItem
 
1459
                            : wxCHK_UNDETERMINED);
 
1460
 
 
1461
        // And do the same thing with the parent's parent too.
 
1462
        item = parent;
 
1463
    }
 
1464
}
 
1465
 
 
1466
wxCheckBoxState wxTreeListCtrl::GetCheckedState(wxTreeListItem item) const
 
1467
{
 
1468
    wxCHECK_MSG( item.IsOk(), wxCHK_UNDETERMINED, "Invalid item" );
 
1469
 
 
1470
    return item->m_checkedState;
 
1471
}
 
1472
 
 
1473
bool
 
1474
wxTreeListCtrl::AreAllChildrenInState(wxTreeListItem item,
 
1475
                                      wxCheckBoxState state) const
 
1476
{
 
1477
    wxCHECK_MSG( item.IsOk(), false, "Invalid item" );
 
1478
 
 
1479
    for ( wxTreeListItem child = GetFirstChild(item);
 
1480
          child.IsOk();
 
1481
          child = GetNextSibling(child) )
 
1482
    {
 
1483
        if ( GetCheckedState(child) != state )
 
1484
            return false;
 
1485
    }
 
1486
 
 
1487
    return true;
 
1488
}
 
1489
 
 
1490
// ----------------------------------------------------------------------------
 
1491
// Sorting
 
1492
// ----------------------------------------------------------------------------
 
1493
 
 
1494
void wxTreeListCtrl::SetSortColumn(unsigned col, bool ascendingOrder)
 
1495
{
 
1496
    wxCHECK_RET( col < m_view->GetColumnCount(), "Invalid column index" );
 
1497
 
 
1498
    m_view->GetColumn(col)->SetSortOrder(ascendingOrder);
 
1499
}
 
1500
 
 
1501
bool wxTreeListCtrl::GetSortColumn(unsigned* col, bool* ascendingOrder)
 
1502
{
 
1503
    const unsigned numColumns = m_view->GetColumnCount();
 
1504
    for ( unsigned n = 0; n < numColumns; n++ )
 
1505
    {
 
1506
        wxDataViewColumn* const column = m_view->GetColumn(n);
 
1507
        if ( column->IsSortKey() )
 
1508
        {
 
1509
            if ( col )
 
1510
                *col = n;
 
1511
 
 
1512
            if ( ascendingOrder )
 
1513
                *ascendingOrder = column->IsSortOrderAscending();
 
1514
 
 
1515
            return true;
 
1516
        }
 
1517
    }
 
1518
 
 
1519
    return false;
 
1520
}
 
1521
 
 
1522
void wxTreeListCtrl::SetItemComparator(wxTreeListItemComparator* comparator)
 
1523
{
 
1524
    m_comparator = comparator;
 
1525
}
 
1526
 
 
1527
// ----------------------------------------------------------------------------
 
1528
// Events
 
1529
// ----------------------------------------------------------------------------
 
1530
 
 
1531
void wxTreeListCtrl::SendItemEvent(wxEventType evt, wxDataViewEvent& eventDV)
 
1532
{
 
1533
    wxTreeListEvent eventTL(evt, this, m_model->FromDVI(eventDV.GetItem()));
 
1534
 
 
1535
    if ( !ProcessWindowEvent(eventTL) )
 
1536
    {
 
1537
        eventDV.Skip();
 
1538
        return;
 
1539
    }
 
1540
 
 
1541
    if ( !eventTL.IsAllowed() )
 
1542
    {
 
1543
        eventDV.Veto();
 
1544
    }
 
1545
}
 
1546
 
 
1547
void wxTreeListCtrl::SendColumnEvent(wxEventType evt, wxDataViewEvent& eventDV)
 
1548
{
 
1549
    wxTreeListEvent eventTL(evt, this, wxTreeListItem());
 
1550
    eventTL.SetColumn(eventDV.GetColumn());
 
1551
 
 
1552
    if ( !ProcessWindowEvent(eventTL) )
 
1553
    {
 
1554
        eventDV.Skip();
 
1555
        return;
 
1556
    }
 
1557
 
 
1558
    if ( !eventTL.IsAllowed() )
 
1559
    {
 
1560
        eventDV.Veto();
 
1561
    }
 
1562
}
 
1563
 
 
1564
void
 
1565
wxTreeListCtrl::OnItemToggled(wxTreeListItem item, wxCheckBoxState stateOld)
 
1566
{
 
1567
    wxTreeListEvent event(wxEVT_TREELIST_ITEM_CHECKED, this, item);
 
1568
    event.SetOldCheckedState(stateOld);
 
1569
 
 
1570
    ProcessWindowEvent(event);
 
1571
}
 
1572
 
 
1573
void wxTreeListCtrl::OnSelectionChanged(wxDataViewEvent& event)
 
1574
{
 
1575
    SendItemEvent(wxEVT_TREELIST_SELECTION_CHANGED, event);
 
1576
}
 
1577
 
 
1578
void wxTreeListCtrl::OnItemExpanding(wxDataViewEvent& event)
 
1579
{
 
1580
    SendItemEvent(wxEVT_TREELIST_ITEM_EXPANDING, event);
 
1581
}
 
1582
 
 
1583
void wxTreeListCtrl::OnItemExpanded(wxDataViewEvent& event)
 
1584
{
 
1585
    SendItemEvent(wxEVT_TREELIST_ITEM_EXPANDED, event);
 
1586
}
 
1587
 
 
1588
void wxTreeListCtrl::OnItemActivated(wxDataViewEvent& event)
 
1589
{
 
1590
    SendItemEvent(wxEVT_TREELIST_ITEM_ACTIVATED, event);
 
1591
}
 
1592
 
 
1593
void wxTreeListCtrl::OnItemContextMenu(wxDataViewEvent& event)
 
1594
{
 
1595
    SendItemEvent(wxEVT_TREELIST_ITEM_CONTEXT_MENU, event);
 
1596
}
 
1597
 
 
1598
void wxTreeListCtrl::OnColumnSorted(wxDataViewEvent& event)
 
1599
{
 
1600
    SendColumnEvent(wxEVT_TREELIST_COLUMN_SORTED, event);
 
1601
}
 
1602
 
 
1603
// ----------------------------------------------------------------------------
 
1604
// Geometry
 
1605
// ----------------------------------------------------------------------------
 
1606
 
 
1607
void wxTreeListCtrl::OnSize(wxSizeEvent& event)
 
1608
{
 
1609
    event.Skip();
 
1610
 
 
1611
    if ( m_view )
 
1612
    {
 
1613
        // Resize the real control to cover our entire client area.
 
1614
        const wxRect rect = GetClientRect();
 
1615
        m_view->SetSize(rect);
 
1616
 
 
1617
#ifdef wxHAS_GENERIC_DATAVIEWCTRL
 
1618
        // The generic implementation doesn't refresh itself immediately which
 
1619
        // is annoying during "live resizing", so do it forcefully here to
 
1620
        // ensure that the items are re-laid out and the focus rectangle is
 
1621
        // redrawn correctly (instead of leaving traces) while our size is
 
1622
        // being changed.
 
1623
        wxWindow* const view = GetView();
 
1624
        view->Refresh();
 
1625
        view->Update();
 
1626
#endif // wxHAS_GENERIC_DATAVIEWCTRL
 
1627
 
 
1628
        // Resize the first column to take the remaining available space.
 
1629
        const unsigned numColumns = GetColumnCount();
 
1630
        if ( !numColumns )
 
1631
            return;
 
1632
 
 
1633
        // There is a bug in generic wxDataViewCtrl: if the column width sums
 
1634
        // up to the total size, horizontal scrollbar (unnecessarily) appears,
 
1635
        // so subtract a bit to ensure this doesn't happen.
 
1636
        int remainingWidth = rect.width - 5;
 
1637
        for ( unsigned n = 1; n < GetColumnCount(); n++ )
 
1638
        {
 
1639
            remainingWidth -= GetColumnWidth(n);
 
1640
            if ( remainingWidth <= 0 )
 
1641
            {
 
1642
                // There is not enough space, as we're not going to give the
 
1643
                // first column negative width anyhow, just don't do anything.
 
1644
                return;
 
1645
            }
 
1646
        }
 
1647
 
 
1648
        SetColumnWidth(0, remainingWidth);
 
1649
    }
 
1650
}
 
1651
 
 
1652
wxWindow* wxTreeListCtrl::GetView() const
 
1653
{
 
1654
#ifdef wxHAS_GENERIC_DATAVIEWCTRL
 
1655
    return m_view->GetMainWindow();
 
1656
#else
 
1657
    return m_view;
 
1658
#endif
 
1659
}
 
1660
 
 
1661
// ============================================================================
 
1662
// wxTreeListEvent implementation
 
1663
// ============================================================================
 
1664
 
 
1665
wxIMPLEMENT_DYNAMIC_CLASS(wxTreeListEvent, wxNotifyEvent)
 
1666
 
 
1667
#define wxDEFINE_TREELIST_EVENT(name) \
 
1668
    wxDEFINE_EVENT(wxEVT_TREELIST_##name, wxTreeListEvent)
 
1669
 
 
1670
wxDEFINE_TREELIST_EVENT(SELECTION_CHANGED);
 
1671
wxDEFINE_TREELIST_EVENT(ITEM_EXPANDING);
 
1672
wxDEFINE_TREELIST_EVENT(ITEM_EXPANDED);
 
1673
wxDEFINE_TREELIST_EVENT(ITEM_CHECKED);
 
1674
wxDEFINE_TREELIST_EVENT(ITEM_ACTIVATED);
 
1675
wxDEFINE_TREELIST_EVENT(ITEM_CONTEXT_MENU);
 
1676
wxDEFINE_TREELIST_EVENT(COLUMN_SORTED);
 
1677
 
 
1678
#undef wxDEFINE_TREELIST_EVENT
 
1679
 
 
1680
#endif // wxUSE_TREELISTCTRL