]> git.saurik.com Git - wxWidgets.git/blobdiff - src/generic/treectlg.cpp
Calss interface updates (wxMotif-only).
[wxWidgets.git] / src / generic / treectlg.cpp
index 5fd8f5b4f21a3d49b6fed230279918bf619ab721..c7732185bc8dac6c09f02d7a895da582cc3cc638 100644 (file)
@@ -45,7 +45,6 @@
 class WXDLLEXPORT wxGenericTreeItem;
 
 WX_DEFINE_EXPORTED_ARRAY(wxGenericTreeItem *, wxArrayGenericTreeItems);
-//WX_DEFINE_OBJARRAY(wxArrayTreeItemIds);
 
 // ----------------------------------------------------------------------------
 // constants
@@ -53,55 +52,55 @@ WX_DEFINE_EXPORTED_ARRAY(wxGenericTreeItem *, wxArrayGenericTreeItems);
 
 static const int NO_IMAGE = -1;
 
-#define PIXELS_PER_UNIT 10
+static const int PIXELS_PER_UNIT = 10;
 
 // ----------------------------------------------------------------------------
 // Aqua arrows
 // ----------------------------------------------------------------------------
 
-        /* XPM */
-        static char *aqua_arrow_right[] = {
-        /* columns rows colors chars-per-pixel */
-        "13 11 4 1",
-        "  c None",
-        "b c #C0C0C0",
-        "c c #707070",
-        "d c #A0A0A0",
-        /* pixels */
-        "    b        ",
-        "    ddb      ",
-        "    cccdb    ",
-        "    cccccd   ",
-        "    ccccccdb ",
-        "    ccccccccd",
-        "    ccccccdb ",
-        "    cccccb   ",
-        "    cccdb    ",
-        "    ddb      ",
-        "    b        "
-        };
-
-        /* XPM */
-        static char *aqua_arrow_down[] = {
-        /* columns rows colors chars-per-pixel */
-        "13 11 4 1",
-        "  c None",
-        "b c #C0C0C0",
-        "c c #707070",
-        "d c #A0A0A0",
-        /* pixels */
-        "             ",
-        "             ",
-        " bdcccccccdb ",
-        "  dcccccccd  ",
-        "  bcccccccb  ",
-        "   dcccccd   ",
-        "   bcccccb   ",
-        "    bcccd    ",
-        "     dcd     ",
-        "     bcb     ",
-        "      d      "
-        };
+/* XPM */
+static const char *aqua_arrow_right[] = {
+/* columns rows colors chars-per-pixel */
+"13 11 4 1",
+"  c None",
+"b c #C0C0C0",
+"c c #707070",
+"d c #A0A0A0",
+/* pixels */
+"    b        ",
+"    ddb      ",
+"    cccdb    ",
+"    cccccd   ",
+"    ccccccdb ",
+"    ccccccccd",
+"    ccccccdb ",
+"    cccccb   ",
+"    cccdb    ",
+"    ddb      ",
+"    b        "
+};
+
+/* XPM */
+static const char *aqua_arrow_down[] = {
+/* columns rows colors chars-per-pixel */
+"13 11 4 1",
+"  c None",
+"b c #C0C0C0",
+"c c #707070",
+"d c #A0A0A0",
+/* pixels */
+"             ",
+"             ",
+" bdcccccccdb ",
+"  dcccccccd  ",
+"  bcccccccb  ",
+"   dcccccd   ",
+"   bcccccb   ",
+"    bcccd    ",
+"     dcd     ",
+"     bcb     ",
+"      d      "
+};
 
 // -----------------------------------------------------------------------------
 // private classes
@@ -111,44 +110,57 @@ static const int NO_IMAGE = -1;
 class WXDLLEXPORT wxTreeRenameTimer: public wxTimer
 {
 public:
+    // start editing the current item after half a second (if the mouse hasn't
+    // been clicked/moved)
+    enum { DELAY = 500 };
+
     wxTreeRenameTimer( wxGenericTreeCtrl *owner );
 
-    void Notify();
+    virtual void Notify();
 
 private:
-    wxGenericTreeCtrl   *m_owner;
+    wxGenericTreeCtrl *m_owner;
 };
 
 // control used for in-place edit
 class WXDLLEXPORT wxTreeTextCtrl: public wxTextCtrl
 {
 public:
-    wxTreeTextCtrl( wxWindow *parent,
-                    const wxWindowID id,
-                    bool *accept,
-                    wxString *res,
-                    wxGenericTreeCtrl *owner,
-                    const wxString &value = wxEmptyString,
-                    const wxPoint &pos = wxDefaultPosition,
-                    const wxSize &size = wxDefaultSize,
-                    int style = wxSIMPLE_BORDER,
-                    const wxValidator& validator = wxDefaultValidator,
-                    const wxString &name = wxTextCtrlNameStr );
+    wxTreeTextCtrl(wxGenericTreeCtrl *owner, wxGenericTreeItem *item);
 
+protected:
     void OnChar( wxKeyEvent &event );
     void OnKeyUp( wxKeyEvent &event );
     void OnKillFocus( wxFocusEvent &event );
 
+    bool AcceptChanges();
+    void Finish();
+
 private:
-    bool               *m_accept;
-    wxString           *m_res;
     wxGenericTreeCtrl  *m_owner;
+    wxGenericTreeItem  *m_itemEdited;
     wxString            m_startValue;
     bool                m_finished;
 
     DECLARE_EVENT_TABLE()
 };
 
