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

« back to all changes in this revision

Viewing changes to src/generic/treectlg.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/generic/treectlg.cpp
 
3
// Purpose:     generic tree control implementation
 
4
// Author:      Robert Roebling
 
5
// Created:     01/02/97
 
6
// Modified:    22/10/98 - almost total rewrite, simpler interface (VZ)
 
7
// Id:          $Id: treectlg.cpp 70625 2012-02-19 14:49:37Z SN $
 
8
// Copyright:   (c) 1998 Robert Roebling and Julian Smart
 
9
// Licence:     wxWindows licence
 
10
/////////////////////////////////////////////////////////////////////////////
 
11
 
 
12
// =============================================================================
 
13
// declarations
 
14
// =============================================================================
 
15
 
 
16
// -----------------------------------------------------------------------------
 
17
// headers
 
18
// -----------------------------------------------------------------------------
 
19
 
 
20
// For compilers that support precompilation, includes "wx.h".
 
21
#include "wx/wxprec.h"
 
22
 
 
23
#ifdef __BORLANDC__
 
24
    #pragma hdrstop
 
25
#endif
 
26
 
 
27
#if wxUSE_TREECTRL
 
28
 
 
29
#include "wx/treectrl.h"
 
30
 
 
31
#ifndef WX_PRECOMP
 
32
    #include "wx/dcclient.h"
 
33
    #include "wx/timer.h"
 
34
    #include "wx/settings.h"
 
35
    #include "wx/listbox.h"
 
36
    #include "wx/textctrl.h"
 
37
#endif
 
38
 
 
39
#include "wx/generic/treectlg.h"
 
40
#include "wx/imaglist.h"
 
41
 
 
42
#include "wx/renderer.h"
 
43
 
 
44
#ifdef __WXMAC__
 
45
    #include "wx/osx/private.h"
 
46
#endif
 
47
 
 
48
// -----------------------------------------------------------------------------
 
49
// array types
 
50
// -----------------------------------------------------------------------------
 
51
 
 
52
class WXDLLIMPEXP_FWD_CORE wxGenericTreeItem;
 
53
 
 
54
WX_DEFINE_ARRAY_PTR(wxGenericTreeItem *, wxArrayGenericTreeItems);
 
55
 
 
56
// ----------------------------------------------------------------------------
 
57
// constants
 
58
// ----------------------------------------------------------------------------
 
59
 
 
60
static const int NO_IMAGE = -1;
 
61
 
 
62
static const int PIXELS_PER_UNIT = 10;
 
63
 
 
64
// the margin between the item state image and the item normal image
 
65
static const int MARGIN_BETWEEN_STATE_AND_IMAGE = 2;
 
66
 
 
67
// the margin between the item image and the item text
 
68
static const int MARGIN_BETWEEN_IMAGE_AND_TEXT = 4;
 
69
 
 
70
// -----------------------------------------------------------------------------
 
71
// private classes
 
72
// -----------------------------------------------------------------------------
 
73
 
 
74
// timer used for enabling in-place edit
 
75
class WXDLLEXPORT wxTreeRenameTimer: public wxTimer
 
76
{
 
77
public:
 
78
    // start editing the current item after half a second (if the mouse hasn't
 
79
    // been clicked/moved)
 
80
    enum { DELAY = 500 };
 
81
 
 
82
    wxTreeRenameTimer( wxGenericTreeCtrl *owner );
 
83
 
 
84
    virtual void Notify();
 
85
 
 
86
private:
 
87
    wxGenericTreeCtrl *m_owner;
 
88
 
 
89
    wxDECLARE_NO_COPY_CLASS(wxTreeRenameTimer);
 
90
};
 
91
 
 
92
// control used for in-place edit
 
93
class WXDLLEXPORT wxTreeTextCtrl: public wxTextCtrl
 
94
{
 
95
public:
 
96
    wxTreeTextCtrl(wxGenericTreeCtrl *owner, wxGenericTreeItem *item);
 
97
 
 
98
    void EndEdit( bool discardChanges );
 
99
 
 
100
    const wxGenericTreeItem* item() const { return m_itemEdited; }
 
101
 
 
102
protected:
 
103
    void OnChar( wxKeyEvent &event );
 
104
    void OnKeyUp( wxKeyEvent &event );
 
105
    void OnKillFocus( wxFocusEvent &event );
 
106
 
 
107
    bool AcceptChanges();
 
108
    void Finish( bool setfocus );
 
109
 
 
110
private:
 
111
    wxGenericTreeCtrl  *m_owner;
 
112
    wxGenericTreeItem  *m_itemEdited;
 
113
    wxString            m_startValue;
 
114
    bool                m_aboutToFinish;
 
115
 
 
116
    DECLARE_EVENT_TABLE()
 
117
    wxDECLARE_NO_COPY_CLASS(wxTreeTextCtrl);
 
118
};
 
119
 
 
120
// timer used to clear wxGenericTreeCtrl::m_findPrefix if no key was pressed
 
121
// for a sufficiently long time
 
122
class WXDLLEXPORT wxTreeFindTimer : public wxTimer
 
123
{
 
124
public:
 
125
    // reset the current prefix after half a second of inactivity
 
126
    enum { DELAY = 500 };
 
127
 
 
128
    wxTreeFindTimer( wxGenericTreeCtrl *owner ) { m_owner = owner; }
 
129
 
 
130
    virtual void Notify() { m_owner->m_findPrefix.clear(); }
 
131
 
 
132
private:
 
133
    wxGenericTreeCtrl *m_owner;
 
134
 
 
135
    wxDECLARE_NO_COPY_CLASS(wxTreeFindTimer);
 
136
};
 
137
 
 
138
// a tree item
 
139
class WXDLLEXPORT wxGenericTreeItem
 
140
{
 
141
public:
 
142
    // ctors & dtor
 
143
    wxGenericTreeItem()
 
144
    {
 
145
        m_data = NULL;
 
146
        m_widthText =
 
147
        m_heightText = -1;
 
148
    }
 
149
 
 
150
    wxGenericTreeItem( wxGenericTreeItem *parent,
 
151
                       const wxString& text,
 
152
                       int image,
 
153
                       int selImage,
 
154
                       wxTreeItemData *data );
 
155
 
 
156
    ~wxGenericTreeItem();
 
157
 
 
158
    // trivial accessors
 
159
    wxArrayGenericTreeItems& GetChildren() { return m_children; }
 
160
 
 
161
    const wxString& GetText() const { return m_text; }
 
162
    int GetImage(wxTreeItemIcon which = wxTreeItemIcon_Normal) const
 
163
        { return m_images[which]; }
 
164
    wxTreeItemData *GetData() const { return m_data; }
 
165
    int GetState() const { return m_state; }
 
166
 
 
167
    // returns the current image for the item (depending on its
 
168
    // selected/expanded/whatever state)
 
169
    int GetCurrentImage() const;
 
170
 
 
171
    void SetText(const wxString& text)
 
172
    {
 
173
        m_text = text;
 
174
 
 
175
        ResetTextSize();
 
176
    }
 
177
 
 
178
    void SetImage(int image, wxTreeItemIcon which)
 
179
    {
 
180
        m_images[which] = image;
 
181
        m_width = 0;
 
182
    }
 
183
 
 
184
    void SetData(wxTreeItemData *data) { m_data = data; }
 
185
    void SetState(int state) { m_state = state; m_width = 0; }
 
186
 
 
187
    void SetHasPlus(bool has = true) { m_hasPlus = has; }
 
188
 
 
189
    void SetBold(bool bold)
 
190
    {
 
191
        m_isBold = bold;
 
192
 
 
193
        ResetTextSize();
 
194
    }
 
195
 
 
196
    int GetX() const { return m_x; }
 
197
    int GetY() const { return m_y; }
 
198
 
 
199
    void SetX(int x) { m_x = x; }
 
200
    void SetY(int y) { m_y = y; }
 
201
 
 
202
    int GetHeight() const { return m_height; }
 
203
    int GetWidth() const { return m_width; }
 
204
 
 
205
    int GetTextHeight() const
 
206
    {
 
207
        wxASSERT_MSG( m_heightText != -1, "must call CalculateSize() first" );
 
208
 
 
209
        return m_heightText;
 
210
    }
 
211
 
 
212
    int GetTextWidth() const
 
213
    {
 
214
        wxASSERT_MSG( m_widthText != -1, "must call CalculateSize() first" );
 
215
 
 
216
        return m_widthText;
 
217
    }
 
218
 
 
219
    wxGenericTreeItem *GetParent() const { return m_parent; }
 
220
 
 
221
    // sets the items font for the specified DC if it uses any special font or
 
222
    // simply returns false otherwise
 
223
    bool SetFont(wxGenericTreeCtrl *control, wxDC& dc) const
 
224
    {
 
225
        wxFont font;
 
226
 
 
227
        wxTreeItemAttr * const attr = GetAttributes();
 
228
        if ( attr && attr->HasFont() )
 
229
            font = attr->GetFont();
 
230
        else if ( IsBold() )
 
231
            font = control->m_boldFont;
 
232
        else
 
233
            return false;
 
234
 
 
235
        dc.SetFont(font);
 
236
        return true;
 
237
    }
 
238
 
 
239
    // operations
 
240
 
 
241
    // deletes all children notifying the treectrl about it
 
242
    void DeleteChildren(wxGenericTreeCtrl *tree);
 
243
 
 
244
    // get count of all children (and grand children if 'recursively')
 
245
    size_t GetChildrenCount(bool recursively = true) const;
 
246
 
 
247
    void Insert(wxGenericTreeItem *child, size_t index)
 
248
        { m_children.Insert(child, index); }
 
249
 
 
250
    // calculate and cache the item size using either the provided DC (which is
 
251
    // supposed to have wxGenericTreeCtrl::m_normalFont selected into it!) or a
 
252
    // wxClientDC on the control window
 
253
    void CalculateSize(wxGenericTreeCtrl *control, wxDC& dc)
 
254
        { DoCalculateSize(control, dc, true /* dc uses normal font */); }
 
255
    void CalculateSize(wxGenericTreeCtrl *control);
 
256
 
 
257
    void GetSize( int &x, int &y, const wxGenericTreeCtrl* );
 
258
 
 
259
    void ResetSize() { m_width = 0; }
 
260
    void ResetTextSize() { m_width = 0; m_widthText = -1; }
 
261
    void RecursiveResetSize();
 
262
    void RecursiveResetTextSize();
 
263
 
 
264
        // return the item at given position (or NULL if no item), onButton is
 
265
        // true if the point belongs to the item's button, otherwise it lies
 
266
        // on the item's label
 
267
    wxGenericTreeItem *HitTest( const wxPoint& point,
 
268
                                const wxGenericTreeCtrl *,
 
269
                                int &flags,
 
270
                                int level );
 
271
 
 
272
    void Expand() { m_isCollapsed = false; }
 
273
    void Collapse() { m_isCollapsed = true; }
 
274
 
 
275
    void SetHilight( bool set = true ) { m_hasHilight = set; }
 
276
 
 
277
    // status inquiries
 
278
    bool HasChildren() const { return !m_children.IsEmpty(); }
 
279
    bool IsSelected()  const { return m_hasHilight != 0; }
 
280
    bool IsExpanded()  const { return !m_isCollapsed; }
 
281
    bool HasPlus()     const { return m_hasPlus || HasChildren(); }
 
282
    bool IsBold()      const { return m_isBold != 0; }
 
283
 
 
284
    // attributes
 
285
        // get them - may be NULL
 
286
    wxTreeItemAttr *GetAttributes() const { return m_attr; }
 
287
        // get them ensuring that the pointer is not NULL
 
288
    wxTreeItemAttr& Attr()
 
289
    {
 
290
        if ( !m_attr )
 
291
        {
 
292
            m_attr = new wxTreeItemAttr;
 
293
            m_ownsAttr = true;
 
294
        }
 
295
        return *m_attr;
 
296
    }
 
297
        // set them
 
298
    void SetAttributes(wxTreeItemAttr *attr)
 
299
    {
 
300
        if ( m_ownsAttr ) delete m_attr;
 
301
        m_attr = attr;
 
302
        m_ownsAttr = false;
 
303
        m_width = 0;
 
304
        m_widthText = -1;
 
305
    }
 
306
        // set them and delete when done
 
307
    void AssignAttributes(wxTreeItemAttr *attr)
 
308
    {
 
309
        SetAttributes(attr);
 
310
        m_ownsAttr = true;
 
311
        m_width = 0;
 
312
        m_widthText = -1;
 
313
    }
 
314
 
 
315
private:
 
316
    // calculate the size of this item, i.e. set m_width, m_height and
 
317
    // m_widthText and m_heightText properly
 
318
    //
 
319
    // if dcUsesNormalFont is true, the current dc font must be the normal tree
 
320
    // control font
 
321
    void DoCalculateSize(wxGenericTreeCtrl *control,
 
322
                         wxDC& dc,
 
323
                         bool dcUsesNormalFont);
 
324
 
 
325
    // since there can be very many of these, we save size by chosing
 
326
    // the smallest representation for the elements and by ordering
 
327
    // the members to avoid padding.
 
328
    wxString            m_text;         // label to be rendered for item
 
329
    int                 m_widthText;
 
330
    int                 m_heightText;
 
331
 
 
332
    wxTreeItemData     *m_data;         // user-provided data
 
333
 
 
334
    int                 m_state;        // item state
 
335
 
 
336
    wxArrayGenericTreeItems m_children; // list of children
 
337
    wxGenericTreeItem  *m_parent;       // parent of this item
 
338
 
 
339
    wxTreeItemAttr     *m_attr;         // attributes???
 
340
 
 
341
    // tree ctrl images for the normal, selected, expanded and
 
342
    // expanded+selected states
 
343
    int                 m_images[wxTreeItemIcon_Max];
 
344
 
 
345
    wxCoord             m_x;            // (virtual) offset from top
 
346
    wxCoord             m_y;            // (virtual) offset from left
 
347
    int                 m_width;        // width of this item
 
348
    int                 m_height;       // height of this item
 
349
 
 
350
    // use bitfields to save size
 
351
    unsigned int        m_isCollapsed :1;
 
352
    unsigned int        m_hasHilight  :1; // same as focused
 
353
    unsigned int        m_hasPlus     :1; // used for item which doesn't have
 
354
                                          // children but has a [+] button
 
355
    unsigned int        m_isBold      :1; // render the label in bold font
 
356
    unsigned int        m_ownsAttr    :1; // delete attribute when done
 
357
 
 
358
    wxDECLARE_NO_COPY_CLASS(wxGenericTreeItem);
 
359
};
 
360
 
 
361
// =============================================================================
 
362
// implementation
 
363
// =============================================================================
 
364
 
 
365
// ----------------------------------------------------------------------------
 
366
// private functions
 
367
// ----------------------------------------------------------------------------
 
368
 
 
369
// translate the key or mouse event flags to the type of selection we're
 
370
// dealing with
 
371
static void EventFlagsToSelType(long style,
 
372
                                bool shiftDown,
 
373
                                bool ctrlDown,
 
374
                                bool &is_multiple,
 
375
                                bool &extended_select,
 
376
                                bool &unselect_others)
 
377
{
 
378
    is_multiple = (style & wxTR_MULTIPLE) != 0;
 
379
    extended_select = shiftDown && is_multiple;
 
380
    unselect_others = !(extended_select || (ctrlDown && is_multiple));
 
381
}
 
382
 
 
383
// check if the given item is under another one
 
384
static bool
 
385
IsDescendantOf(const wxGenericTreeItem *parent, const wxGenericTreeItem *item)
 
386
{
 
387
    while ( item )
 
388
    {
 
389
        if ( item == parent )
 
390
        {
 
391
            // item is a descendant of parent
 
392
            return true;
 
393
        }
 
394
 
 
395
        item = item->GetParent();
 
396
    }
 
397
 
 
398
    return false;
 
399
}
 
400
 
 
401
// -----------------------------------------------------------------------------
 
402
// wxTreeRenameTimer (internal)
 
403
// -----------------------------------------------------------------------------
 
404
 
 
405
wxTreeRenameTimer::wxTreeRenameTimer( wxGenericTreeCtrl *owner )
 
406
{
 
407
    m_owner = owner;
 
408
}
 
409
 
 
410
void wxTreeRenameTimer::Notify()
 
411
{
 
412
    m_owner->OnRenameTimer();
 
413
}
 
414
 
 
415
//-----------------------------------------------------------------------------
 
416
// wxTreeTextCtrl (internal)
 
417
//-----------------------------------------------------------------------------
 
418
 
 
419
BEGIN_EVENT_TABLE(wxTreeTextCtrl,wxTextCtrl)
 
420
    EVT_CHAR           (wxTreeTextCtrl::OnChar)
 
421
    EVT_KEY_UP         (wxTreeTextCtrl::OnKeyUp)
 
422
    EVT_KILL_FOCUS     (wxTreeTextCtrl::OnKillFocus)
 
423
END_EVENT_TABLE()
 
424
 
 
425
wxTreeTextCtrl::wxTreeTextCtrl(wxGenericTreeCtrl *owner,
 
426
                               wxGenericTreeItem *item)
 
427
              : m_itemEdited(item), m_startValue(item->GetText())
 
428
{
 
429
    m_owner = owner;
 
430
    m_aboutToFinish = false;
 
431
 
 
432
    wxRect rect;
 
433
    m_owner->GetBoundingRect(m_itemEdited, rect, true);
 
434
 
 
435
    // corrects position and size for better appearance
 
436
#ifdef __WXMSW__
 
437
    rect.x -= 5;
 
438
    rect.width += 10;
 
439
#elif defined(__WXGTK__)
 
440
    rect.x -= 5;
 
441
    rect.y -= 2;
 
442
    rect.width  += 8;
 
443
    rect.height += 4;
 
444
#elif defined(wxOSX_USE_CARBON) && wxOSX_USE_CARBON
 
445
    int bestHeight = GetBestSize().y - 8;
 
446
    if ( rect.height > bestHeight )
 
447
    {
 
448
        int diff = rect.height - bestHeight;
 
449
        rect.height -= diff;
 
450
        rect.y += diff / 2;
 
451
    }
 
452
#endif // platforms
 
453
 
 
454
    (void)Create(m_owner, wxID_ANY, m_startValue,
 
455
                 rect.GetPosition(), rect.GetSize());
 
456
 
 
457
    SetSelection(-1, -1);
 
458
}
 
459
 
 
460
void wxTreeTextCtrl::EndEdit(bool discardChanges)
 
461
{
 
462
    m_aboutToFinish = true;
 
463
 
 
464
    if ( discardChanges )
 
465
    {
 
466
        m_owner->OnRenameCancelled(m_itemEdited);
 
467
 
 
468
        Finish( true );
 
469
    }
 
470
    else
 
471
    {
 
472
        // Notify the owner about the changes
 
473
        AcceptChanges();
 
474
 
 
475
        // Even if vetoed, close the control (consistent with MSW)
 
476
        Finish( true );
 
477
    }
 
478
}
 
479
 
 
480
bool wxTreeTextCtrl::AcceptChanges()
 
481
{
 
482
    const wxString value = GetValue();
 
483
 
 
484
    if ( value == m_startValue )
 
485
    {
 
486
        // nothing changed, always accept
 
487
        // when an item remains unchanged, the owner
 
488
        // needs to be notified that the user decided
 
489
        // not to change the tree item label, and that
 
490
        // the edit has been cancelled
 
491
 
 
492
        m_owner->OnRenameCancelled(m_itemEdited);
 
493
        return true;
 
494
    }
 
495
 
 
496
    if ( !m_owner->OnRenameAccept(m_itemEdited, value) )
 
497
    {
 
498
        // vetoed by the user
 
499
        return false;
 
500
    }
 
501
 
 
502
    // accepted, do rename the item
 
503
    m_owner->SetItemText(m_itemEdited, value);
 
504
 
 
505
    return true;
 
506
}
 
507
 
 
508
void wxTreeTextCtrl::Finish( bool setfocus )
 
509
{
 
510
    m_owner->ResetTextControl();
 
511
 
 
512
    wxPendingDelete.Append(this);
 
513
 
 
514
    if (setfocus)
 
515
        m_owner->SetFocus();
 
516
}
 
517
 
 
518
void wxTreeTextCtrl::OnChar( wxKeyEvent &event )
 
519
{
 
520
    switch ( event.m_keyCode )
 
521
    {
 
522
        case WXK_RETURN:
 
523
            EndEdit( false );
 
524
            break;
 
525
 
 
526
        case WXK_ESCAPE:
 
527
            EndEdit( true );
 
528
            break;
 
529
 
 
530
        default:
 
531
            event.Skip();
 
532
    }
 
533
}
 
