]> git.saurik.com Git - wxWidgets.git/blobdiff - src/generic/treectlg.cpp
digital mars updated
[wxWidgets.git] / src / generic / treectlg.cpp
index c7732185bc8dac6c09f02d7a895da582cc3cc638..f0be2d87232f673442e02b2739abd069a3031cb6 100644 (file)
@@ -5,7 +5,7 @@
 // Created:     01/02/97
 // Modified:    22/10/98 - almost total rewrite, simpler interface (VZ)
 // Id:          $Id$
-// Copyright:   (c) 1998 Robert Roebling, Julian Smart and Markus Holzem
+// Copyright:   (c) 1998 Robert Roebling and Julian Smart
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
 #include "wx/settings.h"
 #include "wx/dcclient.h"
 
+#ifdef __WXMAC__
+    #include "wx/mac/private.h"
+#endif
+
 // -----------------------------------------------------------------------------
 // array types
 // -----------------------------------------------------------------------------
@@ -120,6 +124,8 @@ public:
 
 private:
     wxGenericTreeCtrl *m_owner;
+
+    DECLARE_NO_COPY_CLASS(wxTreeRenameTimer)
 };
 
 // control used for in-place edit
@@ -143,6 +149,7 @@ private:
     bool                m_finished;
 
     DECLARE_EVENT_TABLE()
+    DECLARE_NO_COPY_CLASS(wxTreeTextCtrl)
 };
 
 // timer used to clear wxGenericTreeCtrl::m_findPrefix if no key was pressed
@@ -159,6 +166,8 @@ public:
 
 private:
     wxGenericTreeCtrl *m_owner;
+
+    DECLARE_NO_COPY_CLASS(wxTreeFindTimer)
 };
 
 // a tree item
@@ -287,7 +296,7 @@ private:
     short               m_images[wxTreeItemIcon_Max];
 
     wxCoord             m_x;            // (virtual) offset from top
-    short               m_y;            // (virtual) offset from left
+    wxCoord             m_y;            // (virtual) offset from left
     short               m_width;        // width of this item
     unsigned char       m_height;       // height of this item
 
@@ -298,6 +307,8 @@ private:
                                           // children but has a [+] button
     int                 m_isBold      :1; // render the label in bold font
     int                 m_ownsAttr    :1; // delete attribute when done
+
+    DECLARE_NO_COPY_CLASS(wxGenericTreeItem)
 };
 
 // =============================================================================
@@ -322,6 +333,23 @@ static void EventFlagsToSelType(long style,
     unselect_others = !(extended_select || (ctrlDown && is_multiple));
 }
 
+// check if the given item is under another one
+static bool IsDescendantOf(wxGenericTreeItem *parent, wxGenericTreeItem *item)
+{
+    while ( item )
+    {
+        if ( item == parent )
+        {
+            // item is a descendant of parent
+            return TRUE;
+        }
+
+        item = item->GetParent();
+    }
+
+    return FALSE;
+}
+
 // -----------------------------------------------------------------------------
 // wxTreeRenameTimer (internal)
 // -----------------------------------------------------------------------------