+// timer used to clear wxGenericTreeCtrl::m_findPrefix if no key was pressed
+// for a sufficiently long time
+class WXDLLEXPORT wxTreeFindTimer : public wxTimer
+{
+public:
+    // reset the current prefix after half a second of inactivity
+    enum { DELAY = 500 };
+
+    wxTreeFindTimer( wxGenericTreeCtrl *owner ) { m_owner = owner; }
+
+    virtual void Notify() { m_owner->m_findPrefix.clear(); }
+
+private:
+    wxGenericTreeCtrl *m_owner;
+};
+
 // a tree item
 class WXDLLEXPORT wxGenericTreeItem
 {
@@ -334,99 +346,131 @@ BEGIN_EVENT_TABLE(wxTreeTextCtrl,wxTextCtrl)
     EVT_KILL_FOCUS     (wxTreeTextCtrl::OnKillFocus)
 END_EVENT_TABLE()
 
-wxTreeTextCtrl::wxTreeTextCtrl( wxWindow *parent,
-                                const wxWindowID id,
-                                bool *accept,
-                                wxString *res,
-                                wxGenericTreeCtrl *owner,
-                                const wxString &value,
-                                const wxPoint &pos,
-                                const wxSize &size,
-                                int style,
-                                const wxValidator& validator,
-                                const wxString &name )
-    : wxTextCtrl( parent, id, value, pos, size, style, validator, name )
-{
-    m_res = res;
-    m_accept = accept;
+wxTreeTextCtrl::wxTreeTextCtrl(wxGenericTreeCtrl *owner,
+                               wxGenericTreeItem *item)
+              : m_itemEdited(item), m_startValue(item->GetText())
+{
     m_owner = owner;
-    (*m_accept) = FALSE;
-    (*m_res) = wxEmptyString;
-    m_startValue = value;
     m_finished = FALSE;
-}
 
-void wxTreeTextCtrl::OnChar( wxKeyEvent &event )
-{
-    if (event.m_keyCode == WXK_RETURN)
+    int w = m_itemEdited->GetWidth(),
+        h = m_itemEdited->GetHeight();
+
+    int x, y;
+    m_owner->CalcScrolledPosition(item->GetX(), item->GetY(), &x, &y);
+
+    int image_h = 0,
+        image_w = 0;
+
+    int image = item->GetCurrentImage();
+    if ( image != NO_IMAGE )
     {
-        (*m_accept) = TRUE;
-        (*m_res) = GetValue();
+        if ( m_owner->m_imageListNormal )
+        {
+            m_owner->m_imageListNormal->GetSize( image, image_w, image_h );
+            image_w += 4;
+        }
+        else
+        {
+            wxFAIL_MSG(_T("you must create an image list to use images!"));
+        }
+    }
 
-        if ((*m_res) != m_startValue)
-            m_owner->OnRenameAccept();
+    // FIXME: what are all these hardcoded 4, 8 and 11s really?
+    x += image_w;
+    w -= image_w + 4;
 
-        if (!wxPendingDelete.Member(this))
-            wxPendingDelete.Append(this);
-            
-        m_finished = TRUE;
-        m_owner->SetFocus(); // This doesn't work. TODO.
+    (void)Create(m_owner, wxID_ANY, m_startValue,
+                 wxPoint(x - 4, y - 4), wxSize(w + 11, h + 8));
+}
 
-        return;
+bool wxTreeTextCtrl::AcceptChanges()
+{
+    const wxString value = GetValue();
+
+    if ( value == m_startValue )
+    {
+        // nothing changed, always accept
+        return TRUE;
     }
-    if (event.m_keyCode == WXK_ESCAPE)
+
+    if ( !m_owner->OnRenameAccept(m_itemEdited, value) )
     {
-        (*m_accept) = FALSE;
-        (*m_res) = "";
+        // vetoed by the user
+        return FALSE;
+    }
 
-        if (!wxPendingDelete.Member(this))
-            wxPendingDelete.Append(this);
+    // accepted, do rename the item
+    m_owner->SetItemText(m_itemEdited, value);
+
+    return TRUE;
+}
+
+void wxTreeTextCtrl::Finish()
+{
+    if ( !m_finished )
+    {
+        wxPendingDelete.Append(this);
 
         m_finished = TRUE;
+
         m_owner->SetFocus(); // This doesn't work. TODO.
+    }
+}
 
-        return;
+void wxTreeTextCtrl::OnChar( wxKeyEvent &event )
+{
+    switch ( event.m_keyCode )
+    {
+        case WXK_RETURN:
+            if ( !AcceptChanges() )
+            {
+                // vetoed by the user, don't disappear
+                break;
+            }
+            //else: fall through
+
+        case WXK_ESCAPE:
+            Finish();
+            break;
+
+        default:
+            event.Skip();
     }
-    event.Skip();
 }
 
 void wxTreeTextCtrl::OnKeyUp( wxKeyEvent &event )
 {
-    if (m_finished)
+    if ( !m_finished )
     {
-        event.Skip();
-        return;
+        // auto-grow the textctrl:
+        wxSize parentSize = m_owner->GetSize();
+        wxPoint myPos = GetPosition();
+        wxSize mySize = GetSize();
+        int sx, sy;
+        GetTextExtent(GetValue() + _T("M"), &sx, &sy);
+        if (myPos.x + sx > parentSize.x)
+            sx = parentSize.x - myPos.x;
+        if (mySize.x > sx)
+            sx = mySize.x;
+        SetSize(sx, -1);
     }
 
-    // auto-grow the textctrl:
-    wxSize parentSize = m_owner->GetSize();
-    wxPoint myPos = GetPosition();
-    wxSize mySize = GetSize();
-    int sx, sy;
-    GetTextExtent(GetValue() + _T("M"), &sx, &sy);
-    if (myPos.x + sx > parentSize.x) sx = parentSize.x - myPos.x;
-    if (mySize.x > sx) sx = mySize.x;
-    SetSize(sx, -1);
-
     event.Skip();
 }
 
 void wxTreeTextCtrl::OnKillFocus( wxFocusEvent &event )
 {
-    if (m_finished)
+    if ( m_finished )
     {
         event.Skip();
         return;
     }
 
-    if (!wxPendingDelete.Member(this))
-        wxPendingDelete.Append(this);
-
-    (*m_accept) = TRUE;
-    (*m_res) = GetValue();
-    
-    if ((*m_res) != m_startValue)
-        m_owner->OnRenameAccept();
+    if ( AcceptChanges() )
+    {
+        Finish();
+    }
 }
 
 // -----------------------------------------------------------------------------
@@ -694,7 +738,9 @@ void wxGenericTreeCtrl::Init()
     m_isDragging = FALSE;
     m_dropTarget = m_oldSelection = (wxGenericTreeItem *)NULL;
 
-    m_renameTimer = new wxTreeRenameTimer( this );
+    m_renameTimer = NULL;
+    m_findTimer = NULL;
+
     m_lastOnSame = FALSE;
 
     m_normalFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
@@ -716,7 +762,7 @@ bool wxGenericTreeCtrl::Create(wxWindow *parent,
 #ifdef __WXMAC__
     int major,minor;
     wxGetOsVersion( &major, &minor );
-    
+
     if (style & wxTR_HAS_BUTTONS) style |= wxTR_MAC_BUTTONS;
     if (style & wxTR_HAS_BUTTONS) style &= ~wxTR_HAS_BUTTONS;
     style &= ~wxTR_LINES_AT_ROOT;
@@ -741,9 +787,9 @@ bool wxGenericTreeCtrl::Create(wxWindow *parent,
     wxScrolledWindow::Create( parent, id, pos, size,
                               style|wxHSCROLL|wxVSCROLL, name );
 
-        // If the tree display has no buttons, but does have
-        // connecting lines, we can use a narrower layout.
-        // It may not be a good idea to force this...
+    // If the tree display has no buttons, but does have
+    // connecting lines, we can use a narrower layout.
+    // It may not be a good idea to force this...
     if (!HasButtons() && !HasFlag(wxTR_NO_LINES))
     {
         m_indent= 10;
@@ -754,7 +800,8 @@ bool wxGenericTreeCtrl::Create(wxWindow *parent,
     SetValidator( validator );
 #endif
 
-    SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_LISTBOX ) );
+    SetForegroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) );
+    SetBackgroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX) );
 
 //  m_dottedPen = wxPen( "grey", 0, wxDOT );  too slow under XFree86
     m_dottedPen = wxPen( wxT("grey"), 0, 0 );
@@ -766,16 +813,21 @@ wxGenericTreeCtrl::~wxGenericTreeCtrl()
 {
     delete m_hilightBrush;
     delete m_hilightUnfocusedBrush;
-    
-    if (m_arrowRight) delete m_arrowRight;
-    if (m_arrowDown) delete m_arrowDown;
+
+    delete m_arrowRight;
+    delete m_arrowDown;
 
     DeleteAllItems();
 
     delete m_renameTimer;
-    if (m_ownsImageListNormal) delete m_imageListNormal;
-    if (m_ownsImageListState) delete m_imageListState;
-    if (m_ownsImageListButtons) delete m_imageListButtons;
+    delete m_findTimer;
+
+    if (m_ownsImageListNormal)
+        delete m_imageListNormal;
+    if (m_ownsImageListState)
+        delete m_imageListState;
+    if (m_ownsImageListButtons)
+        delete m_imageListButtons;
 }
 
 // -----------------------------------------------------------------------------
@@ -789,13 +841,13 @@ size_t wxGenericTreeCtrl::GetCount() const
 
 void wxGenericTreeCtrl::SetIndent(unsigned int indent)
 {
-    m_indent = indent;
+    m_indent = (unsigned short) indent;
     m_dirty = TRUE;
 }
 
 void wxGenericTreeCtrl::SetSpacing(unsigned int spacing)
 {
-    m_spacing = spacing;
+    m_spacing = (unsigned short) spacing;
     m_dirty = TRUE;
 }
 