534
 
 
535
void wxTreeTextCtrl::OnKeyUp( wxKeyEvent &event )
 
536
{
 
537
    if ( !m_aboutToFinish )
 
538
    {
 
539
        // auto-grow the textctrl:
 
540
        wxSize parentSize = m_owner->GetSize();
 
541
        wxPoint myPos = GetPosition();
 
542
        wxSize mySize = GetSize();
 
543
        int sx, sy;
 
544
        GetTextExtent(GetValue() + wxT("M"), &sx, &sy);
 
545
        if (myPos.x + sx > parentSize.x)
 
546
            sx = parentSize.x - myPos.x;
 
547
        if (mySize.x > sx)
 
548
            sx = mySize.x;
 
549
        SetSize(sx, wxDefaultCoord);
 
550
    }
 
551
 
 
552
    event.Skip();
 
553
}
 
554
 
 
555
void wxTreeTextCtrl::OnKillFocus( wxFocusEvent &event )
 
556
{
 
557
    if ( !m_aboutToFinish )
 
558
    {
 
559
        if ( !AcceptChanges() )
 
560
            m_owner->OnRenameCancelled( m_itemEdited );
 
561
 
 
562
        Finish( false );
 
563
    }
 
564
 
 
565
    // We should let the native text control handle focus, too.
 
566
    event.Skip();
 
567
}
 
568
 
 
569
// -----------------------------------------------------------------------------
 
570
// wxGenericTreeItem
 
571
// -----------------------------------------------------------------------------
 
572
 
 
573
wxGenericTreeItem::wxGenericTreeItem(wxGenericTreeItem *parent,
 
574
                                     const wxString& text,
 
575
                                     int image, int selImage,
 
576
                                     wxTreeItemData *data)
 
577
                 : m_text(text)
 
578
{
 
579
    m_images[wxTreeItemIcon_Normal] = image;
 
580
    m_images[wxTreeItemIcon_Selected] = selImage;
 
581
    m_images[wxTreeItemIcon_Expanded] = NO_IMAGE;
 
582
    m_images[wxTreeItemIcon_SelectedExpanded] = NO_IMAGE;
 
583
 
 
584
    m_data = data;
 
585
    m_state = wxTREE_ITEMSTATE_NONE;
 
586
    m_x = m_y = 0;
 
587
 
 
588
    m_isCollapsed = true;
 
589
    m_hasHilight = false;
 
590
    m_hasPlus = false;
 
591
    m_isBold = false;
 
592
 
 
593
    m_parent = parent;
 
594
 
 
595
    m_attr = NULL;
 
596
    m_ownsAttr = false;
 
597
 
 
598
    // We don't know the height here yet.
 
599
    m_width = 0;
 
600
    m_height = 0;
 
601
 
 
602
    m_widthText = -1;
 
603
    m_heightText = -1;
 
604
}
 
605
 
 
606
wxGenericTreeItem::~wxGenericTreeItem()
 
607
{
 
608
    delete m_data;
 
609
 
 
610
    if (m_ownsAttr) delete m_attr;
 
611
 
 
612
    wxASSERT_MSG( m_children.IsEmpty(),
 
613
                  "must call DeleteChildren() before deleting the item" );
 
614
}
 
615
 
 
616
void wxGenericTreeItem::DeleteChildren(wxGenericTreeCtrl *tree)
 
617
{
 
618
    size_t count = m_children.GetCount();
 
619
    for ( size_t n = 0; n < count; n++ )
 
620
    {
 
621
        wxGenericTreeItem *child = m_children[n];
 
622
        tree->SendDeleteEvent(child);
 
623
 
 
624
        child->DeleteChildren(tree);
 
625
        if ( child == tree->m_select_me )
 
626
            tree->m_select_me = NULL;
 
627
        delete child;
 
628
    }
 
629
 
 
630
    m_children.Empty();
 
631
}
 
632
 
 
633
size_t wxGenericTreeItem::GetChildrenCount(bool recursively) const
 
634
{
 
635
    size_t count = m_children.GetCount();
 
636
    if ( !recursively )
 
637
        return count;
 
638
 
 
639
    size_t total = count;
 
640
    for (size_t n = 0; n < count; ++n)
 
641
    {
 
642
        total += m_children[n]->GetChildrenCount();
 
643
    }
 
644
 
 
645
    return total;
 
646
}
 
647
 
 
648
void wxGenericTreeItem::GetSize( int &x, int &y,
 
649
                                 const wxGenericTreeCtrl *theButton )
 
650
{
 
651
    int bottomY=m_y+theButton->GetLineHeight(this);
 
652
    if ( y < bottomY ) y = bottomY;
 
653
    int width = m_x +  m_width;
 
654
    if ( x < width ) x = width;
 
655
 
 
656
    if (IsExpanded())
 
657
    {
 
658
        size_t count = m_children.GetCount();
 
659
        for ( size_t n = 0; n < count; ++n )
 
660
        {
 
661
            m_children[n]->GetSize( x, y, theButton );
 
662
        }
 
663
    }
 
664
}
 
665
 
 
666
wxGenericTreeItem *wxGenericTreeItem::HitTest(const wxPoint& point,
 
667
                                              const wxGenericTreeCtrl *theCtrl,
 
668
                                              int &flags,
 
669
                                              int level)
 
670
{
 
671
    // for a hidden root node, don't evaluate it, but do evaluate children
 
672
    if ( !(level == 0 && theCtrl->HasFlag(wxTR_HIDE_ROOT)) )
 
673
    {
 
674
        // evaluate the item
 
675
        int h = theCtrl->GetLineHeight(this);
 
676
        if ((point.y > m_y) && (point.y < m_y + h))
 
677
        {
 
678
            int y_mid = m_y + h/2;
 
679
            if (point.y < y_mid )
 
680
                flags |= wxTREE_HITTEST_ONITEMUPPERPART;
 
681
            else
 
682
                flags |= wxTREE_HITTEST_ONITEMLOWERPART;
 
683
 
 
684
            int xCross = m_x - theCtrl->GetSpacing();
 
685
#ifdef __WXMAC__
 
686
            // according to the drawing code the triangels are drawn
 
687
            // at -4 , -4  from the position up to +10/+10 max
 
688
            if ((point.x > xCross-4) && (point.x < xCross+10) &&
 
689
                (point.y > y_mid-4) && (point.y < y_mid+10) &&
 
690
                HasPlus() && theCtrl->HasButtons() )
 
691
#else
 
692
            // 5 is the size of the plus sign
 
693
            if ((point.x > xCross-6) && (point.x < xCross+6) &&
 
694
                (point.y > y_mid-6) && (point.y < y_mid+6) &&
 
695
                HasPlus() && theCtrl->HasButtons() )
 
696
#endif
 
697
            {
 
698
                flags |= wxTREE_HITTEST_ONITEMBUTTON;
 
699
                return this;
 
700
            }
 
701
 
 
702
            if ((point.x >= m_x) && (point.x <= m_x+m_width))
 
703
            {
 
704
                int image_w = -1;
 
705
                int image_h;
 
706
 
 
707
                // assuming every image (normal and selected) has the same size!
 
708
                if ( (GetImage() != NO_IMAGE) && theCtrl->m_imageListNormal )
 
709
                {
 
710
                    theCtrl->m_imageListNormal->GetSize(GetImage(),
 
711
                                                        image_w, image_h);
 
712
                }
 
713
 
 
714
                int state_w = -1;
 
715
                int state_h;
 
716
 
 
717
                if ( (GetState() != wxTREE_ITEMSTATE_NONE) &&
 
718
                        theCtrl->m_imageListState )
 
719
                {
 
720
                    theCtrl->m_imageListState->GetSize(GetState(),
 
721
                                                       state_w, state_h);
 
722
                }
 
723
 
 
724
                if ((state_w != -1) && (point.x <= m_x + state_w + 1))
 
725
                    flags |= wxTREE_HITTEST_ONITEMSTATEICON;
 
726
                else if ((image_w != -1) &&
 
727
                         (point.x <= m_x +
 
728
                            (state_w != -1 ? state_w +
 
729
                                                MARGIN_BETWEEN_STATE_AND_IMAGE
 
730
                                           : 0)
 
731
                                            + image_w + 1))
 
732
                    flags |= wxTREE_HITTEST_ONITEMICON;
 
733
                else
 
734
                    flags |= wxTREE_HITTEST_ONITEMLABEL;
 
735
 
 
736
                return this;
 
737
            }
 
738
 
 
739
            if (point.x < m_x)
 
740
                flags |= wxTREE_HITTEST_ONITEMINDENT;
 
741
            if (point.x > m_x+m_width)
 
742
                flags |= wxTREE_HITTEST_ONITEMRIGHT;
 
743
 
 
744
            return this;
 
745
        }
 
746
 
 
747
        // if children are expanded, fall through to evaluate them
 
748
        if (m_isCollapsed) return NULL;
 
749
    }
 
750
 
 
751
    // evaluate children
 
752
    size_t count = m_children.GetCount();
 
753
    for ( size_t n = 0; n < count; n++ )
 
754
    {
 
755
        wxGenericTreeItem *res = m_children[n]->HitTest( point,
 
756
                                                         theCtrl,
 
757
                                                         flags,
 
758
                                                         level + 1 );
 
759
        if ( res != NULL )
 
760
            return res;
 
761
    }
 
762
 
 
763
    return NULL;
 
764
}
 
765
 
 
766
int wxGenericTreeItem::GetCurrentImage() const
 
767
{
 
768
    int image = NO_IMAGE;
 
769
    if ( IsExpanded() )
 
770
    {
 
771
        if ( IsSelected() )
 
772
        {
 
773
            image = GetImage(wxTreeItemIcon_SelectedExpanded);
 
774
        }
 
775
 
 
776
        if ( image == NO_IMAGE )
 
777
        {
 
778
            // we usually fall back to the normal item, but try just the
 
779
            // expanded one (and not selected) first in this case
 
780
            image = GetImage(wxTreeItemIcon_Expanded);
 
781
        }
 
782
    }
 
783
    else // not expanded
 
784
    {
 
785
        if ( IsSelected() )
 
786
            image = GetImage(wxTreeItemIcon_Selected);
 
787
    }
 
788
 
 
789
    // maybe it doesn't have the specific image we want,
 
790
    // try the default one instead
 
791
    if ( image == NO_IMAGE ) image = GetImage();
 
792
 
 
793
    return image;
 
794
}
 
795
 
 
796
void wxGenericTreeItem::CalculateSize(wxGenericTreeCtrl* control)
 
797
{
 
798
    // check if we need to do anything before creating the DC
 
799
    if ( m_width != 0 )
 
800
        return;
 
801
 
 
802
    wxClientDC dc(control);
 
803
    DoCalculateSize(control, dc, false /* normal font not used */);
 
804
}
 
805
 
 
806
void
 
807
wxGenericTreeItem::DoCalculateSize(wxGenericTreeCtrl* control,
 
808
                                   wxDC& dc,
 
809
                                   bool dcUsesNormalFont)
 
810
{
 
811
    if ( m_width != 0 ) // Size known, nothing to do
 
812
        return;
 
813
 
 
814
    if ( m_widthText == -1 )
 
815
    {
 
816
        bool fontChanged;
 
817
        if ( SetFont(control, dc) )
 
818
        {
 
819
            fontChanged = true;
 
820
        }
 
821
        else // we have no special font
 
822
        {
 
823
           if ( !dcUsesNormalFont )
 
824
           {
 
825
               // but we do need to ensure that the normal font is used: notice
 
826
               // that this doesn't count as changing the font as we don't need
 
827
               // to restore it
 
828
               dc.SetFont(control->m_normalFont);
 
829
           }
 
830
 
 
831
           fontChanged = false;
 
832
        }
 
833
 
 
834
        dc.GetTextExtent( GetText(), &m_widthText, &m_heightText );
 
835
 
 
836
        // restore normal font if the DC used it previously and we changed it
 
837
        if ( fontChanged )
 
838
             dc.SetFont(control->m_normalFont);
 
839
    }
 
840
 
 
841
    int text_h = m_heightText + 2;
 
842
 
 
843
    int image_h = 0, image_w = 0;
 
844
    int image = GetCurrentImage();
 
845
    if ( image != NO_IMAGE && control->m_imageListNormal )
 
846
    {
 
847
        control->m_imageListNormal->GetSize(image, image_w, image_h);
 
848
        image_w += MARGIN_BETWEEN_IMAGE_AND_TEXT;
 
849
    }
 
850
 
 
851
    int state_h = 0, state_w = 0;
 
852
    int state = GetState();
 
853
    if ( state != wxTREE_ITEMSTATE_NONE && control->m_imageListState )
 
854
    {
 
855
        control->m_imageListState->GetSize(state, state_w, state_h);
 
856
        if ( image_w != 0 )
 
857
            state_w += MARGIN_BETWEEN_STATE_AND_IMAGE;
 
858
        else
 
859
            state_w += MARGIN_BETWEEN_IMAGE_AND_TEXT;
 
860
    }
 
861
 
 
862
    int img_h = wxMax(state_h, image_h);
 
863
    m_height = wxMax(img_h, text_h);
 
864
 
 
865
    if (m_height < 30)
 
866
        m_height += 2;            // at least 2 pixels
 
867
    else
 
868
        m_height += m_height / 10;   // otherwise 10% extra spacing
 
869
 
 
870
    if (m_height > control->m_lineHeight)
 
871
        control->m_lineHeight = m_height;
 
872
 
 
873
    m_width = state_w + image_w + m_widthText + 2;
 
874
}
 
875
 
 
876
void wxGenericTreeItem::RecursiveResetSize()
 
877
{
 
878
    m_width = 0;
 
879
 
 
880
    const size_t count = m_children.Count();
 
881
    for (size_t i = 0; i < count; i++ )
 
882
        m_children[i]->RecursiveResetSize();
 
883
}
 
884
 
 
885
void wxGenericTreeItem::RecursiveResetTextSize()
 
886
{
 
887
    m_width = 0;
 
888
    m_widthText = -1;
 
889
 
 
890
    const size_t count = m_children.Count();
 
891
    for (size_t i = 0; i < count; i++ )
 
892
        m_children[i]->RecursiveResetTextSize();
 
893
}
 
894
 
 
895
// -----------------------------------------------------------------------------
 
896
// wxGenericTreeCtrl implementation
 
897
// -----------------------------------------------------------------------------
 
898
 
 
899
IMPLEMENT_DYNAMIC_CLASS(wxGenericTreeCtrl, wxControl)
 
900
 
 
901
BEGIN_EVENT_TABLE(wxGenericTreeCtrl, wxTreeCtrlBase)
 
902
    EVT_PAINT          (wxGenericTreeCtrl::OnPaint)
 
903
    EVT_SIZE           (wxGenericTreeCtrl::OnSize)
 
904
    EVT_MOUSE_EVENTS   (wxGenericTreeCtrl::OnMouse)
 
905
    EVT_KEY_DOWN       (wxGenericTreeCtrl::OnKeyDown)
 
906
    EVT_CHAR           (wxGenericTreeCtrl::OnChar)
 
907
    EVT_SET_FOCUS      (wxGenericTreeCtrl::OnSetFocus)
 
908
    EVT_KILL_FOCUS     (wxGenericTreeCtrl::OnKillFocus)
 
909
    EVT_TREE_ITEM_GETTOOLTIP(wxID_ANY, wxGenericTreeCtrl::OnGetToolTip)
 
910
END_EVENT_TABLE()
 
911
 
 
912
// -----------------------------------------------------------------------------
 
913
// construction/destruction
 
914
// -----------------------------------------------------------------------------
 
915
 
 
916
void wxGenericTreeCtrl::Init()
 
917
{
 
918
    m_current =
 
919
    m_key_current =
 
920
    m_anchor =
 
921
    m_select_me = NULL;
 
922
    m_hasFocus = false;
 
923
    m_dirty = false;
 
924
 
 
925
    m_lineHeight = 10;
 
926
    m_indent = 15;
 
927
    m_spacing = 18;
 
928
 
 
929
    m_hilightBrush = new wxBrush
 
930
                         (
 
931
                            wxSystemSettings::GetColour
 
932
                            (
 
933
                                wxSYS_COLOUR_HIGHLIGHT
 
934
                            ),
 
935
                            wxBRUSHSTYLE_SOLID
 
936
                         );
 
937
 
 
938
    m_hilightUnfocusedBrush = new wxBrush
 
939
                              (
 
940
                                 wxSystemSettings::GetColour
 
941
                                 (
 
942
                                     wxSYS_COLOUR_BTNSHADOW
 
943
                                 ),
 
944
                                 wxBRUSHSTYLE_SOLID
 
945
                              );
 
946
 
 
947
    m_imageListButtons = NULL;
 
948
    m_ownsImageListButtons = false;
 
949
 
 
950
    m_dragCount = 0;
 
951
    m_isDragging = false;
 
952
    m_dropTarget = m_oldSelection = NULL;
 
953
    m_underMouse = NULL;
 
954
    m_textCtrl = NULL;
 
955
 
 
956
    m_renameTimer = NULL;
 
957
 
 
958
    m_findTimer = NULL;
 
959
 
 
960
    m_dropEffectAboveItem = false;
 
961
 
 
962
    m_dndEffect = NoEffect;
 
963
    m_dndEffectItem = NULL;
 
964
 
 
965
    m_lastOnSame = false;
 
966
 
 
967
#if defined( __WXMAC__ )
 
968
    m_normalFont = wxFont(wxOSX_SYSTEM_FONT_VIEWS);
 
969
#else
 
970
    m_normalFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
 
971
#endif
 
972
    m_boldFont = m_normalFont.Bold();
 
973
}
 
974
 
 
975
bool wxGenericTreeCtrl::Create(wxWindow *parent,
 
976
                               wxWindowID id,
 
977
                               const wxPoint& pos,
 
978
                               const wxSize& size,
 
979
                               long style,
 
980
                               const wxValidator& validator,
 
981
                               const wxString& name )
 
982
{
 
983
#ifdef __WXMAC__
 
984
    int major, minor;
 
985
    wxGetOsVersion(&major, &minor);
 
986
 
 
987
    if (major < 10)
 
988
        style |= wxTR_ROW_LINES;
 
989
 
 
990
    if (style & wxTR_HAS_BUTTONS)
 
991
        style |= wxTR_NO_LINES;
 
992
#endif // __WXMAC__
 
993
 
 
994
#ifdef __WXGTK20__
 
995
    if (style & wxTR_HAS_BUTTONS)
 
996
        style |= wxTR_NO_LINES;
 
997
#endif
 
998
 
 
999
    if ( !wxControl::Create( parent, id, pos, size,
 
1000
                             style|wxHSCROLL|wxVSCROLL|wxWANTS_CHARS,
 
1001
                             validator,
 
1002
                             name ) )
 
1003
        return false;
 
1004
 
 
1005
    // If the tree display has no buttons, but does have
 
1006
    // connecting lines, we can use a narrower layout.
 
1007
    // It may not be a good idea to force this...
 
1008
    if (!HasButtons() && !HasFlag(wxTR_NO_LINES))
 
1009
    {
 
1010
        m_indent= 10;
 
1011
        m_spacing = 10;
 
1012
    }
 
1013
 
 
1014
    wxVisualAttributes attr = GetDefaultAttributes();
 
1015
    SetOwnForegroundColour( attr.colFg );
 
1016
    SetOwnBackgroundColour( attr.colBg );
 
1017
    if (!m_hasFont)
 
1018
        SetOwnFont(attr.font);
 
1019
 
 
1020
    // this is a misnomer: it's called "dotted pen" but uses (default) wxSOLID
 
1021
    // style because we apparently get performance problems when using dotted
 
1022
    // pen for drawing in some ports -- but under MSW it seems to work fine
 
1023
#ifdef __WXMSW__
 
1024
    m_dottedPen = wxPen(*wxLIGHT_GREY, 0, wxPENSTYLE_DOT);
 
1025
#else
 
1026
    m_dottedPen = *wxGREY_PEN;
 
1027
#endif
 
1028
 
 
1029
    SetInitialSize(size);
 
1030
 
 
1031
    return true;
 
1032
}
 
1033
 
 
1034
wxGenericTreeCtrl::~wxGenericTreeCtrl()
 
1035
{
 
1036
    delete m_hilightBrush;
 
1037
    delete m_hilightUnfocusedBrush;
 
1038
 
 
1039
    DeleteAllItems();
 
1040
 
 
1041
    delete m_renameTimer;
 
1042
    delete m_findTimer;
 
1043
 
 
1044
    if (m_ownsImageListButtons)
 
1045
        delete m_imageListButtons;
 
1046
}
 
