]> git.saurik.com Git - wxWidgets.git/blobdiff - src/generic/treectlg.cpp
don't crash trying to dereference NULL m_selection (happens when showing a grid witho...
[wxWidgets.git] / src / generic / treectlg.cpp
index 562d1a840e32b76a79a8189dcc5730ef796a946b..6d56dc596d4a7b890567776445d9d86f49c78deb 100644 (file)
@@ -45,7 +45,6 @@
 class WXDLLEXPORT wxGenericTreeItem;
 
 WX_DEFINE_EXPORTED_ARRAY(wxGenericTreeItem *, wxArrayGenericTreeItems);
-//WX_DEFINE_OBJARRAY(wxArrayTreeItemIds);
 
 // ----------------------------------------------------------------------------
 // constants
@@ -53,7 +52,7 @@ 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
@@ -111,12 +110,16 @@ static const char *aqua_arrow_down[] = {
 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
@@ -149,6 +152,22 @@ private:
     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
 {
@@ -694,7 +713,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 );
@@ -768,15 +789,20 @@ 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;
 }
 
 // -----------------------------------------------------------------------------
@@ -1160,6 +1186,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
 // -----------------------------------------------------------------------------
@@ -1246,9 +1318,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);
 }
@@ -1357,7 +1433,7 @@ 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(), 
+    wxCHECK_RET( !HasFlag(wxTR_HIDE_ROOT) || itemId != GetRootItem(),
                  _T("can't expand hidden root") );
 
     if ( !item->HasPlus() )
@@ -1403,7 +1479,7 @@ void wxGenericTreeCtrl::ExpandAll(const wxTreeItemId& item)
 
 void wxGenericTreeCtrl::Collapse(const wxTreeItemId& itemId)
 {
-    wxCHECK_RET( !HasFlag(wxTR_HIDE_ROOT) || itemId != GetRootItem(), 
+    wxCHECK_RET( !HasFlag(wxTR_HIDE_ROOT) || itemId != GetRootItem(),
                  _T("can't collapse hidden root") );
 
     wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
@@ -1568,8 +1644,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") );
 
@@ -1651,7 +1727,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));
@@ -1857,7 +1933,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)
@@ -2356,7 +2435,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:
@@ -2416,7 +2497,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;
                         }
                     }
@@ -2434,7 +2514,6 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
 
                     SelectItem( prev, unselect_others, extended_select );
                     m_key_current=(wxGenericTreeItem*) prev.m_pItem;
-                    EnsureVisible( prev );
                 }
             }
             break;
@@ -2450,7 +2529,6 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
                 }
                 if (prev)
                 {
-                    EnsureVisible( prev );
                     SelectItem( prev, unselect_others, extended_select );
                 }
             }
@@ -2470,7 +2548,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
                 {
@@ -2488,7 +2565,6 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
                     {
                         SelectItem( next, unselect_others, extended_select );
                         m_key_current=(wxGenericTreeItem*) next.m_pItem;
-                        EnsureVisible( next );
                     }
                 }
             }
@@ -2514,7 +2590,6 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
 
                 if ( last.IsOk() )
                 {
-                    EnsureVisible( last );
                     SelectItem( last, unselect_others, extended_select );
                 }
             }
@@ -2524,20 +2599,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();
+            }
     }
 }
 
@@ -2808,10 +2919,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,7 +2969,9 @@ 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
@@ -2993,7 +3113,7 @@ void wxGenericTreeCtrl::RefreshSubtree(wxGenericTreeItem *item)
     wxSize client = GetClientSize();
 
     wxRect rect;
-    CalcScrolledPosition(0, item->GetY(), &rect.x, &rect.y);
+    CalcScrolledPosition(0, item->GetY(), NULL, &rect.y);
     rect.width = client.x;
     rect.height = client.y;
 
@@ -3007,7 +3127,7 @@ void wxGenericTreeCtrl::RefreshLine( wxGenericTreeItem *item )
     if (m_dirty) return;
 
     wxRect rect;
-    CalcScrolledPosition(0, item->GetY(), &rect.x, &rect.y);
+    CalcScrolledPosition(0, item->GetY(), NULL, &rect.y);
     rect.width = GetClientSize().x;
     rect.height = GetLineHeight(item); //dc.GetCharHeight() + 6;