+#ifndef TV_FIRST
+ #define TV_FIRST 0x1100
+#endif
+
+#ifndef TVS_CHECKBOXES
+ #define TVS_CHECKBOXES 0x0100
+#endif
+
+#ifndef TVS_FULLROWSELECT
+ #define TVS_FULLROWSELECT 0x1000
+#endif
+
+// old headers might miss these messages (comctl32.dll 4.71+ only)
+#ifndef TVM_SETBKCOLOR
+ #define TVM_SETBKCOLOR (TV_FIRST + 29)
+ #define TVM_SETTEXTCOLOR (TV_FIRST + 30)
+#endif
+
+// a macro to hide the ugliness of nested casts
+#define HITEM(item) (HTREEITEM)(WXHTREEITEM)(item)
+
+// 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
+// checkboxes be the selection status (checked == selected) or by really
+// emulating everything, i.e. intercepting mouse and key events &c. The first
+// approach is much easier but doesn't work with comctl32.dll < 4.71 and also
+// looks quite ugly.
+#define wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE 0
+
+// ----------------------------------------------------------------------------
+// private functions
+// ----------------------------------------------------------------------------
+
+// wrapper for TreeView_HitTest
+static HTREEITEM GetItemFromPoint(HWND hwndTV, int x, int y)
+{
+ TV_HITTESTINFO tvht;
+ tvht.pt.x = x;
+ tvht.pt.y = y;
+
+ return (HTREEITEM)TreeView_HitTest(hwndTV, &tvht);
+}
+
+#if !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
+
+// wrappers for TreeView_GetItem/TreeView_SetItem
+static bool IsItemSelected(HWND hwndTV, HTREEITEM hItem)
+{
+ TV_ITEM tvi;
+ tvi.mask = TVIF_STATE | TVIF_HANDLE;
+ tvi.stateMask = TVIS_SELECTED;
+ tvi.hItem = hItem;
+
+ if ( !TreeView_GetItem(hwndTV, &tvi) )
+ {
+ wxLogLastError(wxT("TreeView_GetItem"));
+ }
+
+ return (tvi.state & TVIS_SELECTED) != 0;
+}
+
+static void SelectItem(HWND hwndTV, HTREEITEM hItem, bool select = TRUE)
+{
+ TV_ITEM tvi;
+ tvi.mask = TVIF_STATE | TVIF_HANDLE;
+ tvi.stateMask = TVIS_SELECTED;
+ tvi.state = select ? TVIS_SELECTED : 0;
+ tvi.hItem = hItem;
+
+ if ( TreeView_SetItem(hwndTV, &tvi) == -1 )
+ {
+ wxLogLastError(wxT("TreeView_SetItem"));
+ }
+}
+
+static inline void UnselectItem(HWND hwndTV, HTREEITEM htItem)
+{
+ SelectItem(hwndTV, htItem, FALSE);
+}
+
+static inline void ToggleItemSelection(HWND hwndTV, HTREEITEM htItem)
+{
+ SelectItem(hwndTV, htItem, !IsItemSelected(hwndTV, htItem));
+}
+
+// helper function which selects all items in a range and, optionally,
+// unselects all others
+static void SelectRange(HWND hwndTV,
+ HTREEITEM htFirst,
+ HTREEITEM htLast,
+ bool unselectOthers = TRUE)
+{
+ // find the first (or last) item and select it
+ bool cont = TRUE;
+ HTREEITEM htItem = (HTREEITEM)TreeView_GetRoot(hwndTV);
+ while ( htItem && cont )
+ {
+ if ( (htItem == htFirst) || (htItem == htLast) )
+ {
+ if ( !IsItemSelected(hwndTV, htItem) )
+ {
+ SelectItem(hwndTV, htItem);
+ }
+
+ cont = FALSE;
+ }
+ else
+ {
+ if ( unselectOthers && IsItemSelected(hwndTV, htItem) )
+ {
+ UnselectItem(hwndTV, htItem);
+ }
+ }
+
+ htItem = (HTREEITEM)TreeView_GetNextVisible(hwndTV, htItem);
+ }
+
+ // select the items in range
+ cont = htFirst != htLast;
+ while ( htItem && cont )
+ {
+ if ( !IsItemSelected(hwndTV, htItem) )
+ {
+ SelectItem(hwndTV, htItem);
+ }
+
+ cont = (htItem != htFirst) && (htItem != htLast);
+
+ htItem = (HTREEITEM)TreeView_GetNextVisible(hwndTV, htItem);
+ }
+
+ // unselect the rest
+ if ( unselectOthers )
+ {
+ while ( htItem )
+ {
+ if ( IsItemSelected(hwndTV, htItem) )
+ {
+ UnselectItem(hwndTV, htItem);
+ }
+
+ htItem = (HTREEITEM)TreeView_GetNextVisible(hwndTV, htItem);
+ }
+ }
+
+ // seems to be necessary - otherwise the just selected items don't always
+ // appear as selected
+ UpdateWindow(hwndTV);
+}
+
+// helper function which tricks the standard control into changing the focused
+// item without changing anything else (if someone knows why Microsoft doesn't
+// allow to do it by just setting TVIS_FOCUSED flag, please tell me!)
+static void SetFocus(HWND hwndTV, HTREEITEM htItem)
+{
+ // the current focus
+ HTREEITEM htFocus = (HTREEITEM)TreeView_GetSelection(hwndTV);
+
+ if ( htItem )
+ {
+ // set the focus
+ if ( htItem != htFocus )
+ {
+ // remember the selection state of the item
+ bool wasSelected = IsItemSelected(hwndTV, htItem);
+
+ if ( htFocus && IsItemSelected(hwndTV, htFocus) )
+ {
+ // prevent the tree from unselecting the old focus which it
+ // would do by default (TreeView_SelectItem unselects the
+ // focused item)
+ TreeView_SelectItem(hwndTV, 0);
+ SelectItem(hwndTV, htFocus);
+ }
+
+ TreeView_SelectItem(hwndTV, htItem);
+
+ if ( !wasSelected )
+ {
+ // need to clear the selection which TreeView_SelectItem() gave
+ // us
+ UnselectItem(hwndTV, htItem);
+ }
+ //else: was selected, still selected - ok
+ }
+ //else: nothing to do, focus already there
+ }
+ else
+ {
+ if ( htFocus )
+ {
+ bool wasFocusSelected = IsItemSelected(hwndTV, htFocus);
+
+ // just clear the focus
+ TreeView_SelectItem(hwndTV, 0);
+
+ if ( wasFocusSelected )
+ {
+ // restore the selection state
+ SelectItem(hwndTV, htFocus);
+ }
+ }
+ //else: nothing to do, no focus already
+ }
+}
+
+#endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
+