1047
 
 
1048
// -----------------------------------------------------------------------------
 
1049
// accessors
 
1050
// -----------------------------------------------------------------------------
 
1051
 
 
1052
unsigned int wxGenericTreeCtrl::GetCount() const
 
1053
{
 
1054
    if ( !m_anchor )
 
1055
    {
 
1056
        // the tree is empty
 
1057
        return 0;
 
1058
    }
 
1059
 
 
1060
    unsigned int count = m_anchor->GetChildrenCount();
 
1061
    if ( !HasFlag(wxTR_HIDE_ROOT) )
 
1062
    {
 
1063
        // take the root itself into account
 
1064
        count++;
 
1065
    }
 
1066
 
 
1067
    return count;
 
1068
}
 
1069
 
 
1070
void wxGenericTreeCtrl::SetIndent(unsigned int indent)
 
1071
{
 
1072
    m_indent = (unsigned short) indent;
 
1073
    m_dirty = true;
 
1074
}
 
1075
 
 
1076
size_t
 
1077
wxGenericTreeCtrl::GetChildrenCount(const wxTreeItemId& item,
 
1078
                                    bool recursively) const
 
1079
{
 
1080
    wxCHECK_MSG( item.IsOk(), 0u, wxT("invalid tree item") );
 
1081
 
 
1082
    return ((wxGenericTreeItem*) item.m_pItem)->GetChildrenCount(recursively);
 
1083
}
 
1084
 
 
1085
void wxGenericTreeCtrl::SetWindowStyle(const long styles)
 
1086
{
 
1087
    // Do not try to expand the root node if it hasn't been created yet
 
1088
    if (m_anchor && !HasFlag(wxTR_HIDE_ROOT) && (styles & wxTR_HIDE_ROOT))
 
1089
    {
 
1090
        // if we will hide the root, make sure children are visible
 
1091
        m_anchor->SetHasPlus();
 
1092
        m_anchor->Expand();
 
1093
        CalculatePositions();
 
1094
    }
 
1095
 
 
1096
    // right now, just sets the styles.  Eventually, we may
 
1097
    // want to update the inherited styles, but right now
 
1098
    // none of the parents has updatable styles
 
1099
    m_windowStyle = styles;
 
1100
    m_dirty = true;
 
1101
}
 
1102
 
 
1103
// -----------------------------------------------------------------------------
 
1104
// functions to work with tree items
 
1105
// -----------------------------------------------------------------------------
 
1106
 
 
1107
wxString wxGenericTreeCtrl::GetItemText(const wxTreeItemId& item) const
 
1108
{
 
1109
    wxCHECK_MSG( item.IsOk(), wxEmptyString, wxT("invalid tree item") );
 
1110
 
 
1111
    return ((wxGenericTreeItem*) item.m_pItem)->GetText();
 
1112
}
 
1113
 
 
1114
int wxGenericTreeCtrl::GetItemImage(const wxTreeItemId& item,
 
1115
                             wxTreeItemIcon which) const
 
1116
{
 
1117
    wxCHECK_MSG( item.IsOk(), -1, wxT("invalid tree item") );
 
1118
 
 
1119
    return ((wxGenericTreeItem*) item.m_pItem)->GetImage(which);
 
1120
}
 
1121
 
 
1122
wxTreeItemData *wxGenericTreeCtrl::GetItemData(const wxTreeItemId& item) const
 
1123
{
 
1124
    wxCHECK_MSG( item.IsOk(), NULL, wxT("invalid tree item") );
 
1125
 
 
1126
    return ((wxGenericTreeItem*) item.m_pItem)->GetData();
 
1127
}
 
1128
 
 
1129
int wxGenericTreeCtrl::DoGetItemState(const wxTreeItemId& item) const
 
1130
{
 
1131
    wxCHECK_MSG( item.IsOk(), wxTREE_ITEMSTATE_NONE, wxT("invalid tree item") );
 
1132
 
 
1133
    wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
 
1134
    return pItem->GetState();
 
1135
}
 
1136
 
 
1137
wxColour wxGenericTreeCtrl::GetItemTextColour(const wxTreeItemId& item) const
 
1138
{
 
1139
    wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") );
 
1140
 
 
1141
    wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
 
1142
    return pItem->Attr().GetTextColour();
 
1143
}
 
1144
 
 
1145
wxColour
 
1146
wxGenericTreeCtrl::GetItemBackgroundColour(const wxTreeItemId& item) const
 
1147
{
 
1148
    wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") );
 
1149
 
 
1150
    wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
 
1151
    return pItem->Attr().GetBackgroundColour();
 
1152
}
 
1153
 
 
1154
wxFont wxGenericTreeCtrl::GetItemFont(const wxTreeItemId& item) const
 
1155
{
 
1156
    wxCHECK_MSG( item.IsOk(), wxNullFont, wxT("invalid tree item") );
 
1157
 
 
1158
    wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
 
1159
    return pItem->Attr().GetFont();
 
1160
}
 
1161
 
 
1162
void
 
1163
wxGenericTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text)
 
1164
{
 
1165
    wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
 
1166
 
 
1167
    wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
 
1168
    pItem->SetText(text);
 
1169
    pItem->CalculateSize(this);
 
1170
    RefreshLine(pItem);
 
1171
}
 
1172
 
 
1173
void wxGenericTreeCtrl::SetItemImage(const wxTreeItemId& item,
 
1174
                              int image,
 
1175
                              wxTreeItemIcon which)
 
1176
{
 
1177
    wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
 
1178
 
 
1179
    wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
 
1180
    pItem->SetImage(image, which);
 
1181
    pItem->CalculateSize(this);
 
1182
    RefreshLine(pItem);
 
1183
}
 
1184
 
 
1185
void
 
1186
wxGenericTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data)
 
1187
{
 
1188
    wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
 
1189
 
 
1190
    if (data)
 
1191
        data->SetId( item );
 
1192
 
 
1193
    ((wxGenericTreeItem*) item.m_pItem)->SetData(data);
 
1194
}
 
1195
 
 
1196
void wxGenericTreeCtrl::DoSetItemState(const wxTreeItemId& item, int state)
 
1197
{
 
1198
    wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
 
1199
 
 
1200
    wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
 
1201
    pItem->SetState(state);
 
1202
    pItem->CalculateSize(this);
 
1203
    RefreshLine(pItem);
 
1204
}
 
1205
 
 
1206
void wxGenericTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has)
 
1207
{
 
1208
    wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
 
1209
 
 
1210
    wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
 
1211
    pItem->SetHasPlus(has);
 
1212
    RefreshLine(pItem);
 
1213
}
 
1214
 
 
1215
void wxGenericTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold)
 
1216
{
 
1217
    wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
 
1218
 
 
1219
    // avoid redrawing the tree if no real change
 
1220
    wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
 
1221
    if ( pItem->IsBold() != bold )
 
1222
    {
 
1223
        pItem->SetBold(bold);
 
1224
 
 
1225
        // recalculate the item size as bold and non bold fonts have different
 
1226
        // widths
 
1227
        pItem->CalculateSize(this);
 
1228
        RefreshLine(pItem);
 
1229
    }
 
1230
}
 
1231
 
 
1232
void wxGenericTreeCtrl::SetItemDropHighlight(const wxTreeItemId& item,
 
1233
                                             bool highlight)
 
1234
{
 
1235
    wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
 
1236
 
 
1237
    wxColour fg, bg;
 
1238
 
 
1239
    if (highlight)
 
1240
    {
 
1241
        bg = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
 
1242
        fg = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
 
1243
    }
 
1244
 
 
1245
    wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
 
1246
    pItem->Attr().SetTextColour(fg);
 
1247
    pItem->Attr().SetBackgroundColour(bg);
 
1248
    RefreshLine(pItem);
 
1249
}
 
1250
 
 
1251
void wxGenericTreeCtrl::SetItemTextColour(const wxTreeItemId& item,
 
1252
                                   const wxColour& col)
 
1253
{
 
1254
    wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
 
1255
 
 
1256
    wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
 
1257
    pItem->Attr().SetTextColour(col);
 
1258
    RefreshLine(pItem);
 
1259
}
 
1260
 
 
1261
void wxGenericTreeCtrl::SetItemBackgroundColour(const wxTreeItemId& item,
 
1262
                                         const wxColour& col)
 
1263
{
 
1264
    wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
 
1265
 
 
1266
    wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
 
1267
    pItem->Attr().SetBackgroundColour(col);
 
1268
    RefreshLine(pItem);
 
1269
}
 
1270
 
 
1271
void
 
1272
wxGenericTreeCtrl::SetItemFont(const wxTreeItemId& item, const wxFont& font)
 
1273
{
 
1274
    wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
 
1275
 
 
1276
    wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
 
1277
    pItem->Attr().SetFont(font);
 
1278
    pItem->ResetTextSize();
 
1279
    pItem->CalculateSize(this);
 
1280
    RefreshLine(pItem);
 
1281
}
 
1282
 
 
1283
bool wxGenericTreeCtrl::SetFont( const wxFont &font )
 
1284
{
 
1285
    wxTreeCtrlBase::SetFont(font);
 
1286
 
 
1287
    m_normalFont = font;
 
1288
    m_boldFont = m_normalFont.Bold();
 
1289
 
 
1290
    if (m_anchor)
 
1291
        m_anchor->RecursiveResetTextSize();
 
1292
 
 
1293
    return true;
 
1294
}
 
1295
 
 
1296
 
 
1297
// -----------------------------------------------------------------------------
 
1298
// item status inquiries
 
1299
// -----------------------------------------------------------------------------
 
1300
 
 
1301
bool wxGenericTreeCtrl::IsVisible(const wxTreeItemId& item) const
 
1302
{
 
1303
    wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
 
1304
 
 
1305
    // An item is only visible if it's not a descendant of a collapsed item
 
1306
    wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
 
1307
    wxGenericTreeItem* parent = pItem->GetParent();
 
1308
    while (parent)
 
1309
    {
 
1310
        if (!parent->IsExpanded())
 
1311
            return false;
 
1312
        parent = parent->GetParent();
 
1313
    }
 
1314
 
 
1315
    int startX, startY;
 
1316
    GetViewStart(& startX, & startY);
 
1317
 
 
1318
    wxSize clientSize = GetClientSize();
 
1319
 
 
1320
    wxRect rect;
 
1321
    if (!GetBoundingRect(item, rect))
 
1322
        return false;
 
1323
    if (rect.GetWidth() == 0 || rect.GetHeight() == 0)
 
1324
        return false;
 
1325
    if (rect.GetBottom() < 0 || rect.GetTop() > clientSize.y)
 
1326
        return false;
 
1327
    if (rect.GetRight() < 0 || rect.GetLeft() > clientSize.x)
 
1328
        return false;
 
1329
 
 
1330
    return true;
 
1331
}
 
1332
 
 
1333
bool wxGenericTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const
 
1334
{
 
1335
    wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
 
1336
 
 
1337
    // consider that the item does have children if it has the "+" button: it
 
1338
    // might not have them (if it had never been expanded yet) but then it
 
1339
    // could have them as well and it's better to err on this side rather than
 
1340
    // disabling some operations which are restricted to the items with
 
1341
    // children for an item which does have them
 
1342
    return ((wxGenericTreeItem*) item.m_pItem)->HasPlus();
 
1343
}
 
1344
 
 
1345
bool wxGenericTreeCtrl::IsExpanded(const wxTreeItemId& item) const
 
1346
{
 
1347
    wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
 
1348
 
 
1349
    return ((wxGenericTreeItem*) item.m_pItem)->IsExpanded();
 
1350
}
 
1351
 
 
1352
bool wxGenericTreeCtrl::IsSelected(const wxTreeItemId& item) const
 
1353
{
 
1354
    wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
 
1355
 
 
1356
    return ((wxGenericTreeItem*) item.m_pItem)->IsSelected();
 
1357
}
 
1358
 
 
1359
bool wxGenericTreeCtrl::IsBold(const wxTreeItemId& item) const
 
1360
{
 
1361
    wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
 
1362
 
 
1363
    return ((wxGenericTreeItem*) item.m_pItem)->IsBold();
 
1364
}
 
1365
 
 
1366
// -----------------------------------------------------------------------------
 
1367
// navigation
 
1368
// -----------------------------------------------------------------------------
 
1369
 
 
1370
wxTreeItemId wxGenericTreeCtrl::GetItemParent(const wxTreeItemId& item) const
 
1371
{
 
1372
    wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
 
1373
 
 
1374
    return ((wxGenericTreeItem*) item.m_pItem)->GetParent();
 
1375
}
 
1376
 
 
1377
wxTreeItemId wxGenericTreeCtrl::GetFirstChild(const wxTreeItemId& item,
 
1378
                                              wxTreeItemIdValue& cookie) const
 
1379
{
 
1380
    wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
 
1381
 
 
1382
    cookie = 0;
 
1383
    return GetNextChild(item, cookie);
 
1384
}
 
1385
 
 
1386
wxTreeItemId wxGenericTreeCtrl::GetNextChild(const wxTreeItemId& item,
 
1387
                                             wxTreeItemIdValue& cookie) const
 
1388
{
 
1389
    wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
 
1390
 
 
1391
    wxArrayGenericTreeItems&
 
1392
        children = ((wxGenericTreeItem*) item.m_pItem)->GetChildren();
 
1393
 
 
1394
    // it's ok to cast cookie to size_t, we never have indices big enough to
 
1395
    // overflow "void *"
 
1396
    size_t *pIndex = (size_t *)&cookie;
 
1397
    if ( *pIndex < children.GetCount() )
 
1398
    {
 
1399
        return children.Item((*pIndex)++);
 
1400
    }
 
1401
    else
 
1402
    {
 
1403
        // there are no more of them
 
1404
        return wxTreeItemId();
 
1405
    }
 
1406
}
 
1407
 
 
1408
wxTreeItemId wxGenericTreeCtrl::GetLastChild(const wxTreeItemId& item) const
 
1409
{
 
1410
    wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
 
1411
 
 
1412
    wxArrayGenericTreeItems&
 
1413
        children = ((wxGenericTreeItem*) item.m_pItem)->GetChildren();
 
1414
    return children.IsEmpty() ? wxTreeItemId() : wxTreeItemId(children.Last());
 
1415
}
 
1416
 
 
1417
wxTreeItemId wxGenericTreeCtrl::GetNextSibling(const wxTreeItemId& item) const
 
1418
{
 
1419
    wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
 
1420
 
 
1421
    wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
 
1422
    wxGenericTreeItem *parent = i->GetParent();
 
1423
    if ( parent == NULL )
 
1424
    {
 
1425
        // root item doesn't have any siblings
 
1426
        return wxTreeItemId();
 
1427
    }
 
1428
 
 
1429
    wxArrayGenericTreeItems& siblings = parent->GetChildren();
 
1430
    int index = siblings.Index(i);
 
1431
    wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
 
1432
 
 
1433
    size_t n = (size_t)(index + 1);
 
1434
    return n == siblings.GetCount() ? wxTreeItemId()
 
1435
                                    : wxTreeItemId(siblings[n]);
 
1436
}
 
1437
 
 
1438
wxTreeItemId wxGenericTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const
 
1439
{
 
1440
    wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
 
1441
 
 
1442
    wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
 
1443
    wxGenericTreeItem *parent = i->GetParent();
 
1444
    if ( parent == NULL )
 
1445
    {
 
1446
        // root item doesn't have any siblings
 
1447
        return wxTreeItemId();
 
1448
    }
 
1449
 
 
1450
    wxArrayGenericTreeItems& siblings = parent->GetChildren();
 
1451
    int index = siblings.Index(i);
 
1452
    wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
 
1453
 
 
1454
    return index == 0 ? wxTreeItemId()
 
1455
                      : wxTreeItemId(siblings[(size_t)(index - 1)]);
 
1456
}
 
1457
 
 
1458
// Only for internal use right now, but should probably be public
 
1459
wxTreeItemId wxGenericTreeCtrl::GetNext(const wxTreeItemId& item) const
 
1460
{
 
1461
    wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
 
1462
 
 
1463
    wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
 
1464
 
 
1465
    // First see if there are any children.
 
1466
    wxArrayGenericTreeItems& children = i->GetChildren();
 
1467
    if (children.GetCount() > 0)
 
1468
    {
 
1469
         return children.Item(0);
 
1470
    }
 
1471
    else
 
1472
    {
 
1473
         // Try a sibling of this or ancestor instead
 
1474
         wxTreeItemId p = item;
 
1475
         wxTreeItemId toFind;
 
1476
         do
 
1477
         {
 
1478
              toFind = GetNextSibling(p);
 
1479
              p = GetItemParent(p);
 
1480
         } while (p.IsOk() && !toFind.IsOk());
 
1481
         return toFind;
 
1482
    }
 
1483
}
 
1484
 
 
1485
wxTreeItemId wxGenericTreeCtrl::GetFirstVisibleItem() const
 
1486
{
 
1487
    wxTreeItemId itemid = GetRootItem();
 
1488
    if (!itemid.IsOk())
 
1489
        return itemid;
 
1490
 
 
1491
    do
 
1492
    {
 
1493
        if (IsVisible(itemid))
 
1494
              return itemid;
 
1495
        itemid = GetNext(itemid);
 
1496
    } while (itemid.IsOk());
 
1497
 
 
1498
    return wxTreeItemId();
 
1499
}
 
1500
 
 
1501
wxTreeItemId wxGenericTreeCtrl::GetNextVisible(const wxTreeItemId& item) const
 
1502
{
 
1503
    wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
 
1504
    wxASSERT_MSG( IsVisible(item), wxT("this item itself should be visible") );
 
1505
 
 
1506
    wxTreeItemId id = item;
 
1507
    if (id.IsOk())
 
1508
    {
 
1509
        while (id = GetNext(id), id.IsOk())
 
1510
        {
 
1511
            if (IsVisible(id))
 
1512
                return id;
 
1513
        }
 
1514
    }
 
1515
    return wxTreeItemId();
 
1516
}
 
1517
 
 
1518
wxTreeItemId wxGenericTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
 
1519
{
 
1520
    wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
 
1521
    wxASSERT_MSG( IsVisible(item), wxT("this item itself should be visible") );
 
1522
 
 
1523
    // find out the starting point
 
1524
    wxTreeItemId prevItem = GetPrevSibling(item);
 
1525
    if ( !prevItem.IsOk() )
 
1526
    {
 
1527
        prevItem = GetItemParent(item);
 
1528
    }
 
1529
 
 
1530
    // find the first visible item after it
 
1531
    while ( prevItem.IsOk() && !IsVisible(prevItem) )
 
1532
    {
 
1533
        prevItem = GetNext(prevItem);
 
1534
        if ( !prevItem.IsOk() || prevItem == item )
 
1535
        {
 
1536
            // there are no visible items before item
 
1537
            return wxTreeItemId();
 
1538
        }
 
1539
    }
 
1540
 
 
1541
    // from there we must be able to navigate until this item
 
1542
    while ( prevItem.IsOk() )
 
1543
    {
 
1544
        const wxTreeItemId nextItem = GetNextVisible(prevItem);
 
1545
        if ( !nextItem.IsOk() || nextItem == item )
 
1546
            break;
 
1547
 
 
1548
        prevItem = nextItem;
 
1549
    }
 
1550
 
 
1551
    return prevItem;
 
1552
}
 
1553
 
 
1554
// called by wxTextTreeCtrl when it marks itself for deletion
 
1555
void wxGenericTreeCtrl::ResetTextControl()
 
1556
{
 
1557
    m_textCtrl = NULL;
 
1558
}
 
1559
 
 
1560
// find the first item starting with the given prefix after the given item
 
1561
wxTreeItemId wxGenericTreeCtrl::FindItem(const wxTreeItemId& idParent,
 
1562
                                         const wxString& prefixOrig) const
 
