X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/f888d61444c1549c5e8a593055f0159dd07eca86..730d336601a3ece761b56e7c05d1099352670a25:/src/msw/treectrl.cpp diff --git a/src/msw/treectrl.cpp b/src/msw/treectrl.cpp index a9ab748593..8cc634935e 100644 --- a/src/msw/treectrl.cpp +++ b/src/msw/treectrl.cpp @@ -17,7 +17,7 @@ // headers // ---------------------------------------------------------------------------- -#ifdef __GNUG__ +#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) #pragma implementation "treectrl.h" #endif @@ -44,36 +44,8 @@ #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(__CYGWIN10__)) - #include -#endif - -// Bug in headers, sometimes -#ifndef TVIS_FOCUSED - #define TVIS_FOCUSED 0x0001 -#endif - -#ifndef TV_FIRST - #define TV_FIRST 0x1100 -#endif - -#ifndef TVS_CHECKBOXES - #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) - #define TVM_SETTEXTCOLOR (TV_FIRST + 30) -#endif +// include "properly" +#include "wx/msw/wrapcctl.h" // macros to hide the cast ugliness // -------------------------------- @@ -125,7 +97,7 @@ static bool IsItemSelected(HWND hwndTV, HTREEITEM hItem) return (tvi.state & TVIS_SELECTED) != 0; } -static void SelectItem(HWND hwndTV, HTREEITEM hItem, bool select = true) +static bool SelectItem(HWND hwndTV, HTREEITEM hItem, bool select = true) { TV_ITEM tvi; tvi.mask = TVIF_STATE | TVIF_HANDLE; @@ -136,7 +108,10 @@ static void SelectItem(HWND hwndTV, HTREEITEM hItem, bool select = true) if ( TreeView_SetItem(hwndTV, &tvi) == -1 ) { wxLogLastError(wxT("TreeView_SetItem")); + return false; } + + return true; } static inline void UnselectItem(HWND hwndTV, HTREEITEM htItem) @@ -398,6 +373,8 @@ public: private: wxArrayTreeItemIds& m_selections; + + DECLARE_NO_COPY_CLASS(TraverseSelections) }; // internal class for counting tree items @@ -425,6 +402,8 @@ public: private: size_t m_count; + + DECLARE_NO_COPY_CLASS(TraverseCounter) }; // ---------------------------------------------------------------------------- @@ -494,7 +473,69 @@ private: // wxWin macros // ---------------------------------------------------------------------------- +#if wxUSE_EXTENDED_RTTI +WX_DEFINE_FLAGS( wxTreeCtrlStyle ) + +wxBEGIN_FLAGS( wxTreeCtrlStyle ) + // new style border flags, we put them first to + // use them for streaming out + wxFLAGS_MEMBER(wxBORDER_SIMPLE) + wxFLAGS_MEMBER(wxBORDER_SUNKEN) + wxFLAGS_MEMBER(wxBORDER_DOUBLE) + wxFLAGS_MEMBER(wxBORDER_RAISED) + wxFLAGS_MEMBER(wxBORDER_STATIC) + wxFLAGS_MEMBER(wxBORDER_NONE) + + // old style border flags + wxFLAGS_MEMBER(wxSIMPLE_BORDER) + wxFLAGS_MEMBER(wxSUNKEN_BORDER) + wxFLAGS_MEMBER(wxDOUBLE_BORDER) + wxFLAGS_MEMBER(wxRAISED_BORDER) + wxFLAGS_MEMBER(wxSTATIC_BORDER) + wxFLAGS_MEMBER(wxBORDER) + + // standard window styles + wxFLAGS_MEMBER(wxTAB_TRAVERSAL) + wxFLAGS_MEMBER(wxCLIP_CHILDREN) + wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW) + wxFLAGS_MEMBER(wxWANTS_CHARS) + wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE) + wxFLAGS_MEMBER(wxALWAYS_SHOW_SB ) + wxFLAGS_MEMBER(wxVSCROLL) + wxFLAGS_MEMBER(wxHSCROLL) + + wxFLAGS_MEMBER(wxTR_EDIT_LABELS) + wxFLAGS_MEMBER(wxTR_NO_BUTTONS) + wxFLAGS_MEMBER(wxTR_HAS_BUTTONS) + wxFLAGS_MEMBER(wxTR_TWIST_BUTTONS) + wxFLAGS_MEMBER(wxTR_NO_LINES) + wxFLAGS_MEMBER(wxTR_FULL_ROW_HIGHLIGHT) + wxFLAGS_MEMBER(wxTR_LINES_AT_ROOT) + wxFLAGS_MEMBER(wxTR_HIDE_ROOT) + wxFLAGS_MEMBER(wxTR_ROW_LINES) + wxFLAGS_MEMBER(wxTR_HAS_VARIABLE_ROW_HEIGHT) + wxFLAGS_MEMBER(wxTR_SINGLE) + wxFLAGS_MEMBER(wxTR_MULTIPLE) + wxFLAGS_MEMBER(wxTR_EXTENDED) + wxFLAGS_MEMBER(wxTR_DEFAULT_STYLE) + +wxEND_FLAGS( wxTreeCtrlStyle ) + +IMPLEMENT_DYNAMIC_CLASS_XTI(wxTreeCtrl, wxControl,"wx/treectrl.h") + +wxBEGIN_PROPERTIES_TABLE(wxTreeCtrl) + wxEVENT_PROPERTY( TextUpdated , wxEVT_COMMAND_TEXT_UPDATED , wxCommandEvent ) + wxEVENT_RANGE_PROPERTY( TreeEvent , wxEVT_COMMAND_TREE_BEGIN_DRAG , wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK , wxTreeEvent ) + wxPROPERTY_FLAGS( WindowStyle , wxTreeCtrlStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style +wxEND_PROPERTIES_TABLE() + +wxBEGIN_HANDLERS_TABLE(wxTreeCtrl) +wxEND_HANDLERS_TABLE() + +wxCONSTRUCTOR_5( wxTreeCtrl , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size , long , WindowStyle ) +#else IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl, wxControl) +#endif // ---------------------------------------------------------------------------- // constants @@ -618,7 +659,7 @@ bool wxTreeCtrl::Create(wxWindow *parent, if ( m_windowStyle & wxTR_FULL_ROW_HIGHLIGHT ) { - if ( wxTheApp->GetComCtl32Version() >= 471 ) + if ( wxApp::GetComCtl32Version() >= 471 ) wstyle |= TVS_FULLROWSELECT; } @@ -636,8 +677,13 @@ bool wxTreeCtrl::Create(wxWindow *parent, wstyle |= TVS_CHECKBOXES; #endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE +#ifndef __WXWINCE__ + // Need so that TVN_GETINFOTIP messages will be sent + wstyle |= TVS_INFOTIP; +#endif + // Create the tree control. - if ( !MSWCreateControl(WC_TREEVIEW, wstyle) ) + if ( !MSWCreateControl(WC_TREEVIEW, wstyle, pos, size) ) return false; #if wxUSE_COMCTL32_SAFELY @@ -670,8 +716,8 @@ bool wxTreeCtrl::Create(wxWindow *parent, HDC hdcMem = CreateCompatibleDC(NULL); // create a mono bitmap of the standard size - int x = GetSystemMetrics(SM_CXMENUCHECK); - int y = GetSystemMetrics(SM_CYMENUCHECK); + int x = ::GetSystemMetrics(SM_CXMENUCHECK); + int y = ::GetSystemMetrics(SM_CYMENUCHECK); wxImageList imagelistCheckboxes(x, y, false, 2); HBITMAP hbmpCheck = CreateBitmap(x, y, // bitmap size 1, // # of color planes @@ -709,7 +755,7 @@ bool wxTreeCtrl::Create(wxWindow *parent, } #endif // 0 - SetSize(pos.x, pos.y, size.x, size.y); + wxSetCCUnicodeFormat(GetHwnd()); return true; } @@ -739,6 +785,18 @@ wxTreeCtrl::~wxTreeCtrl() // accessors // ---------------------------------------------------------------------------- +/* static */ wxVisualAttributes +wxTreeCtrl::GetClassDefaultAttributes(wxWindowVariant variant) +{ + wxVisualAttributes attrs = GetCompositeControlsDefaultAttributes(variant); + + // common controls have their own default font + attrs.font = wxGetCCDefaultFont(); + + return attrs; +} + + // simple wrappers which add error checking in debug mode bool wxTreeCtrl::DoGetItem(wxTreeViewItem* tvItem) const @@ -828,8 +886,9 @@ void wxTreeCtrl::AssignStateImageList(wxImageList *imageList) size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool recursively) const { - TraverseCounter counter(this, item, recursively); + wxCHECK_MSG( item.IsOk(), 0u, wxT("invalid tree item") ); + TraverseCounter counter(this, item, recursively); return counter.GetCount() - 1; } @@ -843,7 +902,7 @@ bool wxTreeCtrl::SetBackgroundColour(const wxColour &colour) if ( !wxWindowBase::SetBackgroundColour(colour) ) return false; - SendMessage(GetHwnd(), TVM_SETBKCOLOR, 0, colour.GetPixel()); + ::SendMessage(GetHwnd(), TVM_SETBKCOLOR, 0, colour.GetPixel()); #endif return true; @@ -855,7 +914,7 @@ bool wxTreeCtrl::SetForegroundColour(const wxColour &colour) if ( !wxWindowBase::SetForegroundColour(colour) ) return false; - SendMessage(GetHwnd(), TVM_SETTEXTCOLOR, 0, colour.GetPixel()); + ::SendMessage(GetHwnd(), TVM_SETTEXTCOLOR, 0, colour.GetPixel()); #endif return true; @@ -867,6 +926,8 @@ bool wxTreeCtrl::SetForegroundColour(const wxColour &colour) wxString wxTreeCtrl::GetItemText(const wxTreeItemId& item) const { + wxCHECK_MSG( item.IsOk(), wxEmptyString, wxT("invalid tree item") ); + wxChar buf[512]; // the size is arbitrary... wxTreeViewItem tvItem(item, TVIF_TEXT); @@ -883,6 +944,8 @@ wxString wxTreeCtrl::GetItemText(const wxTreeItemId& item) const void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text) { + wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); + if ( IS_VIRTUAL_ROOT(item) ) return; @@ -898,7 +961,7 @@ void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text) HWND hwndEdit = TreeView_GetEditControl(GetHwnd()); if ( hwndEdit ) { - if ( item == GetSelection() ) + if ( item == m_idEdited ) { ::SetWindowText(hwndEdit, text); } @@ -958,6 +1021,8 @@ void wxTreeCtrl::DoSetItemImages(const wxTreeItemId& item, int wxTreeCtrl::GetItemImage(const wxTreeItemId& item, wxTreeItemIcon which) const { + wxCHECK_MSG( item.IsOk(), -1, wxT("invalid tree item") ); + if ( (HITEM(item) == TVI_ROOT) && (m_windowStyle & wxTR_HIDE_ROOT) ) { // TODO: Maybe a hidden root can still provide images? @@ -997,6 +1062,8 @@ int wxTreeCtrl::GetItemImage(const wxTreeItemId& item, void wxTreeCtrl::SetItemImage(const wxTreeItemId& item, int image, wxTreeItemIcon which) { + wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); + if ( IS_VIRTUAL_ROOT(item) ) { // TODO: Maybe a hidden root can still store images? @@ -1015,7 +1082,8 @@ void wxTreeCtrl::SetItemImage(const wxTreeItemId& item, int image, case wxTreeItemIcon_Normal: { const int imageNormalOld = GetItemImage(item); - const int imageSelOld = GetItemSelectedImage(item); + const int imageSelOld = + GetItemImage(item, wxTreeItemIcon_Selected); // always set the normal image imageNormal = image; @@ -1040,7 +1108,7 @@ void wxTreeCtrl::SetItemImage(const wxTreeItemId& item, int image, // the wxTreeItemIndirectData GetItemXXXImage() will use it to // get the images imageNormal = GetItemImage(item); - imageSel = GetItemSelectedImage(item); + imageSel = GetItemImage(item, wxTreeItemIcon_Selected); // if it doesn't have it yet, add it wxTreeItemIndirectData *data = new @@ -1068,6 +1136,8 @@ void wxTreeCtrl::SetItemImage(const wxTreeItemId& item, int image, wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const { + wxCHECK_MSG( item.IsOk(), NULL, wxT("invalid tree item") ); + wxTreeViewItem tvItem(item, TVIF_PARAM); // Hidden root may have data. @@ -1093,6 +1163,8 @@ wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const void wxTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data) { + wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); + if ( IS_VIRTUAL_ROOT(item) ) { GET_VIRTUAL_ROOT()->SetData(data); @@ -1152,6 +1224,8 @@ bool wxTreeCtrl::HasIndirectData(const wxTreeItemId& item) const void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has) { + wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); + if ( IS_VIRTUAL_ROOT(item) ) return; @@ -1162,6 +1236,8 @@ void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has) void wxTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold) { + wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); + if ( IS_VIRTUAL_ROOT(item) ) return; @@ -1194,28 +1270,33 @@ void wxTreeCtrl::RefreshItem(const wxTreeItemId& item) wxColour wxTreeCtrl::GetItemTextColour(const wxTreeItemId& item) const { - wxMapTreeAttr::const_iterator it = m_attrs.find(item.m_pItem); + wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") ); + wxMapTreeAttr::const_iterator it = m_attrs.find(item.m_pItem); return it == m_attrs.end() ? wxNullColour : it->second->GetTextColour(); } wxColour wxTreeCtrl::GetItemBackgroundColour(const wxTreeItemId& item) const { - wxMapTreeAttr::const_iterator it = m_attrs.find(item.m_pItem); + wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") ); + wxMapTreeAttr::const_iterator it = m_attrs.find(item.m_pItem); return it == m_attrs.end() ? wxNullColour : it->second->GetBackgroundColour(); } wxFont wxTreeCtrl::GetItemFont(const wxTreeItemId& item) const { - wxMapTreeAttr::const_iterator it = m_attrs.find(item.m_pItem); + wxCHECK_MSG( item.IsOk(), wxNullFont, wxT("invalid tree item") ); + wxMapTreeAttr::const_iterator it = m_attrs.find(item.m_pItem); return it == m_attrs.end() ? wxNullFont : it->second->GetFont(); } void wxTreeCtrl::SetItemTextColour(const wxTreeItemId& item, const wxColour& col) { + wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); + wxTreeItemAttr *attr; wxMapTreeAttr::iterator it = m_attrs.find(item.m_pItem); if ( it == m_attrs.end() ) @@ -1238,6 +1319,8 @@ void wxTreeCtrl::SetItemTextColour(const wxTreeItemId& item, void wxTreeCtrl::SetItemBackgroundColour(const wxTreeItemId& item, const wxColour& col) { + wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); + wxTreeItemAttr *attr; wxMapTreeAttr::iterator it = m_attrs.find(item.m_pItem); if ( it == m_attrs.end() ) @@ -1259,6 +1342,8 @@ void wxTreeCtrl::SetItemBackgroundColour(const wxTreeItemId& item, void wxTreeCtrl::SetItemFont(const wxTreeItemId& item, const wxFont& font) { + wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); + wxTreeItemAttr *attr; wxMapTreeAttr::iterator it = m_attrs.find(item.m_pItem); if ( it == m_attrs.end() ) @@ -1284,6 +1369,8 @@ void wxTreeCtrl::SetItemFont(const wxTreeItemId& item, const wxFont& font) bool wxTreeCtrl::IsVisible(const wxTreeItemId& item) const { + wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") ); + if ( item == wxTreeItemId(TVI_ROOT) ) { // virtual (hidden) root is never visible @@ -1297,12 +1384,24 @@ bool wxTreeCtrl::IsVisible(const wxTreeItemId& item) const // the HTREEITEM with TVM_GETITEMRECT *(HTREEITEM *)&rect = HITEM(item); - // false means get item rect for the whole item, not only text - return SendMessage(GetHwnd(), TVM_GETITEMRECT, false, (LPARAM)&rect) != 0; + // true means to get rect for just the text, not the whole line + if ( !::SendMessage(GetHwnd(), TVM_GETITEMRECT, true, (LPARAM)&rect) ) + { + // if TVM_GETITEMRECT returned false, then the item is definitely not + // visible (because its parent is not expanded) + return false; + } + + // however if it returned true, the item might still be outside the + // currently visible part of the tree, test for it (notice that partly + // visible means visible here) + return rect.bottom > 0 && rect.top < GetClientSize().y; } bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const { + wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") ); + wxTreeViewItem tvItem(item, TVIF_CHILDREN); DoGetItem(&tvItem); @@ -1311,8 +1410,7 @@ bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const bool wxTreeCtrl::IsExpanded(const wxTreeItemId& item) const { - // probably not a good idea to put it here - //wxASSERT( ItemHasChildren(item) ); + wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") ); wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_EXPANDED); DoGetItem(&tvItem); @@ -1322,6 +1420,8 @@ bool wxTreeCtrl::IsExpanded(const wxTreeItemId& item) const bool wxTreeCtrl::IsSelected(const wxTreeItemId& item) const { + wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") ); + wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_SELECTED); DoGetItem(&tvItem); @@ -1330,6 +1430,8 @@ bool wxTreeCtrl::IsSelected(const wxTreeItemId& item) const bool wxTreeCtrl::IsBold(const wxTreeItemId& item) const { + wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") ); + wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_BOLD); DoGetItem(&tvItem); @@ -1359,6 +1461,8 @@ wxTreeItemId wxTreeCtrl::GetSelection() const wxTreeItemId wxTreeCtrl::GetItemParent(const wxTreeItemId& item) const { + wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); + HTREEITEM hItem; if ( IS_VIRTUAL_ROOT(item) ) @@ -1382,6 +1486,8 @@ wxTreeItemId wxTreeCtrl::GetItemParent(const wxTreeItemId& item) const wxTreeItemId wxTreeCtrl::GetFirstChild(const wxTreeItemId& item, wxTreeItemIdValue& cookie) const { + wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); + // remember the last child returned in 'cookie' cookie = TreeView_GetChild(GetHwnd(), HITEM(item)); @@ -1391,8 +1497,14 @@ wxTreeItemId wxTreeCtrl::GetFirstChild(const wxTreeItemId& item, wxTreeItemId wxTreeCtrl::GetNextChild(const wxTreeItemId& WXUNUSED(item), wxTreeItemIdValue& cookie) const { - wxTreeItemId item(TreeView_GetNextSibling(GetHwnd(), - HITEM(wxTreeItemId(cookie)))); + wxTreeItemId fromCookie(cookie); + + HTREEITEM hitem = HITEM(fromCookie); + + hitem = TreeView_GetNextSibling(GetHwnd(), hitem); + + wxTreeItemId item(hitem); + cookie = item.m_pItem; return item; @@ -1403,6 +1515,8 @@ wxTreeItemId wxTreeCtrl::GetNextChild(const wxTreeItemId& WXUNUSED(item), wxTreeItemId wxTreeCtrl::GetFirstChild(const wxTreeItemId& item, long& cookie) const { + wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); + cookie = (long)TreeView_GetChild(GetHwnd(), HITEM(item)); return wxTreeItemId((void *)cookie); @@ -1411,11 +1525,14 @@ wxTreeItemId wxTreeCtrl::GetFirstChild(const wxTreeItemId& item, wxTreeItemId wxTreeCtrl::GetNextChild(const wxTreeItemId& WXUNUSED(item), long& cookie) const { - wxTreeItemId item(TreeView_GetNextSibling - ( - GetHwnd(), - HITEM(wxTreeItemId((void *)cookie) - ))); + wxTreeItemId fromCookie((void *)cookie); + + HTREEITEM hitem = HITEM(fromCookie); + + hitem = TreeView_GetNextSibling(GetHwnd(), hitem); + + wxTreeItemId item(hitem); + cookie = (long)item.m_pItem; return item; @@ -1425,6 +1542,8 @@ wxTreeItemId wxTreeCtrl::GetNextChild(const wxTreeItemId& WXUNUSED(item), wxTreeItemId wxTreeCtrl::GetLastChild(const wxTreeItemId& item) const { + wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); + // can this be done more efficiently? wxTreeItemIdValue cookie; @@ -1441,11 +1560,13 @@ wxTreeItemId wxTreeCtrl::GetLastChild(const wxTreeItemId& item) const wxTreeItemId wxTreeCtrl::GetNextSibling(const wxTreeItemId& item) const { + wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); return wxTreeItemId(TreeView_GetNextSibling(GetHwnd(), HITEM(item))); } wxTreeItemId wxTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const { + wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); return wxTreeItemId(TreeView_GetPrevSibling(GetHwnd(), HITEM(item))); } @@ -1456,6 +1577,7 @@ wxTreeItemId wxTreeCtrl::GetFirstVisibleItem() const wxTreeItemId wxTreeCtrl::GetNextVisible(const wxTreeItemId& item) const { + wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); wxASSERT_MSG( IsVisible(item), wxT("The item you call GetNextVisible() for must be visible itself!")); return wxTreeItemId(TreeView_GetNextVisible(GetHwnd(), HITEM(item))); @@ -1463,6 +1585,7 @@ wxTreeItemId wxTreeCtrl::GetNextVisible(const wxTreeItemId& item) const wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const { + wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); wxASSERT_MSG( IsVisible(item), wxT("The item you call GetPrevVisible() for must be visible itself!")); return wxTreeItemId(TreeView_GetPrevVisible(GetHwnd(), HITEM(item))); @@ -1474,6 +1597,8 @@ wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const bool wxTreeCtrl::IsItemChecked(const wxTreeItemId& item) const { + wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") ); + // receive the desired information. wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_STATEIMAGEMASK); DoGetItem(&tvItem); @@ -1484,6 +1609,8 @@ bool wxTreeCtrl::IsItemChecked(const wxTreeItemId& item) const void wxTreeCtrl::SetItemCheck(const wxTreeItemId& item, bool check) { + wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); + // receive the desired information. wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_STATEIMAGEMASK); @@ -1528,7 +1655,7 @@ wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parent, } UINT mask = 0; - if ( !text.IsEmpty() ) + if ( !text.empty() ) { mask |= TVIF_TEXT; tvIns.item.pszText = (wxChar *)text.c_str(); // cast is ok @@ -1592,6 +1719,26 @@ wxTreeItemId wxTreeCtrl::InsertItem(const wxTreeItemId& parent, image, selImage, NULL); } +wxImageList *wxTreeCtrl::GetImageList(int) const +{ + return GetImageList(); +} + +void wxTreeCtrl::SetImageList(wxImageList *imageList, int) +{ + SetImageList(imageList); +} + +int wxTreeCtrl::GetItemSelectedImage(const wxTreeItemId& item) const +{ + return GetItemImage(item, wxTreeItemIcon_Selected); +} + +void wxTreeCtrl::SetItemSelectedImage(const wxTreeItemId& item, int image) +{ + SetItemImage(item, image, wxTreeItemIcon_Selected); +} + #endif // WXWIN_COMPATIBILITY_2_4 wxTreeItemId wxTreeCtrl::AddRoot(const wxString& text, @@ -1772,11 +1919,15 @@ void wxTreeCtrl::Toggle(const wxTreeItemId& item) DoExpand(item, TVE_TOGGLE); } +#if WXWIN_COMPATIBILITY_2_4 + void wxTreeCtrl::ExpandItem(const wxTreeItemId& item, int action) { DoExpand(item, action); } +#endif + void wxTreeCtrl::Unselect() { wxASSERT_MSG( !(m_windowStyle & wxTR_MULTIPLE), @@ -1808,19 +1959,22 @@ void wxTreeCtrl::UnselectAll() } } -void wxTreeCtrl::SelectItem(const wxTreeItemId& item) +void wxTreeCtrl::SelectItem(const wxTreeItemId& item, bool select) { if ( m_windowStyle & wxTR_MULTIPLE ) { #if wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE // selecting the item means checking it - SetItemCheck(item); + SetItemCheck(item, select); #else // !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE - ::SelectItem(GetHwnd(), HITEM(item)); + ::SelectItem(GetHwnd(), HITEM(item), select); #endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE/!wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE } else { + wxASSERT_MSG( select, + _T("SelectItem(false) works only for multiselect") ); + // inspite of the docs (MSDN Jan 99 edition), we don't seem to receive // the notification from the control (i.e. TVN_SELCHANG{ED|ING}), so // send them ourselves @@ -1836,7 +1990,7 @@ void wxTreeCtrl::SelectItem(const wxTreeItemId& item) { wxLogLastError(wxT("TreeView_SelectItem")); } - else + else // ok { event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED); (void)GetEventHandler()->ProcessEvent(event); @@ -1846,6 +2000,16 @@ void wxTreeCtrl::SelectItem(const wxTreeItemId& item) } } +void wxTreeCtrl::UnselectItem(const wxTreeItemId& item) +{ + SelectItem(item, false); +} + +void wxTreeCtrl::ToggleItemSelection(const wxTreeItemId& item) +{ + SelectItem(item, !IsSelected(item)); +} + void wxTreeCtrl::EnsureVisible(const wxTreeItemId& item) { // no error return @@ -1881,6 +2045,8 @@ void wxTreeCtrl::DeleteTextCtrl() m_textCtrl->SetHWND(0); delete m_textCtrl; m_textCtrl = NULL; + + m_idEdited.Unset(); } } @@ -1891,6 +2057,7 @@ wxTextCtrl* wxTreeCtrl::EditLabel(const wxTreeItemId& item, DeleteTextCtrl(); + m_idEdited = item; m_textCtrl = (wxTextCtrl *)textControlClass->CreateObject(); HWND hWnd = (HWND) TreeView_EditLabel(GetHwnd(), HITEM(item)); @@ -1908,7 +2075,7 @@ wxTextCtrl* wxTreeCtrl::EditLabel(const wxTreeItemId& item, } // End label editing, optionally cancelling the edit -void wxTreeCtrl::EndEditLabel(const wxTreeItemId& WXUNUSED(item), bool discardChanges) +void wxTreeCtrl::DoEndEditLabel(bool discardChanges) { TreeView_EndEditLabelNow(GetHwnd(), discardChanges); @@ -2017,6 +2184,8 @@ int wxTreeCtrl::OnCompareItems(const wxTreeItemId& item1, void wxTreeCtrl::SortChildren(const wxTreeItemId& item) { + wxCHECK_RET( item.IsOk(), wxT("invalid tree item") ); + // rely on the fact that TreeView_SortChildren does the same thing as our // default behaviour, i.e. sorts items alphabetically and so call it // directly if we're not in derived class (much more efficient!) @@ -2066,13 +2235,24 @@ bool wxTreeCtrl::MSWCommand(WXUINT cmd, WXWORD id) // only do it during dragging, minimize wxWin overhead (this is important for // WM_MOUSEMOVE as they're a lot of them) by catching Windows messages directly // instead of passing by wxWin events -long wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) +WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) { bool processed = false; - long rc = 0; - bool isMultiple = (GetWindowStyle() & wxTR_MULTIPLE) != 0; + WXLRESULT rc = 0; + bool isMultiple = HasFlag(wxTR_MULTIPLE); - if ( (nMsg >= WM_MOUSEFIRST) && (nMsg <= WM_MOUSELAST) ) +#ifdef WM_CONTEXTMENU + if ( nMsg == WM_CONTEXTMENU ) + { + wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_MENU, GetId() ); + event.m_item = GetSelection(); + event.SetEventObject( this ); + if ( GetEventHandler()->ProcessEvent(event) ) + processed = true; + //else: continue with generating wxEVT_CONTEXT_MENU in base class code + } +#endif // WM_CONTEXTMENU + else if ( (nMsg >= WM_MOUSEFIRST) && (nMsg <= WM_MOUSELAST) ) { // we only process mouse messages here and these parameters have the // same meaning for all of them @@ -2083,10 +2263,10 @@ long wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) switch ( nMsg ) { case WM_RBUTTONDOWN: - // if the item we are about to right click on - // is not already select, remove the entire - // previous selection - if (!::IsItemSelected(GetHwnd(), htItem)) + // if the item we are about to right click on is not already + // selected or if we click outside of any item, remove the + // entire previous selection + if ( !htItem || !::IsItemSelected(GetHwnd(), htItem) ) { UnselectAll(); } @@ -2095,6 +2275,13 @@ long wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) // newly selected item ::SelectItem(GetHwnd(), htItem); ::SetFocus(GetHwnd(), htItem); + + // default WM_RBUTTONUP handler enters modal loop inside + // DefWindowProc() waiting for WM_RBUTTONDOWN and then sends + // the resulting WM_CONTEXTMENU to the parent window, not us, + // which completely breaks everything so simply don't let it + // see this message at all + processed = true; break; #if !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE @@ -2106,7 +2293,7 @@ long wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) SetFocus(); // toggle selected state - ToggleItemSelection(GetHwnd(), htItem); + ::ToggleItemSelection(GetHwnd(), htItem); ::SetFocus(GetHwnd(), htItem); @@ -2137,7 +2324,7 @@ long wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) { // avoid doing anything if we click on the only // currently selected item - + wxArrayTreeItemIds selections; size_t count = GetSelections(selections); if ( count == 0 || @@ -2148,7 +2335,7 @@ long wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) // user clicked outside of the present selection. // otherwise, perform the deselection on mouse-up. // this allows multiple drag and drop to work. - + if (IsItemSelected(GetHwnd(), htItem)) { ::SetFocus(GetHwnd(), htItem); @@ -2245,9 +2432,9 @@ long wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) // TreeView_GetItemRect() will return false if item is not visible, // which may happen perfectly well if ( TreeView_GetItemRect(GetHwnd(), HITEM_PTR(selections[n]), - &rect, true) ) + &rect, TRUE) ) { - ::InvalidateRect(GetHwnd(), &rect, false); + ::InvalidateRect(GetHwnd(), &rect, FALSE); } } } @@ -2269,7 +2456,7 @@ long wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) { if ( bCtrl ) { - ToggleItemSelection(GetHwnd(), htSel); + ::ToggleItemSelection(GetHwnd(), htSel); } else { @@ -2329,6 +2516,24 @@ long wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) processed = true; } } + else if ( nMsg == WM_COMMAND ) + { + // if we receive a EN_KILLFOCUS command from the in-place edit control + // used for label editing, make sure to end editing + WORD id, cmd; + WXHWND hwnd; + UnpackCommand(wParam, lParam, &id, &hwnd, &cmd); + + if ( cmd == EN_KILLFOCUS ) + { + if ( m_textCtrl && m_textCtrl->GetHandle() == hwnd ) + { + DoEndEditLabel(); + + processed = true; + } + } + } if ( !processed ) rc = wxControl::MSWWindowProc(nMsg, wParam, lParam); @@ -2372,6 +2577,10 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) eventType = wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT; TV_DISPINFO *info = (TV_DISPINFO *)lParam; + // although the user event handler may still veto it, it is + // important to set it now so that calls to SetItemText() from + // the event handler would change the text controls contents + m_idEdited = event.m_item = info->item.hItem; event.m_label = info->item.pszText; event.m_editCancelled = false; @@ -2404,16 +2613,33 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) event.m_item = info->item.hItem; event.m_label = info->item.pszText; - if (info->item.pszText == NULL) - { - event.m_editCancelled = true; - } - else - { - event.m_editCancelled = false; - } + event.m_editCancelled = info->item.pszText == NULL; + break; + } + +#ifndef __WXWINCE__ + // These *must* not be removed or TVN_GETINFOTIP will + // not be processed each time the mouse is moved + // and the tooltip will only ever update once. + case TTN_NEEDTEXTA: + case TTN_NEEDTEXTW: + { + *result = 0; + + break; + } + + case TVN_GETINFOTIP: + { + eventType = wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP; + NMTVGETINFOTIP *info = (NMTVGETINFOTIP*)lParam; + + // Which item are we trying to get a tooltip for? + event.m_item = info->hItem; + break; } +#endif case TVN_GETDISPINFO: eventType = wxEVT_COMMAND_TREE_GET_INFO; @@ -2505,7 +2731,7 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) } break; - // NB: MSLU is broken and sends TVN_SELCHANGEDA instead of + // NB: MSLU is broken and sends TVN_SELCHANGEDA instead of // TVN_SELCHANGEDW in Unicode mode under Win98. Therefore // we have to handle both messages: case TVN_SELCHANGEDA: @@ -2520,7 +2746,7 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) eventType = wxEVT_COMMAND_TREE_SEL_CHANGING; //else: already set above - if (hdr->code == TVN_SELCHANGINGW || + if (hdr->code == TVN_SELCHANGINGW || hdr->code == TVN_SELCHANGEDW) { NM_TREEVIEWW* tv = (NM_TREEVIEWW *)lParam; @@ -2536,7 +2762,9 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) } break; -#if defined(_WIN32_IE) && _WIN32_IE >= 0x300 && !wxUSE_COMCTL32_SAFELY && !( defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 1, 0 ) ) + // instead of explicitly checking for _WIN32_IE, check if the + // required symbols are available in the headers +#if defined(CDDS_PREPAINT) && !wxUSE_COMCTL32_SAFELY case NM_CUSTOMDRAW: { LPNMTVCUSTOMDRAW lptvcd = (LPNMTVCUSTOMDRAW)lParam; @@ -2564,60 +2792,42 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) wxTreeItemAttr * const attr = it->second; - HFONT hFont; - if ( attr->HasFont() ) - { - hFont = GetHfontOf(attr->GetFont()); - } - else - { - hFont = 0; - } - - wxColour colText; - if ( attr->HasTextColour() ) - { - colText = attr->GetTextColour(); - } - else - { - colText = GetForegroundColour(); - } - - // selection colours should override ours - if ( nmcd.uItemState & CDIS_SELECTED ) - { - lptvcd->clrTextBk = - ::GetSysColor(COLOR_HIGHLIGHT); - lptvcd->clrText = - ::GetSysColor(COLOR_HIGHLIGHTTEXT); - } - else // !selected + // selection colours should override ours, + // otherwise it is too confusing ot the user + if ( !(nmcd.uItemState & CDIS_SELECTED) ) { wxColour colBack; if ( attr->HasBackgroundColour() ) { colBack = attr->GetBackgroundColour(); + lptvcd->clrTextBk = wxColourToRGB(colBack); } - else + } + + // but we still want to keep the special foreground + // colour when we don't have focus (we can't keep + // it when we do, it would usually be unreadable on + // the almost inverted bg colour...) + if ( !(nmcd.uItemState & CDIS_SELECTED) || + FindFocus() != this ) + { + wxColour colText; + if ( attr->HasTextColour() ) { - colBack = GetBackgroundColour(); + colText = attr->GetTextColour(); + lptvcd->clrText = wxColourToRGB(colText); } - - lptvcd->clrText = wxColourToRGB(colText); - lptvcd->clrTextBk = wxColourToRGB(colBack); } - // note that if we wanted to set colours for - // individual columns (subitems), we would have - // returned CDRF_NOTIFYSUBITEMREDRAW from here - if ( hFont ) + if ( attr->HasFont() ) { + HFONT hFont = GetHfontOf(attr->GetFont()); + ::SelectObject(nmcd.hdc, hFont); *result = CDRF_NEWFONT; } - else + else // no specific font { *result = CDRF_DODEFAULT; } @@ -2631,7 +2841,7 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) // we always process it return true; -#endif // _WIN32_IE >= 0x300 +#endif // have owner drawn support in headers case NM_CLICK: { @@ -2703,14 +2913,14 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) wxASSERT_MSG( !m_dragImage, _T("starting to drag once again?") ); m_dragImage = new wxDragImage(*this, event.m_item); - m_dragImage->BeginDrag(wxPoint(0, 0), this); + m_dragImage->BeginDrag(wxPoint(0,0), this); m_dragImage->Show(); } break; case TVN_DELETEITEM: { - // NB: we might process this message using wxWindows event + // NB: we might process this message using wxWidgets event // tables, but due to overhead of wxWin event system we // prefer to do it here ourself (otherwise deleting a tree // with many items is just too slow) @@ -2736,13 +2946,14 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) case TVN_BEGINLABELEDIT: // return true to cancel label editing *result = !event.IsAllowed(); + // set ES_WANTRETURN ( like we do in BeginLabelEdit ) - if(event.IsAllowed()) + if ( event.IsAllowed() ) { HWND hText = TreeView_GetEditControl(GetHwnd()); if(hText != NULL) { - // MBN: if m_textCtrl already has an HWND, it is a stale + // MBN: if m_textCtrl already has an HWND, it is a stale // pointer from a previous edit (because the user // didn't modify the label before dismissing the control, // and TVN_ENDLABELEDIT was not sent), so delete it @@ -2762,6 +2973,10 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) | wxTE_PROCESS_ENTER); } } + else // we had set m_idEdited before + { + m_idEdited.Unset(); + } break; case TVN_ENDLABELEDIT: @@ -2777,6 +2992,18 @@ bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) DeleteTextCtrl(); break; +#ifndef __WXWINCE__ + case TVN_GETINFOTIP: + { + // If the user permitted a tooltip change, change it + if (event.IsAllowed()) + { + SetToolTip(event.m_label); + } + } + break; +#endif + case TVN_SELCHANGING: case TVN_ITEMEXPANDING: // return true to prevent the action from happening @@ -2853,7 +3080,7 @@ void wxTreeCtrl::SetState(const wxTreeItemId& node, int state) // Select the specified state, or -1 == cycle to the next one. if ( state == -1 ) { - TreeView_GetItem(GetHwnd(), &tvi); + TreeView_GetItem(GetHwnd(), &tvi); state = STATEIMAGEMASKTOINDEX(tvi.state) + 1; if ( state == m_imageListState->GetImageCount() ) @@ -2863,7 +3090,7 @@ void wxTreeCtrl::SetState(const wxTreeItemId& node, int state) wxCHECK_RET( state < m_imageListState->GetImageCount(), _T("wxTreeCtrl::SetState(): item index out of bounds") ); - tvi.state = INDEXTOSTATEIMAGEMASK(state); + tvi.state = INDEXTOSTATEIMAGEMASK(state); TreeView_SetItem(GetHwnd(), &tvi); } @@ -2874,10 +3101,19 @@ int wxTreeCtrl::GetState(const wxTreeItemId& node) tvi.hItem = (HTREEITEM)node.m_pItem; tvi.mask = TVIF_STATE; tvi.stateMask = TVIS_STATEIMAGEMASK; - TreeView_GetItem(GetHwnd(), &tvi); + TreeView_GetItem(GetHwnd(), &tvi); return STATEIMAGEMASKTOINDEX(tvi.state); } +#if WXWIN_COMPATIBILITY_2_2 + +wxTreeItemId wxTreeCtrl::GetParent(const wxTreeItemId& item) const +{ + return GetItemParent( item ); +} + +#endif // WXWIN_COMPATIBILITY_2_2 + #endif // wxUSE_TREECTRL