@@ -808,9 +860,17 @@ size_t wxGenericTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool recurs
 
 void wxGenericTreeCtrl::SetWindowStyle(const long styles)
 {
-        // right now, just sets the styles.  Eventually, we may
-        // want to update the inherited styles, but right now
-        // none of the parents has updatable styles
+    if (!HasFlag(wxTR_HIDE_ROOT) && (styles & wxTR_HIDE_ROOT))
+    {
+        // if we will hide the root, make sure children are visible
+        m_anchor->SetHasPlus();
+        m_anchor->Expand();
+        CalculatePositions();
+    }
+
+    // right now, just sets the styles.  Eventually, we may
+    // want to update the inherited styles, but right now
+    // none of the parents has updatable styles
     m_windowStyle = styles;
     m_dirty = TRUE;
 }
@@ -1159,6 +1219,52 @@ wxTreeItemId wxGenericTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
     return wxTreeItemId();
 }
 
+// find the first item starting with the given prefix after the given item
+wxTreeItemId wxGenericTreeCtrl::FindItem(const wxTreeItemId& idParent,
+                                         const wxString& prefixOrig) const
+{
+    // match is case insensitive as this is more convenient to the user: having
+    // to press Shift-letter to go to the item starting with a capital letter
+    // would be too bothersome
+    wxString prefix = prefixOrig.Lower();
+
+    // determine the starting point: we shouldn't take the current item (this
+    // allows to switch between two items starting with the same letter just by
+    // pressing it) but we shouldn't jump to the next one if the user is
+    // continuing to type as otherwise he might easily skip the item he wanted
+    wxTreeItemId id = idParent;
+    if ( prefix.length() == 1 )
+    {
+        id = GetNext(id);
+    }
+
+    // look for the item starting with the given prefix after it
+    while ( id.IsOk() && !GetItemText(id).Lower().StartsWith(prefix) )
+    {
+        id = GetNext(id);
+    }
+
+    // if we haven't found anything...
+    if ( !id.IsOk() )
+    {
+        // ... wrap to the beginning
+        id = GetRootItem();
+        if ( HasFlag(wxTR_HIDE_ROOT) )
+        {
+            // can't select virtual root
+            id = GetNext(id);
+        }
+
+        // and try all the items (stop when we get to the one we started from)
+        while ( id != idParent && !GetItemText(id).Lower().StartsWith(prefix) )
+        {
+            id = GetNext(id);
+        }
+    }
+
+    return id;
+}
+
 // -----------------------------------------------------------------------------
 // operations
 // -----------------------------------------------------------------------------
@@ -1211,7 +1317,8 @@ wxTreeItemId wxGenericTreeCtrl::AddRoot(const wxString& text,
         // if root is hidden, make sure we can navigate
         // into children
         m_anchor->SetHasPlus();
-        Expand(m_anchor);
+        m_anchor->Expand();
+        CalculatePositions();
     }
 
     if (!HasFlag(wxTR_MULTIPLE))
@@ -1244,9 +1351,13 @@ wxTreeItemId wxGenericTreeCtrl::InsertItem(const wxTreeItemId& parentId,
         return AddRoot(text, image, selImage, data);
     }
 