1563
{
 
1564
    // match is case insensitive as this is more convenient to the user: having
 
1565
    // to press Shift-letter to go to the item starting with a capital letter
 
1566
    // would be too bothersome
 
1567
    wxString prefix = prefixOrig.Lower();
 
1568
 
 
1569
    // determine the starting point: we shouldn't take the current item (this
 
1570
    // allows to switch between two items starting with the same letter just by
 
1571
    // pressing it) but we shouldn't jump to the next one if the user is
 
1572
    // continuing to type as otherwise he might easily skip the item he wanted
 
1573
    wxTreeItemId itemid = idParent;
 
1574
    if ( prefix.length() == 1 )
 
1575
    {
 
1576
        itemid = GetNext(itemid);
 
1577
    }
 
1578
 
 
1579
    // look for the item starting with the given prefix after it
 
1580
    while ( itemid.IsOk() && !GetItemText(itemid).Lower().StartsWith(prefix) )
 
1581
    {
 
1582
        itemid = GetNext(itemid);
 
1583
    }
 
1584
 
 
1585
    // if we haven't found anything...
 
1586
    if ( !itemid.IsOk() )
 
1587
    {
 
1588
        // ... wrap to the beginning
 
1589
        itemid = GetRootItem();
 
1590
        if ( HasFlag(wxTR_HIDE_ROOT) )
 
1591
        {
 
1592
            // can't select virtual root
 
1593
            itemid = GetNext(itemid);
 
1594
        }
 
1595
 
 
1596
        // and try all the items (stop when we get to the one we started from)
 
1597
        while ( itemid.IsOk() && itemid != idParent &&
 
1598
                    !GetItemText(itemid).Lower().StartsWith(prefix) )
 
1599
        {
 
1600
            itemid = GetNext(itemid);
 
1601
        }
 
1602
        // If we haven't found the item, id.IsOk() will be false, as per
 
1603
        // documentation
 
1604
    }
 
1605
 
 
1606
    return itemid;
 
1607
}
 
1608
 
 
1609
// -----------------------------------------------------------------------------
 
1610
// operations
 
1611
// -----------------------------------------------------------------------------
 
1612
 
 
1613
wxTreeItemId wxGenericTreeCtrl::DoInsertItem(const wxTreeItemId& parentId,
 
1614
                                             size_t previous,
 
1615
                                             const wxString& text,
 
1616
                                             int image,
 
1617
                                             int selImage,
 
1618
                                             wxTreeItemData *data)
 
1619
{
 
1620
    wxGenericTreeItem *parent = (wxGenericTreeItem*) parentId.m_pItem;
 
1621
    if ( !parent )
 
1622
    {
 
1623
        // should we give a warning here?
 
1624
        return AddRoot(text, image, selImage, data);
 
1625
    }
 
1626
 
 
1627
    m_dirty = true;     // do this first so stuff below doesn't cause flicker
 
1628
 
 
1629
    wxGenericTreeItem *item =
 
1630
        new wxGenericTreeItem( parent, text, image, selImage, data );
 
1631
 
 
1632
    if ( data != NULL )
 
1633
    {
 
1634
        data->m_pItem = item;
 
1635
    }
 
1636
 
 
1637
    parent->Insert( item, previous == (size_t)-1 ? parent->GetChildren().size()
 
1638
                                                 : previous );
 
1639
 
 
1640
    InvalidateBestSize();
 
1641
    return item;
 
1642
}
 
1643
 
 
1644
wxTreeItemId wxGenericTreeCtrl::AddRoot(const wxString& text,
 
1645
                                        int image,
 
1646
                                        int selImage,
 
1647
                                        wxTreeItemData *data)
 
1648
{
 
1649
    wxCHECK_MSG( !m_anchor, wxTreeItemId(), "tree can have only one root" );
 
1650
 
 
1651
    m_dirty = true;     // do this first so stuff below doesn't cause flicker
 
1652
 
 
1653
    m_anchor = new wxGenericTreeItem(NULL, text,
 
1654
                                   image, selImage, data);
 
1655
    if ( data != NULL )
 
1656
    {
 
1657
        data->m_pItem = m_anchor;
 
1658
    }
 
1659
 
 
1660
    if (HasFlag(wxTR_HIDE_ROOT))
 
1661
    {
 
1662
        // if root is hidden, make sure we can navigate
 
1663
        // into children
 
1664
        m_anchor->SetHasPlus();
 
1665
        m_anchor->Expand();
 
1666
        CalculatePositions();
 
1667
    }
 
1668
 
 
1669
    if (!HasFlag(wxTR_MULTIPLE))
 
1670
    {
 
1671
        m_current = m_key_current = m_anchor;
 
1672
        m_current->SetHilight( true );
 
1673
    }
 
1674
 
 
1675
    InvalidateBestSize();
 
1676
    return m_anchor;
 
1677
}
 
1678
 
 
1679
wxTreeItemId wxGenericTreeCtrl::DoInsertAfter(const wxTreeItemId& parentId,
 
1680
                                              const wxTreeItemId& idPrevious,
 
1681
                                              const wxString& text,
 
1682
                                              int image, int selImage,
 
1683
                                              wxTreeItemData *data)
 
1684
{
 
1685
    wxGenericTreeItem *parent = (wxGenericTreeItem*) parentId.m_pItem;
 
1686
    if ( !parent )
 
1687
    {
 
1688
        // should we give a warning here?
 
1689
        return AddRoot(text, image, selImage, data);
 
1690
    }
 
1691
 
 
1692
    int index = -1;
 
1693
    if (idPrevious.IsOk())
 
1694
    {
 
1695
        index = parent->GetChildren().Index(
 
1696
                    (wxGenericTreeItem*) idPrevious.m_pItem);
 
1697
        wxASSERT_MSG( index != wxNOT_FOUND,
 
1698
                      "previous item in wxGenericTreeCtrl::InsertItem() "
 
1699
                      "is not a sibling" );
 
1700
    }
 
1701
 
 
1702
    return DoInsertItem(parentId, (size_t)++index, text, image, selImage, data);
 
1703
}
 
1704
 
 
1705
 
 
1706
void wxGenericTreeCtrl::SendDeleteEvent(wxGenericTreeItem *item)
 
1707
{
 
1708
    wxTreeEvent event(wxEVT_COMMAND_TREE_DELETE_ITEM, this, item);
 
1709
    GetEventHandler()->ProcessEvent( event );
 
1710
}
 
1711
 
 
1712
// Don't leave edit or selection on a child which is about to disappear
 
1713
void wxGenericTreeCtrl::ChildrenClosing(wxGenericTreeItem* item)
 
1714
{
 
1715
    if ( m_textCtrl && item != m_textCtrl->item() &&
 
1716
            IsDescendantOf(item, m_textCtrl->item()) )
 
1717
    {
 
1718
        m_textCtrl->EndEdit( true );
 
1719
    }
 
1720
 
 
1721
    if ( item != m_key_current && IsDescendantOf(item, m_key_current) )
 
1722
    {
 
1723
        m_key_current = NULL;
 
1724
    }
 
1725
 
 
1726
    if ( IsDescendantOf(item, m_select_me) )
 
1727
    {
 
1728
        m_select_me = item;
 
1729
    }
 
1730
 
 
1731
    if ( item != m_current && IsDescendantOf(item, m_current) )
 
1732
    {
 
1733
        m_current->SetHilight( false );
 
1734
        m_current = NULL;
 
1735
        m_select_me = item;
 
1736
    }
 
1737
}
 
1738
 
 
1739
void wxGenericTreeCtrl::DeleteChildren(const wxTreeItemId& itemId)
 
1740
{
 
1741
    m_dirty = true;     // do this first so stuff below doesn't cause flicker
 
1742
 
 
1743
    wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
 
1744
    ChildrenClosing(item);
 
1745
    item->DeleteChildren(this);
 
1746
    InvalidateBestSize();
 
1747
}
 
1748
 
 
1749
void wxGenericTreeCtrl::Delete(const wxTreeItemId& itemId)
 
1750
{
 
1751
    m_dirty = true;     // do this first so stuff below doesn't cause flicker
 
1752
 
 
1753
    wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
 
1754
 
 
1755
    if (m_textCtrl != NULL && IsDescendantOf(item, m_textCtrl->item()))
 
1756
    {
 
1757
        // can't delete the item being edited, cancel editing it first
 
1758
        m_textCtrl->EndEdit( true );
 
1759
    }
 
1760
 
 
1761
    wxGenericTreeItem *parent = item->GetParent();
 
1762
 
 
1763
    // if the selected item will be deleted, select the parent ...
 
1764
    wxGenericTreeItem *to_be_selected = parent;
 
1765
    if (parent)
 
1766
    {
 
1767
        // .. unless there is a next sibling like wxMSW does it
 
1768
        int pos = parent->GetChildren().Index( item );
 
1769
        if ((int)(parent->GetChildren().GetCount()) > pos+1)
 
1770
            to_be_selected = parent->GetChildren().Item( pos+1 );
 
1771
    }
 
1772
 
 
1773
    // don't keep stale pointers around!
 
1774
    if ( IsDescendantOf(item, m_key_current) )
 
1775
    {
 
1776
        // Don't silently change the selection:
 
1777
        // do it properly in idle time, so event
 
1778
        // handlers get called.
 
1779
 
 
1780
        // m_key_current = parent;
 
1781
        m_key_current = NULL;
 
1782
    }
 
1783
 
 
1784
    // m_select_me records whether we need to select
 
1785
    // a different item, in idle time.
 
1786
    if ( m_select_me && IsDescendantOf(item, m_select_me) )
 
1787
    {
 
1788
        m_select_me = to_be_selected;
 
1789
    }
 
1790
 
 
1791
    if ( IsDescendantOf(item, m_current) )
 
1792
    {
 
1793
        // Don't silently change the selection:
 
1794
        // do it properly in idle time, so event
 
1795
        // handlers get called.
 
1796
 
 
1797
        // m_current = parent;
 
1798
        m_current = NULL;
 
1799
        m_select_me = to_be_selected;
 
1800
    }
 
1801
 
 
1802
    // remove the item from the tree
 
1803
    if ( parent )
 
1804
    {
 
1805
        parent->GetChildren().Remove( item );  // remove by value
 
1806
    }
 
1807
    else // deleting the root
 
1808
    {
 
1809
        // nothing will be left in the tree
 
1810
        m_anchor = NULL;
 
1811
    }
 
1812
 
 
1813
    // and delete all of its children and the item itself now
 
1814
    item->DeleteChildren(this);
 
1815
    SendDeleteEvent(item);
 
1816
 
 
1817
    if (item == m_select_me)
 
1818
        m_select_me = NULL;
 
1819
 
 
1820
    delete item;
 
1821
 
 
1822
    InvalidateBestSize();
 
1823
}
 
1824
 
 
1825
void wxGenericTreeCtrl::DeleteAllItems()
 
1826
{
 
1827
    if ( m_anchor )
 
1828
    {
 
1829
        Delete(m_anchor);
 
1830
    }
 
1831
}
 
1832
 
 
1833
void wxGenericTreeCtrl::Expand(const wxTreeItemId& itemId)
 
1834
{
 
1835
    wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
 
1836
 
 
1837
    wxCHECK_RET( item, wxT("invalid item in wxGenericTreeCtrl::Expand") );
 
1838
    wxCHECK_RET( !HasFlag(wxTR_HIDE_ROOT) || itemId != GetRootItem(),
 
1839
                 wxT("can't expand hidden root") );
 
1840
 
 
1841
    if ( !item->HasPlus() )
 
1842
        return;
 
1843
 
 
1844
    if ( item->IsExpanded() )
 
1845
        return;
 
1846
 
 
1847
    wxTreeEvent event(wxEVT_COMMAND_TREE_ITEM_EXPANDING, this, item);
 
1848
 
 
1849
    if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() )
 
1850
    {
 
1851
        // cancelled by program
 
1852
        return;
 
1853
    }
 
1854
 
 
1855
    item->Expand();
 
1856
    if ( !IsFrozen() )
 
1857
    {
 
1858
        CalculatePositions();
 
1859
 
 
1860
        RefreshSubtree(item);
 
1861
    }
 
1862
    else // frozen
 
1863
    {
 
1864
        m_dirty = true;
 
1865
    }
 
1866
 
 
1867
    event.SetEventType(wxEVT_COMMAND_TREE_ITEM_EXPANDED);
 
1868
    GetEventHandler()->ProcessEvent( event );
 
1869
}
 
1870
 
 
1871
void wxGenericTreeCtrl::Collapse(const wxTreeItemId& itemId)
 
1872
{
 
1873
    wxCHECK_RET( !HasFlag(wxTR_HIDE_ROOT) || itemId != GetRootItem(),
 
1874
                 wxT("can't collapse hidden root") );
 
1875
 
 
1876
    wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
 
1877
 
 
1878
    if ( !item->IsExpanded() )
 
1879
        return;
 
1880
 
 
1881
    wxTreeEvent event(wxEVT_COMMAND_TREE_ITEM_COLLAPSING, this, item);
 
1882
    if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() )
 
1883
    {
 
1884
        // cancelled by program
 
1885
        return;
 
1886
    }
 
1887
 
 
1888
    ChildrenClosing(item);
 
1889
    item->Collapse();
 
1890
 
 
1891
#if 0  // TODO why should items be collapsed recursively?
 
1892
    wxArrayGenericTreeItems& children = item->GetChildren();
 
1893
    size_t count = children.GetCount();
 
1894
    for ( size_t n = 0; n < count; n++ )
 
1895
    {
 
1896
        Collapse(children[n]);
 
1897
    }
 
1898
#endif
 
1899
 
 
1900
    CalculatePositions();
 
1901
 
 
1902
    RefreshSubtree(item);
 
1903
 
 
1904
    event.SetEventType(wxEVT_COMMAND_TREE_ITEM_COLLAPSED);
 
1905
    GetEventHandler()->ProcessEvent( event );
 
1906
}
 
1907
 
 
1908
void wxGenericTreeCtrl::CollapseAndReset(const wxTreeItemId& item)
 
1909
{
 
1910
    Collapse(item);
 
1911
    DeleteChildren(item);
 
1912
}
 
1913
 
 
1914
void wxGenericTreeCtrl::Toggle(const wxTreeItemId& itemId)
 
1915
{
 
1916
    wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
 
1917
 
 
1918
    if (item->IsExpanded())
 
1919
        Collapse(itemId);
 
1920
    else
 
1921
        Expand(itemId);
 
1922
}
 
1923
 
 
1924
void wxGenericTreeCtrl::Unselect()
 
1925
{
 
1926
    if (m_current)
 
1927
    {
 
1928
        m_current->SetHilight( false );
 
1929
        RefreshLine( m_current );
 
1930
 
 
1931
        m_current = NULL;
 
1932
        m_select_me = NULL;
 
1933
    }
 
1934
}
 
1935
 
 
1936
void wxGenericTreeCtrl::ClearFocusedItem()
 
1937
{
 
1938
    wxTreeItemId item = GetFocusedItem();
 
1939
    if ( item.IsOk() )
 
1940
        SelectItem(item, false);
 
1941
}
 
1942
 
 
1943
void wxGenericTreeCtrl::SetFocusedItem(const wxTreeItemId& item)
 
1944
{
 
1945
    wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
 
1946
 
 
1947
    SelectItem(item, true);
 
1948
}
 
1949
 
 
1950
void wxGenericTreeCtrl::UnselectAllChildren(wxGenericTreeItem *item)
 
1951
{
 
1952
    if (item->IsSelected())
 
1953
    {
 
1954
        item->SetHilight(false);
 
1955
        RefreshLine(item);
 
1956
    }
 
1957
 
 
1958
    if (item->HasChildren())
 
1959
    {
 
1960
        wxArrayGenericTreeItems& children = item->GetChildren();
 
1961
        size_t count = children.GetCount();
 
1962
        for ( size_t n = 0; n < count; ++n )
 
1963
        {
 
1964
            UnselectAllChildren(children[n]);
 
1965
        }
 
1966
    }
 
1967
}
 
1968
 
 
1969
void wxGenericTreeCtrl::UnselectAll()
 
1970
{
 
1971
    wxTreeItemId rootItem = GetRootItem();
 
1972
 
 
1973
    // the tree might not have the root item at all
 
1974
    if ( rootItem )
 
1975
    {
 
1976
        UnselectAllChildren((wxGenericTreeItem*) rootItem.m_pItem);
 
1977
    }
 
1978
}
 
1979
 
 
1980
void wxGenericTreeCtrl::SelectChildren(const wxTreeItemId& parent)
 
1981
{
 
1982
    wxCHECK_RET( HasFlag(wxTR_MULTIPLE),
 
1983
                 "this only works with multiple selection controls" );
 
1984
 
 
1985
    UnselectAll();
 
1986
 
 
1987
    if ( !HasChildren(parent) )
 
1988
        return;
 
1989
 
 
1990
 
 
1991
    wxArrayGenericTreeItems&
 
1992
        children = ((wxGenericTreeItem*) parent.m_pItem)->GetChildren();
 
1993
    size_t count = children.GetCount();
 
1994
 
 
1995
    wxGenericTreeItem *
 
1996
        item = (wxGenericTreeItem*) ((wxTreeItemId)children[0]).m_pItem;
 
1997
    wxTreeEvent event(wxEVT_COMMAND_TREE_SEL_CHANGING, this, item);
 
1998
    event.m_itemOld = m_current;
 
1999
 
 
2000
    if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() )
 
2001
        return;
 
2002
 
 
2003
    for ( size_t n = 0; n < count; ++n )
 
2004
    {
 
2005
        m_current = m_key_current = children[n];
 
2006
        m_current->SetHilight(true);
 
2007
        RefreshSelected();
 
2008
    }
 
2009
 
 
2010
 
 
2011
    event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
 
2012
    GetEventHandler()->ProcessEvent( event );
 
2013
}
 
2014
 
 
2015
 
 
2016
// Recursive function !
 
2017
// To stop we must have crt_item<last_item
 
2018
// Algorithm :
 
2019
// Tag all next children, when no more children,
 
2020
// Move to parent (not to tag)
 
2021
// Keep going... if we found last_item, we stop.
 
2022
bool
 
2023
wxGenericTreeCtrl::TagNextChildren(wxGenericTreeItem *crt_item,
 
2024
                                   wxGenericTreeItem *last_item,
 
2025
                                   bool select)
 
2026
{
 
2027
    wxGenericTreeItem *parent = crt_item->GetParent();
 
2028
 
 
2029
    if (parent == NULL) // This is root item
 
2030
        return TagAllChildrenUntilLast(crt_item, last_item, select);
 
2031
 
 
2032
    wxArrayGenericTreeItems& children = parent->GetChildren();
 
2033
    int index = children.Index(crt_item);
 
2034
    wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
 
2035
 
 
2036
    size_t count = children.GetCount();
 
2037
    for (size_t n=(size_t)(index+1); n<count; ++n)
 
2038
    {
 
2039
        if ( TagAllChildrenUntilLast(children[n], last_item, select) )
 
2040
            return true;
 
2041
    }
 
2042
 
 
2043
    return TagNextChildren(parent, last_item, select);
 
2044
}
 
2045
 
 
2046
bool
 
2047
wxGenericTreeCtrl::TagAllChildrenUntilLast(wxGenericTreeItem *crt_item,
 
2048
                                           wxGenericTreeItem *last_item,
 
2049
                                           bool select)
 
2050
{
 
2051
    crt_item->SetHilight(select);
 
2052
    RefreshLine(crt_item);
 
2053
 
 
2054
    if (crt_item==last_item)
 
2055
        return true;
 
2056
 
 
2057
    // We should leave the not shown children of collapsed items alone.
 
2058
    if (crt_item->HasChildren() && crt_item->IsExpanded())
 
2059
    {
 
2060
        wxArrayGenericTreeItems& children = crt_item->GetChildren();
 
2061
        size_t count = children.GetCount();
 
2062
        for ( size_t n = 0; n < count; ++n )
 
2063
        {
 
2064
            if (TagAllChildrenUntilLast(children[n], last_item, select))
 
2065
                return true;
 
2066
        }
 
2067
    }
 
2068
 
 
2069
  return false;
 
2070
}
 
2071
 
 
2072
void
 
2073
wxGenericTreeCtrl::SelectItemRange(wxGenericTreeItem *item1,
 
2074
                                   wxGenericTreeItem *item2)
 
2075
{
 
2076
    m_select_me = NULL;
 
2077
 
 
2078
    // item2 is not necessary after item1
 
2079
    // choice first' and 'last' between item1 and item2
 
2080
    wxGenericTreeItem *first= (item1->GetY()<item2->GetY()) ? item1 : item2;
 
2081
    wxGenericTreeItem *last = (item1->GetY()<item2->GetY()) ? item2 : item1;
 
2082
 
 
2083
    bool select = m_current->IsSelected();
 
2084
 
 
2085
    if ( TagAllChildrenUntilLast(first,last,select) )
 
2086
        return;
 
2087
 
 
2088
    TagNextChildren(first,last,select);
 
2089
}
 
2090
 
 
2091
void wxGenericTreeCtrl::DoSelectItem(const wxTreeItemId& itemId,
 
2092
                                     bool unselect_others,
 
2093
                                     bool extended_select)
 