@@ -410,6 +438,8 @@ void wxTreeTextCtrl::Finish()
 {
     if ( !m_finished )
     {
+        m_owner->ResetTextControl();
+
         wxPendingDelete.Append(this);
 
         m_finished = TRUE;
@@ -432,6 +462,7 @@ void wxTreeTextCtrl::OnChar( wxKeyEvent &event )
 
         case WXK_ESCAPE:
             Finish();
+            m_owner->OnRenameCancelled(m_itemEdited);
             break;
 
         default:
@@ -703,7 +734,7 @@ IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl, wxGenericTreeCtrl)
 
 void wxGenericTreeCtrl::Init()
 {
-    m_current = m_key_current = m_anchor = (wxGenericTreeItem *) NULL;
+    m_current = m_key_current = m_anchor = m_select_me = (wxGenericTreeItem *) NULL;
     m_hasFocus = FALSE;
     m_dirty = FALSE;
 
@@ -737,6 +768,7 @@ void wxGenericTreeCtrl::Init()
     m_dragCount = 0;
     m_isDragging = FALSE;
     m_dropTarget = m_oldSelection = (wxGenericTreeItem *)NULL;
+    m_textCtrl = NULL;
 
     m_renameTimer = NULL;
     m_findTimer = NULL;
@@ -744,11 +776,13 @@ void wxGenericTreeCtrl::Init()
     m_lastOnSame = FALSE;
 
     m_normalFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
-    m_boldFont = wxFont( m_normalFont.GetPointSize(),
-                         m_normalFont.GetFamily(),
-                         m_normalFont.GetStyle(),
-                         wxBOLD,
-                         m_normalFont.GetUnderlined());
+    m_boldFont = wxFont(m_normalFont.GetPointSize(),
+                        m_normalFont.GetFamily(),
+                        m_normalFont.GetStyle(),
+                        wxBOLD,
+                        m_normalFont.GetUnderlined(),
+                        m_normalFont.GetFaceName(),
+                        m_normalFont.GetEncoding());
 }
 
 bool wxGenericTreeCtrl::Create(wxWindow *parent,
@@ -901,6 +935,30 @@ wxTreeItemData *wxGenericTreeCtrl::GetItemData(const wxTreeItemId& item) const
     return ((wxGenericTreeItem*) item.m_pItem)->GetData();
 }
 
+wxColour wxGenericTreeCtrl::GetItemTextColour(const wxTreeItemId& item) const
+{
+    wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") );
+
+    wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
+    return pItem->Attr().GetTextColour();
+}
+
+wxColour wxGenericTreeCtrl::GetItemBackgroundColour(const wxTreeItemId& item) const
+{
+    wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") );
+
+    wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
+    return pItem->Attr().GetBackgroundColour();
+}
+
+wxFont wxGenericTreeCtrl::GetItemFont(const wxTreeItemId& item) const
+{
+    wxCHECK_MSG( item.IsOk(), wxNullFont, wxT("invalid tree item") );
+
+    wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem;
+    return pItem->Attr().GetFont();
+}
+
 void wxGenericTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text)
 {
     wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
@@ -989,11 +1047,13 @@ bool wxGenericTreeCtrl::SetFont( const wxFont &font )
     wxScrolledWindow::SetFont(font);
 
     m_normalFont = font ;
-    m_boldFont = wxFont( m_normalFont.GetPointSize(),
-                            m_normalFont.GetFamily(),
-                            m_normalFont.GetStyle(),
-                            wxBOLD,
-                            m_normalFont.GetUnderlined());
+    m_boldFont = wxFont(m_normalFont.GetPointSize(),
+                        m_normalFont.GetFamily(),
+                        m_normalFont.GetStyle(),
+                        wxBOLD,
+                        m_normalFont.GetUnderlined(),
+                        m_normalFont.GetFaceName(),
+                        m_normalFont.GetEncoding());
 
     return TRUE;
 }
@@ -1072,7 +1132,7 @@ bool wxGenericTreeCtrl::IsBold(const wxTreeItemId& item) const
 // navigation
 // -----------------------------------------------------------------------------
 
-wxTreeItemId wxGenericTreeCtrl::GetParent(const wxTreeItemId& item) const
+wxTreeItemId wxGenericTreeCtrl::GetItemParent(const wxTreeItemId& item) const
 {
     wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
 
@@ -1172,7 +1232,7 @@ wxTreeItemId wxGenericTreeCtrl::GetNext(const wxTreeItemId& item) const
          do
          {
               toFind = GetNextSibling(p);
-              p = GetParent(p);
+              p = GetItemParent(p);
          } while (p.IsOk() && !toFind.IsOk());
          return toFind;
     }
@@ -1219,6 +1279,12 @@ wxTreeItemId wxGenericTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
     return wxTreeItemId();
 }
 
+// called by wxTextTreeCtrl when it marks itself for deletion
+void wxGenericTreeCtrl::ResetTextControl()
+{
+  m_textCtrl = NULL;
+}
+
 // find the first item starting with the given prefix after the given item
 wxTreeItemId wxGenericTreeCtrl::FindItem(const wxTreeItemId& idParent,
                                          const wxString& prefixOrig) const
