X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/02fe25c21465c93cdae17b7e14c9b707f98623c1..a45068485c8fbc9db4daf77c724747992f81a90e:/src/generic/treectlg.cpp?ds=sidebyside diff --git a/src/generic/treectlg.cpp b/src/generic/treectlg.cpp index c7732185bc..61cfce6b52 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,13 +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_EXPORTED_ARRAY_PTR(wxGenericTreeItem *, wxArrayGenericTreeItems); // ---------------------------------------------------------------------------- // constants @@ -54,54 +60,6 @@ static const int NO_IMAGE = -1; static const int PIXELS_PER_UNIT = 10; -// ---------------------------------------------------------------------------- -// Aqua arrows -// ---------------------------------------------------------------------------- - -/* XPM */ -static const char *aqua_arrow_right[] = { -/* columns rows colors chars-per-pixel */ -"13 11 4 1", -" c None", -"b c #C0C0C0", -"c c #707070", -"d c #A0A0A0", -/* pixels */ -" b ", -" ddb ", -" cccdb ", -" cccccd ", -" ccccccdb ", -" ccccccccd", -" ccccccdb ", -" cccccb ", -" cccdb ", -" ddb ", -" b " -}; - -/* XPM */ -static const char *aqua_arrow_down[] = { -/* columns rows colors chars-per-pixel */ -"13 11 4 1", -" c None", -"b c #C0C0C0", -"c c #707070", -"d c #A0A0A0", -/* pixels */ -" ", -" ", -" bdcccccccdb ", -" dcccccccd ", -" bcccccccb ", -" dcccccd ", -" bcccccb ", -" bcccd ", -" dcd ", -" bcb ", -" d " -}; - // ----------------------------------------------------------------------------- // private classes // ----------------------------------------------------------------------------- @@ -120,6 +78,8 @@ public: private: wxGenericTreeCtrl *m_owner; + + DECLARE_NO_COPY_CLASS(wxTreeRenameTimer) }; // control used for in-place edit @@ -128,11 +88,25 @@ class WXDLLEXPORT wxTreeTextCtrl: public wxTextCtrl public: wxTreeTextCtrl(wxGenericTreeCtrl *owner, wxGenericTreeItem *item); + // wxGenericTreeCtrl can use this one to abandon editing the given item, + // it's not an error to call it if this item is not being edited + void StopEditing(wxGenericTreeItem *item) + { + if ( item == m_itemEdited ) + DoStopEditing(); + } + protected: void OnChar( wxKeyEvent &event ); void OnKeyUp( wxKeyEvent &event ); void OnKillFocus( wxFocusEvent &event ); + void DoStopEditing() + { + Finish(); + m_owner->OnRenameCancelled(m_itemEdited); + } + bool AcceptChanges(); void Finish(); @@ -143,6 +117,7 @@ private: bool m_finished; DECLARE_EVENT_TABLE() + DECLARE_NO_COPY_CLASS(wxTreeTextCtrl) }; // timer used to clear wxGenericTreeCtrl::m_findPrefix if no key was pressed @@ -159,6 +134,8 @@ public: private: wxGenericTreeCtrl *m_owner; + + DECLARE_NO_COPY_CLASS(wxTreeFindTimer) }; // a tree item @@ -191,7 +168,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; } @@ -215,7 +192,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); } @@ -223,17 +200,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(); } @@ -251,7 +228,7 @@ public: if ( !m_attr ) { m_attr = new wxTreeItemAttr; - m_ownsAttr = TRUE; + m_ownsAttr = true; } return *m_attr; } @@ -260,13 +237,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: @@ -287,7 +264,7 @@ private: short m_images[wxTreeItemIcon_Max]; wxCoord m_x; // (virtual) offset from top - short m_y; // (virtual) offset from left + wxCoord m_y; // (virtual) offset from left short m_width; // width of this item unsigned char m_height; // height of this item @@ -298,6 +275,8 @@ private: // children but has a [+] button int m_isBold :1; // render the label in bold font int m_ownsAttr :1; // delete attribute when done + + DECLARE_NO_COPY_CLASS(wxGenericTreeItem) }; // ============================================================================= @@ -322,6 +301,23 @@ static void EventFlagsToSelType(long style, unselect_others = !(extended_select || (ctrlDown && is_multiple)); } +// check if the given item is under another one +static bool IsDescendantOf(wxGenericTreeItem *parent, wxGenericTreeItem *item) +{ + while ( item ) + { + if ( item == parent ) + { + // item is a descendant of parent + return true; + } + + item = item->GetParent(); + } + + return false; +} + // ----------------------------------------------------------------------------- // wxTreeRenameTimer (internal) // ----------------------------------------------------------------------------- @@ -351,7 +347,7 @@ wxTreeTextCtrl::wxTreeTextCtrl(wxGenericTreeCtrl *owner, : m_itemEdited(item), m_startValue(item->GetText()) { m_owner = owner; - m_finished = FALSE; + m_finished = false; int w = m_itemEdited->GetWidth(), h = m_itemEdited->GetHeight(); @@ -391,28 +387,30 @@ bool wxTreeTextCtrl::AcceptChanges() if ( value == m_startValue ) { // nothing changed, always accept - return TRUE; + 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() { if ( !m_finished ) { + m_owner->ResetTextControl(); + wxPendingDelete.Append(this); - m_finished = TRUE; + m_finished = true; m_owner->SetFocus(); // This doesn't work. TODO. } @@ -423,15 +421,16 @@ void wxTreeTextCtrl::OnChar( wxKeyEvent &event ) switch ( event.m_keyCode ) { case WXK_RETURN: - if ( !AcceptChanges() ) + if ( AcceptChanges() ) { - // vetoed by the user, don't disappear - break; + // Close the text control, changes were accepted + Finish(); } - //else: fall through + // else do nothing, do not accept and do not close + break; case WXK_ESCAPE: - Finish(); + DoStopEditing(); break; default: @@ -453,7 +452,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(); @@ -461,16 +460,17 @@ void wxTreeTextCtrl::OnKeyUp( wxKeyEvent &event ) void wxTreeTextCtrl::OnKillFocus( wxFocusEvent &event ) { - if ( m_finished ) - { - event.Skip(); - return; - } - - if ( AcceptChanges() ) + if ( !m_finished ) { + AcceptChanges(); + // We must finish regardless of success, otherwise we'll get + // focus problems: Finish(); } + + // We must let the native text control handle focus, too, otherwise + // it could have problems with the cursor (e.g., in wxGTK): + event.Skip(); } // ----------------------------------------------------------------------------- @@ -491,15 +491,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; @@ -588,11 +588,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; @@ -685,10 +693,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. @@ -703,9 +711,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; @@ -732,23 +740,33 @@ 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_lastOnSame = FALSE; + m_lastOnSame = false; +#if defined( __WXMAC__ ) && __WXMAC_CARBON__ + m_normalFont.MacCreateThemeFont( kThemeViewsFont ) ; +#else m_normalFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ); - m_boldFont = wxFont( m_normalFont.GetPointSize(), - m_normalFont.GetFamily(), - m_normalFont.GetStyle(), - wxBOLD, - m_normalFont.GetUnderlined()); +#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, @@ -756,33 +774,18 @@ 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 ); - if (style & wxTR_HAS_BUTTONS) style |= wxTR_MAC_BUTTONS; - if (style & wxTR_HAS_BUTTONS) style &= ~wxTR_HAS_BUTTONS; style &= ~wxTR_LINES_AT_ROOT; style |= wxTR_NO_LINES; if (major < 10) style |= wxTR_ROW_LINES; - if (major >= 10) - style |= wxTR_AQUA_BUTTONS; -#endif - - if (style & wxTR_AQUA_BUTTONS) - { - m_arrowRight = new wxBitmap( aqua_arrow_right ); - m_arrowDown = new wxBitmap( aqua_arrow_down ); - } - else - { - m_arrowRight = NULL; - m_arrowDown = NULL; - } +#endif // __WXMAC__ wxScrolledWindow::Create( parent, id, pos, size, style|wxHSCROLL|wxVSCROLL, name ); @@ -800,13 +803,18 @@ bool wxGenericTreeCtrl::Create(wxWindow *parent, SetValidator( validator ); #endif - SetForegroundColour( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) ); - SetBackgroundColour( wxSystemSettings::GetColour(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( wxT("grey"), 0, 0 ); - return TRUE; + SetBestSize(size); + + return true; } wxGenericTreeCtrl::~wxGenericTreeCtrl() @@ -814,9 +822,6 @@ wxGenericTreeCtrl::~wxGenericTreeCtrl() delete m_hilightBrush; delete m_hilightUnfocusedBrush; - delete m_arrowRight; - delete m_arrowDown; - DeleteAllItems(); delete m_renameTimer; @@ -836,22 +841,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 = (unsigned short) indent; - m_dirty = TRUE; + 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") ); @@ -872,7 +892,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; } // ----------------------------------------------------------------------------- @@ -901,6 +921,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") ); @@ -930,6 +974,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); } @@ -989,13 +1036,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; } @@ -1005,7 +1054,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; @@ -1013,7 +1062,7 @@ bool wxGenericTreeCtrl::IsVisible(const wxTreeItemId& item) const while (parent) { if (!parent->IsExpanded()) - return FALSE; + return false; parent = parent->GetParent(); } @@ -1024,20 +1073,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 @@ -1049,21 +1098,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(); } @@ -1072,14 +1121,15 @@ 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") ); @@ -1087,7 +1137,40 @@ 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, + 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") ); + + cookie = 0; + return GetNextChild(item, cookie); +} + +wxTreeItemId wxGenericTreeCtrl::GetNextChild(const wxTreeItemId& item, + long& cookie) const { wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); @@ -1103,6 +1186,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") ); @@ -1172,7 +1257,7 @@ wxTreeItemId wxGenericTreeCtrl::GetNext(const wxTreeItemId& item) const do { toFind = GetNextSibling(p); - p = GetParent(p); + p = GetItemParent(p); } while (p.IsOk() && !toFind.IsOk()); return toFind; } @@ -1219,6 +1304,12 @@ wxTreeItemId wxGenericTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const return wxTreeItemId(); } +// called by wxTextTreeCtrl when it marks itself for deletion +void wxGenericTreeCtrl::ResetTextControl() +{ + m_textCtrl = NULL; +} + // find the first item starting with the given prefix after the given item wxTreeItemId wxGenericTreeCtrl::FindItem(const wxTreeItemId& idParent, const wxString& prefixOrig) const @@ -1282,14 +1373,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 wxGenericTreeItem *item = new wxGenericTreeItem( parent, text, image, selImage, data ); if ( data != NULL ) { - data->m_pItem = (long) item; + data->m_pItem = item; } parent->Insert( item, previous ); @@ -1303,13 +1394,13 @@ 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 m_anchor = new wxGenericTreeItem((wxGenericTreeItem *)NULL, text, image, selImage, data); if ( data != NULL ) { - data->m_pItem = (long) m_anchor; + data->m_pItem = m_anchor; } if (HasFlag(wxTR_HIDE_ROOT)) @@ -1324,7 +1415,7 @@ 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 ); } return m_anchor; @@ -1397,14 +1488,14 @@ 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 ); } 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; item->DeleteChildren(this); @@ -1412,37 +1503,59 @@ void wxGenericTreeCtrl::DeleteChildren(const wxTreeItemId& itemId) 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 ) { - 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(item); } 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); delete item; @@ -1452,12 +1565,7 @@ void wxGenericTreeCtrl::DeleteAllItems() { if ( m_anchor ) { - m_dirty = TRUE; - - m_anchor->DeleteChildren(this); - delete m_anchor; - - m_anchor = NULL; + Delete(m_anchor); } } @@ -1476,7 +1584,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() ) @@ -1496,17 +1604,20 @@ void wxGenericTreeCtrl::Expand(const wxTreeItemId& itemId) void wxGenericTreeCtrl::ExpandAll(const wxTreeItemId& item) { - Expand(item); - if ( IsExpanded(item) ) + if ( !HasFlag(wxTR_HIDE_ROOT) || item != GetRootItem()) { - long cookie; - wxTreeItemId child = GetFirstChild(item, cookie); - while ( child.IsOk() ) - { - ExpandAll(child); + Expand(item); + if ( !IsExpanded(item) ) + return; + } - child = GetNextChild(item, cookie); - } + wxTreeItemIdValue cookie; + wxTreeItemId child = GetFirstChild(item, cookie); + while ( child.IsOk() ) + { + ExpandAll(child); + + child = GetNextChild(item, cookie); } } @@ -1521,7 +1632,7 @@ void wxGenericTreeCtrl::Collapse(const wxTreeItemId& itemId) 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() ) { @@ -1568,10 +1679,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; } } @@ -1579,7 +1691,7 @@ void wxGenericTreeCtrl::UnselectAllChildren(wxGenericTreeItem *item) { if (item->IsSelected()) { - item->SetHilight(FALSE); + item->SetHilight(false); RefreshLine(item); } @@ -1625,7 +1737,7 @@ bool wxGenericTreeCtrl::TagNextChildren(wxGenericTreeItem *crt_item, wxGenericTr size_t count = children.Count(); for (size_t n=(size_t)(index+1); nHasChildren()) { @@ -1646,29 +1758,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(); @@ -1678,12 +1782,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; @@ -1695,8 +1801,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() ) { @@ -1707,21 +1813,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 ); @@ -1746,7 +1852,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) @@ -1761,6 +1867,22 @@ 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 { @@ -1791,6 +1913,8 @@ 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; @@ -1800,7 +1924,7 @@ void wxGenericTreeCtrl::EnsureVisible(const wxTreeItemId& item) if ( HasFlag(wxTR_HIDE_ROOT) ) { - while ( parent != m_anchor ) + while ( parent && parent != m_anchor ) { Expand(parent); parent = parent->GetParent(); @@ -1827,8 +1951,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 @@ -1899,7 +2027,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); @@ -1966,8 +2094,8 @@ 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; // 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) @@ -1978,34 +2106,34 @@ 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; } // ----------------------------------------------------------------------------- @@ -2070,7 +2198,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 { @@ -2219,94 +2360,60 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level dc.SetPen(m_dottedPen); dc.SetTextForeground(*wxBLACK); - if (item->HasPlus() && HasButtons()) // should the item show a button? + if ( !HasFlag(wxTR_NO_LINES) ) { - if (!HasFlag(wxTR_NO_LINES)) - { - if (x > (signed)m_indent) - dc.DrawLine(x - m_indent, y_mid, x - 5, y_mid); - else if (HasFlag(wxTR_LINES_AT_ROOT)) - dc.DrawLine(3, y_mid, x - 5, y_mid); - dc.DrawLine(x + 5, y_mid, x + m_spacing, y_mid); - } + // 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); + } - if (m_imageListButtons != NULL) + // 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, image = wxTreeItemIcon_Normal; - if (item->IsExpanded()) image = wxTreeItemIcon_Expanded; - if (item->IsSelected()) + 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>>1); - int yy = y_mid - (image_h>>1); - dc.SetClippingRegion(xx, yy, 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); - dc.DestroyClippingRegion(); - } - else if (HasFlag(wxTR_TWIST_BUTTONS)) - { - // draw the twisty button here - - if (HasFlag(wxTR_AQUA_BUTTONS)) - { - if (item->IsExpanded()) - dc.DrawBitmap( *m_arrowDown, x-5, y_mid-6, TRUE ); - else - dc.DrawBitmap( *m_arrowRight, x-5, y_mid-6, TRUE ); - } - else - { - dc.SetBrush(*m_hilightBrush); - dc.SetPen(*wxBLACK_PEN); - 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)) + else // no custom 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); + 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 + ); } } - 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) - 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); - } } if (item->IsExpanded()) @@ -2327,7 +2434,23 @@ void wxGenericTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level // draw line down to last child oldY += GetLineHeight(children[n-1])>>1; if (HasButtons()) y_mid += 5; - dc.DrawLine(x, y_mid, x, oldY); + + // Only draw the portion of the line that is visible, in case it is huge + wxCoord xOrigin=0, yOrigin=0, width, height; + dc.GetDeviceOrigin(&xOrigin, &yOrigin); + yOrigin = abs(yOrigin); + GetClientSize(&width, &height); + + // Move end points to the begining/end of the view? + if (y_mid < yOrigin) + y_mid = yOrigin; + if (oldY > yOrigin + height) + oldY = yOrigin + height; + + // after the adjustments if y_mid is larger than oldY then the line + // isn't visible at all so don't draw anything + if (y_mid < oldY) + dc.DrawLine(x, y_mid, x, oldY); } } } @@ -2346,7 +2469,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, true /* below */); } SetCursor(wxCURSOR_BULLSEYE); @@ -2396,7 +2519,7 @@ void wxGenericTreeCtrl::DrawLine(const wxTreeItemId &item, bool below) } // ----------------------------------------------------------------------------- -// wxWindows callbacks +// wxWidgets callbacks // ----------------------------------------------------------------------------- void wxGenericTreeCtrl::OnPaint( wxPaintEvent &WXUNUSED(event) ) @@ -2420,7 +2543,7 @@ void wxGenericTreeCtrl::OnPaint( wxPaintEvent &WXUNUSED(event) ) void wxGenericTreeCtrl::OnSetFocus( wxFocusEvent &event ) { - m_hasFocus = TRUE; + m_hasFocus = true; RefreshSelected(); @@ -2429,7 +2552,7 @@ void wxGenericTreeCtrl::OnSetFocus( wxFocusEvent &event ) void wxGenericTreeCtrl::OnKillFocus( wxFocusEvent &event ) { - m_hasFocus = FALSE; + m_hasFocus = false; RefreshSelected(); @@ -2471,7 +2594,7 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) // home : go to root // end : go to last item without opening parents // alnum : start or continue searching for the item with this prefix - int keyCode = event.KeyCode(); + int keyCode = event.GetKeyCode(); switch ( keyCode ) { case '+': @@ -2502,12 +2625,20 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) case ' ': case WXK_RETURN: + if ( !event.HasModifiers() ) { wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() ); - event.m_item = (long) m_current; + event.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 @@ -2517,20 +2648,20 @@ 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; break; } @@ -2547,7 +2678,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; } } @@ -2556,7 +2687,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 @@ -2564,7 +2695,7 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) } if (prev) { - SelectItem( prev, unselect_others, extended_select ); + DoSelectItem( prev, unselect_others, extended_select ); } } break; @@ -2579,9 +2710,9 @@ 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; } else @@ -2590,15 +2721,15 @@ 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; } } @@ -2616,7 +2747,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; @@ -2625,7 +2756,7 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) if ( last.IsOk() ) { - SelectItem( last, unselect_others, extended_select ); + DoSelectItem( last, unselect_others, extended_select ); } } break; @@ -2639,27 +2770,27 @@ void wxGenericTreeCtrl::OnChar( wxKeyEvent &event ) if ( HasFlag(wxTR_HIDE_ROOT) ) { - long dummy; - prev = GetFirstChild(prev, dummy); + wxTreeItemIdValue cookie; + prev = GetFirstChild(prev, cookie); if (!prev) break; } - SelectItem( prev, unselect_others, extended_select ); + DoSelectItem( prev, unselect_others, extended_select ); } break; default: // do not use wxIsalnum() here - if ( !event.HasModifiers() && + if ( !event.HasModifiers() && ((keyCode >= '0' && keyCode <= '9') || (keyCode >= 'a' && keyCode <= 'z') || (keyCode >= 'A' && keyCode <= 'Z' ))) { // find the next item starting with the given prefix - char ch = (char)keyCode; - - wxTreeItemId id = FindItem(m_current, m_findPrefix + (wxChar)ch); + wxChar ch = (wxChar)keyCode; + + wxTreeItemId id = FindItem(m_current, m_findPrefix + ch); if ( !id.IsOk() ) { // no such item @@ -2722,7 +2853,7 @@ bool wxGenericTreeCtrl::GetBoundingRect(const wxTreeItemId& item, 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; @@ -2735,7 +2866,7 @@ bool wxGenericTreeCtrl::GetBoundingRect(const wxTreeItemId& item, //rect.height = i->GetHeight(); rect.height = GetLineHeight(i); - return TRUE; + return true; } void wxGenericTreeCtrl::Edit( const wxTreeItemId& item ) @@ -2745,7 +2876,7 @@ void wxGenericTreeCtrl::Edit( const wxTreeItemId& item ) wxGenericTreeItem *itemEdit = (wxGenericTreeItem *)item.m_pItem; wxTreeEvent te( wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, GetId() ); - te.m_item = (long) itemEdit; + te.m_item = itemEdit; te.SetEventObject( this ); if ( GetEventHandler()->ProcessEvent( te ) && !te.IsAllowed() ) { @@ -2757,24 +2888,49 @@ void wxGenericTreeCtrl::Edit( const wxTreeItemId& item ) // question might just have been added and no screen // update taken place. if ( m_dirty ) +#if defined( __WXMSW__ ) || defined(__WXMAC__) + Update(); +#else wxYieldIfNeeded(); +#endif + + m_textCtrl = new wxTreeTextCtrl(this, itemEdit); - wxTreeTextCtrl *text = new wxTreeTextCtrl(this, itemEdit); + m_textCtrl->SetFocus(); +} - 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; } bool wxGenericTreeCtrl::OnRenameAccept(wxGenericTreeItem *item, const wxString& value) { wxTreeEvent le( wxEVT_COMMAND_TREE_END_LABEL_EDIT, GetId() ); - le.m_item = (long) item; + le.m_item = item; le.SetEventObject( this ); le.m_label = value; + le.m_editCancelled = false; return !GetEventHandler()->ProcessEvent( le ) || le.IsAllowed(); } +void wxGenericTreeCtrl::OnRenameCancelled(wxGenericTreeItem *item) +{ + // let owner know that the edit was cancelled + wxTreeEvent le( wxEVT_COMMAND_TREE_END_LABEL_EDIT, GetId() ); + le.m_item = item; + le.SetEventObject( this ); + le.m_label = wxEmptyString; + le.m_editCancelled = true; + + GetEventHandler()->ProcessEvent( le ); +} + void wxGenericTreeCtrl::OnRenameTimer() { Edit( m_current ); @@ -2784,6 +2940,62 @@ 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 @@ -2799,9 +3011,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 ) @@ -2822,7 +3033,7 @@ 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); // by default the dragging is not supported, the user code must @@ -2832,7 +3043,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 @@ -2845,7 +3056,7 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) if ( m_oldSelection ) { - m_oldSelection->SetHilight(FALSE); + m_oldSelection->SetHilight(false); RefreshLine(m_oldSelection); } } @@ -2865,7 +3076,11 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) // highlight the current drop target if any DrawDropEffect(m_dropTarget); +#if defined( __WXMSW__ ) || defined(__WXMAC__) + Update(); +#else wxYieldIfNeeded(); +#endif } } else if ( (event.LeftUp() || event.RightUp()) && m_isDragging ) @@ -2875,7 +3090,7 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) if ( m_oldSelection ) { - m_oldSelection->SetHilight(TRUE); + m_oldSelection->SetHilight(true); RefreshLine(m_oldSelection); m_oldSelection = (wxGenericTreeItem *)NULL; } @@ -2883,20 +3098,24 @@ 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_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 { @@ -2909,13 +3128,28 @@ void wxGenericTreeCtrl::OnMouse( wxMouseEvent &event ) if ( event.RightDown() ) { wxTreeEvent nevent(wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK, GetId()); - nevent.m_item = (long) item; + nevent.m_item = item; nevent.m_pointDrag = CalcScrolledPosition(pt); nevent.SetEventObject(this); 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.ControlDown() && + !event.ShiftDown()) + { + DoSelectItem(item, true, false); + } + } + if ( m_lastOnSame ) { if ( (item == m_current) && @@ -2932,10 +3166,10 @@ 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() @@ -2958,14 +3192,24 @@ 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. + + if (!IsSelected(item)) + { + // 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); + + 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. @@ -2975,11 +3219,11 @@ 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 = (long) item; + nevent.m_item = item; nevent.m_pointDrag = CalcScrolledPosition(pt); nevent.SetEventObject( this ); if ( !GetEventHandler()->ProcessEvent( nevent ) ) @@ -2997,14 +3241,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(); @@ -3021,6 +3280,8 @@ void wxGenericTreeCtrl::CalculateSize( wxGenericTreeItem *item, wxDC &dc ) 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; @@ -3112,6 +3373,7 @@ void wxGenericTreeCtrl::CalculatePositions() void wxGenericTreeCtrl::RefreshSubtree(wxGenericTreeItem *item) { if (m_dirty) return; + if (m_freezeCount) return; wxSize client = GetClientSize(); @@ -3120,7 +3382,7 @@ void wxGenericTreeCtrl::RefreshSubtree(wxGenericTreeItem *item) rect.width = client.x; rect.height = client.y; - Refresh(TRUE, &rect); + Refresh(true, &rect); AdjustMyScrollbars(); } @@ -3128,17 +3390,20 @@ void wxGenericTreeCtrl::RefreshSubtree(wxGenericTreeItem *item) void wxGenericTreeCtrl::RefreshLine( wxGenericTreeItem *item ) { if (m_dirty) return; + if (m_freezeCount) 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 (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 ) @@ -3147,6 +3412,8 @@ void wxGenericTreeCtrl::RefreshSelected() void wxGenericTreeCtrl::RefreshSelectedUnder(wxGenericTreeItem *item) { + if (m_freezeCount) return; + if ( item->IsSelected() ) RefreshLine(item); @@ -3158,6 +3425,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 // ---------------------------------------------------------------------------- @@ -3165,21 +3447,61 @@ 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(); +} + + +// 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 } #endif // wxUSE_TREECTRL