1 // Written by Christopher E. Miller
2 // See the included license.txt for copyright and license details.
3 
4 
5 ///
6 module dfl.treeview;
7 
8 private import dfl.internal.dlib;
9 
10 private import dfl.control, dfl.application, dfl.base, dfl.internal.winapi;
11 private import dfl.event, dfl.drawing, dfl.collections, dfl.internal.utf;
12 
13 version(DFL_NO_IMAGELIST)
14 {
15 }
16 else
17 {
18 	private import dfl.imagelist;
19 }
20 
21 
22 private extern(Windows) void _initTreeview();
23 
24 
25 ///
26 enum TreeViewAction: ubyte
27 {
28 	UNKNOWN, ///
29 	COLLAPSE, /// ditto
30 	EXPAND, /// ditto
31 	BY_KEYBOARD, /// ditto
32 	BY_MOUSE, /// ditto
33 }
34 
35 
36 ///
37 class TreeViewCancelEventArgs: CancelEventArgs
38 {
39 	///
40 	this(TreeNode node, bool cancel, TreeViewAction action)
41 	{
42 		super(cancel);
43 		
44 		_node = node;
45 		_action = action;
46 	}
47 	
48 	
49 	///
50 	final @property TreeViewAction action() // getter
51 	{
52 		return _action;
53 	}
54 	
55 	
56 	///
57 	final @property TreeNode node() // getter
58 	{
59 		return _node;
60 	}
61 	
62 	
63 	private:
64 	TreeNode _node;
65 	TreeViewAction _action;
66 }
67 
68 
69 ///
70 class TreeViewEventArgs: EventArgs
71 {
72 	///
73 	this(TreeNode node, TreeViewAction action)
74 	{
75 		_node = node;
76 		_action = action;
77 	}
78 	
79 	/// ditto
80 	this(TreeNode node)
81 	{
82 		_node = node;
83 		//_action = TreeViewAction.UNKNOWN;
84 	}
85 	
86 	
87 	///
88 	final @property TreeViewAction action() // getter
89 	{
90 		return _action;
91 	}
92 	
93 	
94 	///
95 	final @property TreeNode node() // getter
96 	{
97 		return _node;
98 	}
99 	
100 	
101 	private:
102 	TreeNode _node;
103 	TreeViewAction _action = TreeViewAction.UNKNOWN;
104 }
105 
106 
107 ///
108 class NodeLabelEditEventArgs: EventArgs
109 {
110 	///
111 	this(TreeNode node, Dstring label)
112 	{
113 		_node = node;
114 		_label = label;
115 	}
116 	
117 	/// ditto
118 	this(TreeNode node)
119 	{
120 		_node = node;
121 	}
122 	
123 	
124 	///
125 	final @property TreeNode node() // getter
126 	{
127 		return _node;
128 	}
129 	
130 	
131 	///
132 	final @property Dstring label() // getter
133 	{
134 		return _label;
135 	}
136 	
137 	
138 	///
139 	final @property void cancelEdit(bool byes) // setter
140 	{
141 		_cancel = byes;
142 	}
143 	
144 	/// ditto
145 	final @property bool cancelEdit() // getter
146 	{
147 		return _cancel;
148 	}
149 	
150 	
151 	private:
152 	TreeNode _node;
153 	Dstring _label;
154 	bool _cancel = false;
155 }
156 
157 
158 ///
159 class TreeNode: DObject
160 {
161 	///
162 	this(Dstring labelText)
163 	{
164 		this();
165 		
166 		ttext = labelText;
167 	}
168 	
169 	/// ditto
170 	this(Dstring labelText, TreeNode[] children)
171 	{
172 		this();
173 		
174 		ttext = labelText;
175 		tchildren.addRange(children);
176 	}
177 	
178 	/// ditto
179 	this()
180 	{
181 		Application.ppin(cast(void*)this);
182 		
183 		/+
184 		bcolor = Color.empty;
185 		fcolor = Color.empty;
186 		+/
187 		
188 		tchildren = new TreeNodeCollection(tview, this);
189 	}
190 	
191 	this(Object val) // package
192 	{
193 		this(getObjectString(val));
194 	}
195 	
196 	
197 	/+
198 	///
199 	final @property void backColor(Color c) // setter
200 	{
201 		bcolor = c;
202 	}
203 	
204 	/// ditto
205 	final @property Color backColor() // getter
206 	{
207 		return bcolor;
208 	}
209 	+/
210 	
211 	
212 	///
213 	final @property Rect bounds() // getter
214 	{
215 		Rect result;
216 		
217 		if(created)
218 		{
219 			RECT rect;
220 			*(cast(HTREEITEM*)&rect) = hnode;
221 			if(SendMessageA(tview.handle, TVM_GETITEMRECT, FALSE, cast(LPARAM)&rect))
222 			{
223 				result = Rect(&rect);
224 			}
225 		}
226 		
227 		return result;
228 	}
229 	
230 	
231 	///
232 	final @property TreeNode firstNode() // getter
233 	{
234 		if(tchildren.length)
235 			return tchildren._nodes[0];
236 		return null;
237 	}
238 	
239 	
240 	/+
241 	///
242 	final @property void foreColor(Color c) // setter
243 	{
244 		fcolor = c;
245 	}
246 	
247 	/// ditto
248 	final @property Color foreColor() // getter
249 	{
250 		return fcolor;
251 	}
252 	+/
253 	
254 	
255 	///
256 	// Path from the root to this node.
257 	final @property Dstring fullPath() // getter
258 	{
259 		if(!tparent)
260 			return ttext;
261 		
262 		// Might want to manually loop through parents and preallocate the whole buffer.
263 		assert(tview !is null);
264 		dchar sep;
265 		sep = tview.pathSeparator;
266 		//return std.string.format("%s%s%s", tparent.fullPath, sep, ttext);
267 		char[4] ssep;
268 		int sseplen = 0;
269 		foreach(char ch; (&sep)[0 .. 1])
270 		{
271 			ssep[sseplen++] = ch;
272 		}
273 		//return tparent.fullPath ~ ssep[0 .. sseplen] ~ ttext;
274 		return tparent.fullPath ~ cast(Dstring)ssep[0 .. sseplen] ~ ttext; // Needed in D2.
275 	}
276 	
277 	
278 	///
279 	final @property HTREEITEM handle() // getter
280 	{
281 		return hnode;
282 	}
283 	
284 	
285 	///
286 	// Index of this node in the parent node.
287 	final @property int index() // getter
288 	{
289 		int result = -1;
290 		if(tparent)
291 		{
292 			result = tparent.tchildren.indexOf(this);
293 			assert(result != -1);
294 		}
295 		return result;
296 	}
297 	
298 	
299 	/+
300 	///
301 	final @property bool isEditing() // getter
302 	{
303 	}
304 	+/
305 	
306 	
307 	///
308 	final @property bool isExpanded() // getter
309 	{
310 		return isState(TVIS_EXPANDED);
311 	}
312 	
313 	
314 	///
315 	final @property bool isSelected() // getter
316 	{
317 		return isState(TVIS_SELECTED);
318 	}
319 	
320 	
321 	/+
322 	///
323 	final @property bool isVisible() // getter
324 	{
325 	}
326 	+/
327 	
328 	
329 	///
330 	final @property TreeNode lastNode() // getter
331 	{
332 		if(tchildren.length)
333 			return tchildren._nodes[tchildren.length - 1];
334 		return null;
335 	}
336 	
337 	
338 	///
339 	// Next sibling node.
340 	final @property TreeNode nextNode() // getter
341 	{
342 		if(tparent)
343 		{
344 			int i;
345 			i = tparent.tchildren.indexOf(this);
346 			assert(i != -1);
347 			
348 			i++;
349 			if(i != tparent.tchildren.length)
350 				return tparent.tchildren._nodes[i];
351 		}
352 		return null;
353 	}
354 	
355 	
356 	/+
357 	///
358 	final @property void nodeFont(Font f) // setter
359 	{
360 		tfont = f;
361 	}
362 	
363 	/// ditto
364 	final @property Font nodeFont() // getter
365 	{
366 		return tfont;
367 	}
368 	+/
369 	
370 	
371 	///
372 	final @property TreeNodeCollection nodes() // getter
373 	{
374 		return tchildren;
375 	}
376 	
377 	
378 	///
379 	final @property TreeNode parent() // getter
380 	{
381 		return tparent;
382 	}
383 	
384 	
385 	///
386 	// Previous sibling node.
387 	final @property TreeNode prevNode() // getter
388 	{
389 		if(tparent)
390 		{
391 			int i;
392 			i = tparent.tchildren.indexOf(this);
393 			assert(i != -1);
394 			
395 			if(i)
396 			{
397 				i--;
398 				return tparent.tchildren._nodes[i];
399 			}
400 		}
401 		return null;
402 	}
403 	
404 	
405 	///
406 	final @property void tag(Object o) // setter
407 	{
408 		ttag = o;
409 	}
410 	
411 	/// ditto
412 	final @property Object tag() // getter
413 	{
414 		return ttag;
415 	}
416 	
417 	
418 	///
419 	final @property void text(Dstring newText) // setter
420 	{
421 		ttext = newText;
422 		
423 		if(created)
424 		{
425 			TV_ITEMA item;
426 			Message m;
427 			
428 			item.mask = TVIF_HANDLE | TVIF_TEXT;
429 			item.hItem = hnode;
430 			/+
431 			item.pszText = stringToStringz(ttext);
432 			//item.cchTextMax = ttext.length; // ?
433 			m = Message(tview.handle, TVM_SETITEMA, 0, cast(LPARAM)&item);
434 			+/
435 			if(dfl.internal.utf.useUnicode)
436 			{
437 				item.pszText = cast(typeof(item.pszText))dfl.internal.utf.toUnicodez(ttext);
438 				m = Message(tview.handle, TVM_SETITEMW, 0, cast(LPARAM)&item);
439 			}
440 			else
441 			{
442 				item.pszText = cast(typeof(item.pszText))dfl.internal.utf.unsafeAnsiz(ttext);
443 				m = Message(tview.handle, TVM_SETITEMA, 0, cast(LPARAM)&item);
444 			}
445 			tview.prevWndProc(m);
446 		}
447 	}
448 	
449 	/// ditto
450 	final @property Dstring text() // getter
451 	{
452 		return ttext;
453 	}
454 	
455 	
456 	///
457 	// Get the TreeView control this node belongs to.
458 	final @property TreeView treeView() // getter
459 	{
460 		return tview;
461 	}
462 	
463 	
464 	///
465 	final void beginEdit()
466 	{
467 		if(created)
468 		{
469 			SetFocus(tview.hwnd); // Needs to have focus.
470 			HWND hwEdit;
471 			hwEdit = cast(HWND)SendMessageA(tview.hwnd, TVM_EDITLABELA, 0, cast(LPARAM)hnode);
472 			if(!hwEdit)
473 				goto err_edit;
474 		}
475 		else
476 		{
477 			err_edit:
478 			throw new DflException("Unable to edit TreeNode");
479 		}
480 	}
481 	
482 	
483 	/+
484 	///
485 	final void endEdit(bool cancel)
486 	{
487 		// ?
488 	}
489 	+/
490 	
491 	
492 	///
493 	final void ensureVisible()
494 	{
495 		if(created)
496 		{
497 			SendMessageA(tview.hwnd, TVM_ENSUREVISIBLE, 0, cast(LPARAM)hnode);
498 		}
499 	}
500 	
501 	
502 	///
503 	final void collapse()
504 	{
505 		if(created)
506 		{
507 			SendMessageA(tview.hwnd, TVM_EXPAND, TVE_COLLAPSE, cast(LPARAM)hnode);
508 		}
509 	}
510 	
511 	
512 	///
513 	final void expand()
514 	{
515 		if(created)
516 		{
517 			SendMessageA(tview.hwnd, TVM_EXPAND, TVE_EXPAND, cast(LPARAM)hnode);
518 		}
519 	}
520 	
521 	
522 	///
523 	final void expandAll()
524 	{
525 		if(created)
526 		{
527 			SendMessageA(tview.hwnd, TVM_EXPAND, TVE_EXPAND, cast(LPARAM)hnode);
528 			
529 			foreach(TreeNode node; tchildren._nodes)
530 			{
531 				node.expandAll();
532 			}
533 		}
534 	}
535 	
536 	
537 	///
538 	static TreeNode fromHandle(TreeView tree, HTREEITEM handle)
539 	{
540 		return tree.treeNodeFromHandle(handle);
541 	}
542 	
543 	
544 	///
545 	final void remove()
546 	{
547 		if(tparent)
548 			tparent.tchildren.remove(this);
549 		else if(tview) // It's a top level node.
550 			tview.tchildren.remove(this);
551 	}
552 	
553 	
554 	///
555 	final void toggle()
556 	{
557 		if(created)
558 		{
559 			SendMessageA(tview.hwnd, TVM_EXPAND, TVE_TOGGLE, cast(LPARAM)hnode);
560 		}
561 	}
562 	
563 	
564 	version(DFL_NO_IMAGELIST)
565 	{
566 	}
567 	else
568 	{
569 		///
570 		final @property void imageIndex(int index) // setter
571 		{
572 			this._imgidx = index;
573 			
574 			if(created)
575 			{
576 				TV_ITEMA item;
577 				Message m;
578 				m = Message(tview.handle, TVM_SETITEMA, 0, cast(LPARAM)&item);
579 				
580 				item.mask = TVIF_HANDLE | TVIF_IMAGE;
581 				item.hItem = hnode;
582 				item.iImage = _imgidx;
583 				if(tview._selimgidx < 0)
584 				{
585 					item.mask |= TVIF_SELECTEDIMAGE;
586 					item.iSelectedImage = _imgidx;
587 				}
588 				tview.prevWndProc(m);
589 			}
590 		}
591 		
592 		/// ditto
593 		final @property int imageIndex() // getter
594 		{
595 			return _imgidx;
596 		}
597 	}
598 	
599 	
600 	override Dstring toString()
601 	{
602 		return ttext;
603 	}
604 	
605 	
606 	override Dequ opEquals(Object o)
607 	{
608 		return 0 == stringICmp(ttext, getObjectString(o)); // ?
609 	}
610 	
611 	Dequ opEquals(TreeNode node)
612 	{
613 		return 0 == stringICmp(ttext, node.ttext);
614 	}
615 	
616 	Dequ opEquals(Dstring val)
617 	{
618 		return 0 == stringICmp(ttext, val);
619 	}
620 	
621 	
622 	override int opCmp(Object o)
623 	{
624 		return stringICmp(ttext, getObjectString(o)); // ?
625 	}
626 	
627 	int opCmp(TreeNode node)
628 	{
629 		return stringICmp(ttext, node.ttext);
630 	}
631 	
632 	int opCmp(Dstring val)
633 	{
634 		return stringICmp(text, val);
635 	}
636 	
637 	
638 	private:
639 	Dstring ttext;
640 	TreeNode tparent;
641 	TreeNodeCollection tchildren;
642 	Object ttag;
643 	HTREEITEM hnode;
644 	TreeView tview;
645 	version(DFL_NO_IMAGELIST)
646 	{
647 	}
648 	else
649 	{
650 		int _imgidx = -1;
651 	}
652 	/+
653 	Color bcolor, fcolor;
654 	Font tfont;
655 	+/
656 	
657 	
658 	package final @property bool created() // getter
659 	{
660 		if(tview && tview.created())
661 		{
662 			assert(hnode);
663 			return true;
664 		}
665 		return false;
666 	}
667 	
668 	
669 	bool isState(UINT state)
670 	{
671 		if(created)
672 		{
673 			TV_ITEMA ti;
674 			ti.mask = TVIF_HANDLE | TVIF_STATE;
675 			ti.hItem = hnode;
676 			ti.stateMask = state;
677 			if(SendMessageA(tview.handle, TVM_GETITEMA, 0, cast(LPARAM)&ti))
678 			{
679 				if(ti.state & state)
680 					return true;
681 			}
682 		}
683 		return false;
684 	}
685 	
686 	
687 	void _reset()
688 	{
689 		hnode = null;
690 		tview = null;
691 		tparent = null;
692 	}
693 }
694 
695 
696 ///
697 class TreeNodeCollection
698 {
699 	void add(TreeNode node)
700 	{
701 		//cprintf("Adding node %p '%.*s'\n", cast(void*)node, getObjectString(node));
702 		
703 		int i;
704 		
705 		if(tview && tview.sorted())
706 		{
707 			// Insertion sort.
708 			
709 			for(i = 0; i != _nodes.length; i++)
710 			{
711 				if(node < _nodes[i])
712 					break;
713 			}
714 		}
715 		else
716 		{
717 			i = _nodes.length;
718 		}
719 		
720 		insert(i, node);
721 	}
722 	
723 	void add(Dstring text)
724 	{
725 		return add(new TreeNode(text));
726 	}
727 	
728 	void add(Object val)
729 	{
730 		return add(new TreeNode(getObjectString(val))); // ?
731 	}
732 	
733 	
734 	void addRange(Object[] range)
735 	{
736 		foreach(Object o; range)
737 		{
738 			add(o);
739 		}
740 	}
741 	
742 	void addRange(TreeNode[] range)
743 	{
744 		foreach(TreeNode node; range)
745 		{
746 			add(node);
747 		}
748 	}
749 	
750 	void addRange(Dstring[] range)
751 	{
752 		foreach(Dstring s; range)
753 		{
754 			add(s);
755 		}
756 	}
757 	
758 	
759 	// Like clear but doesn't bother removing stuff from the lists.
760 	// Used when a parent is being removed and the children only
761 	// need to be reset.
762 	private void _reset()
763 	{
764 		foreach(TreeNode node; _nodes)
765 		{
766 			node._reset();
767 		}
768 	}
769 	
770 	
771 	// Clear node handles when the TreeView window is destroyed so
772 	// that it can be reconstructed.
773 	private void _resetHandles()
774 	{
775 		foreach(TreeNode node; _nodes)
776 		{
777 			node.tchildren._resetHandles();
778 			node.hnode = null;
779 		}
780 	}
781 	
782 	
783 	private:
784 	
785 	TreeView tview; // null if not assigned to a TreeView yet.
786 	TreeNode tparent; // null if root. The parent of -_nodes-.
787 	TreeNode[] _nodes;
788 	
789 	
790 	void verifyNoParent(TreeNode node)
791 	{
792 		if(node.tparent)
793 			throw new DflException("TreeNode already belongs to a TreeView");
794 	}
795 	
796 	
797 	package this(TreeView treeView, TreeNode parentNode)
798 	{
799 		tview = treeView;
800 		tparent = parentNode;
801 	}
802 	
803 	
804 	package final void setTreeView(TreeView treeView)
805 	{
806 		tview = treeView;
807 		foreach(TreeNode node; _nodes)
808 		{
809 			node.tchildren.setTreeView(treeView);
810 		}
811 	}
812 	
813 	
814 	package final @property bool created() // getter
815 	{
816 		return tview && tview.created();
817 	}
818 	
819 	
820 	package void populateInsertChildNode(ref Message m, ref TV_ITEMA dest, TreeNode node)
821 	{
822 		with(dest)
823 		{
824 			mask = /+ TVIF_CHILDREN | +/ TVIF_PARAM | TVIF_TEXT;
825 			version(DFL_NO_IMAGELIST)
826 			{
827 			}
828 			else
829 			{
830 				mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE;
831 				iImage = node._imgidx;
832 				if(tview._selimgidx < 0)
833 					iSelectedImage = node._imgidx;
834 				else
835 					iSelectedImage = tview._selimgidx;
836 			}
837 			/+ cChildren = I_CHILDRENCALLBACK; +/
838 			lParam = cast(LPARAM)cast(void*)node;
839 			/+
840 			pszText = stringToStringz(node.text);
841 			//cchTextMax = node.text.length; // ?
842 			+/
843 			if(dfl.internal.utf.useUnicode)
844 			{
845 				pszText = cast(typeof(pszText))dfl.internal.utf.toUnicodez(node.text);
846 				m.hWnd = tview.handle;
847 				m.msg = TVM_INSERTITEMW;
848 			}
849 			else
850 			{
851 				pszText = cast(typeof(pszText))dfl.internal.utf.unsafeAnsiz(node.text);
852 				m.hWnd = tview.handle;
853 				m.msg = TVM_INSERTITEMA;
854 			}
855 		}
856 	}
857 	
858 	
859 	void doNodes()
860 	in
861 	{
862 		assert(created);
863 	}
864 	body
865 	{
866 		TV_INSERTSTRUCTA tis;
867 		Message m;
868 		
869 		tis.hInsertAfter = TVI_LAST;
870 		
871 		m.hWnd = tview.handle;
872 		m.wParam = 0;
873 		
874 		foreach(TreeNode node; _nodes)
875 		{
876 			assert(!node.handle);
877 			
878 			tis.hParent = tparent ? tparent.handle : TVI_ROOT;
879 			populateInsertChildNode(m, tis.item, node);
880 			
881 			m.lParam = cast(LPARAM)&tis;
882 			tview.prevWndProc(m);
883 			assert(m.result);
884 			node.hnode = cast(HTREEITEM)m.result;
885 			
886 			node.tchildren.doNodes();
887 		}
888 	}
889 	
890 	
891 	void _added(size_t idx, TreeNode val)
892 	{
893 		verifyNoParent(val);
894 		
895 		val.tparent = tparent;
896 		val.tview = tview;
897 		val.tchildren.setTreeView(tview);
898 		
899 		if(created)
900 		{
901 			TV_INSERTSTRUCTA tis;
902 			
903 			if(idx <= 0)
904 			{
905 				tis.hInsertAfter = TVI_FIRST;
906 			}
907 			else if(idx >= cast(int)_nodes.length)
908 			{
909 				tis.hInsertAfter = TVI_LAST;
910 			}
911 			else
912 			{
913 				tis.hInsertAfter = _nodes[idx - 1].handle;
914 			}
915 			
916 			tis.hParent = tparent ? tparent.handle : TVI_ROOT;
917 			assert(tis.hInsertAfter);
918 			
919 			Message m;
920 			m.wParam = 0;
921 			
922 			populateInsertChildNode(m, tis.item, val);
923 			
924 			m.lParam = cast(LPARAM)&tis;
925 			tview.prevWndProc(m);
926 			assert(m.result);
927 			val.hnode = cast(HTREEITEM)m.result;
928 			
929 			val.tchildren.doNodes();
930 			
931 			if(tparent)
932 				tview.invalidate(tparent.bounds);
933 		}
934 	}
935 	
936 	
937 	void _removing(size_t idx, TreeNode val)
938 	{
939 		if(size_t.max == idx) // Clearing all...
940 		{
941 			TreeNode[] nodes = _nodes;
942 			_nodes = _nodes[0 .. 0]; // Not nice to dfl.collections, but OK.
943 			if(created)
944 			{
945 				Message m;
946 				m.hWnd = tview.handle;
947 				m.msg = TVM_DELETEITEM;
948 				m.wParam = 0;
949 				if(tparent)
950 				{
951 					foreach(TreeNode node; nodes)
952 					{
953 						assert(node.handle !is null);
954 						m.lParam = cast(LPARAM)node.handle;
955 						tview.prevWndProc(m);
956 						
957 						node._reset();
958 					}
959 				}
960 				else
961 				{
962 					m.lParam = cast(LPARAM)TVI_ROOT;
963 					tview.prevWndProc(m);
964 					foreach(TreeNode node; nodes)
965 					{
966 						node._reset();
967 					}
968 				}
969 			}
970 		}
971 		else
972 		{
973 		}
974 	}
975 	
976 	
977 	void _removed(size_t idx, TreeNode val)
978 	{
979 		if(size_t.max == idx) // Clear all.
980 		{
981 		}
982 		else
983 		{
984 			if(created)
985 			{
986 				assert(val.hnode);
987 				Message m;
988 				m = Message(tview.handle, TVM_DELETEITEM, 0, cast(LPARAM)val.hnode);
989 				tview.prevWndProc(m);
990 			}
991 			
992 			// Clear children.
993 			val._reset();
994 		}
995 	}
996 	
997 	
998 	public:
999 	
1000 	mixin ListWrapArray!(TreeNode, _nodes,
1001 		_blankListCallback!(TreeNode), _added,
1002 		_removing, _removed,
1003 		true, /+true+/ false, false) _wraparray;
1004 }
1005 
1006 
1007 ///
1008 class TreeView: ControlSuperClass // docmain
1009 {
1010 	this()
1011 	{
1012 		_initTreeview();
1013 		
1014 		wstyle |= WS_TABSTOP | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_HASLINES;
1015 		wexstyle |= WS_EX_CLIENTEDGE;
1016 		ctrlStyle |= ControlStyles.SELECTABLE;
1017 		wclassStyle = treeviewClassStyle;
1018 		
1019 		tchildren = new TreeNodeCollection(this, null);
1020 	}
1021 	
1022 	
1023 	/+
1024 	~this()
1025 	{
1026 		/+
1027 		if(tchildren)
1028 			tchildren._dtorReset();
1029 		+/
1030 	}
1031 	+/
1032 	
1033 	
1034 	static @property Color defaultBackColor() // getter
1035 	{
1036 		return SystemColors.window;
1037 	}
1038 	
1039 	
1040 	override @property Color backColor() // getter
1041 	{
1042 		if(Color.empty == backc)
1043 			return defaultBackColor;
1044 		return backc;
1045 	}
1046 	
1047 	
1048 	override @property void backColor(Color b) // setter
1049 	{
1050 		super.backColor = b;
1051 		
1052 		if(created)
1053 		{
1054 			// For some reason the left edge isn't showing the new color.
1055 			// This causes the entire control to be redrawn with the new color.
1056 			// Sets the same font.
1057 			prevwproc(WM_SETFONT, this.font ? cast(WPARAM)this.font.handle : 0, MAKELPARAM(TRUE, 0));
1058 		}
1059 	}
1060 	
1061 	
1062 	static @property Color defaultForeColor() //getter
1063 	{
1064 		return SystemColors.windowText;
1065 	}
1066 	
1067 	
1068 	override @property Color foreColor() // getter
1069 	{
1070 		if(Color.empty == forec)
1071 			return defaultForeColor;
1072 		return forec;
1073 	}
1074 	
1075 	alias Control.foreColor foreColor; // Overload.
1076 	
1077 	
1078 	final @property void borderStyle(BorderStyle bs) // setter
1079 	{
1080 		final switch(bs)
1081 		{
1082 			case BorderStyle.FIXED_3D:
1083 				_style(_style() & ~WS_BORDER);
1084 				_exStyle(_exStyle() | WS_EX_CLIENTEDGE);
1085 				break;
1086 				
1087 			case BorderStyle.FIXED_SINGLE:
1088 				_exStyle(_exStyle() & ~WS_EX_CLIENTEDGE);
1089 				_style(_style() | WS_BORDER);
1090 				break;
1091 				
1092 			case BorderStyle.NONE:
1093 				_style(_style() & ~WS_BORDER);
1094 				_exStyle(_exStyle() & ~WS_EX_CLIENTEDGE);
1095 				break;
1096 		}
1097 		
1098 		if(created)
1099 		{
1100 			redrawEntire();
1101 		}
1102 	}
1103 	
1104 	
1105 	final @property BorderStyle borderStyle() // getter
1106 	{
1107 		if(_exStyle() & WS_EX_CLIENTEDGE)
1108 			return BorderStyle.FIXED_3D;
1109 		else if(_style() & WS_BORDER)
1110 			return BorderStyle.FIXED_SINGLE;
1111 		return BorderStyle.NONE;
1112 	}
1113 	
1114 	
1115 	/+
1116 	///
1117 	final @property void checkBoxes(bool byes) // setter
1118 	{
1119 		if(byes)
1120 			_style(_style() | TVS_CHECKBOXES);
1121 		else
1122 			_style(_style() & ~TVS_CHECKBOXES);
1123 		
1124 		_crecreate();
1125 	}
1126 	
1127 	/// ditto
1128 	final @property bool checkBoxes() // getter
1129 	{
1130 		return (_style() & TVS_CHECKBOXES) != 0;
1131 	}
1132 	+/
1133 	
1134 	
1135 	///
1136 	final @property void fullRowSelect(bool byes) // setter
1137 	{
1138 		if(byes)
1139 			_style(_style() | TVS_FULLROWSELECT);
1140 		else
1141 			_style(_style() & ~TVS_FULLROWSELECT);
1142 		
1143 		_crecreate(); // ?
1144 	}
1145 	
1146 	/// ditto
1147 	final @property bool fullRowSelect() // getter
1148 	{
1149 		return (_style() & TVS_FULLROWSELECT) != 0;
1150 	}
1151 	
1152 	
1153 	///
1154 	final @property void hideSelection(bool byes) // setter
1155 	{
1156 		if(byes)
1157 			_style(_style() & ~TVS_SHOWSELALWAYS);
1158 		else
1159 			_style(_style() | TVS_SHOWSELALWAYS);
1160 	}
1161 	
1162 	/// ditto
1163 	final @property bool hideSelection() // getter
1164 	{
1165 		return (_style() & TVS_SHOWSELALWAYS) == 0;
1166 	}
1167 	
1168 	
1169 	deprecated alias hoverSelection hotTracking;
1170 	
1171 	///
1172 	final @property void hoverSelection(bool byes) // setter
1173 	{
1174 		if(byes)
1175 			_style(_style() | TVS_TRACKSELECT);
1176 		else
1177 			_style(_style() & ~TVS_TRACKSELECT);
1178 	}
1179 	
1180 	/// ditto
1181 	final @property bool hoverSelection() // getter
1182 	{
1183 		return (_style() & TVS_TRACKSELECT) != 0;
1184 	}
1185 	
1186 	
1187 	///
1188 	final @property void indent(int newIndent) // setter
1189 	{
1190 		if(newIndent < 0)
1191 			newIndent = 0;
1192 		else if(newIndent > 32_000)
1193 			newIndent = 32_000;
1194 		
1195 		ind = newIndent;
1196 		
1197 		if(created)
1198 			SendMessageA(hwnd, TVM_SETINDENT, ind, 0);
1199 	}
1200 	
1201 	/// ditto
1202 	final @property int indent() // getter
1203 	{
1204 		if(created)
1205 			ind = cast(int)SendMessageA(hwnd, TVM_GETINDENT, 0, 0);
1206 		return ind;
1207 	}
1208 	
1209 	
1210 	///
1211 	final @property void itemHeight(int h) // setter
1212 	{
1213 		if(h < 0)
1214 			h = 0;
1215 		
1216 		iheight = h;
1217 		
1218 		if(created)
1219 			SendMessageA(hwnd, TVM_SETITEMHEIGHT, iheight, 0);
1220 	}
1221 	
1222 	/// ditto
1223 	final @property int itemHeight() // getter
1224 	{
1225 		if(created)
1226 			iheight = cast(int)SendMessageA(hwnd, TVM_GETITEMHEIGHT, 0, 0);
1227 		return iheight;
1228 	}
1229 	
1230 	
1231 	///
1232 	final @property void labelEdit(bool byes) // setter
1233 	{
1234 		if(byes)
1235 			_style(_style() | TVS_EDITLABELS);
1236 		else
1237 			_style(_style() & ~TVS_EDITLABELS);
1238 	}
1239 	
1240 	/// ditto
1241 	final @property bool labelEdit() // getter
1242 	{
1243 		return (_style() & TVS_EDITLABELS) != 0;
1244 	}
1245 	
1246 	
1247 	///
1248 	final @property TreeNodeCollection nodes() // getter
1249 	{
1250 		return tchildren;
1251 	}
1252 	
1253 	
1254 	///
1255 	final @property void pathSeparator(dchar sep) // setter
1256 	{
1257 		pathsep = sep;
1258 	}
1259 	
1260 	/// ditto
1261 	final @property dchar pathSeparator() // getter
1262 	{
1263 		return pathsep;
1264 	}
1265 	
1266 	
1267 	///
1268 	final @property void scrollable(bool byes) // setter
1269 	{
1270 		if(byes)
1271 			_style(_style() & ~TVS_NOSCROLL);
1272 		else
1273 			_style(_style() | TVS_NOSCROLL);
1274 		
1275 		if(created)
1276 			redrawEntire();
1277 	}
1278 	
1279 	/// ditto
1280 	final @property bool scrollable() // getter
1281 	{
1282 		return (_style & TVS_NOSCROLL) == 0;
1283 	}
1284 	
1285 	
1286 	///
1287 	final @property void selectedNode(TreeNode node) // setter
1288 	{
1289 		if(created)
1290 		{
1291 			if(node)
1292 			{
1293 				SendMessageA(hwnd, TVM_SELECTITEM, TVGN_CARET, cast(LPARAM)node.handle);
1294 			}
1295 			else
1296 			{
1297 				// Should the selection be cleared if -node- is null?
1298 				//SendMessageA(hwnd, TVM_SELECTITEM, TVGN_CARET, cast(LPARAM)null);
1299 			}
1300 		}
1301 	}
1302 	
1303 	/// ditto
1304 	final @property TreeNode selectedNode() // getter
1305 	{
1306 		if(created)
1307 		{
1308 			HTREEITEM hnode;
1309 			hnode = cast(HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM, TVGN_CARET, cast(LPARAM)null);
1310 			if(hnode)
1311 				return treeNodeFromHandle(hnode);
1312 		}
1313 		return null;
1314 	}
1315 	
1316 	
1317 	///
1318 	final @property void showLines(bool byes) // setter
1319 	{
1320 		if(byes)
1321 			_style(_style() | TVS_HASLINES);
1322 		else
1323 			_style(_style() & ~TVS_HASLINES);
1324 		
1325 		_crecreate(); // ?
1326 	}
1327 	
1328 	/// ditto
1329 	final @property bool showLines() // getter
1330 	{
1331 		return (_style() & TVS_HASLINES) != 0;
1332 	}
1333 	
1334 	
1335 	///
1336 	final @property void showPlusMinus(bool byes) // setter
1337 	{
1338 		if(byes)
1339 			_style(_style() | TVS_HASBUTTONS);
1340 		else
1341 			_style(_style() & ~TVS_HASBUTTONS);
1342 		
1343 		_crecreate(); // ?
1344 	}
1345 	
1346 	/// ditto
1347 	final @property bool showPlusMinus() // getter
1348 	{
1349 		return (_style() & TVS_HASBUTTONS) != 0;
1350 	}
1351 	
1352 	
1353 	///
1354 	// -showPlusMinus- should be false.
1355 	final @property void singleExpand(bool byes) // setter
1356 	{
1357 		if(byes)
1358 			_style(_style() | TVS_SINGLEEXPAND);
1359 		else
1360 			_style(_style() & ~TVS_SINGLEEXPAND);
1361 		
1362 		_crecreate(); // ?
1363 	}
1364 	
1365 	/// ditto
1366 	final @property bool singleExpand() // getter
1367 	{
1368 		return (_style & TVS_SINGLEEXPAND) != 0;
1369 	}
1370 	
1371 	
1372 	///
1373 	final @property void showRootLines(bool byes) // setter
1374 	{
1375 		if(byes)
1376 			_style(_style() | TVS_LINESATROOT);
1377 		else
1378 			_style(_style() & ~TVS_LINESATROOT);
1379 		
1380 		_crecreate(); // ?
1381 	}
1382 	
1383 	/// ditto
1384 	final @property bool showRootLines() // getter
1385 	{
1386 		return (_style() & TVS_LINESATROOT) != 0;
1387 	}
1388 	
1389 	
1390 	///
1391 	final @property void sorted(bool byes) // setter
1392 	{
1393 		_sort = byes;
1394 	}
1395 	
1396 	/// ditto
1397 	final @property bool sorted() // getter
1398 	{
1399 		return _sort;
1400 	}
1401 	
1402 	
1403 	///
1404 	// First visible node, based on the scrolled position.
1405 	final @property TreeNode topNode() // getter
1406 	{
1407 		if(created)
1408 		{
1409 			HTREEITEM hnode;
1410 			hnode = cast(HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM,
1411 				TVGN_FIRSTVISIBLE, cast(LPARAM)null);
1412 			if(hnode)
1413 				return treeNodeFromHandle(hnode);
1414 		}
1415 		return null;
1416 	}
1417 	
1418 	
1419 	///
1420 	// Number of visible nodes, including partially visible.
1421 	final @property int visibleCount() // getter
1422 	{
1423 		if(!created)
1424 			return 0;
1425 		return cast(int)SendMessageA(hwnd, TVM_GETVISIBLECOUNT, 0, 0);
1426 	}
1427 	
1428 	
1429 	///
1430 	final void beginUpdate()
1431 	{
1432 		SendMessageA(handle, WM_SETREDRAW, false, 0);
1433 	}
1434 	
1435 	/// ditto
1436 	final void endUpdate()
1437 	{
1438 		SendMessageA(handle, WM_SETREDRAW, true, 0);
1439 		invalidate(true); // Show updates.
1440 	}
1441 	
1442 	
1443 	///
1444 	final void collapseAll()
1445 	{
1446 		if(created)
1447 		{
1448 			void collapsing(TreeNodeCollection tchildren)
1449 			{
1450 				foreach(TreeNode node; tchildren._nodes)
1451 				{
1452 					SendMessageA(hwnd, TVM_EXPAND, TVE_COLLAPSE, cast(LPARAM)node.hnode);
1453 					collapsing(node.tchildren);
1454 				}
1455 			}
1456 			
1457 			
1458 			collapsing(tchildren);
1459 		}
1460 	}
1461 	
1462 	
1463 	///
1464 	final void expandAll()
1465 	{
1466 		if(created)
1467 		{
1468 			void expanding(TreeNodeCollection tchildren)
1469 			{
1470 				foreach(TreeNode node; tchildren._nodes)
1471 				{
1472 					SendMessageA(hwnd, TVM_EXPAND, TVE_EXPAND, cast(LPARAM)node.hnode);
1473 					expanding(node.tchildren);
1474 				}
1475 			}
1476 			
1477 			
1478 			expanding(tchildren);
1479 		}
1480 	}
1481 	
1482 	
1483 	///
1484 	final TreeNode getNodeAt(int x, int y)
1485 	{
1486 		if(created)
1487 		{
1488 			TVHITTESTINFO thi;
1489 			HTREEITEM hti;
1490 			thi.pt.x = x;
1491 			thi.pt.y = y;
1492 			hti = cast(HTREEITEM)SendMessageA(hwnd, TVM_HITTEST, 0, cast(LPARAM)&thi);
1493 			if(hti)
1494 			{
1495 				TreeNode result;
1496 				result = treeNodeFromHandle(hti);
1497 				if(result)
1498 				{
1499 					assert(result.tview is this);
1500 					return result;
1501 				}
1502 			}
1503 		}
1504 		return null;
1505 	}
1506 	
1507 	/// ditto
1508 	final TreeNode getNodeAt(Point pt)
1509 	{
1510 		return getNodeAt(pt.x, pt.y);
1511 	}
1512 	
1513 	
1514 	/+
1515 	///
1516 	// TODO: finish.
1517 	final int getNodeCount(bool includeSubNodes)
1518 	{
1519 		int result;
1520 		result = tchildren.length();
1521 		
1522 		if(includeSubNodes)
1523 		{
1524 			// ...
1525 		}
1526 		
1527 		return result;
1528 	}
1529 	+/
1530 	
1531 	
1532 	version(DFL_NO_IMAGELIST)
1533 	{
1534 	}
1535 	else
1536 	{
1537 		///
1538 		final @property void imageList(ImageList imglist) // setter
1539 		{
1540 			if(isHandleCreated)
1541 			{
1542 				prevwproc(TVM_SETIMAGELIST, TVSIL_NORMAL,
1543 					cast(LPARAM)(imglist ? imglist.handle : cast(HIMAGELIST)null));
1544 			}
1545 			
1546 			_imglist = imglist;
1547 		}
1548 		
1549 		/// ditto
1550 		final @property ImageList imageList() // getter
1551 		{
1552 			return _imglist;
1553 		}
1554 		
1555 		
1556 		/+
1557 		///
1558 		// Default image index (if -1 use this).
1559 		final @property void imageIndex(int index) // setter
1560 		{
1561 			_defimgidx = index;
1562 		}
1563 		
1564 		/// ditto
1565 		final @property int imageIndex() // getter
1566 		{
1567 			return _defimgidx;
1568 		}
1569 		+/
1570 		
1571 		
1572 		///
1573 		final @property void selectedImageIndex(int index) // setter
1574 		{
1575 			//assert(index >= 0);
1576 			assert(index >= -1);
1577 			_selimgidx = index;
1578 			
1579 			if(isHandleCreated)
1580 			{
1581 				TreeNode curnode = selectedNode;
1582 				_crecreate();
1583 				if(curnode)
1584 					curnode.ensureVisible();
1585 			}
1586 		}
1587 		
1588 		/// ditto
1589 		final @property int selectedImageIndex() // getter
1590 		{
1591 			return _selimgidx;
1592 		}
1593 	}
1594 	
1595 	
1596 	protected override @property Size defaultSize() // getter
1597 	{
1598 		return Size(120, 100);
1599 	}
1600 	
1601 	
1602 	/+
1603 	override void createHandle()
1604 	{
1605 		if(isHandleCreated)
1606 			return;
1607 		
1608 		createClassHandle(TREEVIEW_CLASSNAME);
1609 		
1610 		onHandleCreated(EventArgs.empty);
1611 	}
1612 	+/
1613 	
1614 	
1615 	protected override void createParams(ref CreateParams cp)
1616 	{
1617 		super.createParams(cp);
1618 		
1619 		cp.className = TREEVIEW_CLASSNAME;
1620 	}
1621 	
1622 	
1623 	protected override void onHandleCreated(EventArgs ea)
1624 	{
1625 		super.onHandleCreated(ea);
1626 		
1627 		prevwproc(CCM_SETVERSION, 5, 0); // Fixes font size issue.
1628 		
1629 		prevwproc(TVM_SETINDENT, ind, 0);
1630 		
1631 		prevwproc(TVM_SETITEMHEIGHT, iheight, 0);
1632 		
1633 		version(DFL_NO_IMAGELIST)
1634 		{
1635 		}
1636 		else
1637 		{
1638 			if(_imglist)
1639 				prevwproc(TVM_SETIMAGELIST, TVSIL_NORMAL, cast(LPARAM)_imglist.handle);
1640 		}
1641 		
1642 		tchildren.doNodes();
1643 	}
1644 	
1645 	
1646 	protected override void onHandleDestroyed(EventArgs ea)
1647 	{
1648 		tchildren._resetHandles();
1649 		
1650 		super.onHandleDestroyed(ea);
1651 	}
1652 	
1653 	
1654 	protected override void wndProc(ref Message m)
1655 	{
1656 		// TODO: support these messages.
1657 		switch(m.msg)
1658 		{
1659 			case TVM_INSERTITEMA:
1660 			case TVM_INSERTITEMW:
1661 				m.result = cast(LRESULT)null;
1662 				return;
1663 			
1664 			case TVM_SETITEMA:
1665 			case TVM_SETITEMW:
1666 				m.result = cast(LRESULT)-1;
1667 				return;
1668 			
1669 			case TVM_DELETEITEM:
1670 				m.result = FALSE;
1671 				return;
1672 			
1673 			case TVM_SETIMAGELIST:
1674 				m.result = cast(LRESULT)null;
1675 				return;
1676 			
1677 			default:
1678 		}
1679 		
1680 		super.wndProc(m);
1681 	}
1682 	
1683 	
1684 	protected override void prevWndProc(ref Message msg)
1685 	{
1686 		//msg.result = CallWindowProcA(treeviewPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam);
1687 		msg.result = dfl.internal.utf.callWindowProc(treeviewPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam);
1688 	}
1689 	
1690 	
1691 	//TreeViewEventHandler afterCollapse;
1692 	Event!(TreeView, TreeViewEventArgs) afterCollapse; ///
1693 	//TreeViewEventHandler afterExpand;
1694 	Event!(TreeView, TreeViewEventArgs) afterExpand; ///
1695 	//TreeViewEventHandler afterSelect;
1696 	Event!(TreeView, TreeViewEventArgs) afterSelect; ///
1697 	//NodeLabelEditEventHandler afterLabelEdit;
1698 	Event!(TreeView, NodeLabelEditEventArgs) afterLabelEdit; ///
1699 	//TreeViewCancelEventHandler beforeCollapse;
1700 	Event!(TreeView, TreeViewCancelEventArgs) beforeCollapse; ///
1701 	//TreeViewCancelEventHandler beforeExpand;
1702 	Event!(TreeView, TreeViewCancelEventArgs) beforeExpand; ///
1703 	//TreeViewCancelEventHandler beforeSelect;
1704 	Event!(TreeView, TreeViewCancelEventArgs) beforeSelect; ///
1705 	//NodeLabelEditEventHandler beforeLabelEdit;
1706 	Event!(TreeView, NodeLabelEditEventArgs) beforeLabelEdit; ///
1707 	
1708 	
1709 	///
1710 	protected void onAfterCollapse(TreeViewEventArgs ea)
1711 	{
1712 		afterCollapse(this, ea);
1713 	}
1714 	
1715 	
1716 	///
1717 	protected void onAfterExpand(TreeViewEventArgs ea)
1718 	{
1719 		afterExpand(this, ea);
1720 	}
1721 	
1722 	
1723 	///
1724 	protected void onAfterSelect(TreeViewEventArgs ea)
1725 	{
1726 		afterSelect(this, ea);
1727 	}
1728 	
1729 	
1730 	///
1731 	protected void onAfterLabelEdit(NodeLabelEditEventArgs ea)
1732 	{
1733 		afterLabelEdit(this, ea);
1734 	}
1735 	
1736 	
1737 	///
1738 	protected void onBeforeCollapse(TreeViewCancelEventArgs ea)
1739 	{
1740 		beforeCollapse(this, ea);
1741 	}
1742 	
1743 	
1744 	///
1745 	protected void onBeforeExpand(TreeViewCancelEventArgs ea)
1746 	{
1747 		beforeExpand(this, ea);
1748 	}
1749 	
1750 	
1751 	///
1752 	protected void onBeforeSelect(TreeViewCancelEventArgs ea)
1753 	{
1754 		beforeSelect(this, ea);
1755 	}
1756 	
1757 	
1758 	///
1759 	protected void onBeforeLabelEdit(NodeLabelEditEventArgs ea)
1760 	{
1761 		beforeLabelEdit(this, ea);
1762 	}
1763 	
1764 	
1765 	protected override void onReflectedMessage(ref Message m) // package
1766 	{
1767 		super.onReflectedMessage(m);
1768 		
1769 		switch(m.msg)
1770 		{
1771 			case WM_NOTIFY:
1772 				{
1773 					NMHDR* nmh;
1774 					NM_TREEVIEW* nmtv;
1775 					TreeViewCancelEventArgs cea;
1776 					
1777 					nmh = cast(NMHDR*)m.lParam;
1778 					assert(nmh.hwndFrom == hwnd);
1779 					
1780 					switch(nmh.code)
1781 					{
1782 						case NM_CUSTOMDRAW:
1783 							{
1784 								NMTVCUSTOMDRAW* tvcd;
1785 								tvcd = cast(NMTVCUSTOMDRAW*)nmh;
1786 								//if(tvcd.nmcd.dwDrawStage & CDDS_ITEM)
1787 								{
1788 									//if(tvcd.nmcd.uItemState & CDIS_SELECTED)
1789 									if((tvcd.nmcd.dwDrawStage & CDDS_ITEM)
1790 										&& (tvcd.nmcd.uItemState & CDIS_SELECTED))
1791 									{
1792 										// Note: might not look good with custom colors.
1793 										tvcd.clrText = SystemColors.highlightText.toRgb();
1794 										tvcd.clrTextBk = SystemColors.highlight.toRgb();
1795 									}
1796 									else
1797 									{
1798 										//tvcd.clrText = foreColor.toRgb();
1799 										tvcd.clrText = foreColor.solidColor(backColor).toRgb();
1800 										tvcd.clrTextBk = backColor.toRgb();
1801 									}
1802 								}
1803 								m.result |= CDRF_NOTIFYITEMDRAW; // | CDRF_NOTIFYITEMERASE;
1804 								
1805 								// This doesn't seem to be doing anything.
1806 								Font fon;
1807 								fon = this.font;
1808 								if(fon)
1809 								{
1810 									SelectObject(tvcd.nmcd.hdc, fon.handle);
1811 									m.result |= CDRF_NEWFONT;
1812 								}
1813 							}
1814 							break;
1815 						
1816 						/+
1817 						case TVN_GETDISPINFOA:
1818 							
1819 							break;
1820 						+/
1821 						
1822 						case TVN_SELCHANGINGW:
1823 							goto sel_changing;
1824 						
1825 						case TVN_SELCHANGINGA:
1826 							if(dfl.internal.utf.useUnicode)
1827 								break;
1828 							sel_changing:
1829 							
1830 							nmtv = cast(NM_TREEVIEW*)nmh;
1831 							switch(nmtv.action)
1832 							{
1833 								case TVC_BYMOUSE:
1834 									cea = new TreeViewCancelEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam,
1835 										false, TreeViewAction.BY_MOUSE);
1836 									onBeforeSelect(cea);
1837 									m.result = cea.cancel;
1838 									break;
1839 								
1840 								case TVC_BYKEYBOARD:
1841 									cea = new TreeViewCancelEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam,
1842 										false, TreeViewAction.BY_KEYBOARD);
1843 									onBeforeSelect(cea);
1844 									m.result = cea.cancel;
1845 									break;
1846 								
1847 								//case TVC_UNKNOWN:
1848 								default:
1849 									cea = new TreeViewCancelEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam,
1850 										false, TreeViewAction.UNKNOWN);
1851 									onBeforeSelect(cea);
1852 									m.result = cea.cancel;
1853 							}
1854 							break;
1855 						
1856 						case TVN_SELCHANGEDW:
1857 							goto sel_changed;
1858 						
1859 						case TVN_SELCHANGEDA:
1860 							if(dfl.internal.utf.useUnicode)
1861 								break;
1862 							sel_changed:
1863 							
1864 							nmtv = cast(NM_TREEVIEW*)nmh;
1865 							switch(nmtv.action)
1866 							{
1867 								case TVC_BYMOUSE:
1868 									onAfterSelect(new TreeViewEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam,
1869 										TreeViewAction.BY_MOUSE));
1870 									break;
1871 								
1872 								case TVC_BYKEYBOARD:
1873 									onAfterSelect(new TreeViewEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam,
1874 										TreeViewAction.BY_KEYBOARD));
1875 									break;
1876 								
1877 								//case TVC_UNKNOWN:
1878 								default:
1879 									onAfterSelect(new TreeViewEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam,
1880 										TreeViewAction.UNKNOWN));
1881 							}
1882 							break;
1883 						
1884 						case TVN_ITEMEXPANDINGW:
1885 							goto item_expanding;
1886 						
1887 						case TVN_ITEMEXPANDINGA:
1888 							if(dfl.internal.utf.useUnicode)
1889 								break;
1890 							item_expanding:
1891 							
1892 							nmtv = cast(NM_TREEVIEW*)nmh;
1893 							switch(nmtv.action)
1894 							{
1895 								case TVE_COLLAPSE:
1896 									cea = new TreeViewCancelEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam,
1897 										false, TreeViewAction.COLLAPSE);
1898 									onBeforeCollapse(cea);
1899 									m.result = cea.cancel;
1900 									break;
1901 								
1902 								case TVE_EXPAND:
1903 									cea = new TreeViewCancelEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam,
1904 										false, TreeViewAction.EXPAND);
1905 									onBeforeExpand(cea);
1906 									m.result = cea.cancel;
1907 									break;
1908 								
1909 								default:
1910 							}
1911 							break;
1912 						
1913 						case TVN_ITEMEXPANDEDW:
1914 							goto item_expanded;
1915 						
1916 						case TVN_ITEMEXPANDEDA:
1917 							if(dfl.internal.utf.useUnicode)
1918 								break;
1919 							item_expanded:
1920 							
1921 							nmtv = cast(NM_TREEVIEW*)nmh;
1922 							switch(nmtv.action)
1923 							{
1924 								case TVE_COLLAPSE:
1925 									{
1926 										scope TreeViewEventArgs tvea = new TreeViewEventArgs(
1927 											cast(TreeNode)cast(void*)nmtv.itemNew.lParam,
1928 											TreeViewAction.COLLAPSE);
1929 										onAfterCollapse(tvea);
1930 									}
1931 									break;
1932 								
1933 								case TVE_EXPAND:
1934 									{
1935 										scope TreeViewEventArgs tvea = new TreeViewEventArgs(
1936 											cast(TreeNode)cast(void*)nmtv.itemNew.lParam,
1937 											TreeViewAction.EXPAND);
1938 										onAfterExpand(tvea);
1939 									}
1940 									break;
1941 								
1942 								default:
1943 							}
1944 							break;
1945 						
1946 						case TVN_BEGINLABELEDITW:
1947 							goto begin_label_edit;
1948 						
1949 						case TVN_BEGINLABELEDITA:
1950 							if(dfl.internal.utf.useUnicode)
1951 								break;
1952 							begin_label_edit:
1953 							
1954 							{
1955 								TV_DISPINFOA* nmdi;
1956 								nmdi = cast(TV_DISPINFOA*)nmh;
1957 								TreeNode node;
1958 								node = cast(TreeNode)cast(void*)nmdi.item.lParam;
1959 								scope NodeLabelEditEventArgs nleea = new NodeLabelEditEventArgs(node);
1960 								onBeforeLabelEdit(nleea);
1961 								m.result = nleea.cancelEdit;
1962 							}
1963 							break;
1964 						
1965 						case TVN_ENDLABELEDITW:
1966 							{
1967 								Dstring label;
1968 								TV_DISPINFOW* nmdi;
1969 								nmdi = cast(TV_DISPINFOW*)nmh;
1970 								if(nmdi.item.pszText)
1971 								{
1972 									TreeNode node;
1973 									node = cast(TreeNode)cast(void*)nmdi.item.lParam;
1974 									label = fromUnicodez(nmdi.item.pszText);
1975 									scope NodeLabelEditEventArgs nleea = new NodeLabelEditEventArgs(node, label);
1976 									onAfterLabelEdit(nleea);
1977 									if(nleea.cancelEdit)
1978 									{
1979 										m.result = FALSE;
1980 									}
1981 									else
1982 									{
1983 										// TODO: check if correct implementation.
1984 										// Update the node's cached text..
1985 										node.ttext = label;
1986 										
1987 										m.result = TRUE;
1988 									}
1989 								}
1990 							}
1991 							break;
1992 						
1993 						case TVN_ENDLABELEDITA:
1994 							if(dfl.internal.utf.useUnicode)
1995 							{
1996 								break;
1997 							}
1998 							else
1999 							{
2000 								Dstring label;
2001 								TV_DISPINFOA* nmdi;
2002 								nmdi = cast(TV_DISPINFOA*)nmh;
2003 								if(nmdi.item.pszText)
2004 								{
2005 									TreeNode node;
2006 									node = cast(TreeNode)cast(void*)nmdi.item.lParam;
2007 									label = fromAnsiz(nmdi.item.pszText);
2008 									scope NodeLabelEditEventArgs nleea = new NodeLabelEditEventArgs(node, label);
2009 									onAfterLabelEdit(nleea);
2010 									if(nleea.cancelEdit)
2011 									{
2012 										m.result = FALSE;
2013 									}
2014 									else
2015 									{
2016 										// TODO: check if correct implementation.
2017 										// Update the node's cached text..
2018 										node.ttext = label;
2019 										
2020 										m.result = TRUE;
2021 									}
2022 								}
2023 								break;
2024 							}
2025 						
2026 						default:
2027 					}
2028 				}
2029 				break;
2030 			
2031 			default:
2032 		}
2033 	}
2034 	
2035 	
2036 	private:
2037 	TreeNodeCollection tchildren;
2038 	int ind = 19; // Indent.
2039 	dchar pathsep = '\\';
2040 	bool _sort = false;
2041 	int iheight = 16;
2042 	version(DFL_NO_IMAGELIST)
2043 	{
2044 	}
2045 	else
2046 	{
2047 		ImageList _imglist;
2048 		int _selimgidx = -1; //0;
2049 	}
2050 	
2051 	
2052 	TreeNode treeNodeFromHandle(HTREEITEM hnode)
2053 	{
2054 		TV_ITEMA ti;
2055 		ti.mask = TVIF_HANDLE | TVIF_PARAM;
2056 		ti.hItem = hnode;
2057 		if(SendMessageA(hwnd, TVM_GETITEMA, 0, cast(LPARAM)&ti))
2058 		{
2059 			return cast(TreeNode)cast(void*)ti.lParam;
2060 		}
2061 		return null;
2062 	}
2063 	
2064 	package:
2065 	final:
2066 	LRESULT prevwproc(UINT msg, WPARAM wparam, LPARAM lparam)
2067 	{
2068 		//return CallWindowProcA(treeviewPrevWndProc, hwnd, msg, wparam, lparam);
2069 		return dfl.internal.utf.callWindowProc(treeviewPrevWndProc, hwnd, msg, wparam, lparam);
2070 	}
2071 }
2072