-    int index = parent->GetChildren().Index((wxGenericTreeItem*) idPrevious.m_pItem);
-    wxASSERT_MSG( index != wxNOT_FOUND,
-                  wxT("previous item in wxGenericTreeCtrl::InsertItem() is not a sibling") );
+    int index = -1;
+    if (idPrevious.IsOk())
+    {
+        index = parent->GetChildren().Index((wxGenericTreeItem*) idPrevious.m_pItem);
+        wxASSERT_MSG( index != wxNOT_FOUND,
+                      wxT("previous item in wxGenericTreeCtrl::InsertItem() is not a sibling") );
+    }
 
     return DoInsertItem(parentId, (size_t)++index, text, image, selImage, data);
 }
@@ -1355,6 +1466,8 @@ void wxGenericTreeCtrl::Expand(const wxTreeItemId& itemId)
     wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
 
     wxCHECK_RET( item, _T("invalid item in wxGenericTreeCtrl::Expand") );
+    wxCHECK_RET( !HasFlag(wxTR_HIDE_ROOT) || itemId != GetRootItem(),
+                 _T("can't expand hidden root") );
 
     if ( !item->HasPlus() )
         return;
@@ -1399,6 +1512,9 @@ void wxGenericTreeCtrl::ExpandAll(const wxTreeItemId& item)
 
 void wxGenericTreeCtrl::Collapse(const wxTreeItemId& itemId)
 {
+    wxCHECK_RET( !HasFlag(wxTR_HIDE_ROOT) || itemId != GetRootItem(),
+                 _T("can't collapse hidden root") );
+
     wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
 
     if ( !item->IsExpanded() )
@@ -1454,6 +1570,8 @@ void wxGenericTreeCtrl::Unselect()
     {
         m_current->SetHilight( FALSE );
         RefreshLine( m_current );
+
+        m_current = NULL;
     }
 }
 
@@ -1561,8 +1679,8 @@ void wxGenericTreeCtrl::SelectItemRange(wxGenericTreeItem *item1, wxGenericTreeI
 }
 
 void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId,
-                            bool unselect_others,
-                            bool extended_select)
+                                   bool unselect_others,
+                                   bool extended_select)
 {
     wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") );
 
@@ -1644,7 +1762,7 @@ void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId,
 }
 
 void wxGenericTreeCtrl::FillArray(wxGenericTreeItem *item,
-                           wxArrayTreeItemIds &array) const
+                                  wxArrayTreeItemIds &array) const
 {
     if ( item->IsSelected() )
         array.Add(wxTreeItemId(item));
@@ -1679,10 +1797,22 @@ void wxGenericTreeCtrl::EnsureVisible(const wxTreeItemId& item)
 
     // first expand all parent branches
     wxGenericTreeItem *parent = gitem->GetParent();
-    while ( parent )
+
+    if ( HasFlag(wxTR_HIDE_ROOT) )
     {
-        Expand(parent);
-        parent = parent->GetParent();
+        while ( parent != m_anchor )
+        {
+            Expand(parent);
+            parent = parent->GetParent();
+        }
+    }
+    else
+    {
+        while ( parent )
+        {
+            Expand(parent);
+            parent = parent->GetParent();
+        }
     }
 
     //if (parent) CalculatePositions();
@@ -1838,7 +1968,10 @@ void wxGenericTreeCtrl::SetImageList(wxImageList *imageList)
     m_imageListNormal = imageList;
     m_ownsImageListNormal = FALSE;
     m_dirty = TRUE;
-    CalculateLineHeight();
+    // Don't do any drawing if we're setting the list to NULL,
+    // since we may be in the process of deleting the tree control.
+    if (imageList)
+        CalculateLineHeight();
 }
 
 void wxGenericTreeCtrl::SetStateImageList(wxImageList *imageList)
@@ -2061,7 +2194,7 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level
             if (attr && attr->HasTextColour())
                 colText = attr->GetTextColour();
             else
-                colText = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
+                colText = GetForegroundColour();
         }
 
         // prepare to draw
@@ -2115,7 +2248,7 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level
             else if (HasFlag(wxTR_TWIST_BUTTONS))
             {
                 // draw the twisty button here
-                
+
                 if (HasFlag(wxTR_AQUA_BUTTONS))
                 {
                     if (item->IsExpanded())
@@ -2337,7 +2470,9 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
     // right : open if parent and go next
     // home  : go to root
     // end   : go to last item without opening parents
-    switch (event.KeyCode())
+    // alnum : start or continue searching for the item with this prefix
+    int keyCode = event.KeyCode();
+    switch ( keyCode )
     {
         case '+':
         case WXK_ADD:
@@ -2397,7 +2532,6 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
                             // otherwise we return to where we came from
                             SelectItem( prev, unselect_others, extended_select );
                             m_key_current= (wxGenericTreeItem*) prev.m_pItem;
-                            EnsureVisible( prev );
                             break;
                         }
                     }
@@ -2415,7 +2549,6 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
 
                     SelectItem( prev, unselect_others, extended_select );
                     m_key_current=(wxGenericTreeItem*) prev.m_pItem;
-                    EnsureVisible( prev );
                 }
             }
             break;