2094
{
 
2095
    wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") );
 
2096
 
 
2097
    m_select_me = NULL;
 
2098
 
 
2099
    bool is_single=!(GetWindowStyleFlag() & wxTR_MULTIPLE);
 
2100
    wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
 
2101
 
 
2102
    //wxCHECK_RET( ( (!unselect_others) && is_single),
 
2103
    //           wxT("this is a single selection tree") );
 
2104
 
 
2105
    // to keep going anyhow !!!
 
2106
    if (is_single)
 
2107
    {
 
2108
        if (item->IsSelected())
 
2109
            return; // nothing to do
 
2110
        unselect_others = true;
 
2111
        extended_select = false;
 
2112
    }
 
2113
    else if ( unselect_others && item->IsSelected() )
 
2114
    {
 
2115
        // selection change if there is more than one item currently selected
 
2116
        wxArrayTreeItemIds selected_items;
 
2117
        if ( GetSelections(selected_items) == 1 )
 
2118
            return;
 
2119
    }
 
2120
 
 
2121
    wxTreeEvent event(wxEVT_COMMAND_TREE_SEL_CHANGING, this, item);
 
2122
    event.m_itemOld = m_current;
 
2123
    // TODO : Here we don't send any selection mode yet !
 
2124
 
 
2125
    if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() )
 
2126
        return;
 
2127
 
 
2128
    wxTreeItemId parent = GetItemParent( itemId );
 
2129
    while (parent.IsOk())
 
2130
    {
 
2131
        if (!IsExpanded(parent))
 
2132
            Expand( parent );
 
2133
 
 
2134
        parent = GetItemParent( parent );
 
2135
    }
 
2136
 
 
2137
    // ctrl press
 
2138
    if (unselect_others)
 
2139
    {
 
2140
        if (is_single) Unselect(); // to speed up thing
 
2141
        else UnselectAll();
 
2142
    }
 
2143
 
 
2144
    // shift press
 
2145
    if (extended_select)
 
2146
    {
 
2147
        if ( !m_current )
 
2148
        {
 
2149
            m_current =
 
2150
            m_key_current = (wxGenericTreeItem*) GetRootItem().m_pItem;
 
2151
        }
 
2152
 
 
2153
        // don't change the mark (m_current)
 
2154
        SelectItemRange(m_current, item);
 
2155
    }
 
2156
    else
 
2157
    {
 
2158
        bool select = true; // the default
 
2159
 
 
2160
        // Check if we need to toggle hilight (ctrl mode)
 
2161
        if (!unselect_others)
 
2162
            select=!item->IsSelected();
 
2163
 
 
2164
        m_current = m_key_current = item;
 
2165
        m_current->SetHilight(select);
 
2166
        RefreshLine( m_current );
 
2167
    }
 
2168
 
 
2169
    // This can cause idle processing to select the root
 
2170
    // if no item is selected, so it must be after the
 
2171
    // selection is set
 
2172
    EnsureVisible( itemId );
 
2173
 
 
2174
    event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
 
2175
    GetEventHandler()->ProcessEvent( event );
 
2176
}
 
2177
 
 
2178
void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId, bool select)
 
2179
{
 
2180
    wxGenericTreeItem * const item = (wxGenericTreeItem*) itemId.m_pItem;
 
2181
    wxCHECK_RET( item, wxT("SelectItem(): invalid tree item") );
 
2182
 
 
2183
    if ( select )
 
2184
    {
 
2185
        if ( !item->IsSelected() )
 
2186
            DoSelectItem(itemId, !HasFlag(wxTR_MULTIPLE));
 
2187
    }
 
2188
    else // deselect
 
2189
    {
 
2190
        wxTreeEvent event(wxEVT_COMMAND_TREE_SEL_CHANGING, this, item);
 
2191
        if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() )
 
2192
            return;
 
2193
 
 
2194
        item->SetHilight(false);
 
2195
        RefreshLine(item);
 
2196
 
 
2197
        event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
 
2198
        GetEventHandler()->ProcessEvent( event );
 
2199
    }
 
2200
}
 
2201
 
 
2202
void wxGenericTreeCtrl::FillArray(wxGenericTreeItem *item,
 
2203
                                  wxArrayTreeItemIds &array) const
 
2204
{
 
2205
    if ( item->IsSelected() )
 
2206
        array.Add(wxTreeItemId(item));
 
2207
 
 
2208
    if ( item->HasChildren() )
 
2209
    {
 
2210
        wxArrayGenericTreeItems& children = item->GetChildren();
 
2211
        size_t count = children.GetCount();
 
2212
        for ( size_t n = 0; n < count; ++n )
 
2213
            FillArray(children[n], array);
 
2214
    }
 
2215
}
 
2216
 
 
2217
size_t wxGenericTreeCtrl::GetSelections(wxArrayTreeItemIds &array) const
 
2218
{
 
2219
    array.Empty();
 
2220
    wxTreeItemId idRoot = GetRootItem();
 
2221
    if ( idRoot.IsOk() )
 
2222
    {
 
2223
        FillArray((wxGenericTreeItem*) idRoot.m_pItem, array);
 
2224
    }
 
2225
    //else: the tree is empty, so no selections
 
2226
 
 
2227
    return array.GetCount();
 
2228
}
 
2229
 
 
2230
void wxGenericTreeCtrl::EnsureVisible(const wxTreeItemId& item)
 
2231
{
 
2232
    wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
 
2233
 
 
2234
    if (!item.IsOk()) return;
 
2235
 
 
2236
    wxGenericTreeItem *gitem = (wxGenericTreeItem*) item.m_pItem;
 
2237
 
 
2238
    // first expand all parent branches
 
2239
    wxGenericTreeItem *parent = gitem->GetParent();
 
2240
 
 
2241
    if ( HasFlag(wxTR_HIDE_ROOT) )
 
2242
    {
 
2243
        while ( parent && parent != m_anchor )
 
2244
        {
 
2245
            Expand(parent);
 
2246
            parent = parent->GetParent();
 
2247
        }
 
2248
    }
 
2249
    else
 
2250
    {
 
2251
        while ( parent )
 
2252
        {
 
2253
            Expand(parent);
 
2254
            parent = parent->GetParent();
 
2255
        }
 
2256
    }
 
2257
 
 
2258
    //if (parent) CalculatePositions();
 
2259
 
 
2260
    ScrollTo(item);
 
2261
}
 
2262
 
 
2263
void wxGenericTreeCtrl::ScrollTo(const wxTreeItemId &item)
 
2264
{
 
2265
    if (!item.IsOk())
 
2266
        return;
 
2267
 
 
2268
    // update the control before scrolling it
 
2269
    if (m_dirty)
 
2270
#if defined( __WXMSW__ ) || defined(__WXMAC__)
 
2271
        Update();
 
2272
#else
 
2273
        DoDirtyProcessing();
 
2274
#endif
 
2275
 
 
2276
    wxGenericTreeItem *gitem = (wxGenericTreeItem*) item.m_pItem;
 
2277
 
 
2278
    int itemY = gitem->GetY();
 
2279
 
 
2280
    int start_x = 0;
 
2281
    int start_y = 0;
 
2282
    GetViewStart( &start_x, &start_y );
 
2283
 
 
2284
    const int clientHeight = GetClientSize().y;
 
2285
 
 
2286
    const int itemHeight = GetLineHeight(gitem) + 2;
 
2287
 
 
2288
    if ( itemY + itemHeight > start_y*PIXELS_PER_UNIT + clientHeight )
 
2289
    {
 
2290
        // need to scroll up by enough to show this item fully
 
2291
        itemY += itemHeight - clientHeight;
 
2292
    }
 
2293
    else if ( itemY > start_y*PIXELS_PER_UNIT )
 
2294
    {
 
2295
        // item is already fully visible, don't do anything
 
2296
        return;
 
2297
    }
 
2298
    //else: scroll down to make this item the top one displayed
 
2299
 
 
2300
    Scroll(-1, itemY/PIXELS_PER_UNIT);
 
2301
}
 
2302
 
 
2303
// FIXME: tree sorting functions are not reentrant and not MT-safe!
 
2304
static wxGenericTreeCtrl *s_treeBeingSorted = NULL;
 
2305
 
 
2306
static int LINKAGEMODE tree_ctrl_compare_func(wxGenericTreeItem **item1,
 
2307
                                  wxGenericTreeItem **item2)
 
2308
{
 
2309
    wxCHECK_MSG( s_treeBeingSorted, 0,
 
2310
                 "bug in wxGenericTreeCtrl::SortChildren()" );
 
2311
 
 
2312
    return s_treeBeingSorted->OnCompareItems(*item1, *item2);
 
2313
}
 
2314
 
 
2315
void wxGenericTreeCtrl::SortChildren(const wxTreeItemId& itemId)
 
2316
{
 
2317
    wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") );
 
2318
 
 
2319
    wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
 
2320
 
 
2321
    wxCHECK_RET( !s_treeBeingSorted,
 
2322
                 wxT("wxGenericTreeCtrl::SortChildren is not reentrant") );
 
2323
 
 
2324
    wxArrayGenericTreeItems& children = item->GetChildren();
 
2325
    if ( children.GetCount() > 1 )
 
2326
    {
 
2327
        m_dirty = true;
 
2328
 
 
2329
        s_treeBeingSorted = this;
 
2330
        children.Sort(tree_ctrl_compare_func);
 
2331
        s_treeBeingSorted = NULL;
 
2332
    }
 
2333
    //else: don't make the tree dirty as nothing changed
 
2334
}
 
2335
 
 
2336
void wxGenericTreeCtrl::CalculateLineHeight()
 
2337
{
 
2338
    wxClientDC dc(this);
 
2339
    m_lineHeight = (int)(dc.GetCharHeight() + 4);
 
2340
 
 
2341
    if ( m_imageListNormal )
 
2342
    {
 
2343
        // Calculate a m_lineHeight value from the normal Image sizes.
 
2344
        // May be toggle off. Then wxGenericTreeCtrl will spread when
 
2345
        // necessary (which might look ugly).
 
2346
        int n = m_imageListNormal->GetImageCount();
 
2347
        for (int i = 0; i < n ; i++)
 
2348
        {
 
2349
            int width = 0, height = 0;
 
2350
            m_imageListNormal->GetSize(i, width, height);
 
2351
            if (height > m_lineHeight) m_lineHeight = height;
 
2352
        }
 
2353
    }
 
2354
 
 
2355
    if ( m_imageListState )
 
2356
    {
 
2357
        // Calculate a m_lineHeight value from the state Image sizes.
 
2358
        // May be toggle off. Then wxGenericTreeCtrl will spread when
 
2359
        // necessary (which might look ugly).
 
2360
        int n = m_imageListState->GetImageCount();
 
2361
        for (int i = 0; i < n ; i++)
 
2362
        {
 
2363
            int width = 0, height = 0;
 
2364
            m_imageListState->GetSize(i, width, height);
 
2365
            if (height > m_lineHeight) m_lineHeight = height;
 
2366
        }
 
2367
    }
 
2368
 
 
2369
    if (m_imageListButtons)
 
2370
    {
 
2371
        // Calculate a m_lineHeight value from the Button image sizes.
 
2372
        // May be toggle off. Then wxGenericTreeCtrl will spread when
 
2373
        // necessary (which might look ugly).
 
2374
        int n = m_imageListButtons->GetImageCount();
 
2375
        for (int i = 0; i < n ; i++)
 
2376
        {
 
2377
            int width = 0, height = 0;
 
2378
            m_imageListButtons->GetSize(i, width, height);
 
2379
            if (height > m_lineHeight) m_lineHeight = height;
 
2380
        }
 
2381
    }
 
2382
 
 
2383
    if (m_lineHeight < 30)
 
2384
        m_lineHeight += 2;                 // at least 2 pixels
 
2385
    else
 
2386
        m_lineHeight += m_lineHeight/10;   // otherwise 10% extra spacing
 
2387
}
 
2388
 
 
2389
void wxGenericTreeCtrl::SetImageList(wxImageList *imageList)
 
2390
{
 
2391
    if (m_ownsImageListNormal) delete m_imageListNormal;
 
2392
    m_imageListNormal = imageList;
 
2393
    m_ownsImageListNormal = false;
 
2394
    m_dirty = true;
 
2395
 
 
2396
    if (m_anchor)
 
2397
        m_anchor->RecursiveResetSize();
 
2398
 
 
2399
    // Don't do any drawing if we're setting the list to NULL,
 
2400
    // since we may be in the process of deleting the tree control.
 
2401
    if (imageList)
 
2402
        CalculateLineHeight();
 
2403
}
 
2404
 
 
2405
void wxGenericTreeCtrl::SetStateImageList(wxImageList *imageList)
 
2406
{
 
2407
    if (m_ownsImageListState) delete m_imageListState;
 
2408
    m_imageListState = imageList;
 
2409
    m_ownsImageListState = false;
 
2410
    m_dirty = true;
 
2411
 
 
2412
    if (m_anchor)
 
2413
        m_anchor->RecursiveResetSize();
 
2414
 
 
2415
    // Don't do any drawing if we're setting the list to NULL,
 
2416
    // since we may be in the process of deleting the tree control.
 
2417
    if (imageList)
 
2418
        CalculateLineHeight();
 
2419
}
 
2420
 
 
2421
void wxGenericTreeCtrl::SetButtonsImageList(wxImageList *imageList)
 
2422
{
 
2423
    if (m_ownsImageListButtons) delete m_imageListButtons;
 
2424
    m_imageListButtons = imageList;
 
2425
    m_ownsImageListButtons = false;
 
2426
    m_dirty = true;
 
2427
 
 
2428
    if (m_anchor)
 
2429
        m_anchor->RecursiveResetSize();
 
2430
 
 
2431
    CalculateLineHeight();
 
2432
}
 
2433
 
 
2434
void wxGenericTreeCtrl::AssignButtonsImageList(wxImageList *imageList)
 
2435
{
 
2436
    SetButtonsImageList(imageList);
 
2437
    m_ownsImageListButtons = true;
 
2438
}
 
2439
 
 
2440
// -----------------------------------------------------------------------------
 
2441
// helpers
 
2442
// -----------------------------------------------------------------------------
 
2443
 
 
2444
void wxGenericTreeCtrl::AdjustMyScrollbars()
 
2445
{
 
2446
    if (m_anchor)
 
2447
    {
 
2448
        int x = 0, y = 0;
 
2449
        m_anchor->GetSize( x, y, this );
 
2450
        y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
 
2451
        x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
 
2452
        int x_pos = GetScrollPos( wxHORIZONTAL );
 
2453
        int y_pos = GetScrollPos( wxVERTICAL );
 
2454
        SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT,
 
2455
                       x/PIXELS_PER_UNIT, y/PIXELS_PER_UNIT,
 
2456
                       x_pos, y_pos );
 
2457
    }
 
2458
    else
 
2459
    {
 
2460
        SetScrollbars( 0, 0, 0, 0 );
 
2461
    }
 
2462
}
 
2463
 
 
2464
int wxGenericTreeCtrl::GetLineHeight(wxGenericTreeItem *item) const
 
2465
{
 
2466
    if (GetWindowStyleFlag() & wxTR_HAS_VARIABLE_ROW_HEIGHT)
 
2467
        return item->GetHeight();
 
2468
    else
 
2469
        return m_lineHeight;
 
2470
}
 
2471
 
 
2472
void wxGenericTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc)
 
2473
{
 
2474
    item->SetFont(this, dc);
 
2475
    item->CalculateSize(this, dc);
 
2476
 
 
2477
    wxCoord text_h = item->GetTextHeight();
 
2478
 
 
2479
    int image_h = 0, image_w = 0;
 
2480
    int image = item->GetCurrentImage();
 
2481
    if ( image != NO_IMAGE )
 
2482
    {
 
2483
        if ( m_imageListNormal )
 
2484
        {
 
2485
            m_imageListNormal->GetSize(image, image_w, image_h);
 
2486
            image_w += MARGIN_BETWEEN_IMAGE_AND_TEXT;
 
2487
        }
 
2488
        else
 
2489
        {
 
2490
            image = NO_IMAGE;
 
2491
        }
 
2492
    }
 
2493
 
 
2494
    int state_h = 0, state_w = 0;
 
2495
    int state = item->GetState();
 
2496
    if ( state != wxTREE_ITEMSTATE_NONE )
 
2497
    {
 
2498
        if ( m_imageListState )
 
2499
        {
 
2500
            m_imageListState->GetSize(state, state_w, state_h);
 
2501
            if ( image_w != 0 )
 
2502
                state_w += MARGIN_BETWEEN_STATE_AND_IMAGE;
 
2503
            else
 
2504
                state_w += MARGIN_BETWEEN_IMAGE_AND_TEXT;
 
2505
        }
 
2506
        else
 
2507
        {
 
2508
            state = wxTREE_ITEMSTATE_NONE;
 
2509
        }
 
2510
    }
 
2511
 
 
2512
    int total_h = GetLineHeight(item);
 
2513
    bool drawItemBackground = false,
 
2514
         hasBgColour = false;
 
2515
 
 
2516
    if ( item->IsSelected() )
 
2517
    {
 
2518
        dc.SetBrush(*(m_hasFocus ? m_hilightBrush : m_hilightUnfocusedBrush));
 
2519
        drawItemBackground = true;
 
2520
    }
 
2521
    else
 
2522
    {
 
2523
        wxColour colBg;
 
2524
        wxTreeItemAttr * const attr = item->GetAttributes();
 
2525
        if ( attr && attr->HasBackgroundColour() )
 
2526
        {
 
2527
            drawItemBackground =
 
2528
            hasBgColour = true;
 
2529
            colBg = attr->GetBackgroundColour();
 
2530
        }
 
2531
        else
 
2532
        {
 
2533
            colBg = GetBackgroundColour();
 
2534
        }
 
2535
        dc.SetBrush(wxBrush(colBg, wxBRUSHSTYLE_SOLID));
 
2536
    }
 
2537
 
 
2538
    int offset = HasFlag(wxTR_ROW_LINES) ? 1 : 0;
 
2539
 
 
2540
    if ( HasFlag(wxTR_FULL_ROW_HIGHLIGHT) )
 
2541
    {
 
2542
        int x, w, h;
 
2543
        x=0;
 
2544
        GetVirtualSize(&w, &h);
 
2545
        wxRect rect( x, item->GetY()+offset, w, total_h-offset);
 
2546
        if (!item->IsSelected())
 
2547
        {
 
2548
            dc.DrawRectangle(rect);
 
2549
        }
 
2550
        else
 
2551
        {
 
2552
            int flags = wxCONTROL_SELECTED;
 
2553
            if (m_hasFocus
 
2554
#if defined( __WXMAC__ ) && !defined(__WXUNIVERSAL__) && wxOSX_USE_CARBON // TODO CS
 
2555
                && IsControlActive( (ControlRef)GetHandle() )
 
2556
#endif
 
2557
            )
 
2558
                flags |= wxCONTROL_FOCUSED;
 
2559
            if ((item == m_current) && (m_hasFocus))
 
2560
                flags |= wxCONTROL_CURRENT;
 
2561
 
 
2562
            wxRendererNative::Get().
 
2563
                DrawItemSelectionRect(this, dc, rect, flags);
 
2564
        }
 
2565
    }
 
2566
    else // no full row highlight
 
2567
    {
 
2568
        if ( item->IsSelected() &&
 
2569
                (state != wxTREE_ITEMSTATE_NONE || image != NO_IMAGE) )
 
2570
        {
 
2571
            // If it's selected, and there's an state image or normal image,
 
2572
            // then we should take care to leave the area under the image
 
2573
            // painted in the background colour.
 
2574
            wxRect rect( item->GetX() + state_w + image_w - 2,
 
2575
                         item->GetY() + offset,
 
2576
                         item->GetWidth() - state_w - image_w + 2,
 
2577
                         total_h - offset );
 
2578
#if !defined(__WXGTK20__) && !defined(__WXMAC__)
 
2579
            dc.DrawRectangle( rect );
 
2580
#else
 
2581
            rect.x -= 1;
 
2582
            rect.width += 2;
 
2583
 
 
2584
            int flags = wxCONTROL_SELECTED;
 
2585
            if (m_hasFocus)
 
2586
                flags |= wxCONTROL_FOCUSED;
 
2587
            if ((item == m_current) && (m_hasFocus))
 
2588
                flags |= wxCONTROL_CURRENT;
 
2589
            wxRendererNative::Get().
 
2590
                DrawItemSelectionRect(this, dc, rect, flags);
 
2591
#endif
 
2592
        }
 
2593
        // On GTK+ 2, drawing a 'normal' background is wrong for themes that
 
2594
        // don't allow backgrounds to be customized. Not drawing the background,
 
2595
        // except for custom item backgrounds, works for both kinds of theme.
 
2596
        else if (drawItemBackground)
 
2597
        {
 
2598
            wxRect rect( item->GetX() + state_w + image_w - 2,
 
2599
                         item->GetY() + offset,
 
2600
                         item->GetWidth() - state_w - image_w + 2,
 
2601
                         total_h - offset );
 
2602
            if ( hasBgColour )
 
2603
            {
 
2604
                dc.DrawRectangle( rect );
 
2605
            }
 
2606
            else // no specific background colour
 
2607
            {
 
2608
                rect.x -= 1;
 
2609
                rect.width += 2;
 
2610
 
 
2611
                int flags = wxCONTROL_SELECTED;
 
2612
                if (m_hasFocus)
 
2613
                    flags |= wxCONTROL_FOCUSED;
 
2614
                if ((item == m_current) && (m_hasFocus))
 
2615
                    flags |= wxCONTROL_CURRENT;
 
2616
                wxRendererNative::Get().
 
2617
                    DrawItemSelectionRect(this, dc, rect, flags);
 
2618
            }
 
2619
        }
 
2620
    }
 
2621
 
 
2622
    if ( state != wxTREE_ITEMSTATE_NONE )
 
2623
    {
 
2624
        dc.SetClippingRegion( item->GetX(), item->GetY(), state_w, total_h );
 
2625
        m_imageListState->Draw( state, dc,
 
2626
                                item->GetX(),
 
2627
                                item->GetY() +
 
2628
                                    (total_h > state_h ? (total_h-state_h)/2
 
2629
                                                       : 0),
 
2630
                                wxIMAGELIST_DRAW_TRANSPARENT );
 
2631
        dc.DestroyClippingRegion();
 
2632
    }
 
2633
 
 
2634
    if ( image != NO_IMAGE )
 
2635
    {
 
2636
        dc.SetClippingRegion(item->GetX() + state_w, item->GetY(),
 
2637
                             image_w, total_h);
 
2638
        m_imageListNormal->Draw( image, dc,
 
2639
                                 item->GetX() + state_w,
 
2640
                                 item->GetY() +
 
2641
                                    (total_h > image_h ? (total_h-image_h)/2
 
2642
                                                       : 0),
 
2643
                                 wxIMAGELIST_DRAW_TRANSPARENT );
 
2644
        dc.DestroyClippingRegion();
 
2645
    }
 
2646
 
 
2647
    dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
 
2648
    int extraH = (total_h > text_h) ? (total_h - text_h)/2 : 0;
 
2649
    dc.DrawText( item->GetText(),
 
2650
                 (wxCoord)(state_w + image_w + item->GetX()),
 
2651
                 (wxCoord)(item->GetY() + extraH));
 
2652
 
 
2653
    // restore normal font
 
2654
    dc.SetFont( m_normalFont );
 
2655
 
 
2656
    if (item == m_dndEffectItem)
 
2657
    {
 
2658
        dc.SetPen( *wxBLACK_PEN );
 
2659
        // DnD visual effects
 
2660
        switch (m_dndEffect)
 
2661
        {
 
2662
            case BorderEffect:
 
2663
            {
 
2664
                dc.SetBrush(*wxTRANSPARENT_BRUSH);
 
2665
                int w = item->GetWidth() + 2;
 
2666
                int h = total_h + 2;
 
2667
                dc.DrawRectangle( item->GetX() - 1, item->GetY() - 1, w, h);
 
2668
                break;
 
2669
            }
 
2670
            case AboveEffect:
 
2671
            {
 
2672
                int x = item->GetX(),
 
2673
                    y = item->GetY();
 
2674
                dc.DrawLine( x, y, x + item->GetWidth(), y);
 
2675
                break;
 
2676
            }
 
2677
            case BelowEffect:
 
2678
            {
 
2679
                int x = item->GetX(),
 
2680
                    y = item->GetY();
 
2681
                y += total_h - 1;
 
2682
                dc.DrawLine( x, y, x + item->GetWidth(), y);
 
2683
                break;
 
2684
            }
 
2685
            case NoEffect:
 
2686
                break;
 
2687
        }
 
2688
    }
 
2689
}
 
