HTREEITEM TreeItemUnlocker::ms_unlockedItem = NULL;
+// another helper class: set the variable to true during its lifetime and reset
+// it to false when it is destroyed
+//
+// it is currently always used with wxTreeCtrl::m_changingSelection
+class TempSetter
+{
+public:
+ TempSetter(bool& var) : m_var(var)
+ {
+ wxASSERT_MSG( !m_var, "variable shouldn't be already set" );
+ m_var = true;
+ }
+
+ ~TempSetter()
+ {
+ m_var = false;
+ }
+
+private:
+ bool& m_var;
+
+ wxDECLARE_NO_COPY_CLASS(TempSetter);
+};
+
// ----------------------------------------------------------------------------
// private functions
// ----------------------------------------------------------------------------
break;
default:
- wxFAIL_MSG( _T("unsupported wxTreeItemIcon value") );
+ wxFAIL_MSG( wxT("unsupported wxTreeItemIcon value") );
}
}
m_pVirtualRoot = NULL;
m_dragStarted = false;
m_focusLost = true;
+ m_changingSelection = false;
m_triggerStateImageClick = false;
// initialize the global array of events now as it can't be done statically
bool wxTreeCtrl::DoGetItem(wxTreeViewItem *tvItem) const
{
wxCHECK_MSG( tvItem->hItem != TVI_ROOT, false,
- _T("can't retrieve virtual root item") );
+ wxT("can't retrieve virtual root item") );
if ( !TreeView_GetItem(GetHwnd(), tvItem) )
{
wxCHECK_MSG( !HasFlag(wxTR_MULTIPLE), wxTreeItemId(),
wxT("this only works with single selection controls") );
+ return GetFocusedItem();
+}
+
+wxTreeItemId wxTreeCtrl::GetFocusedItem() const
+{
return wxTreeItemId(TreeView_GetSelection(GetHwnd()));
}
{
wxCHECK_MSG( parent.IsOk() || !TreeView_GetRoot(GetHwnd()),
wxTreeItemId(),
- _T("can't have more than one root in the tree") );
+ wxT("can't have more than one root in the tree") );
TV_INSERTSTRUCT tvIns;
tvIns.hParent = HITEM(parent);
{
if ( HasFlag(wxTR_HIDE_ROOT) )
{
- wxASSERT_MSG( !m_pVirtualRoot, _T("tree can have only a single root") );
+ wxASSERT_MSG( !m_pVirtualRoot, wxT("tree can have only a single root") );
// create a virtual root item, the parent for all the others
wxTreeItemParam *param = new wxTreeItemParam;
// assert, not check: if the index is invalid, we will append the item
// to the end
- wxASSERT_MSG( index == 0, _T("bad index in wxTreeCtrl::InsertItem") );
+ wxASSERT_MSG( index == 0, wxT("bad index in wxTreeCtrl::InsertItem") );
}
return DoInsertAfter(parent, idPrev, text, image, selectedImage, data);
}
}
- if ( !TreeView_DeleteItem(GetHwnd(), HITEM(item)) )
{
- wxLogLastError(wxT("TreeView_DeleteItem"));
- return;
+ TempSetter set(m_changingSelection);
+ if ( !TreeView_DeleteItem(GetHwnd(), HITEM(item)) )
+ {
+ wxLogLastError(wxT("TreeView_DeleteItem"));
+ return;
+ }
}
if ( !selected )
}
else
{
- ::SelectItem(GetHwnd(), HITEM(next), false);
- TreeView_SelectItem(GetHwnd(), 0);
+ DoUnselectItem(next);
+ ClearFocusedItem();
}
}
}
if ( IsTreeEventAllowed(changingEvent) )
{
- TreeView_SelectItem(GetHwnd(), 0);
+ ClearFocusedItem();
wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED,
this, wxTreeItemId());
}
else
{
- TreeView_SelectItem(GetHwnd(), 0);
+ ClearFocusedItem();
}
}
for ( size_t n = 0; n < count; n++ )
{
- ::UnselectItem(GetHwnd(), HITEM(selections[n]));
+ DoUnselectItem(selections[n]);
}
m_htSelStart.Unset();
if ( IsTreeEventAllowed(changingEvent) )
{
DoUnselectAll();
- TreeView_SelectItem(GetHwnd(), 0);
wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this);
changedEvent.m_itemOld = htFocus;
}
}
+void wxTreeCtrl::DoSelectItem(const wxTreeItemId& item, bool select)
+{
+ TempSetter set(m_changingSelection);
+
+ ::SelectItem(GetHwnd(), HITEM(item), select);
+}
+
void wxTreeCtrl::SelectItem(const wxTreeItemId& item, bool select)
{
- wxCHECK_RET( !IsHiddenRoot(item), _T("can't select hidden root item") );
+ wxCHECK_RET( !IsHiddenRoot(item), wxT("can't select hidden root item") );
- if ( IsSelected(item) == select )
+ if ( select == IsSelected(item) )
{
+ // nothing to do, the item is already in the requested state
return;
}
if ( IsTreeEventAllowed(changingEvent) )
{
HTREEITEM htFocus = (HTREEITEM)TreeView_GetSelection(GetHwnd());
- ::SelectItem(GetHwnd(), HITEM(item), select);
+ DoSelectItem(item, select);
if ( !htFocus )
{
- ::SetFocus(GetHwnd(), HITEM(item));
+ SetFocusedItem(item);
}
wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED,
(void)HandleTreeEvent(changedEvent);
}
}
- else
+ else // single selection
{
- wxASSERT_MSG( select,
- _T("SelectItem(false) works only for multiselect") );
+ wxTreeItemId itemOld, itemNew;
+ if ( select )
+ {
+ itemOld = GetSelection();
+ itemNew = item;
+ }
+ else // deselecting the currently selected item
+ {
+ itemOld = item;
+ // leave itemNew invalid
+ }
// in spite 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
- wxTreeEvent changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING, this, item);
+ wxTreeEvent
+ changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING, this, itemNew);
+ changingEvent.SetOldItem(itemOld);
if ( IsTreeEventAllowed(changingEvent) )
{
- if ( !TreeView_SelectItem(GetHwnd(), HITEM(item)) )
+ if ( !TreeView_SelectItem(GetHwnd(), HITEM(itemNew)) )
{
wxLogLastError(wxT("TreeView_SelectItem"));
}
else // ok
{
- ::SetFocus(GetHwnd(), HITEM(item));
+ SetFocusedItem(item);
wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED,
- this, item);
+ this, itemNew);
+ changedEvent.SetOldItem(itemOld);
(void)HandleTreeEvent(changedEvent);
}
}
void wxTreeCtrl::EnsureVisible(const wxTreeItemId& item)
{
- wxCHECK_RET( !IsHiddenRoot(item), _T("can't show hidden root item") );
+ wxCHECK_RET( !IsHiddenRoot(item), wxT("can't show hidden root item") );
// no error return
TreeView_EnsureVisible(GetHwnd(), HITEM(item));
}
}
+void wxTreeCtrl::ClearFocusedItem()
+{
+ TempSetter set(m_changingSelection);
+
+ if ( !TreeView_SelectItem(GetHwnd(), 0) )
+ {
+ wxLogLastError(wxT("TreeView_SelectItem"));
+ }
+}
+
+void wxTreeCtrl::SetFocusedItem(const wxTreeItemId& item)
+{
+ TempSetter set(m_changingSelection);
+
+ ::SetFocus(GetHwnd(), HITEM(item));
+}
+
+void wxTreeCtrl::DoUnselectItem(const wxTreeItemId& item)
+{
+ TempSetter set(m_changingSelection);
+
+ ::UnselectItem(GetHwnd(), HITEM(item));
+}
+
+void wxTreeCtrl::DoToggleItemSelection(const wxTreeItemId& item)
+{
+ TempSetter set(m_changingSelection);
+
+ ::ToggleItemSelection(GetHwnd(), HITEM(item));
+}
+
// ----------------------------------------------------------------------------
// sorting stuff
// ----------------------------------------------------------------------------
return true;
}
-bool wxTreeCtrl::MSWHandleSelectionKey(WPARAM vkey)
+bool wxTreeCtrl::MSWHandleSelectionKey(unsigned vkey)
{
const bool bCtrl = wxIsCtrlDown();
const bool bShift = wxIsShiftDown();
if ( IsTreeEventAllowed(changingEvent) )
{
- ::ToggleItemSelection(GetHwnd(), htSel);
+ DoToggleItemSelection(wxTreeItemId(htSel));
wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED,
this, htSel);
if ( IsTreeEventAllowed(changingEvent) )
{
DoUnselectAll();
- ::SelectItem(GetHwnd(), htSel);
+ DoSelectItem(wxTreeItemId(htSel));
wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED,
this, htSel);
if ( !bCtrl && !bShift )
{
wxArrayTreeItemIds selections;
- size_t count = GetSelections(selections);
wxTreeItemId next;
- if ( htSel && count > 0 )
+ if ( htSel )
{
next = vkey == VK_UP
? TreeView_GetPrevVisible(GetHwnd(), htSel)
if ( IsHiddenRoot(next) )
next = TreeView_GetChild(GetHwnd(), HITEM(next));
-
- if ( vkey == VK_DOWN )
- {
- wxTreeItemId next2 = TreeView_GetNextVisible(
- GetHwnd(), HITEM(next));
- if ( next2 )
- next = next2;
- }
}
if ( !next.IsOk() )
if ( IsTreeEventAllowed(changingEvent) )
{
DoUnselectAll();
- ::SelectItem(GetHwnd(), HITEM(next));
- ::SetFocus(GetHwnd(), HITEM(next));
+ DoSelectItem(next);
+ SetFocusedItem(next);
wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED,
this, next);
}
}
- ::SetFocus(GetHwnd(), HITEM(next));
+ SetFocusedItem(next);
}
break;
if ( IsTreeEventAllowed(changingEvent) )
{
DoUnselectAll();
- ::SelectItem(GetHwnd(), HITEM(next));
- ::SetFocus(GetHwnd(), HITEM(next));
+ DoSelectItem(next);
+ SetFocusedItem(next);
wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED,
this, next);
if ( IsTreeEventAllowed(changingEvent) )
{
DoUnselectAll();
- ::SelectItem(GetHwnd(), HITEM(next));
- ::SetFocus(GetHwnd(), HITEM(next));
+ DoSelectItem(next);
+ SetFocusedItem(next);
wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this, next);
changedEvent.m_itemOld = htSel;
SelectRange(GetHwnd(),
HITEM(m_htSelStart), HITEM(next),
SR_UNSELECT_OTHERS);
- ::SetFocus(GetHwnd(), HITEM(next));
+ SetFocusedItem(next);
wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED,
this, next);
if ( IsTreeEventAllowed(changingEvent) )
{
DoUnselectAll();
- ::SelectItem(GetHwnd(), HITEM(next));
- ::SetFocus(GetHwnd(), HITEM(next));
+ DoSelectItem(next);
+ SetFocusedItem(next);
wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED,
this, next);
{
DoUnselectAll();
m_htSelStart.Unset();
- ::SelectItem(GetHwnd(), HITEM(next));
- ::SetFocus(GetHwnd(), HITEM(next));
+ DoSelectItem(next);
+ SetFocusedItem(next);
wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED,
this, next);
return true;
}
+bool wxTreeCtrl::MSWHandleTreeKeyDownEvent(WXWPARAM wParam, WXLPARAM lParam)
+{
+ wxTreeEvent keyEvent(wxEVT_COMMAND_TREE_KEY_DOWN, this);
+
+ int keyCode = wxCharCodeMSWToWX(wParam);
+
+ if ( !keyCode )
+ {
+ // wxCharCodeMSWToWX() returns 0 to indicate that this is a
+ // simple ASCII key
+ keyCode = wParam;
+ }
+
+ keyEvent.m_evtKey = CreateKeyEvent(wxEVT_KEY_DOWN, keyCode,
+ lParam, wParam);
+
+ 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
if ( !isMultiple )
break;
- processed = true;
m_htClickedItem.Unset();
if ( !(tvht.flags & TVHT_ONITEM) )
Collapse(htItem);
}
}
+
+ processed = true;
}
+ m_focusLost = false;
break;
}
+ processed = true;
SetFocus();
m_htClickedItem = (WXHTREEITEM) htItem;
m_ptClick = wxPoint(x, y);
if ( IsTreeEventAllowed(changingEvent) )
{
// toggle selected state
- ::ToggleItemSelection(GetHwnd(), htItem);
+ DoToggleItemSelection(wxTreeItemId(htItem));
- ::SetFocus(GetHwnd(), htItem);
+ SetFocusedItem(wxTreeItemId(htItem));
// reset on any click without Shift
m_htSelStart.Unset();
}
else
{
- ::SelectItem(GetHwnd(), htItem);
+ DoSelectItem(wxTreeItemId(htItem));
}
- ::SetFocus(GetHwnd(), htItem);
+ SetFocusedItem(wxTreeItemId(htItem));
wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED,
this, htItem);
// 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,
if ( IsTreeEventAllowed(changingEvent) )
{
DoUnselectAll();
- ::SelectItem(GetHwnd(), htItem);
- ::SetFocus(GetHwnd(), htItem);
+ DoSelectItem(wxTreeItemId(htItem));
+ SetFocusedItem(wxTreeItemId(htItem));
wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED,
this, htItem);
}
else
{
- ::SetFocus(GetHwnd(), htItem);
+ SetFocusedItem(wxTreeItemId(htItem));
}
}
else // click on a single selected item
// since previous click
if ( m_focusLost )
{
- TreeView_SelectItem(GetHwnd(), 0);
- ::SelectItem(GetHwnd(), htItem);
+ ClearFocusedItem();
+ DoSelectItem(wxTreeItemId(htItem));
+ SetFocusedItem(wxTreeItemId(htItem));
+ }
+ else
+ {
+ processed = false;
}
-
- processed = false;
}
// reset on any click without Shift
if ( IsTreeEventAllowed(changingEvent) )
{
DoUnselectAll();
- ::SelectItem(GetHwnd(), htItem);
- ::SetFocus(GetHwnd(), htItem);
+ DoSelectItem(wxTreeItemId(htItem));
+ SetFocusedItem(wxTreeItemId(htItem));
wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED,
this, htItem);
if ( IsTreeEventAllowed(changingEvent) )
{
DoUnselectAll();
- ::SelectItem(GetHwnd(), htItem);
- ::SetFocus(GetHwnd(), htItem);
+ DoSelectItem(wxTreeItemId(htItem));
+ SetFocusedItem(wxTreeItemId(htItem));
wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED,
this, htItem);
}
else if ( (nMsg == WM_KEYDOWN || nMsg == WM_SYSKEYDOWN) && isMultiple )
{
- wxTreeEvent keyEvent(wxEVT_COMMAND_TREE_KEY_DOWN, this);
-
- int keyCode = wxCharCodeMSWToWX(wParam);
-
- if ( !keyCode )
+ // normally we want to generate wxEVT_KEY_DOWN events from TVN_KEYDOWN
+ // notification but for the keys which can be used to change selection
+ // we need to do it from here so as to not apply the default behaviour
+ // if the events are handled by the user code
+ switch ( wParam )
{
- // wxCharCodeMSWToWX() returns 0 to indicate that this is a
- // simple ASCII key
- keyCode = wParam;
- }
+ case VK_RETURN:
+ case VK_SPACE:
+ case VK_UP:
+ case VK_DOWN:
+ case VK_LEFT:
+ case VK_RIGHT:
+ case VK_HOME:
+ case VK_END:
+ case VK_PRIOR:
+ case VK_NEXT:
+ if ( !MSWHandleTreeKeyDownEvent(wParam, lParam) )
+ {
+ // use the key to update the selection if it was left
+ // unprocessed
+ MSWHandleSelectionKey(wParam);
+ }
- keyEvent.m_evtKey = CreateKeyEvent(wxEVT_KEY_DOWN, keyCode,
- lParam, wParam);
+ // pretend that we did process it in any case as we already
+ // generated an event for it
+ processed = true;
- processed = HandleTreeEvent(keyEvent);
- if ( !processed )
- {
- // update the selection if key was left unprocessed
- processed = MSWHandleSelectionKey(wParam);
+ //default: for all the other keys leave processed as false so that
+ // the tree control generates a TVN_KEYDOWN for us
}
- // 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);
- }
- }
}
else if ( nMsg == WM_COMMAND )
{
// 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;
-
- WXWPARAM wParam = info->wVKey;
-
- int keyCode = wxCharCodeMSWToWX(wParam);
- if ( !keyCode )
- {
- // wxCharCodeMSWToWX() returns 0 to indicate that this is a
- // simple ASCII key
- keyCode = wParam;
- }
-
- wxTreeEvent keyEvent(wxEVT_COMMAND_TREE_KEY_DOWN, this);
- keyEvent.m_evtKey = CreateKeyEvent(wxEVT_KEY_DOWN,
- keyCode,
- lParam,
- wParam);
-
- if ( HandleTreeEvent(keyEvent) )
- {
- return true;
- }
-
- wxTreeItemId item = wxTreeItemId(TreeView_GetSelection(GetHwnd()));
-
- // a separate event for Space/Return
- if ( !wxIsCtrlDown() && !wxIsShiftDown() && !isAltDown &&
- ((info->wVKey == VK_SPACE) || (info->wVKey == VK_RETURN)) &&
- item )
- {
- wxTreeEvent activatedEvent(wxEVT_COMMAND_TREE_ITEM_ACTIVATED,
- this, item);
- (void)HandleTreeEvent(activatedEvent);
- }
-
- return false;
+ return MSWHandleTreeKeyDownEvent(
+ info->wVKey, (wxIsAltDown() ? KF_ALTDOWN : 0) << 16);
}
- break;
// Vista's tree control has introduced some problems with our
// we have to handle both messages:
case TVN_SELCHANGEDA:
case TVN_SELCHANGEDW:
- if ( !HasFlag(wxTR_MULTIPLE) )
+ if ( !HasFlag(wxTR_MULTIPLE) || !m_changingSelection )
{
eventType = wxEVT_COMMAND_TREE_SEL_CHANGED;
}
case TVN_SELCHANGINGA:
case TVN_SELCHANGINGW:
- if ( !HasFlag(wxTR_MULTIPLE) )
+ if ( !HasFlag(wxTR_MULTIPLE) || !m_changingSelection )
{
if ( eventType == wxEVT_NULL )
eventType = wxEVT_COMMAND_TREE_SEL_CHANGING;
if ( !loaded )
{
- wxLoadedDLL dllComCtl32(_T("comctl32.dll"));
+ wxLoadedDLL dllComCtl32(wxT("comctl32.dll"));
if ( dllComCtl32.IsLoaded() )
wxDL_INIT_FUNC(s_pfn, ImageList_Copy, dllComCtl32);
}
{
// normally this is impossible because the m_dragImage is
// deleted once the drag operation is over
- wxASSERT_MSG( !m_dragImage, _T("starting to drag once again?") );
+ wxASSERT_MSG( !m_dragImage, wxT("starting to drag once again?") );
m_dragImage = new wxDragImage(*this, event.m_item);
m_dragImage->BeginDrag(wxPoint(0,0), this);