@@ -2431,7 +2564,6 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
                 }
                 if (prev)
                 {
-                    EnsureVisible( prev );
                     SelectItem( prev, unselect_others, extended_select );
                 }
             }
@@ -2451,7 +2583,6 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
                     wxTreeItemId child = GetFirstChild( m_key_current, cookie );
                     SelectItem( child, unselect_others, extended_select );
                     m_key_current=(wxGenericTreeItem*) child.m_pItem;
-                    EnsureVisible( child );
                 }
                 else
                 {
@@ -2469,7 +2600,6 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
                     {
                         SelectItem( next, unselect_others, extended_select );
                         m_key_current=(wxGenericTreeItem*) next.m_pItem;
-                        EnsureVisible( next );
                     }
                 }
             }
@@ -2495,7 +2625,6 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
 
                 if ( last.IsOk() )
                 {
-                    EnsureVisible( last );
                     SelectItem( last, unselect_others, extended_select );
                 }
             }
@@ -2505,20 +2634,56 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
         case WXK_HOME:
             {
                 wxTreeItemId prev = GetRootItem();
-                if (!prev) break;
-                if (HasFlag(wxTR_HIDE_ROOT))
+                if (!prev)
+                    break;
+
+                if ( HasFlag(wxTR_HIDE_ROOT) )
                 {
                     long dummy;
                     prev = GetFirstChild(prev, dummy);
-                    if (!prev) break;
+                    if (!prev)
+                        break;
                 }
-                EnsureVisible( prev );
+
                 SelectItem( prev, unselect_others, extended_select );
             }
             break;
 
         default:
-            event.Skip();
+            // do not use wxIsalnum() here
+            if ( !event.HasModifiers() && 
+                 ((keyCode >= '0' && keyCode <= '9') ||
+                  (keyCode >= 'a' && keyCode <= 'z') ||
+                  (keyCode >= 'A' && keyCode <= 'Z' )))
+            {
+                // find the next item starting with the given prefix
+                char ch = (char)keyCode;
+                
+                wxTreeItemId id = FindItem(m_current, m_findPrefix + (wxChar)ch);
+                if ( !id.IsOk() )
+                {
+                    // no such item
+                    break;
+                }
+
+                SelectItem(id);
+
+                m_findPrefix += ch;
+
+                // also start the timer to reset the current prefix if the user
+                // doesn't press any more alnum keys soon -- we wouldn't want
+                // to use this prefix for a new item search
+                if ( !m_findTimer )
+                {
+                    m_findTimer = new wxTreeFindTimer(this);
+                }
+
+                m_findTimer->Start(wxTreeFindTimer::DELAY, wxTIMER_ONE_SHOT);
+            }
+            else
+            {
+                event.Skip();
+            }
     }
 }
 
@@ -2542,14 +2707,8 @@ wxTreeItemId wxGenericTreeCtrl::HitTest(const wxPoint& point, int& flags)
         return wxTreeItemId();
     }
 
