1 // Copyright 2001-2019 Crytek GmbH / Crytek Group. All rights reserved.
4 using System
.Collections
;
5 using System
.Collections
.Generic
;
6 using System
.Windows
.Forms
;
10 public class CheckboxTreeView
: TreeView
12 Stack
<Dictionary
<TreeNode
, CNodeCheckHistoryState
>> m_nodeCheckStateUndoStack
= new Stack
<Dictionary
<TreeNode
, CNodeCheckHistoryState
>>();
13 Stack
<Dictionary
<TreeNode
, CNodeCheckHistoryState
>> m_nodeCheckStateRedoStack
= new Stack
<Dictionary
<TreeNode
, CNodeCheckHistoryState
>>();
15 protected ToolTip m_toolTip
;
16 protected bool m_refreshOnTreeNodeChecked
= true;
17 bool m_doingUndoNodeCheckReplay
= false;
18 bool m_addToCurrentNodeCheckUndoStates
= false;
20 public HierarchyTreeNodeCollection TreeNodeHierarchy
;
22 public EventHandler HistoryStateListener
;
24 public CheckboxTreeView()
28 Dock
= DockStyle
.Fill
;
30 TreeNodeHierarchy
= new HierarchyTreeNodeCollection(Nodes
);
31 m_toolTip
= new ToolTip();
32 m_toolTip
.InitialDelay
= 2000;
33 m_toolTip
.ShowAlways
= true;
34 m_toolTip
.SetToolTip(this,
35 "Right-click: toggle subtree" + Environment
.NewLine
+
36 "Ctrl-left-click: isolate subtree");
39 private const int WM_LBUTTONDBLCLK
= 0x0203;
41 // a hack to get round a bug in the TreeView implementation
42 protected override void WndProc(ref Message m
)
44 if (m
.Msg
== WM_LBUTTONDBLCLK
)
46 TreeViewHitTestInfo tvhti
= HitTest(new System
.Drawing
.Point((int)m
.LParam
));
47 if (tvhti
!= null && tvhti
.Location
== TreeViewHitTestLocations
.StateImage
)
49 m
.Result
= IntPtr
.Zero
;
56 class CNodeCheckHistoryState
58 public bool m_beforeChecked
;
59 public bool m_afterChecked
;
61 public CNodeCheckHistoryState(bool beforeChecked
)
63 m_beforeChecked
= beforeChecked
;
67 void StartCheckStateBlock()
69 m_nodeCheckStateUndoStack
.Push(new Dictionary
<TreeNode
, CNodeCheckHistoryState
>());
70 m_addToCurrentNodeCheckUndoStates
= true;
71 m_refreshOnTreeNodeChecked
= false;
72 UpdateHistoryStateListeners();
75 void EndCheckStateBlock()
77 m_refreshOnTreeNodeChecked
= true;
78 m_addToCurrentNodeCheckUndoStates
= false;
81 public class CheckStateScope
: IDisposable
83 private readonly CheckboxTreeView CTV
;
85 public CheckStateScope(CheckboxTreeView ctv
)
88 CTV
.StartCheckStateBlock();
93 CTV
.EndCheckStateBlock();
97 public CheckStateScope
CheckStateBlock()
99 return new CheckStateScope(this);
102 void SetUndoNodeBeforeCheckState(TreeNode node
, bool beforeCheckedState
)
104 if (!m_doingUndoNodeCheckReplay
)
106 m_nodeCheckStateRedoStack
.Clear();
108 Dictionary
<TreeNode
, CNodeCheckHistoryState
> currentUndoNodeCheckStates
;
110 if (m_addToCurrentNodeCheckUndoStates
)
112 currentUndoNodeCheckStates
= m_nodeCheckStateUndoStack
.Peek();
116 currentUndoNodeCheckStates
= new Dictionary
<TreeNode
, CNodeCheckHistoryState
>();
117 m_nodeCheckStateUndoStack
.Push(currentUndoNodeCheckStates
);
120 if (!currentUndoNodeCheckStates
.ContainsKey(node
))
122 currentUndoNodeCheckStates
.Add(node
, new CNodeCheckHistoryState(beforeCheckedState
));
125 UpdateHistoryStateListeners();
129 void SetUndoNodeAfterCheckState(TreeNode node
, bool afterCheckedState
)
131 if (!m_doingUndoNodeCheckReplay
)
133 Dictionary
<TreeNode
, CNodeCheckHistoryState
> currentUndoNodeCheckStates
= m_nodeCheckStateUndoStack
.Peek();
134 currentUndoNodeCheckStates
[node
].m_afterChecked
= afterCheckedState
;
135 UpdateHistoryStateListeners();
139 void UpdateHistoryStateListeners()
141 if (HistoryStateListener
!= null)
142 HistoryStateListener(this, EventArgs
.Empty
);
145 public virtual void Undo()
147 m_doingUndoNodeCheckReplay
= true;
148 m_refreshOnTreeNodeChecked
= false;
150 if (m_nodeCheckStateUndoStack
.Count
> 0)
152 Dictionary
<TreeNode
, CNodeCheckHistoryState
> topUndoNodeCheckStates
= m_nodeCheckStateUndoStack
.Pop();
154 foreach (KeyValuePair
<TreeNode
, CNodeCheckHistoryState
> undoNodeCheckStatePair
in topUndoNodeCheckStates
)
156 TreeNode node
= undoNodeCheckStatePair
.Key
;
157 node
.Checked
= undoNodeCheckStatePair
.Value
.m_beforeChecked
;
160 m_nodeCheckStateRedoStack
.Push(topUndoNodeCheckStates
);
161 UpdateHistoryStateListeners();
164 m_refreshOnTreeNodeChecked
= true;
165 m_doingUndoNodeCheckReplay
= false;
168 public virtual void Redo()
170 m_doingUndoNodeCheckReplay
= true;
171 m_refreshOnTreeNodeChecked
= false;
173 if (m_nodeCheckStateRedoStack
.Count
> 0)
175 Dictionary
<TreeNode
, CNodeCheckHistoryState
> topRedoNodeCheckStates
= m_nodeCheckStateRedoStack
.Pop();
177 foreach (KeyValuePair
<TreeNode
, CNodeCheckHistoryState
> redoNodeCheckStatePair
in topRedoNodeCheckStates
)
179 TreeNode node
= redoNodeCheckStatePair
.Key
;
180 node
.Checked
= redoNodeCheckStatePair
.Value
.m_afterChecked
;
183 m_nodeCheckStateUndoStack
.Push(topRedoNodeCheckStates
);
184 UpdateHistoryStateListeners();
187 m_refreshOnTreeNodeChecked
= true;
188 m_doingUndoNodeCheckReplay
= false;
191 public virtual void FilterTextChanged(string filterText
)
193 m_refreshOnTreeNodeChecked
= false;
195 using (CheckStateBlock())
197 if (filterText
== "")
200 foreach (TreeNode node
in GetTreeNodeEnumerator(Nodes
))
207 foreach (TreeNode node
in GetTreeNodeEnumerator(Nodes
))
209 if (node
.Text
.ToUpper().Contains(filterText
))
213 // check all parent nodes
214 for (TreeNode tn
= node
; tn
!= null; tn
= tn
.Parent
)
221 node
.Checked
= false;
227 m_refreshOnTreeNodeChecked
= true;
230 public int NumUndoStates
232 get { return m_nodeCheckStateUndoStack.Count; }
235 public int NumRedoStates
237 get { return m_nodeCheckStateRedoStack.Count; }
240 // select treeNode in treeView, toggle its check state and set the entire subtree's check state to the new state
241 public void ToggleSubTree(TreeNode treeNode
)
243 using (CheckStateBlock())
245 SelectedNode
= treeNode
;
247 foreach (TreeNode node
in GetTreeNodeEnumerator(treeNode
.Nodes
))
248 node
.Checked
= !treeNode
.Checked
;
250 treeNode
.Checked
= !treeNode
.Checked
;
254 public void IsolateSubTree(TreeNode treeNode
)
256 using (CheckStateBlock())
258 // uncheck everything
259 foreach (TreeNode subNode
in GetTreeNodeEnumerator(Nodes
))
260 subNode
.Checked
= false;
262 // check all sub nodes
263 foreach (TreeNode subNode
in GetTreeNodeEnumerator(treeNode
.Nodes
))
264 subNode
.Checked
= true;
266 // check all parent nodes
267 for (TreeNode tn
= treeNode
; tn
!= null; tn
= tn
.Parent
)
272 static IEnumerable
<TreeNode
> GetTreeNodeEnumerator(TreeNodeCollection nodes
)
274 foreach (TreeNode node
in nodes
)
278 foreach (TreeNode subNode
in GetTreeNodeEnumerator(node
.Nodes
))
280 yield return subNode
;
285 protected void SelectNode(string[] pathLocations
)
287 SelectedNode
= TreeNodeHierarchy
[pathLocations
];
292 protected void SelectNextClosestNode()
294 if (SelectedNode
.NextNode
!= null)
295 SelectedNode
= SelectedNode
.NextNode
;
296 else if (SelectedNode
.PrevNode
!= null)
297 SelectedNode
= SelectedNode
.PrevNode
;
298 else if (SelectedNode
.Parent
!= null)
299 SelectedNode
= SelectedNode
.Parent
;
302 protected override void OnBeforeCheck(TreeViewCancelEventArgs e
)
304 base.OnBeforeCheck(e
);
305 SetUndoNodeBeforeCheckState(e
.Node
, e
.Node
.Checked
);
308 protected override void OnAfterCheck(TreeViewEventArgs e
)
310 base.OnAfterCheck(e
);
311 SetUndoNodeAfterCheckState(e
.Node
, e
.Node
.Checked
);
314 protected override void OnNodeMouseClick(TreeNodeMouseClickEventArgs e
)
316 base.OnNodeMouseClick(e
);
318 if (e
.Button
== MouseButtons
.Left
)
320 if (StatoscopeForm
.m_ctrlIsBeingPressed
)
322 IsolateSubTree(e
.Node
);
325 else if (e
.Button
== MouseButtons
.Right
)
327 ToggleSubTree(e
.Node
);
331 protected override void OnItemDrag(ItemDragEventArgs e
)
335 TreeNode treeNode
= (TreeNode
)e
.Item
;
336 DoDragDrop("/" + treeNode
.FullPath
, DragDropEffects
.Copy
);
339 // a convenience for accessing all TreeNodeHierarchy in a hierarchy - a TreeNodeCollection is just the current level
340 public class HierarchyTreeNodeCollection
: IEnumerable
342 TreeNodeCollection Nodes
;
344 public HierarchyTreeNodeCollection(TreeNodeCollection nodes
)
349 public TreeNode
this[string path
]
353 return this[path
.PathLocations()];
357 public TreeNode
this[IEnumerable
<string> pathLocations
]
361 TreeNode node
= null;
362 TreeNodeCollection nodes
= Nodes
;
364 foreach (string location
in pathLocations
)
366 if (!nodes
.ContainsKey(location
))
371 node
= nodes
[location
];
379 public bool IsChecked(string path
)
381 TreeNodeCollection nodes
= Nodes
;
383 foreach (string location
in path
.PathLocations())
385 TreeNode node
= nodes
[location
];
386 if (node
== null || !node
.Checked
)
394 public IEnumerator
GetEnumerator()
396 foreach (TreeNode node
in Nodes
)
400 foreach (TreeNode subNode
in GetTreeNodeEnumerator(node
.Nodes
))
402 yield return subNode
;