2690
 
 
2691
void
 
2692
wxGenericTreeCtrl::PaintLevel(wxGenericTreeItem *item,
 
2693
                              wxDC &dc,
 
2694
                              int level,
 
2695
                              int &y)
 
2696
{
 
2697
    int x = level*m_indent;
 
2698
    if (!HasFlag(wxTR_HIDE_ROOT))
 
2699
    {
 
2700
        x += m_indent;
 
2701
    }
 
2702
    else if (level == 0)
 
2703
    {
 
2704
        // always expand hidden root
 
2705
        int origY = y;
 
2706
        wxArrayGenericTreeItems& children = item->GetChildren();
 
2707
        int count = children.GetCount();
 
2708
        if (count > 0)
 
2709
        {
 
2710
            int n = 0, oldY;
 
2711
            do {
 
2712
                oldY = y;
 
2713
                PaintLevel(children[n], dc, 1, y);
 
2714
            } while (++n < count);
 
2715
 
 
2716
            if ( !HasFlag(wxTR_NO_LINES) && HasFlag(wxTR_LINES_AT_ROOT)
 
2717
                    && count > 0 )
 
2718
            {
 
2719
                // draw line down to last child
 
2720
                origY += GetLineHeight(children[0])>>1;
 
2721
                oldY += GetLineHeight(children[n-1])>>1;
 
2722
                dc.DrawLine(3, origY, 3, oldY);
 
2723
            }
 
2724
        }
 
2725
        return;
 
2726
    }
 
2727
 
 
2728
    item->SetX(x+m_spacing);
 
2729
    item->SetY(y);
 
2730
 
 
2731
    int h = GetLineHeight(item);
 
2732
    int y_top = y;
 
2733
    int y_mid = y_top + (h>>1);
 
2734
    y += h;
 
2735
 
 
2736
    int exposed_x = dc.LogicalToDeviceX(0);
 
2737
    int exposed_y = dc.LogicalToDeviceY(y_top);
 
2738
 
 
2739
    if (IsExposed(exposed_x, exposed_y, 10000, h))  // 10000 = very much
 
2740
    {
 
2741
        const wxPen *pen =
 
2742
#ifndef __WXMAC__
 
2743
            // don't draw rect outline if we already have the
 
2744
            // background color under Mac
 
2745
            (item->IsSelected() && m_hasFocus) ? wxBLACK_PEN :
 
2746
#endif // !__WXMAC__
 
2747
            wxTRANSPARENT_PEN;
 
2748
 
 
2749
        wxColour colText;
 
2750
        if ( item->IsSelected()
 
2751
#if defined( __WXMAC__ ) && !defined(__WXUNIVERSAL__) && wxOSX_USE_CARBON // TODO CS
 
2752
            // On wxMac, if the tree doesn't have the focus we draw an empty
 
2753
            // rectangle, so we want to make sure that the text is visible
 
2754
            // against the normal background, not the highlightbackground, so
 
2755
            // don't use the highlight text colour unless we have the focus.
 
2756
             && m_hasFocus && IsControlActive( (ControlRef)GetHandle() )
 
2757
#endif
 
2758
            )
 
2759
        {
 
2760
#ifdef __WXMAC__
 
2761
            colText = *wxWHITE;
 
2762
#else
 
2763
            if (m_hasFocus)
 
2764
                colText = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
 
2765
            else
 
2766
                colText = wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOXHIGHLIGHTTEXT);
 
2767
#endif
 
2768
        }
 
2769
        else
 
2770
        {
 
2771
            wxTreeItemAttr *attr = item->GetAttributes();
 
2772
            if (attr && attr->HasTextColour())
 
2773
                colText = attr->GetTextColour();
 
2774
            else
 
2775
                colText = GetForegroundColour();
 
2776
        }
 
2777
 
 
2778
        // prepare to draw
 
2779
        dc.SetTextForeground(colText);
 
2780
        dc.SetPen(*pen);
 
2781
 
 
2782
        // draw
 
2783
        PaintItem(item, dc);
 
2784
 
 
2785
        if (HasFlag(wxTR_ROW_LINES))
 
2786
        {
 
2787
            // if the background colour is white, choose a
 
2788
            // contrasting color for the lines
 
2789
            dc.SetPen(*((GetBackgroundColour() == *wxWHITE)
 
2790
                         ? wxMEDIUM_GREY_PEN : wxWHITE_PEN));
 
2791
            dc.DrawLine(0, y_top, 10000, y_top);
 
2792
            dc.DrawLine(0, y, 10000, y);
 
2793
        }
 
2794
 
 
2795
        // restore DC objects
 
2796
        dc.SetBrush(*wxWHITE_BRUSH);
 
2797
        dc.SetPen(m_dottedPen);
 
2798
        dc.SetTextForeground(*wxBLACK);
 
2799
 
 
2800
        if ( !HasFlag(wxTR_NO_LINES) )
 
2801
        {
 
2802
            // draw the horizontal line here
 
2803
            int x_start = x;
 
2804
            if (x > (signed)m_indent)
 
2805
                x_start -= m_indent;
 
2806
            else if (HasFlag(wxTR_LINES_AT_ROOT))
 
2807
                x_start = 3;
 
2808
            dc.DrawLine(x_start, y_mid, x + m_spacing, y_mid);
 
2809
        }
 
2810
 
 
2811
        // should the item show a button?
 
2812
        if ( item->HasPlus() && HasButtons() )
 
2813
        {
 
2814
            if ( m_imageListButtons )
 
2815
            {
 
2816
                // draw the image button here
 
2817
                int image_h = 0,
 
2818
                    image_w = 0;
 
2819
                int image = item->IsExpanded() ? wxTreeItemIcon_Expanded
 
2820
                                               : wxTreeItemIcon_Normal;
 
2821
                if ( item->IsSelected() )
 
2822
                    image += wxTreeItemIcon_Selected - wxTreeItemIcon_Normal;
 
2823
 
 
2824
                m_imageListButtons->GetSize(image, image_w, image_h);
 
2825
                int xx = x - image_w/2;
 
2826
                int yy = y_mid - image_h/2;
 
2827
 
 
2828
                wxDCClipper clip(dc, xx, yy, image_w, image_h);
 
2829
                m_imageListButtons->Draw(image, dc, xx, yy,
 
2830
                                         wxIMAGELIST_DRAW_TRANSPARENT);
 
2831
            }
 
2832
            else // no custom buttons
 
2833
            {
 
2834
                static const int wImage = 9;
 
2835
                static const int hImage = 9;
 
2836
 
 
2837
                int flag = 0;
 
2838
                if (item->IsExpanded())
 
2839
                    flag |= wxCONTROL_EXPANDED;
 
2840
                if (item == m_underMouse)
 
2841
                    flag |= wxCONTROL_CURRENT;
 
2842
 
 
2843
                wxRendererNative::Get().DrawTreeItemButton
 
2844
                                        (
 
2845
                                            this,
 
2846
                                            dc,
 
2847
                                            wxRect(x - wImage/2,
 
2848
                                                   y_mid - hImage/2,
 
2849
                                                   wImage, hImage),
 
2850
                                            flag
 
2851
                                        );
 
2852
            }
 
2853
        }
 
2854
    }
 
2855
 
 
2856
    if (item->IsExpanded())
 
2857
    {
 
2858
        wxArrayGenericTreeItems& children = item->GetChildren();
 
2859
        int count = children.GetCount();
 
2860
        if (count > 0)
 
2861
        {
 
2862
            int n = 0, oldY;
 
2863
            ++level;
 
2864
            do {
 
2865
                oldY = y;
 
2866
                PaintLevel(children[n], dc, level, y);
 
2867
            } while (++n < count);
 
2868
 
 
2869
            if (!HasFlag(wxTR_NO_LINES) && count > 0)
 
2870
            {
 
2871
                // draw line down to last child
 
2872
                oldY += GetLineHeight(children[n-1])>>1;
 
2873
                if (HasButtons()) y_mid += 5;
 
2874
 
 
2875
                // Only draw the portion of the line that is visible, in case
 
2876
                // it is huge
 
2877
                wxCoord xOrigin=0, yOrigin=0, width, height;
 
2878
                dc.GetDeviceOrigin(&xOrigin, &yOrigin);
 
2879
                yOrigin = abs(yOrigin);
 
2880
                GetClientSize(&width, &height);
 
2881
 
 
2882
                // Move end points to the beginning/end of the view?
 
2883
                if (y_mid < yOrigin)
 
2884
                    y_mid = yOrigin;
 
2885
                if (oldY > yOrigin + height)
 
2886
                    oldY = yOrigin + height;
 
2887
 
 
2888
                // after the adjustments if y_mid is larger than oldY then the
 
2889
                // line isn't visible at all so don't draw anything
 
2890
                if (y_mid < oldY)
 
2891
                    dc.DrawLine(x, y_mid, x, oldY);
 
2892
            }
 
2893
        }
 
2894
    }
 
2895
}
 
2896
 
 
2897
void wxGenericTreeCtrl::DrawDropEffect(wxGenericTreeItem *item)
 
2898
{
 
2899
    if ( item )
 
2900
    {
 
2901
        if ( item->HasPlus() )
 
2902
        {
 
2903
            // it's a folder, indicate it by a border
 
2904
            DrawBorder(item);
 
2905
        }
 
2906
        else
 
2907
        {
 
2908
            // draw a line under the drop target because the item will be
 
2909
            // dropped there
 
2910
            DrawLine(item, !m_dropEffectAboveItem );
 
2911
        }
 
2912
 
 
2913
        SetCursor(*wxSTANDARD_CURSOR);
 
2914
    }
 
2915
    else
 
2916
    {
 
2917
        // can't drop here
 
2918
        SetCursor(wxCURSOR_NO_ENTRY);
 
2919
    }
 
2920
}
 
2921
 
 
2922
void wxGenericTreeCtrl::DrawBorder(const wxTreeItemId &item)
 
2923
{
 
2924
    wxCHECK_RET( item.IsOk(), "invalid item in wxGenericTreeCtrl::DrawLine" );
 
2925
 
 
2926
    wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
 
2927
 
 
2928
    if (m_dndEffect == NoEffect)
 
2929
    {
 
2930
        m_dndEffect = BorderEffect;
 
2931
        m_dndEffectItem = i;
 
2932
    }
 
2933
    else
 
2934
    {
 
2935
        m_dndEffect = NoEffect;
 
2936
        m_dndEffectItem = NULL;
 
2937
    }
 
2938
 
 
2939
    wxRect rect( i->GetX()-1, i->GetY()-1, i->GetWidth()+2, GetLineHeight(i)+2 );
 
2940
    CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
 
2941
    RefreshRect( rect );
 
2942
}
 
2943
 
 
2944
void wxGenericTreeCtrl::DrawLine(const wxTreeItemId &item, bool below)
 
2945
{
 
2946
    wxCHECK_RET( item.IsOk(), "invalid item in wxGenericTreeCtrl::DrawLine" );
 
2947
 
 
2948
    wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
 
2949
 
 
2950
    if (m_dndEffect == NoEffect)
 
2951
    {
 
2952
        if (below)
 
2953
            m_dndEffect = BelowEffect;
 
2954
        else
 
2955
            m_dndEffect = AboveEffect;
 
2956
        m_dndEffectItem = i;
 
2957
    }
 
2958
    else
 
2959
    {
 
2960
        m_dndEffect = NoEffect;
 
2961
        m_dndEffectItem = NULL;
 
2962
    }
 
2963
 
 
2964
    wxRect rect( i->GetX()-1, i->GetY()-1, i->GetWidth()+2, GetLineHeight(i)+2 );
 
2965
    CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
 
2966
    RefreshRect( rect );
 
2967
}
 
2968
 
 
2969
// -----------------------------------------------------------------------------
 
2970
// wxWidgets callbacks
 
2971
// -----------------------------------------------------------------------------
 
2972
 
 
2973
void wxGenericTreeCtrl::OnSize( wxSizeEvent &event )
 
2974
{
 
2975
#ifdef __WXGTK__
 
2976
    if (HasFlag( wxTR_FULL_ROW_HIGHLIGHT) && m_current)
 
2977
        RefreshLine( m_current );
 
2978
#endif
 
2979
 
 
2980
    event.Skip(true);
 
2981
}
 
2982
 
 
2983
void wxGenericTreeCtrl::OnPaint( wxPaintEvent &WXUNUSED(event) )
 
2984
{
 
2985
    wxPaintDC dc(this);
 
2986
    PrepareDC( dc );
 
2987
 
 
2988
    if ( !m_anchor)
 
2989
        return;
 
2990
 
 
2991
    dc.SetFont( m_normalFont );
 
2992
    dc.SetPen( m_dottedPen );
 
2993
 
 
2994
    // this is now done dynamically
 
2995
    //if(GetImageList() == NULL)
 
2996
    // m_lineHeight = (int)(dc.GetCharHeight() + 4);
 
2997
 
 
2998
    int y = 2;
 
2999
    PaintLevel( m_anchor, dc, 0, y );
 
3000
}
 
3001
 
 
3002
void wxGenericTreeCtrl::OnSetFocus( wxFocusEvent &event )
 
3003
{
 
3004
    m_hasFocus = true;
 
3005
 
 
3006
    RefreshSelected();
 
3007
 
 
3008
    event.Skip();
 
3009
}
 
3010
 
 
3011
void wxGenericTreeCtrl::OnKillFocus( wxFocusEvent &event )
 
3012
{
 
3013
    m_hasFocus = false;
 
3014
 
 
3015
    RefreshSelected();
 
3016
 
 
3017
    event.Skip();
 
3018
}
 
3019
 
 
3020
void wxGenericTreeCtrl::OnKeyDown( wxKeyEvent &event )
 
3021
{
 
3022
    // send a tree event
 
3023
    wxTreeEvent te( wxEVT_COMMAND_TREE_KEY_DOWN, this);
 
3024
    te.m_evtKey = event;
 
3025
    if ( GetEventHandler()->ProcessEvent( te ) )
 
3026
        return;
 
3027
 
 
3028
    event.Skip();
 
3029
}
 
3030
 
 
3031
void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
 