-    wxClientDC dc(this);
-    PrepareDC(dc);
-    wxCoord x = dc.DeviceToLogicalX( point.x );
-    wxCoord y = dc.DeviceToLogicalY( point.y );
-    wxGenericTreeItem *hit =  m_anchor->HitTest(wxPoint(x, y),
-                                                this,
-                                                flags,
-                                                0 );
+    wxGenericTreeItem *hit =  m_anchor->HitTest(CalcUnscrolledPosition(point),
+                                                this, flags, 0);
     if (hit == NULL)
     {
         flags = wxTREE_HITTEST_NOWHERE;
@@ -2560,8 +2719,8 @@ wxTreeItemId wxGenericTreeCtrl::HitTest(const wxPoint& point, int& flags)
 
 // get the bounding rectangle of the item (or of its label only)
 bool wxGenericTreeCtrl::GetBoundingRect(const wxTreeItemId& item,
-                         wxRect& rect,
-                         bool WXUNUSED(textOnly)) const
+                                        wxRect& rect,
+                                        bool WXUNUSED(textOnly)) const
 {
     wxCHECK_MSG( item.IsOk(), FALSE, _T("invalid item in wxGenericTreeCtrl::GetBoundingRect") );
 
@@ -2579,83 +2738,46 @@ bool wxGenericTreeCtrl::GetBoundingRect(const wxTreeItemId& item,
     return TRUE;
 }
 
-/* **** */
-
 void wxGenericTreeCtrl::Edit( const wxTreeItemId& item )
 {
-    if (!item.IsOk()) return;
+    wxCHECK_RET( item.IsOk(), _T("can't edit an invalid item") );
 
-    m_currentEdit = (wxGenericTreeItem*) item.m_pItem;
+    wxGenericTreeItem *itemEdit = (wxGenericTreeItem *)item.m_pItem;
 
     wxTreeEvent te( wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, GetId() );
-    te.m_item = (long) m_currentEdit;
+    te.m_item = (long) itemEdit;
     te.SetEventObject( this );
-    GetEventHandler()->ProcessEvent( te );
-
-    if (!te.IsAllowed()) return;
+    if ( GetEventHandler()->ProcessEvent( te ) && !te.IsAllowed() )
+    {
+        // vetoed by user
+        return;
+    }
 
     // We have to call this here because the label in
     // question might just have been added and no screen
     // update taken place.
-    if (m_dirty) wxYieldIfNeeded();
-
-    wxString s = m_currentEdit->GetText();
-    int x = m_currentEdit->GetX();
-    int y = m_currentEdit->GetY();
-    int w = m_currentEdit->GetWidth();
-    int h = m_currentEdit->GetHeight();
-
-    int image_h = 0;
-    int image_w = 0;
+    if ( m_dirty )
+        wxYieldIfNeeded();
 
-    int image = m_currentEdit->GetCurrentImage();
-    if ( image != NO_IMAGE )
-    {
-        if ( m_imageListNormal )
-        {
-            m_imageListNormal->GetSize( image, image_w, image_h );
-            image_w += 4;
-        }
-        else
-        {
-            wxFAIL_MSG(_T("you must create an image list to use images!"));
-        }
-    }
-    x += image_w;
-    w -= image_w + 4; // I don't know why +4 is needed
+    wxTreeTextCtrl *text = new wxTreeTextCtrl(this, itemEdit);
 
-    wxClientDC dc(this);
-    PrepareDC( dc );
-    x = dc.LogicalToDeviceX( x );
-    y = dc.LogicalToDeviceY( y );
-
-    wxTreeTextCtrl *text = new wxTreeTextCtrl(this, -1,
-                                              &m_renameAccept,
-                                              &m_renameRes,
-                                              this,
-                                              s,
-                                              wxPoint(x-4,y-4),
-                                              wxSize(w+11,h+8));
     text->SetFocus();
 }
 
-void wxGenericTreeCtrl::OnRenameTimer()
-{
-    Edit( m_current );
-}
-
-void wxGenericTreeCtrl::OnRenameAccept()
+bool wxGenericTreeCtrl::OnRenameAccept(wxGenericTreeItem *item,
+                                       const wxString& value)
 {
-    // TODO if the validator fails this causes a crash
     wxTreeEvent le( wxEVT_COMMAND_TREE_END_LABEL_EDIT, GetId() );
-    le.m_item = (long) m_currentEdit;
+    le.m_item = (long) item;
     le.SetEventObject( this );
-    le.m_label = m_renameRes;
-    GetEventHandler()->ProcessEvent( le );
+    le.m_label = value;
 
-    if (!le.IsAllowed()) return;
+    return !GetEventHandler()->ProcessEvent( le ) || le.IsAllowed();
+}
 
-    SetItemText( m_currentEdit, m_renameRes );
+void wxGenericTreeCtrl::OnRenameTimer()
+{
+    Edit( m_current );
 }
 
 void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event )
@@ -2677,21 +2799,15 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event )
         return;
     }
 
-    wxClientDC dc(this);
-    PrepareDC(dc);
-    wxCoord x = dc.DeviceToLogicalX( event.GetX() );
-    wxCoord y = dc.DeviceToLogicalY( event.GetY() );
+    wxPoint pt = CalcUnscrolledPosition(event.GetPosition());
 
     int flags = 0;
-    wxGenericTreeItem *item = m_anchor->HitTest( wxPoint(x,y),
-                                                 this,
-                                                 flags,
-                                                 0 );
+    wxGenericTreeItem *item = m_anchor->HitTest(pt, this, flags, 0);
 
     if ( event.Dragging() && !m_isDragging )
     {
         if (m_dragCount == 0)
-            m_dragStart = wxPoint(x,y);
+            m_dragStart = pt;
 
         m_dragCount++;
 
@@ -2768,7 +2884,7 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event )
         wxTreeEvent event(wxEVT_COMMAND_TREE_END_DRAG, GetId());
 
         event.m_item = (long) item;
