#endif
-// this global variable is used on vista systems for preventing unwanted
-// item state changes in the vista tree control. It is only used in
+// this helper class is used on vista systems for preventing unwanted
+// item state changes in the vista tree control. It is only effective in
// multi-select mode on vista systems.
-static HTREEITEM gs_unlockItem = NULL;
+// The vista tree control includes some new code that originally broke the
+// multi-selection tree, causing seemingly spurious item selection state changes
+// during Shift or Ctrl-click item selection. (To witness the original broken
+// behavior, simply make IsLocked() below always return false). This problem was
+// solved by using the following class to 'unlock' an item's selection state.
class TreeItemUnlocker
{
public:
- TreeItemUnlocker(HTREEITEM item) { gs_unlockItem = item; }
- ~TreeItemUnlocker() { gs_unlockItem = NULL; }
+ // unlock a single item
+ TreeItemUnlocker(HTREEITEM item) { ms_unlockedItem = item; }
+
+ // unlock all items, don't use unless absolutely necessary
+ TreeItemUnlocker() { ms_unlockedItem = (HTREEITEM)-1; }
+
+ // lock everything back
+ ~TreeItemUnlocker() { ms_unlockedItem = NULL; }
+
+
+ // check if the item state is currently locked
+ static bool IsLocked(HTREEITEM item)
+ { return ms_unlockedItem != (HTREEITEM)-1 && item != ms_unlockedItem; }
+
+private:
+ static HTREEITEM ms_unlockedItem;
};
+HTREEITEM TreeItemUnlocker::ms_unlockedItem = NULL;
// ----------------------------------------------------------------------------
// private functions
tvIns.item.lParam = (LPARAM)param;
tvIns.item.mask = mask;
+ // don't use the hack below for the children of hidden root: this results
+ // in a crash inside comctl32.dll when we call TreeView_GetItemRect()
+ const bool firstChild = !IsHiddenRoot(parent) &&
+ !TreeView_GetChild(GetHwnd(), HITEM(parent));
+
HTREEITEM id = TreeView_InsertItem(GetHwnd(), &tvIns);
if ( id == 0 )
{
wxLogLastError(wxT("TreeView_InsertItem"));
}
+ // apparently some Windows versions (2000 and XP are reported to do this)
+ // sometimes don't refresh the tree after adding the first child and so we
+ // need this to make the "[+]" appear
+ if ( firstChild )
+ {
+ RECT rect;
+ TreeView_GetItemRect(GetHwnd(), HITEM(parent), &rect, FALSE);
+ ::InvalidateRect(GetHwnd(), &rect, FALSE);
+ }
+
// associate the application tree item with Win32 tree item handle
param->SetItem(id);
void wxTreeCtrl::Delete(const wxTreeItemId& item)
{
+ // unlock tree selections on vista, without this the
+ // tree ctrl will eventually crash after item deletion
+ TreeItemUnlocker unlock_all;
+
if ( !TreeView_DeleteItem(GetHwnd(), HITEM(item)) )
{
wxLogLastError(wxT("TreeView_DeleteItem"));
// delete all children (but don't delete the item itself)
void wxTreeCtrl::DeleteChildren(const wxTreeItemId& item)
{
+ // unlock tree selections on vista for the duration of this call
+ TreeItemUnlocker unlock_all;
+
wxTreeItemIdValue cookie;
wxArrayTreeItemIds children;
void wxTreeCtrl::DeleteAllItems()
{
+ // unlock tree selections on vista for the duration of this call
+ TreeItemUnlocker unlock_all;
+
// delete the "virtual" root item.
if ( GET_VIRTUAL_ROOT() )
{
: IDX_COLLAPSE]
[IDX_DONE],
this, item);
- (void)GetEventHandler()->ProcessEvent(event);
+ (void)HandleWindowEvent(event);
}
//else: change didn't took place, so do nothing at all
}
_T("SelectItem(false) works only for multiselect") );
wxTreeEvent event(wxEVT_COMMAND_TREE_SEL_CHANGING, this, item);
- if ( !GetEventHandler()->ProcessEvent(event) || event.IsAllowed() )
+ if ( !HandleWindowEvent(event) || event.IsAllowed() )
{
if ( HasFlag(wxTR_MULTIPLE) )
{
}
event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
- (void)GetEventHandler()->ProcessEvent(event);
+ (void)HandleWindowEvent(event);
}
//else: program vetoed the change
}
{
if ( msg->message == WM_KEYDOWN )
{
- if ( msg->wParam == VK_RETURN )
+ // Only eat VK_RETURN if not being used by the application in
+ // conjunction with modifiers
+ if ( (msg->wParam == VK_RETURN) && !wxIsAnyModifierDown() )
{
// we need VK_RETURN to generate wxEVT_COMMAND_TREE_ITEM_ACTIVATED
return false;
event.m_pointDrag = pt;
- if ( GetEventHandler()->ProcessEvent(event) )
+ if ( HandleWindowEvent(event) )
processed = true;
//else: continue with generating wxEVT_CONTEXT_MENU in base class code
}
// fire EVT_RIGHT_DOWN
HandleMouseEvent(nMsg, x, y, wParam);
-
+
// send NM_RCLICK
NMHDR nmhdr;
nmhdr.hwndFrom = GetHwnd();
nmhdr.idFrom, (LPARAM)&nmhdr);
// prevent tree control default processing, as we've
- // already done everything
+ // already done everything
processed = true;
}
break;
wxTreeEvent event(wxEVT_COMMAND_TREE_END_DRAG, this, htItem);
event.m_pointDrag = wxPoint(x, y);
- (void)GetEventHandler()->ProcessEvent(event);
+ (void)HandleWindowEvent(event);
// if we don't do it, the tree seems to think that 2 items
// are selected simultaneously which is quite weird
// fabricate the lParam and wParam parameters sufficiently
// similar to the ones from a "real" WM_KEYDOWN so that
// CreateKeyEvent() works correctly
- const bool isAltDown = ::GetKeyState(VK_MENU) < 0;
- WXLPARAM lParam = (isAltDown ? KF_ALTDOWN : 0) << 16;
+ WXLPARAM lParam = (wxIsAltDown() ? KF_ALTDOWN : 0) << 16;
WXWPARAM wParam = info->wVKey;
wParam);
// a separate event for Space/Return
- if ( !wxIsCtrlDown() && !wxIsShiftDown() && !isAltDown &&
+ if ( !wxIsAnyModifierDown() &&
((info->wVKey == VK_SPACE) || (info->wVKey == VK_RETURN)) )
{
wxTreeItemId item;
wxTreeEvent event2(wxEVT_COMMAND_TREE_ITEM_ACTIVATED,
this, item);
- (void)GetEventHandler()->ProcessEvent(event2);
+ (void)HandleWindowEvent(event2);
}
}
break;
// Vista's tree control has introduced some problems with our
// multi-selection tree. When TreeView_SelectItem() is called,
// the wrong items are deselected.
-
+
// Fortunately, Vista provides a new notification, TVN_ITEMCHANGING
// that can be used to regulate this incorrect behavior. The
// following messages will allow only the unlocked item's selection
// state to change
+
case TVN_ITEMCHANGINGA:
case TVN_ITEMCHANGINGW:
{
{
// get info about the item about to be changed
NMTVITEMCHANGE* info = (NMTVITEMCHANGE*)lParam;
- if (info->hItem != gs_unlockItem)
+ if (TreeItemUnlocker::IsLocked(info->hItem))
{
// item's state is locked, don't allow the change
// returning 1 will disallow the change
- *result = 1;
+ *result = 1;
return true;
}
}
if ( event.m_item.IsOk() )
event.SetClientObject(GetItemData(event.m_item));
- bool processed = GetEventHandler()->ProcessEvent(event);
+ bool processed = HandleWindowEvent(event);
// post processing
switch ( hdr->code )