// 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
+// behaviour, 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:
// unlock a single item
- TreeItemUnlocker(HTREEITEM item) { ms_unlockedItem = item; }
+ TreeItemUnlocker(HTREEITEM item)
+ {
+ m_oldUnlockedItem = ms_unlockedItem;
+ ms_unlockedItem = item;
+ }
// unlock all items, don't use unless absolutely necessary
- TreeItemUnlocker() { ms_unlockedItem = (HTREEITEM)-1; }
+ TreeItemUnlocker()
+ {
+ m_oldUnlockedItem = ms_unlockedItem;
+ ms_unlockedItem = (HTREEITEM)-1;
+ }
// lock everything back
- ~TreeItemUnlocker() { ms_unlockedItem = NULL; }
+ ~TreeItemUnlocker() { ms_unlockedItem = m_oldUnlockedItem; }
// check if the item state is currently locked
private:
static HTREEITEM ms_unlockedItem;
+ HTREEITEM m_oldUnlockedItem;
+
+ wxDECLARE_NO_COPY_CLASS(TreeItemUnlocker);
};
HTREEITEM TreeItemUnlocker::ms_unlockedItem = NULL;
// private functions
// ----------------------------------------------------------------------------
+namespace
+{
+
+// Work around a problem with TreeView_GetItemRect() when using MinGW/Cygwin:
+// it results in warnings about breaking strict aliasing rules because HITEM is
+// passed via a RECT pointer, so use a union to avoid them and define our own
+// version of the standard macro using it.
+union TVGetItemRectParam
+{
+ RECT rect;
+ HTREEITEM hItem;
+};
+
+inline bool
+wxTreeView_GetItemRect(HWND hwnd,
+ HTREEITEM hItem,
+ TVGetItemRectParam& param,
+ BOOL fItemRect)
+{
+ param.hItem = hItem;
+ return ::SendMessage(hwnd, TVM_GETITEMRECT, fItemRect,
+ (LPARAM)¶m) == TRUE;
+}
+
+} // anonymous namespace
+
// wrappers for TreeView_GetItem/TreeView_SetItem
static bool IsItemSelected(HWND hwndTV, HTREEITEM hItem)
{
switch ( which )
{
case wxTreeItemIcon_SelectedExpanded:
- image = GetImage(wxTreeItemIcon_Expanded);
+ // We consider that expanded icon is more important than
+ // selected so test for it first.
+ image = m_images[wxTreeItemIcon_Expanded];
+ if ( image == -1 )
+ image = m_images[wxTreeItemIcon_Selected];
if ( image != -1 )
break;
//else: fall through
case wxTreeItemIcon_Selected:
case wxTreeItemIcon_Expanded:
- image = GetImage(wxTreeItemIcon_Normal);
+ image = m_images[wxTreeItemIcon_Normal];
break;
case wxTreeItemIcon_Normal:
wxTreeCtrl::~wxTreeCtrl()
{
+ m_isBeingDeleted = true;
+
// delete any attributes
if ( m_hasAnyAttr )
{
return;
wxTreeViewItem tvItem(item, TVIF_TEXT);
- tvItem.pszText = (wxChar *)text.wx_str(); // conversion is ok
+ tvItem.pszText = wxMSW_CONV_LPTSTR(text);
DoSetItem(&tvItem);
// when setting the text of the item being edited, the text control should
{
if ( item == m_idEdited )
{
- ::SetWindowText(hwndEdit, text.wx_str());
+ ::SetWindowText(hwndEdit, text.t_str());
}
}
}
}
// Bug in Gnu-Win32 headers, so don't use the macro TreeView_GetItemRect
- RECT rect;
-
- // this ugliness comes directly from MSDN - it *is* the correct way to pass
- // the HTREEITEM with TVM_GETITEMRECT
- *(HTREEITEM *)&rect = HITEM(item);
+ TVGetItemRectParam param;
// true means to get rect for just the text, not the whole line
- if ( !::SendMessage(GetHwnd(), TVM_GETITEMRECT, true, (LPARAM)&rect) )
+ if ( !wxTreeView_GetItemRect(GetHwnd(), HITEM(item), param, TRUE) )
{
// if TVM_GETITEMRECT returned false, then the item is definitely not
// visible (because its parent is not expanded)
// however if it returned true, the item might still be outside the
// currently visible part of the tree, test for it (notice that partly
// visible means visible here)
- return rect.bottom > 0 && rect.top < GetClientSize().y;
+ return param.rect.bottom > 0 && param.rect.top < GetClientSize().y;
}
bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const
if ( !text.empty() )
{
mask |= TVIF_TEXT;
- tvIns.item.pszText = (wxChar *)text.wx_str(); // cast is ok
+ tvIns.item.pszText = wxMSW_CONV_LPTSTR(text);
}
else
{
// need this to make the "[+]" appear
if ( firstChild )
{
- RECT rect;
- TreeView_GetItemRect(GetHwnd(), HITEM(parent), &rect, FALSE);
- ::InvalidateRect(GetHwnd(), &rect, FALSE);
+ TVGetItemRectParam param;
+
+ wxTreeView_GetItemRect(GetHwnd(), HITEM(parent), param, FALSE);
+ ::InvalidateRect(GetHwnd(), ¶m.rect, FALSE);
}
// associate the application tree item with Win32 tree item handle
wxTextCtrl *wxTreeCtrl::EditLabel(const wxTreeItemId& item,
wxClassInfo *textControlClass)
{
- wxASSERT( textControlClass->IsKindOf(CLASSINFO(wxTextCtrl)) );
+ wxASSERT( textControlClass->IsKindOf(wxCLASSINFO(wxTextCtrl)) );
DeleteTextCtrl();
wxRect& rect,
bool textOnly) const
{
- RECT rc;
-
// Virtual root items have no bounding rectangle
if ( IS_VIRTUAL_ROOT(item) )
{
return false;
}
- if ( TreeView_GetItemRect(GetHwnd(), HITEM(item),
- &rc, textOnly) )
+ TVGetItemRectParam param;
+
+ if ( wxTreeView_GetItemRect(GetHwnd(), HITEM(item), param, textOnly) )
{
- rect = wxRect(wxPoint(rc.left, rc.top), wxPoint(rc.right, rc.bottom));
+ rect = wxRect(wxPoint(param.rect.left, param.rect.top),
+ wxPoint(param.rect.right, param.rect.bottom));
return true;
}
// may be why as if you don't use the DECLARE_CLASS/IMPLEMENT_CLASS
// combo for your derived wxTreeCtrl if will sort without
// OnCompareItems
- if ( GetClassInfo() == CLASSINFO(wxTreeCtrl) )
+ if ( GetClassInfo() == wxCLASSINFO(wxTreeCtrl) )
{
TreeView_SortChildren(GetHwnd(), HITEM(item), 0);
}
// click if needed
if ( processed )
{
- int htFlags = 0;
- wxTreeItemId item = HitTest(wxPoint(x, y), htFlags);
-
- if ( htFlags & wxTREE_HITTEST_ONITEMSTATEICON )
+ if ( tvht.flags & TVHT_ONITEMSTATEICON )
{
m_triggerStateImageClick = true;
}
// do it for the other items itself - help it
wxArrayTreeItemIds selections;
size_t count = GetSelections(selections);
- RECT rect;
+ TVGetItemRectParam param;
for ( size_t n = 0; n < count; n++ )
{
// TreeView_GetItemRect() will return false if item is not
// visible, which may happen perfectly well
- if ( TreeView_GetItemRect(GetHwnd(), HITEM(selections[n]),
- &rect, TRUE) )
+ if ( wxTreeView_GetItemRect(GetHwnd(), HITEM(selections[n]),
+ param, TRUE) )
{
- ::InvalidateRect(GetHwnd(), &rect, FALSE);
+ ::InvalidateRect(GetHwnd(), ¶m.rect, FALSE);
}
}
}
// the wrong items are deselected.
// Fortunately, Vista provides a new notification, TVN_ITEMCHANGING
- // that can be used to regulate this incorrect behavior. The
+ // that can be used to regulate this incorrect behaviour. The
// following messages will allow only the unlocked item's selection
// state to change
//
// to avoid such surprises, we force the generation of focus events
// now, before we generate the selection change ones
- if ( !m_changingSelection )
+ if ( !m_changingSelection && !m_isBeingDeleted )
SetFocus();
break;
{
wxLoadedDLL dllComCtl32(wxT("comctl32.dll"));
if ( dllComCtl32.IsLoaded() )
+ {
wxDL_INIT_FUNC(s_pfn, ImageList_Copy, dllComCtl32);
+ loaded = true;
+ }
}
if ( !s_pfnImageList_Copy )
case NM_RCLICK:
{
TV_HITTESTINFO tvhti;
- ::GetCursorPos(&tvhti.pt);
+ wxGetCursorPosMSW(&tvhti.pt);
::ScreenToClient(GetHwnd(), &tvhti.pt);
if ( TreeView_HitTest(GetHwnd(), &tvhti) )
{
DoSetItem(&tvItem);
}
+// ----------------------------------------------------------------------------
+// Update locking.
+// ----------------------------------------------------------------------------
+
+// Using WM_SETREDRAW with the native control is a bad idea as it's broken in
+// some Windows versions (see http://support.microsoft.com/kb/130611) and
+// doesn't seem to do anything in other ones (e.g. under Windows 7 the tree
+// control keeps updating its scrollbars while the items are added to it,
+// resulting in horrible flicker when adding even a couple of dozen items).
+// So we hide it instead of freezing -- this still flickers, but actually not
+// as badly as it would if we didn't do it.
+
+void wxTreeCtrl::DoFreeze()
+{
+ // Notice that we don't call wxWindow::Hide() here as we want the window to
+ // remain shown from wxWidgets point of view and also because
+ // wxWindowMSW::Show() calls Do{Freeze,Thaw}() itself, so we'd get into
+ // infinite recursion this way.
+ if ( IsShown() )
+ ::ShowWindow(GetHwnd(), SW_HIDE);
+}
+
+void wxTreeCtrl::DoThaw()
+{
+ if ( IsShown() )
+ ::ShowWindow(GetHwnd(), SW_SHOW);
+}
+
#endif // wxUSE_TREECTRL