-        event.m_pointDrag = wxPoint(x, y);
+        event.m_pointDrag = pt;
         event.SetEventObject(this);
 
         (void)GetEventHandler()->ProcessEvent(event);
@@ -2794,9 +2910,7 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event )
         {
             wxTreeEvent nevent(wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK, GetId());
             nevent.m_item = (long) item;
-            CalcScrolledPosition(x, y,
-                                 &nevent.m_pointDrag.x,
-                                 &nevent.m_pointDrag.y);
+            nevent.m_pointDrag = CalcScrolledPosition(pt);
             nevent.SetEventObject(this);
             GetEventHandler()->ProcessEvent(nevent);
         }
@@ -2808,10 +2922,17 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event )
                      (flags & wxTREE_HITTEST_ONITEMLABEL) &&
                      HasFlag(wxTR_EDIT_LABELS) )
                 {
-                    if ( m_renameTimer->IsRunning() )
-                        m_renameTimer->Stop();
+                    if ( m_renameTimer )
+                    {
+                        if ( m_renameTimer->IsRunning() )
+                            m_renameTimer->Stop();
+                    }
+                    else
+                    {
+                        m_renameTimer = new wxTreeRenameTimer( this );
+                    }
 
-                    m_renameTimer->Start( 100, TRUE );
+                    m_renameTimer->Start( wxTreeRenameTimer::DELAY, TRUE );
                 }
 
                 m_lastOnSame = FALSE;
@@ -2851,15 +2972,15 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event )
             if ( event.LeftDClick() )
             {
                 // double clicking should not start editing the item label
-                m_renameTimer->Stop();
+                if ( m_renameTimer )
+                    m_renameTimer->Stop();
+
                 m_lastOnSame = FALSE;
 
                 // send activate event first
                 wxTreeEvent nevent( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
                 nevent.m_item = (long) item;
-                CalcScrolledPosition(x, y,
-                                     &nevent.m_pointDrag.x,
-                                     &nevent.m_pointDrag.y);
+                nevent.m_pointDrag = CalcScrolledPosition(pt);
                 nevent.SetEventObject( this );
                 if ( !GetEventHandler()->ProcessEvent( nevent ) )
                 {
@@ -2895,7 +3016,10 @@ void wxGenericTreeCtrl::CalculateSize( wxGenericTreeItem *item, wxDC &dc )
     wxCoord text_w = 0;
     wxCoord text_h = 0;
 
-    if (item->IsBold())
+    wxTreeItemAttr *attr = item->GetAttributes();
+    if ( attr && attr->HasFont() )
+        dc.SetFont(attr->GetFont());
+    else if ( item->IsBold() )
         dc.SetFont(m_boldFont);
 
     dc.GetTextExtent( item->GetText(), &text_w, &text_h );
@@ -2989,20 +3113,14 @@ void wxGenericTreeCtrl::RefreshSubtree(wxGenericTreeItem *item)
 {
     if (m_dirty) return;
 
-    wxClientDC dc(this);
-    PrepareDC(dc);
-
-    int cw = 0;
-    int ch = 0;
-    GetClientSize( &cw, &ch );
+    wxSize client = GetClientSize();
 
     wxRect rect;
-    rect.x = dc.LogicalToDeviceX( 0 );
-    rect.width = cw;
-    rect.y = dc.LogicalToDeviceY( item->GetY() );
-    rect.height = ch;
+    CalcScrolledPosition(0, item->GetY(), NULL, &rect.y);
+    rect.width = client.x;
+    rect.height = client.y;
 
-    Refresh( TRUE, &rect );
+    Refresh(TRUE, &rect);
 
     AdjustMyScrollbars();
 }
@@ -3011,20 +3129,12 @@ void wxGenericTreeCtrl::RefreshLine( wxGenericTreeItem *item )
 {
     if (m_dirty) return;
 
-    wxClientDC dc(this);
-    PrepareDC( dc );
-
-    int cw = 0;
-    int ch = 0;
-    GetClientSize( &cw, &ch );
-
     wxRect rect;
-    rect.x = dc.LogicalToDeviceX( 0 );
-    rect.y = dc.LogicalToDeviceY( item->GetY() );
-    rect.width = cw;
+    CalcScrolledPosition(0, item->GetY(), NULL, &rect.y);
+    rect.width = GetClientSize().x;
     rect.height = GetLineHeight(item); //dc.GetCharHeight() + 6;
 
-    Refresh( TRUE, &rect );
+    Refresh(TRUE, &rect);
 }
 
 void wxGenericTreeCtrl::RefreshSelected()