3032
{
 
3033
    if ( (m_current == 0) || (m_key_current == 0) )
 
3034
    {
 
3035
        event.Skip();
 
3036
        return;
 
3037
    }
 
3038
 
 
3039
    // how should the selection work for this event?
 
3040
    bool is_multiple, extended_select, unselect_others;
 
3041
    EventFlagsToSelType(GetWindowStyleFlag(),
 
3042
                        event.ShiftDown(),
 
3043
                        event.CmdDown(),
 
3044
                        is_multiple, extended_select, unselect_others);
 
3045
 
 
3046
    if (GetLayoutDirection() == wxLayout_RightToLeft)
 
3047
    {
 
3048
        if (event.GetKeyCode() == WXK_RIGHT)
 
3049
            event.m_keyCode = WXK_LEFT;
 
3050
        else if (event.GetKeyCode() == WXK_LEFT)
 
3051
            event.m_keyCode = WXK_RIGHT;
 
3052
    }
 
3053
 
 
3054
    // + : Expand
 
3055
    // - : Collaspe
 
3056
    // * : Expand all/Collapse all
 
3057
    // ' ' | return : activate
 
3058
    // up    : go up (not last children!)
 
3059
    // down  : go down
 
3060
    // left  : go to parent
 
3061
    // right : open if parent and go next
 
3062
    // home  : go to root
 
3063
    // end   : go to last item without opening parents
 
3064
    // alnum : start or continue searching for the item with this prefix
 
3065
    int keyCode = event.GetKeyCode();
 
3066
 
 
3067
#ifdef __WXOSX__
 
3068
    // Make the keys work as they do in the native control:
 
3069
    // right => expand
 
3070
    // left => collapse if current item is expanded
 
3071
    if (keyCode == WXK_RIGHT)
 
3072
    {
 
3073
        keyCode = '+';
 
3074
    }
 
3075
    else if (keyCode == WXK_LEFT && IsExpanded(m_current))
 
3076
    {
 
3077
        keyCode = '-';
 
3078
    }
 
3079
#endif // __WXOSX__
 
3080
 
 
3081
    switch ( keyCode )
 
3082
    {
 
3083
        case '+':
 
3084
        case WXK_ADD:
 
3085
            if (m_current->HasPlus() && !IsExpanded(m_current))
 
3086
            {
 
3087
                Expand(m_current);
 
3088
            }
 
3089
            break;
 
3090
 
 
3091
        case '*':
 
3092
        case WXK_MULTIPLY:
 
3093
            if ( !IsExpanded(m_current) )
 
3094
            {
 
3095
                // expand all
 
3096
                ExpandAllChildren(m_current);
 
3097
                break;
 
3098
            }
 
3099
            //else: fall through to Collapse() it
 
3100
 
 
3101
        case '-':
 
3102
        case WXK_SUBTRACT:
 
3103
            if (IsExpanded(m_current))
 
3104
            {
 
3105
                Collapse(m_current);
 
3106
            }
 
3107
            break;
 
3108
 
 
3109
        case WXK_MENU:
 
3110
            {
 
3111
                // Use the item's bounding rectangle to determine position for
 
3112
                // the event
 
3113
                wxRect ItemRect;
 
3114
                GetBoundingRect(m_current, ItemRect, true);
 
3115
 
 
3116
                wxTreeEvent
 
3117
                    eventMenu(wxEVT_COMMAND_TREE_ITEM_MENU, this, m_current);
 
3118
                // Use the left edge, vertical middle
 
3119
                eventMenu.m_pointDrag = wxPoint(ItemRect.GetX(),
 
3120
                                                ItemRect.GetY() +
 
3121
                                                    ItemRect.GetHeight() / 2);
 
3122
                GetEventHandler()->ProcessEvent( eventMenu );
 
3123
            }
 
3124
            break;
 
3125
 
 
3126
        case ' ':
 
3127
        case WXK_RETURN:
 
3128
            if ( !event.HasModifiers() )
 
3129
            {
 
3130
                wxTreeEvent
 
3131
                   eventAct(wxEVT_COMMAND_TREE_ITEM_ACTIVATED, this, m_current);
 
3132
                GetEventHandler()->ProcessEvent( eventAct );
 
3133
            }
 
3134
 
 
3135
            // in any case, also generate the normal key event for this key,
 
3136
            // even if we generated the ACTIVATED event above: this is what
 
3137
            // wxMSW does and it makes sense because you might not want to
 
3138
            // process ACTIVATED event at all and handle Space and Return
 
3139
            // directly (and differently) which would be impossible otherwise
 
3140
            event.Skip();
 
3141
            break;
 
3142
 
 
3143
            // up goes to the previous sibling or to the last
 
3144
            // of its children if it's expanded
 
3145
        case WXK_UP:
 
3146
            {
 
3147
                wxTreeItemId prev = GetPrevSibling( m_key_current );
 
3148
                if (!prev)
 
3149
                {
 
3150
                    prev = GetItemParent( m_key_current );
 
3151
                    if ((prev == GetRootItem()) && HasFlag(wxTR_HIDE_ROOT))
 
3152
                    {
 
3153
                        break;  // don't go to root if it is hidden
 
3154
                    }
 
3155
                    if (prev)
 
3156
                    {
 
3157
                        wxTreeItemIdValue cookie;
 
3158
                        wxTreeItemId current = m_key_current;
 
3159
                        // TODO: Huh?  If we get here, we'd better be the first
 
3160
                        // child of our parent.  How else could it be?
 
3161
                        if (current == GetFirstChild( prev, cookie ))
 
3162
                        {
 
3163
                            // otherwise we return to where we came from
 
3164
                            DoSelectItem(prev,
 
3165
                                         unselect_others,
 
3166
                                         extended_select);
 
3167
                            m_key_current = (wxGenericTreeItem*) prev.m_pItem;
 
3168
                            break;
 
3169
                        }
 
3170
                    }
 
3171
                }
 
3172
                if (prev)
 
3173
                {
 
3174
                    while ( IsExpanded(prev) && HasChildren(prev) )
 
3175
                    {
 
3176
                        wxTreeItemId child = GetLastChild(prev);
 
3177
                        if ( child )
 
3178
                        {
 
3179
                            prev = child;
 
3180
                        }
 
3181
                    }
 
3182
 
 
3183
                    DoSelectItem( prev, unselect_others, extended_select );
 
3184
                    m_key_current=(wxGenericTreeItem*) prev.m_pItem;
 
3185
                }
 
3186
            }
 
3187
            break;
 
3188
 
 
3189
            // left arrow goes to the parent
 
3190
        case WXK_LEFT:
 
3191
            {
 
3192
                wxTreeItemId prev = GetItemParent( m_current );
 
3193
                if ((prev == GetRootItem()) && HasFlag(wxTR_HIDE_ROOT))
 
3194
                {
 
3195
                    // don't go to root if it is hidden
 
3196
                    prev = GetPrevSibling( m_current );
 
3197
                }
 
3198
                if (prev)
 
3199
                {
 
3200
                    DoSelectItem( prev, unselect_others, extended_select );
 
3201
                }
 
3202
            }
 
3203
            break;
 
3204
 
 
3205
        case WXK_RIGHT:
 
3206
            // this works the same as the down arrow except that we
 
3207
            // also expand the item if it wasn't expanded yet
 
3208
            if (m_current != GetRootItem().m_pItem || !HasFlag(wxTR_HIDE_ROOT))
 
3209
                Expand(m_current);
 
3210
            //else: don't try to expand hidden root item (which can be the
 
3211
            //      current one when the tree is empty)
 
3212
 
 
3213
            // fall through
 
3214
 
 
3215
        case WXK_DOWN:
 
3216
            {
 
3217
                if (IsExpanded(m_key_current) && HasChildren(m_key_current))
 
3218
                {
 
3219
                    wxTreeItemIdValue cookie;
 
3220
                    wxTreeItemId child = GetFirstChild( m_key_current, cookie );
 
3221
                    if ( !child )
 
3222
                        break;
 
3223
 
 
3224
                    DoSelectItem( child, unselect_others, extended_select );
 
3225
                    m_key_current=(wxGenericTreeItem*) child.m_pItem;
 
3226
                }
 
3227
                else
 
3228
                {
 
3229
                    wxTreeItemId next = GetNextSibling( m_key_current );
 
3230
                    if (!next)
 
3231
                    {
 
3232
                        wxTreeItemId current = m_key_current;
 
3233
                        while (current.IsOk() && !next)
 
3234
                        {
 
3235
                            current = GetItemParent( current );
 
3236
                            if (current) next = GetNextSibling( current );
 
3237
                        }
 
3238
                    }
 
3239
                    if (next)
 
3240
                    {
 
3241
                        DoSelectItem( next, unselect_others, extended_select );
 
3242
                        m_key_current=(wxGenericTreeItem*) next.m_pItem;
 
3243
                    }
 
3244
                }
 
3245
            }
 
3246
            break;
 
3247
 
 
3248
            // <End> selects the last visible tree item
 
3249
        case WXK_END:
 
3250
            {
 
3251
                wxTreeItemId last = GetRootItem();
 
3252
 
 
3253
                while ( last.IsOk() && IsExpanded(last) )
 
3254
                {
 
3255
                    wxTreeItemId lastChild = GetLastChild(last);
 
3256
 
 
3257
                    // it may happen if the item was expanded but then all of
 
3258
                    // its children have been deleted - so IsExpanded() returned
 
3259
                    // true, but GetLastChild() returned invalid item
 
3260
                    if ( !lastChild )
 
3261
                        break;
 
3262
 
 
3263
                    last = lastChild;
 
3264
                }
 
3265
 
 
3266
                if ( last.IsOk() )
 
3267
                {
 
3268
                    DoSelectItem( last, unselect_others, extended_select );
 
3269
                }
 
3270
            }
 
3271
            break;
 
3272
 
 
3273
            // <Home> selects the root item
 
3274
        case WXK_HOME:
 
3275
            {
 
3276
                wxTreeItemId prev = GetRootItem();
 
3277
                if (!prev)
 
3278
                    break;
 
3279
 
 
3280
                if ( HasFlag(wxTR_HIDE_ROOT) )
 
3281
                {
 
3282
                    wxTreeItemIdValue cookie;
 
3283
                    prev = GetFirstChild(prev, cookie);
 
3284
                    if (!prev)
 
3285
                        break;
 
3286
                }
 
3287
 
 
3288
                DoSelectItem( prev, unselect_others, extended_select );
 
3289
            }
 
3290
            break;
 
3291
 
 
3292
        default:
 
3293
            // do not use wxIsalnum() here
 
3294
            if ( !event.HasModifiers() &&
 
3295
                 ((keyCode >= '0' && keyCode <= '9') ||
 
3296
                  (keyCode >= 'a' && keyCode <= 'z') ||
 
3297
                  (keyCode >= 'A' && keyCode <= 'Z' )))
 
3298
            {
 
3299
                // find the next item starting with the given prefix
 
3300
                wxChar ch = (wxChar)keyCode;
 
3301
 
 
3302
                wxTreeItemId id = FindItem(m_current, m_findPrefix + ch);
 
3303
                if ( !id.IsOk() )
 
3304
                {
 
3305
                    // no such item
 
3306
                    break;
 
3307
                }
 
3308
 
 
3309
                SelectItem(id);
 
3310
 
 
3311
                m_findPrefix += ch;
 
3312
 
 
3313
                // also start the timer to reset the current prefix if the user
 
3314
                // doesn't press any more alnum keys soon -- we wouldn't want
 
3315
                // to use this prefix for a new item search
 
3316
                if ( !m_findTimer )
 
3317
                {
 
3318
                    m_findTimer = new wxTreeFindTimer(this);
 
3319
                }
 
3320
 
 
3321
                m_findTimer->Start(wxTreeFindTimer::DELAY, wxTIMER_ONE_SHOT);
 
3322
            }
 
3323
            else
 
3324
            {
 
3325
                event.Skip();
 
3326
            }
 
3327
    }
 
3328
}
 
3329
 
 
3330
wxTreeItemId
 
3331
wxGenericTreeCtrl::DoTreeHitTest(const wxPoint& point, int& flags) const
 
3332
{
 
3333
    int w, h;
 
3334
    GetSize(&w, &h);
 
3335
    flags=0;
 
3336
    if (point.x<0) flags |= wxTREE_HITTEST_TOLEFT;
 
3337
    if (point.x>w) flags |= wxTREE_HITTEST_TORIGHT;
 
3338
    if (point.y<0) flags |= wxTREE_HITTEST_ABOVE;
 
3339
    if (point.y>h) flags |= wxTREE_HITTEST_BELOW;
 
3340
    if (flags) return wxTreeItemId();
 
3341
 
 
3342
    if (m_anchor == NULL)
 
3343
    {
 
3344
        flags = wxTREE_HITTEST_NOWHERE;
 
3345
        return wxTreeItemId();
 
3346
    }
 
3347
 
 
3348
    wxGenericTreeItem *hit =  m_anchor->HitTest(CalcUnscrolledPosition(point),
 
3349
                                                this, flags, 0);
 
3350
    if (hit == NULL)
 
3351
    {
 
3352
        flags = wxTREE_HITTEST_NOWHERE;
 
3353
        return wxTreeItemId();
 
3354
    }
 
3355
    return hit;
 
3356
}
 
3357
 
 
3358
// get the bounding rectangle of the item (or of its label only)
 
3359
bool wxGenericTreeCtrl::GetBoundingRect(const wxTreeItemId& item,
 
3360
                                        wxRect& rect,
 
3361
                                        bool textOnly) const
 
3362
{
 
3363
    wxCHECK_MSG( item.IsOk(), false,
 
3364
                 "invalid item in wxGenericTreeCtrl::GetBoundingRect" );
 
3365
 
 
3366
    wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
 
3367
 
 
3368
    if ( textOnly )
 
3369
    {
 
3370
        int image_h = 0, image_w = 0;
 
3371
        int image = ((wxGenericTreeItem*) item.m_pItem)->GetCurrentImage();
 
3372
        if ( image != NO_IMAGE && m_imageListNormal )
 
3373
        {
 
3374
            m_imageListNormal->GetSize( image, image_w, image_h );
 
3375
            image_w += MARGIN_BETWEEN_IMAGE_AND_TEXT;
 
3376
        }
 
3377
 
 
3378
        int state_h = 0, state_w = 0;
 
3379
        int state = ((wxGenericTreeItem*) item.m_pItem)->GetState();
 
3380
        if ( state != wxTREE_ITEMSTATE_NONE && m_imageListState )
 
3381
        {
 
3382
            m_imageListState->GetSize( state, state_w, state_h );
 
3383
            if ( image_w != 0 )
 
3384
                state_w += MARGIN_BETWEEN_STATE_AND_IMAGE;
 
3385
            else
 
3386
                state_w += MARGIN_BETWEEN_IMAGE_AND_TEXT;
 
3387
        }
 
3388
 
 
3389
        rect.x = i->GetX() + state_w + image_w;
 
3390
        rect.width = i->GetWidth() - state_w - image_w;
 
3391
 
 
3392
    }
 
3393
    else // the entire line
 
3394
    {
 
3395
        rect.x = 0;
 
3396
        rect.width = GetClientSize().x;
 
3397
    }
 
3398
 
 
3399
    rect.y = i->GetY();
 
3400
    rect.height = GetLineHeight(i);
 
3401
 
 
3402
    // we have to return the logical coordinates, not physical ones
 
3403
    rect.SetTopLeft(CalcScrolledPosition(rect.GetTopLeft()));
 
3404
 
 
3405
    return true;
 
3406
}
 
3407
 
 
3408
wxTextCtrl *wxGenericTreeCtrl::EditLabel(const wxTreeItemId& item,
 
3409
                                  wxClassInfo * WXUNUSED(textCtrlClass))
 
3410
{
 
3411
    wxCHECK_MSG( item.IsOk(), NULL, wxT("can't edit an invalid item") );
 
3412
 
 
3413
    wxGenericTreeItem *itemEdit = (wxGenericTreeItem *)item.m_pItem;
 
3414
 
 
3415
    wxTreeEvent te(wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, this, itemEdit);
 
3416
    if ( GetEventHandler()->ProcessEvent( te ) && !te.IsAllowed() )
 
3417
    {
 
3418
        // vetoed by user
 
3419
        return NULL;
 
3420
    }
 
3421
 
 
3422
    // We have to call this here because the label in
 
3423
    // question might just have been added and no screen
 
3424
    // update taken place.
 
3425
    if ( m_dirty )
 
3426
#if defined( __WXMSW__ ) || defined(__WXMAC__)
 
3427
        Update();
 
3428
#else
 
3429
        DoDirtyProcessing();
 
3430
#endif
 
3431
 
 
3432
    // TODO: use textCtrlClass here to create the control of correct class
 
3433
    m_textCtrl = new wxTreeTextCtrl(this, itemEdit);
 
3434
 
 
3435
    m_textCtrl->SetFocus();
 
3436
 
 
3437
    return m_textCtrl;
 
3438
}
 
3439
 
 
3440
// returns a pointer to the text edit control if the item is being
 
3441
// edited, NULL otherwise (it's assumed that no more than one item may
 
3442
// be edited simultaneously)
 
3443
wxTextCtrl* wxGenericTreeCtrl::GetEditControl() const
 
3444
{
 
3445
    return m_textCtrl;
 
3446
}
 
3447
 
 
3448
void wxGenericTreeCtrl::EndEditLabel(const wxTreeItemId& WXUNUSED(item),
 
3449
                                     bool discardChanges)
 
3450
{
 
3451
    wxCHECK_RET( m_textCtrl, wxT("not editing label") );
 
3452
 
 
3453
    m_textCtrl->EndEdit(discardChanges);
 
3454
}
 
3455
 
 
3456
bool wxGenericTreeCtrl::OnRenameAccept(wxGenericTreeItem *item,
 
3457
                                       const wxString& value)
 
3458
{
 
3459
    wxTreeEvent le(wxEVT_COMMAND_TREE_END_LABEL_EDIT, this, item);
 
3460
    le.m_label = value;
 
3461
    le.m_editCancelled = false;
 
3462
 
 
3463
    return !GetEventHandler()->ProcessEvent( le ) || le.IsAllowed();
 
3464
}
 
3465
 
 
3466
void wxGenericTreeCtrl::OnRenameCancelled(wxGenericTreeItem *item)
 
3467
{
 
3468
    // let owner know that the edit was cancelled
 
3469
    wxTreeEvent le(wxEVT_COMMAND_TREE_END_LABEL_EDIT, this, item);
 
3470
    le.m_label = wxEmptyString;
 
3471
    le.m_editCancelled = true;
 
3472
 
 
3473
    GetEventHandler()->ProcessEvent( le );
 
3474
}
 
3475
 
 
3476
void wxGenericTreeCtrl::OnRenameTimer()
 
3477
{
 
3478
    EditLabel( m_current );
 
3479
}
 
3480
 
 
3481
void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event )
 
