#include "wx/dynlib.h"
#include "wx/msw/private.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/imaglist.h"
#include "wx/msw/dragimag.h"
+#include "wx/msw/uxtheme.h"
// macros to hide the cast ugliness
// --------------------------------
// 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
break;
default:
- wxFAIL_MSG( _T("unsupported wxTreeItemIcon value") );
+ wxFAIL_MSG( wxT("unsupported wxTreeItemIcon value") );
}
}
// wxWin macros
// ----------------------------------------------------------------------------
-#if wxUSE_EXTENDED_RTTI
-WX_DEFINE_FLAGS( wxTreeCtrlStyle )
-
-wxBEGIN_FLAGS( wxTreeCtrlStyle )
- // new style border flags, we put them first to
- // use them for streaming out
- wxFLAGS_MEMBER(wxBORDER_SIMPLE)
- wxFLAGS_MEMBER(wxBORDER_SUNKEN)
- wxFLAGS_MEMBER(wxBORDER_DOUBLE)
- wxFLAGS_MEMBER(wxBORDER_RAISED)
- wxFLAGS_MEMBER(wxBORDER_STATIC)
- wxFLAGS_MEMBER(wxBORDER_NONE)
-
- // old style border flags
- wxFLAGS_MEMBER(wxSIMPLE_BORDER)
- wxFLAGS_MEMBER(wxSUNKEN_BORDER)
- wxFLAGS_MEMBER(wxDOUBLE_BORDER)
- wxFLAGS_MEMBER(wxRAISED_BORDER)
- wxFLAGS_MEMBER(wxSTATIC_BORDER)
- wxFLAGS_MEMBER(wxBORDER)
-
- // standard window styles
- wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
- wxFLAGS_MEMBER(wxCLIP_CHILDREN)
- wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
- wxFLAGS_MEMBER(wxWANTS_CHARS)
- wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
- wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
- wxFLAGS_MEMBER(wxVSCROLL)
- wxFLAGS_MEMBER(wxHSCROLL)
-
- wxFLAGS_MEMBER(wxTR_EDIT_LABELS)
- wxFLAGS_MEMBER(wxTR_NO_BUTTONS)
- wxFLAGS_MEMBER(wxTR_HAS_BUTTONS)
- wxFLAGS_MEMBER(wxTR_TWIST_BUTTONS)
- wxFLAGS_MEMBER(wxTR_NO_LINES)
- wxFLAGS_MEMBER(wxTR_FULL_ROW_HIGHLIGHT)
- wxFLAGS_MEMBER(wxTR_LINES_AT_ROOT)
- wxFLAGS_MEMBER(wxTR_HIDE_ROOT)
- wxFLAGS_MEMBER(wxTR_ROW_LINES)
- wxFLAGS_MEMBER(wxTR_HAS_VARIABLE_ROW_HEIGHT)
- wxFLAGS_MEMBER(wxTR_SINGLE)
- wxFLAGS_MEMBER(wxTR_MULTIPLE)
-#if WXWIN_COMPATIBILITY_2_8
- wxFLAGS_MEMBER(wxTR_EXTENDED)
-#endif
- wxFLAGS_MEMBER(wxTR_DEFAULT_STYLE)
-
-wxEND_FLAGS( wxTreeCtrlStyle )
-
-IMPLEMENT_DYNAMIC_CLASS_XTI(wxTreeCtrl, wxControl,"wx/treectrl.h")
-
-wxBEGIN_PROPERTIES_TABLE(wxTreeCtrl)
- wxEVENT_PROPERTY( TextUpdated , wxEVT_COMMAND_TEXT_UPDATED , wxCommandEvent )
- wxEVENT_RANGE_PROPERTY( TreeEvent , wxEVT_COMMAND_TREE_BEGIN_DRAG , wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK , wxTreeEvent )
- wxPROPERTY_FLAGS( WindowStyle , wxTreeCtrlStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
-wxEND_PROPERTIES_TABLE()
-
-wxBEGIN_HANDLERS_TABLE(wxTreeCtrl)
-wxEND_HANDLERS_TABLE()
-
-wxCONSTRUCTOR_5( wxTreeCtrl , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size , long , WindowStyle )
-#else
-IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl, wxControl)
-#endif
-
// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------
m_focusLost = true;
m_changingSelection = false;
m_triggerStateImageClick = false;
+ m_mouseUpDeselect = false;
// initialize the global array of events now as it can't be done statically
// with the wxEVT_XXX values being allocated during run-time only
if ( !MSWCreateControl(WC_TREEVIEW, wstyle, pos, size) )
return false;
-#if wxUSE_COMCTL32_SAFELY
- wxWindow::SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
- wxWindow::SetForegroundColour(wxWindow::GetParent()->GetForegroundColour());
-#elif 1
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
SetForegroundColour(wxWindow::GetParent()->GetForegroundColour());
-#else
- // This works around a bug in the Windows tree control whereby for some versions
- // of comctrl32, setting any colour actually draws the background in black.
- // This will initialise the background to the system colour.
- // THIS FIX NOW REVERTED since it caused problems on _other_ systems.
- // Assume the user has an updated comctl32.dll.
- ::SendMessage(GetHwnd(), TVM_SETBKCOLOR, 0,-1);
- wxWindow::SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
- SetForegroundColour(wxWindow::GetParent()->GetForegroundColour());
-#endif
wxSetCCUnicodeFormat(GetHwnd());
+ if ( m_windowStyle & wxTR_TWIST_BUTTONS )
+ {
+ // Under Vista and later Explorer uses rotating ("twist") buttons
+ // instead of the default "+/-" ones so apply its theme to the tree
+ // control to implement this style.
+ if ( wxGetWinVersion() >= wxWinVersion_Vista )
+ {
+ if ( wxUxThemeEngine *theme = wxUxThemeEngine::GetIfActive() )
+ {
+ theme->SetWindowTheme(GetHwnd(), L"EXPLORER", NULL);
+ }
+ }
+ }
+
return true;
}
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) )
{
bool wxTreeCtrl::SetBackgroundColour(const wxColour &colour)
{
-#if !wxUSE_COMCTL32_SAFELY
if ( !wxWindowBase::SetBackgroundColour(colour) )
return false;
::SendMessage(GetHwnd(), TVM_SETBKCOLOR, 0, colour.GetPixel());
-#endif
return true;
}
bool wxTreeCtrl::SetForegroundColour(const wxColour &colour)
{
-#if !wxUSE_COMCTL32_SAFELY
if ( !wxWindowBase::SetForegroundColour(colour) )
return false;
::SendMessage(GetHwnd(), TVM_SETTEXTCOLOR, 0, colour.GetPixel());
-#endif
return true;
}
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);
return;
}
+ if ( item == m_htSelStart )
+ m_htSelStart.Unset();
+
+ if ( item == m_htClickedItem )
+ m_htClickedItem.Unset();
+
if ( next.IsOk() )
{
wxTreeEvent changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING, this, next);
// unlock tree selections on vista for the duration of this call
TreeItemUnlocker unlock_all;
+ // invalidate all the items we store as they're going to become invalid
+ m_htSelStart =
+ m_htClickedItem = wxTreeItemId();
+
// delete the "virtual" root item.
if ( GET_VIRTUAL_ROOT() )
{
}
}
+void wxTreeCtrl::DoSelectChildren(const wxTreeItemId& parent)
+{
+ DoUnselectAll();
+
+ wxTreeItemIdValue cookie;
+ wxTreeItemId child = GetFirstChild(parent, cookie);
+ while ( child.IsOk() )
+ {
+ DoSelectItem(child, true);
+ child = GetNextChild(child, cookie);
+ }
+}
+
+void wxTreeCtrl::SelectChildren(const wxTreeItemId& parent)
+{
+ wxCHECK_RET( HasFlag(wxTR_MULTIPLE),
+ "this only works with multiple selection controls" );
+
+ HTREEITEM htFocus = (HTREEITEM)TreeView_GetSelection(GetHwnd());
+
+ wxTreeEvent changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING, this);
+ changingEvent.m_itemOld = htFocus;
+
+ if ( IsTreeEventAllowed(changingEvent) )
+ {
+ DoSelectChildren(parent);
+
+ wxTreeEvent changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED, this);
+ changedEvent.m_itemOld = htFocus;
+ (void)HandleTreeEvent(changedEvent);
+ }
+}
+
void wxTreeCtrl::DoSelectItem(const wxTreeItemId& item, bool select)
{
TempSetter set(m_changingSelection);
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;
}
(void)HandleTreeEvent(changedEvent);
}
}
- else
+ else // single selection
{
- wxASSERT_MSG( select,
- _T("SelectItem(false) works only for multiselect") );
-
- // 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
+ wxTreeItemId itemOld, itemNew;
+ if ( select )
+ {
+ itemOld = GetSelection();
+ itemNew = item;
+ }
+ else // deselecting the currently selected item
+ {
+ itemOld = item;
+ // leave itemNew invalid
+ }
- wxTreeEvent changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING, this, item);
+ // Recent versions of comctl32.dll send TVN_SELCHANG{ED,ING} events
+ // when we call TreeView_SelectItem() but apparently some old ones did
+ // not so send the events ourselves and ignore those generated by
+ // TreeView_SelectItem() if m_changingSelection is set.
+ wxTreeEvent
+ changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING, this, itemNew);
+ changingEvent.SetOldItem(itemOld);
if ( IsTreeEventAllowed(changingEvent) )
{
- if ( !TreeView_SelectItem(GetHwnd(), HITEM(item)) )
+ TempSetter set(m_changingSelection);
+
+ if ( !TreeView_SelectItem(GetHwnd(), HITEM(itemNew)) )
{
wxLogLastError(wxT("TreeView_SelectItem"));
}
else // ok
{
- SetFocusedItem(item);
+ ::SetFocus(GetHwnd(), HITEM(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));
m_textCtrl->UnsubclassWin();
m_textCtrl->SetHWND(0);
- delete m_textCtrl;
- m_textCtrl = NULL;
+ wxDELETE(m_textCtrl);
m_idEdited.Unset();
}
// returned false
if ( !hWnd )
{
- delete m_textCtrl;
- m_textCtrl = NULL;
+ wxDELETE(m_textCtrl);
return NULL;
}
void wxTreeCtrl::SetFocusedItem(const wxTreeItemId& item)
{
+ wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
+
TempSetter set(m_changingSelection);
::SetFocus(GetHwnd(), HITEM(item));
return true;
}
+bool wxTreeCtrl::MSWIsOnItem(unsigned flags) const
+{
+ unsigned mask = TVHT_ONITEM;
+ if ( HasFlag(wxTR_FULL_ROW_HIGHLIGHT) )
+ mask |= TVHT_ONITEMINDENT | TVHT_ONITEMRIGHT;
+
+ return (flags & mask) != 0;
+}
+
bool wxTreeCtrl::MSWHandleSelectionKey(unsigned vkey)
{
const bool bCtrl = wxIsCtrlDown();
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);
+ keyEvent.m_evtKey = CreateKeyEvent(wxEVT_KEY_DOWN, wParam, lParam);
bool processed = HandleTreeEvent(keyEvent);
}
// create the event
- wxTreeEvent event(wxEVT_COMMAND_TREE_ITEM_MENU, this, item);
+ if ( item.IsOk() )
+ {
+ wxTreeEvent event(wxEVT_COMMAND_TREE_ITEM_MENU, this, item);
- event.m_pointDrag = pt;
+ event.m_pointDrag = pt;
- if ( HandleTreeEvent(event) )
- processed = true;
- //else: continue with generating wxEVT_CONTEXT_MENU in base class code
+ if ( HandleTreeEvent(event) )
+ processed = true;
+ //else: continue with generating wxEVT_CONTEXT_MENU in base class code
+ }
}
else if ( (nMsg >= WM_MOUSEFIRST) && (nMsg <= WM_MOUSELAST) )
{
m_htClickedItem.Unset();
- if ( !(tvht.flags & TVHT_ONITEM) )
+ if ( !MSWIsOnItem(tvht.flags) )
{
- if ( !HandleMouseEvent(nMsg, x, y, wParam) )
+ if ( tvht.flags & TVHT_ONITEMBUTTON )
{
- 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) )
{
Collapse(htItem);
}
}
-
- processed = true;
}
m_focusLost = false;
else
{
SetFocusedItem(wxTreeItemId(htItem));
+ m_mouseUpDeselect = true;
}
}
else // click on a single selected item
case WM_LBUTTONUP:
if ( isMultiple )
{
- // deselect other items if multiple items selected
+ // deselect other items if needed
if ( htItem )
{
- wxArrayTreeItemIds selections;
- size_t count = GetSelections(selections);
-
- if ( count > 1 &&
- !(wParam & MK_CONTROL) &&
- !(wParam & MK_SHIFT) )
+ if ( m_mouseUpDeselect )
{
+ m_mouseUpDeselect = false;
+
wxTreeEvent changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING,
this, htItem);
changingEvent.m_itemOld = htOldItem;
}
}
- if ( !m_dragStarted &&
- (tvht.flags & TVHT_ONITEMSTATEICON ||
- tvht.flags & TVHT_ONITEMICON ||
- tvht.flags & TVHT_ONITEM) )
+ if ( !m_dragStarted && MSWIsOnItem(tvht.flags) )
{
processed = true;
}
if ( m_dragImage )
{
m_dragImage->EndDrag();
- delete m_dragImage;
- m_dragImage = NULL;
+ wxDELETE(m_dragImage);
// generate the drag end event
wxTreeEvent event(wxEVT_COMMAND_TREE_END_DRAG,
case VK_END:
case VK_PRIOR:
case VK_NEXT:
- if ( !MSWHandleTreeKeyDownEvent(wParam, lParam) )
+ if ( !HandleKeyDown(wParam, lParam) &&
+ !MSWHandleTreeKeyDownEvent(wParam, lParam) )
{
// use the key to update the selection if it was left
// unprocessed
if ( m_dragImage )
{
m_dragImage->EndDrag();
- delete m_dragImage;
- m_dragImage = NULL;
+ wxDELETE(m_dragImage);
// if we don't do it, the tree seems to think that 2 items
// are selected simultaneously which is quite weird
return MSWHandleTreeKeyDownEvent(
info->wVKey, (wxIsAltDown() ? KF_ALTDOWN : 0) << 16);
}
- break;
// Vista's tree control has introduced some problems with our
// 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
// we have to handle both messages:
case TVN_SELCHANGEDA:
case TVN_SELCHANGEDW:
- if ( !HasFlag(wxTR_MULTIPLE) || !m_changingSelection )
+ if ( !m_changingSelection )
{
eventType = wxEVT_COMMAND_TREE_SEL_CHANGED;
}
case TVN_SELCHANGINGA:
case TVN_SELCHANGINGW:
- if ( !HasFlag(wxTR_MULTIPLE) || !m_changingSelection )
+ if ( !m_changingSelection )
{
if ( eventType == wxEVT_NULL )
eventType = wxEVT_COMMAND_TREE_SEL_CHANGING;
//
// to avoid such surprises, we force the generation of focus events
// now, before we generate the selection change ones
- SetFocus();
+ if ( !m_changingSelection )
+ SetFocus();
break;
// instead of explicitly checking for _WIN32_IE, check if the
// required symbols are available in the headers
-#if defined(CDDS_PREPAINT) && !wxUSE_COMCTL32_SAFELY
+#if defined(CDDS_PREPAINT)
case NM_CUSTOMDRAW:
{
LPNMTVCUSTOMDRAW lptvcd = (LPNMTVCUSTOMDRAW)lParam;
if ( !loaded )
{
- wxLoadedDLL dllComCtl32(_T("comctl32.dll"));
+ wxLoadedDLL dllComCtl32(wxT("comctl32.dll"));
if ( dllComCtl32.IsLoaded() )
wxDL_INIT_FUNC(s_pfn, ImageList_Copy, dllComCtl32);
}
::ScreenToClient(GetHwnd(), &tvhti.pt);
if ( TreeView_HitTest(GetHwnd(), &tvhti) )
{
- if ( tvhti.flags & TVHT_ONITEM )
+ if ( MSWIsOnItem(tvhti.flags) )
{
event.m_item = tvhti.hItem;
eventType = (int)hdr->code == NM_DBLCLK
{
// 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);