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