#include "wx/msw/private.h"
+// include <commctrl.h> "properly"
+#include "wx/msw/wrapcctl.h"
+
+#include "wx/msw/missing.h"
+
// Set this to 1 to be _absolutely_ sure that repainting will work for all
// comctl32.dll versions
#define wxUSE_COMCTL32_SAFELY 0
#include "wx/msw/treectrl.h"
#include "wx/msw/dragimag.h"
-// include <commctrl.h> "properly"
-#include "wx/msw/wrapcctl.h"
-
// macros to hide the cast ugliness
// --------------------------------
{
m_selections.Empty();
- DoTraverse(tree->GetRootItem());
+ if (tree->GetCount() > 0)
+ DoTraverse(tree->GetRootItem());
}
virtual bool OnVisit(const wxTreeItemId& item)
if ( m_windowStyle & wxTR_FULL_ROW_HIGHLIGHT )
{
- if ( wxTheApp->GetComCtl32Version() >= 471 )
+ if ( wxApp::GetComCtl32Version() >= 471 )
wstyle |= TVS_FULLROWSELECT;
}
wstyle |= TVS_CHECKBOXES;
#endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
-#ifndef __WXWINCE__
+#if !defined(__WXWINCE__) && defined(TVS_INFOTIP)
// Need so that TVN_GETINFOTIP messages will be sent
wstyle |= TVS_INFOTIP;
#endif
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
if ( !wxWindowBase::SetBackgroundColour(colour) )
return false;
- SendMessage(GetHwnd(), TVM_SETBKCOLOR, 0, colour.GetPixel());
+ ::SendMessage(GetHwnd(), TVM_SETBKCOLOR, 0, colour.GetPixel());
#endif
return true;
if ( !wxWindowBase::SetForegroundColour(colour) )
return false;
- SendMessage(GetHwnd(), TVM_SETTEXTCOLOR, 0, colour.GetPixel());
+ ::SendMessage(GetHwnd(), TVM_SETTEXTCOLOR, 0, colour.GetPixel());
#endif
return true;
wxString wxTreeCtrl::GetItemText(const wxTreeItemId& item) const
{
- wxCHECK_MSG( item.IsOk(), wxT(""), wxT("invalid tree item") );
+ wxCHECK_MSG( item.IsOk(), wxEmptyString, wxT("invalid tree item") );
wxChar buf[512]; // the size is arbitrary...
bool wxTreeCtrl::IsVisible(const wxTreeItemId& item) const
{
- wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
+ wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
if ( item == wxTreeItemId(TVI_ROOT) )
{
bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const
{
- wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
+ wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
wxTreeViewItem tvItem(item, TVIF_CHILDREN);
DoGetItem(&tvItem);
bool wxTreeCtrl::IsExpanded(const wxTreeItemId& item) const
{
- wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
+ wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_EXPANDED);
DoGetItem(&tvItem);
bool wxTreeCtrl::IsSelected(const wxTreeItemId& item) const
{
- wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
+ wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_SELECTED);
DoGetItem(&tvItem);
bool wxTreeCtrl::IsBold(const wxTreeItemId& item) const
{
- wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
+ wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_BOLD);
DoGetItem(&tvItem);
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;
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;
bool wxTreeCtrl::IsItemChecked(const wxTreeItemId& item) const
{
- wxCHECK_MSG( item.IsOk(), FALSE, wxT("invalid tree item") );
+ wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
// receive the desired information.
wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_STATEIMAGEMASK);
}
UINT mask = 0;
- if ( !text.IsEmpty() )
+ if ( !text.empty() )
{
mask |= TVIF_TEXT;
tvIns.item.pszText = (wxChar *)text.c_str(); // cast is ok
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,
}
#if WXWIN_COMPATIBILITY_2_4
+
void wxTreeCtrl::ExpandItem(const wxTreeItemId& item, int action)
{
DoExpand(item, action);
}
+
#endif
void wxTreeCtrl::Unselect()
::UnselectItem(GetHwnd(), HITEM_PTR(selections[n]));
#endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE/!wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
}
+
+ m_htSelStart.Unset();
}
else
{
}
// 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);
{
bool processed = false;
WXLRESULT rc = 0;
- bool isMultiple = (GetWindowStyle() & wxTR_MULTIPLE) != 0;
+ bool isMultiple = HasFlag(wxTR_MULTIPLE);
- if ( (nMsg >= WM_MOUSEFIRST) && (nMsg <= WM_MOUSELAST) )
+ // This message is sent after a right-click, or when the "menu" key is pressed
+ if ( nMsg == WM_CONTEXTMENU )
+ {
+ int x = GET_X_LPARAM(lParam),
+ y = GET_Y_LPARAM(lParam);
+ // Convert the screen point to a client point
+ wxPoint MenuPoint = ScreenToClient(wxPoint(x, y));
+
+ wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_MENU, GetId() );
+
+ // can't use GetSelection() here as it would assert in multiselect mode
+ event.m_item = wxTreeItemId(TreeView_GetSelection(GetHwnd()));
+ event.SetEventObject( this );
+
+ // Get the bounding rectangle for the item, including the non-text areas
+ wxRect ItemRect;
+ GetBoundingRect(event.m_item, ItemRect, false);
+ // If the point is inside the bounding rectangle, use it as the click position.
+ // This should be the case for WM_CONTEXTMENU as the result of a right-click
+ if (ItemRect.Inside(MenuPoint))
+ {
+ event.m_pointDrag = MenuPoint;
+ }
+ // Use the Explorer standard of putting the menu at the left edge of the text,
+ // in the vertical middle of the text. Should be the case for the "menu" key
+ else
+ {
+ // Use the bounding rectangle of only the text part
+ GetBoundingRect(event.m_item, ItemRect, true);
+ event.m_pointDrag = wxPoint(ItemRect.GetX(), ItemRect.GetY() + ItemRect.GetHeight() / 2);
+ }
+
+ if ( GetEventHandler()->ProcessEvent(event) )
+ processed = true;
+ //else: continue with generating wxEVT_CONTEXT_MENU in base class code
+ }
+ 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
int x = GET_X_LPARAM(lParam),
y = GET_Y_LPARAM(lParam);
HTREEITEM htItem = GetItemFromPoint(GetHwnd(), x, y);
+
+ TV_HITTESTINFO tvht;
+ tvht.pt.x = x;
+ tvht.pt.y = y;
+
+ TreeView_HitTest(GetHwnd(), &tvht);
switch ( nMsg )
{
#if !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
case WM_LBUTTONDOWN:
- if ( htItem && isMultiple )
+ if ( htItem && isMultiple && (tvht.flags & TVHT_ONITEM) != 0 )
{
+ m_htClickedItem = (WXHTREEITEM) htItem;
+ m_ptClick = wxPoint(x, y);
+
if ( wParam & MK_CONTROL )
{
SetFocus();
m_htSelStart = TreeView_GetSelection(GetHwnd());
}
- SelectRange(GetHwnd(), HITEM(m_htSelStart), htItem,
+ if ( m_htSelStart )
+ SelectRange(GetHwnd(), HITEM(m_htSelStart), htItem,
!(wParam & MK_CONTROL));
+ else
+ ::SelectItem(GetHwnd(), htItem);
::SetFocus(GetHwnd(), htItem);
{
// avoid doing anything if we click on the only
// currently selected item
+
+ SetFocus();
wxArrayTreeItemIds selections;
size_t count = GetSelections(selections);
// otherwise, perform the deselection on mouse-up.
// this allows multiple drag and drop to work.
- if (IsItemSelected(GetHwnd(), htItem))
- {
- ::SetFocus(GetHwnd(), htItem);
- }
- else
+ if (!IsItemSelected(GetHwnd(), htItem))
{
UnselectAll();
TreeView_SelectItem(GetHwnd(), 0);
::SelectItem(GetHwnd(), htItem);
}
+ ::SetFocus(GetHwnd(), htItem);
+ processed = true;
}
// reset on any click without Shift
#endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
case WM_MOUSEMOVE:
+#ifndef __WXWINCE__
+ if ( m_htClickedItem )
+ {
+ int cx = abs(m_ptClick.x - x);
+ int cy = abs(m_ptClick.y - y);
+
+ if ( cx > GetSystemMetrics( SM_CXDRAG ) || cy > GetSystemMetrics( SM_CYDRAG ) )
+ {
+ HWND pWnd = ::GetParent( GetHwnd() );
+ if ( pWnd )
+ {
+ NM_TREEVIEW tv;
+
+ tv.hdr.hwndFrom = GetHwnd();
+ tv.hdr.idFrom = ::GetWindowLong( GetHwnd(), GWL_ID );
+ tv.hdr.code = TVN_BEGINDRAG;
+
+ tv.itemNew.hItem = HITEM(m_htClickedItem);
+
+ TVITEM tviAux;
+ ZeroMemory(&tviAux, sizeof(tviAux));
+ tviAux.hItem = HITEM(m_htClickedItem);
+ tviAux.mask = TVIF_STATE | TVIF_PARAM;
+ tviAux.stateMask = 0xffffffff;
+ TreeView_GetItem( GetHwnd(), &tviAux );
+
+ tv.itemNew.state = tviAux.state;
+ tv.itemNew.lParam = tviAux.lParam;
+
+ tv.ptDrag.x = x;
+ tv.ptDrag.y = y;
+
+ ::SendMessage( pWnd, WM_NOTIFY, tv.hdr.idFrom, (LPARAM)&tv );
+ }
+ m_htClickedItem.Unset();
+ }
+ }
+#endif // __WXWINCE__
+
if ( m_dragImage )
{
m_dragImage->Move(wxPoint(x, y));
{
UnselectAll();
TreeView_SelectItem(GetHwnd(), htItem);
+ ::SelectItem(GetHwnd(), htItem);
+ ::SetFocus(GetHwnd(), htItem);
}
+ m_htClickedItem.Unset();
}
// fall through
// 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);
}
}
}
bool bCtrl = wxIsCtrlDown(),
bShift = wxIsShiftDown();
- // we handle.arrows and space, but not page up/down and home/end: the
- // latter should be easy, but not the former
-
HTREEITEM htSel = (HTREEITEM)TreeView_GetSelection(GetHwnd());
- if ( !m_htSelStart )
- {
- m_htSelStart = htSel;
- }
-
- if ( wParam == VK_SPACE )
+ switch ( wParam )
{
- if ( bCtrl )
- {
- ::ToggleItemSelection(GetHwnd(), htSel);
- }
- else
- {
- UnselectAll();
-
- ::SelectItem(GetHwnd(), htSel);
- }
+ case VK_SPACE:
+ if ( bCtrl )
+ {
+ ::ToggleItemSelection(GetHwnd(), htSel);
+ }
+ else
+ {
+ UnselectAll();
- processed = true;
- }
- else if ( wParam == VK_UP || wParam == VK_DOWN )
- {
- if ( !bCtrl && !bShift )
- {
- // no modifiers, just clear selection and then let the default
- // processing to take place
- UnselectAll();
- }
- else if ( htSel )
- {
- (void)wxControl::MSWWindowProc(nMsg, wParam, lParam);
+ ::SelectItem(GetHwnd(), htSel);
+ }
- HTREEITEM htNext = (HTREEITEM)(wParam == VK_UP
- ? TreeView_GetPrevVisible(GetHwnd(), htSel)
- : TreeView_GetNextVisible(GetHwnd(), htSel));
+ processed = true;
+ break;
- if ( !htNext )
+ case VK_UP:
+ case VK_DOWN:
+ if ( !bCtrl && !bShift )
{
- // at the top/bottom
- htNext = htSel;
+ // no modifiers, just clear selection and then let the default
+ // processing to take place
+ UnselectAll();
}
-
- if ( bShift )
+ else if ( htSel )
{
- SelectRange(GetHwnd(), HITEM(m_htSelStart), htNext);
+ (void)wxControl::MSWWindowProc(nMsg, wParam, lParam);
+
+ HTREEITEM htNext = (HTREEITEM)
+ TreeView_GetNextItem
+ (
+ GetHwnd(),
+ htSel,
+ wParam == VK_UP ? TVGN_PREVIOUSVISIBLE
+ : TVGN_NEXTVISIBLE
+ );
+
+ if ( !htNext )
+ {
+ // at the top/bottom
+ htNext = htSel;
+ }
+
+ if ( bShift )
+ {
+ if ( !m_htSelStart )
+ m_htSelStart = htSel;
+
+ SelectRange(GetHwnd(), HITEM(m_htSelStart), htNext);
+ }
+ else // bCtrl
+ {
+ // without changing selection
+ ::SetFocus(GetHwnd(), htNext);
+ }
+
+ processed = true;
}
- else // bCtrl
+ break;
+
+ case VK_HOME:
+ case VK_END:
+ case VK_PRIOR:
+ case VK_NEXT:
+ // TODO: handle Shift/Ctrl with these keys
+ if ( !bCtrl && !bShift )
{
- // without changing selection
- ::SetFocus(GetHwnd(), htNext);
+ UnselectAll();
+
+ m_htSelStart.Unset();
}
+ }
+ }
+#endif // !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
+ 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;
}
}
}
-#endif // !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
- else if ( nMsg == WM_CHAR )
+
+ if ( !processed )
+ rc = wxControl::MSWWindowProc(nMsg, wParam, lParam);
+
+ return rc;
+}
+
+WXLRESULT
+wxTreeCtrl::MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
+{
+ // default WM_RBUTTONDOWN handler enters modal loop inside DefWindowProc()
+ // waiting for WM_RBUTTONUP 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
+ if ( nMsg == WM_RBUTTONDOWN )
+ return 0;
+
+ // but because of the above we don't get NM_RCLICK which is normally
+ // generated by tree window proc when the modal loop mentioned above ends
+ // because the mouse is released -- synthesize it ourselves instead
+ if ( nMsg == WM_RBUTTONUP )
{
- // don't let the control process Space and Return keys because it
+ NMHDR hdr;
+ hdr.hwndFrom = GetHwnd();
+ hdr.idFrom = GetId();
+ hdr.code = NM_RCLICK;
+
+ WXLPARAM rc;
+ MSWOnNotify(GetId(), (LPARAM)&hdr, &rc);
+
+ // continue as usual
+ }
+
+ if ( nMsg == WM_CHAR )
+ {
+ // also don't let the control process Space and Return keys because it
// doesn't do anything useful with them anyhow but always beeps
// annoyingly when it receives them and there is no way to turn it off
// simply if you just process TREEITEM_ACTIVATED event to which Space
// and Enter presses are mapped in your code
if ( wParam == VK_SPACE || wParam == VK_RETURN )
- {
- processed = true;
- }
+ return 0;
}
- if ( !processed )
- rc = wxControl::MSWWindowProc(nMsg, wParam, lParam);
-
- return rc;
+ return wxControl::MSWDefWindowProc(nMsg, wParam, lParam);
}
// process WM_NOTIFY Windows message
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;
break;
}
+#ifdef TVN_GETINFOTIP
case TVN_GETINFOTIP:
{
eventType = wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP;
break;
}
+#endif
#endif
case TVN_GETDISPINFO:
// fabricate the lParam and wParam parameters sufficiently
// similar to the ones from a "real" WM_KEYDOWN so that
// CreateKeyEvent() works correctly
- WXLPARAM lParam =
- (::GetKeyState(VK_MENU) < 0 ? KF_ALTDOWN : 0) << 16;
+ const bool isAltDown = ::GetKeyState(VK_MENU) < 0;
+ WXLPARAM lParam = (isAltDown ? KF_ALTDOWN : 0) << 16;
WXWPARAM wParam = info->wVKey;
wParam);
// a separate event for Space/Return
- if ( !wxIsCtrlDown() && !wxIsShiftDown() &&
+ if ( !wxIsCtrlDown() && !wxIsShiftDown() && !isAltDown &&
((info->wVKey == VK_SPACE) || (info->wVKey == VK_RETURN)) )
{
wxTreeEvent event2(wxEVT_COMMAND_TREE_ITEM_ACTIVATED,
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_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)
| wxTE_PROCESS_ENTER);
}
}
+ else // we had set m_idEdited before
+ {
+ m_idEdited.Unset();
+ }
break;
case TVN_ENDLABELEDIT:
break;
#ifndef __WXWINCE__
+#ifdef TVN_GETINFOTIP
case TVN_GETINFOTIP:
{
// If the user permitted a tooltip change, change it
}
}
break;
+#endif
#endif
case TVN_SELCHANGING:
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