@@ -1416,33 +1482,49 @@ void wxGenericTreeCtrl::Delete(const wxTreeItemId& itemId)
 
     wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
 
-    // don't stay with invalid m_key_current or we will crash in
-    // the next call to OnChar()
-    bool changeKeyCurrent = FALSE;
-    wxGenericTreeItem *itemKey = m_key_current;
-    while ( itemKey )
+    wxGenericTreeItem *parent = item->GetParent();
+
+    // don't keep stale pointers around!
+    if ( IsDescendantOf(item, m_key_current) )
     {
-        if ( itemKey == item )
-        {
-            // m_key_current is a descendant of the item being deleted
-            changeKeyCurrent = TRUE;
-            break;
-        }
-        itemKey = itemKey->GetParent();
+        // Don't silently change the selection:
+        // do it properly in idle time, so event
+        // handlers get called.
+        
+        // m_key_current = parent;
+        m_key_current = NULL;
     }
 
-    wxGenericTreeItem *parent = item->GetParent();
+    // m_select_me records whether we need to select
+    // a different item, in idle time.
+    if ( m_select_me && IsDescendantOf(item, m_select_me) )
+    {
+        m_select_me = parent;
+    }
+
+    if ( IsDescendantOf(item, m_current) )
+    {
+        // Don't silently change the selection:
+        // do it properly in idle time, so event
+        // handlers get called.
+        
+        // m_current = parent;
+        m_current = NULL;
+        m_select_me = parent;
+    }
+
+    // remove the item from the tree
     if ( parent )
     {
         parent->GetChildren().Remove( item );  // remove by value
     }
-
-    if ( changeKeyCurrent )
+    else // deleting the root
     {
-        // may be NULL or not
-        m_key_current = parent;
+        // nothing will be left in the tree
+        m_anchor = NULL;
     }
 
+    // and delete all of its children and the item itself now
     item->DeleteChildren(this);
     SendDeleteEvent(item);
     delete item;
@@ -1452,12 +1534,7 @@ void wxGenericTreeCtrl::DeleteAllItems()
 {
     if ( m_anchor )
     {
-        m_dirty = TRUE;
-
-        m_anchor->DeleteChildren(this);
-        delete m_anchor;
-
-        m_anchor = NULL;
+        Delete(m_anchor);
     }
 }
 
@@ -1496,17 +1573,20 @@ void wxGenericTreeCtrl::Expand(const wxTreeItemId& itemId)
 
 void wxGenericTreeCtrl::ExpandAll(const wxTreeItemId& item)
 {
-    Expand(item);
-    if ( IsExpanded(item) )
+    if ( !HasFlag(wxTR_HIDE_ROOT) || item != GetRootItem())
     {
-        long cookie;
-        wxTreeItemId child = GetFirstChild(item, cookie);
-        while ( child.IsOk() )
-        {
-            ExpandAll(child);
+        Expand(item);
+        if ( !IsExpanded(item) )
+            return;
+    }
 
-            child = GetNextChild(item, cookie);
-        }
+    long cookie;
+    wxTreeItemId child = GetFirstChild(item, cookie);
+    while ( child.IsOk() )
+    {
+        ExpandAll(child);
+
+        child = GetNextChild(item, cookie);
     }
 }
 
@@ -1572,6 +1652,7 @@ void wxGenericTreeCtrl::Unselect()
         RefreshLine( m_current );
 
         m_current = NULL;
+        m_select_me = NULL;
     }
 }
 