3482
{
 
3483
    if ( !m_anchor )return;
 
3484
 
 
3485
    wxPoint pt = CalcUnscrolledPosition(event.GetPosition());
 
3486
 
 
3487
    // Is the mouse over a tree item button?
 
3488
    int flags = 0;
 
3489
    wxGenericTreeItem *thisItem = m_anchor->HitTest(pt, this, flags, 0);
 
3490
    wxGenericTreeItem *underMouse = thisItem;
 
3491
#if wxUSE_TOOLTIPS
 
3492
    bool underMouseChanged = (underMouse != m_underMouse) ;
 
3493
#endif // wxUSE_TOOLTIPS
 
3494
 
 
3495
    if ((underMouse) &&
 
3496
        (flags & wxTREE_HITTEST_ONITEMBUTTON) &&
 
3497
        (!event.LeftIsDown()) &&
 
3498
        (!m_isDragging) &&
 
3499
        (!m_renameTimer || !m_renameTimer->IsRunning()))
 
3500
    {
 
3501
    }
 
3502
    else
 
3503
    {
 
3504
        underMouse = NULL;
 
3505
    }
 
3506
 
 
3507
    if (underMouse != m_underMouse)
 
3508
    {
 
3509
         if (m_underMouse)
 
3510
         {
 
3511
            // unhighlight old item
 
3512
            wxGenericTreeItem *tmp = m_underMouse;
 
3513
            m_underMouse = NULL;
 
3514
            RefreshLine( tmp );
 
3515
         }
 
3516
 
 
3517
         m_underMouse = underMouse;
 
3518
         if (m_underMouse)
 
3519
            RefreshLine( m_underMouse );
 
3520
    }
 
3521
 
 
3522
#if wxUSE_TOOLTIPS
 
3523
    // Determines what item we are hovering over and need a tooltip for
 
3524
    wxTreeItemId hoverItem = thisItem;
 
3525
 
 
3526
    // We do not want a tooltip if we are dragging, or if the rename timer is
 
3527
    // running
 
3528
    if ( underMouseChanged &&
 
3529
            hoverItem.IsOk() &&
 
3530
              !m_isDragging &&
 
3531
                (!m_renameTimer || !m_renameTimer->IsRunning()) )
 
3532
    {
 
3533
        // Ask the tree control what tooltip (if any) should be shown
 
3534
        wxTreeEvent
 
3535
            hevent(wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP,  this, hoverItem);
 
3536
 
 
3537
        if ( GetEventHandler()->ProcessEvent(hevent) && hevent.IsAllowed() )
 
3538
        {
 
3539
            SetToolTip(hevent.m_label);
 
3540
        }
 
3541
    }
 
3542
#endif
 
3543
 
 
3544
    // we process left mouse up event (enables in-place edit), middle/right down
 
3545
    // (pass to the user code), left dbl click (activate item) and
 
3546
    // dragging/moving events for items drag-and-drop
 
3547
    if ( !(event.LeftDown() ||
 
3548
           event.LeftUp() ||
 
3549
           event.MiddleDown() ||
 
3550
           event.RightDown() ||
 
3551
           event.LeftDClick() ||
 
3552
           event.Dragging() ||
 
3553
           ((event.Moving() || event.RightUp()) && m_isDragging)) )
 
3554
    {
 
3555
        event.Skip();
 
3556
 
 
3557
        return;
 
3558
    }
 
3559
 
 
3560
 
 
3561
    flags = 0;
 
3562
    wxGenericTreeItem *item = m_anchor->HitTest(pt, this, flags, 0);
 
3563
 
 
3564
    if ( event.Dragging() && !m_isDragging )
 
3565
    {
 
3566
        if (m_dragCount == 0)
 
3567
            m_dragStart = pt;
 
3568
 
 
3569
        m_dragCount++;
 
3570
 
 
3571
        if (m_dragCount != 3)
 
3572
        {
 
3573
            // wait until user drags a bit further...
 
3574
            return;
 
3575
        }
 
3576
 
 
3577
        wxEventType command = event.RightIsDown()
 
3578
                              ? wxEVT_COMMAND_TREE_BEGIN_RDRAG
 
3579
                              : wxEVT_COMMAND_TREE_BEGIN_DRAG;
 
3580
 
 
3581
        wxTreeEvent nevent(command,  this, m_current);
 
3582
        nevent.SetPoint(CalcScrolledPosition(pt));
 
3583
 
 
3584
        // by default the dragging is not supported, the user code must
 
3585
        // explicitly allow the event for it to take place
 
3586
        nevent.Veto();
 
3587
 
 
3588
        if ( GetEventHandler()->ProcessEvent(nevent) && nevent.IsAllowed() )
 
3589
        {
 
3590
            // we're going to drag this item
 
3591
            m_isDragging = true;
 
3592
 
 
3593
            // remember the old cursor because we will change it while
 
3594
            // dragging
 
3595
            m_oldCursor = m_cursor;
 
3596
 
 
3597
            // in a single selection control, hide the selection temporarily
 
3598
            if ( !(GetWindowStyleFlag() & wxTR_MULTIPLE) )
 
3599
            {
 
3600
                m_oldSelection = (wxGenericTreeItem*) GetSelection().m_pItem;
 
3601
 
 
3602
                if ( m_oldSelection )
 
3603
                {
 
3604
                    m_oldSelection->SetHilight(false);
 
3605
                    RefreshLine(m_oldSelection);
 
3606
                }
 
3607
            }
 
3608
 
 
3609
            CaptureMouse();
 
3610
        }
 
3611
    }
 
3612
    else if ( event.Dragging() )
 
3613
    {
 
3614
        if ( item != m_dropTarget )
 
3615
        {
 
3616
            // unhighlight the previous drop target
 
3617
            DrawDropEffect(m_dropTarget);
 
3618
 
 
3619
            m_dropTarget = item;
 
3620
 
 
3621
            // highlight the current drop target if any
 
3622
            DrawDropEffect(m_dropTarget);
 
3623
 
 
3624
#if defined(__WXMSW__) || defined(__WXMAC__) || defined(__WXGTK20__)
 
3625
            Update();
 
3626
#else
 
3627
            // TODO: remove this call or use wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI)
 
3628
            //       instead (needs to be tested!)
 
3629
            wxYieldIfNeeded();
 
3630
#endif
 
3631
        }
 
3632
    }
 
3633
    else if ( (event.LeftUp() || event.RightUp()) && m_isDragging )
 
3634
    {
 
3635
        ReleaseMouse();
 
3636
 
 
3637
        // erase the highlighting
 
3638
        DrawDropEffect(m_dropTarget);
 
3639
 
 
3640
        if ( m_oldSelection )
 
3641
        {
 
3642
            m_oldSelection->SetHilight(true);
 
3643
            RefreshLine(m_oldSelection);
 
3644
            m_oldSelection = NULL;
 
3645
        }
 
3646
 
 
3647
        // generate the drag end event
 
3648
        wxTreeEvent eventEndDrag(wxEVT_COMMAND_TREE_END_DRAG,  this, item);
 
3649
 
 
3650
        eventEndDrag.m_pointDrag = CalcScrolledPosition(pt);
 
3651
 
 
3652
        (void)GetEventHandler()->ProcessEvent(eventEndDrag);
 
3653
 
 
3654
        m_isDragging = false;
 
3655
        m_dropTarget = NULL;
 
3656
 
 
3657
        SetCursor(m_oldCursor);
 
3658
 
 
3659
#if defined( __WXMSW__ ) || defined(__WXMAC__) || defined(__WXGTK20__)
 
3660
        Update();
 
3661
#else
 
3662
        // TODO: remove this call or use wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI)
 
3663
        //       instead (needs to be tested!)
 
3664
        wxYieldIfNeeded();
 
3665
#endif
 
3666
    }
 
3667
    else
 
3668
    {
 
3669
        // If we got to this point, we are not dragging or moving the mouse.
 
3670
        // Because the code in carbon/toplevel.cpp will only set focus to the
 
3671
        // tree if we skip for EVT_LEFT_DOWN, we MUST skip this event here for
 
3672
        // focus to work.
 
3673
        // We skip even if we didn't hit an item because we still should
 
3674
        // restore focus to the tree control even if we didn't exactly hit an
 
3675
        // item.
 
3676
        if ( event.LeftDown() )
 
3677
        {
 
3678
            event.Skip();
 
3679
        }
 
3680
 
 
3681
        // here we process only the messages which happen on tree items
 
3682
 
 
3683
        m_dragCount = 0;
 
3684
 
 
3685
        if (item == NULL) return;  /* we hit the blank area */
 
3686
 
 
3687
        if ( event.RightDown() )
 
3688
        {
 
3689
            // If the item is already selected, do not update the selection.
 
3690
            // Multi-selections should not be cleared if a selected item is
 
3691
            // clicked.
 
3692
            if (!IsSelected(item))
 
3693
            {
 
3694
                DoSelectItem(item, true, false);
 
3695
            }
 
3696
 
 
3697
            wxTreeEvent
 
3698
                nevent(wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK,  this, item);
 
3699
            nevent.m_pointDrag = CalcScrolledPosition(pt);
 
3700
            event.Skip(!GetEventHandler()->ProcessEvent(nevent));
 
3701
 
 
3702
            // Consistent with MSW (for now), send the ITEM_MENU *after*
 
3703
            // the RIGHT_CLICK event. TODO: This behaviour may change.
 
3704
            wxTreeEvent nevent2(wxEVT_COMMAND_TREE_ITEM_MENU,  this, item);
 
3705
            nevent2.m_pointDrag = CalcScrolledPosition(pt);
 
3706
            GetEventHandler()->ProcessEvent(nevent2);
 
3707
        }
 
3708
        else if ( event.MiddleDown() )
 
3709
        {
 
3710
            wxTreeEvent
 
3711
                nevent(wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK,  this, item);
 
3712
            nevent.m_pointDrag = CalcScrolledPosition(pt);
 
3713
            event.Skip(!GetEventHandler()->ProcessEvent(nevent));
 
3714
        }
 
3715
        else if ( event.LeftUp() )
 
3716
        {
 
3717
            if (flags & wxTREE_HITTEST_ONITEMSTATEICON)
 
3718
            {
 
3719
                wxTreeEvent
 
3720
                    nevent(wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK, this, item);
 
3721
                GetEventHandler()->ProcessEvent(nevent);
 
3722
            }
 
3723
 
 
3724
            // this facilitates multiple-item drag-and-drop
 
3725
 
 
3726
            if ( /* item && */ HasFlag(wxTR_MULTIPLE))
 
3727
            {
 
3728
                wxArrayTreeItemIds selections;
 
3729
                size_t count = GetSelections(selections);
 
3730
 
 
3731
                if (count > 1 &&
 
3732
                    !event.CmdDown() &&
 
3733
                    !event.ShiftDown())
 
3734
                {
 
3735
                    DoSelectItem(item, true, false);
 
3736
                }
 
3737
            }
 
3738
 
 
3739
            if ( m_lastOnSame )
 
3740
            {
 
3741
                if ( (item == m_current) &&
 
3742
                     (flags & wxTREE_HITTEST_ONITEMLABEL) &&
 
3743
                     HasFlag(wxTR_EDIT_LABELS) )
 
3744
                {
 
3745
                    if ( m_renameTimer )
 
3746
                    {
 
3747
                        if ( m_renameTimer->IsRunning() )
 
3748
                            m_renameTimer->Stop();
 
3749
                    }
 
3750
                    else
 
3751
                    {
 
3752
                        m_renameTimer = new wxTreeRenameTimer( this );
 
3753
                    }
 
3754
 
 
3755
                    m_renameTimer->Start( wxTreeRenameTimer::DELAY, true );
 
3756
                }
 
3757
 
 
3758
                m_lastOnSame = false;
 
3759
            }
 
3760
        }
 
3761
        else // !RightDown() && !MiddleDown() && !LeftUp()
 
3762
        {
 
3763
            // ==> LeftDown() || LeftDClick()
 
3764
            if ( event.LeftDown() )
 
3765
            {
 
3766
                m_lastOnSame = item == m_current;
 
3767
            }
 
3768
 
 
3769
            if ( flags & wxTREE_HITTEST_ONITEMBUTTON )
 
3770
            {
 
3771
                // only toggle the item for a single click, double click on
 
3772
                // the button doesn't do anything (it toggles the item twice)
 
3773
                if ( event.LeftDown() )
 
3774
                {
 
3775
                    Toggle( item );
 
3776
                }
 
3777
 
 
3778
                // don't select the item if the button was clicked
 
3779
                return;
 
3780
            }
 
3781
 
 
3782
 
 
3783
            // clear the previously selected items, if the
 
3784
            // user clicked outside of the present selection.
 
3785
            // otherwise, perform the deselection on mouse-up.
 
3786
            // this allows multiple drag and drop to work.
 
3787
            // but if Cmd is down, toggle selection of the clicked item
 
3788
            if (!IsSelected(item) || event.CmdDown())
 
3789
            {
 
3790
                // how should the selection work for this event?
 
3791
                bool is_multiple, extended_select, unselect_others;
 
3792
                EventFlagsToSelType(GetWindowStyleFlag(),
 
3793
                                    event.ShiftDown(),
 
3794
                                    event.CmdDown(),
 
3795
                                    is_multiple,
 
3796
                                    extended_select,
 
3797
                                    unselect_others);
 
3798
 
 
3799
                DoSelectItem(item, unselect_others, extended_select);
 
3800
            }
 
3801
 
 
3802
 
 
3803
            // For some reason, Windows isn't recognizing a left double-click,
 
3804
            // so we need to simulate it here.  Allow 200 milliseconds for now.
 
3805
            if ( event.LeftDClick() )
 
3806
            {
 
3807
                // double clicking should not start editing the item label
 
3808
                if ( m_renameTimer )
 
3809
                    m_renameTimer->Stop();
 
3810
 
 
3811
                m_lastOnSame = false;
 
3812
 
 
3813
                // send activate event first
 
3814
                wxTreeEvent
 
3815
                    nevent(wxEVT_COMMAND_TREE_ITEM_ACTIVATED,  this, item);
 
3816
                nevent.m_pointDrag = CalcScrolledPosition(pt);
 
3817
                if ( !GetEventHandler()->ProcessEvent( nevent ) )
 
3818
                {
 
3819
                    // if the user code didn't process the activate event,
 
3820
                    // handle it ourselves by toggling the item when it is
 
3821
                    // double clicked
 
3822
                    if ( item->HasPlus() )
 
3823
                    {
 
3824
                        Toggle(item);
 
3825
                    }
 
3826
                }
 
3827
            }
 
3828
        }
 
3829
    }
 
3830
}
 
3831
 
 
3832
void wxGenericTreeCtrl::OnInternalIdle()
 
3833
{
 
3834
    wxWindow::OnInternalIdle();
 
3835
 
 
3836
    // Check if we need to select the root item
 
3837
    // because nothing else has been selected.
 
3838
    // Delaying it means that we can invoke event handlers
 
3839
    // as required, when a first item is selected.
 
3840
    if (!HasFlag(wxTR_MULTIPLE) && !GetSelection().IsOk())
 
3841
    {
 
3842
        if (m_select_me)
 
3843
            SelectItem(m_select_me);
 
3844
        else if (GetRootItem().IsOk())
 
3845
            SelectItem(GetRootItem());
 
3846
    }
 
3847
 
 
3848
    // after all changes have been done to the tree control,
 
3849
    // actually redraw the tree when everything is over
 
3850
    if (m_dirty)
 
3851
        DoDirtyProcessing();
 
3852
}
 
3853
 
 
3854
void
 
3855
wxGenericTreeCtrl::CalculateLevel(wxGenericTreeItem *item,
 
3856
                                  wxDC &dc,
 
3857
                                  int level,
 
3858
                                  int &y )
 
3859
{
 
3860
    int x = level*m_indent;
 
3861
    if (!HasFlag(wxTR_HIDE_ROOT))
 
3862
    {
 
3863
        x += m_indent;
 
3864
    }
 
3865
    else if (level == 0)
 
3866
    {
 
3867
        // a hidden root is not evaluated, but its
 
3868
        // children are always calculated
 
3869
        goto Recurse;
 
3870
    }
 
3871
 
 
3872
    item->CalculateSize(this, dc);
 
3873
 
 
3874
    // set its position
 
3875
    item->SetX( x+m_spacing );
 
3876
    item->SetY( y );
 
3877
    y += GetLineHeight(item);
 
3878
 
 
3879
    if ( !item->IsExpanded() )
 
3880
    {
 
3881
        // we don't need to calculate collapsed branches
 
3882
        return;
 
3883
    }
 
3884
 
 
3885
  Recurse:
 
3886
    wxArrayGenericTreeItems& children = item->GetChildren();
 
3887
    size_t n, count = children.GetCount();
 
3888
    ++level;
 
3889
    for (n = 0; n < count; ++n )
 
3890
        CalculateLevel( children[n], dc, level, y );  // recurse
 
3891
}
 
3892
 
 
3893
void wxGenericTreeCtrl::CalculatePositions()
 
3894
{
 
3895
    if ( !m_anchor ) return;
 
3896
 
 
3897
    wxClientDC dc(this);
 
3898
    PrepareDC( dc );
 
3899
 
 
3900
    dc.SetFont( m_normalFont );
 
3901
 
 
3902
    dc.SetPen( m_dottedPen );
 
3903
    //if(GetImageList() == NULL)
 
3904
    // m_lineHeight = (int)(dc.GetCharHeight() + 4);
 
3905
 
 
3906
    int y = 2;
 
3907
    CalculateLevel( m_anchor, dc, 0, y ); // start recursion
 
3908
}
 
3909
 
 
3910
void wxGenericTreeCtrl::Refresh(bool eraseBackground, const wxRect *rect)
 
3911
{
 
3912
    if ( !IsFrozen() )
 
3913
        wxTreeCtrlBase::Refresh(eraseBackground, rect);
 
3914
}
 
3915
 
 
3916
void wxGenericTreeCtrl::RefreshSubtree(wxGenericTreeItem *item)
 
3917
{
 
3918
    if (m_dirty || IsFrozen() )
 
3919
        return;
 
3920
 
 
3921
    wxSize client = GetClientSize();
 
3922
 
 
3923
    wxRect rect;
 
3924
    CalcScrolledPosition(0, item->GetY(), NULL, &rect.y);
 
3925
    rect.width = client.x;
 
3926
    rect.height = client.y;
 
3927
 
 
3928
    Refresh(true, &rect);
 
3929
 
 
3930
    AdjustMyScrollbars();
 
3931
}
 
3932
 
 
3933
void wxGenericTreeCtrl::RefreshLine( wxGenericTreeItem *item )
 
3934
{
 
3935
    if (m_dirty || IsFrozen() )
 
3936
        return;
 
3937
 
 
3938
    wxRect rect;
 
3939
    CalcScrolledPosition(0, item->GetY(), NULL, &rect.y);
 
3940
    rect.width = GetClientSize().x;
 
3941
    rect.height = GetLineHeight(item); //dc.GetCharHeight() + 6;
 
3942
 
 
3943
    Refresh(true, &rect);
 
3944
}
 
3945
 
 
3946
void wxGenericTreeCtrl::RefreshSelected()
 
3947
{
 
3948
    if (IsFrozen())
 
3949
        return;
 
3950
 
 
3951
    // TODO: this is awfully inefficient, we should keep the list of all
 
3952
    //       selected items internally, should be much faster
 
3953
    if ( m_anchor )
 
3954
        RefreshSelectedUnder(m_anchor);
 
3955
}
 
3956
 
 
3957
void wxGenericTreeCtrl::RefreshSelectedUnder(wxGenericTreeItem *item)
 
3958
{
 
3959
    if (IsFrozen())
 
3960
        return;
 
3961
 
 
3962
    if ( item->IsSelected() )
 
3963
        RefreshLine(item);
 
3964
 
 
3965
    const wxArrayGenericTreeItems& children = item->GetChildren();
 
3966
    size_t count = children.GetCount();
 
3967
    for ( size_t n = 0; n < count; n++ )
 
3968
    {
 
3969
        RefreshSelectedUnder(children[n]);
 
3970
    }
 
3971
}
 
3972
 
 
3973
void wxGenericTreeCtrl::DoThaw()
 
3974
{
 
3975
    wxTreeCtrlBase::DoThaw();
 
3976
 
 
3977
    if ( m_dirty )
 
3978
        DoDirtyProcessing();
 
3979
    else
 
3980
        Refresh();
 
3981
}
 
3982
 
 
3983
// ----------------------------------------------------------------------------
 
3984
// changing colours: we need to refresh the tree control
 
3985
// ----------------------------------------------------------------------------
 
3986
 
 
3987
bool wxGenericTreeCtrl::SetBackgroundColour(const wxColour& colour)
 
3988
{
 
3989
    if ( !wxWindow::SetBackgroundColour(colour) )
 
3990
        return false;
 
3991
 
 
3992
    Refresh();
 
3993
 
 
3994
    return true;
 
3995
}
 
3996
 
 
3997
bool wxGenericTreeCtrl::SetForegroundColour(const wxColour& colour)
 
3998
{
 
3999
    if ( !wxWindow::SetForegroundColour(colour) )
 
4000
        return false;
 
4001
 
 
4002
    Refresh();
 
4003
 
 
4004
    return true;
 
4005
}
 
4006
 
 
4007
// Process the tooltip event, to speed up event processing.
 
4008
// Doesn't actually get a tooltip.
 
4009
void wxGenericTreeCtrl::OnGetToolTip( wxTreeEvent &event )
 
4010
{
 
4011
    event.Veto();
 
4012
}
 
4013
 
 
4014
 
 
4015
// NOTE: If using the wxListBox visual attributes works everywhere then this can
 
4016
// be removed, as well as the #else case below.
 
4017
#define _USE_VISATTR 0
 
4018
 
 
4019
//static
 
4020
wxVisualAttributes
 
4021
#if _USE_VISATTR
 
4022
wxGenericTreeCtrl::GetClassDefaultAttributes(wxWindowVariant variant)
 
4023
#else
 
4024
wxGenericTreeCtrl::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
 
4025
#endif
 
4026
{
 
4027
#if _USE_VISATTR
 
4028
    // Use the same color scheme as wxListBox
 
4029
    return wxListBox::GetClassDefaultAttributes(variant);
 
4030
#else
 
4031
    wxVisualAttributes attr;
 
4032
    attr.colFg = wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOXTEXT);
 
4033
    attr.colBg = wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX);
 
4034
    attr.font  = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
 
4035
    return attr;
 
4036
#endif
 
4037
}
 
4038
 
 
4039
void wxGenericTreeCtrl::DoDirtyProcessing()
 
4040
{
 
4041
    if (IsFrozen())
 
4042
        return;
 
4043
 
 
4044
    m_dirty = false;
 
4045
 
 
4046
    CalculatePositions();
 
4047
    Refresh();
 
4048
    AdjustMyScrollbars();
 
4049
}
 
4050
 
 
4051
wxSize wxGenericTreeCtrl::DoGetBestSize() const
 
4052
{
 
4053
    // make sure all positions are calculated as normally this only done during
 
4054
    // idle time but we need them for base class DoGetBestSize() to return the
 
4055
    // correct result
 
4056
    wxConstCast(this, wxGenericTreeCtrl)->CalculatePositions();
 
4057
 
 
4058
    wxSize size = wxTreeCtrlBase::DoGetBestSize();
 
4059
 
 
4060
    // there seems to be an implicit extra border around the items, although
 
4061
    // I'm not really sure where does it come from -- but without this, the
 
4062
    // scrollbars appear in a tree with default/best size
 
4063
    size.IncBy(4, 4);
 
4064
 
 
4065
    // and the border has to be rounded up to a multiple of PIXELS_PER_UNIT or
 
4066
    // scrollbars still appear
 
4067
    const wxSize& borderSize = GetWindowBorderSize();
 
4068
 
 
4069
    int dx = (size.x - borderSize.x) % PIXELS_PER_UNIT;
 
4070
    if ( dx )
 
4071
        size.x += PIXELS_PER_UNIT - dx;
 
4072
    int dy = (size.y - borderSize.y) % PIXELS_PER_UNIT;
 
4073
    if ( dy )
 
4074
        size.y += PIXELS_PER_UNIT - dy;
 
4075
 
 
4076
    // we need to update the cache too as the base class cached its own value
 
4077
    CacheBestSize(size);
 
4078
 
 
4079
    return size;
 
4080
}
 
4081
 
 
4082
#endif // wxUSE_TREECTRL