home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / wxos2233.zip / wxOS2-2_3_3.zip / wxWindows-2.3.3 / src / generic / treectlg.cpp < prev    next >
C/C++ Source or Header  |  2002-09-05  |  97KB  |  3,224 lines

  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name:        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,v 1.85 2002/09/05 19:50:41 JS Exp $
  8. // Copyright:   (c) 1998 Robert Roebling, Julian Smart and Markus Holzem
  9. // Licence:     wxWindows licence
  10. /////////////////////////////////////////////////////////////////////////////
  11.  
  12. // =============================================================================
  13. // declarations
  14. // =============================================================================
  15.  
  16. // -----------------------------------------------------------------------------
  17. // headers
  18. // -----------------------------------------------------------------------------
  19.  
  20. #ifdef __GNUG__
  21.   #pragma implementation "treectlg.h"
  22. #endif
  23.  
  24. // For compilers that support precompilation, includes "wx.h".
  25. #include "wx/wxprec.h"
  26.  
  27. #ifdef __BORLANDC__
  28.     #pragma hdrstop
  29. #endif
  30.  
  31. #if wxUSE_TREECTRL
  32.  
  33. #include "wx/treebase.h"
  34. #include "wx/generic/treectlg.h"
  35. #include "wx/timer.h"
  36. #include "wx/textctrl.h"
  37. #include "wx/imaglist.h"
  38. #include "wx/settings.h"
  39. #include "wx/dcclient.h"
  40.  
  41. // -----------------------------------------------------------------------------
  42. // array types
  43. // -----------------------------------------------------------------------------
  44.  
  45. class WXDLLEXPORT wxGenericTreeItem;
  46.  
  47. WX_DEFINE_EXPORTED_ARRAY(wxGenericTreeItem *, wxArrayGenericTreeItems);
  48.  
  49. // ----------------------------------------------------------------------------
  50. // constants
  51. // ----------------------------------------------------------------------------
  52.  
  53. static const int NO_IMAGE = -1;
  54.  
  55. static const int PIXELS_PER_UNIT = 10;
  56.  
  57. // ----------------------------------------------------------------------------
  58. // Aqua arrows
  59. // ----------------------------------------------------------------------------
  60.  
  61. /* XPM */
  62. static const char *aqua_arrow_right[] = {
  63. /* columns rows colors chars-per-pixel */
  64. "13 11 4 1",
  65. "  c None",
  66. "b c #C0C0C0",
  67. "c c #707070",
  68. "d c #A0A0A0",
  69. /* pixels */
  70. "    b        ",
  71. "    ddb      ",
  72. "    cccdb    ",
  73. "    cccccd   ",
  74. "    ccccccdb ",
  75. "    ccccccccd",
  76. "    ccccccdb ",
  77. "    cccccb   ",
  78. "    cccdb    ",
  79. "    ddb      ",
  80. "    b        "
  81. };
  82.  
  83. /* XPM */
  84. static const char *aqua_arrow_down[] = {
  85. /* columns rows colors chars-per-pixel */
  86. "13 11 4 1",
  87. "  c None",
  88. "b c #C0C0C0",
  89. "c c #707070",
  90. "d c #A0A0A0",
  91. /* pixels */
  92. "             ",
  93. "             ",
  94. " bdcccccccdb ",
  95. "  dcccccccd  ",
  96. "  bcccccccb  ",
  97. "   dcccccd   ",
  98. "   bcccccb   ",
  99. "    bcccd    ",
  100. "     dcd     ",
  101. "     bcb     ",
  102. "      d      "
  103. };
  104.  
  105. // -----------------------------------------------------------------------------
  106. // private classes
  107. // -----------------------------------------------------------------------------
  108.  
  109. // timer used for enabling in-place edit
  110. class WXDLLEXPORT wxTreeRenameTimer: public wxTimer
  111. {
  112. public:
  113.     // start editing the current item after half a second (if the mouse hasn't
  114.     // been clicked/moved)
  115.     enum { DELAY = 500 };
  116.  
  117.     wxTreeRenameTimer( wxGenericTreeCtrl *owner );
  118.  
  119.     virtual void Notify();
  120.  
  121. private:
  122.     wxGenericTreeCtrl *m_owner;
  123. };
  124.  
  125. // control used for in-place edit
  126. class WXDLLEXPORT wxTreeTextCtrl: public wxTextCtrl
  127. {
  128. public:
  129.     wxTreeTextCtrl(wxGenericTreeCtrl *owner, wxGenericTreeItem *item);
  130.  
  131. protected:
  132.     void OnChar( wxKeyEvent &event );
  133.     void OnKeyUp( wxKeyEvent &event );
  134.     void OnKillFocus( wxFocusEvent &event );
  135.  
  136.     bool AcceptChanges();
  137.     void Finish();
  138.  
  139. private:
  140.     wxGenericTreeCtrl  *m_owner;
  141.     wxGenericTreeItem  *m_itemEdited;
  142.     wxString            m_startValue;
  143.     bool                m_finished;
  144.  
  145.     DECLARE_EVENT_TABLE()
  146. };
  147.  
  148. // timer used to clear wxGenericTreeCtrl::m_findPrefix if no key was pressed
  149. // for a sufficiently long time
  150. class WXDLLEXPORT wxTreeFindTimer : public wxTimer
  151. {
  152. public:
  153.     // reset the current prefix after half a second of inactivity
  154.     enum { DELAY = 500 };
  155.  
  156.     wxTreeFindTimer( wxGenericTreeCtrl *owner ) { m_owner = owner; }
  157.  
  158.     virtual void Notify() { m_owner->m_findPrefix.clear(); }
  159.  
  160. private:
  161.     wxGenericTreeCtrl *m_owner;
  162. };
  163.  
  164. // a tree item
  165. class WXDLLEXPORT wxGenericTreeItem
  166. {
  167. public:
  168.     // ctors & dtor
  169.     wxGenericTreeItem() { m_data = NULL; }
  170.     wxGenericTreeItem( wxGenericTreeItem *parent,
  171.                        const wxString& text,
  172.                        int image,
  173.                        int selImage,
  174.                        wxTreeItemData *data );
  175.  
  176.     ~wxGenericTreeItem();
  177.  
  178.     // trivial accessors
  179.     wxArrayGenericTreeItems& GetChildren() { return m_children; }
  180.  
  181.     const wxString& GetText() const { return m_text; }
  182.     int GetImage(wxTreeItemIcon which = wxTreeItemIcon_Normal) const
  183.         { return m_images[which]; }
  184.     wxTreeItemData *GetData() const { return m_data; }
  185.  
  186.     // returns the current image for the item (depending on its
  187.     // selected/expanded/whatever state)
  188.     int GetCurrentImage() const;
  189.  
  190.     void SetText( const wxString &text );
  191.     void SetImage(int image, wxTreeItemIcon which) { m_images[which] = image; }
  192.     void SetData(wxTreeItemData *data) { m_data = data; }
  193.  
  194.     void SetHasPlus(bool has = TRUE) { m_hasPlus = has; }
  195.  
  196.     void SetBold(bool bold) { m_isBold = bold; }
  197.  
  198.     int GetX() const { return m_x; }
  199.     int GetY() const { return m_y; }
  200.  
  201.     void SetX(int x) { m_x = x; }
  202.     void SetY(int y) { m_y = y; }
  203.  
  204.     int  GetHeight() const { return m_height; }
  205.     int  GetWidth()  const { return m_width; }
  206.  
  207.     void SetHeight(int h) { m_height = h; }
  208.     void SetWidth(int w) { m_width = w; }
  209.  
  210.     wxGenericTreeItem *GetParent() const { return m_parent; }
  211.  
  212.     // operations
  213.         // deletes all children notifying the treectrl about it if !NULL
  214.         // pointer given
  215.     void DeleteChildren(wxGenericTreeCtrl *tree = NULL);
  216.  
  217.     // get count of all children (and grand children if 'recursively')
  218.     size_t GetChildrenCount(bool recursively = TRUE) const;
  219.  
  220.     void Insert(wxGenericTreeItem *child, size_t index)
  221.     { m_children.Insert(child, index); }
  222.  
  223.     void GetSize( int &x, int &y, const wxGenericTreeCtrl* );
  224.  
  225.         // return the item at given position (or NULL if no item), onButton is
  226.         // TRUE if the point belongs to the item's button, otherwise it lies
  227.         // on the button's label
  228.     wxGenericTreeItem *HitTest( const wxPoint& point,
  229.                                 const wxGenericTreeCtrl *,
  230.                                 int &flags,
  231.                                 int level );
  232.  
  233.     void Expand() { m_isCollapsed = FALSE; }
  234.     void Collapse() { m_isCollapsed = TRUE; }
  235.  
  236.     void SetHilight( bool set = TRUE ) { m_hasHilight = set; }
  237.  
  238.     // status inquiries
  239.     bool HasChildren() const { return !m_children.IsEmpty(); }
  240.     bool IsSelected()  const { return m_hasHilight != 0; }
  241.     bool IsExpanded()  const { return !m_isCollapsed; }
  242.     bool HasPlus()     const { return m_hasPlus || HasChildren(); }
  243.     bool IsBold()      const { return m_isBold != 0; }
  244.  
  245.     // attributes
  246.         // get them - may be NULL
  247.     wxTreeItemAttr *GetAttributes() const { return m_attr; }
  248.         // get them ensuring that the pointer is not NULL
  249.     wxTreeItemAttr& Attr()
  250.     {
  251.         if ( !m_attr )
  252.         {
  253.             m_attr = new wxTreeItemAttr;
  254.             m_ownsAttr = TRUE;
  255.         }
  256.         return *m_attr;
  257.     }
  258.         // set them
  259.     void SetAttributes(wxTreeItemAttr *attr)
  260.     {
  261.         if ( m_ownsAttr ) delete m_attr;
  262.         m_attr = attr;
  263.         m_ownsAttr = FALSE;
  264.     }
  265.         // set them and delete when done
  266.     void AssignAttributes(wxTreeItemAttr *attr)
  267.     {
  268.         SetAttributes(attr);
  269.         m_ownsAttr = TRUE;
  270.     }
  271.  
  272. private:
  273.     // since there can be very many of these, we save size by chosing
  274.     // the smallest representation for the elements and by ordering
  275.     // the members to avoid padding.
  276.     wxString            m_text;         // label to be rendered for item
  277.  
  278.     wxTreeItemData     *m_data;         // user-provided data
  279.  
  280.     wxArrayGenericTreeItems m_children; // list of children
  281.     wxGenericTreeItem  *m_parent;       // parent of this item
  282.  
  283.     wxTreeItemAttr     *m_attr;         // attributes???
  284.  
  285.     // tree ctrl images for the normal, selected, expanded and
  286.     // expanded+selected states
  287.     short               m_images[wxTreeItemIcon_Max];
  288.  
  289.     wxCoord             m_x;            // (virtual) offset from top
  290.     short               m_y;            // (virtual) offset from left
  291.     short               m_width;        // width of this item
  292.     unsigned char       m_height;       // height of this item
  293.  
  294.     // use bitfields to save size
  295.     int                 m_isCollapsed :1;
  296.     int                 m_hasHilight  :1; // same as focused
  297.     int                 m_hasPlus     :1; // used for item which doesn't have
  298.                                           // children but has a [+] button
  299.     int                 m_isBold      :1; // render the label in bold font
  300.     int                 m_ownsAttr    :1; // delete attribute when done
  301. };
  302.  
  303. // =============================================================================
  304. // implementation
  305. // =============================================================================
  306.  
  307. // ----------------------------------------------------------------------------
  308. // private functions
  309. // ----------------------------------------------------------------------------
  310.  
  311. // translate the key or mouse event flags to the type of selection we're
  312. // dealing with
  313. static void EventFlagsToSelType(long style,
  314.                                 bool shiftDown,
  315.                                 bool ctrlDown,
  316.                                 bool &is_multiple,
  317.                                 bool &extended_select,
  318.                                 bool &unselect_others)
  319. {
  320.     is_multiple = (style & wxTR_MULTIPLE) != 0;
  321.     extended_select = shiftDown && is_multiple;
  322.     unselect_others = !(extended_select || (ctrlDown && is_multiple));
  323. }
  324.  
  325. // check if the given item is under another one
  326. static bool IsDescendantOf(wxGenericTreeItem *parent, wxGenericTreeItem *item)
  327. {
  328.     while ( item )
  329.     {
  330.         if ( item == parent )
  331.         {
  332.             // item is a descendant of parent
  333.             return TRUE;
  334.         }
  335.  
  336.         item = item->GetParent();
  337.     }
  338.  
  339.     return FALSE;
  340. }
  341.  
  342. // -----------------------------------------------------------------------------
  343. // wxTreeRenameTimer (internal)
  344. // -----------------------------------------------------------------------------
  345.  
  346. wxTreeRenameTimer::wxTreeRenameTimer( wxGenericTreeCtrl *owner )
  347. {
  348.     m_owner = owner;
  349. }
  350.  
  351. void wxTreeRenameTimer::Notify()
  352. {
  353.     m_owner->OnRenameTimer();
  354. }
  355.  
  356. //-----------------------------------------------------------------------------
  357. // wxTreeTextCtrl (internal)
  358. //-----------------------------------------------------------------------------
  359.  
  360. BEGIN_EVENT_TABLE(wxTreeTextCtrl,wxTextCtrl)
  361.     EVT_CHAR           (wxTreeTextCtrl::OnChar)
  362.     EVT_KEY_UP         (wxTreeTextCtrl::OnKeyUp)
  363.     EVT_KILL_FOCUS     (wxTreeTextCtrl::OnKillFocus)
  364. END_EVENT_TABLE()
  365.  
  366. wxTreeTextCtrl::wxTreeTextCtrl(wxGenericTreeCtrl *owner,
  367.                                wxGenericTreeItem *item)
  368.               : m_itemEdited(item), m_startValue(item->GetText())
  369. {
  370.     m_owner = owner;
  371.     m_finished = FALSE;
  372.  
  373.     int w = m_itemEdited->GetWidth(),
  374.         h = m_itemEdited->GetHeight();
  375.  
  376.     int x, y;
  377.     m_owner->CalcScrolledPosition(item->GetX(), item->GetY(), &x, &y);
  378.  
  379.     int image_h = 0,
  380.         image_w = 0;
  381.  
  382.     int image = item->GetCurrentImage();
  383.     if ( image != NO_IMAGE )
  384.     {
  385.         if ( m_owner->m_imageListNormal )
  386.         {
  387.             m_owner->m_imageListNormal->GetSize( image, image_w, image_h );
  388.             image_w += 4;
  389.         }
  390.         else
  391.         {
  392.             wxFAIL_MSG(_T("you must create an image list to use images!"));
  393.         }
  394.     }
  395.  
  396.     // FIXME: what are all these hardcoded 4, 8 and 11s really?
  397.     x += image_w;
  398.     w -= image_w + 4;
  399.  
  400.     (void)Create(m_owner, wxID_ANY, m_startValue,
  401.                  wxPoint(x - 4, y - 4), wxSize(w + 11, h + 8));
  402. }
  403.  
  404. bool wxTreeTextCtrl::AcceptChanges()
  405. {
  406.     const wxString value = GetValue();
  407.  
  408.     if ( value == m_startValue )
  409.     {
  410.         // nothing changed, always accept
  411.         return TRUE;
  412.     }
  413.  
  414.     if ( !m_owner->OnRenameAccept(m_itemEdited, value) )
  415.     {
  416.         // vetoed by the user
  417.         return FALSE;
  418.     }
  419.  
  420.     // accepted, do rename the item
  421.     m_owner->SetItemText(m_itemEdited, value);
  422.  
  423.     return TRUE;
  424. }
  425.  
  426. void wxTreeTextCtrl::Finish()
  427. {
  428.     if ( !m_finished )
  429.     {
  430.         wxPendingDelete.Append(this);
  431.  
  432.         m_finished = TRUE;
  433.  
  434.         m_owner->SetFocus(); // This doesn't work. TODO.
  435.     }
  436. }
  437.  
  438. void wxTreeTextCtrl::OnChar( wxKeyEvent &event )
  439. {
  440.     switch ( event.m_keyCode )
  441.     {
  442.         case WXK_RETURN:
  443.             if ( !AcceptChanges() )
  444.             {
  445.                 // vetoed by the user, don't disappear
  446.                 break;
  447.             }
  448.             //else: fall through
  449.  
  450.         case WXK_ESCAPE:
  451.             Finish();
  452.             m_owner->OnRenameCancelled(m_itemEdited);
  453.             break;
  454.  
  455.         default:
  456.             event.Skip();
  457.     }
  458. }
  459.  
  460. void wxTreeTextCtrl::OnKeyUp( wxKeyEvent &event )
  461. {
  462.     if ( !m_finished )
  463.     {
  464.         // auto-grow the textctrl:
  465.         wxSize parentSize = m_owner->GetSize();
  466.         wxPoint myPos = GetPosition();
  467.         wxSize mySize = GetSize();
  468.         int sx, sy;
  469.         GetTextExtent(GetValue() + _T("M"), &sx, &sy);
  470.         if (myPos.x + sx > parentSize.x)
  471.             sx = parentSize.x - myPos.x;
  472.         if (mySize.x > sx)
  473.             sx = mySize.x;
  474.         SetSize(sx, -1);
  475.     }
  476.  
  477.     event.Skip();
  478. }
  479.  
  480. void wxTreeTextCtrl::OnKillFocus( wxFocusEvent &event )
  481. {
  482.     if ( m_finished )
  483.     {
  484.         event.Skip();
  485.         return;
  486.     }
  487.  
  488.     if ( AcceptChanges() )
  489.     {
  490.         Finish();
  491.     }
  492. }
  493.  
  494. // -----------------------------------------------------------------------------
  495. // wxGenericTreeItem
  496. // -----------------------------------------------------------------------------
  497.  
  498. wxGenericTreeItem::wxGenericTreeItem(wxGenericTreeItem *parent,
  499.                                      const wxString& text,
  500.                                      int image, int selImage,
  501.                                      wxTreeItemData *data)
  502.                  : m_text(text)
  503. {
  504.     m_images[wxTreeItemIcon_Normal] = image;
  505.     m_images[wxTreeItemIcon_Selected] = selImage;
  506.     m_images[wxTreeItemIcon_Expanded] = NO_IMAGE;
  507.     m_images[wxTreeItemIcon_SelectedExpanded] = NO_IMAGE;
  508.  
  509.     m_data = data;
  510.     m_x = m_y = 0;
  511.  
  512.     m_isCollapsed = TRUE;
  513.     m_hasHilight = FALSE;
  514.     m_hasPlus = FALSE;
  515.     m_isBold = FALSE;
  516.  
  517.     m_parent = parent;
  518.  
  519.     m_attr = (wxTreeItemAttr *)NULL;
  520.     m_ownsAttr = FALSE;
  521.  
  522.     // We don't know the height here yet.
  523.     m_width = 0;
  524.     m_height = 0;
  525. }
  526.  
  527. wxGenericTreeItem::~wxGenericTreeItem()
  528. {
  529.     delete m_data;
  530.  
  531.     if (m_ownsAttr) delete m_attr;
  532.  
  533.     wxASSERT_MSG( m_children.IsEmpty(),
  534.                   wxT("please call DeleteChildren() before deleting the item") );
  535. }
  536.  
  537. void wxGenericTreeItem::DeleteChildren(wxGenericTreeCtrl *tree)
  538. {
  539.     size_t count = m_children.Count();
  540.     for ( size_t n = 0; n < count; n++ )
  541.     {
  542.         wxGenericTreeItem *child = m_children[n];
  543.         if (tree)
  544.             tree->SendDeleteEvent(child);
  545.  
  546.         child->DeleteChildren(tree);
  547.         delete child;
  548.     }
  549.  
  550.     m_children.Empty();
  551. }
  552.  
  553. void wxGenericTreeItem::SetText( const wxString &text )
  554. {
  555.     m_text = text;
  556. }
  557.  
  558. size_t wxGenericTreeItem::GetChildrenCount(bool recursively) const
  559. {
  560.     size_t count = m_children.Count();
  561.     if ( !recursively )
  562.         return count;
  563.  
  564.     size_t total = count;
  565.     for (size_t n = 0; n < count; ++n)
  566.     {
  567.         total += m_children[n]->GetChildrenCount();
  568.     }
  569.  
  570.     return total;
  571. }
  572.  
  573. void wxGenericTreeItem::GetSize( int &x, int &y,
  574.                                  const wxGenericTreeCtrl *theButton )
  575. {
  576.     int bottomY=m_y+theButton->GetLineHeight(this);
  577.     if ( y < bottomY ) y = bottomY;
  578.     int width = m_x +  m_width;
  579.     if ( x < width ) x = width;
  580.  
  581.     if (IsExpanded())
  582.     {
  583.         size_t count = m_children.Count();
  584.         for ( size_t n = 0; n < count; ++n )
  585.         {
  586.             m_children[n]->GetSize( x, y, theButton );
  587.         }
  588.     }
  589. }
  590.  
  591. wxGenericTreeItem *wxGenericTreeItem::HitTest(const wxPoint& point,
  592.                                               const wxGenericTreeCtrl *theCtrl,
  593.                                               int &flags,
  594.                                               int level)
  595. {
  596.     // for a hidden root node, don't evaluate it, but do evaluate children
  597.     if ( !(level == 0 && theCtrl->HasFlag(wxTR_HIDE_ROOT)) )
  598.     {
  599.         // evaluate the item
  600.         int h = theCtrl->GetLineHeight(this);
  601.         if ((point.y > m_y) && (point.y < m_y + h))
  602.         {
  603.             int y_mid = m_y + h/2;
  604.             if (point.y < y_mid )
  605.                 flags |= wxTREE_HITTEST_ONITEMUPPERPART;
  606.             else
  607.                 flags |= wxTREE_HITTEST_ONITEMLOWERPART;
  608.  
  609.             // 5 is the size of the plus sign
  610.             int xCross = m_x - theCtrl->GetSpacing();
  611.             if ((point.x > xCross-5) && (point.x < xCross+5) &&
  612.                 (point.y > y_mid-5) && (point.y < y_mid+5) &&
  613.                 HasPlus() && theCtrl->HasButtons() )
  614.             {
  615.                 flags |= wxTREE_HITTEST_ONITEMBUTTON;
  616.                 return this;
  617.             }
  618.  
  619.             if ((point.x >= m_x) && (point.x <= m_x+m_width))
  620.             {
  621.                 int image_w = -1;
  622.                 int image_h;
  623.  
  624.                 // assuming every image (normal and selected) has the same size!
  625.                 if ( (GetImage() != NO_IMAGE) && theCtrl->m_imageListNormal )
  626.                     theCtrl->m_imageListNormal->GetSize(GetImage(),
  627.                                                         image_w, image_h);
  628.  
  629.                 if ((image_w != -1) && (point.x <= m_x + image_w + 1))
  630.                     flags |= wxTREE_HITTEST_ONITEMICON;
  631.                 else
  632.                     flags |= wxTREE_HITTEST_ONITEMLABEL;
  633.  
  634.                 return this;
  635.             }
  636.  
  637.             if (point.x < m_x)
  638.                 flags |= wxTREE_HITTEST_ONITEMINDENT;
  639.             if (point.x > m_x+m_width)
  640.                 flags |= wxTREE_HITTEST_ONITEMRIGHT;
  641.  
  642.             return this;
  643.         }
  644.  
  645.         // if children are expanded, fall through to evaluate them
  646.         if (m_isCollapsed) return (wxGenericTreeItem*) NULL;
  647.     }
  648.  
  649.     // evaluate children
  650.     size_t count = m_children.Count();
  651.     for ( size_t n = 0; n < count; n++ )
  652.     {
  653.         wxGenericTreeItem *res = m_children[n]->HitTest( point,
  654.                                                          theCtrl,
  655.                                                          flags,
  656.                                                          level + 1 );
  657.         if ( res != NULL )
  658.             return res;
  659.     }
  660.  
  661.     return (wxGenericTreeItem*) NULL;
  662. }
  663.  
  664. int wxGenericTreeItem::GetCurrentImage() const
  665. {
  666.     int image = NO_IMAGE;
  667.     if ( IsExpanded() )
  668.     {
  669.         if ( IsSelected() )
  670.         {
  671.             image = GetImage(wxTreeItemIcon_SelectedExpanded);
  672.         }
  673.  
  674.         if ( image == NO_IMAGE )
  675.         {
  676.             // we usually fall back to the normal item, but try just the
  677.             // expanded one (and not selected) first in this case
  678.             image = GetImage(wxTreeItemIcon_Expanded);
  679.         }
  680.     }
  681.     else // not expanded
  682.     {
  683.         if ( IsSelected() )
  684.             image = GetImage(wxTreeItemIcon_Selected);
  685.     }
  686.  
  687.     // maybe it doesn't have the specific image we want,
  688.     // try the default one instead
  689.     if ( image == NO_IMAGE ) image = GetImage();
  690.  
  691.     return image;
  692. }
  693.  
  694. // -----------------------------------------------------------------------------
  695. // wxGenericTreeCtrl implementation
  696. // -----------------------------------------------------------------------------
  697.  
  698. IMPLEMENT_DYNAMIC_CLASS(wxGenericTreeCtrl, wxScrolledWindow)
  699.  
  700. BEGIN_EVENT_TABLE(wxGenericTreeCtrl,wxScrolledWindow)
  701.     EVT_PAINT          (wxGenericTreeCtrl::OnPaint)
  702.     EVT_MOUSE_EVENTS   (wxGenericTreeCtrl::OnMouse)
  703.     EVT_CHAR           (wxGenericTreeCtrl::OnChar)
  704.     EVT_SET_FOCUS      (wxGenericTreeCtrl::OnSetFocus)
  705.     EVT_KILL_FOCUS     (wxGenericTreeCtrl::OnKillFocus)
  706.     EVT_IDLE           (wxGenericTreeCtrl::OnIdle)
  707. END_EVENT_TABLE()
  708.  
  709. #if !defined(__WXMSW__) || defined(__WIN16__) || defined(__WXUNIVERSAL__)
  710. /*
  711.  * wxTreeCtrl has to be a real class or we have problems with
  712.  * the run-time information.
  713.  */
  714.  
  715. IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl, wxGenericTreeCtrl)
  716. #endif
  717.  
  718. // -----------------------------------------------------------------------------
  719. // construction/destruction
  720. // -----------------------------------------------------------------------------
  721.  
  722. void wxGenericTreeCtrl::Init()
  723. {
  724.     m_current = m_key_current = m_anchor = (wxGenericTreeItem *) NULL;
  725.     m_hasFocus = FALSE;
  726.     m_dirty = FALSE;
  727.  
  728.     m_lineHeight = 10;
  729.     m_indent = 15;
  730.     m_spacing = 18;
  731.  
  732.     m_hilightBrush = new wxBrush
  733.                          (
  734.                             wxSystemSettings::GetColour
  735.                             (
  736.                                 wxSYS_COLOUR_HIGHLIGHT
  737.                             ),
  738.                             wxSOLID
  739.                          );
  740.  
  741.     m_hilightUnfocusedBrush = new wxBrush
  742.                               (
  743.                                  wxSystemSettings::GetColour
  744.                                  (
  745.                                      wxSYS_COLOUR_BTNSHADOW
  746.                                  ),
  747.                                  wxSOLID
  748.                               );
  749.  
  750.     m_imageListNormal = m_imageListButtons =
  751.     m_imageListState = (wxImageList *) NULL;
  752.     m_ownsImageListNormal = m_ownsImageListButtons =
  753.     m_ownsImageListState = FALSE;
  754.  
  755.     m_dragCount = 0;
  756.     m_isDragging = FALSE;
  757.     m_dropTarget = m_oldSelection = (wxGenericTreeItem *)NULL;
  758.  
  759.     m_renameTimer = NULL;
  760.     m_findTimer = NULL;
  761.  
  762.     m_lastOnSame = FALSE;
  763.  
  764.     m_normalFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
  765.     m_boldFont = wxFont( m_normalFont.GetPointSize(),
  766.                          m_normalFont.GetFamily(),
  767.                          m_normalFont.GetStyle(),
  768.                          wxBOLD,
  769.                          m_normalFont.GetUnderlined());
  770. }
  771.  
  772. bool wxGenericTreeCtrl::Create(wxWindow *parent,
  773.                                wxWindowID id,
  774.                                const wxPoint& pos,
  775.                                const wxSize& size,
  776.                                long style,
  777.                                const wxValidator &validator,
  778.                                const wxString& name )
  779. {
  780. #ifdef __WXMAC__
  781.     int major,minor;
  782.     wxGetOsVersion( &major, &minor );
  783.  
  784.     if (style & wxTR_HAS_BUTTONS) style |= wxTR_MAC_BUTTONS;
  785.     if (style & wxTR_HAS_BUTTONS) style &= ~wxTR_HAS_BUTTONS;
  786.     style &= ~wxTR_LINES_AT_ROOT;
  787.     style |= wxTR_NO_LINES;
  788.     if (major < 10)
  789.         style |= wxTR_ROW_LINES;
  790.     if (major >= 10)
  791.         style |= wxTR_AQUA_BUTTONS;
  792. #endif
  793.  
  794.     if (style & wxTR_AQUA_BUTTONS)
  795.     {
  796.         m_arrowRight = new wxBitmap( aqua_arrow_right );
  797.         m_arrowDown = new wxBitmap( aqua_arrow_down );
  798.     }
  799.     else
  800.     {
  801.         m_arrowRight = NULL;
  802.         m_arrowDown = NULL;
  803.     }
  804.  
  805.     wxScrolledWindow::Create( parent, id, pos, size,
  806.                               style|wxHSCROLL|wxVSCROLL, name );
  807.  
  808.     // If the tree display has no buttons, but does have
  809.     // connecting lines, we can use a narrower layout.
  810.     // It may not be a good idea to force this...
  811.     if (!HasButtons() && !HasFlag(wxTR_NO_LINES))
  812.     {
  813.         m_indent= 10;
  814.         m_spacing = 10;
  815.     }
  816.  
  817. #if wxUSE_VALIDATORS
  818.     SetValidator( validator );
  819. #endif
  820.  
  821.     SetForegroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) );
  822.     SetBackgroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX) );
  823.  
  824. //  m_dottedPen = wxPen( "grey", 0, wxDOT );  too slow under XFree86
  825.     m_dottedPen = wxPen( wxT("grey"), 0, 0 );
  826.  
  827.     return TRUE;
  828. }
  829.  
  830. wxGenericTreeCtrl::~wxGenericTreeCtrl()
  831. {
  832.     delete m_hilightBrush;
  833.     delete m_hilightUnfocusedBrush;
  834.  
  835.     delete m_arrowRight;
  836.     delete m_arrowDown;
  837.  
  838.     DeleteAllItems();
  839.  
  840.     delete m_renameTimer;
  841.     delete m_findTimer;
  842.  
  843.     if (m_ownsImageListNormal)
  844.         delete m_imageListNormal;
  845.     if (m_ownsImageListState)
  846.         delete m_imageListState;
  847.     if (m_ownsImageListButtons)
  848.         delete m_imageListButtons;
  849. }
  850.  
  851. // -----------------------------------------------------------------------------
  852. // accessors
  853. // -----------------------------------------------------------------------------
  854.  
  855. size_t wxGenericTreeCtrl::GetCount() const
  856. {
  857.     return m_anchor == NULL ? 0u : m_anchor->GetChildrenCount();
  858. }
  859.  
  860. void wxGenericTreeCtrl::SetIndent(unsigned int indent)
  861. {
  862.     m_indent = (unsigned short) indent;
  863.     m_dirty = TRUE;
  864. }
  865.  
  866. void wxGenericTreeCtrl::SetSpacing(unsigned int spacing)
  867. {
  868.     m_spacing = (unsigned short) spacing;
  869.     m_dirty = TRUE;
  870. }
  871.  
  872. size_t wxGenericTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool recursively)
  873. {
  874.     wxCHECK_MSG( item.IsOk(), 0u, wxT("invalid tree item") );
  875.  
  876.     return ((wxGenericTreeItem*) item.m_pItem)->GetChildrenCount(recursively);
  877. }
  878.  
  879. void wxGenericTreeCtrl::SetWindowStyle(const long styles)
  880. {
  881.     if (!HasFlag(wxTR_HIDE_ROOT) && (styles & wxTR_HIDE_ROOT))
  882.     {
  883.         // if we will hide the root, make sure children are visible
  884.         m_anchor->SetHasPlus();
  885.         m_anchor->Expand();
  886.         CalculatePositions();
  887.     }
  888.  
  889.     // right now, just sets the styles.  Eventually, we may
  890.     // want to update the inherited styles, but right now
  891.     // none of the parents has updatable styles
  892.     m_windowStyle = styles;
  893.     m_dirty = TRUE;
  894. }
  895.  
  896. // -----------------------------------------------------------------------------
  897. // functions to work with tree items
  898. // -----------------------------------------------------------------------------
  899.  
  900. wxString wxGenericTreeCtrl::GetItemText(const wxTreeItemId& item) const
  901. {
  902.     wxCHECK_MSG( item.IsOk(), wxT(""), wxT("invalid tree item") );
  903.  
  904.     return ((wxGenericTreeItem*) item.m_pItem)->GetText();
  905. }
  906.  
  907. int wxGenericTreeCtrl::GetItemImage(const wxTreeItemId& item,
  908.                              wxTreeItemIcon which) const
  909. {
  910.     wxCHECK_MSG( item.IsOk(), -1, wxT("invalid tree item") );
  911.  
  912.     return ((wxGenericTreeItem*) item.m_pItem)->GetImage(which);
  913. }
  914.  
  915. wxTreeItemData *wxGenericTreeCtrl::GetItemData(const wxTreeItemId& item) const
  916. {
  917.     wxCHECK_MSG( item.IsOk(), NULL, wxT("invalid tree item") );
  918.  
  919.     return ((wxGenericTreeItem*) item.m_pItem)->GetData();
  920. }
  921.  
  922. void wxGenericTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text)
  923. {
  924.     wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
  925.  
  926.     wxClientDC dc(this);
  927.     wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
  928.     pItem->SetText(text);
  929.     CalculateSize(pItem, dc);
  930.     RefreshLine(pItem);
  931. }
  932.  
  933. void wxGenericTreeCtrl::SetItemImage(const wxTreeItemId& item,
  934.                               int image,
  935.                               wxTreeItemIcon which)
  936. {
  937.     wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
  938.  
  939.     wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
  940.     pItem->SetImage(image, which);
  941.  
  942.     wxClientDC dc(this);
  943.     CalculateSize(pItem, dc);
  944.     RefreshLine(pItem);
  945. }
  946.  
  947. void wxGenericTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data)
  948. {
  949.     wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
  950.  
  951.     ((wxGenericTreeItem*) item.m_pItem)->SetData(data);
  952. }
  953.  
  954. void wxGenericTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has)
  955. {
  956.     wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
  957.  
  958.     wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
  959.     pItem->SetHasPlus(has);
  960.     RefreshLine(pItem);
  961. }
  962.  
  963. void wxGenericTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold)
  964. {
  965.     wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
  966.  
  967.     // avoid redrawing the tree if no real change
  968.     wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
  969.     if ( pItem->IsBold() != bold )
  970.     {
  971.         pItem->SetBold(bold);
  972.         RefreshLine(pItem);
  973.     }
  974. }
  975.  
  976. void wxGenericTreeCtrl::SetItemTextColour(const wxTreeItemId& item,
  977.                                    const wxColour& col)
  978. {
  979.     wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
  980.  
  981.     wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
  982.     pItem->Attr().SetTextColour(col);
  983.     RefreshLine(pItem);
  984. }
  985.  
  986. void wxGenericTreeCtrl::SetItemBackgroundColour(const wxTreeItemId& item,
  987.                                          const wxColour& col)
  988. {
  989.     wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
  990.  
  991.     wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
  992.     pItem->Attr().SetBackgroundColour(col);
  993.     RefreshLine(pItem);
  994. }
  995.  
  996. void wxGenericTreeCtrl::SetItemFont(const wxTreeItemId& item, const wxFont& font)
  997. {
  998.     wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
  999.  
  1000.     wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
  1001.     pItem->Attr().SetFont(font);
  1002.     RefreshLine(pItem);
  1003. }
  1004.  
  1005. bool wxGenericTreeCtrl::SetFont( const wxFont &font )
  1006. {
  1007.     wxScrolledWindow::SetFont(font);
  1008.  
  1009.     m_normalFont = font ;
  1010.     m_boldFont = wxFont( m_normalFont.GetPointSize(),
  1011.                             m_normalFont.GetFamily(),
  1012.                             m_normalFont.GetStyle(),
  1013.                             wxBOLD,
  1014.                             m_normalFont.GetUnderlined());
  1015.  
  1016.     return TRUE;
  1017. }
  1018.  
  1019.  
  1020. // -----------------------------------------------------------------------------
  1021. // item status inquiries
  1022. // -----------------------------------------------------------------------------
  1023.  
  1024. bool wxGenericTreeCtrl::IsVisible(const wxTreeItemId& item) const
  1025. {
  1026.     wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
  1027.  
  1028.     // An item is only visible if it's not a descendant of a collapsed item
  1029.     wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
  1030.     wxGenericTreeItem* parent = pItem->GetParent();
  1031.     while (parent)
  1032.     {
  1033.         if (!parent->IsExpanded())
  1034.             return FALSE;
  1035.         parent = parent->GetParent();
  1036.     }
  1037.  
  1038.     int startX, startY;
  1039.     GetViewStart(& startX, & startY);
  1040.  
  1041.     wxSize clientSize = GetClientSize();
  1042.  
  1043.     wxRect rect;
  1044.     if (!GetBoundingRect(item, rect))
  1045.         return FALSE;
  1046.     if (rect.GetWidth() == 0 || rect.GetHeight() == 0)
  1047.         return FALSE;
  1048.     if (rect.GetBottom() < 0 || rect.GetTop() > clientSize.y)
  1049.         return FALSE;
  1050.     if (rect.GetRight() < 0 || rect.GetLeft() > clientSize.x)
  1051.         return FALSE;
  1052.  
  1053.     return TRUE;
  1054. }
  1055.  
  1056. bool wxGenericTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const
  1057. {
  1058.     wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
  1059.  
  1060.     // consider that the item does have children if it has the "+" button: it
  1061.     // might not have them (if it had never been expanded yet) but then it
  1062.     // could have them as well and it's better to err on this side rather than
  1063.     // disabling some operations which are restricted to the items with
  1064.     // children for an item which does have them
  1065.     return ((wxGenericTreeItem*) item.m_pItem)->HasPlus();
  1066. }
  1067.  
  1068. bool wxGenericTreeCtrl::IsExpanded(const wxTreeItemId& item) const
  1069. {
  1070.     wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
  1071.  
  1072.     return ((wxGenericTreeItem*) item.m_pItem)->IsExpanded();
  1073. }
  1074.  
  1075. bool wxGenericTreeCtrl::IsSelected(const wxTreeItemId& item) const
  1076. {
  1077.     wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
  1078.  
  1079.     return ((wxGenericTreeItem*) item.m_pItem)->IsSelected();
  1080. }
  1081.  
  1082. bool wxGenericTreeCtrl::IsBold(const wxTreeItemId& item) const
  1083. {
  1084.     wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
  1085.  
  1086.     return ((wxGenericTreeItem*) item.m_pItem)->IsBold();
  1087. }
  1088.  
  1089. // -----------------------------------------------------------------------------
  1090. // navigation
  1091. // -----------------------------------------------------------------------------
  1092.  
  1093. wxTreeItemId wxGenericTreeCtrl::GetParent(const wxTreeItemId& item) const
  1094. {
  1095.     wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
  1096.  
  1097.     return ((wxGenericTreeItem*) item.m_pItem)->GetParent();
  1098. }
  1099.  
  1100. wxTreeItemId wxGenericTreeCtrl::GetFirstChild(const wxTreeItemId& item, long& cookie) const
  1101. {
  1102.     wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
  1103.  
  1104.     cookie = 0;
  1105.     return GetNextChild(item, cookie);
  1106. }
  1107.  
  1108. wxTreeItemId wxGenericTreeCtrl::GetNextChild(const wxTreeItemId& item, long& cookie) const
  1109. {
  1110.     wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
  1111.  
  1112.     wxArrayGenericTreeItems& children = ((wxGenericTreeItem*) item.m_pItem)->GetChildren();
  1113.     if ( (size_t)cookie < children.Count() )
  1114.     {
  1115.         return children.Item((size_t)cookie++);
  1116.     }
  1117.     else
  1118.     {
  1119.         // there are no more of them
  1120.         return wxTreeItemId();
  1121.     }
  1122. }
  1123.  
  1124. wxTreeItemId wxGenericTreeCtrl::GetLastChild(const wxTreeItemId& item) const
  1125. {
  1126.     wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
  1127.  
  1128.     wxArrayGenericTreeItems& children = ((wxGenericTreeItem*) item.m_pItem)->GetChildren();
  1129.     return (children.IsEmpty() ? wxTreeItemId() : wxTreeItemId(children.Last()));
  1130. }
  1131.  
  1132. wxTreeItemId wxGenericTreeCtrl::GetNextSibling(const wxTreeItemId& item) const
  1133. {
  1134.     wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
  1135.  
  1136.     wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
  1137.     wxGenericTreeItem *parent = i->GetParent();
  1138.     if ( parent == NULL )
  1139.     {
  1140.         // root item doesn't have any siblings
  1141.         return wxTreeItemId();
  1142.     }
  1143.  
  1144.     wxArrayGenericTreeItems& siblings = parent->GetChildren();
  1145.     int index = siblings.Index(i);
  1146.     wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
  1147.  
  1148.     size_t n = (size_t)(index + 1);
  1149.     return n == siblings.Count() ? wxTreeItemId() : wxTreeItemId(siblings[n]);
  1150. }
  1151.  
  1152. wxTreeItemId wxGenericTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const
  1153. {
  1154.     wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
  1155.  
  1156.     wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
  1157.     wxGenericTreeItem *parent = i->GetParent();
  1158.     if ( parent == NULL )
  1159.     {
  1160.         // root item doesn't have any siblings
  1161.         return wxTreeItemId();
  1162.     }
  1163.  
  1164.     wxArrayGenericTreeItems& siblings = parent->GetChildren();
  1165.     int index = siblings.Index(i);
  1166.     wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
  1167.  
  1168.     return index == 0 ? wxTreeItemId()
  1169.                       : wxTreeItemId(siblings[(size_t)(index - 1)]);
  1170. }
  1171.  
  1172. // Only for internal use right now, but should probably be public
  1173. wxTreeItemId wxGenericTreeCtrl::GetNext(const wxTreeItemId& item) const
  1174. {
  1175.     wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
  1176.  
  1177.     wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
  1178.  
  1179.     // First see if there are any children.
  1180.     wxArrayGenericTreeItems& children = i->GetChildren();
  1181.     if (children.GetCount() > 0)
  1182.     {
  1183.          return children.Item(0);
  1184.     }
  1185.     else
  1186.     {
  1187.          // Try a sibling of this or ancestor instead
  1188.          wxTreeItemId p = item;
  1189.          wxTreeItemId toFind;
  1190.          do
  1191.          {
  1192.               toFind = GetNextSibling(p);
  1193.               p = GetParent(p);
  1194.          } while (p.IsOk() && !toFind.IsOk());
  1195.          return toFind;
  1196.     }
  1197. }
  1198.  
  1199. wxTreeItemId wxGenericTreeCtrl::GetFirstVisibleItem() const
  1200. {
  1201.     wxTreeItemId id = GetRootItem();
  1202.     if (!id.IsOk())
  1203.         return id;
  1204.  
  1205.     do
  1206.     {
  1207.         if (IsVisible(id))
  1208.               return id;
  1209.         id = GetNext(id);
  1210.     } while (id.IsOk());
  1211.  
  1212.     return wxTreeItemId();
  1213. }
  1214.  
  1215. wxTreeItemId wxGenericTreeCtrl::GetNextVisible(const wxTreeItemId& item) const
  1216. {
  1217.     wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
  1218.  
  1219.     wxTreeItemId id = item;
  1220.     if (id.IsOk())
  1221.     {
  1222.         while (id = GetNext(id), id.IsOk())
  1223.         {
  1224.             if (IsVisible(id))
  1225.                 return id;
  1226.         }
  1227.     }
  1228.     return wxTreeItemId();
  1229. }
  1230.  
  1231. wxTreeItemId wxGenericTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
  1232. {
  1233.     wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
  1234.  
  1235.     wxFAIL_MSG(wxT("not implemented"));
  1236.  
  1237.     return wxTreeItemId();
  1238. }
  1239.  
  1240. // find the first item starting with the given prefix after the given item
  1241. wxTreeItemId wxGenericTreeCtrl::FindItem(const wxTreeItemId& idParent,
  1242.                                          const wxString& prefixOrig) const
  1243. {
  1244.     // match is case insensitive as this is more convenient to the user: having
  1245.     // to press Shift-letter to go to the item starting with a capital letter
  1246.     // would be too bothersome
  1247.     wxString prefix = prefixOrig.Lower();
  1248.  
  1249.     // determine the starting point: we shouldn't take the current item (this
  1250.     // allows to switch between two items starting with the same letter just by
  1251.     // pressing it) but we shouldn't jump to the next one if the user is
  1252.     // continuing to type as otherwise he might easily skip the item he wanted
  1253.     wxTreeItemId id = idParent;
  1254.     if ( prefix.length() == 1 )
  1255.     {
  1256.         id = GetNext(id);
  1257.     }
  1258.  
  1259.     // look for the item starting with the given prefix after it
  1260.     while ( id.IsOk() && !GetItemText(id).Lower().StartsWith(prefix) )
  1261.     {
  1262.         id = GetNext(id);
  1263.     }
  1264.  
  1265.     // if we haven't found anything...
  1266.     if ( !id.IsOk() )
  1267.     {
  1268.         // ... wrap to the beginning
  1269.         id = GetRootItem();
  1270.         if ( HasFlag(wxTR_HIDE_ROOT) )
  1271.         {
  1272.             // can't select virtual root
  1273.             id = GetNext(id);
  1274.         }
  1275.  
  1276.         // and try all the items (stop when we get to the one we started from)
  1277.         while ( id != idParent && !GetItemText(id).Lower().StartsWith(prefix) )
  1278.         {
  1279.             id = GetNext(id);
  1280.         }
  1281.     }
  1282.  
  1283.     return id;
  1284. }
  1285.  
  1286. // -----------------------------------------------------------------------------
  1287. // operations
  1288. // -----------------------------------------------------------------------------
  1289.  
  1290. wxTreeItemId wxGenericTreeCtrl::DoInsertItem(const wxTreeItemId& parentId,
  1291.                                       size_t previous,
  1292.                                       const wxString& text,
  1293.                                       int image, int selImage,
  1294.                                       wxTreeItemData *data)
  1295. {
  1296.     wxGenericTreeItem *parent = (wxGenericTreeItem*) parentId.m_pItem;
  1297.     if ( !parent )
  1298.     {
  1299.         // should we give a warning here?
  1300.         return AddRoot(text, image, selImage, data);
  1301.     }
  1302.  
  1303.     m_dirty = TRUE;     // do this first so stuff below doesn't cause flicker
  1304.  
  1305.     wxGenericTreeItem *item =
  1306.         new wxGenericTreeItem( parent, text, image, selImage, data );
  1307.  
  1308.     if ( data != NULL )
  1309.     {
  1310.         data->m_pItem = (long) item;
  1311.     }
  1312.  
  1313.     parent->Insert( item, previous );
  1314.  
  1315.     return item;
  1316. }
  1317.  
  1318. wxTreeItemId wxGenericTreeCtrl::AddRoot(const wxString& text,
  1319.                                  int image, int selImage,
  1320.                                  wxTreeItemData *data)
  1321. {
  1322.     wxCHECK_MSG( !m_anchor, wxTreeItemId(), wxT("tree can have only one root") );
  1323.  
  1324.     m_dirty = TRUE;     // do this first so stuff below doesn't cause flicker
  1325.  
  1326.     m_anchor = new wxGenericTreeItem((wxGenericTreeItem *)NULL, text,
  1327.                                    image, selImage, data);
  1328.     if ( data != NULL )
  1329.     {
  1330.         data->m_pItem = (long) m_anchor;
  1331.     }
  1332.  
  1333.     if (HasFlag(wxTR_HIDE_ROOT))
  1334.     {
  1335.         // if root is hidden, make sure we can navigate
  1336.         // into children
  1337.         m_anchor->SetHasPlus();
  1338.         m_anchor->Expand();
  1339.         CalculatePositions();
  1340.     }
  1341.  
  1342.     if (!HasFlag(wxTR_MULTIPLE))
  1343.     {
  1344.         m_current = m_key_current = m_anchor;
  1345.         m_current->SetHilight( TRUE );
  1346.     }
  1347.  
  1348.     return m_anchor;
  1349. }
  1350.  
  1351. wxTreeItemId wxGenericTreeCtrl::PrependItem(const wxTreeItemId& parent,
  1352.                                      const wxString& text,
  1353.                                      int image, int selImage,
  1354.                                      wxTreeItemData *data)
  1355. {
  1356.     return DoInsertItem(parent, 0u, text, image, selImage, data);
  1357. }
  1358.  
  1359. wxTreeItemId wxGenericTreeCtrl::InsertItem(const wxTreeItemId& parentId,
  1360.                                     const wxTreeItemId& idPrevious,
  1361.                                     const wxString& text,
  1362.                                     int image, int selImage,
  1363.                                     wxTreeItemData *data)
  1364. {
  1365.     wxGenericTreeItem *parent = (wxGenericTreeItem*) parentId.m_pItem;
  1366.     if ( !parent )
  1367.     {
  1368.         // should we give a warning here?
  1369.         return AddRoot(text, image, selImage, data);
  1370.     }
  1371.  
  1372.     int index = -1;
  1373.     if (idPrevious.IsOk())
  1374.     {
  1375.         index = parent->GetChildren().Index((wxGenericTreeItem*) idPrevious.m_pItem);
  1376.         wxASSERT_MSG( index != wxNOT_FOUND,
  1377.                       wxT("previous item in wxGenericTreeCtrl::InsertItem() is not a sibling") );
  1378.     }
  1379.  
  1380.     return DoInsertItem(parentId, (size_t)++index, text, image, selImage, data);
  1381. }
  1382.  
  1383. wxTreeItemId wxGenericTreeCtrl::InsertItem(const wxTreeItemId& parentId,
  1384.                                     size_t before,
  1385.                                     const wxString& text,
  1386.                                     int image, int selImage,
  1387.                                     wxTreeItemData *data)
  1388. {
  1389.     wxGenericTreeItem *parent = (wxGenericTreeItem*) parentId.m_pItem;
  1390.     if ( !parent )
  1391.     {
  1392.         // should we give a warning here?
  1393.         return AddRoot(text, image, selImage, data);
  1394.     }
  1395.  
  1396.     return DoInsertItem(parentId, before, text, image, selImage, data);
  1397. }
  1398.  
  1399. wxTreeItemId wxGenericTreeCtrl::AppendItem(const wxTreeItemId& parentId,
  1400.                                     const wxString& text,
  1401.                                     int image, int selImage,
  1402.                                     wxTreeItemData *data)
  1403. {
  1404.     wxGenericTreeItem *parent = (wxGenericTreeItem*) parentId.m_pItem;
  1405.     if ( !parent )
  1406.     {
  1407.         // should we give a warning here?
  1408.         return AddRoot(text, image, selImage, data);
  1409.     }
  1410.  
  1411.     return DoInsertItem( parent, parent->GetChildren().Count(), text,
  1412.                          image, selImage, data);
  1413. }
  1414.  
  1415. void wxGenericTreeCtrl::SendDeleteEvent(wxGenericTreeItem *item)
  1416. {
  1417.     wxTreeEvent event( wxEVT_COMMAND_TREE_DELETE_ITEM, GetId() );
  1418.     event.m_item = (long) item;
  1419.     event.SetEventObject( this );
  1420.     ProcessEvent( event );
  1421. }
  1422.  
  1423. void wxGenericTreeCtrl::DeleteChildren(const wxTreeItemId& itemId)
  1424. {
  1425.     m_dirty = TRUE;     // do this first so stuff below doesn't cause flicker
  1426.  
  1427.     wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
  1428.     item->DeleteChildren(this);
  1429. }
  1430.  
  1431. void wxGenericTreeCtrl::Delete(const wxTreeItemId& itemId)
  1432. {
  1433.     m_dirty = TRUE;     // do this first so stuff below doesn't cause flicker
  1434.  
  1435.     wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
  1436.  
  1437.     wxGenericTreeItem *parent = item->GetParent();
  1438.  
  1439.     // don't keep stale pointers around!
  1440.     if ( IsDescendantOf(item, m_key_current) )
  1441.     {
  1442.         m_key_current = parent;
  1443.     }
  1444.  
  1445.     if ( IsDescendantOf(item, m_current) )
  1446.     {
  1447.         m_current = parent;
  1448.     }
  1449.  
  1450.     // remove the item from the tree
  1451.     if ( parent )
  1452.     {
  1453.         parent->GetChildren().Remove( item );  // remove by value
  1454.     }
  1455.     else // deleting the root
  1456.     {
  1457.         // nothing will be left in the tree
  1458.         m_anchor = NULL;
  1459.     }
  1460.  
  1461.     // and delete all of its children and the item itself now
  1462.     item->DeleteChildren(this);
  1463.     SendDeleteEvent(item);
  1464.     delete item;
  1465. }
  1466.  
  1467. void wxGenericTreeCtrl::DeleteAllItems()
  1468. {
  1469.     if ( m_anchor )
  1470.     {
  1471.         Delete(m_anchor);
  1472.     }
  1473. }
  1474.  
  1475. void wxGenericTreeCtrl::Expand(const wxTreeItemId& itemId)
  1476. {
  1477.     wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
  1478.  
  1479.     wxCHECK_RET( item, _T("invalid item in wxGenericTreeCtrl::Expand") );
  1480.     wxCHECK_RET( !HasFlag(wxTR_HIDE_ROOT) || itemId != GetRootItem(),
  1481.                  _T("can't expand hidden root") );
  1482.  
  1483.     if ( !item->HasPlus() )
  1484.         return;
  1485.  
  1486.     if ( item->IsExpanded() )
  1487.         return;
  1488.  
  1489.     wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_EXPANDING, GetId() );
  1490.     event.m_item = (long) item;
  1491.     event.SetEventObject( this );
  1492.  
  1493.     if ( ProcessEvent( event ) && !event.IsAllowed() )
  1494.     {
  1495.         // cancelled by program
  1496.         return;
  1497.     }
  1498.  
  1499.     item->Expand();
  1500.     CalculatePositions();
  1501.  
  1502.     RefreshSubtree(item);
  1503.  
  1504.     event.SetEventType(wxEVT_COMMAND_TREE_ITEM_EXPANDED);
  1505.     ProcessEvent( event );
  1506. }
  1507.  
  1508. void wxGenericTreeCtrl::ExpandAll(const wxTreeItemId& item)
  1509. {
  1510.     if ( !HasFlag(wxTR_HIDE_ROOT) || item != GetRootItem())
  1511.     {
  1512.         Expand(item);
  1513.         if ( !IsExpanded(item) )
  1514.             return;
  1515.     }
  1516.  
  1517.     long cookie;
  1518.     wxTreeItemId child = GetFirstChild(item, cookie);
  1519.     while ( child.IsOk() )
  1520.     {
  1521.         ExpandAll(child);
  1522.  
  1523.         child = GetNextChild(item, cookie);
  1524.     }
  1525. }
  1526.  
  1527. void wxGenericTreeCtrl::Collapse(const wxTreeItemId& itemId)
  1528. {
  1529.     wxCHECK_RET( !HasFlag(wxTR_HIDE_ROOT) || itemId != GetRootItem(),
  1530.                  _T("can't collapse hidden root") );
  1531.  
  1532.     wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
  1533.  
  1534.     if ( !item->IsExpanded() )
  1535.         return;
  1536.  
  1537.     wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_COLLAPSING, GetId() );
  1538.     event.m_item = (long) item;
  1539.     event.SetEventObject( this );
  1540.     if ( ProcessEvent( event ) && !event.IsAllowed() )
  1541.     {
  1542.         // cancelled by program
  1543.         return;
  1544.     }
  1545.  
  1546.     item->Collapse();
  1547.  
  1548. #if 0  // TODO why should items be collapsed recursively?
  1549.     wxArrayGenericTreeItems& children = item->GetChildren();
  1550.     size_t count = children.Count();
  1551.     for ( size_t n = 0; n < count; n++ )
  1552.     {
  1553.         Collapse(children[n]);
  1554.     }
  1555. #endif
  1556.  
  1557.     CalculatePositions();
  1558.  
  1559.     RefreshSubtree(item);
  1560.  
  1561.     event.SetEventType(wxEVT_COMMAND_TREE_ITEM_COLLAPSED);
  1562.     ProcessEvent( event );
  1563. }
  1564.  
  1565. void wxGenericTreeCtrl::CollapseAndReset(const wxTreeItemId& item)
  1566. {
  1567.     Collapse(item);
  1568.     DeleteChildren(item);
  1569. }
  1570.  
  1571. void wxGenericTreeCtrl::Toggle(const wxTreeItemId& itemId)
  1572. {
  1573.     wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
  1574.  
  1575.     if (item->IsExpanded())
  1576.         Collapse(itemId);
  1577.     else
  1578.         Expand(itemId);
  1579. }
  1580.  
  1581. void wxGenericTreeCtrl::Unselect()
  1582. {
  1583.     if (m_current)
  1584.     {
  1585.         m_current->SetHilight( FALSE );
  1586.         RefreshLine( m_current );
  1587.  
  1588.         m_current = NULL;
  1589.     }
  1590. }
  1591.  
  1592. void wxGenericTreeCtrl::UnselectAllChildren(wxGenericTreeItem *item)
  1593. {
  1594.     if (item->IsSelected())
  1595.     {
  1596.         item->SetHilight(FALSE);
  1597.         RefreshLine(item);
  1598.     }
  1599.  
  1600.     if (item->HasChildren())
  1601.     {
  1602.         wxArrayGenericTreeItems& children = item->GetChildren();
  1603.         size_t count = children.Count();
  1604.         for ( size_t n = 0; n < count; ++n )
  1605.         {
  1606.             UnselectAllChildren(children[n]);
  1607.         }
  1608.     }
  1609. }
  1610.  
  1611. void wxGenericTreeCtrl::UnselectAll()
  1612. {
  1613.     wxTreeItemId rootItem = GetRootItem();
  1614.  
  1615.     // the tree might not have the root item at all
  1616.     if ( rootItem )
  1617.     {
  1618.         UnselectAllChildren((wxGenericTreeItem*) rootItem.m_pItem);
  1619.     }
  1620. }
  1621.  
  1622. // Recursive function !
  1623. // To stop we must have crt_item<last_item
  1624. // Algorithm :
  1625. // Tag all next children, when no more children,
  1626. // Move to parent (not to tag)
  1627. // Keep going... if we found last_item, we stop.
  1628. bool wxGenericTreeCtrl::TagNextChildren(wxGenericTreeItem *crt_item, wxGenericTreeItem *last_item, bool select)
  1629. {
  1630.     wxGenericTreeItem *parent = crt_item->GetParent();
  1631.  
  1632.     if (parent == NULL) // This is root item
  1633.         return TagAllChildrenUntilLast(crt_item, last_item, select);
  1634.  
  1635.     wxArrayGenericTreeItems& children = parent->GetChildren();
  1636.     int index = children.Index(crt_item);
  1637.     wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
  1638.  
  1639.     size_t count = children.Count();
  1640.     for (size_t n=(size_t)(index+1); n<count; ++n)
  1641.     {
  1642.         if (TagAllChildrenUntilLast(children[n], last_item, select)) return TRUE;
  1643.     }
  1644.  
  1645.     return TagNextChildren(parent, last_item, select);
  1646. }
  1647.  
  1648. bool wxGenericTreeCtrl::TagAllChildrenUntilLast(wxGenericTreeItem *crt_item, wxGenericTreeItem *last_item, bool select)
  1649. {
  1650.     crt_item->SetHilight(select);
  1651.     RefreshLine(crt_item);
  1652.  
  1653.     if (crt_item==last_item)
  1654.         return TRUE;
  1655.  
  1656.     if (crt_item->HasChildren())
  1657.     {
  1658.         wxArrayGenericTreeItems& children = crt_item->GetChildren();
  1659.         size_t count = children.Count();
  1660.         for ( size_t n = 0; n < count; ++n )
  1661.         {
  1662.             if (TagAllChildrenUntilLast(children[n], last_item, select))
  1663.                 return TRUE;
  1664.         }
  1665.     }
  1666.  
  1667.   return FALSE;
  1668. }
  1669.  
  1670. void wxGenericTreeCtrl::SelectItemRange(wxGenericTreeItem *item1, wxGenericTreeItem *item2)
  1671. {
  1672.     // item2 is not necessary after item1
  1673.     wxGenericTreeItem *first=NULL, *last=NULL;
  1674.  
  1675.     // choice first' and 'last' between item1 and item2
  1676.     if (item1->GetY()<item2->GetY())
  1677.     {
  1678.         first=item1;
  1679.         last=item2;
  1680.     }
  1681.     else
  1682.     {
  1683.         first=item2;
  1684.         last=item1;
  1685.     }
  1686.  
  1687.     bool select = m_current->IsSelected();
  1688.  
  1689.     if ( TagAllChildrenUntilLast(first,last,select) )
  1690.         return;
  1691.  
  1692.     TagNextChildren(first,last,select);
  1693. }
  1694.  
  1695. void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId,
  1696.                                    bool unselect_others,
  1697.                                    bool extended_select)
  1698. {
  1699.     wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") );
  1700.  
  1701.     bool is_single=!(GetWindowStyleFlag() & wxTR_MULTIPLE);
  1702.     wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
  1703.  
  1704.     //wxCHECK_RET( ( (!unselect_others) && is_single),
  1705.     //           wxT("this is a single selection tree") );
  1706.  
  1707.     // to keep going anyhow !!!
  1708.     if (is_single)
  1709.     {
  1710.         if (item->IsSelected())
  1711.             return; // nothing to do
  1712.         unselect_others = TRUE;
  1713.         extended_select = FALSE;
  1714.     }
  1715.     else if ( unselect_others && item->IsSelected() )
  1716.     {
  1717.         // selection change if there is more than one item currently selected
  1718.         wxArrayTreeItemIds selected_items;
  1719.         if ( GetSelections(selected_items) == 1 )
  1720.             return;
  1721.     }
  1722.  
  1723.     wxTreeEvent event( wxEVT_COMMAND_TREE_SEL_CHANGING, GetId() );
  1724.     event.m_item = (long) item;
  1725.     event.m_itemOld = (long) m_current;
  1726.     event.SetEventObject( this );
  1727.     // TODO : Here we don't send any selection mode yet !
  1728.  
  1729.     if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() )
  1730.         return;
  1731.  
  1732.     wxTreeItemId parent = GetParent( itemId );
  1733.     while (parent.IsOk())
  1734.     {
  1735.         if (!IsExpanded(parent))
  1736.             Expand( parent );
  1737.  
  1738.         parent = GetParent( parent );
  1739.     }
  1740.  
  1741.     EnsureVisible( itemId );
  1742.  
  1743.     // ctrl press
  1744.     if (unselect_others)
  1745.     {
  1746.         if (is_single) Unselect(); // to speed up thing
  1747.         else UnselectAll();
  1748.     }
  1749.  
  1750.     // shift press
  1751.     if (extended_select)
  1752.     {
  1753.         if ( !m_current )
  1754.         {
  1755.             m_current = m_key_current = (wxGenericTreeItem*) GetRootItem().m_pItem;
  1756.         }
  1757.  
  1758.         // don't change the mark (m_current)
  1759.         SelectItemRange(m_current, item);
  1760.     }
  1761.     else
  1762.     {
  1763.         bool select=TRUE; // the default
  1764.  
  1765.         // Check if we need to toggle hilight (ctrl mode)
  1766.         if (!unselect_others)
  1767.             select=!item->IsSelected();
  1768.  
  1769.         m_current = m_key_current = item;
  1770.         m_current->SetHilight(select);
  1771.         RefreshLine( m_current );
  1772.     }
  1773.  
  1774.     event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
  1775.     GetEventHandler()->ProcessEvent( event );
  1776. }
  1777.  
  1778. void wxGenericTreeCtrl::FillArray(wxGenericTreeItem *item,
  1779.                                   wxArrayTreeItemIds &array) const
  1780. {
  1781.     if ( item->IsSelected() )
  1782.         array.Add(wxTreeItemId(item));
  1783.  
  1784.     if ( item->HasChildren() )
  1785.     {
  1786.         wxArrayGenericTreeItems& children = item->GetChildren();
  1787.         size_t count = children.GetCount();
  1788.         for ( size_t n = 0; n < count; ++n )
  1789.             FillArray(children[n], array);
  1790.     }
  1791. }
  1792.  
  1793. size_t wxGenericTreeCtrl::GetSelections(wxArrayTreeItemIds &array) const
  1794. {
  1795.     array.Empty();
  1796.     wxTreeItemId idRoot = GetRootItem();
  1797.     if ( idRoot.IsOk() )
  1798.     {
  1799.         FillArray((wxGenericTreeItem*) idRoot.m_pItem, array);
  1800.     }
  1801.     //else: the tree is empty, so no selections
  1802.  
  1803.     return array.Count();
  1804. }
  1805.  
  1806. void wxGenericTreeCtrl::EnsureVisible(const wxTreeItemId& item)
  1807. {
  1808.     if (!item.IsOk()) return;
  1809.  
  1810.     wxGenericTreeItem *gitem = (wxGenericTreeItem*) item.m_pItem;
  1811.  
  1812.     // first expand all parent branches
  1813.     wxGenericTreeItem *parent = gitem->GetParent();
  1814.  
  1815.     if ( HasFlag(wxTR_HIDE_ROOT) )
  1816.     {
  1817.         while ( parent != m_anchor )
  1818.         {
  1819.             Expand(parent);
  1820.             parent = parent->GetParent();
  1821.         }
  1822.     }
  1823.     else
  1824.     {
  1825.         while ( parent )
  1826.         {
  1827.             Expand(parent);
  1828.             parent = parent->GetParent();
  1829.         }
  1830.     }
  1831.  
  1832.     //if (parent) CalculatePositions();
  1833.  
  1834.     ScrollTo(item);
  1835. }
  1836.  
  1837. void wxGenericTreeCtrl::ScrollTo(const wxTreeItemId &item)
  1838. {
  1839.     if (!item.IsOk()) return;
  1840.  
  1841.     // We have to call this here because the label in
  1842.     // question might just have been added and no screen
  1843.     // update taken place.
  1844.     if (m_dirty) wxYieldIfNeeded();
  1845.  
  1846.     wxGenericTreeItem *gitem = (wxGenericTreeItem*) item.m_pItem;
  1847.  
  1848.     // now scroll to the item
  1849.     int item_y = gitem->GetY();
  1850.  
  1851.     int start_x = 0;
  1852.     int start_y = 0;
  1853.     GetViewStart( &start_x, &start_y );
  1854.     start_y *= PIXELS_PER_UNIT;
  1855.  
  1856.     int client_h = 0;
  1857.     int client_w = 0;
  1858.     GetClientSize( &client_w, &client_h );
  1859.  
  1860.     if (item_y < start_y+3)
  1861.     {
  1862.         // going down
  1863.         int x = 0;
  1864.         int y = 0;
  1865.         m_anchor->GetSize( x, y, this );
  1866.         y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
  1867.         x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
  1868.         int x_pos = GetScrollPos( wxHORIZONTAL );
  1869.         // Item should appear at top
  1870.         SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT, x/PIXELS_PER_UNIT, y/PIXELS_PER_UNIT, x_pos, item_y/PIXELS_PER_UNIT );
  1871.     }
  1872.     else if (item_y+GetLineHeight(gitem) > start_y+client_h)
  1873.     {
  1874.         // going up
  1875.         int x = 0;
  1876.         int y = 0;
  1877.         m_anchor->GetSize( x, y, this );
  1878.         y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
  1879.         x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
  1880.         item_y += PIXELS_PER_UNIT+2;
  1881.         int x_pos = GetScrollPos( wxHORIZONTAL );
  1882.         // Item should appear at bottom
  1883.         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 );
  1884.     }
  1885. }
  1886.  
  1887. // FIXME: tree sorting functions are not reentrant and not MT-safe!
  1888. static wxGenericTreeCtrl *s_treeBeingSorted = NULL;
  1889.  
  1890. static int LINKAGEMODE tree_ctrl_compare_func(wxGenericTreeItem **item1,
  1891.                                   wxGenericTreeItem **item2)
  1892. {
  1893.     wxCHECK_MSG( s_treeBeingSorted, 0, wxT("bug in wxGenericTreeCtrl::SortChildren()") );
  1894.  
  1895.     return s_treeBeingSorted->OnCompareItems(*item1, *item2);
  1896. }
  1897.  
  1898. int wxGenericTreeCtrl::OnCompareItems(const wxTreeItemId& item1,
  1899.                                const wxTreeItemId& item2)
  1900. {
  1901.     return wxStrcmp(GetItemText(item1), GetItemText(item2));
  1902. }
  1903.  
  1904. void wxGenericTreeCtrl::SortChildren(const wxTreeItemId& itemId)
  1905. {
  1906.     wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") );
  1907.  
  1908.     wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
  1909.  
  1910.     wxCHECK_RET( !s_treeBeingSorted,
  1911.                  wxT("wxGenericTreeCtrl::SortChildren is not reentrant") );
  1912.  
  1913.     wxArrayGenericTreeItems& children = item->GetChildren();
  1914.     if ( children.Count() > 1 )
  1915.     {
  1916.         m_dirty = TRUE;
  1917.  
  1918.         s_treeBeingSorted = this;
  1919.         children.Sort(tree_ctrl_compare_func);
  1920.         s_treeBeingSorted = NULL;
  1921.     }
  1922.     //else: don't make the tree dirty as nothing changed
  1923. }
  1924.  
  1925. wxImageList *wxGenericTreeCtrl::GetImageList() const
  1926. {
  1927.     return m_imageListNormal;
  1928. }
  1929.  
  1930. wxImageList *wxGenericTreeCtrl::GetButtonsImageList() const
  1931. {
  1932.     return m_imageListButtons;
  1933. }
  1934.  
  1935. wxImageList *wxGenericTreeCtrl::GetStateImageList() const
  1936. {
  1937.     return m_imageListState;
  1938. }
  1939.  
  1940. void wxGenericTreeCtrl::CalculateLineHeight()
  1941. {
  1942.     wxClientDC dc(this);
  1943.     m_lineHeight = (int)(dc.GetCharHeight() + 4);
  1944.  
  1945.     if ( m_imageListNormal )
  1946.     {
  1947.         // Calculate a m_lineHeight value from the normal Image sizes.
  1948.         // May be toggle off. Then wxGenericTreeCtrl will spread when
  1949.         // necessary (which might look ugly).
  1950.         int n = m_imageListNormal->GetImageCount();
  1951.         for (int i = 0; i < n ; i++)
  1952.         {
  1953.             int width = 0, height = 0;
  1954.             m_imageListNormal->GetSize(i, width, height);
  1955.             if (height > m_lineHeight) m_lineHeight = height;
  1956.         }
  1957.     }
  1958.  
  1959.     if (m_imageListButtons)
  1960.     {
  1961.         // Calculate a m_lineHeight value from the Button image sizes.
  1962.         // May be toggle off. Then wxGenericTreeCtrl will spread when
  1963.         // necessary (which might look ugly).
  1964.         int n = m_imageListButtons->GetImageCount();
  1965.         for (int i = 0; i < n ; i++)
  1966.         {
  1967.             int width = 0, height = 0;
  1968.             m_imageListButtons->GetSize(i, width, height);
  1969.             if (height > m_lineHeight) m_lineHeight = height;
  1970.         }
  1971.     }
  1972.  
  1973.     if (m_lineHeight < 30)
  1974.         m_lineHeight += 2;                 // at least 2 pixels
  1975.     else
  1976.         m_lineHeight += m_lineHeight/10;   // otherwise 10% extra spacing
  1977. }
  1978.  
  1979. void wxGenericTreeCtrl::SetImageList(wxImageList *imageList)
  1980. {
  1981.     if (m_ownsImageListNormal) delete m_imageListNormal;
  1982.     m_imageListNormal = imageList;
  1983.     m_ownsImageListNormal = FALSE;
  1984.     m_dirty = TRUE;
  1985.     // Don't do any drawing if we're setting the list to NULL,
  1986.     // since we may be in the process of deleting the tree control.
  1987.     if (imageList)
  1988.         CalculateLineHeight();
  1989. }
  1990.  
  1991. void wxGenericTreeCtrl::SetStateImageList(wxImageList *imageList)
  1992. {
  1993.     if (m_ownsImageListState) delete m_imageListState;
  1994.     m_imageListState = imageList;
  1995.     m_ownsImageListState = FALSE;
  1996. }
  1997.  
  1998. void wxGenericTreeCtrl::SetButtonsImageList(wxImageList *imageList)
  1999. {
  2000.     if (m_ownsImageListButtons) delete m_imageListButtons;
  2001.     m_imageListButtons = imageList;
  2002.     m_ownsImageListButtons = FALSE;
  2003.     m_dirty = TRUE;
  2004.     CalculateLineHeight();
  2005. }
  2006.  
  2007. void wxGenericTreeCtrl::AssignImageList(wxImageList *imageList)
  2008. {
  2009.     SetImageList(imageList);
  2010.     m_ownsImageListNormal = TRUE;
  2011. }
  2012.  
  2013. void wxGenericTreeCtrl::AssignStateImageList(wxImageList *imageList)
  2014. {
  2015.     SetStateImageList(imageList);
  2016.     m_ownsImageListState = TRUE;
  2017. }
  2018.  
  2019. void wxGenericTreeCtrl::AssignButtonsImageList(wxImageList *imageList)
  2020. {
  2021.     SetButtonsImageList(imageList);
  2022.     m_ownsImageListButtons = TRUE;
  2023. }
  2024.  
  2025. // -----------------------------------------------------------------------------
  2026. // helpers
  2027. // -----------------------------------------------------------------------------
  2028.  
  2029. void wxGenericTreeCtrl::AdjustMyScrollbars()
  2030. {
  2031.     if (m_anchor)
  2032.     {
  2033.         int x = 0, y = 0;
  2034.         m_anchor->GetSize( x, y, this );
  2035.         y += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
  2036.         x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels
  2037.         int x_pos = GetScrollPos( wxHORIZONTAL );
  2038.         int y_pos = GetScrollPos( wxVERTICAL );
  2039.         SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT, x/PIXELS_PER_UNIT, y/PIXELS_PER_UNIT, x_pos, y_pos );
  2040.     }
  2041.     else
  2042.     {
  2043.         SetScrollbars( 0, 0, 0, 0 );
  2044.     }
  2045. }
  2046.  
  2047. int wxGenericTreeCtrl::GetLineHeight(wxGenericTreeItem *item) const
  2048. {
  2049.     if (GetWindowStyleFlag() & wxTR_HAS_VARIABLE_ROW_HEIGHT)
  2050.         return item->GetHeight();
  2051.     else
  2052.         return m_lineHeight;
  2053. }
  2054.  
  2055. void wxGenericTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc)
  2056. {
  2057.     // TODO implement "state" icon on items
  2058.  
  2059.     wxTreeItemAttr *attr = item->GetAttributes();
  2060.     if ( attr && attr->HasFont() )
  2061.         dc.SetFont(attr->GetFont());
  2062.     else if (item->IsBold())
  2063.         dc.SetFont(m_boldFont);
  2064.  
  2065.     long text_w = 0, text_h = 0;
  2066.     dc.GetTextExtent( item->GetText(), &text_w, &text_h );
  2067.  
  2068.     int image_h = 0, image_w = 0;
  2069.     int image = item->GetCurrentImage();
  2070.     if ( image != NO_IMAGE )
  2071.     {
  2072.         if ( m_imageListNormal )
  2073.         {
  2074.             m_imageListNormal->GetSize( image, image_w, image_h );
  2075.             image_w += 4;
  2076.         }
  2077.         else
  2078.         {
  2079.             image = NO_IMAGE;
  2080.         }
  2081.     }
  2082.  
  2083.     int total_h = GetLineHeight(item);
  2084.  
  2085.     if ( item->IsSelected() )
  2086.     {
  2087.         dc.SetBrush(*(m_hasFocus ? m_hilightBrush : m_hilightUnfocusedBrush));
  2088.     }
  2089.     else
  2090.     {
  2091.         wxColour colBg;
  2092.         if ( attr && attr->HasBackgroundColour() )
  2093.             colBg = attr->GetBackgroundColour();
  2094.         else
  2095.             colBg = m_backgroundColour;
  2096.         dc.SetBrush(wxBrush(colBg, wxSOLID));
  2097.     }
  2098.  
  2099.     int offset = HasFlag(wxTR_ROW_LINES) ? 1 : 0;
  2100.  
  2101.     if ( HasFlag(wxTR_FULL_ROW_HIGHLIGHT) )
  2102.     {
  2103.         int x, y, w, h;
  2104.  
  2105.         DoGetPosition(&x, &y);
  2106.         DoGetSize(&w, &h);
  2107.         dc.DrawRectangle(x, item->GetY()+offset, w, total_h-offset);
  2108.     }
  2109.     else
  2110.     {
  2111.         if ( item->IsSelected() && image != NO_IMAGE )
  2112.         {
  2113.             // If it's selected, and there's an image, then we should
  2114.             // take care to leave the area under the image painted in the
  2115.             // background colour.
  2116.             dc.DrawRectangle( item->GetX() + image_w - 2, item->GetY()+offset,
  2117.                               item->GetWidth() - image_w + 2, total_h-offset );
  2118.         }
  2119.         else
  2120.         {
  2121.             dc.DrawRectangle( item->GetX()-2, item->GetY()+offset,
  2122.                               item->GetWidth()+2, total_h-offset );
  2123.         }
  2124.     }
  2125.  
  2126.     if ( image != NO_IMAGE )
  2127.     {
  2128.         dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, total_h );
  2129.         m_imageListNormal->Draw( image, dc,
  2130.                                  item->GetX(),
  2131.                                  item->GetY() +((total_h > image_h)?((total_h-image_h)/2):0),
  2132.                                  wxIMAGELIST_DRAW_TRANSPARENT );
  2133.         dc.DestroyClippingRegion();
  2134.     }
  2135.  
  2136.     dc.SetBackgroundMode(wxTRANSPARENT);
  2137.     int extraH = (total_h > text_h) ? (total_h - text_h)/2 : 0;
  2138.     dc.DrawText( item->GetText(),
  2139.                  (wxCoord)(image_w + item->GetX()),
  2140.                  (wxCoord)(item->GetY() + extraH));
  2141.  
  2142.     // restore normal font
  2143.     dc.SetFont( m_normalFont );
  2144. }
  2145.  
  2146. // Now y stands for the top of the item, whereas it used to stand for middle !
  2147. void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y )
  2148. {
  2149.     int x = level*m_indent;
  2150.     if (!HasFlag(wxTR_HIDE_ROOT))
  2151.     {
  2152.         x += m_indent;
  2153.     }
  2154.     else if (level == 0)
  2155.     {
  2156.         // always expand hidden root
  2157.         int origY = y;
  2158.         wxArrayGenericTreeItems& children = item->GetChildren();
  2159.         int count = children.Count();
  2160.         if (count > 0)
  2161.         {
  2162.             int n = 0, oldY;
  2163.             do {
  2164.                 oldY = y;
  2165.                 PaintLevel(children[n], dc, 1, y);
  2166.             } while (++n < count);
  2167.  
  2168.             if (!HasFlag(wxTR_NO_LINES) && HasFlag(wxTR_LINES_AT_ROOT) && count > 0)
  2169.             {
  2170.                 // draw line down to last child
  2171.                 origY += GetLineHeight(children[0])>>1;
  2172.                 oldY += GetLineHeight(children[n-1])>>1;
  2173.                 dc.DrawLine(3, origY, 3, oldY);
  2174.             }
  2175.         }
  2176.         return;
  2177.     }
  2178.  
  2179.     item->SetX(x+m_spacing);
  2180.     item->SetY(y);
  2181.  
  2182.     int h = GetLineHeight(item);
  2183.     int y_top = y;
  2184.     int y_mid = y_top + (h>>1);
  2185.     y += h;
  2186.  
  2187.     int exposed_x = dc.LogicalToDeviceX(0);
  2188.     int exposed_y = dc.LogicalToDeviceY(y_top);
  2189.  
  2190.     if (IsExposed(exposed_x, exposed_y, 10000, h))  // 10000 = very much
  2191.     {
  2192.         wxPen *pen =
  2193. #ifndef __WXMAC__
  2194.             // don't draw rect outline if we already have the
  2195.             // background color under Mac
  2196.             (item->IsSelected() && m_hasFocus) ? wxBLACK_PEN :
  2197. #endif // !__WXMAC__
  2198.             wxTRANSPARENT_PEN;
  2199.  
  2200.         wxColour colText;
  2201.         if ( item->IsSelected() )
  2202.         {
  2203.             colText = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
  2204.         }
  2205.         else
  2206.         {
  2207.             wxTreeItemAttr *attr = item->GetAttributes();
  2208.             if (attr && attr->HasTextColour())
  2209.                 colText = attr->GetTextColour();
  2210.             else
  2211.                 colText = GetForegroundColour();
  2212.         }
  2213.  
  2214.         // prepare to draw
  2215.         dc.SetTextForeground(colText);
  2216.         dc.SetPen(*pen);
  2217.  
  2218.         // draw
  2219.         PaintItem(item, dc);
  2220.  
  2221.         if (HasFlag(wxTR_ROW_LINES))
  2222.         {
  2223.             // if the background colour is white, choose a
  2224.             // contrasting color for the lines
  2225.             dc.SetPen(*((GetBackgroundColour() == *wxWHITE)
  2226.                          ? wxMEDIUM_GREY_PEN : wxWHITE_PEN));
  2227.             dc.DrawLine(0, y_top, 10000, y_top);
  2228.             dc.DrawLine(0, y, 10000, y);
  2229.         }
  2230.  
  2231.         // restore DC objects
  2232.         dc.SetBrush(*wxWHITE_BRUSH);
  2233.         dc.SetPen(m_dottedPen);
  2234.         dc.SetTextForeground(*wxBLACK);
  2235.  
  2236.         if (item->HasPlus() && HasButtons())  // should the item show a button?
  2237.         {
  2238.             if (!HasFlag(wxTR_NO_LINES))
  2239.             {
  2240.                 if (x > (signed)m_indent)
  2241.                     dc.DrawLine(x - m_indent, y_mid, x - 5, y_mid);
  2242.                 else if (HasFlag(wxTR_LINES_AT_ROOT))
  2243.                     dc.DrawLine(3, y_mid, x - 5, y_mid);
  2244.                 dc.DrawLine(x + 5, y_mid, x + m_spacing, y_mid);
  2245.             }
  2246.  
  2247.             if (m_imageListButtons != NULL)
  2248.             {
  2249.                 // draw the image button here
  2250.                 int image_h = 0, image_w = 0, image = wxTreeItemIcon_Normal;
  2251.                 if (item->IsExpanded()) image = wxTreeItemIcon_Expanded;
  2252.                 if (item->IsSelected())
  2253.                     image += wxTreeItemIcon_Selected - wxTreeItemIcon_Normal;
  2254.                 m_imageListButtons->GetSize(image, image_w, image_h);
  2255.                 int xx = x - (image_w>>1);
  2256.                 int yy = y_mid - (image_h>>1);
  2257.                 dc.SetClippingRegion(xx, yy, image_w, image_h);
  2258.                 m_imageListButtons->Draw(image, dc, xx, yy,
  2259.                                          wxIMAGELIST_DRAW_TRANSPARENT);
  2260.                 dc.DestroyClippingRegion();
  2261.             }
  2262.             else if (HasFlag(wxTR_TWIST_BUTTONS))
  2263.             {
  2264.                 // draw the twisty button here
  2265.  
  2266.                 if (HasFlag(wxTR_AQUA_BUTTONS))
  2267.                 {
  2268.                     if (item->IsExpanded())
  2269.                         dc.DrawBitmap( *m_arrowDown, x-5, y_mid-6, TRUE );
  2270.                     else
  2271.                         dc.DrawBitmap( *m_arrowRight, x-5, y_mid-6, TRUE );
  2272.                 }
  2273.                 else
  2274.                 {
  2275.                     dc.SetBrush(*m_hilightBrush);
  2276.                     dc.SetPen(*wxBLACK_PEN);
  2277.                     wxPoint button[3];
  2278.  
  2279.                     if (item->IsExpanded())
  2280.                     {
  2281.                         button[0].x = x-5;
  2282.                         button[0].y = y_mid-2;
  2283.                         button[1].x = x+5;
  2284.                         button[1].y = y_mid-2;
  2285.                         button[2].x = x;
  2286.                         button[2].y = y_mid+3;
  2287.                     }
  2288.                     else
  2289.                     {
  2290.                         button[0].y = y_mid-5;
  2291.                         button[0].x = x-2;
  2292.                         button[1].y = y_mid+5;
  2293.                         button[1].x = x-2;
  2294.                         button[2].y = y_mid;
  2295.                         button[2].x = x+3;
  2296.                     }
  2297.                     dc.DrawPolygon(3, button);
  2298.                     dc.SetPen(m_dottedPen);
  2299.                 }
  2300.             }
  2301.             else // if (HasFlag(wxTR_HAS_BUTTONS))
  2302.             {
  2303.                 // draw the plus sign here
  2304.                 dc.SetPen(*wxGREY_PEN);
  2305.                 dc.SetBrush(*wxWHITE_BRUSH);
  2306.                 dc.DrawRectangle(x-5, y_mid-4, 11, 9);
  2307.                 dc.SetPen(*wxBLACK_PEN);
  2308.                 dc.DrawLine(x-2, y_mid, x+3, y_mid);
  2309.                 if (!item->IsExpanded())
  2310.                     dc.DrawLine(x, y_mid-2, x, y_mid+3);
  2311.                 dc.SetPen(m_dottedPen);
  2312.             }
  2313.         }
  2314.         else if (!HasFlag(wxTR_NO_LINES))  // no button; maybe a line?
  2315.         {
  2316.             // draw the horizontal line here
  2317.             int x_start = x;
  2318.             if (x > (signed)m_indent)
  2319.                 x_start -= m_indent;
  2320.             else if (HasFlag(wxTR_LINES_AT_ROOT))
  2321.                 x_start = 3;
  2322.             dc.DrawLine(x_start, y_mid, x + m_spacing, y_mid);
  2323.         }
  2324.     }
  2325.  
  2326.     if (item->IsExpanded())
  2327.     {
  2328.         wxArrayGenericTreeItems& children = item->GetChildren();
  2329.         int count = children.Count();
  2330.         if (count > 0)
  2331.         {
  2332.             int n = 0, oldY;
  2333.             ++level;
  2334.             do {
  2335.                 oldY = y;
  2336.                 PaintLevel(children[n], dc, level, y);
  2337.             } while (++n < count);
  2338.  
  2339.             if (!HasFlag(wxTR_NO_LINES) && count > 0)
  2340.             {
  2341.                 // draw line down to last child
  2342.                 oldY += GetLineHeight(children[n-1])>>1;
  2343.                 if (HasButtons()) y_mid += 5;
  2344.                 dc.DrawLine(x, y_mid, x, oldY);
  2345.             }
  2346.         }
  2347.     }
  2348. }
  2349.  
  2350. void wxGenericTreeCtrl::DrawDropEffect(wxGenericTreeItem *item)
  2351. {
  2352.     if ( item )
  2353.     {
  2354.         if ( item->HasPlus() )
  2355.         {
  2356.             // it's a folder, indicate it by a border
  2357.             DrawBorder(item);
  2358.         }
  2359.         else
  2360.         {
  2361.             // draw a line under the drop target because the item will be
  2362.             // dropped there
  2363.             DrawLine(item, TRUE /* below */);
  2364.         }
  2365.  
  2366.         SetCursor(wxCURSOR_BULLSEYE);
  2367.     }
  2368.     else
  2369.     {
  2370.         // can't drop here
  2371.         SetCursor(wxCURSOR_NO_ENTRY);
  2372.     }
  2373. }
  2374.  
  2375. void wxGenericTreeCtrl::DrawBorder(const wxTreeItemId &item)
  2376. {
  2377.     wxCHECK_RET( item.IsOk(), _T("invalid item in wxGenericTreeCtrl::DrawLine") );
  2378.  
  2379.     wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
  2380.  
  2381.     wxClientDC dc(this);
  2382.     PrepareDC( dc );
  2383.     dc.SetLogicalFunction(wxINVERT);
  2384.     dc.SetBrush(*wxTRANSPARENT_BRUSH);
  2385.  
  2386.     int w = i->GetWidth() + 2;
  2387.     int h = GetLineHeight(i) + 2;
  2388.  
  2389.     dc.DrawRectangle( i->GetX() - 1, i->GetY() - 1, w, h);
  2390. }
  2391.  
  2392. void wxGenericTreeCtrl::DrawLine(const wxTreeItemId &item, bool below)
  2393. {
  2394.     wxCHECK_RET( item.IsOk(), _T("invalid item in wxGenericTreeCtrl::DrawLine") );
  2395.  
  2396.     wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
  2397.  
  2398.     wxClientDC dc(this);
  2399.     PrepareDC( dc );
  2400.     dc.SetLogicalFunction(wxINVERT);
  2401.  
  2402.     int x = i->GetX(),
  2403.         y = i->GetY();
  2404.     if ( below )
  2405.     {
  2406.         y += GetLineHeight(i) - 1;
  2407.     }
  2408.  
  2409.     dc.DrawLine( x, y, x + i->GetWidth(), y);
  2410. }
  2411.  
  2412. // -----------------------------------------------------------------------------
  2413. // wxWindows callbacks
  2414. // -----------------------------------------------------------------------------
  2415.  
  2416. void wxGenericTreeCtrl::OnPaint( wxPaintEvent &WXUNUSED(event) )
  2417. {
  2418.     wxPaintDC dc(this);
  2419.     PrepareDC( dc );
  2420.  
  2421.     if ( !m_anchor)
  2422.         return;
  2423.  
  2424.     dc.SetFont( m_normalFont );
  2425.     dc.SetPen( m_dottedPen );
  2426.  
  2427.     // this is now done dynamically
  2428.     //if(GetImageList() == NULL)
  2429.     // m_lineHeight = (int)(dc.GetCharHeight() + 4);
  2430.  
  2431.     int y = 2;
  2432.     PaintLevel( m_anchor, dc, 0, y );
  2433. }
  2434.  
  2435. void wxGenericTreeCtrl::OnSetFocus( wxFocusEvent &event )
  2436. {
  2437.     m_hasFocus = TRUE;
  2438.  
  2439.     RefreshSelected();
  2440.  
  2441.     event.Skip();
  2442. }
  2443.  
  2444. void wxGenericTreeCtrl::OnKillFocus( wxFocusEvent &event )
  2445. {
  2446.     m_hasFocus = FALSE;
  2447.  
  2448.     RefreshSelected();
  2449.  
  2450.     event.Skip();
  2451. }
  2452.  
  2453. void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
  2454. {
  2455.     wxTreeEvent te( wxEVT_COMMAND_TREE_KEY_DOWN, GetId() );
  2456.     te.m_evtKey = event;
  2457.     te.SetEventObject( this );
  2458.     if ( GetEventHandler()->ProcessEvent( te ) )
  2459.     {
  2460.         // intercepted by the user code
  2461.         return;
  2462.     }
  2463.  
  2464.     if ( (m_current == 0) || (m_key_current == 0) )
  2465.     {
  2466.         event.Skip();
  2467.         return;
  2468.     }
  2469.  
  2470.     // how should the selection work for this event?
  2471.     bool is_multiple, extended_select, unselect_others;
  2472.     EventFlagsToSelType(GetWindowStyleFlag(),
  2473.                         event.ShiftDown(),
  2474.                         event.ControlDown(),
  2475.                         is_multiple, extended_select, unselect_others);
  2476.  
  2477.     // + : Expand
  2478.     // - : Collaspe
  2479.     // * : Expand all/Collapse all
  2480.     // ' ' | return : activate
  2481.     // up    : go up (not last children!)
  2482.     // down  : go down
  2483.     // left  : go to parent
  2484.     // right : open if parent and go next
  2485.     // home  : go to root
  2486.     // end   : go to last item without opening parents
  2487.     // alnum : start or continue searching for the item with this prefix
  2488.     int keyCode = event.KeyCode();
  2489.     switch ( keyCode )
  2490.     {
  2491.         case '+':
  2492.         case WXK_ADD:
  2493.             if (m_current->HasPlus() && !IsExpanded(m_current))
  2494.             {
  2495.                 Expand(m_current);
  2496.             }
  2497.             break;
  2498.  
  2499.         case '*':
  2500.         case WXK_MULTIPLY:
  2501.             if ( !IsExpanded(m_current) )
  2502.             {
  2503.                 // expand all
  2504.                 ExpandAll(m_current);
  2505.                 break;
  2506.             }
  2507.             //else: fall through to Collapse() it
  2508.  
  2509.         case '-':
  2510.         case WXK_SUBTRACT:
  2511.             if (IsExpanded(m_current))
  2512.             {
  2513.                 Collapse(m_current);
  2514.             }
  2515.             break;
  2516.  
  2517.         case ' ':
  2518.         case WXK_RETURN:
  2519.             if ( !event.HasModifiers() )
  2520.             {
  2521.                 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
  2522.                 event.m_item = (long) m_current;
  2523.                 event.SetEventObject( this );
  2524.                 GetEventHandler()->ProcessEvent( event );
  2525.             }
  2526.  
  2527.             // in any case, also generate the normal key event for this key,
  2528.             // even if we generated the ACTIVATED event above: this is what
  2529.             // wxMSW does and it makes sense because you might not want to
  2530.             // process ACTIVATED event at all and handle Space and Return
  2531.             // directly (and differently) which would be impossible otherwise
  2532.             event.Skip();
  2533.             break;
  2534.  
  2535.             // up goes to the previous sibling or to the last
  2536.             // of its children if it's expanded
  2537.         case WXK_UP:
  2538.             {
  2539.                 wxTreeItemId prev = GetPrevSibling( m_key_current );
  2540.                 if (!prev)
  2541.                 {
  2542.                     prev = GetParent( m_key_current );
  2543.                     if ((prev == GetRootItem()) && HasFlag(wxTR_HIDE_ROOT))
  2544.                     {
  2545.                         break;  // don't go to root if it is hidden
  2546.                     }
  2547.                     if (prev)
  2548.                     {
  2549.                         long cookie = 0;
  2550.                         wxTreeItemId current = m_key_current;
  2551.                         // TODO: Huh?  If we get here, we'd better be the first child of our parent.  How else could it be?
  2552.                         if (current == GetFirstChild( prev, cookie ))
  2553.                         {
  2554.                             // otherwise we return to where we came from
  2555.                             SelectItem( prev, unselect_others, extended_select );
  2556.                             m_key_current= (wxGenericTreeItem*) prev.m_pItem;
  2557.                             break;
  2558.                         }
  2559.                     }
  2560.                 }
  2561.                 if (prev)
  2562.                 {
  2563.                     while ( IsExpanded(prev) && HasChildren(prev) )
  2564.                     {
  2565.                         wxTreeItemId child = GetLastChild(prev);
  2566.                         if ( child )
  2567.                         {
  2568.                             prev = child;
  2569.                         }
  2570.                     }
  2571.  
  2572.                     SelectItem( prev, unselect_others, extended_select );
  2573.                     m_key_current=(wxGenericTreeItem*) prev.m_pItem;
  2574.                 }
  2575.             }
  2576.             break;
  2577.  
  2578.             // left arrow goes to the parent
  2579.         case WXK_LEFT:
  2580.             {
  2581.                 wxTreeItemId prev = GetParent( m_current );
  2582.                 if ((prev == GetRootItem()) && HasFlag(wxTR_HIDE_ROOT))
  2583.                 {
  2584.                     // don't go to root if it is hidden
  2585.                     prev = GetPrevSibling( m_current );
  2586.                 }
  2587.                 if (prev)
  2588.                 {
  2589.                     SelectItem( prev, unselect_others, extended_select );
  2590.                 }
  2591.             }
  2592.             break;
  2593.  
  2594.         case WXK_RIGHT:
  2595.             // this works the same as the down arrow except that we
  2596.             // also expand the item if it wasn't expanded yet
  2597.             Expand(m_current);
  2598.             // fall through
  2599.  
  2600.         case WXK_DOWN:
  2601.             {
  2602.                 if (IsExpanded(m_key_current) && HasChildren(m_key_current))
  2603.                 {
  2604.                     long cookie = 0;
  2605.                     wxTreeItemId child = GetFirstChild( m_key_current, cookie );
  2606.                     SelectItem( child, unselect_others, extended_select );
  2607.                     m_key_current=(wxGenericTreeItem*) child.m_pItem;
  2608.                 }
  2609.                 else
  2610.                 {
  2611.                     wxTreeItemId next = GetNextSibling( m_key_current );
  2612.                     if (!next)
  2613.                     {
  2614.                         wxTreeItemId current = m_key_current;
  2615.                         while (current && !next)
  2616.                         {
  2617.                             current = GetParent( current );
  2618.                             if (current) next = GetNextSibling( current );
  2619.                         }
  2620.                     }
  2621.                     if (next)
  2622.                     {
  2623.                         SelectItem( next, unselect_others, extended_select );
  2624.                         m_key_current=(wxGenericTreeItem*) next.m_pItem;
  2625.                     }
  2626.                 }
  2627.             }
  2628.             break;
  2629.  
  2630.             // <End> selects the last visible tree item
  2631.         case WXK_END:
  2632.             {
  2633.                 wxTreeItemId last = GetRootItem();
  2634.  
  2635.                 while ( last.IsOk() && IsExpanded(last) )
  2636.                 {
  2637.                     wxTreeItemId lastChild = GetLastChild(last);
  2638.  
  2639.                     // it may happen if the item was expanded but then all of
  2640.                     // its children have been deleted - so IsExpanded() returned
  2641.                     // TRUE, but GetLastChild() returned invalid item
  2642.                     if ( !lastChild )
  2643.                         break;
  2644.  
  2645.                     last = lastChild;
  2646.                 }
  2647.  
  2648.                 if ( last.IsOk() )
  2649.                 {
  2650.                     SelectItem( last, unselect_others, extended_select );
  2651.                 }
  2652.             }
  2653.             break;
  2654.  
  2655.             // <Home> selects the root item
  2656.         case WXK_HOME:
  2657.             {
  2658.                 wxTreeItemId prev = GetRootItem();
  2659.                 if (!prev)
  2660.                     break;
  2661.  
  2662.                 if ( HasFlag(wxTR_HIDE_ROOT) )
  2663.                 {
  2664.                     long dummy;
  2665.                     prev = GetFirstChild(prev, dummy);
  2666.                     if (!prev)
  2667.                         break;
  2668.                 }
  2669.  
  2670.                 SelectItem( prev, unselect_others, extended_select );
  2671.             }
  2672.             break;
  2673.  
  2674.         default:
  2675.             // do not use wxIsalnum() here
  2676.             if ( !event.HasModifiers() &&
  2677.                  ((keyCode >= '0' && keyCode <= '9') ||
  2678.                   (keyCode >= 'a' && keyCode <= 'z') ||
  2679.                   (keyCode >= 'A' && keyCode <= 'Z' )))
  2680.             {
  2681.                 // find the next item starting with the given prefix
  2682.                 char ch = (char)keyCode;
  2683.  
  2684.                 wxTreeItemId id = FindItem(m_current, m_findPrefix + (wxChar)ch);
  2685.                 if ( !id.IsOk() )
  2686.                 {
  2687.                     // no such item
  2688.                     break;
  2689.                 }
  2690.  
  2691.                 SelectItem(id);
  2692.  
  2693.                 m_findPrefix += ch;
  2694.  
  2695.                 // also start the timer to reset the current prefix if the user
  2696.                 // doesn't press any more alnum keys soon -- we wouldn't want
  2697.                 // to use this prefix for a new item search
  2698.                 if ( !m_findTimer )
  2699.                 {
  2700.                     m_findTimer = new wxTreeFindTimer(this);
  2701.                 }
  2702.  
  2703.                 m_findTimer->Start(wxTreeFindTimer::DELAY, wxTIMER_ONE_SHOT);
  2704.             }
  2705.             else
  2706.             {
  2707.                 event.Skip();
  2708.             }
  2709.     }
  2710. }
  2711.  
  2712. wxTreeItemId wxGenericTreeCtrl::HitTest(const wxPoint& point, int& flags)
  2713. {
  2714.     // JACS: removed wxYieldIfNeeded() because it can cause the window
  2715.     // to be deleted from under us if a close window event is pending
  2716.  
  2717.     int w, h;
  2718.     GetSize(&w, &h);
  2719.     flags=0;
  2720.     if (point.x<0) flags |= wxTREE_HITTEST_TOLEFT;
  2721.     if (point.x>w) flags |= wxTREE_HITTEST_TORIGHT;
  2722.     if (point.y<0) flags |= wxTREE_HITTEST_ABOVE;
  2723.     if (point.y>h) flags |= wxTREE_HITTEST_BELOW;
  2724.     if (flags) return wxTreeItemId();
  2725.  
  2726.     if (m_anchor == NULL)
  2727.     {
  2728.         flags = wxTREE_HITTEST_NOWHERE;
  2729.         return wxTreeItemId();
  2730.     }
  2731.  
  2732.     wxGenericTreeItem *hit =  m_anchor->HitTest(CalcUnscrolledPosition(point),
  2733.                                                 this, flags, 0);
  2734.     if (hit == NULL)
  2735.     {
  2736.         flags = wxTREE_HITTEST_NOWHERE;
  2737.         return wxTreeItemId();
  2738.     }
  2739.     return hit;
  2740. }
  2741.  
  2742. // get the bounding rectangle of the item (or of its label only)
  2743. bool wxGenericTreeCtrl::GetBoundingRect(const wxTreeItemId& item,
  2744.                                         wxRect& rect,
  2745.                                         bool WXUNUSED(textOnly)) const
  2746. {
  2747.     wxCHECK_MSG( item.IsOk(), FALSE, _T("invalid item in wxGenericTreeCtrl::GetBoundingRect") );
  2748.  
  2749.     wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem;
  2750.  
  2751.     int startX, startY;
  2752.     GetViewStart(& startX, & startY);
  2753.  
  2754.     rect.x = i->GetX() - startX*PIXELS_PER_UNIT;
  2755.     rect.y = i->GetY() - startY*PIXELS_PER_UNIT;
  2756.     rect.width = i->GetWidth();
  2757.     //rect.height = i->GetHeight();
  2758.     rect.height = GetLineHeight(i);
  2759.  
  2760.     return TRUE;
  2761. }
  2762.  
  2763. void wxGenericTreeCtrl::Edit( const wxTreeItemId& item )
  2764. {
  2765.     wxCHECK_RET( item.IsOk(), _T("can't edit an invalid item") );
  2766.  
  2767.     wxGenericTreeItem *itemEdit = (wxGenericTreeItem *)item.m_pItem;
  2768.  
  2769.     wxTreeEvent te( wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, GetId() );
  2770.     te.m_item = (long) itemEdit;
  2771.     te.SetEventObject( this );
  2772.     if ( GetEventHandler()->ProcessEvent( te ) && !te.IsAllowed() )
  2773.     {
  2774.         // vetoed by user
  2775.         return;
  2776.     }
  2777.  
  2778.     // We have to call this here because the label in
  2779.     // question might just have been added and no screen
  2780.     // update taken place.
  2781.     if ( m_dirty )
  2782.         wxYieldIfNeeded();
  2783.  
  2784.     wxTreeTextCtrl *text = new wxTreeTextCtrl(this, itemEdit);
  2785.  
  2786.     text->SetFocus();
  2787. }
  2788.  
  2789. bool wxGenericTreeCtrl::OnRenameAccept(wxGenericTreeItem *item,
  2790.                                        const wxString& value)
  2791. {
  2792.     wxTreeEvent le( wxEVT_COMMAND_TREE_END_LABEL_EDIT, GetId() );
  2793.     le.m_item = (long) item;
  2794.     le.SetEventObject( this );
  2795.     le.m_label = value;
  2796.     le.m_editCancelled = FALSE;
  2797.  
  2798.     return !GetEventHandler()->ProcessEvent( le ) || le.IsAllowed();
  2799. }
  2800.  
  2801. void wxGenericTreeCtrl::OnRenameCancelled(wxGenericTreeItem *item)
  2802. {
  2803.     // let owner know that the edit was cancelled
  2804.     wxTreeEvent le( wxEVT_COMMAND_TREE_END_LABEL_EDIT, GetId() );
  2805.     le.m_item = (long) item;
  2806.     le.SetEventObject( this );
  2807.     le.m_label = wxEmptyString;
  2808.     le.m_editCancelled = FALSE;
  2809.  
  2810.     GetEventHandler()->ProcessEvent( le );
  2811. }
  2812.  
  2813.  
  2814.  
  2815.  
  2816. void wxGenericTreeCtrl::OnRenameTimer()
  2817. {
  2818.     Edit( m_current );
  2819. }
  2820.  
  2821. void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event )
  2822. {
  2823.     if ( !m_anchor ) return;
  2824.  
  2825.     // we process left mouse up event (enables in-place edit), right down
  2826.     // (pass to the user code), left dbl click (activate item) and
  2827.     // dragging/moving events for items drag-and-drop
  2828.     if ( !(event.LeftDown() ||
  2829.            event.LeftUp() ||
  2830.            event.RightDown() ||
  2831.            event.LeftDClick() ||
  2832.            event.Dragging() ||
  2833.            ((event.Moving() || event.RightUp()) && m_isDragging)) )
  2834.     {
  2835.         event.Skip();
  2836.  
  2837.         return;
  2838.     }
  2839.  
  2840.     wxPoint pt = CalcUnscrolledPosition(event.GetPosition());
  2841.  
  2842.     int flags = 0;
  2843.     wxGenericTreeItem *item = m_anchor->HitTest(pt, this, flags, 0);
  2844.  
  2845.     if ( event.Dragging() && !m_isDragging )
  2846.     {
  2847.         if (m_dragCount == 0)
  2848.             m_dragStart = pt;
  2849.  
  2850.         m_dragCount++;
  2851.  
  2852.         if (m_dragCount != 3)
  2853.         {
  2854.             // wait until user drags a bit further...
  2855.             return;
  2856.         }
  2857.  
  2858.         wxEventType command = event.RightIsDown()
  2859.                               ? wxEVT_COMMAND_TREE_BEGIN_RDRAG
  2860.                               : wxEVT_COMMAND_TREE_BEGIN_DRAG;
  2861.  
  2862.         wxTreeEvent nevent( command, GetId() );
  2863.         nevent.m_item = (long) m_current;
  2864.         nevent.SetEventObject(this);
  2865.  
  2866.         // by default the dragging is not supported, the user code must
  2867.         // explicitly allow the event for it to take place
  2868.         nevent.Veto();
  2869.  
  2870.         if ( GetEventHandler()->ProcessEvent(nevent) && nevent.IsAllowed() )
  2871.         {
  2872.             // we're going to drag this item
  2873.             m_isDragging = TRUE;
  2874.  
  2875.             // remember the old cursor because we will change it while
  2876.             // dragging
  2877.             m_oldCursor = m_cursor;
  2878.  
  2879.             // in a single selection control, hide the selection temporarily
  2880.             if ( !(GetWindowStyleFlag() & wxTR_MULTIPLE) )
  2881.             {
  2882.                 m_oldSelection = (wxGenericTreeItem*) GetSelection().m_pItem;
  2883.  
  2884.                 if ( m_oldSelection )
  2885.                 {
  2886.                     m_oldSelection->SetHilight(FALSE);
  2887.                     RefreshLine(m_oldSelection);
  2888.                 }
  2889.             }
  2890.  
  2891.             CaptureMouse();
  2892.         }
  2893.     }
  2894.     else if ( event.Moving() )
  2895.     {
  2896.         if ( item != m_dropTarget )
  2897.         {
  2898.             // unhighlight the previous drop target
  2899.             DrawDropEffect(m_dropTarget);
  2900.  
  2901.             m_dropTarget = item;
  2902.  
  2903.             // highlight the current drop target if any
  2904.             DrawDropEffect(m_dropTarget);
  2905.  
  2906.             wxYieldIfNeeded();
  2907.         }
  2908.     }
  2909.     else if ( (event.LeftUp() || event.RightUp()) && m_isDragging )
  2910.     {
  2911.         // erase the highlighting
  2912.         DrawDropEffect(m_dropTarget);
  2913.  
  2914.         if ( m_oldSelection )
  2915.         {
  2916.             m_oldSelection->SetHilight(TRUE);
  2917.             RefreshLine(m_oldSelection);
  2918.             m_oldSelection = (wxGenericTreeItem *)NULL;
  2919.         }
  2920.  
  2921.         // generate the drag end event
  2922.         wxTreeEvent event(wxEVT_COMMAND_TREE_END_DRAG, GetId());
  2923.  
  2924.         event.m_item = (long) item;
  2925.         event.m_pointDrag = pt;
  2926.         event.SetEventObject(this);
  2927.  
  2928.         (void)GetEventHandler()->ProcessEvent(event);
  2929.  
  2930.         m_isDragging = FALSE;
  2931.         m_dropTarget = (wxGenericTreeItem *)NULL;
  2932.  
  2933.         ReleaseMouse();
  2934.  
  2935.         SetCursor(m_oldCursor);
  2936.  
  2937.         wxYieldIfNeeded();
  2938.     }
  2939.     else
  2940.     {
  2941.         // here we process only the messages which happen on tree items
  2942.  
  2943.         m_dragCount = 0;
  2944.  
  2945.         if (item == NULL) return;  /* we hit the blank area */
  2946.  
  2947.         if ( event.RightDown() )
  2948.         {
  2949.             wxTreeEvent nevent(wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK, GetId());
  2950.             nevent.m_item = (long) item;
  2951.             nevent.m_pointDrag = CalcScrolledPosition(pt);
  2952.             nevent.SetEventObject(this);
  2953.             GetEventHandler()->ProcessEvent(nevent);
  2954.         }
  2955.         else if ( event.LeftUp() )
  2956.         {
  2957.             if ( m_lastOnSame )
  2958.             {
  2959.                 if ( (item == m_current) &&
  2960.                      (flags & wxTREE_HITTEST_ONITEMLABEL) &&
  2961.                      HasFlag(wxTR_EDIT_LABELS) )
  2962.                 {
  2963.                     if ( m_renameTimer )
  2964.                     {
  2965.                         if ( m_renameTimer->IsRunning() )
  2966.                             m_renameTimer->Stop();
  2967.                     }
  2968.                     else
  2969.                     {
  2970.                         m_renameTimer = new wxTreeRenameTimer( this );
  2971.                     }
  2972.  
  2973.                     m_renameTimer->Start( wxTreeRenameTimer::DELAY, TRUE );
  2974.                 }
  2975.  
  2976.                 m_lastOnSame = FALSE;
  2977.             }
  2978.         }
  2979.         else // !RightDown() && !LeftUp() ==> LeftDown() || LeftDClick()
  2980.         {
  2981.             if ( event.LeftDown() )
  2982.             {
  2983.                 m_lastOnSame = item == m_current;
  2984.             }
  2985.  
  2986.             if ( flags & wxTREE_HITTEST_ONITEMBUTTON )
  2987.             {
  2988.                 // only toggle the item for a single click, double click on
  2989.                 // the button doesn't do anything (it toggles the item twice)
  2990.                 if ( event.LeftDown() )
  2991.                 {
  2992.                     Toggle( item );
  2993.                 }
  2994.  
  2995.                 // don't select the item if the button was clicked
  2996.                 return;
  2997.             }
  2998.  
  2999.             // how should the selection work for this event?
  3000.             bool is_multiple, extended_select, unselect_others;
  3001.             EventFlagsToSelType(GetWindowStyleFlag(),
  3002.                                 event.ShiftDown(),
  3003.                                 event.ControlDown(),
  3004.                                 is_multiple, extended_select, unselect_others);
  3005.  
  3006.             SelectItem(item, unselect_others, extended_select);
  3007.  
  3008.             // For some reason, Windows isn't recognizing a left double-click,
  3009.             // so we need to simulate it here.  Allow 200 milliseconds for now.
  3010.             if ( event.LeftDClick() )
  3011.             {
  3012.                 // double clicking should not start editing the item label
  3013.                 if ( m_renameTimer )
  3014.                     m_renameTimer->Stop();
  3015.  
  3016.                 m_lastOnSame = FALSE;
  3017.  
  3018.                 // send activate event first
  3019.                 wxTreeEvent nevent( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
  3020.                 nevent.m_item = (long) item;
  3021.                 nevent.m_pointDrag = CalcScrolledPosition(pt);
  3022.                 nevent.SetEventObject( this );
  3023.                 if ( !GetEventHandler()->ProcessEvent( nevent ) )
  3024.                 {
  3025.                     // if the user code didn't process the activate event,
  3026.                     // handle it ourselves by toggling the item when it is
  3027.                     // double clicked
  3028.                     if ( item->HasPlus() )
  3029.                     {
  3030.                         Toggle(item);
  3031.                     }
  3032.                 }
  3033.             }
  3034.         }
  3035.     }
  3036. }
  3037.  
  3038. void wxGenericTreeCtrl::OnIdle( wxIdleEvent &WXUNUSED(event) )
  3039. {
  3040.     /* after all changes have been done to the tree control,
  3041.      * we actually redraw the tree when everything is over */
  3042.  
  3043.     if (!m_dirty) return;
  3044.  
  3045.     m_dirty = FALSE;
  3046.  
  3047.     CalculatePositions();
  3048.     Refresh();
  3049.     AdjustMyScrollbars();
  3050. }
  3051.  
  3052. void wxGenericTreeCtrl::CalculateSize( wxGenericTreeItem *item, wxDC &dc )
  3053. {
  3054.     wxCoord text_w = 0;
  3055.     wxCoord text_h = 0;
  3056.  
  3057.     wxTreeItemAttr *attr = item->GetAttributes();
  3058.     if ( attr && attr->HasFont() )
  3059.         dc.SetFont(attr->GetFont());
  3060.     else if ( item->IsBold() )
  3061.         dc.SetFont(m_boldFont);
  3062.  
  3063.     dc.GetTextExtent( item->GetText(), &text_w, &text_h );
  3064.     text_h+=2;
  3065.  
  3066.     // restore normal font
  3067.     dc.SetFont( m_normalFont );
  3068.  
  3069.     int image_h = 0;
  3070.     int image_w = 0;
  3071.     int image = item->GetCurrentImage();
  3072.     if ( image != NO_IMAGE )
  3073.     {
  3074.         if ( m_imageListNormal )
  3075.         {
  3076.             m_imageListNormal->GetSize( image, image_w, image_h );
  3077.             image_w += 4;
  3078.         }
  3079.     }
  3080.  
  3081.     int total_h = (image_h > text_h) ? image_h : text_h;
  3082.  
  3083.     if (total_h < 30)
  3084.         total_h += 2;            // at least 2 pixels
  3085.     else
  3086.         total_h += total_h/10;   // otherwise 10% extra spacing
  3087.  
  3088.     item->SetHeight(total_h);
  3089.     if (total_h>m_lineHeight)
  3090.         m_lineHeight=total_h;
  3091.  
  3092.     item->SetWidth(image_w+text_w+2);
  3093. }
  3094.  
  3095. // -----------------------------------------------------------------------------
  3096. // for developper : y is now the top of the level
  3097. // not the middle of it !
  3098. void wxGenericTreeCtrl::CalculateLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y )
  3099. {
  3100.     int x = level*m_indent;
  3101.     if (!HasFlag(wxTR_HIDE_ROOT))
  3102.     {
  3103.         x += m_indent;
  3104.     }
  3105.     else if (level == 0)
  3106.     {
  3107.         // a hidden root is not evaluated, but its
  3108.         // children are always calculated
  3109.         goto Recurse;
  3110.     }
  3111.  
  3112.     CalculateSize( item, dc );
  3113.  
  3114.     // set its position
  3115.     item->SetX( x+m_spacing );
  3116.     item->SetY( y );
  3117.     y += GetLineHeight(item);
  3118.  
  3119.     if ( !item->IsExpanded() )
  3120.     {
  3121.         // we don't need to calculate collapsed branches
  3122.         return;
  3123.     }
  3124.  
  3125.   Recurse:
  3126.     wxArrayGenericTreeItems& children = item->GetChildren();
  3127.     size_t n, count = children.Count();
  3128.     ++level;
  3129.     for (n = 0; n < count; ++n )
  3130.         CalculateLevel( children[n], dc, level, y );  // recurse
  3131. }
  3132.  
  3133. void wxGenericTreeCtrl::CalculatePositions()
  3134. {
  3135.     if ( !m_anchor ) return;
  3136.  
  3137.     wxClientDC dc(this);
  3138.     PrepareDC( dc );
  3139.  
  3140.     dc.SetFont( m_normalFont );
  3141.  
  3142.     dc.SetPen( m_dottedPen );
  3143.     //if(GetImageList() == NULL)
  3144.     // m_lineHeight = (int)(dc.GetCharHeight() + 4);
  3145.  
  3146.     int y = 2;
  3147.     CalculateLevel( m_anchor, dc, 0, y ); // start recursion
  3148. }
  3149.  
  3150. void wxGenericTreeCtrl::RefreshSubtree(wxGenericTreeItem *item)
  3151. {
  3152.     if (m_dirty) return;
  3153.  
  3154.     wxSize client = GetClientSize();
  3155.  
  3156.     wxRect rect;
  3157.     CalcScrolledPosition(0, item->GetY(), NULL, &rect.y);
  3158.     rect.width = client.x;
  3159.     rect.height = client.y;
  3160.  
  3161.     Refresh(TRUE, &rect);
  3162.  
  3163.     AdjustMyScrollbars();
  3164. }
  3165.  
  3166. void wxGenericTreeCtrl::RefreshLine( wxGenericTreeItem *item )
  3167. {
  3168.     if (m_dirty) return;
  3169.  
  3170.     wxRect rect;
  3171.     CalcScrolledPosition(0, item->GetY(), NULL, &rect.y);
  3172.     rect.width = GetClientSize().x;
  3173.     rect.height = GetLineHeight(item); //dc.GetCharHeight() + 6;
  3174.  
  3175.     Refresh(TRUE, &rect);
  3176. }
  3177.  
  3178. void wxGenericTreeCtrl::RefreshSelected()
  3179. {
  3180.     // TODO: this is awfully inefficient, we should keep the list of all
  3181.     //       selected items internally, should be much faster
  3182.     if ( m_anchor )
  3183.         RefreshSelectedUnder(m_anchor);
  3184. }
  3185.  
  3186. void wxGenericTreeCtrl::RefreshSelectedUnder(wxGenericTreeItem *item)
  3187. {
  3188.     if ( item->IsSelected() )
  3189.         RefreshLine(item);
  3190.  
  3191.     const wxArrayGenericTreeItems& children = item->GetChildren();
  3192.     size_t count = children.GetCount();
  3193.     for ( size_t n = 0; n < count; n++ )
  3194.     {
  3195.         RefreshSelectedUnder(children[n]);
  3196.     }
  3197. }
  3198.  
  3199. // ----------------------------------------------------------------------------
  3200. // changing colours: we need to refresh the tree control
  3201. // ----------------------------------------------------------------------------
  3202.  
  3203. bool wxGenericTreeCtrl::SetBackgroundColour(const wxColour& colour)
  3204. {
  3205.     if ( !wxWindow::SetBackgroundColour(colour) )
  3206.         return FALSE;
  3207.  
  3208.     Refresh();
  3209.  
  3210.     return TRUE;
  3211. }
  3212.  
  3213. bool wxGenericTreeCtrl::SetForegroundColour(const wxColour& colour)
  3214. {
  3215.     if ( !wxWindow::SetForegroundColour(colour) )
  3216.         return FALSE;
  3217.  
  3218.     Refresh();
  3219.  
  3220.     return TRUE;
  3221. }
  3222.  
  3223. #endif // wxUSE_TREECTRL
  3224.