@@ -1657,6 +1738,7 @@ void wxGenericTreeCtrl::SelectItemRange(wxGenericTreeItem *item1, wxGenericTreeI
 {
     // item2 is not necessary after item1
     wxGenericTreeItem *first=NULL, *last=NULL;
+    m_select_me = NULL;
 
     // choice first' and 'last' between item1 and item2
     if (item1->GetY()<item2->GetY())
@@ -1684,6 +1766,8 @@ void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId,
 {
     wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") );
 
+    m_select_me = NULL;
+    
     bool is_single=!(GetWindowStyleFlag() & wxTR_MULTIPLE);
     wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem;
 
@@ -1715,13 +1799,13 @@ void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId,
     if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() )
         return;
 
-    wxTreeItemId parent = GetParent( itemId );
+    wxTreeItemId parent = GetItemParent( itemId );
     while (parent.IsOk())
     {
         if (!IsExpanded(parent))
             Expand( parent );
 
-        parent = GetParent( parent );
+        parent = GetItemParent( parent );
     }
 
     EnsureVisible( itemId );
@@ -2070,7 +2154,20 @@ void wxGenericTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc)
 
     if ( item->IsSelected() )
     {
+// under mac selections are only a rectangle in case they don't have the focus
+#ifdef __WXMAC__
+        if ( !m_hasFocus )
+        {
+            dc.SetBrush( *wxTRANSPARENT_BRUSH ) ;
+            dc.SetPen( wxPen( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) , 1 , wxSOLID ) ) ;
+        }
+        else
+        {
+            dc.SetBrush( *m_hilightBrush ) ;
+        }
+#else
         dc.SetBrush(*(m_hasFocus ? m_hilightBrush : m_hilightUnfocusedBrush));
+#endif
     }
     else
     {
@@ -2251,10 +2348,26 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level
 
                 if (HasFlag(wxTR_AQUA_BUTTONS))
                 {
+                    // This causes update problems, so disabling for now.
+#if 0 // def __WXMAC__
+                    wxMacPortSetter helper(&dc) ;
+                    wxMacWindowClipper clipper(this) ;
+                    wxDC::MacSetupBackgroundForCurrentPort( MacGetBackgroundBrush() ) ;
+
+                    int loc_x = x - 5 ;
+                    int loc_y = y_mid - 6 ;
+                    MacWindowToRootWindow( & loc_x , & loc_y ) ;
+                    Rect bounds = { loc_y , loc_x , loc_y + 18 , loc_x + 12 } ;
+                    ThemeButtonDrawInfo info = { kThemeStateActive , item->IsExpanded() ? kThemeDisclosureDown : kThemeDisclosureRight ,
+                        kThemeAdornmentNone }; 
+                    DrawThemeButton( &bounds, kThemeDisclosureButton , 
+                        &info , NULL , NULL , NULL , NULL ) ;
+#else
                     if (item->IsExpanded())
                         dc.DrawBitmap( *m_arrowDown, x-5, y_mid-6, TRUE );
                     else
                         dc.DrawBitmap( *m_arrowRight, x-5, y_mid-6, TRUE );
+#endif
                 }
                 else
                 {
@@ -2327,7 +2440,23 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level
                 // draw line down to last child
                 oldY += GetLineHeight(children[n-1])>>1;
                 if (HasButtons()) y_mid += 5;
-                dc.DrawLine(x, y_mid, x, oldY);
+
+                // Only draw the portion of the line that is visible, in case it is huge
+                wxCoord        xOrigin=0, yOrigin=0, width, height;
+                dc.GetDeviceOrigin(&xOrigin, &yOrigin);
+                yOrigin = abs(yOrigin);
+                GetClientSize(&width, &height);
+
+                // Move end points to the begining/end of the view?
+                if (y_mid < yOrigin)
+                    y_mid = yOrigin;
+                if (oldY > yOrigin + height)
+                    oldY = yOrigin + height;
+
+                // after the adjustments if y_mid is larger than oldY then the line
+                // isn't visible at all so don't draw anything
+                if (y_mid < oldY)
+                    dc.DrawLine(x, y_mid, x, oldY);
             }
         }
     }
@@ -2471,7 +2600,7 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
     // home  : go to root
     // end   : go to last item without opening parents
     // alnum : start or continue searching for the item with this prefix
