X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/afff720b28d92364a9d88e214deddf831a60c1c2..ee3510132b8b4160f848bd4f502a6f5315f90dbc:/src/msw/treectrl.cpp diff --git a/src/msw/treectrl.cpp b/src/msw/treectrl.cpp index 63f54e57d3..c5c49719b4 100644 --- a/src/msw/treectrl.cpp +++ b/src/msw/treectrl.cpp @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: treectrl.cpp +// Name: src/msw/treectrl.cpp // Purpose: wxTreeCtrl // Author: Julian Smart // Modified by: Vadim Zeitlin to be less MSW-specific on 10.10.98 @@ -16,6 +16,7 @@ // ---------------------------------------------------------------------------- // headers // ---------------------------------------------------------------------------- + #ifdef __GNUG__ #pragma implementation "treectrl.h" #endif @@ -27,8 +28,14 @@ #pragma hdrstop #endif +#if wxUSE_TREECTRL + #include "wx/msw/private.h" +// Set this to 1 to be _absolutely_ sure that repainting will work for all +// comctl32.dll versions +#define wxUSE_COMCTL32_SAFELY 0 + // Mingw32 is a bit mental even though this is done in winundef #ifdef GetFirstChild #undef GetFirstChild @@ -40,19 +47,19 @@ #if defined(__WIN95__) +#include "wx/app.h" #include "wx/log.h" #include "wx/dynarray.h" #include "wx/imaglist.h" -#include "wx/treectrl.h" #include "wx/settings.h" - +#include "wx/msw/treectrl.h" #include "wx/msw/dragimag.h" #ifdef __GNUWIN32_OLD__ #include "wx/msw/gnuwin32/extra.h" #endif -#if defined(__WIN95__) && !(defined(__GNUWIN32_OLD__) || defined(__TWIN32__)) +#if defined(__WIN95__) && !((defined(__GNUWIN32_OLD__) || defined(__TWIN32__)) && !defined(__CYGWIN10__)) #include #endif @@ -69,6 +76,10 @@ #define TVS_CHECKBOXES 0x0100 #endif +#ifndef TVS_FULLROWSELECT + #define TVS_FULLROWSELECT 0x1000 +#endif + // old headers might miss these messages (comctl32.dll 4.71+ only) #ifndef TVM_SETBKCOLOR #define TVM_SETBKCOLOR (TV_FIRST + 29) @@ -86,10 +97,6 @@ // looks quite ugly. #define wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE 0 -// from msw/window.cpp -extern bool wxIsShiftDown(); -extern bool wxIsCtrlDown(); - // ---------------------------------------------------------------------------- // private functions // ---------------------------------------------------------------------------- @@ -101,7 +108,6 @@ static HTREEITEM GetItemFromPoint(HWND hwndTV, int x, int y) tvht.pt.x = x; tvht.pt.y = y; - // TreeView_HitTest() doesn't do the right cast in mingw32 headers return (HTREEITEM)TreeView_HitTest(hwndTV, &tvht); } @@ -117,7 +123,7 @@ static bool IsItemSelected(HWND hwndTV, HTREEITEM hItem) if ( !TreeView_GetItem(hwndTV, &tvi) ) { - wxLogLastError("TreeView_GetItem"); + wxLogLastError(wxT("TreeView_GetItem")); } return (tvi.state & TVIS_SELECTED) != 0; @@ -133,7 +139,7 @@ static void SelectItem(HWND hwndTV, HTREEITEM hItem, bool select = TRUE) if ( TreeView_SetItem(hwndTV, &tvi) == -1 ) { - wxLogLastError("TreeView_SetItem"); + wxLogLastError(wxT("TreeView_SetItem")); } } @@ -156,7 +162,7 @@ static void SelectRange(HWND hwndTV, { // find the first (or last) item and select it bool cont = TRUE; - HTREEITEM htItem = TreeView_GetRoot(hwndTV); + HTREEITEM htItem = (HTREEITEM)TreeView_GetRoot(hwndTV); while ( htItem && cont ) { if ( (htItem == htFirst) || (htItem == htLast) ) @@ -176,7 +182,7 @@ static void SelectRange(HWND hwndTV, } } - htItem = TreeView_GetNextVisible(hwndTV, htItem); + htItem = (HTREEITEM)TreeView_GetNextVisible(hwndTV, htItem); } // select the items in range @@ -190,7 +196,7 @@ static void SelectRange(HWND hwndTV, cont = (htItem != htFirst) && (htItem != htLast); - htItem = TreeView_GetNextVisible(hwndTV, htItem); + htItem = (HTREEITEM)TreeView_GetNextVisible(hwndTV, htItem); } // unselect the rest @@ -203,7 +209,7 @@ static void SelectRange(HWND hwndTV, UnselectItem(hwndTV, htItem); } - htItem = TreeView_GetNextVisible(hwndTV, htItem); + htItem = (HTREEITEM)TreeView_GetNextVisible(hwndTV, htItem); } } @@ -218,7 +224,7 @@ static void SelectRange(HWND hwndTV, static void SetFocus(HWND hwndTV, HTREEITEM htItem) { // the current focus - HTREEITEM htFocus = TreeView_GetSelection(hwndTV); + HTREEITEM htFocus = (HTREEITEM)TreeView_GetSelection(hwndTV); if ( htItem ) { @@ -371,7 +377,7 @@ public: DoTraverse(root, recursively); } - virtual bool OnVisit(const wxTreeItemId& item) + virtual bool OnVisit(const wxTreeItemId& WXUNUSED(item)) { m_count++; @@ -394,11 +400,12 @@ private: // // There is only one problem with this: when we retrieve the item's data, we // don't know whether we get a pointer to wxTreeItemData or -// wxTreeItemIndirectData. So we have to maintain a list of all items which -// have indirect data inside the listctrl itself. +// wxTreeItemIndirectData. So we always set the item id to an invalid value +// in this class and the code using the client data checks for it and retrieves +// the real client data in this case. // ---------------------------------------------------------------------------- -class wxTreeItemIndirectData +class wxTreeItemIndirectData : public wxTreeItemData { public: // ctor associates this data with the item and the real item data becomes @@ -415,10 +422,13 @@ public: // and set ourselves as the new one tree->SetIndirectItemData(item, this); + + // we must have the invalid value for the item + m_pItem = 0l; } // dtor deletes the associated data as well - ~wxTreeItemIndirectData() { delete m_data; } + virtual ~wxTreeItemIndirectData() { delete m_data; } // accessors // get the real data associated with the item @@ -437,6 +447,7 @@ private: // all the images associated with the item int m_images[wxTreeItemIcon_Max]; + // the real client data wxTreeItemData *m_data; }; @@ -447,15 +458,36 @@ private: IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl, wxControl) // ---------------------------------------------------------------------------- -// variables +// constants // ---------------------------------------------------------------------------- -// handy table for sending events -static const wxEventType g_events[2][2] = +// indices in gs_expandEvents table below +enum +{ + IDX_COLLAPSE, + IDX_EXPAND, + IDX_WHAT_MAX +}; + +enum +{ + IDX_DONE, + IDX_DOING, + IDX_HOW_MAX +}; + +// handy table for sending events - it has to be initialized during run-time +// now so can't be const any more +static /* const */ wxEventType gs_expandEvents[IDX_WHAT_MAX][IDX_HOW_MAX]; + +/* + but logically it's a const table with the following entries: += { { wxEVT_COMMAND_TREE_ITEM_COLLAPSED, wxEVT_COMMAND_TREE_ITEM_COLLAPSING }, { wxEVT_COMMAND_TREE_ITEM_EXPANDED, wxEVT_COMMAND_TREE_ITEM_EXPANDING } }; +*/ // ============================================================================ // implementation @@ -500,11 +532,18 @@ void wxTreeCtrl::Init() { m_imageListNormal = NULL; m_imageListState = NULL; + m_ownsImageListNormal = m_ownsImageListState = FALSE; m_textCtrl = NULL; m_hasAnyAttr = FALSE; m_dragImage = NULL; - m_htSelStart = 0; + + // initialize the global array of events now as it can't be done statically + // with the wxEVT_XXX values being allocated during run-time only + gs_expandEvents[IDX_COLLAPSE][IDX_DONE] = wxEVT_COMMAND_TREE_ITEM_COLLAPSED; + gs_expandEvents[IDX_COLLAPSE][IDX_DOING] = wxEVT_COMMAND_TREE_ITEM_COLLAPSING; + gs_expandEvents[IDX_EXPAND][IDX_DONE] = wxEVT_COMMAND_TREE_ITEM_EXPANDED; + gs_expandEvents[IDX_EXPAND][IDX_DOING] = wxEVT_COMMAND_TREE_ITEM_EXPANDING; } bool wxTreeCtrl::Create(wxWindow *parent, @@ -521,8 +560,13 @@ bool wxTreeCtrl::Create(wxWindow *parent, return FALSE; DWORD wstyle = WS_VISIBLE | WS_CHILD | WS_TABSTOP | - TVS_HASLINES | TVS_SHOWSELALWAYS; + TVS_SHOWSELALWAYS; + if ( m_windowStyle & wxCLIP_SIBLINGS ) + wstyle |= WS_CLIPSIBLINGS; + + if ((m_windowStyle & wxTR_NO_LINES) == 0) + wstyle |= TVS_HASLINES; if ( m_windowStyle & wxTR_HAS_BUTTONS ) wstyle |= TVS_HASBUTTONS; @@ -532,6 +576,12 @@ bool wxTreeCtrl::Create(wxWindow *parent, if ( m_windowStyle & wxTR_LINES_AT_ROOT ) wstyle |= TVS_LINESATROOT; + if ( m_windowStyle & wxTR_FULL_ROW_HIGHLIGHT ) + { + if ( wxTheApp->GetComCtl32Version() >= 471 ) + wstyle |= TVS_FULLROWSELECT; + } + // using TVS_CHECKBOXES for emulation of a multiselection tree control // doesn't work without the new enough headers #if wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE && \ @@ -550,8 +600,23 @@ bool wxTreeCtrl::Create(wxWindow *parent, if ( !MSWCreateControl(WC_TREEVIEW, wstyle) ) return FALSE; - SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW)); +#if wxUSE_COMCTL32_SAFELY + wxWindow::SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + wxWindow::SetForegroundColour(wxWindow::GetParent()->GetForegroundColour()); +#elif 1 + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); SetForegroundColour(wxWindow::GetParent()->GetForegroundColour()); +#else + // This works around a bug in the Windows tree control whereby for some versions + // of comctrl32, setting any colour actually draws the background in black. + // This will initialise the background to the system colour. + // THIS FIX NOW REVERTED since it caused problems on _other_ systems. + // Assume the user has an updated comctl32.dll. + ::SendMessage(GetHwnd(), TVM_SETBKCOLOR, 0,-1); + wxWindow::SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + SetForegroundColour(wxWindow::GetParent()->GetForegroundColour()); +#endif + // VZ: this is some experimental code which may be used to get the // TVS_CHECKBOXES style functionality for comctl32.dll < 4.71. @@ -627,6 +692,9 @@ wxTreeCtrl::~wxTreeCtrl() // delete user data to prevent memory leaks DeleteAllItems(); + + if (m_ownsImageListNormal) delete m_imageListNormal; + if (m_ownsImageListState) delete m_imageListState; } // ---------------------------------------------------------------------------- @@ -639,7 +707,7 @@ bool wxTreeCtrl::DoGetItem(wxTreeViewItem* tvItem) const { if ( !TreeView_GetItem(GetHwnd(), tvItem) ) { - wxLogLastError("TreeView_GetItem"); + wxLogLastError(wxT("TreeView_GetItem")); return FALSE; } @@ -651,7 +719,7 @@ void wxTreeCtrl::DoSetItem(wxTreeViewItem* tvItem) { if ( TreeView_SetItem(GetHwnd(), tvItem) == -1 ) { - wxLogLastError("TreeView_SetItem"); + wxLogLastError(wxT("TreeView_SetItem")); } } @@ -690,12 +758,28 @@ void wxTreeCtrl::SetAnyImageList(wxImageList *imageList, int which) void wxTreeCtrl::SetImageList(wxImageList *imageList) { + if (m_ownsImageListNormal) delete m_imageListNormal; SetAnyImageList(m_imageListNormal = imageList, TVSIL_NORMAL); + m_ownsImageListNormal = FALSE; } void wxTreeCtrl::SetStateImageList(wxImageList *imageList) { + if (m_ownsImageListState) delete m_imageListState; SetAnyImageList(m_imageListState = imageList, TVSIL_STATE); + m_ownsImageListState = FALSE; +} + +void wxTreeCtrl::AssignImageList(wxImageList *imageList) +{ + SetImageList(imageList); + m_ownsImageListNormal = TRUE; +} + +void wxTreeCtrl::AssignStateImageList(wxImageList *imageList) +{ + SetStateImageList(imageList); + m_ownsImageListState = TRUE; } size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId& item, @@ -712,20 +796,24 @@ size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool wxTreeCtrl::SetBackgroundColour(const wxColour &colour) { +#if !wxUSE_COMCTL32_SAFELY if ( !wxWindowBase::SetBackgroundColour(colour) ) return FALSE; SendMessage(GetHwnd(), TVM_SETBKCOLOR, 0, colour.GetPixel()); +#endif return TRUE; } bool wxTreeCtrl::SetForegroundColour(const wxColour &colour) { +#if !wxUSE_COMCTL32_SAFELY if ( !wxWindowBase::SetForegroundColour(colour) ) return FALSE; SendMessage(GetHwnd(), TVM_SETTEXTCOLOR, 0, colour.GetPixel()); +#endif return TRUE; } @@ -901,18 +989,23 @@ wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const return NULL; } - if ( HasIndirectData(item) ) + wxTreeItemData *data = (wxTreeItemData *)tvItem.lParam; + if ( IsDataIndirect(data) ) { - return ((wxTreeItemIndirectData *)tvItem.lParam)->GetData(); - } - else - { - return (wxTreeItemData *)tvItem.lParam; + data = ((wxTreeItemIndirectData *)data)->GetData(); } + + return data; } void wxTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data) { + // first, associate this piece of data with this item + if ( data ) + { + data->SetId(item); + } + wxTreeViewItem tvItem(item, TVIF_PARAM); if ( HasIndirectData(item) ) @@ -942,14 +1035,21 @@ void wxTreeCtrl::SetIndirectItemData(const wxTreeItemId& item, // wxTreeItemIndirectData as well wxASSERT_MSG( !HasIndirectData(item), wxT("setting indirect data twice?") ); - SetItemData(item, (wxTreeItemData *)data); - - m_itemsWithIndirectData.Add(item); + SetItemData(item, data); } bool wxTreeCtrl::HasIndirectData(const wxTreeItemId& item) const { - return m_itemsWithIndirectData.Index(item) != wxNOT_FOUND; + // query the item itself + wxTreeViewItem tvItem(item, TVIF_PARAM); + if ( !DoGetItem(&tvItem) ) + { + return FALSE; + } + + wxTreeItemData *data = (wxTreeItemData *)tvItem.lParam; + + return data && IsDataIndirect(data); } void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has) @@ -973,6 +1073,15 @@ void wxTreeCtrl::SetItemDropHighlight(const wxTreeItemId& item, bool highlight) DoSetItem(&tvItem); } +void wxTreeCtrl::RefreshItem(const wxTreeItemId& item) +{ + wxRect rect; + if ( GetBoundingRect(item, rect) ) + { + RefreshRect(rect); + } +} + void wxTreeCtrl::SetItemTextColour(const wxTreeItemId& item, const wxColour& col) { @@ -987,6 +1096,8 @@ void wxTreeCtrl::SetItemTextColour(const wxTreeItemId& item, } attr->SetTextColour(col); + + RefreshItem(item); } void wxTreeCtrl::SetItemBackgroundColour(const wxTreeItemId& item, @@ -1003,6 +1114,8 @@ void wxTreeCtrl::SetItemBackgroundColour(const wxTreeItemId& item, } attr->SetBackgroundColour(col); + + RefreshItem(item); } void wxTreeCtrl::SetItemFont(const wxTreeItemId& item, const wxFont& font) @@ -1018,6 +1131,8 @@ void wxTreeCtrl::SetItemFont(const wxTreeItemId& item, const wxFont& font) } attr->SetFont(font); + + RefreshItem(item); } // ---------------------------------------------------------------------------- @@ -1035,7 +1150,6 @@ bool wxTreeCtrl::IsVisible(const wxTreeItemId& item) const // FALSE means get item rect for the whole item, not only text return SendMessage(GetHwnd(), TVM_GETITEMRECT, FALSE, (LPARAM)&rect) != 0; - } bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const @@ -1084,7 +1198,7 @@ wxTreeItemId wxTreeCtrl::GetRootItem() const wxTreeItemId wxTreeCtrl::GetSelection() const { - wxCHECK_MSG( !(m_windowStyle & wxTR_MULTIPLE), (WXHTREEITEM)0, + wxCHECK_MSG( !(m_windowStyle & wxTR_MULTIPLE), (long)(WXHTREEITEM)0, wxT("this only works with single selection controls") ); return wxTreeItemId((WXHTREEITEM) TreeView_GetSelection(GetHwnd())); @@ -1147,16 +1261,14 @@ wxTreeItemId wxTreeCtrl::GetFirstVisibleItem() const wxTreeItemId wxTreeCtrl::GetNextVisible(const wxTreeItemId& item) const { - wxASSERT_MSG( IsVisible(item), wxT("The item you call GetNextVisible() " - "for must be visible itself!")); + wxASSERT_MSG( IsVisible(item), wxT("The item you call GetNextVisible() for must be visible itself!")); return wxTreeItemId((WXHTREEITEM) TreeView_GetNextVisible(GetHwnd(), HITEM(item))); } wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const { - wxASSERT_MSG( IsVisible(item), wxT("The item you call GetPrevVisible() " - "for must be visible itself!")); + wxASSERT_MSG( IsVisible(item), wxT("The item you call GetPrevVisible() for must be visible itself!")); return wxTreeItemId((WXHTREEITEM) TreeView_GetPrevVisible(GetHwnd(), HITEM(item))); } @@ -1203,6 +1315,10 @@ wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parent, int image, int selectedImage, wxTreeItemData *data) { + wxCHECK_MSG( parent.IsOk() || !TreeView_GetRoot(GetHwnd()), + wxTreeItemId(), + _T("can't have more than one root in the tree") ); + TV_INSERTSTRUCT tvIns; tvIns.hParent = HITEM(parent); tvIns.hInsertAfter = HITEM(hInsertAfter); @@ -1220,6 +1336,11 @@ wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parent, mask |= TVIF_TEXT; tvIns.item.pszText = (wxChar *)text.c_str(); // cast is ok } + else + { + tvIns.item.pszText = NULL; + tvIns.item.cchTextMax = 0; + } if ( image != -1 ) { @@ -1250,7 +1371,7 @@ wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parent, HTREEITEM id = (HTREEITEM) TreeView_InsertItem(GetHwnd(), &tvIns); if ( id == 0 ) { - wxLogLastError("TreeView_InsertItem"); + wxLogLastError(wxT("TreeView_InsertItem")); } if ( data != NULL ) @@ -1276,7 +1397,7 @@ wxTreeItemId wxTreeCtrl::AddRoot(const wxString& text, int image, int selectedImage, wxTreeItemData *data) { - return DoInsertItem(wxTreeItemId((WXHTREEITEM) 0), (WXHTREEITEM) 0, + return DoInsertItem(wxTreeItemId((long) (WXHTREEITEM) 0), (long)(WXHTREEITEM) 0, text, image, selectedImage, data); } @@ -1335,7 +1456,7 @@ void wxTreeCtrl::Delete(const wxTreeItemId& item) { if ( !TreeView_DeleteItem(GetHwnd(), HITEM(item)) ) { - wxLogLastError("TreeView_DeleteItem"); + wxLogLastError(wxT("TreeView_DeleteItem")); } } @@ -1358,7 +1479,7 @@ void wxTreeCtrl::DeleteChildren(const wxTreeItemId& item) { if ( !TreeView_DeleteItem(GetHwnd(), (HTREEITEM)children[n]) ) { - wxLogLastError("TreeView_DeleteItem"); + wxLogLastError(wxT("TreeView_DeleteItem")); } } } @@ -1367,7 +1488,7 @@ void wxTreeCtrl::DeleteAllItems() { if ( !TreeView_DeleteAllItems(GetHwnd()) ) { - wxLogLastError("TreeView_DeleteAllItems"); + wxLogLastError(wxT("TreeView_DeleteAllItems")); } } @@ -1395,17 +1516,15 @@ void wxTreeCtrl::DoExpand(const wxTreeItemId& item, int flag) { wxTreeEvent event(wxEVT_NULL, m_windowId); event.m_item = item; - - bool isExpanded = IsExpanded(item); - event.SetEventObject(this); - // FIXME return value of {EXPAND|COLLAPS}ING event handler is discarded - event.SetEventType(g_events[isExpanded][TRUE]); - GetEventHandler()->ProcessEvent(event); + // note that the {EXPAND|COLLAPS}ING event is sent by TreeView_Expand() + // itself + event.SetEventType(gs_expandEvents[IsExpanded(item) ? IDX_EXPAND + : IDX_COLLAPSE] + [IDX_DONE]); - event.SetEventType(g_events[isExpanded][FALSE]); - GetEventHandler()->ProcessEvent(event); + (void)GetEventHandler()->ProcessEvent(event); } //else: change didn't took place, so do nothing at all } @@ -1441,7 +1560,7 @@ void wxTreeCtrl::Unselect() wxT("doesn't make sense, may be you want UnselectAll()?") ); // just remove the selection - SelectItem(wxTreeItemId((WXHTREEITEM) 0)); + SelectItem(wxTreeItemId((long) (WXHTREEITEM) 0)); } void wxTreeCtrl::UnselectAll() @@ -1492,7 +1611,7 @@ void wxTreeCtrl::SelectItem(const wxTreeItemId& item) { if ( !TreeView_SelectItem(GetHwnd(), HITEM(item)) ) { - wxLogLastError("TreeView_SelectItem"); + wxLogLastError(wxT("TreeView_SelectItem")); } else { @@ -1514,7 +1633,7 @@ void wxTreeCtrl::ScrollTo(const wxTreeItemId& item) { if ( !TreeView_SelectSetFirstVisible(GetHwnd(), HITEM(item)) ) { - wxLogLastError("TreeView_SelectSetFirstVisible"); + wxLogLastError(wxT("TreeView_SelectSetFirstVisible")); } } @@ -1545,6 +1664,14 @@ void wxTreeCtrl::DeleteTextCtrl() { if ( m_textCtrl ) { + // the HWND corresponding to this control is deleted by the tree + // control itself and we don't know when exactly this happens, so check + // if the window still exists before calling UnsubclassWin() + if ( !::IsWindow(GetHwndOf(m_textCtrl)) ) + { + m_textCtrl->SetHWND(0); + } + m_textCtrl->UnsubclassWin(); m_textCtrl->SetHWND(0); delete m_textCtrl; @@ -1569,6 +1696,7 @@ wxTextCtrl* wxTreeCtrl::EditLabel(const wxTreeItemId& item, } m_textCtrl = (wxTextCtrl *)textControlClass->CreateObject(); + m_textCtrl->SetParent(this); m_textCtrl->SetHWND((WXHWND)hWnd); m_textCtrl->SubclassWin((WXHWND)hWnd); @@ -1576,7 +1704,7 @@ wxTextCtrl* wxTreeCtrl::EditLabel(const wxTreeItemId& item, } // End label editing, optionally cancelling the edit -void wxTreeCtrl::EndEditLabel(const wxTreeItemId& item, bool discardChanges) +void wxTreeCtrl::EndEditLabel(const wxTreeItemId& WXUNUSED(item), bool discardChanges) { TreeView_EndEditLabelNow(GetHwnd(), discardChanges); @@ -1637,14 +1765,37 @@ bool wxTreeCtrl::GetBoundingRect(const wxTreeItemId& item, // sorting stuff // ---------------------------------------------------------------------------- -static int CALLBACK TreeView_CompareCallback(wxTreeItemData *pItem1, - wxTreeItemData *pItem2, - wxTreeCtrl *tree) +// this is just a tiny namespace which is friend to wxTreeCtrl and so can use +// functions such as IsDataIndirect() +class wxTreeSortHelper +{ +public: + static int CALLBACK Compare(LPARAM data1, LPARAM data2, LPARAM tree); + +private: + static wxTreeItemId GetIdFromData(wxTreeCtrl *tree, LPARAM item) + { + wxTreeItemData *data = (wxTreeItemData *)item; + if ( tree->IsDataIndirect(data) ) + { + data = ((wxTreeItemIndirectData *)data)->GetData(); + } + + return data->GetId(); + } +}; + +int CALLBACK wxTreeSortHelper::Compare(LPARAM pItem1, + LPARAM pItem2, + LPARAM htree) { wxCHECK_MSG( pItem1 && pItem2, 0, wxT("sorting tree without data doesn't make sense") ); - return tree->OnCompareItems(pItem1->GetId(), pItem2->GetId()); + wxTreeCtrl *tree = (wxTreeCtrl *)htree; + + return tree->OnCompareItems(GetIdFromData(tree, pItem1), + GetIdFromData(tree, pItem2)); } int wxTreeCtrl::OnCompareItems(const wxTreeItemId& item1, @@ -1666,7 +1817,7 @@ void wxTreeCtrl::SortChildren(const wxTreeItemId& item) { TV_SORTCB tvSort; tvSort.hParent = HITEM(item); - tvSort.lpfnCompare = (PFNTVCOMPARE)TreeView_CompareCallback; + tvSort.lpfnCompare = wxTreeSortHelper::Compare; tvSort.lParam = (LPARAM)this; TreeView_SortChildrenCB(GetHwnd(), &tvSort, 0 /* reserved */); } @@ -1708,7 +1859,6 @@ long wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) { bool processed = FALSE; long rc = 0; - bool isMultiple = (GetWindowStyle() & wxTR_MULTIPLE) != 0; if ( (nMsg >= WM_MOUSEFIRST) && (nMsg <= WM_MOUSELAST) ) @@ -1842,7 +1992,7 @@ long wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) // we handle.arrows and space, but not page up/down and home/end: the // latter should be easy, but not the former - HTREEITEM htSel = TreeView_GetSelection(GetHwnd()); + HTREEITEM htSel = (HTREEITEM)TreeView_GetSelection(GetHwnd()); if ( !m_htSelStart ) { m_htSelStart = (WXHTREEITEM)htSel; @@ -1875,9 +2025,9 @@ long wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) { (void)wxControl::MSWWindowProc(nMsg, wParam, lParam); - HTREEITEM htNext = - wParam == VK_UP ? TreeView_GetPrevVisible(GetHwnd(), htSel) - : TreeView_GetNextVisible(GetHwnd(), htSel); + HTREEITEM htNext = (HTREEITEM)(wParam == VK_UP + ? TreeView_GetPrevVisible(GetHwnd(), htSel) + : TreeView_GetNextVisible(GetHwnd(), htSel)); if ( !htNext ) { @@ -1900,7 +2050,6 @@ long wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) } } #endif // !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE - if ( !processed ) rc = wxControl::MSWWindowProc(nMsg, wParam, lParam); @@ -1916,25 +2065,6 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) switch ( hdr->code ) { - case NM_RCLICK: - { - if ( wxControl::MSWOnNotify(idCtrl, lParam, result) ) - return TRUE; - - TV_HITTESTINFO tvhti; - ::GetCursorPos(&(tvhti.pt)); - ::ScreenToClient(GetHwnd(),&(tvhti.pt)); - if ( TreeView_HitTest(GetHwnd(),&tvhti) ) - { - if( tvhti.flags & TVHT_ONITEM ) - { - event.m_item = (WXHTREEITEM) tvhti.hItem; - eventType = wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK; - } - } - } - break; - case TVN_BEGINDRAG: eventType = wxEVT_COMMAND_TREE_BEGIN_DRAG; // fall through @@ -2011,31 +2141,30 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) } case TVN_ITEMEXPANDING: - event.m_code = FALSE; - // fall through - case TVN_ITEMEXPANDED: { NM_TREEVIEW* tv = (NM_TREEVIEW*)lParam; - bool expand = FALSE; + int what; switch ( tv->action ) { + default: + wxLogDebug(wxT("unexpected code %d in TVN_ITEMEXPAND message"), tv->action); + // fall through + case TVE_EXPAND: - expand = TRUE; + what = IDX_EXPAND; break; case TVE_COLLAPSE: - expand = FALSE; + what = IDX_COLLAPSE; break; - - default: - wxLogDebug(wxT("unexpected code %d in TVN_ITEMEXPAND " - "message"), tv->action); } - bool ing = ((int)hdr->code == TVN_ITEMEXPANDING); - eventType = g_events[expand][ing]; + int how = (int)hdr->code == TVN_ITEMEXPANDING ? IDX_DOING + : IDX_DONE; + + eventType = gs_expandEvents[what][how]; event.m_item = (WXHTREEITEM) tv->itemNew.hItem; } @@ -2046,7 +2175,13 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) eventType = wxEVT_COMMAND_TREE_KEY_DOWN; TV_KEYDOWN *info = (TV_KEYDOWN *)lParam; - event.m_code = wxCharCodeMSWToWX(info->wVKey); + // we pass 0 as last CreateKeyEvent() parameter because we + // don't have access to the real key press flags here - but as + // it is only used to determin wxKeyEvent::m_altDown flag it's + // not too bad + event.m_evtKey = CreateKeyEvent(wxEVT_KEY_DOWN, + wxCharCodeMSWToWX(info->wVKey), + 0); // a separate event for Space/Return if ( !wxIsCtrlDown() && !wxIsShiftDown() && @@ -2083,19 +2218,19 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) } break; -#if defined(_WIN32_IE) && _WIN32_IE >= 0x300 +#if defined(_WIN32_IE) && _WIN32_IE >= 0x300 && !wxUSE_COMCTL32_SAFELY && !( defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 1, 0 ) ) case NM_CUSTOMDRAW: { LPNMTVCUSTOMDRAW lptvcd = (LPNMTVCUSTOMDRAW)lParam; NMCUSTOMDRAW& nmcd = lptvcd->nmcd; - switch( nmcd.dwDrawStage ) + switch ( nmcd.dwDrawStage ) { case CDDS_PREPAINT: // if we've got any items with non standard attributes, // notify us before painting each item *result = m_hasAnyAttr ? CDRF_NOTIFYITEMDRAW : CDRF_DODEFAULT; - return TRUE; + break; case CDDS_ITEMPREPAINT: { @@ -2105,7 +2240,8 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) if ( !attr ) { // nothing to do for this item - return CDRF_DODEFAULT; + *result = CDRF_DODEFAULT; + break; } HFONT hFont; @@ -2168,18 +2304,42 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) { *result = CDRF_DODEFAULT; } - - return TRUE; } + break; default: *result = CDRF_DODEFAULT; - return TRUE; } } - break; + + // we always process it + return TRUE; #endif // _WIN32_IE >= 0x300 + case NM_DBLCLK: + case NM_RCLICK: + { + TV_HITTESTINFO tvhti; + ::GetCursorPos(&tvhti.pt); + ::ScreenToClient(GetHwnd(), &tvhti.pt); + if ( TreeView_HitTest(GetHwnd(), &tvhti) ) + { + if ( tvhti.flags & TVHT_ONITEM ) + { + event.m_item = (WXHTREEITEM) tvhti.hItem; + eventType = (int)hdr->code == NM_DBLCLK + ? wxEVT_COMMAND_TREE_ITEM_ACTIVATED + : wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK; + + event.m_pointDrag.x = tvhti.pt.x; + event.m_pointDrag.y = tvhti.pt.y; + } + + break; + } + } + // fall through + default: return wxControl::MSWOnNotify(idCtrl, lParam, result); } @@ -2192,6 +2352,14 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) // post processing switch ( hdr->code ) { + case NM_DBLCLK: + // we translate NM_DBLCLK into ACTIVATED event, so don't interpret + // the return code of this event handler as the return value for + // NM_DBLCLK - otherwise, double clicking the item to toggle its + // expanded status would never work + *result = FALSE; + break; + case TVN_BEGINDRAG: case TVN_BEGINRDRAG: if ( event.IsAllowed() ) @@ -2220,8 +2388,6 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) wxTreeItemIndirectData *data = (wxTreeItemIndirectData *) tv->itemOld.lParam; delete data; // can't be NULL here - - m_itemsWithIndirectData.Remove(item); } else { @@ -2239,8 +2405,12 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) break; case TVN_ENDLABELEDIT: - // return TRUE to set the label to the new string + // return TRUE to set the label to the new string: note that we + // also must pretend that we did process the message or it is going + // to be passed to DefWindowProc() which will happily return FALSE + // cancelling the label change *result = event.IsAllowed(); + processed = TRUE; // ensure that we don't have the text ctrl which is going to be // deleted any more @@ -2253,10 +2423,29 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) *result = !event.IsAllowed(); break; + case TVN_ITEMEXPANDED: + // the item is not refreshed properly after expansion when it has + // an image depending on the expanded/collapsed state - bug in + // comctl32.dll or our code? + { + NM_TREEVIEW* tv = (NM_TREEVIEW*)lParam; + if ( tv->action == TVE_EXPAND ) + { + wxTreeItemId id = (WXHTREEITEM)tv->itemNew.hItem; + + int image = GetItemImage(id, wxTreeItemIcon_Expanded); + if ( image != -1 ) + { + RefreshItem(id); + } + } + } + break; + case TVN_GETDISPINFO: // NB: so far the user can't set the image himself anyhow, so do it // anyway - but this may change later - if ( /* !processed && */ 1 ) +// if ( /* !processed && */ 1 ) { wxTreeItemId item = event.m_item; TV_DISPINFO *info = (TV_DISPINFO *)lParam; @@ -2287,22 +2476,9 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) // for the other messages the return value is ignored and there is // nothing special to do } - return processed; } -// ---------------------------------------------------------------------------- -// Tree event -// ---------------------------------------------------------------------------- - -IMPLEMENT_DYNAMIC_CLASS(wxTreeEvent, wxNotifyEvent) - -wxTreeEvent::wxTreeEvent(wxEventType commandType, int id) - : wxNotifyEvent(commandType, id) -{ - m_code = 0; - m_itemOld = 0; -} - #endif // __WIN95__ +#endif // wxUSE_TREECTRL