X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/a0fad723410981793735e10284a3fdb16e9b0682..9b49405777342458dc1666001865eef7309b6c30:/src/generic/treectlg.cpp diff --git a/src/generic/treectlg.cpp b/src/generic/treectlg.cpp index 986c96f5b4..e63d023e05 100644 --- a/src/generic/treectlg.cpp +++ b/src/generic/treectlg.cpp @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: treectlg.cpp +// Name: src/generic/treectlg.cpp // Purpose: generic tree control implementation // Author: Robert Roebling // Created: 01/02/97 @@ -17,10 +17,6 @@ // headers // ----------------------------------------------------------------------------- -#ifdef __GNUG__ - #pragma implementation "treectlg.h" -#endif - // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" @@ -30,23 +26,32 @@ #if wxUSE_TREECTRL -#include "wx/treebase.h" +#include "wx/treectrl.h" + +#ifndef WX_PRECOMP + #include "wx/dcclient.h" + #include "wx/timer.h" + #include "wx/settings.h" + #include "wx/listbox.h" + #include "wx/textctrl.h" +#endif + #include "wx/generic/treectlg.h" -#include "wx/timer.h" -#include "wx/textctrl.h" #include "wx/imaglist.h" -#include "wx/settings.h" -#include "wx/dcclient.h" #include "wx/renderer.h" +#ifdef __WXMAC__ + #include "wx/osx/private.h" +#endif + // ----------------------------------------------------------------------------- // array types // ----------------------------------------------------------------------------- -class WXDLLEXPORT wxGenericTreeItem; +class WXDLLIMPEXP_FWD_CORE wxGenericTreeItem; -WX_DEFINE_EXPORTED_ARRAY_NO_PTR(wxGenericTreeItem *, wxArrayGenericTreeItems); +WX_DEFINE_ARRAY_PTR(wxGenericTreeItem *, wxArrayGenericTreeItems); // ---------------------------------------------------------------------------- // constants @@ -56,6 +61,12 @@ static const int NO_IMAGE = -1; static const int PIXELS_PER_UNIT = 10; +// the margin between the item state image and the item normal image +static const int MARGIN_BETWEEN_STATE_AND_IMAGE = 2; + +// the margin between the item image and the item text +static const int MARGIN_BETWEEN_IMAGE_AND_TEXT = 4; + // ----------------------------------------------------------------------------- // private classes // ----------------------------------------------------------------------------- @@ -75,7 +86,7 @@ public: private: wxGenericTreeCtrl *m_owner; - DECLARE_NO_COPY_CLASS(wxTreeRenameTimer) + wxDECLARE_NO_COPY_CLASS(wxTreeRenameTimer); }; // control used for in-place edit @@ -84,22 +95,26 @@ class WXDLLEXPORT wxTreeTextCtrl: public wxTextCtrl public: wxTreeTextCtrl(wxGenericTreeCtrl *owner, wxGenericTreeItem *item); + void EndEdit( bool discardChanges ); + + const wxGenericTreeItem* item() const { return m_itemEdited; } + protected: void OnChar( wxKeyEvent &event ); void OnKeyUp( wxKeyEvent &event ); void OnKillFocus( wxFocusEvent &event ); bool AcceptChanges(); - void Finish(); + void Finish( bool setfocus ); private: wxGenericTreeCtrl *m_owner; wxGenericTreeItem *m_itemEdited; wxString m_startValue; - bool m_finished; + bool m_aboutToFinish; DECLARE_EVENT_TABLE() - DECLARE_NO_COPY_CLASS(wxTreeTextCtrl) + wxDECLARE_NO_COPY_CLASS(wxTreeTextCtrl); }; // timer used to clear wxGenericTreeCtrl::m_findPrefix if no key was pressed @@ -117,7 +132,7 @@ public: private: wxGenericTreeCtrl *m_owner; - DECLARE_NO_COPY_CLASS(wxTreeFindTimer) + wxDECLARE_NO_COPY_CLASS(wxTreeFindTimer); }; // a tree item @@ -125,7 +140,13 @@ class WXDLLEXPORT wxGenericTreeItem { public: // ctors & dtor - wxGenericTreeItem() { m_data = NULL; } + wxGenericTreeItem() + { + m_data = NULL; + m_widthText = + m_heightText = -1; + } + wxGenericTreeItem( wxGenericTreeItem *parent, const wxString& text, int image, @@ -141,18 +162,36 @@ public: int GetImage(wxTreeItemIcon which = wxTreeItemIcon_Normal) const { return m_images[which]; } wxTreeItemData *GetData() const { return m_data; } + int GetState() const { return m_state; } // returns the current image for the item (depending on its // selected/expanded/whatever state) int GetCurrentImage() const; - void SetText( const wxString &text ); - void SetImage(int image, wxTreeItemIcon which) { m_images[which] = image; } + void SetText(const wxString& text) + { + m_text = text; + + ResetTextSize(); + } + + void SetImage(int image, wxTreeItemIcon which) + { + m_images[which] = image; + m_width = 0; + } + void SetData(wxTreeItemData *data) { m_data = data; } + void SetState(int state) { m_state = state; m_width = 0; } - void SetHasPlus(bool has = TRUE) { m_hasPlus = has; } + void SetHasPlus(bool has = true) { m_hasPlus = has; } + + void SetBold(bool bold) + { + m_isBold = bold; - void SetBold(bool bold) { m_isBold = bold; } + ResetTextSize(); + } int GetX() const { return m_x; } int GetY() const { return m_y; } @@ -160,39 +199,80 @@ public: void SetX(int x) { m_x = x; } void SetY(int y) { m_y = y; } - int GetHeight() const { return m_height; } - int GetWidth() const { return m_width; } + int GetHeight() const { return m_height; } + int GetWidth() const { return m_width; } + + int GetTextHeight() const + { + wxASSERT_MSG( m_heightText != -1, "must call CalculateSize() first" ); + + return m_heightText; + } + + int GetTextWidth() const + { + wxASSERT_MSG( m_widthText != -1, "must call CalculateSize() first" ); - void SetHeight(int h) { m_height = h; } - void SetWidth(int w) { m_width = w; } + return m_widthText; + } wxGenericTreeItem *GetParent() const { return m_parent; } + // sets the items font for the specified DC if it uses any special font or + // simply returns false otherwise + bool SetFont(wxGenericTreeCtrl *control, wxDC& dc) const + { + wxFont font; + + wxTreeItemAttr * const attr = GetAttributes(); + if ( attr && attr->HasFont() ) + font = attr->GetFont(); + else if ( IsBold() ) + font = control->m_boldFont; + else + return false; + + dc.SetFont(font); + return true; + } + // operations - // deletes all children notifying the treectrl about it if !NULL - // pointer given - void DeleteChildren(wxGenericTreeCtrl *tree = NULL); + + // deletes all children notifying the treectrl about it + void DeleteChildren(wxGenericTreeCtrl *tree); // get count of all children (and grand children if 'recursively') - size_t GetChildrenCount(bool recursively = TRUE) const; + size_t GetChildrenCount(bool recursively = true) const; void Insert(wxGenericTreeItem *child, size_t index) - { m_children.Insert(child, index); } + { m_children.Insert(child, index); } + + // calculate and cache the item size using either the provided DC (which is + // supposed to have wxGenericTreeCtrl::m_normalFont selected into it!) or a + // wxClientDC on the control window + void CalculateSize(wxGenericTreeCtrl *control, wxDC& dc) + { DoCalculateSize(control, dc, true /* dc uses normal font */); } + void CalculateSize(wxGenericTreeCtrl *control); void GetSize( int &x, int &y, const wxGenericTreeCtrl* ); + void ResetSize() { m_width = 0; } + void ResetTextSize() { m_width = 0; m_widthText = -1; } + void RecursiveResetSize(); + void RecursiveResetTextSize(); + // return the item at given position (or NULL if no item), onButton is - // TRUE if the point belongs to the item's button, otherwise it lies - // on the button's label + // true if the point belongs to the item's button, otherwise it lies + // on the item's label wxGenericTreeItem *HitTest( const wxPoint& point, const wxGenericTreeCtrl *, int &flags, int level ); - void Expand() { m_isCollapsed = FALSE; } - void Collapse() { m_isCollapsed = TRUE; } + void Expand() { m_isCollapsed = false; } + void Collapse() { m_isCollapsed = true; } - void SetHilight( bool set = TRUE ) { m_hasHilight = set; } + void SetHilight( bool set = true ) { m_hasHilight = set; } // status inquiries bool HasChildren() const { return !m_children.IsEmpty(); } @@ -210,7 +290,7 @@ public: if ( !m_attr ) { m_attr = new wxTreeItemAttr; - m_ownsAttr = TRUE; + m_ownsAttr = true; } return *m_attr; } @@ -219,23 +299,40 @@ public: { if ( m_ownsAttr ) delete m_attr; m_attr = attr; - m_ownsAttr = FALSE; + m_ownsAttr = false; + m_width = 0; + m_widthText = -1; } // set them and delete when done void AssignAttributes(wxTreeItemAttr *attr) { SetAttributes(attr); - m_ownsAttr = TRUE; + m_ownsAttr = true; + m_width = 0; + m_widthText = -1; } private: + // calculate the size of this item, i.e. set m_width, m_height and + // m_widthText and m_heightText properly + // + // if dcUsesNormalFont is true, the current dc font must be the normal tree + // control font + void DoCalculateSize(wxGenericTreeCtrl *control, + wxDC& dc, + bool dcUsesNormalFont); + // since there can be very many of these, we save size by chosing // the smallest representation for the elements and by ordering // the members to avoid padding. wxString m_text; // label to be rendered for item + int m_widthText; + int m_heightText; wxTreeItemData *m_data; // user-provided data + int m_state; // item state + wxArrayGenericTreeItems m_children; // list of children wxGenericTreeItem *m_parent; // parent of this item @@ -243,22 +340,22 @@ private: // tree ctrl images for the normal, selected, expanded and // expanded+selected states - short m_images[wxTreeItemIcon_Max]; + int m_images[wxTreeItemIcon_Max]; wxCoord m_x; // (virtual) offset from top wxCoord m_y; // (virtual) offset from left - short m_width; // width of this item - unsigned char m_height; // height of this item + int m_width; // width of this item + int m_height; // height of this item // use bitfields to save size - int m_isCollapsed :1; - int m_hasHilight :1; // same as focused - int m_hasPlus :1; // used for item which doesn't have + unsigned int m_isCollapsed :1; + unsigned int m_hasHilight :1; // same as focused + unsigned int m_hasPlus :1; // used for item which doesn't have // children but has a [+] button - int m_isBold :1; // render the label in bold font - int m_ownsAttr :1; // delete attribute when done + unsigned int m_isBold :1; // render the label in bold font + unsigned int m_ownsAttr :1; // delete attribute when done - DECLARE_NO_COPY_CLASS(wxGenericTreeItem) + wxDECLARE_NO_COPY_CLASS(wxGenericTreeItem); }; // ============================================================================= @@ -284,20 +381,21 @@ static void EventFlagsToSelType(long style, } // check if the given item is under another one -static bool IsDescendantOf(wxGenericTreeItem *parent, wxGenericTreeItem *item) +static bool +IsDescendantOf(const wxGenericTreeItem *parent, const wxGenericTreeItem *item) { while ( item ) { if ( item == parent ) { // item is a descendant of parent - return TRUE; + return true; } item = item->GetParent(); } - return FALSE; + return false; } // ----------------------------------------------------------------------------- @@ -329,37 +427,54 @@ wxTreeTextCtrl::wxTreeTextCtrl(wxGenericTreeCtrl *owner, : m_itemEdited(item), m_startValue(item->GetText()) { m_owner = owner; - m_finished = FALSE; + m_aboutToFinish = false; - int w = m_itemEdited->GetWidth(), - h = m_itemEdited->GetHeight(); + wxRect rect; + m_owner->GetBoundingRect(m_itemEdited, rect, true); + + // corrects position and size for better appearance +#ifdef __WXMSW__ + rect.x -= 5; + rect.width += 10; +#elif defined(__WXGTK__) + rect.x -= 5; + rect.y -= 2; + rect.width += 8; + rect.height += 4; +#elif defined(__WXMAC__) + int bestHeight = GetBestSize().y - 8; + if ( rect.height > bestHeight ) + { + int diff = rect.height - bestHeight; + rect.height -= diff; + rect.y += diff / 2; + } +#endif // platforms - int x, y; - m_owner->CalcScrolledPosition(item->GetX(), item->GetY(), &x, &y); + (void)Create(m_owner, wxID_ANY, m_startValue, + rect.GetPosition(), rect.GetSize()); - int image_h = 0, - image_w = 0; + SetSelection(-1, -1); +} - int image = item->GetCurrentImage(); - if ( image != NO_IMAGE ) +void wxTreeTextCtrl::EndEdit(bool discardChanges) +{ + m_aboutToFinish = true; + + if ( discardChanges ) { - 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!")); - } - } + m_owner->OnRenameCancelled(m_itemEdited); - // FIXME: what are all these hardcoded 4, 8 and 11s really? - x += image_w; - w -= image_w + 4; + Finish( true ); + } + else + { + // Notify the owner about the changes + AcceptChanges(); - (void)Create(m_owner, wxID_ANY, m_startValue, - wxPoint(x - 4, y - 4), wxSize(w + 11, h + 8)); + // Even if vetoed, close the control (consistent with MSW) + Finish( true ); + } } bool wxTreeTextCtrl::AcceptChanges() @@ -369,33 +484,35 @@ bool wxTreeTextCtrl::AcceptChanges() if ( value == m_startValue ) { // nothing changed, always accept - return TRUE; + // when an item remains unchanged, the owner + // needs to be notified that the user decided + // not to change the tree item label, and that + // the edit has been cancelled + + m_owner->OnRenameCancelled(m_itemEdited); + return true; } if ( !m_owner->OnRenameAccept(m_itemEdited, value) ) { // vetoed by the user - return FALSE; + return false; } // accepted, do rename the item m_owner->SetItemText(m_itemEdited, value); - return TRUE; + return true; } -void wxTreeTextCtrl::Finish() +void wxTreeTextCtrl::Finish( bool setfocus ) { - if ( !m_finished ) - { - m_owner->ResetTextControl(); - - wxPendingDelete.Append(this); + m_owner->ResetTextControl(); - m_finished = TRUE; + wxPendingDelete.Append(this); - m_owner->SetFocus(); // This doesn't work. TODO. - } + if (setfocus) + m_owner->SetFocus(); } void wxTreeTextCtrl::OnChar( wxKeyEvent &event ) @@ -403,16 +520,11 @@ 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 + EndEdit( false ); + break; case WXK_ESCAPE: - Finish(); - m_owner->OnRenameCancelled(m_itemEdited); + EndEdit( true ); break; default: @@ -422,7 +534,7 @@ void wxTreeTextCtrl::OnChar( wxKeyEvent &event ) void wxTreeTextCtrl::OnKeyUp( wxKeyEvent &event ) { - if ( !m_finished ) + if ( !m_aboutToFinish ) { // auto-grow the textctrl: wxSize parentSize = m_owner->GetSize(); @@ -434,7 +546,7 @@ void wxTreeTextCtrl::OnKeyUp( wxKeyEvent &event ) sx = parentSize.x - myPos.x; if (mySize.x > sx) sx = mySize.x; - SetSize(sx, -1); + SetSize(sx, wxDefaultCoord); } event.Skip(); @@ -442,16 +554,16 @@ void wxTreeTextCtrl::OnKeyUp( wxKeyEvent &event ) void wxTreeTextCtrl::OnKillFocus( wxFocusEvent &event ) { - if ( m_finished ) + if ( !m_aboutToFinish ) { - event.Skip(); - return; - } + if ( !AcceptChanges() ) + m_owner->OnRenameCancelled( m_itemEdited ); - if ( AcceptChanges() ) - { - Finish(); + Finish( false ); } + + // We should let the native text control handle focus, too. + event.Skip(); } // ----------------------------------------------------------------------------- @@ -470,21 +582,25 @@ wxGenericTreeItem::wxGenericTreeItem(wxGenericTreeItem *parent, m_images[wxTreeItemIcon_SelectedExpanded] = NO_IMAGE; m_data = data; + m_state = wxTREE_ITEMSTATE_NONE; m_x = m_y = 0; - m_isCollapsed = TRUE; - m_hasHilight = FALSE; - m_hasPlus = FALSE; - m_isBold = FALSE; + m_isCollapsed = true; + m_hasHilight = false; + m_hasPlus = false; + m_isBold = false; m_parent = parent; - m_attr = (wxTreeItemAttr *)NULL; - m_ownsAttr = FALSE; + m_attr = NULL; + m_ownsAttr = false; // We don't know the height here yet. m_width = 0; m_height = 0; + + m_widthText = -1; + m_heightText = -1; } wxGenericTreeItem::~wxGenericTreeItem() @@ -494,33 +610,29 @@ wxGenericTreeItem::~wxGenericTreeItem() if (m_ownsAttr) delete m_attr; wxASSERT_MSG( m_children.IsEmpty(), - wxT("please call DeleteChildren() before deleting the item") ); + "must call DeleteChildren() before deleting the item" ); } void wxGenericTreeItem::DeleteChildren(wxGenericTreeCtrl *tree) { - size_t count = m_children.Count(); + size_t count = m_children.GetCount(); for ( size_t n = 0; n < count; n++ ) { wxGenericTreeItem *child = m_children[n]; - if (tree) - tree->SendDeleteEvent(child); + tree->SendDeleteEvent(child); child->DeleteChildren(tree); + if ( child == tree->m_select_me ) + tree->m_select_me = NULL; delete child; } m_children.Empty(); } -void wxGenericTreeItem::SetText( const wxString &text ) -{ - m_text = text; -} - size_t wxGenericTreeItem::GetChildrenCount(bool recursively) const { - size_t count = m_children.Count(); + size_t count = m_children.GetCount(); if ( !recursively ) return count; @@ -543,7 +655,7 @@ void wxGenericTreeItem::GetSize( int &x, int &y, if (IsExpanded()) { - size_t count = m_children.Count(); + size_t count = m_children.GetCount(); for ( size_t n = 0; n < count; ++n ) { m_children[n]->GetSize( x, y, theButton ); @@ -569,11 +681,19 @@ wxGenericTreeItem *wxGenericTreeItem::HitTest(const wxPoint& point, else flags |= wxTREE_HITTEST_ONITEMLOWERPART; - // 5 is the size of the plus sign int xCross = m_x - theCtrl->GetSpacing(); - if ((point.x > xCross-5) && (point.x < xCross+5) && - (point.y > y_mid-5) && (point.y < y_mid+5) && +#ifdef __WXMAC__ + // according to the drawing code the triangels are drawn + // at -4 , -4 from the position up to +10/+10 max + if ((point.x > xCross-4) && (point.x < xCross+10) && + (point.y > y_mid-4) && (point.y < y_mid+10) && + HasPlus() && theCtrl->HasButtons() ) +#else + // 5 is the size of the plus sign + if ((point.x > xCross-6) && (point.x < xCross+6) && + (point.y > y_mid-6) && (point.y < y_mid+6) && HasPlus() && theCtrl->HasButtons() ) +#endif { flags |= wxTREE_HITTEST_ONITEMBUTTON; return this; @@ -586,10 +706,29 @@ wxGenericTreeItem *wxGenericTreeItem::HitTest(const wxPoint& point, // assuming every image (normal and selected) has the same size! if ( (GetImage() != NO_IMAGE) && theCtrl->m_imageListNormal ) + { theCtrl->m_imageListNormal->GetSize(GetImage(), image_w, image_h); + } + + int state_w = -1; + int state_h; - if ((image_w != -1) && (point.x <= m_x + image_w + 1)) + if ( (GetState() != wxTREE_ITEMSTATE_NONE) && + theCtrl->m_imageListState ) + { + theCtrl->m_imageListState->GetSize(GetState(), + state_w, state_h); + } + + if ((state_w != -1) && (point.x <= m_x + state_w + 1)) + flags |= wxTREE_HITTEST_ONITEMSTATEICON; + else if ((image_w != -1) && + (point.x <= m_x + + (state_w != -1 ? state_w + + MARGIN_BETWEEN_STATE_AND_IMAGE + : 0) + + image_w + 1)) flags |= wxTREE_HITTEST_ONITEMICON; else flags |= wxTREE_HITTEST_ONITEMLABEL; @@ -606,11 +745,11 @@ wxGenericTreeItem *wxGenericTreeItem::HitTest(const wxPoint& point, } // if children are expanded, fall through to evaluate them - if (m_isCollapsed) return (wxGenericTreeItem*) NULL; + if (m_isCollapsed) return NULL; } // evaluate children - size_t count = m_children.Count(); + size_t count = m_children.GetCount(); for ( size_t n = 0; n < count; n++ ) { wxGenericTreeItem *res = m_children[n]->HitTest( point, @@ -621,7 +760,7 @@ wxGenericTreeItem *wxGenericTreeItem::HitTest(const wxPoint& point, return res; } - return (wxGenericTreeItem*) NULL; + return NULL; } int wxGenericTreeItem::GetCurrentImage() const @@ -654,21 +793,122 @@ int wxGenericTreeItem::GetCurrentImage() const return image; } +void wxGenericTreeItem::CalculateSize(wxGenericTreeCtrl* control) +{ + // check if we need to do anything before creating the DC + if ( m_width != 0 ) + return; + + wxClientDC dc(control); + DoCalculateSize(control, dc, false /* normal font not used */); +} + +void +wxGenericTreeItem::DoCalculateSize(wxGenericTreeCtrl* control, + wxDC& dc, + bool dcUsesNormalFont) +{ + if ( m_width != 0 ) // Size known, nothing to do + return; + + if ( m_widthText == -1 ) + { + bool fontChanged; + if ( SetFont(control, dc) ) + { + fontChanged = true; + } + else // we have no special font + { + if ( !dcUsesNormalFont ) + { + // but we do need to ensure that the normal font is used: notice + // that this doesn't count as changing the font as we don't need + // to restore it + dc.SetFont(control->m_normalFont); + } + + fontChanged = false; + } + + dc.GetTextExtent( GetText(), &m_widthText, &m_heightText ); + + // restore normal font if the DC used it previously and we changed it + if ( fontChanged ) + dc.SetFont(control->m_normalFont); + } + + int text_h = m_heightText + 2; + + int image_h = 0, image_w = 0; + int image = GetCurrentImage(); + if ( image != NO_IMAGE && control->m_imageListNormal ) + { + control->m_imageListNormal->GetSize(image, image_w, image_h); + image_w += MARGIN_BETWEEN_IMAGE_AND_TEXT; + } + + int state_h = 0, state_w = 0; + int state = GetState(); + if ( state != wxTREE_ITEMSTATE_NONE && control->m_imageListState ) + { + control->m_imageListState->GetSize(state, state_w, state_h); + if ( image_w != 0 ) + state_w += MARGIN_BETWEEN_STATE_AND_IMAGE; + else + state_w += MARGIN_BETWEEN_IMAGE_AND_TEXT; + } + + int img_h = wxMax(state_h, image_h); + m_height = wxMax(img_h, text_h); + + if (m_height < 30) + m_height += 2; // at least 2 pixels + else + m_height += m_height / 10; // otherwise 10% extra spacing + + if (m_height > control->m_lineHeight) + control->m_lineHeight = m_height; + + m_width = state_w + image_w + m_widthText + 2; +} + +void wxGenericTreeItem::RecursiveResetSize() +{ + m_width = 0; + + const size_t count = m_children.Count(); + for (size_t i = 0; i < count; i++ ) + m_children[i]->RecursiveResetSize(); +} + +void wxGenericTreeItem::RecursiveResetTextSize() +{ + m_width = 0; + m_widthText = -1; + + const size_t count = m_children.Count(); + for (size_t i = 0; i < count; i++ ) + m_children[i]->RecursiveResetTextSize(); +} + // ----------------------------------------------------------------------------- // wxGenericTreeCtrl implementation // ----------------------------------------------------------------------------- -IMPLEMENT_DYNAMIC_CLASS(wxGenericTreeCtrl, wxScrolledWindow) +IMPLEMENT_DYNAMIC_CLASS(wxGenericTreeCtrl, wxControl) -BEGIN_EVENT_TABLE(wxGenericTreeCtrl,wxScrolledWindow) +BEGIN_EVENT_TABLE(wxGenericTreeCtrl, wxTreeCtrlBase) EVT_PAINT (wxGenericTreeCtrl::OnPaint) + EVT_SIZE (wxGenericTreeCtrl::OnSize) EVT_MOUSE_EVENTS (wxGenericTreeCtrl::OnMouse) EVT_CHAR (wxGenericTreeCtrl::OnChar) EVT_SET_FOCUS (wxGenericTreeCtrl::OnSetFocus) EVT_KILL_FOCUS (wxGenericTreeCtrl::OnKillFocus) + EVT_TREE_ITEM_GETTOOLTIP(wxID_ANY, wxGenericTreeCtrl::OnGetToolTip) END_EVENT_TABLE() -#if !defined(__WXMSW__) || defined(__WIN16__) || defined(__WXUNIVERSAL__) +#if !defined(__WXMSW__) || defined(__WXUNIVERSAL__) /* * wxTreeCtrl has to be a real class or we have problems with * the run-time information. @@ -683,9 +923,12 @@ IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl, wxGenericTreeCtrl) void wxGenericTreeCtrl::Init() { - m_current = m_key_current = m_anchor = m_select_me = (wxGenericTreeItem *) NULL; - m_hasFocus = FALSE; - m_dirty = FALSE; + m_current = + m_key_current = + m_anchor = + m_select_me = NULL; + m_hasFocus = false; + m_dirty = false; m_lineHeight = 10; m_indent = 15; @@ -697,7 +940,7 @@ void wxGenericTreeCtrl::Init() ( wxSYS_COLOUR_HIGHLIGHT ), - wxSOLID + wxBRUSHSTYLE_SOLID ); m_hilightUnfocusedBrush = new wxBrush @@ -706,25 +949,34 @@ void wxGenericTreeCtrl::Init() ( wxSYS_COLOUR_BTNSHADOW ), - wxSOLID + wxBRUSHSTYLE_SOLID ); - m_imageListNormal = m_imageListButtons = - m_imageListState = (wxImageList *) NULL; - m_ownsImageListNormal = m_ownsImageListButtons = - m_ownsImageListState = FALSE; + m_imageListButtons = NULL; + m_ownsImageListButtons = false; m_dragCount = 0; - m_isDragging = FALSE; - m_dropTarget = m_oldSelection = (wxGenericTreeItem *)NULL; + m_isDragging = false; + m_dropTarget = m_oldSelection = NULL; + m_underMouse = NULL; m_textCtrl = NULL; m_renameTimer = NULL; + m_findTimer = NULL; - m_lastOnSame = FALSE; + m_dropEffectAboveItem = false; + + m_dndEffect = NoEffect; + m_dndEffectItem = NULL; + m_lastOnSame = false; + +#if defined( __WXMAC__ ) + m_normalFont.CreateSystemFont(wxOSX_SYSTEM_FONT_VIEWS); +#else m_normalFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ); +#endif m_boldFont = wxFont(m_normalFont.GetPointSize(), m_normalFont.GetFamily(), m_normalFont.GetStyle(), @@ -739,21 +991,30 @@ bool wxGenericTreeCtrl::Create(wxWindow *parent, const wxPoint& pos, const wxSize& size, long style, - const wxValidator &validator, + const wxValidator& validator, const wxString& name ) { #ifdef __WXMAC__ - int major,minor; - wxGetOsVersion( &major, &minor ); + int major, minor; + wxGetOsVersion(&major, &minor); - style &= ~wxTR_LINES_AT_ROOT; - style |= wxTR_NO_LINES; if (major < 10) style |= wxTR_ROW_LINES; + + if (style & wxTR_HAS_BUTTONS) + style |= wxTR_NO_LINES; #endif // __WXMAC__ - wxScrolledWindow::Create( parent, id, pos, size, - style|wxHSCROLL|wxVSCROLL, name ); +#ifdef __WXGTK20__ + if (style & wxTR_HAS_BUTTONS) + style |= wxTR_NO_LINES; +#endif + + if ( !wxControl::Create( parent, id, pos, size, + style|wxHSCROLL|wxVSCROLL, + validator, + name ) ) + return false; // If the tree display has no buttons, but does have // connecting lines, we can use a narrower layout. @@ -764,17 +1025,24 @@ bool wxGenericTreeCtrl::Create(wxWindow *parent, m_spacing = 10; } -#if wxUSE_VALIDATORS - SetValidator( validator ); -#endif + wxVisualAttributes attr = GetDefaultAttributes(); + SetOwnForegroundColour( attr.colFg ); + SetOwnBackgroundColour( attr.colBg ); + if (!m_hasFont) + SetOwnFont(attr.font); - SetForegroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) ); - SetBackgroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX) ); + // this is a misnomer: it's called "dotted pen" but uses (default) wxSOLID + // style because we apparently get performance problems when using dotted + // pen for drawing in some ports -- but under MSW it seems to work fine +#ifdef __WXMSW__ + m_dottedPen = wxPen(*wxLIGHT_GREY, 0, wxPENSTYLE_DOT); +#else + m_dottedPen = *wxGREY_PEN; +#endif -// m_dottedPen = wxPen( "grey", 0, wxDOT ); too slow under XFree86 - m_dottedPen = wxPen( wxT("grey"), 0, 0 ); + SetInitialSize(size); - return TRUE; + return true; } wxGenericTreeCtrl::~wxGenericTreeCtrl() @@ -787,10 +1055,6 @@ wxGenericTreeCtrl::~wxGenericTreeCtrl() delete m_renameTimer; delete m_findTimer; - if (m_ownsImageListNormal) - delete m_imageListNormal; - if (m_ownsImageListState) - delete m_imageListState; if (m_ownsImageListButtons) delete m_imageListButtons; } @@ -799,24 +1063,33 @@ wxGenericTreeCtrl::~wxGenericTreeCtrl() // accessors // ----------------------------------------------------------------------------- -size_t wxGenericTreeCtrl::GetCount() const +unsigned int wxGenericTreeCtrl::GetCount() const { - return m_anchor == NULL ? 0u : m_anchor->GetChildrenCount(); + if ( !m_anchor ) + { + // the tree is empty + return 0; + } + + unsigned int count = m_anchor->GetChildrenCount(); + if ( !HasFlag(wxTR_HIDE_ROOT) ) + { + // take the root itself into account + count++; + } + + return count; } void wxGenericTreeCtrl::SetIndent(unsigned int indent) { m_indent = (unsigned short) indent; - m_dirty = TRUE; -} - -void wxGenericTreeCtrl::SetSpacing(unsigned int spacing) -{ - m_spacing = (unsigned short) spacing; - m_dirty = TRUE; + m_dirty = true; } -size_t wxGenericTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool recursively) +size_t +wxGenericTreeCtrl::GetChildrenCount(const wxTreeItemId& item, + bool recursively) const { wxCHECK_MSG( item.IsOk(), 0u, wxT("invalid tree item") ); @@ -825,7 +1098,8 @@ size_t wxGenericTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool recurs void wxGenericTreeCtrl::SetWindowStyle(const long styles) { - if (!HasFlag(wxTR_HIDE_ROOT) && (styles & wxTR_HIDE_ROOT)) + // Do not try to expand the root node if it hasn't been created yet + if (m_anchor && !HasFlag(wxTR_HIDE_ROOT) && (styles & wxTR_HIDE_ROOT)) { // if we will hide the root, make sure children are visible m_anchor->SetHasPlus(); @@ -837,7 +1111,7 @@ void wxGenericTreeCtrl::SetWindowStyle(const long styles) // want to update the inherited styles, but right now // none of the parents has updatable styles m_windowStyle = styles; - m_dirty = TRUE; + m_dirty = true; } // ----------------------------------------------------------------------------- @@ -846,7 +1120,7 @@ void wxGenericTreeCtrl::SetWindowStyle(const long styles) wxString wxGenericTreeCtrl::GetItemText(const wxTreeItemId& item) const { - wxCHECK_MSG( item.IsOk(), wxT(""), wxT("invalid tree item") ); + wxCHECK_MSG( item.IsOk(), wxEmptyString, wxT("invalid tree item") ); return ((wxGenericTreeItem*) item.m_pItem)->GetText(); } @@ -866,6 +1140,14 @@ wxTreeItemData *wxGenericTreeCtrl::GetItemData(const wxTreeItemId& item) const return ((wxGenericTreeItem*) item.m_pItem)->GetData(); } +int wxGenericTreeCtrl::DoGetItemState(const wxTreeItemId& item) const +{ + wxCHECK_MSG( item.IsOk(), wxTREE_ITEMSTATE_NONE, wxT("invalid tree item") ); + + wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem; + return pItem->GetState(); +} + wxColour wxGenericTreeCtrl::GetItemTextColour(const wxTreeItemId& item) const { wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") ); @@ -874,7 +1156,8 @@ wxColour wxGenericTreeCtrl::GetItemTextColour(const wxTreeItemId& item) const return pItem->Attr().GetTextColour(); } -wxColour wxGenericTreeCtrl::GetItemBackgroundColour(const wxTreeItemId& item) const +wxColour +wxGenericTreeCtrl::GetItemBackgroundColour(const wxTreeItemId& item) const { wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") ); @@ -890,14 +1173,14 @@ wxFont wxGenericTreeCtrl::GetItemFont(const wxTreeItemId& item) const return pItem->Attr().GetFont(); } -void wxGenericTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text) +void +wxGenericTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text) { wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); - wxClientDC dc(this); wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem; pItem->SetText(text); - CalculateSize(pItem, dc); + pItem->CalculateSize(this); RefreshLine(pItem); } @@ -909,19 +1192,31 @@ void wxGenericTreeCtrl::SetItemImage(const wxTreeItemId& item, wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem; pItem->SetImage(image, which); - - wxClientDC dc(this); - CalculateSize(pItem, dc); + pItem->CalculateSize(this); RefreshLine(pItem); } -void wxGenericTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data) +void +wxGenericTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data) { wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); + if (data) + data->SetId( item ); + ((wxGenericTreeItem*) item.m_pItem)->SetData(data); } +void wxGenericTreeCtrl::DoSetItemState(const wxTreeItemId& item, int state) +{ + wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); + + wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem; + pItem->SetState(state); + pItem->CalculateSize(this); + RefreshLine(pItem); +} + void wxGenericTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has) { wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); @@ -940,10 +1235,33 @@ void wxGenericTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold) if ( pItem->IsBold() != bold ) { pItem->SetBold(bold); + + // recalculate the item size as bold and non bold fonts have different + // widths + pItem->CalculateSize(this); RefreshLine(pItem); } } +void wxGenericTreeCtrl::SetItemDropHighlight(const wxTreeItemId& item, + bool highlight) +{ + wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); + + wxColour fg, bg; + + if (highlight) + { + bg = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT); + fg = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT); + } + + wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem; + pItem->Attr().SetTextColour(fg); + pItem->Attr().SetBackgroundColour(bg); + RefreshLine(pItem); +} + void wxGenericTreeCtrl::SetItemTextColour(const wxTreeItemId& item, const wxColour& col) { @@ -964,18 +1282,21 @@ void wxGenericTreeCtrl::SetItemBackgroundColour(const wxTreeItemId& item, RefreshLine(pItem); } -void wxGenericTreeCtrl::SetItemFont(const wxTreeItemId& item, const wxFont& font) +void +wxGenericTreeCtrl::SetItemFont(const wxTreeItemId& item, const wxFont& font) { wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem; pItem->Attr().SetFont(font); + pItem->ResetTextSize(); + pItem->CalculateSize(this); RefreshLine(pItem); } bool wxGenericTreeCtrl::SetFont( const wxFont &font ) { - wxScrolledWindow::SetFont(font); + wxTreeCtrlBase::SetFont(font); m_normalFont = font ; m_boldFont = wxFont(m_normalFont.GetPointSize(), @@ -986,7 +1307,10 @@ bool wxGenericTreeCtrl::SetFont( const wxFont &font ) m_normalFont.GetFaceName(), m_normalFont.GetEncoding()); - return TRUE; + if (m_anchor) + m_anchor->RecursiveResetTextSize(); + + return true; } @@ -996,7 +1320,7 @@ bool wxGenericTreeCtrl::SetFont( const wxFont &font ) bool wxGenericTreeCtrl::IsVisible(const wxTreeItemId& item) const { - wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") ); + wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") ); // An item is only visible if it's not a descendant of a collapsed item wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem; @@ -1004,7 +1328,7 @@ bool wxGenericTreeCtrl::IsVisible(const wxTreeItemId& item) const while (parent) { if (!parent->IsExpanded()) - return FALSE; + return false; parent = parent->GetParent(); } @@ -1015,20 +1339,20 @@ bool wxGenericTreeCtrl::IsVisible(const wxTreeItemId& item) const wxRect rect; if (!GetBoundingRect(item, rect)) - return FALSE; + return false; if (rect.GetWidth() == 0 || rect.GetHeight() == 0) - return FALSE; + return false; if (rect.GetBottom() < 0 || rect.GetTop() > clientSize.y) - return FALSE; + return false; if (rect.GetRight() < 0 || rect.GetLeft() > clientSize.x) - return FALSE; + return false; - return TRUE; + return true; } bool wxGenericTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const { - wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") ); + wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") ); // consider that the item does have children if it has the "+" button: it // might not have them (if it had never been expanded yet) but then it @@ -1040,21 +1364,21 @@ bool wxGenericTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const bool wxGenericTreeCtrl::IsExpanded(const wxTreeItemId& item) const { - wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") ); + wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") ); return ((wxGenericTreeItem*) item.m_pItem)->IsExpanded(); } bool wxGenericTreeCtrl::IsSelected(const wxTreeItemId& item) const { - wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") ); + wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") ); return ((wxGenericTreeItem*) item.m_pItem)->IsSelected(); } bool wxGenericTreeCtrl::IsBold(const wxTreeItemId& item) const { - wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") ); + wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") ); return ((wxGenericTreeItem*) item.m_pItem)->IsBold(); } @@ -1084,12 +1408,13 @@ wxTreeItemId wxGenericTreeCtrl::GetNextChild(const wxTreeItemId& item, { wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); - wxArrayGenericTreeItems& children = ((wxGenericTreeItem*) item.m_pItem)->GetChildren(); + wxArrayGenericTreeItems& + children = ((wxGenericTreeItem*) item.m_pItem)->GetChildren(); // it's ok to cast cookie to size_t, we never have indices big enough to // overflow "void *" size_t *pIndex = (size_t *)&cookie; - if ( *pIndex < children.Count() ) + if ( *pIndex < children.GetCount() ) { return children.Item((*pIndex)++); } @@ -1100,42 +1425,13 @@ wxTreeItemId wxGenericTreeCtrl::GetNextChild(const wxTreeItemId& item, } } -#if WXWIN_COMPATIBILITY_2_4 - -wxTreeItemId wxGenericTreeCtrl::GetFirstChild(const wxTreeItemId& item, - long& cookie) const -{ - wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); - - cookie = 0; - return GetNextChild(item, cookie); -} - -wxTreeItemId wxGenericTreeCtrl::GetNextChild(const wxTreeItemId& item, - long& cookie) const -{ - wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); - - wxArrayGenericTreeItems& children = ((wxGenericTreeItem*) item.m_pItem)->GetChildren(); - if ( (size_t)cookie < children.Count() ) - { - return children.Item((size_t)cookie++); - } - else - { - // there are no more of them - return wxTreeItemId(); - } -} - -#endif // WXWIN_COMPATIBILITY_2_4 - wxTreeItemId wxGenericTreeCtrl::GetLastChild(const wxTreeItemId& item) const { wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); - wxArrayGenericTreeItems& children = ((wxGenericTreeItem*) item.m_pItem)->GetChildren(); - return (children.IsEmpty() ? wxTreeItemId() : wxTreeItemId(children.Last())); + wxArrayGenericTreeItems& + children = ((wxGenericTreeItem*) item.m_pItem)->GetChildren(); + return children.IsEmpty() ? wxTreeItemId() : wxTreeItemId(children.Last()); } wxTreeItemId wxGenericTreeCtrl::GetNextSibling(const wxTreeItemId& item) const @@ -1155,7 +1451,8 @@ wxTreeItemId wxGenericTreeCtrl::GetNextSibling(const wxTreeItemId& item) const wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent? size_t n = (size_t)(index + 1); - return n == siblings.Count() ? wxTreeItemId() : wxTreeItemId(siblings[n]); + return n == siblings.GetCount() ? wxTreeItemId() + : wxTreeItemId(siblings[n]); } wxTreeItemId wxGenericTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const @@ -1224,6 +1521,7 @@ wxTreeItemId wxGenericTreeCtrl::GetFirstVisibleItem() const wxTreeItemId wxGenericTreeCtrl::GetNextVisible(const wxTreeItemId& item) const { wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); + wxASSERT_MSG( IsVisible(item), wxT("this item itself should be visible") ); wxTreeItemId id = item; if (id.IsOk()) @@ -1240,16 +1538,43 @@ wxTreeItemId wxGenericTreeCtrl::GetNextVisible(const wxTreeItemId& item) const wxTreeItemId wxGenericTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const { wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); + wxASSERT_MSG( IsVisible(item), wxT("this item itself should be visible") ); + + // find out the starting point + wxTreeItemId prevItem = GetPrevSibling(item); + if ( !prevItem.IsOk() ) + { + prevItem = GetItemParent(item); + } - wxFAIL_MSG(wxT("not implemented")); + // find the first visible item after it + while ( prevItem.IsOk() && !IsVisible(prevItem) ) + { + prevItem = GetNext(prevItem); + if ( !prevItem.IsOk() || prevItem == item ) + { + // there are no visible items before item + return wxTreeItemId(); + } + } - return wxTreeItemId(); + // from there we must be able to navigate until this item + while ( prevItem.IsOk() ) + { + const wxTreeItemId nextItem = GetNextVisible(prevItem); + if ( !nextItem.IsOk() || nextItem == item ) + break; + + prevItem = nextItem; + } + + return prevItem; } // called by wxTextTreeCtrl when it marks itself for deletion void wxGenericTreeCtrl::ResetTextControl() { - m_textCtrl = NULL; + m_textCtrl = NULL; } // find the first item starting with the given prefix after the given item @@ -1289,10 +1614,13 @@ wxTreeItemId wxGenericTreeCtrl::FindItem(const wxTreeItemId& idParent, } // and try all the items (stop when we get to the one we started from) - while ( id != idParent && !GetItemText(id).Lower().StartsWith(prefix) ) + while ( id.IsOk() && id != idParent && + !GetItemText(id).Lower().StartsWith(prefix) ) { id = GetNext(id); } + // If we haven't found the item, id.IsOk() will be false, as per + // documentation } return id; @@ -1303,10 +1631,11 @@ wxTreeItemId wxGenericTreeCtrl::FindItem(const wxTreeItemId& idParent, // ----------------------------------------------------------------------------- wxTreeItemId wxGenericTreeCtrl::DoInsertItem(const wxTreeItemId& parentId, - size_t previous, - const wxString& text, - int image, int selImage, - wxTreeItemData *data) + size_t previous, + const wxString& text, + int image, + int selImage, + wxTreeItemData *data) { wxGenericTreeItem *parent = (wxGenericTreeItem*) parentId.m_pItem; if ( !parent ) @@ -1315,7 +1644,7 @@ wxTreeItemId wxGenericTreeCtrl::DoInsertItem(const wxTreeItemId& parentId, return AddRoot(text, image, selImage, data); } - m_dirty = TRUE; // do this first so stuff below doesn't cause flicker + m_dirty = true; // do this first so stuff below doesn't cause flicker wxGenericTreeItem *item = new wxGenericTreeItem( parent, text, image, selImage, data ); @@ -1325,20 +1654,23 @@ wxTreeItemId wxGenericTreeCtrl::DoInsertItem(const wxTreeItemId& parentId, data->m_pItem = item; } - parent->Insert( item, previous ); + parent->Insert( item, previous == (size_t)-1 ? parent->GetChildren().size() + : previous ); + InvalidateBestSize(); return item; } wxTreeItemId wxGenericTreeCtrl::AddRoot(const wxString& text, - int image, int selImage, - wxTreeItemData *data) + int image, + int selImage, + wxTreeItemData *data) { - wxCHECK_MSG( !m_anchor, wxTreeItemId(), wxT("tree can have only one root") ); + wxCHECK_MSG( !m_anchor, wxTreeItemId(), "tree can have only one root" ); - m_dirty = TRUE; // do this first so stuff below doesn't cause flicker + m_dirty = true; // do this first so stuff below doesn't cause flicker - m_anchor = new wxGenericTreeItem((wxGenericTreeItem *)NULL, text, + m_anchor = new wxGenericTreeItem(NULL, text, image, selImage, data); if ( data != NULL ) { @@ -1357,25 +1689,18 @@ wxTreeItemId wxGenericTreeCtrl::AddRoot(const wxString& text, if (!HasFlag(wxTR_MULTIPLE)) { m_current = m_key_current = m_anchor; - m_current->SetHilight( TRUE ); + m_current->SetHilight( true ); } + InvalidateBestSize(); return m_anchor; } -wxTreeItemId wxGenericTreeCtrl::PrependItem(const wxTreeItemId& parent, - const wxString& text, - int image, int selImage, - wxTreeItemData *data) -{ - return DoInsertItem(parent, 0u, text, image, selImage, data); -} - -wxTreeItemId wxGenericTreeCtrl::InsertItem(const wxTreeItemId& parentId, - const wxTreeItemId& idPrevious, - const wxString& text, - int image, int selImage, - wxTreeItemData *data) +wxTreeItemId wxGenericTreeCtrl::DoInsertAfter(const wxTreeItemId& parentId, + const wxTreeItemId& idPrevious, + const wxString& text, + int image, int selImage, + wxTreeItemData *data) { wxGenericTreeItem *parent = (wxGenericTreeItem*) parentId.m_pItem; if ( !parent ) @@ -1387,69 +1712,83 @@ wxTreeItemId wxGenericTreeCtrl::InsertItem(const wxTreeItemId& parentId, int index = -1; if (idPrevious.IsOk()) { - index = parent->GetChildren().Index((wxGenericTreeItem*) idPrevious.m_pItem); + index = parent->GetChildren().Index( + (wxGenericTreeItem*) idPrevious.m_pItem); wxASSERT_MSG( index != wxNOT_FOUND, - wxT("previous item in wxGenericTreeCtrl::InsertItem() is not a sibling") ); + "previous item in wxGenericTreeCtrl::InsertItem() " + "is not a sibling" ); } return DoInsertItem(parentId, (size_t)++index, text, image, selImage, data); } -wxTreeItemId wxGenericTreeCtrl::InsertItem(const wxTreeItemId& parentId, - size_t before, - const wxString& text, - int image, int selImage, - wxTreeItemData *data) -{ - wxGenericTreeItem *parent = (wxGenericTreeItem*) parentId.m_pItem; - if ( !parent ) - { - // should we give a warning here? - return AddRoot(text, image, selImage, data); - } - return DoInsertItem(parentId, before, text, image, selImage, data); +void wxGenericTreeCtrl::SendDeleteEvent(wxGenericTreeItem *item) +{ + wxTreeEvent event(wxEVT_COMMAND_TREE_DELETE_ITEM, this, item); + GetEventHandler()->ProcessEvent( event ); } -wxTreeItemId wxGenericTreeCtrl::AppendItem(const wxTreeItemId& parentId, - const wxString& text, - int image, int selImage, - wxTreeItemData *data) +// Don't leave edit or selection on a child which is about to disappear +void wxGenericTreeCtrl::ChildrenClosing(wxGenericTreeItem* item) { - wxGenericTreeItem *parent = (wxGenericTreeItem*) parentId.m_pItem; - if ( !parent ) + if ( m_textCtrl && item != m_textCtrl->item() && + IsDescendantOf(item, m_textCtrl->item()) ) { - // should we give a warning here? - return AddRoot(text, image, selImage, data); + m_textCtrl->EndEdit( true ); } - return DoInsertItem( parent, parent->GetChildren().Count(), text, - image, selImage, data); -} + if ( item != m_key_current && IsDescendantOf(item, m_key_current) ) + { + m_key_current = NULL; + } -void wxGenericTreeCtrl::SendDeleteEvent(wxGenericTreeItem *item) -{ - wxTreeEvent event( wxEVT_COMMAND_TREE_DELETE_ITEM, GetId() ); - event.m_item = item; - event.SetEventObject( this ); - ProcessEvent( event ); + if ( IsDescendantOf(item, m_select_me) ) + { + m_select_me = item; + } + + if ( item != m_current && IsDescendantOf(item, m_current) ) + { + m_current->SetHilight( false ); + m_current = NULL; + m_select_me = item; + } } void wxGenericTreeCtrl::DeleteChildren(const wxTreeItemId& itemId) { - m_dirty = TRUE; // do this first so stuff below doesn't cause flicker + m_dirty = true; // do this first so stuff below doesn't cause flicker wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem; + ChildrenClosing(item); item->DeleteChildren(this); + InvalidateBestSize(); } void wxGenericTreeCtrl::Delete(const wxTreeItemId& itemId) { - m_dirty = TRUE; // do this first so stuff below doesn't cause flicker + m_dirty = true; // do this first so stuff below doesn't cause flicker wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem; - wxGenericTreeItem *parent = item->GetParent(); + if (m_textCtrl != NULL && IsDescendantOf(item, m_textCtrl->item())) + { + // can't delete the item being edited, cancel editing it first + m_textCtrl->EndEdit( true ); + } + + wxGenericTreeItem *parent = item->GetParent(); + + // if the selected item will be deleted, select the parent ... + wxGenericTreeItem *to_be_selected = parent; + if (parent) + { + // .. unless there is a next sibling like wxMSW does it + int pos = parent->GetChildren().Index( item ); + if ((int)(parent->GetChildren().GetCount()) > pos+1) + to_be_selected = parent->GetChildren().Item( pos+1 ); + } // don't keep stale pointers around! if ( IsDescendantOf(item, m_key_current) ) @@ -1457,7 +1796,7 @@ void wxGenericTreeCtrl::Delete(const wxTreeItemId& itemId) // 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; } @@ -1466,7 +1805,7 @@ void wxGenericTreeCtrl::Delete(const wxTreeItemId& itemId) // a different item, in idle time. if ( m_select_me && IsDescendantOf(item, m_select_me) ) { - m_select_me = parent; + m_select_me = to_be_selected; } if ( IsDescendantOf(item, m_current) ) @@ -1474,10 +1813,10 @@ void wxGenericTreeCtrl::Delete(const wxTreeItemId& itemId) // 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; + m_select_me = to_be_selected; } // remove the item from the tree @@ -1494,7 +1833,13 @@ void wxGenericTreeCtrl::Delete(const wxTreeItemId& itemId) // and delete all of its children and the item itself now item->DeleteChildren(this); SendDeleteEvent(item); + + if (item == m_select_me) + m_select_me = NULL; + delete item; + + InvalidateBestSize(); } void wxGenericTreeCtrl::DeleteAllItems() @@ -1519,42 +1864,28 @@ void wxGenericTreeCtrl::Expand(const wxTreeItemId& itemId) if ( item->IsExpanded() ) return; - wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_EXPANDING, GetId() ); - event.m_item = item; - event.SetEventObject( this ); + wxTreeEvent event(wxEVT_COMMAND_TREE_ITEM_EXPANDING, this, item); - if ( ProcessEvent( event ) && !event.IsAllowed() ) + if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() ) { // cancelled by program return; } item->Expand(); - CalculatePositions(); - - RefreshSubtree(item); - - event.SetEventType(wxEVT_COMMAND_TREE_ITEM_EXPANDED); - ProcessEvent( event ); -} - -void wxGenericTreeCtrl::ExpandAll(const wxTreeItemId& item) -{ - if ( !HasFlag(wxTR_HIDE_ROOT) || item != GetRootItem()) + if ( !IsFrozen() ) { - Expand(item); - if ( !IsExpanded(item) ) - return; - } + CalculatePositions(); - wxTreeItemIdValue cookie; - wxTreeItemId child = GetFirstChild(item, cookie); - while ( child.IsOk() ) + RefreshSubtree(item); + } + else // frozen { - ExpandAll(child); - - child = GetNextChild(item, cookie); + m_dirty = true; } + + event.SetEventType(wxEVT_COMMAND_TREE_ITEM_EXPANDED); + GetEventHandler()->ProcessEvent( event ); } void wxGenericTreeCtrl::Collapse(const wxTreeItemId& itemId) @@ -1567,20 +1898,19 @@ void wxGenericTreeCtrl::Collapse(const wxTreeItemId& itemId) if ( !item->IsExpanded() ) return; - wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_COLLAPSING, GetId() ); - event.m_item = item; - event.SetEventObject( this ); - if ( ProcessEvent( event ) && !event.IsAllowed() ) + wxTreeEvent event(wxEVT_COMMAND_TREE_ITEM_COLLAPSING, this, item); + if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() ) { // cancelled by program return; } + ChildrenClosing(item); item->Collapse(); #if 0 // TODO why should items be collapsed recursively? wxArrayGenericTreeItems& children = item->GetChildren(); - size_t count = children.Count(); + size_t count = children.GetCount(); for ( size_t n = 0; n < count; n++ ) { Collapse(children[n]); @@ -1592,7 +1922,7 @@ void wxGenericTreeCtrl::Collapse(const wxTreeItemId& itemId) RefreshSubtree(item); event.SetEventType(wxEVT_COMMAND_TREE_ITEM_COLLAPSED); - ProcessEvent( event ); + GetEventHandler()->ProcessEvent( event ); } void wxGenericTreeCtrl::CollapseAndReset(const wxTreeItemId& item) @@ -1615,7 +1945,7 @@ void wxGenericTreeCtrl::Unselect() { if (m_current) { - m_current->SetHilight( FALSE ); + m_current->SetHilight( false ); RefreshLine( m_current ); m_current = NULL; @@ -1627,14 +1957,14 @@ void wxGenericTreeCtrl::UnselectAllChildren(wxGenericTreeItem *item) { if (item->IsSelected()) { - item->SetHilight(FALSE); + item->SetHilight(false); RefreshLine(item); } if (item->HasChildren()) { wxArrayGenericTreeItems& children = item->GetChildren(); - size_t count = children.Count(); + size_t count = children.GetCount(); for ( size_t n = 0; n < count; ++n ) { UnselectAllChildren(children[n]); @@ -1659,7 +1989,10 @@ void wxGenericTreeCtrl::UnselectAll() // Tag all next children, when no more children, // Move to parent (not to tag) // Keep going... if we found last_item, we stop. -bool wxGenericTreeCtrl::TagNextChildren(wxGenericTreeItem *crt_item, wxGenericTreeItem *last_item, bool select) +bool +wxGenericTreeCtrl::TagNextChildren(wxGenericTreeItem *crt_item, + wxGenericTreeItem *last_item, + bool select) { wxGenericTreeItem *parent = crt_item->GetParent(); @@ -1670,54 +2003,51 @@ bool wxGenericTreeCtrl::TagNextChildren(wxGenericTreeItem *crt_item, wxGenericTr int index = children.Index(crt_item); wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent? - size_t count = children.Count(); + size_t count = children.GetCount(); for (size_t n=(size_t)(index+1); nSetHilight(select); RefreshLine(crt_item); if (crt_item==last_item) - return TRUE; + return true; if (crt_item->HasChildren()) { wxArrayGenericTreeItems& children = crt_item->GetChildren(); - size_t count = children.Count(); + size_t count = children.GetCount(); for ( size_t n = 0; n < count; ++n ) { if (TagAllChildrenUntilLast(children[n], last_item, select)) - return TRUE; + return true; } } - return FALSE; + return false; } -void wxGenericTreeCtrl::SelectItemRange(wxGenericTreeItem *item1, wxGenericTreeItem *item2) +void +wxGenericTreeCtrl::SelectItemRange(wxGenericTreeItem *item1, + wxGenericTreeItem *item2) { - // item2 is not necessary after item1 - wxGenericTreeItem *first=NULL, *last=NULL; m_select_me = NULL; + // item2 is not necessary after item1 // choice first' and 'last' between item1 and item2 - if (item1->GetY()GetY()) - { - first=item1; - last=item2; - } - else - { - first=item2; - last=item1; - } + wxGenericTreeItem *first= (item1->GetY()GetY()) ? item1 : item2; + wxGenericTreeItem *last = (item1->GetY()GetY()) ? item2 : item1; bool select = m_current->IsSelected(); @@ -1727,14 +2057,14 @@ void wxGenericTreeCtrl::SelectItemRange(wxGenericTreeItem *item1, wxGenericTreeI TagNextChildren(first,last,select); } -void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId, - bool unselect_others, - bool extended_select) +void wxGenericTreeCtrl::DoSelectItem(const wxTreeItemId& itemId, + bool unselect_others, + bool extended_select) { wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") ); m_select_me = NULL; - + bool is_single=!(GetWindowStyleFlag() & wxTR_MULTIPLE); wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem; @@ -1746,8 +2076,8 @@ void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId, { if (item->IsSelected()) return; // nothing to do - unselect_others = TRUE; - extended_select = FALSE; + unselect_others = true; + extended_select = false; } else if ( unselect_others && item->IsSelected() ) { @@ -1757,10 +2087,8 @@ void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId, return; } - wxTreeEvent event( wxEVT_COMMAND_TREE_SEL_CHANGING, GetId() ); - event.m_item = item; + wxTreeEvent event(wxEVT_COMMAND_TREE_SEL_CHANGING, this, item); event.m_itemOld = m_current; - event.SetEventObject( this ); // TODO : Here we don't send any selection mode yet ! if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() ) @@ -1775,8 +2103,6 @@ void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId, parent = GetItemParent( parent ); } - EnsureVisible( itemId ); - // ctrl press if (unselect_others) { @@ -1789,7 +2115,8 @@ void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId, { if ( !m_current ) { - m_current = m_key_current = (wxGenericTreeItem*) GetRootItem().m_pItem; + m_current = + m_key_current = (wxGenericTreeItem*) GetRootItem().m_pItem; } // don't change the mark (m_current) @@ -1797,7 +2124,7 @@ void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId, } else { - bool select=TRUE; // the default + bool select = true; // the default // Check if we need to toggle hilight (ctrl mode) if (!unselect_others) @@ -1808,10 +2135,38 @@ void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId, RefreshLine( m_current ); } + // This can cause idle processing to select the root + // if no item is selected, so it must be after the + // selection is set + EnsureVisible( itemId ); + event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED); GetEventHandler()->ProcessEvent( event ); } +void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId, bool select) +{ + if ( select ) + { + DoSelectItem(itemId, !HasFlag(wxTR_MULTIPLE)); + } + else // deselect + { + wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem; + wxCHECK_RET( item, wxT("SelectItem(): invalid tree item") ); + + wxTreeEvent event(wxEVT_COMMAND_TREE_SEL_CHANGING, this, item); + if ( GetEventHandler()->ProcessEvent( event ) && !event.IsAllowed() ) + return; + + item->SetHilight(false); + RefreshLine(item); + + event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED); + GetEventHandler()->ProcessEvent( event ); + } +} + void wxGenericTreeCtrl::FillArray(wxGenericTreeItem *item, wxArrayTreeItemIds &array) const { @@ -1837,11 +2192,13 @@ size_t wxGenericTreeCtrl::GetSelections(wxArrayTreeItemIds &array) const } //else: the tree is empty, so no selections - return array.Count(); + return array.GetCount(); } void wxGenericTreeCtrl::EnsureVisible(const wxTreeItemId& item) { + wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); + if (!item.IsOk()) return; wxGenericTreeItem *gitem = (wxGenericTreeItem*) item.m_pItem; @@ -1878,8 +2235,12 @@ void wxGenericTreeCtrl::ScrollTo(const wxTreeItemId &item) // 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(); - + if (m_dirty) +#if defined( __WXMSW__ ) || defined(__WXMAC__) + Update(); +#else + DoDirtyProcessing(); +#endif wxGenericTreeItem *gitem = (wxGenericTreeItem*) item.m_pItem; // now scroll to the item @@ -1904,7 +2265,9 @@ void wxGenericTreeCtrl::ScrollTo(const wxTreeItemId &item) x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels int x_pos = GetScrollPos( wxHORIZONTAL ); // Item should appear at top - SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT, x/PIXELS_PER_UNIT, y/PIXELS_PER_UNIT, x_pos, item_y/PIXELS_PER_UNIT ); + SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT, + x/PIXELS_PER_UNIT, y/PIXELS_PER_UNIT, + x_pos, item_y/PIXELS_PER_UNIT ); } else if (item_y+GetLineHeight(gitem) > start_y+client_h) { @@ -1917,7 +2280,10 @@ void wxGenericTreeCtrl::ScrollTo(const wxTreeItemId &item) item_y += PIXELS_PER_UNIT+2; int x_pos = GetScrollPos( wxHORIZONTAL ); // Item should appear at bottom - 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 ); + 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 ); } } @@ -1927,17 +2293,12 @@ static wxGenericTreeCtrl *s_treeBeingSorted = NULL; static int LINKAGEMODE tree_ctrl_compare_func(wxGenericTreeItem **item1, wxGenericTreeItem **item2) { - wxCHECK_MSG( s_treeBeingSorted, 0, wxT("bug in wxGenericTreeCtrl::SortChildren()") ); + wxCHECK_MSG( s_treeBeingSorted, 0, + "bug in wxGenericTreeCtrl::SortChildren()" ); return s_treeBeingSorted->OnCompareItems(*item1, *item2); } -int wxGenericTreeCtrl::OnCompareItems(const wxTreeItemId& item1, - const wxTreeItemId& item2) -{ - return wxStrcmp(GetItemText(item1), GetItemText(item2)); -} - void wxGenericTreeCtrl::SortChildren(const wxTreeItemId& itemId) { wxCHECK_RET( itemId.IsOk(), wxT("invalid tree item") ); @@ -1948,9 +2309,9 @@ void wxGenericTreeCtrl::SortChildren(const wxTreeItemId& itemId) wxT("wxGenericTreeCtrl::SortChildren is not reentrant") ); wxArrayGenericTreeItems& children = item->GetChildren(); - if ( children.Count() > 1 ) + if ( children.GetCount() > 1 ) { - m_dirty = TRUE; + m_dirty = true; s_treeBeingSorted = this; children.Sort(tree_ctrl_compare_func); @@ -1959,21 +2320,6 @@ void wxGenericTreeCtrl::SortChildren(const wxTreeItemId& itemId) //else: don't make the tree dirty as nothing changed } -wxImageList *wxGenericTreeCtrl::GetImageList() const -{ - return m_imageListNormal; -} - -wxImageList *wxGenericTreeCtrl::GetButtonsImageList() const -{ - return m_imageListButtons; -} - -wxImageList *wxGenericTreeCtrl::GetStateImageList() const -{ - return m_imageListState; -} - void wxGenericTreeCtrl::CalculateLineHeight() { wxClientDC dc(this); @@ -1993,6 +2339,20 @@ void wxGenericTreeCtrl::CalculateLineHeight() } } + if ( m_imageListState ) + { + // Calculate a m_lineHeight value from the state Image sizes. + // May be toggle off. Then wxGenericTreeCtrl will spread when + // necessary (which might look ugly). + int n = m_imageListState->GetImageCount(); + for (int i = 0; i < n ; i++) + { + int width = 0, height = 0; + m_imageListState->GetSize(i, width, height); + if (height > m_lineHeight) m_lineHeight = height; + } + } + if (m_imageListButtons) { // Calculate a m_lineHeight value from the Button image sizes. @@ -2017,8 +2377,12 @@ void wxGenericTreeCtrl::SetImageList(wxImageList *imageList) { if (m_ownsImageListNormal) delete m_imageListNormal; m_imageListNormal = imageList; - m_ownsImageListNormal = FALSE; - m_dirty = TRUE; + m_ownsImageListNormal = false; + m_dirty = true; + + if (m_anchor) + m_anchor->RecursiveResetSize(); + // 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) @@ -2029,34 +2393,35 @@ void wxGenericTreeCtrl::SetStateImageList(wxImageList *imageList) { if (m_ownsImageListState) delete m_imageListState; m_imageListState = imageList; - m_ownsImageListState = FALSE; + m_ownsImageListState = false; + m_dirty = true; + + if (m_anchor) + m_anchor->RecursiveResetSize(); + + // 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::SetButtonsImageList(wxImageList *imageList) { if (m_ownsImageListButtons) delete m_imageListButtons; m_imageListButtons = imageList; - m_ownsImageListButtons = FALSE; - m_dirty = TRUE; - CalculateLineHeight(); -} + m_ownsImageListButtons = false; + m_dirty = true; -void wxGenericTreeCtrl::AssignImageList(wxImageList *imageList) -{ - SetImageList(imageList); - m_ownsImageListNormal = TRUE; -} + if (m_anchor) + m_anchor->RecursiveResetSize(); -void wxGenericTreeCtrl::AssignStateImageList(wxImageList *imageList) -{ - SetStateImageList(imageList); - m_ownsImageListState = TRUE; + CalculateLineHeight(); } void wxGenericTreeCtrl::AssignButtonsImageList(wxImageList *imageList) { SetButtonsImageList(imageList); - m_ownsImageListButtons = TRUE; + m_ownsImageListButtons = true; } // ----------------------------------------------------------------------------- @@ -2073,7 +2438,9 @@ void wxGenericTreeCtrl::AdjustMyScrollbars() x += PIXELS_PER_UNIT+2; // one more scrollbar unit + 2 pixels int x_pos = GetScrollPos( wxHORIZONTAL ); int y_pos = GetScrollPos( wxVERTICAL ); - SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT, x/PIXELS_PER_UNIT, y/PIXELS_PER_UNIT, x_pos, y_pos ); + SetScrollbars( PIXELS_PER_UNIT, PIXELS_PER_UNIT, + x/PIXELS_PER_UNIT, y/PIXELS_PER_UNIT, + x_pos, y_pos ); } else { @@ -2091,16 +2458,10 @@ int wxGenericTreeCtrl::GetLineHeight(wxGenericTreeItem *item) const void wxGenericTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc) { - // TODO implement "state" icon on items + item->SetFont(this, dc); + item->CalculateSize(this, dc); - wxTreeItemAttr *attr = item->GetAttributes(); - if ( attr && attr->HasFont() ) - dc.SetFont(attr->GetFont()); - else if (item->IsBold()) - dc.SetFont(m_boldFont); - - long text_w = 0, text_h = 0; - dc.GetTextExtent( item->GetText(), &text_w, &text_h ); + wxCoord text_h = item->GetTextHeight(); int image_h = 0, image_w = 0; int image = item->GetCurrentImage(); @@ -2108,8 +2469,8 @@ void wxGenericTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc) { if ( m_imageListNormal ) { - m_imageListNormal->GetSize( image, image_w, image_h ); - image_w += 4; + m_imageListNormal->GetSize(image, image_w, image_h); + image_w += MARGIN_BETWEEN_IMAGE_AND_TEXT; } else { @@ -2117,84 +2478,208 @@ void wxGenericTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc) } } - int total_h = GetLineHeight(item); - - if ( item->IsSelected() ) + int state_h = 0, state_w = 0; + int state = item->GetState(); + if ( state != wxTREE_ITEMSTATE_NONE ) { -// under mac selections are only a rectangle in case they don't have the focus -#ifdef __WXMAC__ - if ( !m_hasFocus ) + if ( m_imageListState ) { - dc.SetBrush( *wxTRANSPARENT_BRUSH ) ; - dc.SetPen( wxPen( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) , 1 , wxSOLID ) ) ; + m_imageListState->GetSize(state, state_w, state_h); + if ( image_w != 0 ) + state_w += MARGIN_BETWEEN_STATE_AND_IMAGE; + else + state_w += MARGIN_BETWEEN_IMAGE_AND_TEXT; } else { - dc.SetBrush( *m_hilightBrush ) ; + state = wxTREE_ITEMSTATE_NONE; } -#else + } + + int total_h = GetLineHeight(item); + bool drawItemBackground = false, + hasBgColour = false; + + if ( item->IsSelected() ) + { dc.SetBrush(*(m_hasFocus ? m_hilightBrush : m_hilightUnfocusedBrush)); -#endif + drawItemBackground = true; } else { wxColour colBg; + wxTreeItemAttr * const attr = item->GetAttributes(); if ( attr && attr->HasBackgroundColour() ) + { + drawItemBackground = + hasBgColour = true; colBg = attr->GetBackgroundColour(); + } else - colBg = m_backgroundColour; - dc.SetBrush(wxBrush(colBg, wxSOLID)); + { + colBg = GetBackgroundColour(); + } + dc.SetBrush(wxBrush(colBg, wxBRUSHSTYLE_SOLID)); } int offset = HasFlag(wxTR_ROW_LINES) ? 1 : 0; if ( HasFlag(wxTR_FULL_ROW_HIGHLIGHT) ) { - int x, y, w, h; + int x, w, h; + x=0; + GetVirtualSize(&w, &h); + wxRect rect( x, item->GetY()+offset, w, total_h-offset); + if (!item->IsSelected()) + { + dc.DrawRectangle(rect); + } + else + { + int flags = wxCONTROL_SELECTED; + if (m_hasFocus +#if defined( __WXMAC__ ) && !defined(__WXUNIVERSAL__) && wxOSX_USE_CARBON // TODO CS + && IsControlActive( (ControlRef)GetHandle() ) +#endif + ) + flags |= wxCONTROL_FOCUSED; + if ((item == m_current) && (m_hasFocus)) + flags |= wxCONTROL_CURRENT; - DoGetPosition(&x, &y); - DoGetSize(&w, &h); - dc.DrawRectangle(x, item->GetY()+offset, w, total_h-offset); + wxRendererNative::Get(). + DrawItemSelectionRect(this, dc, rect, flags); + } } - else + else // no full row highlight { - if ( item->IsSelected() && image != NO_IMAGE ) + if ( item->IsSelected() && + (state != wxTREE_ITEMSTATE_NONE || image != NO_IMAGE) ) { - // If it's selected, and there's an image, then we should - // take care to leave the area under the image painted in the - // background colour. - dc.DrawRectangle( item->GetX() + image_w - 2, item->GetY()+offset, - item->GetWidth() - image_w + 2, total_h-offset ); + // If it's selected, and there's an state image or normal image, + // then we should take care to leave the area under the image + // painted in the background colour. + wxRect rect( item->GetX() + state_w + image_w - 2, + item->GetY() + offset, + item->GetWidth() - state_w - image_w + 2, + total_h - offset ); +#if !defined(__WXGTK20__) && !defined(__WXMAC__) + dc.DrawRectangle( rect ); +#else + rect.x -= 1; + rect.width += 2; + + int flags = wxCONTROL_SELECTED; + if (m_hasFocus) + flags |= wxCONTROL_FOCUSED; + if ((item == m_current) && (m_hasFocus)) + flags |= wxCONTROL_CURRENT; + wxRendererNative::Get(). + DrawItemSelectionRect(this, dc, rect, flags); +#endif } - else + // On GTK+ 2, drawing a 'normal' background is wrong for themes that + // don't allow backgrounds to be customized. Not drawing the background, + // except for custom item backgrounds, works for both kinds of theme. + else if (drawItemBackground) { - dc.DrawRectangle( item->GetX()-2, item->GetY()+offset, - item->GetWidth()+2, total_h-offset ); + wxRect rect( item->GetX() + state_w + image_w - 2, + item->GetY() + offset, + item->GetWidth() - state_w - image_w + 2, + total_h - offset ); + if ( hasBgColour ) + { + dc.DrawRectangle( rect ); + } + else // no specific background colour + { + rect.x -= 1; + rect.width += 2; + + int flags = wxCONTROL_SELECTED; + if (m_hasFocus) + flags |= wxCONTROL_FOCUSED; + if ((item == m_current) && (m_hasFocus)) + flags |= wxCONTROL_CURRENT; + wxRendererNative::Get(). + DrawItemSelectionRect(this, dc, rect, flags); + } } } + if ( state != wxTREE_ITEMSTATE_NONE ) + { + dc.SetClippingRegion( item->GetX(), item->GetY(), state_w, total_h ); + m_imageListState->Draw( state, dc, + item->GetX(), + item->GetY() + + (total_h > state_h ? (total_h-state_h)/2 + : 0), + wxIMAGELIST_DRAW_TRANSPARENT ); + dc.DestroyClippingRegion(); + } + if ( image != NO_IMAGE ) { - dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, total_h ); + dc.SetClippingRegion(item->GetX() + state_w, item->GetY(), + image_w, total_h); m_imageListNormal->Draw( image, dc, - item->GetX(), - item->GetY() +((total_h > image_h)?((total_h-image_h)/2):0), + item->GetX() + state_w, + item->GetY() + + (total_h > image_h ? (total_h-image_h)/2 + : 0), wxIMAGELIST_DRAW_TRANSPARENT ); dc.DestroyClippingRegion(); } - dc.SetBackgroundMode(wxTRANSPARENT); + dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT); int extraH = (total_h > text_h) ? (total_h - text_h)/2 : 0; dc.DrawText( item->GetText(), - (wxCoord)(image_w + item->GetX()), + (wxCoord)(state_w + image_w + item->GetX()), (wxCoord)(item->GetY() + extraH)); // restore normal font dc.SetFont( m_normalFont ); + + if (item == m_dndEffectItem) + { + dc.SetPen( *wxBLACK_PEN ); + // DnD visual effects + switch (m_dndEffect) + { + case BorderEffect: + { + dc.SetBrush(*wxTRANSPARENT_BRUSH); + int w = item->GetWidth() + 2; + int h = total_h + 2; + dc.DrawRectangle( item->GetX() - 1, item->GetY() - 1, w, h); + break; + } + case AboveEffect: + { + int x = item->GetX(), + y = item->GetY(); + dc.DrawLine( x, y, x + item->GetWidth(), y); + break; + } + case BelowEffect: + { + int x = item->GetX(), + y = item->GetY(); + y += total_h - 1; + dc.DrawLine( x, y, x + item->GetWidth(), y); + break; + } + case NoEffect: + break; + } + } } -// Now y stands for the top of the item, whereas it used to stand for middle ! -void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y ) +void +wxGenericTreeCtrl::PaintLevel(wxGenericTreeItem *item, + wxDC &dc, + int level, + int &y) { int x = level*m_indent; if (!HasFlag(wxTR_HIDE_ROOT)) @@ -2206,7 +2691,7 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level // always expand hidden root int origY = y; wxArrayGenericTreeItems& children = item->GetChildren(); - int count = children.Count(); + int count = children.GetCount(); if (count > 0) { int n = 0, oldY; @@ -2215,7 +2700,8 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level PaintLevel(children[n], dc, 1, y); } while (++n < count); - if (!HasFlag(wxTR_NO_LINES) && HasFlag(wxTR_LINES_AT_ROOT) && count > 0) + if ( !HasFlag(wxTR_NO_LINES) && HasFlag(wxTR_LINES_AT_ROOT) + && count > 0 ) { // draw line down to last child origY += GetLineHeight(children[0])>>1; @@ -2239,7 +2725,7 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level if (IsExposed(exposed_x, exposed_y, 10000, h)) // 10000 = very much { - wxPen *pen = + const wxPen *pen = #ifndef __WXMAC__ // don't draw rect outline if we already have the // background color under Mac @@ -2248,9 +2734,21 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level wxTRANSPARENT_PEN; wxColour colText; - if ( item->IsSelected() ) + if ( item->IsSelected() +#if defined( __WXMAC__ ) && !defined(__WXUNIVERSAL__) && wxOSX_USE_CARBON // TODO CS + // On wxMac, if the tree doesn't have the focus we draw an empty + // rectangle, so we want to make sure that the text is visible + // against the normal background, not the highlightbackground, so + // don't use the highlight text colour unless we have the focus. + && m_hasFocus && IsControlActive( (ControlRef)GetHandle() ) +#endif + ) { +#ifdef __WXMAC__ + colText = *wxWHITE; +#else colText = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT); +#endif } else { @@ -2317,8 +2815,14 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level } else // no custom buttons { - static const int wImage = 10; - static const int hImage = 12; + static const int wImage = 9; + static const int hImage = 9; + + int flag = 0; + if (item->IsExpanded()) + flag |= wxCONTROL_EXPANDED; + if (item == m_underMouse) + flag |= wxCONTROL_CURRENT; wxRendererNative::Get().DrawTreeItemButton ( @@ -2327,9 +2831,7 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level wxRect(x - wImage/2, y_mid - hImage/2, wImage, hImage), - item->IsExpanded() - ? wxCONTROL_EXPANDED - : 0 + flag ); } } @@ -2338,7 +2840,7 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level if (item->IsExpanded()) { wxArrayGenericTreeItems& children = item->GetChildren(); - int count = children.Count(); + int count = children.GetCount(); if (count > 0) { int n = 0, oldY; @@ -2354,8 +2856,9 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level oldY += GetLineHeight(children[n-1])>>1; if (HasButtons()) y_mid += 5; - // Only draw the portion of the line that is visible, in case it is huge - wxCoord xOrigin=0, yOrigin=0, width, height; + // 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); @@ -2366,8 +2869,8 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level 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 + // 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); } @@ -2388,10 +2891,10 @@ void wxGenericTreeCtrl::DrawDropEffect(wxGenericTreeItem *item) { // draw a line under the drop target because the item will be // dropped there - DrawLine(item, TRUE /* below */); + DrawLine(item, !m_dropEffectAboveItem ); } - SetCursor(wxCURSOR_BULLSEYE); + SetCursor(*wxSTANDARD_CURSOR); } else { @@ -2402,45 +2905,65 @@ void wxGenericTreeCtrl::DrawDropEffect(wxGenericTreeItem *item) void wxGenericTreeCtrl::DrawBorder(const wxTreeItemId &item) { - wxCHECK_RET( item.IsOk(), _T("invalid item in wxGenericTreeCtrl::DrawLine") ); + wxCHECK_RET( item.IsOk(), "invalid item in wxGenericTreeCtrl::DrawLine" ); wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem; - wxClientDC dc(this); - PrepareDC( dc ); - dc.SetLogicalFunction(wxINVERT); - dc.SetBrush(*wxTRANSPARENT_BRUSH); - - int w = i->GetWidth() + 2; - int h = GetLineHeight(i) + 2; + if (m_dndEffect == NoEffect) + { + m_dndEffect = BorderEffect; + m_dndEffectItem = i; + } + else + { + m_dndEffect = NoEffect; + m_dndEffectItem = NULL; + } - dc.DrawRectangle( i->GetX() - 1, i->GetY() - 1, w, h); + wxRect rect( i->GetX()-1, i->GetY()-1, i->GetWidth()+2, GetLineHeight(i)+2 ); + CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y ); + RefreshRect( rect ); } void wxGenericTreeCtrl::DrawLine(const wxTreeItemId &item, bool below) { - wxCHECK_RET( item.IsOk(), _T("invalid item in wxGenericTreeCtrl::DrawLine") ); + wxCHECK_RET( item.IsOk(), "invalid item in wxGenericTreeCtrl::DrawLine" ); wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem; - wxClientDC dc(this); - PrepareDC( dc ); - dc.SetLogicalFunction(wxINVERT); - - int x = i->GetX(), - y = i->GetY(); - if ( below ) + if (m_dndEffect == NoEffect) { - y += GetLineHeight(i) - 1; + if (below) + m_dndEffect = BelowEffect; + else + m_dndEffect = AboveEffect; + m_dndEffectItem = i; + } + else + { + m_dndEffect = NoEffect; + m_dndEffectItem = NULL; } - dc.DrawLine( x, y, x + i->GetWidth(), y); + wxRect rect( i->GetX()-1, i->GetY()-1, i->GetWidth()+2, GetLineHeight(i)+2 ); + CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y ); + RefreshRect( rect ); } // ----------------------------------------------------------------------------- -// wxWindows callbacks +// wxWidgets callbacks // ----------------------------------------------------------------------------- +void wxGenericTreeCtrl::OnSize( wxSizeEvent &event ) +{ +#ifdef __WXGTK__ + if (HasFlag( wxTR_FULL_ROW_HIGHLIGHT) && m_current) + RefreshLine( m_current ); +#endif + + event.Skip(true); +} + void wxGenericTreeCtrl::OnPaint( wxPaintEvent &WXUNUSED(event) ) { wxPaintDC dc(this); @@ -2462,7 +2985,7 @@ void wxGenericTreeCtrl::OnPaint( wxPaintEvent &WXUNUSED(event) ) void wxGenericTreeCtrl::OnSetFocus( wxFocusEvent &event ) { - m_hasFocus = TRUE; + m_hasFocus = true; RefreshSelected(); @@ -2471,7 +2994,7 @@ void wxGenericTreeCtrl::OnSetFocus( wxFocusEvent &event ) void wxGenericTreeCtrl::OnKillFocus( wxFocusEvent &event ) { - m_hasFocus = FALSE; + m_hasFocus = false; RefreshSelected(); @@ -2480,9 +3003,8 @@ void wxGenericTreeCtrl::OnKillFocus( wxFocusEvent &event ) void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) { - wxTreeEvent te( wxEVT_COMMAND_TREE_KEY_DOWN, GetId() ); + wxTreeEvent te( wxEVT_COMMAND_TREE_KEY_DOWN, this); te.m_evtKey = event; - te.SetEventObject( this ); if ( GetEventHandler()->ProcessEvent( te ) ) { // intercepted by the user code @@ -2499,9 +3021,17 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) bool is_multiple, extended_select, unselect_others; EventFlagsToSelType(GetWindowStyleFlag(), event.ShiftDown(), - event.ControlDown(), + event.CmdDown(), is_multiple, extended_select, unselect_others); + if (GetLayoutDirection() == wxLayout_RightToLeft) + { + if (event.GetKeyCode() == WXK_RIGHT) + event.m_keyCode = WXK_LEFT; + else if (event.GetKeyCode() == WXK_LEFT) + event.m_keyCode = WXK_RIGHT; + } + // + : Expand // - : Collaspe // * : Expand all/Collapse all @@ -2529,7 +3059,7 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) if ( !IsExpanded(m_current) ) { // expand all - ExpandAll(m_current); + ExpandAllChildren(m_current); break; } //else: fall through to Collapse() it @@ -2542,14 +3072,30 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) } break; + case WXK_MENU: + { + // Use the item's bounding rectangle to determine position for + // the event + wxRect ItemRect; + GetBoundingRect(m_current, ItemRect, true); + + wxTreeEvent + eventMenu(wxEVT_COMMAND_TREE_ITEM_MENU, this, m_current); + // Use the left edge, vertical middle + eventMenu.m_pointDrag = wxPoint(ItemRect.GetX(), + ItemRect.GetY() + + ItemRect.GetHeight() / 2); + GetEventHandler()->ProcessEvent( eventMenu ); + } + break; + case ' ': case WXK_RETURN: if ( !event.HasModifiers() ) { - wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() ); - event.m_item = m_current; - event.SetEventObject( this ); - GetEventHandler()->ProcessEvent( event ); + wxTreeEvent + eventAct(wxEVT_COMMAND_TREE_ITEM_ACTIVATED, this, m_current); + GetEventHandler()->ProcessEvent( eventAct ); } // in any case, also generate the normal key event for this key, @@ -2576,12 +3122,15 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) { wxTreeItemIdValue cookie; wxTreeItemId current = m_key_current; - // TODO: Huh? If we get here, we'd better be the first child of our parent. How else could it be? + // TODO: Huh? If we get here, we'd better be the first + // child of our parent. How else could it be? if (current == GetFirstChild( prev, cookie )) { // otherwise we return to where we came from - SelectItem( prev, unselect_others, extended_select ); - m_key_current= (wxGenericTreeItem*) prev.m_pItem; + DoSelectItem(prev, + unselect_others, + extended_select); + m_key_current = (wxGenericTreeItem*) prev.m_pItem; break; } } @@ -2597,7 +3146,7 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) } } - SelectItem( prev, unselect_others, extended_select ); + DoSelectItem( prev, unselect_others, extended_select ); m_key_current=(wxGenericTreeItem*) prev.m_pItem; } } @@ -2614,7 +3163,7 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) } if (prev) { - SelectItem( prev, unselect_others, extended_select ); + DoSelectItem( prev, unselect_others, extended_select ); } } break; @@ -2622,7 +3171,11 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) case WXK_RIGHT: // this works the same as the down arrow except that we // also expand the item if it wasn't expanded yet - Expand(m_current); + if (m_current != GetRootItem().m_pItem || !HasFlag(wxTR_HIDE_ROOT)) + Expand(m_current); + //else: don't try to expand hidden root item (which can be the + // current one when the tree is empty) + // fall through case WXK_DOWN: @@ -2631,7 +3184,10 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) { wxTreeItemIdValue cookie; wxTreeItemId child = GetFirstChild( m_key_current, cookie ); - SelectItem( child, unselect_others, extended_select ); + if ( !child ) + break; + + DoSelectItem( child, unselect_others, extended_select ); m_key_current=(wxGenericTreeItem*) child.m_pItem; } else @@ -2648,7 +3204,7 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) } if (next) { - SelectItem( next, unselect_others, extended_select ); + DoSelectItem( next, unselect_others, extended_select ); m_key_current=(wxGenericTreeItem*) next.m_pItem; } } @@ -2666,7 +3222,7 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) // it may happen if the item was expanded but then all of // its children have been deleted - so IsExpanded() returned - // TRUE, but GetLastChild() returned invalid item + // true, but GetLastChild() returned invalid item if ( !lastChild ) break; @@ -2675,7 +3231,7 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) if ( last.IsOk() ) { - SelectItem( last, unselect_others, extended_select ); + DoSelectItem( last, unselect_others, extended_select ); } } break; @@ -2695,7 +3251,7 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) break; } - SelectItem( prev, unselect_others, extended_select ); + DoSelectItem( prev, unselect_others, extended_select ); } break; @@ -2707,9 +3263,9 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) (keyCode >= 'A' && keyCode <= 'Z' ))) { // find the next item starting with the given prefix - char ch = (char)keyCode; + wxChar ch = (wxChar)keyCode; - wxTreeItemId id = FindItem(m_current, m_findPrefix + (wxChar)ch); + wxTreeItemId id = FindItem(m_current, m_findPrefix + ch); if ( !id.IsOk() ) { // no such item @@ -2737,11 +3293,9 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) } } -wxTreeItemId wxGenericTreeCtrl::HitTest(const wxPoint& point, int& flags) +wxTreeItemId +wxGenericTreeCtrl::DoTreeHitTest(const wxPoint& point, int& flags) const { - // JACS: removed wxYieldIfNeeded() because it can cause the window - // to be deleted from under us if a close window event is pending - int w, h; GetSize(&w, &h); flags=0; @@ -2770,48 +3324,83 @@ 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 + bool textOnly) const { - wxCHECK_MSG( item.IsOk(), FALSE, _T("invalid item in wxGenericTreeCtrl::GetBoundingRect") ); + wxCHECK_MSG( item.IsOk(), false, + "invalid item in wxGenericTreeCtrl::GetBoundingRect" ); wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem; - int startX, startY; - GetViewStart(& startX, & startY); + if ( textOnly ) + { + int image_h = 0, image_w = 0; + int image = ((wxGenericTreeItem*) item.m_pItem)->GetCurrentImage(); + if ( image != NO_IMAGE && m_imageListNormal ) + { + m_imageListNormal->GetSize( image, image_w, image_h ); + image_w += MARGIN_BETWEEN_IMAGE_AND_TEXT; + } + + int state_h = 0, state_w = 0; + int state = ((wxGenericTreeItem*) item.m_pItem)->GetState(); + if ( state != wxTREE_ITEMSTATE_NONE && m_imageListState ) + { + m_imageListState->GetSize( state, state_w, state_h ); + if ( image_w != 0 ) + state_w += MARGIN_BETWEEN_STATE_AND_IMAGE; + else + state_w += MARGIN_BETWEEN_IMAGE_AND_TEXT; + } + + rect.x = i->GetX() + state_w + image_w; + rect.width = i->GetWidth() - state_w - image_w; + + } + else // the entire line + { + rect.x = 0; + rect.width = GetClientSize().x; + } - rect.x = i->GetX() - startX*PIXELS_PER_UNIT; - rect.y = i->GetY() - startY*PIXELS_PER_UNIT; - rect.width = i->GetWidth(); - //rect.height = i->GetHeight(); + rect.y = i->GetY(); rect.height = GetLineHeight(i); - return TRUE; + // we have to return the logical coordinates, not physical ones + rect.SetTopLeft(CalcScrolledPosition(rect.GetTopLeft())); + + return true; } -void wxGenericTreeCtrl::Edit( const wxTreeItemId& item ) +wxTextCtrl *wxGenericTreeCtrl::EditLabel(const wxTreeItemId& item, + wxClassInfo * WXUNUSED(textCtrlClass)) { - wxCHECK_RET( item.IsOk(), _T("can't edit an invalid item") ); + wxCHECK_MSG( item.IsOk(), NULL, _T("can't edit an invalid item") ); wxGenericTreeItem *itemEdit = (wxGenericTreeItem *)item.m_pItem; - wxTreeEvent te( wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, GetId() ); - te.m_item = itemEdit; - te.SetEventObject( this ); + wxTreeEvent te(wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, this, itemEdit); if ( GetEventHandler()->ProcessEvent( te ) && !te.IsAllowed() ) { // vetoed by user - return; + return NULL; } // 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(); +#if defined( __WXMSW__ ) || defined(__WXMAC__) + Update(); +#else + DoDirtyProcessing(); +#endif + // TODO: use textCtrlClass here to create the control of correct class m_textCtrl = new wxTreeTextCtrl(this, itemEdit); m_textCtrl->SetFocus(); + + return m_textCtrl; } // returns a pointer to the text edit control if the item is being @@ -2822,14 +3411,20 @@ wxTextCtrl* wxGenericTreeCtrl::GetEditControl() const return m_textCtrl; } +void wxGenericTreeCtrl::EndEditLabel(const wxTreeItemId& WXUNUSED(item), + bool discardChanges) +{ + wxCHECK_RET( m_textCtrl, _T("not editing label") ); + + m_textCtrl->EndEdit(discardChanges); +} + bool wxGenericTreeCtrl::OnRenameAccept(wxGenericTreeItem *item, const wxString& value) { - wxTreeEvent le( wxEVT_COMMAND_TREE_END_LABEL_EDIT, GetId() ); - le.m_item = item; - le.SetEventObject( this ); + wxTreeEvent le(wxEVT_COMMAND_TREE_END_LABEL_EDIT, this, item); le.m_label = value; - le.m_editCancelled = FALSE; + le.m_editCancelled = false; return !GetEventHandler()->ProcessEvent( le ) || le.IsAllowed(); } @@ -2837,32 +3432,87 @@ bool wxGenericTreeCtrl::OnRenameAccept(wxGenericTreeItem *item, 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 = item; - le.SetEventObject( this ); + wxTreeEvent le(wxEVT_COMMAND_TREE_END_LABEL_EDIT, this, item); le.m_label = wxEmptyString; - le.m_editCancelled = TRUE; + le.m_editCancelled = true; GetEventHandler()->ProcessEvent( le ); } - - - void wxGenericTreeCtrl::OnRenameTimer() { - Edit( m_current ); + EditLabel( m_current ); } void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) { - if ( !m_anchor ) return; + if ( !m_anchor )return; + + wxPoint pt = CalcUnscrolledPosition(event.GetPosition()); + + // Is the mouse over a tree item button? + int flags = 0; + wxGenericTreeItem *thisItem = m_anchor->HitTest(pt, this, flags, 0); + wxGenericTreeItem *underMouse = thisItem; +#if wxUSE_TOOLTIPS + bool underMouseChanged = (underMouse != m_underMouse) ; +#endif // wxUSE_TOOLTIPS + + if ((underMouse) && + (flags & wxTREE_HITTEST_ONITEMBUTTON) && + (!event.LeftIsDown()) && + (!m_isDragging) && + (!m_renameTimer || !m_renameTimer->IsRunning())) + { + } + else + { + underMouse = NULL; + } + + if (underMouse != m_underMouse) + { + if (m_underMouse) + { + // unhighlight old item + wxGenericTreeItem *tmp = m_underMouse; + m_underMouse = NULL; + RefreshLine( tmp ); + } + + m_underMouse = underMouse; + if (m_underMouse) + RefreshLine( m_underMouse ); + } + +#if wxUSE_TOOLTIPS + // Determines what item we are hovering over and need a tooltip for + wxTreeItemId hoverItem = thisItem; + + // We do not want a tooltip if we are dragging, or if the rename timer is + // running + if ( underMouseChanged && + hoverItem.IsOk() && + !m_isDragging && + (!m_renameTimer || !m_renameTimer->IsRunning()) ) + { + // Ask the tree control what tooltip (if any) should be shown + wxTreeEvent + hevent(wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP, this, hoverItem); + + if ( GetEventHandler()->ProcessEvent(hevent) && hevent.IsAllowed() ) + { + SetToolTip(hevent.m_label); + } + } +#endif - // we process left mouse up event (enables in-place edit), right down + // we process left mouse up event (enables in-place edit), middle/right down // (pass to the user code), left dbl click (activate item) and // dragging/moving events for items drag-and-drop if ( !(event.LeftDown() || event.LeftUp() || + event.MiddleDown() || event.RightDown() || event.LeftDClick() || event.Dragging() || @@ -2873,9 +3523,8 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) return; } - wxPoint pt = CalcUnscrolledPosition(event.GetPosition()); - int flags = 0; + flags = 0; wxGenericTreeItem *item = m_anchor->HitTest(pt, this, flags, 0); if ( event.Dragging() && !m_isDragging ) @@ -2895,9 +3544,8 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) ? wxEVT_COMMAND_TREE_BEGIN_RDRAG : wxEVT_COMMAND_TREE_BEGIN_DRAG; - wxTreeEvent nevent( command, GetId() ); - nevent.m_item = m_current; - nevent.SetEventObject(this); + wxTreeEvent nevent(command, this, m_current); + nevent.SetPoint(CalcScrolledPosition(pt)); // by default the dragging is not supported, the user code must // explicitly allow the event for it to take place @@ -2906,7 +3554,7 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) if ( GetEventHandler()->ProcessEvent(nevent) && nevent.IsAllowed() ) { // we're going to drag this item - m_isDragging = TRUE; + m_isDragging = true; // remember the old cursor because we will change it while // dragging @@ -2919,7 +3567,7 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) if ( m_oldSelection ) { - m_oldSelection->SetHilight(FALSE); + m_oldSelection->SetHilight(false); RefreshLine(m_oldSelection); } } @@ -2927,7 +3575,7 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) CaptureMouse(); } } - else if ( event.Moving() ) + else if ( event.Dragging() ) { if ( item != m_dropTarget ) { @@ -2939,41 +3587,63 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) // highlight the current drop target if any DrawDropEffect(m_dropTarget); +#if defined(__WXMSW__) || defined(__WXMAC__) || defined(__WXGTK20__) + Update(); +#else + // TODO: remove this call or use wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI) + // instead (needs to be tested!) wxYieldIfNeeded(); +#endif } } else if ( (event.LeftUp() || event.RightUp()) && m_isDragging ) { + ReleaseMouse(); + // erase the highlighting DrawDropEffect(m_dropTarget); if ( m_oldSelection ) { - m_oldSelection->SetHilight(TRUE); + m_oldSelection->SetHilight(true); RefreshLine(m_oldSelection); - m_oldSelection = (wxGenericTreeItem *)NULL; + m_oldSelection = NULL; } // generate the drag end event - wxTreeEvent event(wxEVT_COMMAND_TREE_END_DRAG, GetId()); - - event.m_item = item; - event.m_pointDrag = pt; - event.SetEventObject(this); + wxTreeEvent eventEndDrag(wxEVT_COMMAND_TREE_END_DRAG, this, item); - (void)GetEventHandler()->ProcessEvent(event); + eventEndDrag.m_pointDrag = CalcScrolledPosition(pt); - m_isDragging = FALSE; - m_dropTarget = (wxGenericTreeItem *)NULL; + (void)GetEventHandler()->ProcessEvent(eventEndDrag); - ReleaseMouse(); + m_isDragging = false; + m_dropTarget = NULL; SetCursor(m_oldCursor); +#if defined( __WXMSW__ ) || defined(__WXMAC__) || defined(__WXGTK20__) + Update(); +#else + // TODO: remove this call or use wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI) + // instead (needs to be tested!) wxYieldIfNeeded(); +#endif } else { + // If we got to this point, we are not dragging or moving the mouse. + // Because the code in carbon/toplevel.cpp will only set focus to the + // tree if we skip for EVT_LEFT_DOWN, we MUST skip this event here for + // focus to work. + // We skip even if we didn't hit an item because we still should + // restore focus to the tree control even if we didn't exactly hit an + // item. + if ( event.LeftDown() ) + { + event.Skip(); + } + // here we process only the messages which happen on tree items m_dragCount = 0; @@ -2982,26 +3652,53 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) if ( event.RightDown() ) { - wxTreeEvent nevent(wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK, GetId()); - nevent.m_item = item; + // If the item is already selected, do not update the selection. + // Multi-selections should not be cleared if a selected item is + // clicked. + if (!IsSelected(item)) + { + DoSelectItem(item, true, false); + } + + wxTreeEvent + nevent(wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK, this, item); + nevent.m_pointDrag = CalcScrolledPosition(pt); + event.Skip(!GetEventHandler()->ProcessEvent(nevent)); + + // Consistent with MSW (for now), send the ITEM_MENU *after* + // the RIGHT_CLICK event. TODO: This behavior may change. + wxTreeEvent nevent2(wxEVT_COMMAND_TREE_ITEM_MENU, this, item); + nevent2.m_pointDrag = CalcScrolledPosition(pt); + GetEventHandler()->ProcessEvent(nevent2); + } + else if ( event.MiddleDown() ) + { + wxTreeEvent + nevent(wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK, this, item); nevent.m_pointDrag = CalcScrolledPosition(pt); - nevent.SetEventObject(this); - GetEventHandler()->ProcessEvent(nevent); + event.Skip(!GetEventHandler()->ProcessEvent(nevent)); } else if ( event.LeftUp() ) { + if (flags & wxTREE_HITTEST_ONITEMSTATEICON) + { + wxTreeEvent + nevent(wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK, this, item); + GetEventHandler()->ProcessEvent(nevent); + } + // this facilitates multiple-item drag-and-drop - if (item && HasFlag(wxTR_MULTIPLE)) + if ( /* item && */ HasFlag(wxTR_MULTIPLE)) { wxArrayTreeItemIds selections; size_t count = GetSelections(selections); if (count > 1 && - !event.ControlDown() && + !event.CmdDown() && !event.ShiftDown()) { - SelectItem(item, true, false); + DoSelectItem(item, true, false); } } @@ -3021,14 +3718,15 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) m_renameTimer = new wxTreeRenameTimer( this ); } - m_renameTimer->Start( wxTreeRenameTimer::DELAY, TRUE ); + m_renameTimer->Start( wxTreeRenameTimer::DELAY, true ); } - m_lastOnSame = FALSE; + m_lastOnSame = false; } } - else // !RightDown() && !LeftUp() ==> LeftDown() || LeftDClick() + else // !RightDown() && !MiddleDown() && !LeftUp() { + // ==> LeftDown() || LeftDClick() if ( event.LeftDown() ) { m_lastOnSame = item == m_current; @@ -3052,17 +3750,19 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) // user clicked outside of the present selection. // otherwise, perform the deselection on mouse-up. // this allows multiple drag and drop to work. - - if (!IsSelected(item)) + // but if Cmd is down, toggle selection of the clicked item + if (!IsSelected(item) || event.CmdDown()) { // how should the selection work for this event? bool is_multiple, extended_select, unselect_others; EventFlagsToSelType(GetWindowStyleFlag(), event.ShiftDown(), - event.ControlDown(), - is_multiple, extended_select, unselect_others); + event.CmdDown(), + is_multiple, + extended_select, + unselect_others); - SelectItem(item, unselect_others, extended_select); + DoSelectItem(item, unselect_others, extended_select); } @@ -3074,13 +3774,12 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) if ( m_renameTimer ) m_renameTimer->Stop(); - m_lastOnSame = FALSE; + m_lastOnSame = false; // send activate event first - wxTreeEvent nevent( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() ); - nevent.m_item = item; + wxTreeEvent + nevent(wxEVT_COMMAND_TREE_ITEM_ACTIVATED, this, item); nevent.m_pointDrag = CalcScrolledPosition(pt); - nevent.SetEventObject( this ); if ( !GetEventHandler()->ProcessEvent( nevent ) ) { // if the user code didn't process the activate event, @@ -3099,7 +3798,7 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) void wxGenericTreeCtrl::OnInternalIdle() { wxWindow::OnInternalIdle(); - + // Check if we need to select the root item // because nothing else has been selected. // Delaying it means that we can invoke event handlers @@ -3112,65 +3811,17 @@ void wxGenericTreeCtrl::OnInternalIdle() SelectItem(GetRootItem()); } - /* after all changes have been done to the tree control, - * we actually redraw the tree when everything is over */ - - if (!m_dirty) return; - - m_dirty = FALSE; - - CalculatePositions(); - Refresh(); - AdjustMyScrollbars(); -} - -void wxGenericTreeCtrl::CalculateSize( wxGenericTreeItem *item, wxDC &dc ) -{ - wxCoord text_w = 0; - wxCoord text_h = 0; - - 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 ); - text_h+=2; - - // restore normal font - dc.SetFont( m_normalFont ); - - int image_h = 0; - int image_w = 0; - int image = item->GetCurrentImage(); - if ( image != NO_IMAGE ) - { - if ( m_imageListNormal ) - { - m_imageListNormal->GetSize( image, image_w, image_h ); - image_w += 4; - } - } - - int total_h = (image_h > text_h) ? image_h : text_h; - - if (total_h < 30) - total_h += 2; // at least 2 pixels - else - total_h += total_h/10; // otherwise 10% extra spacing - - item->SetHeight(total_h); - if (total_h>m_lineHeight) - m_lineHeight=total_h; - - item->SetWidth(image_w+text_w+2); + // after all changes have been done to the tree control, + // actually redraw the tree when everything is over + if (m_dirty) + DoDirtyProcessing(); } -// ----------------------------------------------------------------------------- -// for developper : y is now the top of the level -// not the middle of it ! -void wxGenericTreeCtrl::CalculateLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y ) +void +wxGenericTreeCtrl::CalculateLevel(wxGenericTreeItem *item, + wxDC &dc, + int level, + int &y ) { int x = level*m_indent; if (!HasFlag(wxTR_HIDE_ROOT)) @@ -3184,7 +3835,7 @@ void wxGenericTreeCtrl::CalculateLevel( wxGenericTreeItem *item, wxDC &dc, int l goto Recurse; } - CalculateSize( item, dc ); + item->CalculateSize(this, dc); // set its position item->SetX( x+m_spacing ); @@ -3199,7 +3850,7 @@ void wxGenericTreeCtrl::CalculateLevel( wxGenericTreeItem *item, wxDC &dc, int l Recurse: wxArrayGenericTreeItems& children = item->GetChildren(); - size_t n, count = children.Count(); + size_t n, count = children.GetCount(); ++level; for (n = 0; n < count; ++n ) CalculateLevel( children[n], dc, level, y ); // recurse @@ -3222,9 +3873,16 @@ void wxGenericTreeCtrl::CalculatePositions() CalculateLevel( m_anchor, dc, 0, y ); // start recursion } +void wxGenericTreeCtrl::Refresh(bool eraseBackground, const wxRect *rect) +{ + if ( !IsFrozen() ) + wxTreeCtrlBase::Refresh(eraseBackground, rect); +} + void wxGenericTreeCtrl::RefreshSubtree(wxGenericTreeItem *item) { - if (m_dirty) return; + if (m_dirty || IsFrozen() ) + return; wxSize client = GetClientSize(); @@ -3233,25 +3891,29 @@ void wxGenericTreeCtrl::RefreshSubtree(wxGenericTreeItem *item) rect.width = client.x; rect.height = client.y; - Refresh(TRUE, &rect); + Refresh(true, &rect); AdjustMyScrollbars(); } void wxGenericTreeCtrl::RefreshLine( wxGenericTreeItem *item ) { - if (m_dirty) return; + if (m_dirty || IsFrozen() ) + return; wxRect rect; 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() { + if (IsFrozen()) + return; + // TODO: this is awfully inefficient, we should keep the list of all // selected items internally, should be much faster if ( m_anchor ) @@ -3260,6 +3922,9 @@ void wxGenericTreeCtrl::RefreshSelected() void wxGenericTreeCtrl::RefreshSelectedUnder(wxGenericTreeItem *item) { + if (IsFrozen()) + return; + if ( item->IsSelected() ) RefreshLine(item); @@ -3271,6 +3936,16 @@ void wxGenericTreeCtrl::RefreshSelectedUnder(wxGenericTreeItem *item) } } +void wxGenericTreeCtrl::DoThaw() +{ + wxTreeCtrlBase::DoThaw(); + + if ( m_dirty ) + DoDirtyProcessing(); + else + Refresh(); +} + // ---------------------------------------------------------------------------- // changing colours: we need to refresh the tree control // ---------------------------------------------------------------------------- @@ -3278,21 +3953,96 @@ void wxGenericTreeCtrl::RefreshSelectedUnder(wxGenericTreeItem *item) bool wxGenericTreeCtrl::SetBackgroundColour(const wxColour& colour) { if ( !wxWindow::SetBackgroundColour(colour) ) - return FALSE; + return false; Refresh(); - return TRUE; + return true; } bool wxGenericTreeCtrl::SetForegroundColour(const wxColour& colour) { if ( !wxWindow::SetForegroundColour(colour) ) - return FALSE; + return false; Refresh(); - return TRUE; + return true; +} + +// Process the tooltip event, to speed up event processing. +// Doesn't actually get a tooltip. +void wxGenericTreeCtrl::OnGetToolTip( wxTreeEvent &event ) +{ + event.Veto(); +} + + +// NOTE: If using the wxListBox visual attributes works everywhere then this can +// be removed, as well as the #else case below. +#define _USE_VISATTR 0 + +//static +wxVisualAttributes +#if _USE_VISATTR +wxGenericTreeCtrl::GetClassDefaultAttributes(wxWindowVariant variant) +#else +wxGenericTreeCtrl::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant)) +#endif +{ +#if _USE_VISATTR + // Use the same color scheme as wxListBox + return wxListBox::GetClassDefaultAttributes(variant); +#else + wxVisualAttributes attr; + attr.colFg = wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOXTEXT); + attr.colBg = wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX); + attr.font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + return attr; +#endif +} + +void wxGenericTreeCtrl::DoDirtyProcessing() +{ + if (IsFrozen()) + return; + + m_dirty = false; + + CalculatePositions(); + Refresh(); + AdjustMyScrollbars(); +} + +wxSize wxGenericTreeCtrl::DoGetBestSize() const +{ + // make sure all positions are calculated as normally this only done during + // idle time but we need them for base class DoGetBestSize() to return the + // correct result + wxConstCast(this, wxGenericTreeCtrl)->CalculatePositions(); + + wxSize size = wxTreeCtrlBase::DoGetBestSize(); + + // there seems to be an implicit extra border around the items, although + // I'm not really sure where does it come from -- but without this, the + // scrollbars appear in a tree with default/best size + size.IncBy(4, 4); + + // and the border has to be rounded up to a multiple of PIXELS_PER_UNIT or + // scrollbars still appear + const wxSize& borderSize = GetWindowBorderSize(); + + int dx = (size.x - borderSize.x) % PIXELS_PER_UNIT; + if ( dx ) + size.x += PIXELS_PER_UNIT - dx; + int dy = (size.y - borderSize.y) % PIXELS_PER_UNIT; + if ( dy ) + size.y += PIXELS_PER_UNIT - dy; + + // we need to update the cache too as the base class cached its own value + CacheBestSize(size); + + return size; } #endif // wxUSE_TREECTRL