-    int keyCode = event.KeyCode();
+    int keyCode = event.GetKeyCode();
     switch ( keyCode )
     {
         case '+':
@@ -2502,12 +2631,20 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
 
         case ' ':
         case WXK_RETURN:
+            if ( !event.HasModifiers() )
             {
                 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
                 event.m_item = (long) m_current;
                 event.SetEventObject( this );
                 GetEventHandler()->ProcessEvent( event );
             }
+
+            // in any case, also generate the normal key event for this key,
+            // even if we generated the ACTIVATED event above: this is what
+            // wxMSW does and it makes sense because you might not want to
+            // process ACTIVATED event at all and handle Space and Return
+            // directly (and differently) which would be impossible otherwise
+            event.Skip();
             break;
 
             // up goes to the previous sibling or to the last
@@ -2517,7 +2654,7 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
                 wxTreeItemId prev = GetPrevSibling( m_key_current );
                 if (!prev)
                 {
-                    prev = GetParent( m_key_current );
+                    prev = GetItemParent( m_key_current );
                     if ((prev == GetRootItem()) && HasFlag(wxTR_HIDE_ROOT))
                     {
                         break;  // don't go to root if it is hidden
@@ -2556,7 +2693,7 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
             // left arrow goes to the parent
         case WXK_LEFT:
             {
-                wxTreeItemId prev = GetParent( m_current );
+                wxTreeItemId prev = GetItemParent( m_current );
                 if ((prev == GetRootItem()) && HasFlag(wxTR_HIDE_ROOT))
                 {
                     // don't go to root if it is hidden
@@ -2592,7 +2729,7 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
                         wxTreeItemId current = m_key_current;
                         while (current && !next)
                         {
-                            current = GetParent( current );
+                            current = GetItemParent( current );
                             if (current) next = GetNextSibling( current );
                         }
                     }
@@ -2651,14 +2788,14 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event )
 
         default:
             // do not use wxIsalnum() here
-            if ( !event.HasModifiers() && 
+            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() )
                 {
@@ -2759,9 +2896,17 @@ void wxGenericTreeCtrl::Edit( const wxTreeItemId& item )
     if ( m_dirty )
         wxYieldIfNeeded();
 
-    wxTreeTextCtrl *text = new wxTreeTextCtrl(this, itemEdit);
+    m_textCtrl = new wxTreeTextCtrl(this, itemEdit);
 
-    text->SetFocus();
+    m_textCtrl->SetFocus();
+}
+
+// returns a pointer to the text edit control if the item is being
+// edited, NULL otherwise (it's assumed that no more than one item may
+// be edited simultaneously)
+wxTextCtrl* wxGenericTreeCtrl::GetEditControl() const
+{
+    return m_textCtrl;
 }
 
 bool wxGenericTreeCtrl::OnRenameAccept(wxGenericTreeItem *item,
@@ -2771,10 +2916,26 @@ bool wxGenericTreeCtrl::OnRenameAccept(wxGenericTreeItem *item,
     le.m_item = (long) item;
     le.SetEventObject( this );
     le.m_label = value;
+    le.m_editCancelled = FALSE;
 
     return !GetEventHandler()->ProcessEvent( le ) || le.IsAllowed();
 }
 
+void wxGenericTreeCtrl::OnRenameCancelled(wxGenericTreeItem *item)
+{
+    // let owner know that the edit was cancelled
+    wxTreeEvent le( wxEVT_COMMAND_TREE_END_LABEL_EDIT, GetId() );
+    le.m_item = (long) item;
+    le.SetEventObject( this );
+    le.m_label = wxEmptyString;
+    le.m_editCancelled = FALSE;
+
+    GetEventHandler()->ProcessEvent( le );
+}
+
+
+
+
 void wxGenericTreeCtrl::OnRenameTimer()
 {
     Edit( m_current );
@@ -2999,6 +3160,18 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event )
 
 void wxGenericTreeCtrl::OnIdle( wxIdleEvent &WXUNUSED(event) )
 {
+    // Check if we need to select the root item
+    // because nothing else has been selected.
+    // Delaying it means that we can invoke event handlers
+    // as required, when a first item is selected.
+    if (!HasFlag(wxTR_MULTIPLE) && !GetSelection().IsOk())
+    {
+        if (m_select_me)
+            SelectItem(m_select_me);
+        else if (GetRootItem().IsOk())
+            SelectItem(GetRootItem());
+    }
+
     /* after all changes have been done to the tree control,
      * we actually redraw the tree when everything is over */