X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/b09bda689c8525886d217a9b171b354391cd6d78..c3732409acc7a1e0b3cdb1f0a5dec7cc49a4b28b:/src/generic/treectlg.cpp diff --git a/src/generic/treectlg.cpp b/src/generic/treectlg.cpp index 34c4ccd440..25d04b0dee 100644 --- a/src/generic/treectlg.cpp +++ b/src/generic/treectlg.cpp @@ -5,7 +5,7 @@ // Created: 01/02/97 // Modified: 22/10/98 - almost total rewrite, simpler interface (VZ) // Id: $Id$ -// Copyright: (c) 1998 Robert Roebling, Julian Smart and Markus Holzem +// Copyright: (c) 1998 Robert Roebling and Julian Smart // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// @@ -17,7 +17,7 @@ // headers // ----------------------------------------------------------------------------- -#ifdef __GNUG__ +#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) #pragma implementation "treectlg.h" #endif @@ -38,14 +38,19 @@ #include "wx/settings.h" #include "wx/dcclient.h" +#include "wx/renderer.h" + +#ifdef __WXMAC__ + #include "wx/mac/private.h" +#endif + // ----------------------------------------------------------------------------- // array types // ----------------------------------------------------------------------------- class WXDLLEXPORT wxGenericTreeItem; -WX_DEFINE_EXPORTED_ARRAY(wxGenericTreeItem *, wxArrayGenericTreeItems); -//WX_DEFINE_OBJARRAY(wxArrayTreeItemIds); +WX_DEFINE_EXPORTED_ARRAY_PTR(wxGenericTreeItem *, wxArrayGenericTreeItems); // ---------------------------------------------------------------------------- // constants @@ -53,7 +58,7 @@ WX_DEFINE_EXPORTED_ARRAY(wxGenericTreeItem *, wxArrayGenericTreeItems); static const int NO_IMAGE = -1; -#define PIXELS_PER_UNIT 10 +static const int PIXELS_PER_UNIT = 10; // ----------------------------------------------------------------------------- // private classes @@ -63,41 +68,68 @@ static const int NO_IMAGE = -1; class WXDLLEXPORT wxTreeRenameTimer: public wxTimer { public: + // start editing the current item after half a second (if the mouse hasn't + // been clicked/moved) + enum { DELAY = 500 }; + wxTreeRenameTimer( wxGenericTreeCtrl *owner ); - void Notify(); + virtual void Notify(); private: - wxGenericTreeCtrl *m_owner; + wxGenericTreeCtrl *m_owner; + + DECLARE_NO_COPY_CLASS(wxTreeRenameTimer) }; // control used for in-place edit class WXDLLEXPORT wxTreeTextCtrl: public wxTextCtrl { public: - wxTreeTextCtrl( wxWindow *parent, - const wxWindowID id, - bool *accept, - wxString *res, - wxGenericTreeCtrl *owner, - const wxString &value = wxEmptyString, - const wxPoint &pos = wxDefaultPosition, - const wxSize &size = wxDefaultSize, - int style = wxSIMPLE_BORDER, - const wxValidator& validator = wxDefaultValidator, - const wxString &name = wxTextCtrlNameStr ); + wxTreeTextCtrl(wxGenericTreeCtrl *owner, wxGenericTreeItem *item); + + void StopEditing() + { + Finish(); + m_owner->OnRenameCancelled(m_itemEdited); + } + const wxGenericTreeItem* item() const { return m_itemEdited; } +protected: void OnChar( wxKeyEvent &event ); void OnKeyUp( wxKeyEvent &event ); void OnKillFocus( wxFocusEvent &event ); + bool AcceptChanges(); + void Finish(); + private: - bool *m_accept; - wxString *m_res; wxGenericTreeCtrl *m_owner; + wxGenericTreeItem *m_itemEdited; wxString m_startValue; + bool m_finished; + bool m_aboutToFinish; DECLARE_EVENT_TABLE() + DECLARE_NO_COPY_CLASS(wxTreeTextCtrl) +}; + +// timer used to clear wxGenericTreeCtrl::m_findPrefix if no key was pressed +// for a sufficiently long time +class WXDLLEXPORT wxTreeFindTimer : public wxTimer +{ +public: + // reset the current prefix after half a second of inactivity + enum { DELAY = 500 }; + + wxTreeFindTimer( wxGenericTreeCtrl *owner ) { m_owner = owner; } + + virtual void Notify() { m_owner->m_findPrefix.clear(); } + +private: + wxGenericTreeCtrl *m_owner; + + DECLARE_NO_COPY_CLASS(wxTreeFindTimer) }; // a tree item @@ -108,7 +140,6 @@ public: wxGenericTreeItem() { m_data = NULL; } wxGenericTreeItem( wxGenericTreeItem *parent, const wxString& text, - wxDC& dc, int image, int selImage, wxTreeItemData *data ); @@ -131,7 +162,7 @@ public: void SetImage(int image, wxTreeItemIcon which) { m_images[which] = image; } void SetData(wxTreeItemData *data) { m_data = data; } - void SetHasPlus(bool has = TRUE) { m_hasPlus = has; } + void SetHasPlus(bool has = true) { m_hasPlus = has; } void SetBold(bool bold) { m_isBold = bold; } @@ -155,7 +186,7 @@ public: void DeleteChildren(wxGenericTreeCtrl *tree = NULL); // 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); } @@ -163,17 +194,17 @@ public: void GetSize( int &x, int &y, const wxGenericTreeCtrl* ); // 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(); } @@ -191,7 +222,7 @@ public: if ( !m_attr ) { m_attr = new wxTreeItemAttr; - m_ownsAttr = TRUE; + m_ownsAttr = true; } return *m_attr; } @@ -200,13 +231,13 @@ public: { if ( m_ownsAttr ) delete m_attr; m_attr = attr; - m_ownsAttr = FALSE; + m_ownsAttr = false; } // set them and delete when done void AssignAttributes(wxTreeItemAttr *attr) { SetAttributes(attr); - m_ownsAttr = TRUE; + m_ownsAttr = true; } private: @@ -224,12 +255,12 @@ 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 - short m_y; // (virtual) offset from left - short m_width; // width of this item - unsigned char m_height; // height of this item + wxCoord m_y; // (virtual) offset from left + int m_width; // width of this item + int m_height; // height of this item // use bitfields to save size int m_isCollapsed :1; @@ -238,6 +269,8 @@ private: // children but has a [+] button int m_isBold :1; // render the label in bold font int m_ownsAttr :1; // delete attribute when done + + DECLARE_NO_COPY_CLASS(wxGenericTreeItem) }; // ============================================================================= @@ -262,6 +295,23 @@ static void EventFlagsToSelType(long style, unselect_others = !(extended_select || (ctrlDown && is_multiple)); } +// check if the given item is under another one +static bool IsDescendantOf(const wxGenericTreeItem *parent, const wxGenericTreeItem *item) +{ + while ( item ) + { + if ( item == parent ) + { + // item is a descendant of parent + return true; + } + + item = item->GetParent(); + } + + return false; +} + // ----------------------------------------------------------------------------- // wxTreeRenameTimer (internal) // ----------------------------------------------------------------------------- @@ -286,78 +336,153 @@ BEGIN_EVENT_TABLE(wxTreeTextCtrl,wxTextCtrl) EVT_KILL_FOCUS (wxTreeTextCtrl::OnKillFocus) END_EVENT_TABLE() -wxTreeTextCtrl::wxTreeTextCtrl( wxWindow *parent, - const wxWindowID id, - bool *accept, - wxString *res, - wxGenericTreeCtrl *owner, - const wxString &value, - const wxPoint &pos, - const wxSize &size, - int style, - const wxValidator& validator, - const wxString &name ) - : wxTextCtrl( parent, id, value, pos, size, style, validator, name ) -{ - m_res = res; - m_accept = accept; +wxTreeTextCtrl::wxTreeTextCtrl(wxGenericTreeCtrl *owner, + wxGenericTreeItem *item) + : m_itemEdited(item), m_startValue(item->GetText()) +{ m_owner = owner; - (*m_accept) = FALSE; - (*m_res) = wxEmptyString; - m_startValue = value; + m_finished = false; + m_aboutToFinish = false; + + int w = m_itemEdited->GetWidth(), + h = m_itemEdited->GetHeight(); + + int x, y; + m_owner->CalcScrolledPosition(item->GetX(), item->GetY(), &x, &y); + + int image_h = 0, + image_w = 0; + + int image = item->GetCurrentImage(); + if ( image != NO_IMAGE ) + { + 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!")); + } + } + + // FIXME: what are all these hardcoded 4, 8 and 11s really? + x += image_w; + w -= image_w + 4; +#ifdef __WXMAC__ + wxSize bs = DoGetBestSize() ; + // edit control height + if ( h > bs.y - 8 ) + { + int diff = h - ( bs.y - 8 ) ; + h -= diff ; + y += diff / 2 ; + } +#endif + + (void)Create(m_owner, wxID_ANY, m_startValue, + wxPoint(x - 4, y - 4), wxSize(w + 11, h + 8)); } -void wxTreeTextCtrl::OnChar( wxKeyEvent &event ) +bool wxTreeTextCtrl::AcceptChanges() { - // TODO focus doesn't return to the wxTextCtrl when this closes... - if (event.m_keyCode == WXK_RETURN) + const wxString value = GetValue(); + + if ( value == m_startValue ) { - (*m_accept) = TRUE; - (*m_res) = GetValue(); + // nothing changed, always accept + // 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 - if ((*m_accept) && ((*m_res) != m_startValue)) - m_owner->OnRenameAccept(); + m_owner->OnRenameCancelled(m_itemEdited); + return true; + } - if (!wxPendingDelete.Member(this)) - wxPendingDelete.Append(this); + if ( !m_owner->OnRenameAccept(m_itemEdited, value) ) + { + // vetoed by the user + return false; + } - return; + // accepted, do rename the item + m_owner->SetItemText(m_itemEdited, value); + + return true; +} + +void wxTreeTextCtrl::Finish() +{ + if ( !m_finished ) + { + m_owner->ResetTextControl(); + + wxPendingDelete.Append(this); + + m_finished = true; + + m_owner->SetFocusIgnoringChildren(); } - if (event.m_keyCode == WXK_ESCAPE) +} + +void wxTreeTextCtrl::OnChar( wxKeyEvent &event ) +{ + switch ( event.m_keyCode ) { - (*m_accept) = FALSE; - (*m_res) = ""; + case WXK_RETURN: + m_aboutToFinish = true; + // Notify the owner about the changes + AcceptChanges(); + // Even if vetoed, close the control (consistent with MSW) + Finish(); + break; - if (!wxPendingDelete.Member(this)) - wxPendingDelete.Append(this); + case WXK_ESCAPE: + StopEditing(); + break; - return; + default: + event.Skip(); } - event.Skip(); } void wxTreeTextCtrl::OnKeyUp( wxKeyEvent &event ) { - // auto-grow the textctrl: - wxSize parentSize = m_owner->GetSize(); - wxPoint myPos = GetPosition(); - wxSize mySize = GetSize(); - int sx, sy; - GetTextExtent(GetValue() + _T("MM"), &sx, &sy); - if (myPos.x + sx > parentSize.x) sx = parentSize.x - myPos.x; - if (mySize.x > sx) sx = mySize.x; - SetSize(sx, -1); + if ( !m_finished ) + { + // auto-grow the textctrl: + wxSize parentSize = m_owner->GetSize(); + wxPoint myPos = GetPosition(); + wxSize mySize = GetSize(); + int sx, sy; + GetTextExtent(GetValue() + _T("M"), &sx, &sy); + if (myPos.x + sx > parentSize.x) + sx = parentSize.x - myPos.x; + if (mySize.x > sx) + sx = mySize.x; + SetSize(sx, wxDefaultCoord); + } event.Skip(); } -void wxTreeTextCtrl::OnKillFocus( wxFocusEvent &WXUNUSED(event) ) +void wxTreeTextCtrl::OnKillFocus( wxFocusEvent &event ) { - if (!wxPendingDelete.Member(this)) - wxPendingDelete.Append(this); + if ( !m_finished && !m_aboutToFinish ) + { + // We must finish regardless of success, otherwise we'll get + // focus problems: + Finish(); + + if ( !AcceptChanges() ) + m_owner->OnRenameCancelled( m_itemEdited ); + } - if ((*m_accept) && ((*m_res) != m_startValue)) - m_owner->OnRenameAccept(); + // We must let the native text control handle focus, too, otherwise + // it could have problems with the cursor (e.g., in wxGTK). + event.Skip(); } // ----------------------------------------------------------------------------- @@ -366,7 +491,6 @@ void wxTreeTextCtrl::OnKillFocus( wxFocusEvent &WXUNUSED(event) ) wxGenericTreeItem::wxGenericTreeItem(wxGenericTreeItem *parent, const wxString& text, - wxDC& WXUNUSED(dc), int image, int selImage, wxTreeItemData *data) : m_text(text) @@ -379,15 +503,15 @@ wxGenericTreeItem::wxGenericTreeItem(wxGenericTreeItem *parent, m_data = data; 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_ownsAttr = false; // We don't know the height here yet. m_width = 0; @@ -414,6 +538,8 @@ void wxGenericTreeItem::DeleteChildren(wxGenericTreeCtrl *tree) tree->SendDeleteEvent(child); child->DeleteChildren(tree); + if (child == tree->m_select_me) + tree->m_select_me = NULL; delete child; } @@ -476,11 +602,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; @@ -573,10 +707,10 @@ BEGIN_EVENT_TABLE(wxGenericTreeCtrl,wxScrolledWindow) EVT_CHAR (wxGenericTreeCtrl::OnChar) EVT_SET_FOCUS (wxGenericTreeCtrl::OnSetFocus) EVT_KILL_FOCUS (wxGenericTreeCtrl::OnKillFocus) - EVT_IDLE (wxGenericTreeCtrl::OnIdle) + 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. @@ -591,9 +725,9 @@ IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl, wxGenericTreeCtrl) void wxGenericTreeCtrl::Init() { - m_current = m_key_current = m_anchor = (wxGenericTreeItem *) NULL; - m_hasFocus = FALSE; - m_dirty = FALSE; + m_current = m_key_current = m_anchor = m_select_me = (wxGenericTreeItem *) NULL; + m_hasFocus = false; + m_dirty = false; m_lineHeight = 10; m_indent = 15; @@ -601,7 +735,7 @@ void wxGenericTreeCtrl::Init() m_hilightBrush = new wxBrush ( - wxSystemSettings::GetSystemColour + wxSystemSettings::GetColour ( wxSYS_COLOUR_HIGHLIGHT ), @@ -610,7 +744,7 @@ void wxGenericTreeCtrl::Init() m_hilightUnfocusedBrush = new wxBrush ( - wxSystemSettings::GetSystemColour + wxSystemSettings::GetColour ( wxSYS_COLOUR_BTNSHADOW ), @@ -620,21 +754,35 @@ void wxGenericTreeCtrl::Init() m_imageListNormal = m_imageListButtons = m_imageListState = (wxImageList *) NULL; m_ownsImageListNormal = m_ownsImageListButtons = - m_ownsImageListState = FALSE; + m_ownsImageListState = 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_freezeCount = 0; + + m_findTimer = NULL; - m_renameTimer = new wxTreeRenameTimer( this ); - m_lastOnSame = FALSE; + m_dropEffectAboveItem = false; - m_normalFont = wxSystemSettings::GetSystemFont( wxSYS_DEFAULT_GUI_FONT ); - m_boldFont = wxFont( m_normalFont.GetPointSize(), - m_normalFont.GetFamily(), - m_normalFont.GetStyle(), - wxBOLD, - m_normalFont.GetUnderlined()); + m_lastOnSame = false; + +#ifdef __WXMAC_CARBON__ + m_normalFont.MacCreateThemeFont( kThemeViewsFont ) ; +#else + m_normalFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ); +#endif + m_boldFont = wxFont(m_normalFont.GetPointSize(), + m_normalFont.GetFamily(), + m_normalFont.GetStyle(), + wxBOLD, + m_normalFont.GetUnderlined(), + m_normalFont.GetFaceName(), + m_normalFont.GetEncoding()); } bool wxGenericTreeCtrl::Create(wxWindow *parent, @@ -642,15 +790,25 @@ bool wxGenericTreeCtrl::Create(wxWindow *parent, const wxPoint& pos, const wxSize& size, long style, - const wxValidator &validator, + const wxValidator& wxVALIDATOR_PARAM(validator), const wxString& name ) { +#ifdef __WXMAC__ + int major,minor; + wxGetOsVersion( &major, &minor ); + + style &= ~wxTR_LINES_AT_ROOT; + style |= wxTR_NO_LINES; + if (major < 10) + style |= wxTR_ROW_LINES; +#endif // __WXMAC__ + wxScrolledWindow::Create( parent, id, pos, size, style|wxHSCROLL|wxVSCROLL, name ); - // If the tree display has no buttons, but does have - // connecting lines, we can use a narrower layout. - // It may not be a good idea to force this... + // If the tree display has no buttons, but does have + // connecting lines, we can use a narrower layout. + // It may not be a good idea to force this... if (!HasButtons() && !HasFlag(wxTR_NO_LINES)) { m_indent= 10; @@ -661,12 +819,18 @@ bool wxGenericTreeCtrl::Create(wxWindow *parent, SetValidator( validator ); #endif - SetBackgroundColour( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_LISTBOX ) ); + wxVisualAttributes attr = GetDefaultAttributes(); + SetOwnForegroundColour( attr.colFg ); + SetOwnBackgroundColour( attr.colBg ); + if (!m_hasFont) + SetOwnFont(attr.font); // m_dottedPen = wxPen( "grey", 0, wxDOT ); too slow under XFree86 - m_dottedPen = wxPen( "grey", 0, 0 ); + m_dottedPen = wxPen( wxT("grey"), 0, 0 ); - return TRUE; + SetBestSize(size); + + return true; } wxGenericTreeCtrl::~wxGenericTreeCtrl() @@ -677,9 +841,14 @@ wxGenericTreeCtrl::~wxGenericTreeCtrl() DeleteAllItems(); delete m_renameTimer; - if (m_ownsImageListNormal) delete m_imageListNormal; - if (m_ownsImageListState) delete m_imageListState; - if (m_ownsImageListButtons) delete m_imageListButtons; + delete m_findTimer; + + if (m_ownsImageListNormal) + delete m_imageListNormal; + if (m_ownsImageListState) + delete m_imageListState; + if (m_ownsImageListButtons) + delete m_imageListButtons; } // ----------------------------------------------------------------------------- @@ -688,22 +857,37 @@ wxGenericTreeCtrl::~wxGenericTreeCtrl() size_t wxGenericTreeCtrl::GetCount() const { - return m_anchor == NULL ? 0u : m_anchor->GetChildrenCount(); + if ( !m_anchor ) + { + // the tree is empty + return 0; + } + + size_t 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 = indent; - m_dirty = TRUE; + m_indent = (unsigned short) indent; + m_dirty = true; } void wxGenericTreeCtrl::SetSpacing(unsigned int spacing) { - m_spacing = spacing; - m_dirty = TRUE; + m_spacing = (unsigned short) spacing; + 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") ); @@ -712,11 +896,19 @@ size_t wxGenericTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool recurs void wxGenericTreeCtrl::SetWindowStyle(const long styles) { - // right now, just sets the styles. Eventually, we may - // want to update the inherited styles, but right now - // none of the parents has updatable styles + if (!HasFlag(wxTR_HIDE_ROOT) && (styles & wxTR_HIDE_ROOT)) + { + // if we will hide the root, make sure children are visible + m_anchor->SetHasPlus(); + m_anchor->Expand(); + CalculatePositions(); + } + + // right now, just sets the styles. Eventually, we may + // want to update the inherited styles, but right now + // none of the parents has updatable styles m_windowStyle = styles; - m_dirty = TRUE; + m_dirty = true; } // ----------------------------------------------------------------------------- @@ -725,7 +917,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(); } @@ -745,6 +937,30 @@ wxTreeItemData *wxGenericTreeCtrl::GetItemData(const wxTreeItemId& item) const return ((wxGenericTreeItem*) item.m_pItem)->GetData(); } +wxColour wxGenericTreeCtrl::GetItemTextColour(const wxTreeItemId& item) const +{ + wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") ); + + wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem; + return pItem->Attr().GetTextColour(); +} + +wxColour wxGenericTreeCtrl::GetItemBackgroundColour(const wxTreeItemId& item) const +{ + wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") ); + + wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem; + return pItem->Attr().GetBackgroundColour(); +} + +wxFont wxGenericTreeCtrl::GetItemFont(const wxTreeItemId& item) const +{ + wxCHECK_MSG( item.IsOk(), wxNullFont, wxT("invalid tree item") ); + + wxGenericTreeItem *pItem = (wxGenericTreeItem*) item.m_pItem; + return pItem->Attr().GetFont(); +} + void wxGenericTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text) { wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); @@ -774,6 +990,9 @@ void wxGenericTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *da { wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); + if (data) + data->SetId( item ); + ((wxGenericTreeItem*) item.m_pItem)->SetData(data); } @@ -799,6 +1018,25 @@ void wxGenericTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold) } } +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) { @@ -833,13 +1071,15 @@ bool wxGenericTreeCtrl::SetFont( const wxFont &font ) wxScrolledWindow::SetFont(font); m_normalFont = font ; - m_boldFont = wxFont( m_normalFont.GetPointSize(), - m_normalFont.GetFamily(), - m_normalFont.GetStyle(), - wxBOLD, - m_normalFont.GetUnderlined()); + m_boldFont = wxFont(m_normalFont.GetPointSize(), + m_normalFont.GetFamily(), + m_normalFont.GetStyle(), + wxBOLD, + m_normalFont.GetUnderlined(), + m_normalFont.GetFaceName(), + m_normalFont.GetEncoding()); - return TRUE; + return true; } @@ -849,7 +1089,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; @@ -857,7 +1097,7 @@ bool wxGenericTreeCtrl::IsVisible(const wxTreeItemId& item) const while (parent) { if (!parent->IsExpanded()) - return FALSE; + return false; parent = parent->GetParent(); } @@ -868,41 +1108,46 @@ 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") ); - return !((wxGenericTreeItem*) item.m_pItem)->GetChildren().IsEmpty(); + // 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 + // could have them as well and it's better to err on this side rather than + // disabling some operations which are restricted to the items with + // children for an item which does have them + return ((wxGenericTreeItem*) item.m_pItem)->HasPlus(); } 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(); } @@ -911,14 +1156,47 @@ bool wxGenericTreeCtrl::IsBold(const wxTreeItemId& item) const // navigation // ----------------------------------------------------------------------------- -wxTreeItemId wxGenericTreeCtrl::GetParent(const wxTreeItemId& item) const +wxTreeItemId wxGenericTreeCtrl::GetItemParent(const wxTreeItemId& item) const { wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); return ((wxGenericTreeItem*) item.m_pItem)->GetParent(); } -wxTreeItemId wxGenericTreeCtrl::GetFirstChild(const wxTreeItemId& item, long& cookie) const +wxTreeItemId wxGenericTreeCtrl::GetFirstChild(const wxTreeItemId& item, + wxTreeItemIdValue& cookie) const +{ + wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); + + cookie = 0; + return GetNextChild(item, cookie); +} + +wxTreeItemId wxGenericTreeCtrl::GetNextChild(const wxTreeItemId& item, + wxTreeItemIdValue& cookie) const +{ + wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); + + 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() ) + { + return children.Item((*pIndex)++); + } + else + { + // there are no more of them + return wxTreeItemId(); + } +} + +#if WXWIN_COMPATIBILITY_2_4 + +wxTreeItemId wxGenericTreeCtrl::GetFirstChild(const wxTreeItemId& item, + long& cookie) const { wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); @@ -926,7 +1204,8 @@ wxTreeItemId wxGenericTreeCtrl::GetFirstChild(const wxTreeItemId& item, long& co return GetNextChild(item, cookie); } -wxTreeItemId wxGenericTreeCtrl::GetNextChild(const wxTreeItemId& item, long& cookie) const +wxTreeItemId wxGenericTreeCtrl::GetNextChild(const wxTreeItemId& item, + long& cookie) const { wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); @@ -942,6 +1221,8 @@ wxTreeItemId wxGenericTreeCtrl::GetNextChild(const wxTreeItemId& item, long& coo } } +#endif // WXWIN_COMPATIBILITY_2_4 + wxTreeItemId wxGenericTreeCtrl::GetLastChild(const wxTreeItemId& item) const { wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); @@ -1011,7 +1292,7 @@ wxTreeItemId wxGenericTreeCtrl::GetNext(const wxTreeItemId& item) const do { toFind = GetNextSibling(p); - p = GetParent(p); + p = GetItemParent(p); } while (p.IsOk() && !toFind.IsOk()); return toFind; } @@ -1058,6 +1339,58 @@ wxTreeItemId wxGenericTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const return wxTreeItemId(); } +// called by wxTextTreeCtrl when it marks itself for deletion +void wxGenericTreeCtrl::ResetTextControl() +{ + m_textCtrl = NULL; +} + +// find the first item starting with the given prefix after the given item +wxTreeItemId wxGenericTreeCtrl::FindItem(const wxTreeItemId& idParent, + const wxString& prefixOrig) const +{ + // match is case insensitive as this is more convenient to the user: having + // to press Shift-letter to go to the item starting with a capital letter + // would be too bothersome + wxString prefix = prefixOrig.Lower(); + + // determine the starting point: we shouldn't take the current item (this + // allows to switch between two items starting with the same letter just by + // pressing it) but we shouldn't jump to the next one if the user is + // continuing to type as otherwise he might easily skip the item he wanted + wxTreeItemId id = idParent; + if ( prefix.length() == 1 ) + { + id = GetNext(id); + } + + // look for the item starting with the given prefix after it + while ( id.IsOk() && !GetItemText(id).Lower().StartsWith(prefix) ) + { + id = GetNext(id); + } + + // if we haven't found anything... + if ( !id.IsOk() ) + { + // ... wrap to the beginning + id = GetRootItem(); + if ( HasFlag(wxTR_HIDE_ROOT) ) + { + // can't select virtual root + id = GetNext(id); + } + + // and try all the items (stop when we get to the one we started from) + while ( id != idParent && !GetItemText(id).Lower().StartsWith(prefix) ) + { + id = GetNext(id); + } + } + + return id; +} + // ----------------------------------------------------------------------------- // operations // ----------------------------------------------------------------------------- @@ -1075,15 +1408,14 @@ 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 - wxClientDC dc(this); wxGenericTreeItem *item = - new wxGenericTreeItem( parent, text, dc, image, selImage, data ); + new wxGenericTreeItem( parent, text, image, selImage, data ); if ( data != NULL ) { - data->m_pItem = (long) item; + data->m_pItem = item; } parent->Insert( item, previous ); @@ -1097,27 +1429,28 @@ wxTreeItemId wxGenericTreeCtrl::AddRoot(const wxString& text, { wxCHECK_MSG( !m_anchor, wxTreeItemId(), wxT("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 - wxClientDC dc(this); - m_anchor = new wxGenericTreeItem((wxGenericTreeItem *)NULL, text, dc, + m_anchor = new wxGenericTreeItem((wxGenericTreeItem *)NULL, text, image, selImage, data); + if ( data != NULL ) + { + data->m_pItem = m_anchor; + } + if (HasFlag(wxTR_HIDE_ROOT)) { // if root is hidden, make sure we can navigate // into children m_anchor->SetHasPlus(); - Expand(m_anchor); - } - if ( data != NULL ) - { - data->m_pItem = (long) m_anchor; + m_anchor->Expand(); + CalculatePositions(); } if (!HasFlag(wxTR_MULTIPLE)) { m_current = m_key_current = m_anchor; - m_current->SetHilight( TRUE ); + m_current->SetHilight( true ); } return m_anchor; @@ -1144,9 +1477,13 @@ wxTreeItemId wxGenericTreeCtrl::InsertItem(const wxTreeItemId& parentId, return AddRoot(text, image, selImage, data); } - int index = parent->GetChildren().Index((wxGenericTreeItem*) idPrevious.m_pItem); - wxASSERT_MSG( index != wxNOT_FOUND, - wxT("previous item in wxGenericTreeCtrl::InsertItem() is not a sibling") ); + int index = -1; + if (idPrevious.IsOk()) + { + index = parent->GetChildren().Index((wxGenericTreeItem*) idPrevious.m_pItem); + wxASSERT_MSG( index != wxNOT_FOUND, + wxT("previous item in wxGenericTreeCtrl::InsertItem() is not a sibling") ); + } return DoInsertItem(parentId, (size_t)++index, text, image, selImage, data); } @@ -1186,54 +1523,100 @@ wxTreeItemId wxGenericTreeCtrl::AppendItem(const wxTreeItemId& parentId, void wxGenericTreeCtrl::SendDeleteEvent(wxGenericTreeItem *item) { wxTreeEvent event( wxEVT_COMMAND_TREE_DELETE_ITEM, GetId() ); - event.m_item = (long) item; + event.m_item = item; event.SetEventObject( this ); ProcessEvent( event ); } +// Don't leave edit or selection on a child which is about to disappear +void wxGenericTreeCtrl::ChildrenClosing(wxGenericTreeItem* item) +{ + if (m_textCtrl != NULL && item != m_textCtrl->item() && IsDescendantOf(item, m_textCtrl->item())) { + m_textCtrl->StopEditing(); + } + if (item != m_key_current && IsDescendantOf(item, m_key_current)) { + m_key_current = NULL; + } + 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); } 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; - // don't stay with invalid m_key_current or we will crash in - // the next call to OnChar() - bool changeKeyCurrent = FALSE; - wxGenericTreeItem *itemKey = m_key_current; - while ( itemKey ) + if (m_textCtrl != NULL && IsDescendantOf(item, m_textCtrl->item())) { - if ( itemKey == item ) - { - // m_key_current is a descendant of the item being deleted - changeKeyCurrent = TRUE; - break; - } - itemKey = itemKey->GetParent(); + // can't delete the item being edited, cancel editing it first + m_textCtrl->StopEditing(); } wxGenericTreeItem *parent = item->GetParent(); + + // don't keep stale pointers around! + if ( IsDescendantOf(item, m_key_current) ) + { + // 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; + } + + // m_select_me records whether we need to select + // a different item, in idle time. + if ( m_select_me && IsDescendantOf(item, m_select_me) ) + { + m_select_me = parent; + } + + if ( IsDescendantOf(item, m_current) ) + { + // Don't silently change the selection: + // do it properly in idle time, so event + // handlers get called. + + // m_current = parent; + m_current = NULL; + m_select_me = parent; + } + + // remove the item from the tree if ( parent ) { parent->GetChildren().Remove( item ); // remove by value } - - if ( changeKeyCurrent ) + else // deleting the root { - // may be NULL or not - m_key_current = parent; + // nothing will be left in the tree + m_anchor = NULL; } + // and delete all of its children and the item itself now item->DeleteChildren(this); SendDeleteEvent(item); + + if (item == m_select_me) + m_select_me = NULL; + delete item; } @@ -1241,12 +1624,7 @@ void wxGenericTreeCtrl::DeleteAllItems() { if ( m_anchor ) { - m_dirty = TRUE; - - m_anchor->DeleteChildren(this); - delete m_anchor; - - m_anchor = NULL; + Delete(m_anchor); } } @@ -1255,6 +1633,8 @@ void wxGenericTreeCtrl::Expand(const wxTreeItemId& itemId) wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem; wxCHECK_RET( item, _T("invalid item in wxGenericTreeCtrl::Expand") ); + wxCHECK_RET( !HasFlag(wxTR_HIDE_ROOT) || itemId != GetRootItem(), + _T("can't expand hidden root") ); if ( !item->HasPlus() ) return; @@ -1263,7 +1643,7 @@ void wxGenericTreeCtrl::Expand(const wxTreeItemId& itemId) return; wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_EXPANDING, GetId() ); - event.m_item = (long) item; + event.m_item = item; event.SetEventObject( this ); if ( ProcessEvent( event ) && !event.IsAllowed() ) @@ -1283,29 +1663,35 @@ void wxGenericTreeCtrl::Expand(const wxTreeItemId& itemId) void wxGenericTreeCtrl::ExpandAll(const wxTreeItemId& item) { - Expand(item); - if ( IsExpanded(item) ) + if ( !HasFlag(wxTR_HIDE_ROOT) || item != GetRootItem()) { - long cookie; - wxTreeItemId child = GetFirstChild(item, cookie); - while ( child.IsOk() ) - { - ExpandAll(child); + Expand(item); + if ( !IsExpanded(item) ) + return; + } - child = GetNextChild(item, cookie); - } + wxTreeItemIdValue cookie; + wxTreeItemId child = GetFirstChild(item, cookie); + while ( child.IsOk() ) + { + ExpandAll(child); + + child = GetNextChild(item, cookie); } } void wxGenericTreeCtrl::Collapse(const wxTreeItemId& itemId) { + wxCHECK_RET( !HasFlag(wxTR_HIDE_ROOT) || itemId != GetRootItem(), + _T("can't collapse hidden root") ); + wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem; if ( !item->IsExpanded() ) return; wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_COLLAPSING, GetId() ); - event.m_item = (long) item; + event.m_item = item; event.SetEventObject( this ); if ( ProcessEvent( event ) && !event.IsAllowed() ) { @@ -1313,6 +1699,7 @@ void wxGenericTreeCtrl::Collapse(const wxTreeItemId& itemId) return; } + ChildrenClosing(item); item->Collapse(); #if 0 // TODO why should items be collapsed recursively? @@ -1352,8 +1739,11 @@ void wxGenericTreeCtrl::Unselect() { if (m_current) { - m_current->SetHilight( FALSE ); + m_current->SetHilight( false ); RefreshLine( m_current ); + + m_current = NULL; + m_select_me = NULL; } } @@ -1361,7 +1751,7 @@ void wxGenericTreeCtrl::UnselectAllChildren(wxGenericTreeItem *item) { if (item->IsSelected()) { - item->SetHilight(FALSE); + item->SetHilight(false); RefreshLine(item); } @@ -1378,7 +1768,13 @@ void wxGenericTreeCtrl::UnselectAllChildren(wxGenericTreeItem *item) void wxGenericTreeCtrl::UnselectAll() { - UnselectAllChildren((wxGenericTreeItem*) GetRootItem().m_pItem); + wxTreeItemId rootItem = GetRootItem(); + + // the tree might not have the root item at all + if ( rootItem ) + { + UnselectAllChildren((wxGenericTreeItem*) rootItem.m_pItem); + } } // Recursive function ! @@ -1401,7 +1797,7 @@ bool wxGenericTreeCtrl::TagNextChildren(wxGenericTreeItem *crt_item, wxGenericTr size_t count = children.Count(); for (size_t n=(size_t)(index+1); nHasChildren()) { @@ -1422,29 +1818,21 @@ bool wxGenericTreeCtrl::TagAllChildrenUntilLast(wxGenericTreeItem *crt_item, wxG 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) { - // 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(); @@ -1454,12 +1842,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; @@ -1471,8 +1861,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() ) { @@ -1483,21 +1873,21 @@ void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId, } wxTreeEvent event( wxEVT_COMMAND_TREE_SEL_CHANGING, GetId() ); - event.m_item = (long) item; - event.m_itemOld = (long) m_current; + event.m_item = 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() ) return; - wxTreeItemId parent = GetParent( itemId ); + wxTreeItemId parent = GetItemParent( itemId ); while (parent.IsOk()) { if (!IsExpanded(parent)) Expand( parent ); - parent = GetParent( parent ); + parent = GetItemParent( parent ); } EnsureVisible( itemId ); @@ -1522,7 +1912,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) @@ -1537,8 +1927,24 @@ void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId, GetEventHandler()->ProcessEvent( event ); } +void wxGenericTreeCtrl::SelectItem(const wxTreeItemId& itemId, bool select) +{ + if ( select ) + { + DoSelectItem(itemId); + } + else // deselect + { + wxGenericTreeItem *item = (wxGenericTreeItem*) itemId.m_pItem; + wxCHECK_RET( item, wxT("SelectItem(): invalid tree item") ); + + item->SetHilight(false); + RefreshLine(item); + } +} + void wxGenericTreeCtrl::FillArray(wxGenericTreeItem *item, - wxArrayTreeItemIds &array) const + wxArrayTreeItemIds &array) const { if ( item->IsSelected() ) array.Add(wxTreeItemId(item)); @@ -1567,18 +1973,32 @@ size_t wxGenericTreeCtrl::GetSelections(wxArrayTreeItemIds &array) const void wxGenericTreeCtrl::EnsureVisible(const wxTreeItemId& item) { + wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); + if (!item.IsOk()) return; wxGenericTreeItem *gitem = (wxGenericTreeItem*) item.m_pItem; // first expand all parent branches wxGenericTreeItem *parent = gitem->GetParent(); - while ( parent ) + + if ( HasFlag(wxTR_HIDE_ROOT) ) { - Expand(parent); - parent = parent->GetParent(); + while ( parent && parent != m_anchor ) + { + Expand(parent); + parent = parent->GetParent(); + } } - + else + { + while ( parent ) + { + Expand(parent); + parent = parent->GetParent(); + } + } + //if (parent) CalculatePositions(); ScrollTo(item); @@ -1591,8 +2011,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 + wxYieldIfNeeded(); +#endif wxGenericTreeItem *gitem = (wxGenericTreeItem*) item.m_pItem; // now scroll to the item @@ -1663,7 +2087,7 @@ void wxGenericTreeCtrl::SortChildren(const wxTreeItemId& itemId) wxArrayGenericTreeItems& children = item->GetChildren(); if ( children.Count() > 1 ) { - m_dirty = TRUE; + m_dirty = true; s_treeBeingSorted = this; children.Sort(tree_ctrl_compare_func); @@ -1730,43 +2154,46 @@ void wxGenericTreeCtrl::SetImageList(wxImageList *imageList) { if (m_ownsImageListNormal) delete m_imageListNormal; m_imageListNormal = imageList; - m_ownsImageListNormal = FALSE; - m_dirty = TRUE; - CalculateLineHeight(); + m_ownsImageListNormal = false; + m_dirty = true; + // Don't do any drawing if we're setting the list to NULL, + // since we may be in the process of deleting the tree control. + if (imageList) + CalculateLineHeight(); } void wxGenericTreeCtrl::SetStateImageList(wxImageList *imageList) { if (m_ownsImageListState) delete m_imageListState; m_imageListState = imageList; - m_ownsImageListState = FALSE; + m_ownsImageListState = false; } void wxGenericTreeCtrl::SetButtonsImageList(wxImageList *imageList) { if (m_ownsImageListButtons) delete m_imageListButtons; m_imageListButtons = imageList; - m_ownsImageListButtons = FALSE; - m_dirty = TRUE; + m_ownsImageListButtons = false; + m_dirty = true; CalculateLineHeight(); } void wxGenericTreeCtrl::AssignImageList(wxImageList *imageList) { SetImageList(imageList); - m_ownsImageListNormal = TRUE; + m_ownsImageListNormal = true; } void wxGenericTreeCtrl::AssignStateImageList(wxImageList *imageList) { SetStateImageList(imageList); - m_ownsImageListState = TRUE; + m_ownsImageListState = true; } void wxGenericTreeCtrl::AssignButtonsImageList(wxImageList *imageList) { SetButtonsImageList(imageList); - m_ownsImageListButtons = TRUE; + m_ownsImageListButtons = true; } // ----------------------------------------------------------------------------- @@ -1831,7 +2258,20 @@ void wxGenericTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc) if ( item->IsSelected() ) { +// under mac selections are only a rectangle in case they don't have the focus +#ifdef __WXMAC__ + if ( !m_hasFocus ) + { + dc.SetBrush( *wxTRANSPARENT_BRUSH ) ; + dc.SetPen( wxPen( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) , 1 , wxSOLID ) ) ; + } + else + { + dc.SetBrush( *m_hilightBrush ) ; + } +#else dc.SetBrush(*(m_hasFocus ? m_hilightBrush : m_hilightUnfocusedBrush)); +#endif } else { @@ -1845,18 +2285,29 @@ void wxGenericTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc) int offset = HasFlag(wxTR_ROW_LINES) ? 1 : 0; - if ( item->IsSelected() && image != NO_IMAGE ) + if ( HasFlag(wxTR_FULL_ROW_HIGHLIGHT) ) { - // 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 ); + int x, y, w, h; + + DoGetPosition(&x, &y); + DoGetSize(&w, &h); + dc.DrawRectangle(x, item->GetY()+offset, w, total_h-offset); } else { - dc.DrawRectangle( item->GetX()-2, item->GetY()+offset, - item->GetWidth()+2, total_h-offset ); + if ( item->IsSelected() && 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 ); + } + else + { + dc.DrawRectangle( item->GetX()-2, item->GetY()+offset, + item->GetWidth()+2, total_h-offset ); + } } if ( image != NO_IMAGE ) @@ -1889,10 +2340,26 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level } else if (level == 0) { + // always expand hidden root + int origY = y; wxArrayGenericTreeItems& children = item->GetChildren(); - size_t n, count = children.Count(); - for (n = 0; n < count; ++n) - PaintLevel(children[n], dc, 1, y); + int count = children.Count(); + if (count > 0) + { + int n = 0, oldY; + do { + oldY = y; + PaintLevel(children[n], dc, 1, y); + } while (++n < count); + + if (!HasFlag(wxTR_NO_LINES) && HasFlag(wxTR_LINES_AT_ROOT) && count > 0) + { + // draw line down to last child + origY += GetLineHeight(children[0])>>1; + oldY += GetLineHeight(children[n-1])>>1; + dc.DrawLine(3, origY, 3, oldY); + } + } return; } @@ -1909,82 +2376,6 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level if (IsExposed(exposed_x, exposed_y, 10000, h)) // 10000 = very much { - if (item->HasPlus() && HasButtons()) // should the item show a button? - { - if (!HasFlag(wxTR_NO_LINES)) - { - if (x > (signed)m_indent || HasFlag(wxTR_LINES_AT_ROOT)) - dc.DrawLine(x - m_indent, y_mid, x - 5, y_mid); - dc.DrawLine(x + 5, y_mid, x + m_spacing, y_mid); - } - - if (m_imageListButtons != NULL) - { - // draw the image button here - int image_h = 0, image_w = 0, image = wxCLOSED_BUTTON; - if (item->IsExpanded()) image = wxOPEN_BUTTON; - if (item->IsSelected()) - image += wxOPEN_BUTTON_SELECTED - wxOPEN_BUTTON; - m_imageListButtons->GetSize(image, image_w, image_h); - int xx = x - (image_w>>1); - int yy = y_mid - (image_h>>1); - dc.SetClippingRegion(xx, yy, image_w, image_h); - m_imageListButtons->Draw(image, dc, xx, yy, - wxIMAGELIST_DRAW_TRANSPARENT); - dc.DestroyClippingRegion(); - } - else if (HasFlag(wxTR_TWIST_BUTTONS)) - { - // draw the twisty button here - dc.SetPen(*wxBLACK_PEN); - dc.SetBrush(*m_hilightBrush); - - wxPoint button[3]; - - if (item->IsExpanded()) - { - button[0].x = x-5; - button[0].y = y_mid-2; - button[1].x = x+5; - button[1].y = y_mid-2; - button[2].x = x; - button[2].y = y_mid+3; - } - else - { - button[0].y = y_mid-5; - button[0].x = x-2; - button[1].y = y_mid+5; - button[1].x = x-2; - button[2].y = y_mid; - button[2].x = x+3; - } - dc.DrawPolygon(3, button); - - dc.SetPen(m_dottedPen); - } - else // if (HasFlag(wxTR_HAS_BUTTONS)) - { - // draw the plus sign here - dc.SetPen(*wxGREY_PEN); - dc.SetBrush(*wxWHITE_BRUSH); - dc.DrawRectangle(x-5, y_mid-4, 11, 9); - dc.SetPen(*wxBLACK_PEN); - dc.DrawLine(x-2, y_mid, x+3, y_mid); - if (!item->IsExpanded()) - dc.DrawLine(x, y_mid-2, x, y_mid+3); - dc.SetPen(m_dottedPen); - } - } - else if (!HasFlag(wxTR_NO_LINES)) // no button; maybe a line? - { - // draw the horizontal line here - int x_start = x; - if (x > (signed)m_indent || HasFlag(wxTR_LINES_AT_ROOT)) - x_start -= m_indent; - dc.DrawLine(x_start, y_mid, x + m_spacing, y_mid); - } - wxPen *pen = #ifndef __WXMAC__ // don't draw rect outline if we already have the @@ -1996,7 +2387,7 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level wxColour colText; if ( item->IsSelected() ) { - colText = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHTTEXT); + colText = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT); } else { @@ -2004,7 +2395,7 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level if (attr && attr->HasTextColour()) colText = attr->GetTextColour(); else - colText = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOWTEXT); + colText = GetForegroundColour(); } // prepare to draw @@ -2016,7 +2407,10 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level if (HasFlag(wxTR_ROW_LINES)) { - dc.SetPen(*wxWHITE_PEN); + // if the background colour is white, choose a + // contrasting color for the lines + dc.SetPen(*((GetBackgroundColour() == *wxWHITE) + ? wxMEDIUM_GREY_PEN : wxWHITE_PEN)); dc.DrawLine(0, y_top, 10000, y_top); dc.DrawLine(0, y, 10000, y); } @@ -2025,6 +2419,61 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level dc.SetBrush(*wxWHITE_BRUSH); dc.SetPen(m_dottedPen); dc.SetTextForeground(*wxBLACK); + + if ( !HasFlag(wxTR_NO_LINES) ) + { + // draw the horizontal line here + int x_start = x; + if (x > (signed)m_indent) + x_start -= m_indent; + else if (HasFlag(wxTR_LINES_AT_ROOT)) + x_start = 3; + dc.DrawLine(x_start, y_mid, x + m_spacing, y_mid); + } + + // should the item show a button? + if ( item->HasPlus() && HasButtons() ) + { + if ( m_imageListButtons ) + { + // draw the image button here + int image_h = 0, + image_w = 0; + int image = item->IsExpanded() ? wxTreeItemIcon_Expanded + : wxTreeItemIcon_Normal; + if ( item->IsSelected() ) + image += wxTreeItemIcon_Selected - wxTreeItemIcon_Normal; + + m_imageListButtons->GetSize(image, image_w, image_h); + int xx = x - image_w/2; + int yy = y_mid - image_h/2; + + wxDCClipper clip(dc, xx, yy, image_w, image_h); + m_imageListButtons->Draw(image, dc, xx, yy, + wxIMAGELIST_DRAW_TRANSPARENT); + } + else // no custom buttons + { + 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 + ( + this, + dc, + wxRect(x - wImage/2, + y_mid - hImage/2, + wImage, hImage), + flag + ); + } + } } if (item->IsExpanded()) @@ -2040,12 +2489,28 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level PaintLevel(children[n], dc, level, y); } while (++n < count); - if (!HasFlag(wxTR_NO_LINES)) + if (!HasFlag(wxTR_NO_LINES) && count > 0) { // draw line down to last child - oldY += GetLineHeight(children[n-1])/2; + oldY += GetLineHeight(children[n-1])>>1; if (HasButtons()) y_mid += 5; - dc.DrawLine(x, y_mid, x, oldY); + + // Only draw the portion of the line that is visible, in case it is huge + wxCoord xOrigin=0, yOrigin=0, width, height; + dc.GetDeviceOrigin(&xOrigin, &yOrigin); + yOrigin = abs(yOrigin); + GetClientSize(&width, &height); + + // Move end points to the begining/end of the view? + if (y_mid < yOrigin) + y_mid = yOrigin; + if (oldY > yOrigin + height) + oldY = yOrigin + height; + + // after the adjustments if y_mid is larger than oldY then the line + // isn't visible at all so don't draw anything + if (y_mid < oldY) + dc.DrawLine(x, y_mid, x, oldY); } } } @@ -2064,7 +2529,7 @@ 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); @@ -2114,7 +2579,7 @@ void wxGenericTreeCtrl::DrawLine(const wxTreeItemId &item, bool below) } // ----------------------------------------------------------------------------- -// wxWindows callbacks +// wxWidgets callbacks // ----------------------------------------------------------------------------- void wxGenericTreeCtrl::OnPaint( wxPaintEvent &WXUNUSED(event) ) @@ -2138,7 +2603,7 @@ void wxGenericTreeCtrl::OnPaint( wxPaintEvent &WXUNUSED(event) ) void wxGenericTreeCtrl::OnSetFocus( wxFocusEvent &event ) { - m_hasFocus = TRUE; + m_hasFocus = true; RefreshSelected(); @@ -2147,7 +2612,7 @@ void wxGenericTreeCtrl::OnSetFocus( wxFocusEvent &event ) void wxGenericTreeCtrl::OnKillFocus( wxFocusEvent &event ) { - m_hasFocus = FALSE; + m_hasFocus = false; RefreshSelected(); @@ -2175,7 +2640,7 @@ 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); // + : Expand @@ -2188,7 +2653,9 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) // right : open if parent and go next // home : go to root // end : go to last item without opening parents - switch (event.KeyCode()) + // alnum : start or continue searching for the item with this prefix + int keyCode = event.GetKeyCode(); + switch ( keyCode ) { case '+': case WXK_ADD: @@ -2216,14 +2683,30 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) } break; + case WXK_MENU: + { + wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_MENU, GetId() ); + event.m_item = m_current; + event.SetEventObject( this ); + GetEventHandler()->ProcessEvent( event ); + break; + } case ' ': case WXK_RETURN: + if ( !event.HasModifiers() ) { wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() ); - event.m_item = (long) m_current; + event.m_item = m_current; event.SetEventObject( this ); GetEventHandler()->ProcessEvent( event ); } + + // in any case, also generate the normal key event for this key, + // even if we generated the ACTIVATED event above: this is what + // wxMSW does and it makes sense because you might not want to + // process ACTIVATED event at all and handle Space and Return + // directly (and differently) which would be impossible otherwise + event.Skip(); break; // up goes to the previous sibling or to the last @@ -2233,22 +2716,21 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) wxTreeItemId prev = GetPrevSibling( m_key_current ); if (!prev) { - prev = GetParent( m_key_current ); + prev = GetItemParent( m_key_current ); if ((prev == GetRootItem()) && HasFlag(wxTR_HIDE_ROOT)) { break; // don't go to root if it is hidden } if (prev) { - long cookie = 0; + 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? if (current == GetFirstChild( prev, cookie )) { // otherwise we return to where we came from - SelectItem( prev, unselect_others, extended_select ); + DoSelectItem( prev, unselect_others, extended_select ); m_key_current= (wxGenericTreeItem*) prev.m_pItem; - EnsureVisible( prev ); break; } } @@ -2264,9 +2746,8 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) } } - SelectItem( prev, unselect_others, extended_select ); + DoSelectItem( prev, unselect_others, extended_select ); m_key_current=(wxGenericTreeItem*) prev.m_pItem; - EnsureVisible( prev ); } } break; @@ -2274,7 +2755,7 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) // left arrow goes to the parent case WXK_LEFT: { - wxTreeItemId prev = GetParent( m_current ); + wxTreeItemId prev = GetItemParent( m_current ); if ((prev == GetRootItem()) && HasFlag(wxTR_HIDE_ROOT)) { // don't go to root if it is hidden @@ -2282,8 +2763,7 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) } if (prev) { - EnsureVisible( prev ); - SelectItem( prev, unselect_others, extended_select ); + DoSelectItem( prev, unselect_others, extended_select ); } } break; @@ -2298,11 +2778,10 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) { if (IsExpanded(m_key_current) && HasChildren(m_key_current)) { - long cookie = 0; + wxTreeItemIdValue cookie; wxTreeItemId child = GetFirstChild( m_key_current, cookie ); - SelectItem( child, unselect_others, extended_select ); + DoSelectItem( child, unselect_others, extended_select ); m_key_current=(wxGenericTreeItem*) child.m_pItem; - EnsureVisible( child ); } else { @@ -2310,17 +2789,16 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) if (!next) { wxTreeItemId current = m_key_current; - while (current && !next) + while (current.IsOk() && !next) { - current = GetParent( current ); + current = GetItemParent( current ); if (current) next = GetNextSibling( current ); } } if (next) { - SelectItem( next, unselect_others, extended_select ); + DoSelectItem( next, unselect_others, extended_select ); m_key_current=(wxGenericTreeItem*) next.m_pItem; - EnsureVisible( next ); } } } @@ -2337,7 +2815,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; @@ -2346,8 +2824,7 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) if ( last.IsOk() ) { - EnsureVisible( last ); - SelectItem( last, unselect_others, extended_select ); + DoSelectItem( last, unselect_others, extended_select ); } } break; @@ -2356,20 +2833,56 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) case WXK_HOME: { wxTreeItemId prev = GetRootItem(); - if (!prev) break; - if (HasFlag(wxTR_HIDE_ROOT)) + if (!prev) + break; + + if ( HasFlag(wxTR_HIDE_ROOT) ) { - long dummy; - prev = GetFirstChild(prev, dummy); - if (!prev) break; + wxTreeItemIdValue cookie; + prev = GetFirstChild(prev, cookie); + if (!prev) + break; } - EnsureVisible( prev ); - SelectItem( prev, unselect_others, extended_select ); + + DoSelectItem( prev, unselect_others, extended_select ); } break; default: - event.Skip(); + // do not use wxIsalnum() here + if ( !event.HasModifiers() && + ((keyCode >= '0' && keyCode <= '9') || + (keyCode >= 'a' && keyCode <= 'z') || + (keyCode >= 'A' && keyCode <= 'Z' ))) + { + // find the next item starting with the given prefix + wxChar ch = (wxChar)keyCode; + + wxTreeItemId id = FindItem(m_current, m_findPrefix + ch); + if ( !id.IsOk() ) + { + // no such item + break; + } + + SelectItem(id); + + m_findPrefix += ch; + + // also start the timer to reset the current prefix if the user + // doesn't press any more alnum keys soon -- we wouldn't want + // to use this prefix for a new item search + if ( !m_findTimer ) + { + m_findTimer = new wxTreeFindTimer(this); + } + + m_findTimer->Start(wxTreeFindTimer::DELAY, wxTIMER_ONE_SHOT); + } + else + { + event.Skip(); + } } } @@ -2393,14 +2906,8 @@ wxTreeItemId wxGenericTreeCtrl::HitTest(const wxPoint& point, int& flags) return wxTreeItemId(); } - wxClientDC dc(this); - PrepareDC(dc); - wxCoord x = dc.DeviceToLogicalX( point.x ); - wxCoord y = dc.DeviceToLogicalY( point.y ); - wxGenericTreeItem *hit = m_anchor->HitTest(wxPoint(x, y), - this, - flags, - 0 ); + wxGenericTreeItem *hit = m_anchor->HitTest(CalcUnscrolledPosition(point), + this, flags, 0); if (hit == NULL) { flags = wxTREE_HITTEST_NOWHERE; @@ -2411,10 +2918,10 @@ wxTreeItemId wxGenericTreeCtrl::HitTest(const wxPoint& point, int& flags) // get the bounding rectangle of the item (or of its label only) bool wxGenericTreeCtrl::GetBoundingRect(const wxTreeItemId& item, - wxRect& rect, - bool WXUNUSED(textOnly)) const + wxRect& rect, + bool WXUNUSED(textOnly)) const { - wxCHECK_MSG( item.IsOk(), FALSE, _T("invalid item in wxGenericTreeCtrl::GetBoundingRect") ); + wxCHECK_MSG( item.IsOk(), false, _T("invalid item in wxGenericTreeCtrl::GetBoundingRect") ); wxGenericTreeItem *i = (wxGenericTreeItem*) item.m_pItem; @@ -2427,92 +2934,136 @@ bool wxGenericTreeCtrl::GetBoundingRect(const wxTreeItemId& item, //rect.height = i->GetHeight(); rect.height = GetLineHeight(i); - return TRUE; + return true; } -/* **** */ - void wxGenericTreeCtrl::Edit( const wxTreeItemId& item ) { - if (!item.IsOk()) return; + wxCHECK_RET( item.IsOk(), _T("can't edit an invalid item") ); - m_currentEdit = (wxGenericTreeItem*) item.m_pItem; + wxGenericTreeItem *itemEdit = (wxGenericTreeItem *)item.m_pItem; wxTreeEvent te( wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, GetId() ); - te.m_item = (long) m_currentEdit; + te.m_item = itemEdit; te.SetEventObject( this ); - GetEventHandler()->ProcessEvent( te ); - - if (!te.IsAllowed()) return; + if ( GetEventHandler()->ProcessEvent( te ) && !te.IsAllowed() ) + { + // vetoed by user + return; + } // We have to call this here because the label in // question might just have been added and no screen // update taken place. - if (m_dirty) wxYieldIfNeeded(); - - wxString s = m_currentEdit->GetText(); - int x = m_currentEdit->GetX(); - int y = m_currentEdit->GetY(); - int w = m_currentEdit->GetWidth(); - int h = m_currentEdit->GetHeight(); - - int image_h = 0; - int image_w = 0; + if ( m_dirty ) +#if defined( __WXMSW__ ) || defined(__WXMAC__) + Update(); +#else + wxYieldIfNeeded(); +#endif - int image = m_currentEdit->GetCurrentImage(); - if ( image != NO_IMAGE ) - { - if ( m_imageListNormal ) - { - m_imageListNormal->GetSize( image, image_w, image_h ); - image_w += 4; - } - else - { - wxFAIL_MSG(_T("you must create an image list to use images!")); - } - } - x += image_w; - w -= image_w + 4; // I don't know why +4 is needed + m_textCtrl = new wxTreeTextCtrl(this, itemEdit); - wxClientDC dc(this); - PrepareDC( dc ); - x = dc.LogicalToDeviceX( x ); - y = dc.LogicalToDeviceY( y ); + m_textCtrl->SetFocus(); +} - wxTreeTextCtrl *text = new wxTreeTextCtrl(this, -1, - &m_renameAccept, - &m_renameRes, - this, - s, - wxPoint(x-4,y-4), - wxSize(w+11,h+8)); - text->SetFocus(); +// returns a pointer to the text edit control if the item is being +// edited, NULL otherwise (it's assumed that no more than one item may +// be edited simultaneously) +wxTextCtrl* wxGenericTreeCtrl::GetEditControl() const +{ + return m_textCtrl; } -void wxGenericTreeCtrl::OnRenameTimer() +bool wxGenericTreeCtrl::OnRenameAccept(wxGenericTreeItem *item, + const wxString& value) { - Edit( m_current ); + wxTreeEvent le( wxEVT_COMMAND_TREE_END_LABEL_EDIT, GetId() ); + le.m_item = item; + le.SetEventObject( this ); + le.m_label = value; + le.m_editCancelled = false; + + return !GetEventHandler()->ProcessEvent( le ) || le.IsAllowed(); } -void wxGenericTreeCtrl::OnRenameAccept() +void wxGenericTreeCtrl::OnRenameCancelled(wxGenericTreeItem *item) { - // TODO if the validator fails this causes a crash + // let owner know that the edit was cancelled wxTreeEvent le( wxEVT_COMMAND_TREE_END_LABEL_EDIT, GetId() ); - le.m_item = (long) m_currentEdit; + le.m_item = item; le.SetEventObject( this ); - le.m_label = m_renameRes; - GetEventHandler()->ProcessEvent( le ); + le.m_label = wxEmptyString; + le.m_editCancelled = true; - if (!le.IsAllowed()) return; + GetEventHandler()->ProcessEvent( le ); +} - SetItemText( m_currentEdit, m_renameRes ); +void wxGenericTreeCtrl::OnRenameTimer() +{ + Edit( m_current ); } void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) { 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, GetId()); + hevent.m_item = hoverItem; + hevent.SetEventObject(this); + + if ( GetEventHandler()->ProcessEvent(hevent) && hevent.IsAllowed() ) + { + SetToolTip(hevent.m_label); + } + } +#endif + // we process left mouse up event (enables in-place edit), right down // (pass to the user code), left dbl click (activate item) and // dragging/moving events for items drag-and-drop @@ -2528,21 +3079,14 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) return; } - wxClientDC dc(this); - PrepareDC(dc); - wxCoord x = dc.DeviceToLogicalX( event.GetX() ); - wxCoord y = dc.DeviceToLogicalY( event.GetY() ); - int flags = 0; - wxGenericTreeItem *item = m_anchor->HitTest( wxPoint(x,y), - this, - flags, - 0 ); + flags = 0; + wxGenericTreeItem *item = m_anchor->HitTest(pt, this, flags, 0); if ( event.Dragging() && !m_isDragging ) { if (m_dragCount == 0) - m_dragStart = wxPoint(x,y); + m_dragStart = pt; m_dragCount++; @@ -2557,8 +3101,9 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) : wxEVT_COMMAND_TREE_BEGIN_DRAG; wxTreeEvent nevent( command, GetId() ); - nevent.m_item = (long) m_current; + nevent.m_item = m_current; nevent.SetEventObject(this); + nevent.SetPoint(pt); // by default the dragging is not supported, the user code must // explicitly allow the event for it to take place @@ -2567,7 +3112,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 @@ -2580,7 +3125,7 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) if ( m_oldSelection ) { - m_oldSelection->SetHilight(FALSE); + m_oldSelection->SetHilight(false); RefreshLine(m_oldSelection); } } @@ -2588,7 +3133,7 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) CaptureMouse(); } } - else if ( event.Moving() ) + else if ( event.Dragging() ) { if ( item != m_dropTarget ) { @@ -2600,7 +3145,11 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) // highlight the current drop target if any DrawDropEffect(m_dropTarget); +#if defined(__WXMSW__) || defined(__WXMAC__) || defined(__WXGTK20__) + Update(); +#else wxYieldIfNeeded(); +#endif } } else if ( (event.LeftUp() || event.RightUp()) && m_isDragging ) @@ -2610,7 +3159,7 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) if ( m_oldSelection ) { - m_oldSelection->SetHilight(TRUE); + m_oldSelection->SetHilight(true); RefreshLine(m_oldSelection); m_oldSelection = (wxGenericTreeItem *)NULL; } @@ -2618,23 +3167,37 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) // generate the drag end event wxTreeEvent event(wxEVT_COMMAND_TREE_END_DRAG, GetId()); - event.m_item = (long) item; - event.m_pointDrag = wxPoint(x, y); + event.m_item = item; + event.m_pointDrag = pt; event.SetEventObject(this); (void)GetEventHandler()->ProcessEvent(event); - m_isDragging = FALSE; + m_isDragging = false; m_dropTarget = (wxGenericTreeItem *)NULL; ReleaseMouse(); SetCursor(m_oldCursor); +#if defined( __WXMSW__ ) || defined(__WXMAC__) + Update(); +#else 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; @@ -2643,29 +3206,56 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) if ( event.RightDown() ) { + // 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, GetId()); - nevent.m_item = (long) item; - CalcScrolledPosition(x, y, - &nevent.m_pointDrag.x, - &nevent.m_pointDrag.y); + nevent.m_item = item; + nevent.m_pointDrag = CalcScrolledPosition(pt); nevent.SetEventObject(this); - GetEventHandler()->ProcessEvent(nevent); + event.Skip(!GetEventHandler()->ProcessEvent(nevent)); } else if ( event.LeftUp() ) { + // this facilitates multiple-item drag-and-drop + + if (item && HasFlag(wxTR_MULTIPLE)) + { + wxArrayTreeItemIds selections; + size_t count = GetSelections(selections); + + if (count > 1 && + !event.CmdDown() && + !event.ShiftDown()) + { + DoSelectItem(item, true, false); + } + } + if ( m_lastOnSame ) { if ( (item == m_current) && (flags & wxTREE_HITTEST_ONITEMLABEL) && HasFlag(wxTR_EDIT_LABELS) ) { - if ( m_renameTimer->IsRunning() ) - m_renameTimer->Stop(); + if ( m_renameTimer ) + { + if ( m_renameTimer->IsRunning() ) + m_renameTimer->Stop(); + } + else + { + m_renameTimer = new wxTreeRenameTimer( this ); + } - m_renameTimer->Start( 100, TRUE ); + m_renameTimer->Start( wxTreeRenameTimer::DELAY, true ); } - m_lastOnSame = FALSE; + m_lastOnSame = false; } } else // !RightDown() && !LeftUp() ==> LeftDown() || LeftDClick() @@ -2688,29 +3278,39 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) return; } - // 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); - SelectItem(item, unselect_others, extended_select); + // clear the previously selected items, if the + // user clicked outside of the present selection. + // otherwise, perform the deselection on mouse-up. + // this allows multiple drag and drop to work. + // 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.CmdDown(), + is_multiple, extended_select, unselect_others); + + DoSelectItem(item, unselect_others, extended_select); + } + // For some reason, Windows isn't recognizing a left double-click, // so we need to simulate it here. Allow 200 milliseconds for now. if ( event.LeftDClick() ) { // double clicking should not start editing the item label - m_renameTimer->Stop(); - m_lastOnSame = FALSE; + if ( m_renameTimer ) + m_renameTimer->Stop(); + + m_lastOnSame = false; // send activate event first wxTreeEvent nevent( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() ); - nevent.m_item = (long) item; - CalcScrolledPosition(x, y, - &nevent.m_pointDrag.x, - &nevent.m_pointDrag.y); + nevent.m_item = item; + nevent.m_pointDrag = CalcScrolledPosition(pt); nevent.SetEventObject( this ); if ( !GetEventHandler()->ProcessEvent( nevent ) ) { @@ -2727,14 +3327,29 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) } } -void wxGenericTreeCtrl::OnIdle( wxIdleEvent &WXUNUSED(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 + // as required, when a first item is selected. + if (!HasFlag(wxTR_MULTIPLE) && !GetSelection().IsOk()) + { + if (m_select_me) + SelectItem(m_select_me); + else if (GetRootItem().IsOk()) + SelectItem(GetRootItem()); + } + /* after all changes have been done to the tree control, * we actually redraw the tree when everything is over */ if (!m_dirty) return; + if (m_freezeCount) return; - m_dirty = FALSE; + m_dirty = false; CalculatePositions(); Refresh(); @@ -2746,8 +3361,13 @@ void wxGenericTreeCtrl::CalculateSize( wxGenericTreeItem *item, wxDC &dc ) wxCoord text_w = 0; wxCoord text_h = 0; - if (item->IsBold()) + wxTreeItemAttr *attr = item->GetAttributes(); + if ( attr && attr->HasFont() ) + dc.SetFont(attr->GetFont()); + else if ( item->IsBold() ) dc.SetFont(m_boldFont); + else + dc.SetFont(m_normalFont); dc.GetTextExtent( item->GetText(), &text_w, &text_h ); text_h+=2; @@ -2839,21 +3459,16 @@ void wxGenericTreeCtrl::CalculatePositions() void wxGenericTreeCtrl::RefreshSubtree(wxGenericTreeItem *item) { if (m_dirty) return; + if (m_freezeCount) return; - wxClientDC dc(this); - PrepareDC(dc); - - int cw = 0; - int ch = 0; - GetClientSize( &cw, &ch ); + wxSize client = GetClientSize(); wxRect rect; - rect.x = dc.LogicalToDeviceX( 0 ); - rect.width = cw; - rect.y = dc.LogicalToDeviceY( item->GetY() ); - rect.height = ch; + CalcScrolledPosition(0, item->GetY(), NULL, &rect.y); + rect.width = client.x; + rect.height = client.y; - Refresh( TRUE, &rect ); + Refresh(true, &rect); AdjustMyScrollbars(); } @@ -2861,25 +3476,20 @@ void wxGenericTreeCtrl::RefreshSubtree(wxGenericTreeItem *item) void wxGenericTreeCtrl::RefreshLine( wxGenericTreeItem *item ) { if (m_dirty) return; - - wxClientDC dc(this); - PrepareDC( dc ); - - int cw = 0; - int ch = 0; - GetClientSize( &cw, &ch ); + if (m_freezeCount) return; wxRect rect; - rect.x = dc.LogicalToDeviceX( 0 ); - rect.y = dc.LogicalToDeviceY( item->GetY() ); - rect.width = cw; + CalcScrolledPosition(0, item->GetY(), NULL, &rect.y); + rect.width = GetClientSize().x; rect.height = GetLineHeight(item); //dc.GetCharHeight() + 6; - Refresh( TRUE, &rect ); + Refresh(true, &rect); } void wxGenericTreeCtrl::RefreshSelected() { + if (m_freezeCount) return; + // TODO: this is awfully inefficient, we should keep the list of all // selected items internally, should be much faster if ( m_anchor ) @@ -2888,6 +3498,8 @@ void wxGenericTreeCtrl::RefreshSelected() void wxGenericTreeCtrl::RefreshSelectedUnder(wxGenericTreeItem *item) { + if (m_freezeCount) return; + if ( item->IsSelected() ) RefreshLine(item); @@ -2899,6 +3511,21 @@ void wxGenericTreeCtrl::RefreshSelectedUnder(wxGenericTreeItem *item) } } +void wxGenericTreeCtrl::Freeze() +{ + m_freezeCount++; +} + +void wxGenericTreeCtrl::Thaw() +{ + wxCHECK_RET( m_freezeCount > 0, _T("thawing unfrozen tree control?") ); + + if ( !--m_freezeCount ) + { + Refresh(); + } +} + // ---------------------------------------------------------------------------- // changing colours: we need to refresh the tree control // ---------------------------------------------------------------------------- @@ -2906,21 +3533,93 @@ void wxGenericTreeCtrl::RefreshSelectedUnder(wxGenericTreeItem *item) bool wxGenericTreeCtrl::SetBackgroundColour(const wxColour& colour) { if ( !wxWindow::SetBackgroundColour(colour) ) - return FALSE; + return false; + + if (m_freezeCount) return true; Refresh(); - return TRUE; + return true; } bool wxGenericTreeCtrl::SetForegroundColour(const wxColour& colour) { if ( !wxWindow::SetForegroundColour(colour) ) - return FALSE; + return false; + + if (m_freezeCount) return true; 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(); +} + + +wxSize wxGenericTreeCtrl::DoGetBestSize() const +{ + // something is better than nothing... + // 100x80 is what the MSW version will get from the default + // wxControl::DoGetBestSize + return wxSize(100,80); +} + + +// 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 + +#if _USE_VISATTR +#include "wx/listbox.h" +#endif + +//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_WINDOWTEXT); + attr.colBg = wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX); + attr.font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + return attr; +#endif +} + +#if WXWIN_COMPATIBILITY_2_4 + +int wxGenericTreeCtrl::GetItemSelectedImage(const wxTreeItemId& item) const +{ + return GetItemImage(item, wxTreeItemIcon_Selected); +} + +void wxGenericTreeCtrl::SetItemSelectedImage(const wxTreeItemId& item, int image) +{ + SetItemImage(item, image, wxTreeItemIcon_Selected); +} + +#endif // WXWIN_COMPATIBILITY_2_4 + +#if WXWIN_COMPATIBILITY_2_2 + +wxTreeItemId wxGenericTreeCtrl::GetParent(const wxTreeItemId& item) const +{ + return GetItemParent( item ); } +#endif // WXWIN_COMPATIBILITY_2_2 + #endif // wxUSE_TREECTRL