// headers
// ----------------------------------------------------------------------------
-#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
- #pragma implementation "treectrl.h"
-#endif
-
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#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
// --------------------------------
-// ptr is the real item id, i.e. wxTreeItemId::m_pItem
-#define HITEM_PTR(ptr) (HTREEITEM)(ptr)
-
-// item here is a wxTreeItemId
-#define HITEM(item) HITEM_PTR((item).m_pItem)
+// get HTREEITEM from wxTreeItemId
+#define HITEM(item) ((HTREEITEM)(((item).m_pItem)))
// the native control doesn't support multiple selections under MSW and we
// have 2 ways to emulate them: either using TVS_CHECKBOXES style and let
{
m_selections.Empty();
- DoTraverse(tree->GetRootItem());
+ if (tree->GetCount() > 0)
+ DoTraverse(tree->GetRootItem());
}
virtual bool OnVisit(const wxTreeItemId& item)
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
size_t nCount = children.Count();
for ( size_t n = 0; n < nCount; n++ )
{
- if ( !TreeView_DeleteItem(GetHwnd(), HITEM_PTR(children[n])) )
+ if ( !TreeView_DeleteItem(GetHwnd(), HITEM(children[n])) )
{
wxLogLastError(wxT("TreeView_DeleteItem"));
}
for ( size_t n = 0; n < count; n++ )
{
#if wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
- SetItemCheck(HITEM_PTR(selections[n]), false);
+ SetItemCheck(HITEM(selections[n]), false);
#else // !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
- ::UnselectItem(GetHwnd(), HITEM_PTR(selections[n]));
+ ::UnselectItem(GetHwnd(), HITEM(selections[n]));
#endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE/!wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
}
WXLRESULT rc = 0;
bool isMultiple = HasFlag(wxTR_MULTIPLE);
-#ifdef WM_CONTEXTMENU
+ // 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
}
-#endif // WM_CONTEXTMENU
else if ( (nMsg >= WM_MOUSEFIRST) && (nMsg <= WM_MOUSELAST) )
{
// we only process mouse messages here and these parameters have the
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);
if ( count == 0 ||
count > 1 ||
- HITEM_PTR(selections[0]) != htItem )
+ HITEM(selections[0]) != htItem )
{
// 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))
- {
- ::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]),
+ if ( TreeView_GetItemRect(GetHwnd(), HITEM(selections[n]),
&rect, TRUE) )
{
::InvalidateRect(GetHwnd(), &rect, FALSE);
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 )
+ {
+ 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
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,
break;
#ifndef __WXWINCE__
+#ifdef TVN_GETINFOTIP
case TVN_GETINFOTIP:
{
// If the user permitted a tooltip change, change it
}
}
break;
+#endif
#endif
case TVN_SELCHANGING: