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