+ return true;
+}
+
+bool wxTreeCtrl::MSWHandleTreeKeyDownEvent(WXWPARAM wParam, WXLPARAM lParam)
+{
+ wxTreeEvent keyEvent(wxEVT_COMMAND_TREE_KEY_DOWN, this);
+ keyEvent.m_evtKey = CreateKeyEvent(wxEVT_KEY_DOWN, wParam, lParam);
+
+ bool processed = HandleTreeEvent(keyEvent);
+
+ // generate a separate event for Space/Return
+ if ( !wxIsCtrlDown() && !wxIsShiftDown() && !wxIsAltDown() &&
+ ((wParam == VK_SPACE) || (wParam == VK_RETURN)) )
+ {
+ const HTREEITEM htSel = (HTREEITEM)TreeView_GetSelection(GetHwnd());
+ if ( htSel )
+ {
+ wxTreeEvent activatedEvent(wxEVT_COMMAND_TREE_ITEM_ACTIVATED,
+ this, htSel);
+ (void)HandleTreeEvent(activatedEvent);
+ }
+ }
+
+ return processed;
+}
+
+// we hook into WndProc to process WM_MOUSEMOVE/WM_BUTTONUP messages - as we
+// 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
+WXLRESULT
+wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
+{
+ bool processed = false;
+ WXLRESULT rc = 0;
+ bool isMultiple = HasFlag(wxTR_MULTIPLE);
+
+ if ( nMsg == WM_CONTEXTMENU )
+ {
+ int x = GET_X_LPARAM(lParam),
+ y = GET_Y_LPARAM(lParam);
+
+ // the item for which the menu should be shown
+ wxTreeItemId item;
+
+ // the position where the menu should be shown in client coordinates
+ // (so that it can be passed directly to PopupMenu())
+ wxPoint pt;
+
+ if ( x == -1 || y == -1 )
+ {
+ // this means that the event was generated from keyboard (e.g. with
+ // Shift-F10 or special Windows menu key)
+ //
+ // use the Explorer standard of putting the menu at the left edge
+ // of the text, in the vertical middle of the text
+ item = wxTreeItemId(TreeView_GetSelection(GetHwnd()));
+ if ( item.IsOk() )
+ {
+ // Use the bounding rectangle of only the text part
+ wxRect rect;
+ GetBoundingRect(item, rect, true);
+ pt = wxPoint(rect.GetX(), rect.GetY() + rect.GetHeight() / 2);
+ }
+ }
+ else // event from mouse, use mouse position
+ {
+ pt = ScreenToClient(wxPoint(x, y));
+
+ TV_HITTESTINFO tvhti;
+ tvhti.pt.x = pt.x;
+ tvhti.pt.y = pt.y;
+
+ if ( TreeView_HitTest(GetHwnd(), &tvhti) )
+ item = wxTreeItemId(tvhti.hItem);
+ }
+
+ // create the event
+ if ( item.IsOk() )
+ {
+ wxTreeEvent event(wxEVT_COMMAND_TREE_ITEM_MENU, this, item);
+
+ event.m_pointDrag = pt;
+
+ if ( HandleTreeEvent(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);
+
+ TV_HITTESTINFO tvht;
+ tvht.pt.x = x;
+ tvht.pt.y = y;
+
+ HTREEITEM htOldItem = TreeView_GetSelection(GetHwnd());
+ HTREEITEM htItem = TreeView_HitTest(GetHwnd(), &tvht);
+
+ switch ( nMsg )
+ {
+ case WM_LBUTTONDOWN:
+ if ( !isMultiple )
+ break;
+
+ m_htClickedItem.Unset();
+
+ if ( !MSWIsOnItem(tvht.flags) )
+ {
+ if ( tvht.flags & TVHT_ONITEMBUTTON )
+ {
+ // either it's going to be handled by user code or
+ // we're going to use it ourselves to toggle the
+ // branch, in either case don't pass it to the base
+ // class which would generate another mouse click event
+ // for it even though it's already handled here
+ processed = true;
+ SetFocus();
+
+ if ( !HandleMouseEvent(nMsg, x, y, wParam) )
+ {
+ if ( !IsExpanded(htItem) )
+ {
+ Expand(htItem);
+ }
+ else
+ {
+ Collapse(htItem);
+ }
+ }
+ }
+
+ m_focusLost = false;
+ break;
+ }
+
+ processed = true;
+ SetFocus();
+ m_htClickedItem = (WXHTREEITEM) htItem;
+ m_ptClick = wxPoint(x, y);
+
+ if ( wParam & MK_CONTROL )
+ {
+ if ( HandleMouseEvent(nMsg, x, y, wParam) )
+ {
+ m_htClickedItem.Unset();
+ break;
+ }
+
+ wxTreeEvent changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING,
+ this, htItem);
+ changingEvent.m_itemOld = htOldItem;
+
+ if ( IsTreeEventAllowed(changingEvent) )
+ {
+ // toggle selected state
+ DoToggleItemSelection(wxTreeItemId(htItem));
+
+ SetFocusedItem(wxTreeItemId(htItem));
+
+ // reset on any click without Shift
+ m_htSelStart.Unset();
+
+ wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED,
+ this, htItem);
+ changedEvent.m_itemOld = htOldItem;
+ (void)HandleTreeEvent(changedEvent);
+ }
+ }
+ else if ( wParam & MK_SHIFT )
+ {
+ if ( HandleMouseEvent(nMsg, x, y, wParam) )
+ {
+ m_htClickedItem.Unset();
+ break;
+ }
+
+ int srFlags = 0;
+ bool willChange = true;
+
+ if ( !(wParam & MK_CONTROL) )
+ {
+ srFlags |= SR_UNSELECT_OTHERS;
+ }
+
+ if ( !m_htSelStart )
+ {
+ // take the focused item
+ m_htSelStart = htOldItem;
+ }
+ else
+ {
+ willChange = SelectRange(GetHwnd(), HITEM(m_htSelStart),
+ htItem, srFlags | SR_SIMULATE);
+ }
+
+ if ( willChange )
+ {
+ wxTreeEvent changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING,
+ this, htItem);
+ changingEvent.m_itemOld = htOldItem;
+
+ if ( IsTreeEventAllowed(changingEvent) )
+ {
+ // this selects all items between the starting one
+ // and the current
+ if ( m_htSelStart )
+ {
+ SelectRange(GetHwnd(), HITEM(m_htSelStart),
+ htItem, srFlags);
+ }
+ else
+ {
+ DoSelectItem(wxTreeItemId(htItem));
+ }
+
+ SetFocusedItem(wxTreeItemId(htItem));
+
+ wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED,
+ this, htItem);
+ changedEvent.m_itemOld = htOldItem;
+ (void)HandleTreeEvent(changedEvent);
+ }
+ }
+ }
+ else // normal click
+ {
+ // avoid doing anything if we click on the only
+ // currently selected item
+
+ wxArrayTreeItemIds selections;
+ size_t count = GetSelections(selections);
+
+ if ( count == 0 ||
+ count > 1 ||
+ HITEM(selections[0]) != htItem )
+ {
+ if ( HandleMouseEvent(nMsg, x, y, wParam) )
+ {
+ m_htClickedItem.Unset();
+ break;
+ }
+
+ // 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 ( !IsItemSelected(GetHwnd(), htItem))
+ {
+ wxTreeEvent changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING,
+ this, htItem);
+ changingEvent.m_itemOld = htOldItem;
+
+ if ( IsTreeEventAllowed(changingEvent) )
+ {
+ DoUnselectAll();
+ DoSelectItem(wxTreeItemId(htItem));
+ SetFocusedItem(wxTreeItemId(htItem));
+
+ wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED,
+ this, htItem);
+ changedEvent.m_itemOld = htOldItem;
+ (void)HandleTreeEvent(changedEvent);
+ }
+ }
+ else
+ {
+ SetFocusedItem(wxTreeItemId(htItem));
+ m_mouseUpDeselect = true;
+ }
+ }
+ else // click on a single selected item
+ {
+ // don't interfere with the default processing in
+ // WM_MOUSEMOVE handler below as the default window
+ // proc will start the drag itself if we let have
+ // WM_LBUTTONDOWN
+ m_htClickedItem.Unset();
+
+ // prevent in-place editing from starting if focus lost
+ // since previous click
+ if ( m_focusLost )
+ {
+ ClearFocusedItem();
+ DoSelectItem(wxTreeItemId(htItem));
+ SetFocusedItem(wxTreeItemId(htItem));
+ }
+ else
+ {
+ processed = false;
+ }
+ }
+
+ // reset on any click without Shift
+ m_htSelStart.Unset();
+ }
+
+ m_focusLost = false;
+
+ // we consumed the event so we need to trigger state image
+ // click if needed
+ if ( processed )
+ {
+ if ( tvht.flags & TVHT_ONITEMSTATEICON )
+ {
+ m_triggerStateImageClick = true;
+ }
+ }
+ break;
+
+ case WM_RBUTTONDOWN:
+ if ( !isMultiple )
+ break;
+
+ processed = true;
+ SetFocus();
+
+ if ( HandleMouseEvent(nMsg, x, y, wParam) || !htItem )
+ {
+ break;
+ }
+
+ // default handler removes the highlight from the currently
+ // focused item when right mouse button is pressed on another
+ // one but keeps the remaining items highlighted, which is
+ // confusing, so override this default behaviour
+ if ( !IsItemSelected(GetHwnd(), htItem) )
+ {
+ wxTreeEvent changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING,
+ this, htItem);
+ changingEvent.m_itemOld = htOldItem;