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 		/*
690 		if(hnode !is null)
691 			hnode = null;
692 		if(tview !is null)
693 			tview = null;
694 		if(tparent!is null)
695 			tparent = null;
696 		*/
697 	}
698 }
699 
700 
701 ///
702 class TreeNodeCollection
703 {
704 	void add(TreeNode node)
705 	{
706 		//cprintf("Adding node %p '%.*s'\n", cast(void*)node, getObjectString(node));
707 		
708 		int i;
709 		
710 		if(tview && tview.sorted())
711 		{
712 			// Insertion sort.
713 			
714 			for(i = 0; i != _nodes.length; i++)
715 			{
716 				if(node < _nodes[i])
717 					break;
718 			}
719 		}
720 		else
721 		{
722 			i = cast(int)_nodes.length;
723 		}
724 		
725 		insert(i, node);
726 	}
727 	
728 	void add(Dstring text)
729 	{
730 		return add(new TreeNode(text));
731 	}
732 	
733 	void add(Object val)
734 	{
735 		return add(new TreeNode(getObjectString(val))); // ?
736 	}
737 	
738 	
739 	void addRange(Object[] range)
740 	{
741 		foreach(Object o; range)
742 		{
743 			add(o);
744 		}
745 	}
746 	
747 	void addRange(TreeNode[] range)
748 	{
749 		foreach(TreeNode node; range)
750 		{
751 			add(node);
752 		}
753 	}
754 	
755 	void addRange(Dstring[] range)
756 	{
757 		foreach(Dstring s; range)
758 		{
759 			add(s);
760 		}
761 	}
762 	
763 	
764 	// Like clear but doesn't bother removing stuff from the lists.
765 	// Used when a parent is being removed and the children only
766 	// need to be reset.
767 	private void _reset()
768 	{
769 		foreach(TreeNode node; _nodes)
770 		{
771 			node._reset();
772 		}
773 	}
774 	
775 	
776 	// Clear node handles when the TreeView window is destroyed so
777 	// that it can be reconstructed.
778 	private void _resetHandles()
779 	{
780 		foreach(TreeNode node; _nodes)
781 		{
782 			node.tchildren._resetHandles();
783 			node.hnode = null;
784 		}
785 	}
786 	
787 	
788 	private:
789 	
790 	TreeView tview; // null if not assigned to a TreeView yet.
791 	TreeNode tparent; // null if root. The parent of -_nodes-.
792 	TreeNode[] _nodes;
793 	
794 	
795 	void verifyNoParent(TreeNode node)
796 	{
797 		if(node.tparent)
798 			throw new DflException("TreeNode already belongs to a TreeView");
799 	}
800 	
801 	
802 	package this(TreeView treeView, TreeNode parentNode)
803 	{
804 		tview = treeView;
805 		tparent = parentNode;
806 	}
807 	
808 	
809 	package final void setTreeView(TreeView treeView)
810 	{
811 		tview = treeView;
812 		foreach(TreeNode node; _nodes)
813 		{
814 			node.tchildren.setTreeView(treeView);
815 		}
816 	}
817 	
818 	
819 	package final @property bool created() // getter
820 	{
821 		return tview && tview.created();
822 	}
823 	
824 	
825 	package void populateInsertChildNode(ref Message m, ref TV_ITEMA dest, TreeNode node)
826 	{
827 		with(dest)
828 		{
829 			mask = /+ TVIF_CHILDREN | +/ TVIF_PARAM | TVIF_TEXT;
830 			version(DFL_NO_IMAGELIST)
831 			{
832 			}
833 			else
834 			{
835 				mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE;
836 				iImage = node._imgidx;
837 				if(tview._selimgidx < 0)
838 					iSelectedImage = node._imgidx;
839 				else
840 					iSelectedImage = tview._selimgidx;
841 			}
842 			/+ cChildren = I_CHILDRENCALLBACK; +/
843 			lParam = cast(LPARAM)cast(void*)node;
844 			/+
845 			pszText = stringToStringz(node.text);
846 			//cchTextMax = node.text.length; // ?
847 			+/
848 			if(dfl.internal.utf.useUnicode)
849 			{
850 				pszText = cast(typeof(pszText))dfl.internal.utf.toUnicodez(node.text);
851 				m.hWnd = tview.handle;
852 				m.msg = TVM_INSERTITEMW;
853 			}
854 			else
855 			{
856 				pszText = cast(typeof(pszText))dfl.internal.utf.unsafeAnsiz(node.text);
857 				m.hWnd = tview.handle;
858 				m.msg = TVM_INSERTITEMA;
859 			}
860 		}
861 	}
862 	
863 	
864 	void doNodes()
865 	in
866 	{
867 		assert(created);
868 	}
869 	body
870 	{
871 		TV_INSERTSTRUCTA tis;
872 		Message m;
873 		
874 		tis.hInsertAfter = TVI_LAST;
875 		
876 		m.hWnd = tview.handle;
877 		m.wParam = 0;
878 		
879 		foreach(TreeNode node; _nodes)
880 		{
881 			assert(!node.handle);
882 			
883 			tis.hParent = tparent ? tparent.handle : TVI_ROOT;
884 			populateInsertChildNode(m, tis.item, node);
885 			
886 			m.lParam = cast(LPARAM)&tis;
887 			tview.prevWndProc(m);
888 			assert(m.result);
889 			node.hnode = cast(HTREEITEM)m.result;
890 			
891 			node.tchildren.doNodes();
892 		}
893 	}
894 	
895 	
896 	void _added(size_t idx, TreeNode val)
897 	{
898 		verifyNoParent(val);
899 		
900 		val.tparent = tparent;
901 		val.tview = tview;
902 		val.tchildren.setTreeView(tview);
903 		
904 		if(created)
905 		{
906 			TV_INSERTSTRUCTA tis;
907 			
908 			if(idx <= 0)
909 			{
910 				tis.hInsertAfter = TVI_FIRST;
911 			}
912 			else if(idx >= cast(int)_nodes.length)
913 			{
914 				tis.hInsertAfter = TVI_LAST;
915 			}
916 			else
917 			{
918 				tis.hInsertAfter = _nodes[idx - 1].handle;
919 			}
920 			
921 			tis.hParent = tparent ? tparent.handle : TVI_ROOT;
922 			assert(tis.hInsertAfter);
923 			
924 			Message m;
925 			m.wParam = 0;
926 			
927 			populateInsertChildNode(m, tis.item, val);
928 			
929 			m.lParam = cast(LPARAM)&tis;
930 			tview.prevWndProc(m);
931 			assert(m.result);
932 			val.hnode = cast(HTREEITEM)m.result;
933 			
934 			val.tchildren.doNodes();
935 			
936 			if(tparent)
937 				tview.invalidate(tparent.bounds);
938 		}
939 	}
940 	
941 	
942 	void _removing(size_t idx, TreeNode val)
943 	{
944 		if(size_t.max == idx) // Clearing all...
945 		{
946 			TreeNode[] nodes = _nodes;
947 			_nodes = _nodes[0 .. 0]; // Not nice to dfl.collections, but OK.
948 			if(created)
949 			{
950 				Message m;
951 				m.hWnd = tview.handle;
952 				m.msg = TVM_DELETEITEM;
953 				m.wParam = 0;
954 				if(tparent)
955 				{
956 					foreach(TreeNode node; nodes)
957 					{
958 						assert(node.handle !is null);
959 						m.lParam = cast(LPARAM)node.handle;
960 						tview.prevWndProc(m);
961 						
962 						node._reset();
963 					}
964 				}
965 				else
966 				{
967 					m.lParam =cast(LPARAM)TVI_ROOT;
968 					tview.prevWndProc(m);
969 					foreach(TreeNode node; nodes)
970 					{
971 						node._reset();
972 					}
973 				}
974 			}
975 		}
976 		else
977 		{
978 		}
979 	}
980 	
981 	
982 	void _removed(size_t idx, TreeNode val)
983 	{
984 		if(size_t.max == idx) // Clear all.
985 		{
986 		}
987 		else
988 		{
989 			if(created)
990 			{
991 				assert(val.hnode);
992 				Message m;
993 				m = Message(tview.handle, TVM_DELETEITEM, 0, cast(LPARAM)&val.hnode);//
994 				tview.prevWndProc(m);
995 			}
996 			
997 			// Clear children.
998 			val._reset();
999 		}
1000 	}
1001 	
1002 	
1003 	public:
1004 	
1005 	mixin ListWrapArray!(TreeNode, _nodes,
1006 		_blankListCallback!(TreeNode), _added,
1007 		_removing, _removed,
1008 		true, /+true+/ false, false) _wraparray;
1009 }
1010 
1011 
1012 ///
1013 class TreeView: ControlSuperClass // docmain
1014 {
1015 	this()
1016 	{
1017 		_initTreeview();
1018 		
1019 		wstyle |= WS_TABSTOP | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_HASLINES;
1020 		wexstyle |= WS_EX_CLIENTEDGE;
1021 		ctrlStyle |= ControlStyles.SELECTABLE;
1022 		wclassStyle = treeviewClassStyle;
1023 		
1024 		tchildren = new TreeNodeCollection(this, null);
1025 	}
1026 	
1027 	
1028 	/+
1029 	~this()
1030 	{
1031 		/+
1032 		if(tchildren)
1033 			tchildren._dtorReset();
1034 		+/
1035 	}
1036 	+/
1037 	
1038 	
1039 	static @property Color defaultBackColor() // getter
1040 	{
1041 		return SystemColors.window;
1042 	}
1043 	
1044 	
1045 	override @property Color backColor() // getter
1046 	{
1047 		if(Color.empty == backc)
1048 			return defaultBackColor;
1049 		return backc;
1050 	}
1051 	
1052 	
1053 	override @property void backColor(Color b) // setter
1054 	{
1055 		super.backColor = b;
1056 		
1057 		if(created)
1058 		{
1059 			// For some reason the left edge isn't showing the new color.
1060 			// This causes the entire control to be redrawn with the new color.
1061 			// Sets the same font.
1062 			prevwproc(WM_SETFONT, this.font ? cast(WPARAM)this.font.handle : 0, MAKELPARAM(TRUE, 0));
1063 		}
1064 	}
1065 	
1066 	
1067 	static @property Color defaultForeColor() //getter
1068 	{
1069 		return SystemColors.windowText;
1070 	}
1071 	
1072 	
1073 	override @property Color foreColor() // getter
1074 	{
1075 		if(Color.empty == forec)
1076 			return defaultForeColor;
1077 		return forec;
1078 	}
1079 	
1080 	alias Control.foreColor foreColor; // Overload.
1081 	
1082 	
1083 	final @property void borderStyle(BorderStyle bs) // setter
1084 	{
1085 		final switch(bs)
1086 		{
1087 			case BorderStyle.FIXED_3D:
1088 				_style(_style() & ~WS_BORDER);
1089 				_exStyle(_exStyle() | WS_EX_CLIENTEDGE);
1090 				break;
1091 				
1092 			case BorderStyle.FIXED_SINGLE:
1093 				_exStyle(_exStyle() & ~WS_EX_CLIENTEDGE);
1094 				_style(_style() | WS_BORDER);
1095 				break;
1096 				
1097 			case BorderStyle.NONE:
1098 				_style(_style() & ~WS_BORDER);
1099 				_exStyle(_exStyle() & ~WS_EX_CLIENTEDGE);
1100 				break;
1101 		}
1102 		
1103 		if(created)
1104 		{
1105 			redrawEntire();
1106 		}
1107 	}
1108 	
1109 	
1110 	final @property BorderStyle borderStyle() // getter
1111 	{
1112 		if(_exStyle() & WS_EX_CLIENTEDGE)
1113 			return BorderStyle.FIXED_3D;
1114 		else if(_style() & WS_BORDER)
1115 			return BorderStyle.FIXED_SINGLE;
1116 		return BorderStyle.NONE;
1117 	}
1118 	
1119 	
1120 	/+
1121 	///
1122 	final @property void checkBoxes(bool byes) // setter
1123 	{
1124 		if(byes)
1125 			_style(_style() | TVS_CHECKBOXES);
1126 		else
1127 			_style(_style() & ~TVS_CHECKBOXES);
1128 		
1129 		_crecreate();
1130 	}
1131 	
1132 	/// ditto
1133 	final @property bool checkBoxes() // getter
1134 	{
1135 		return (_style() & TVS_CHECKBOXES) != 0;
1136 	}
1137 	+/
1138 	
1139 	
1140 	///
1141 	final @property void fullRowSelect(bool byes) // setter
1142 	{
1143 		if(byes)
1144 			_style(_style() | TVS_FULLROWSELECT);
1145 		else
1146 			_style(_style() & ~TVS_FULLROWSELECT);
1147 		
1148 		_crecreate(); // ?
1149 	}
1150 	
1151 	/// ditto
1152 	final @property bool fullRowSelect() // getter
1153 	{
1154 		return (_style() & TVS_FULLROWSELECT) != 0;
1155 	}
1156 	
1157 	
1158 	///
1159 	final @property void hideSelection(bool byes) // setter
1160 	{
1161 		if(byes)
1162 			_style(_style() & ~TVS_SHOWSELALWAYS);
1163 		else
1164 			_style(_style() | TVS_SHOWSELALWAYS);
1165 	}
1166 	
1167 	/// ditto
1168 	final @property bool hideSelection() // getter
1169 	{
1170 		return (_style() & TVS_SHOWSELALWAYS) == 0;
1171 	}
1172 	
1173 	
1174 	deprecated alias hoverSelection hotTracking;
1175 	
1176 	///
1177 	final @property void hoverSelection(bool byes) // setter
1178 	{
1179 		if(byes)
1180 			_style(_style() | TVS_TRACKSELECT);
1181 		else
1182 			_style(_style() & ~TVS_TRACKSELECT);
1183 	}
1184 	
1185 	/// ditto
1186 	final @property bool hoverSelection() // getter
1187 	{
1188 		return (_style() & TVS_TRACKSELECT) != 0;
1189 	}
1190 	
1191 	
1192 	///
1193 	final @property void indent(int newIndent) // setter
1194 	{
1195 		if(newIndent < 0)
1196 			newIndent = 0;
1197 		else if(newIndent > 32_000)
1198 			newIndent = 32_000;
1199 		
1200 		ind = newIndent;
1201 		
1202 		if(created)
1203 			SendMessageA(hwnd, TVM_SETINDENT, ind, 0);
1204 	}
1205 	
1206 	/// ditto
1207 	final @property int indent() // getter
1208 	{
1209 		if(created)
1210 			ind = cast(int)SendMessageA(hwnd, TVM_GETINDENT, 0, 0);
1211 		return ind;
1212 	}
1213 	
1214 	
1215 	///
1216 	final @property void itemHeight(int h) // setter
1217 	{
1218 		if(h < 0)
1219 			h = 0;
1220 		
1221 		iheight = h;
1222 		
1223 		if(created)
1224 			SendMessageA(hwnd, TVM_SETITEMHEIGHT, iheight, 0);
1225 	}
1226 	
1227 	/// ditto
1228 	final @property int itemHeight() // getter
1229 	{
1230 		if(created)
1231 			iheight = cast(int)SendMessageA(hwnd, TVM_GETITEMHEIGHT, 0, 0);
1232 		return iheight;
1233 	}
1234 	
1235 	
1236 	///
1237 	final @property void labelEdit(bool byes) // setter
1238 	{
1239 		if(byes)
1240 			_style(_style() | TVS_EDITLABELS);
1241 		else
1242 			_style(_style() & ~TVS_EDITLABELS);
1243 	}
1244 	
1245 	/// ditto
1246 	final @property bool labelEdit() // getter
1247 	{
1248 		return (_style() & TVS_EDITLABELS) != 0;
1249 	}
1250 	
1251 	
1252 	///
1253 	final @property TreeNodeCollection nodes() // getter
1254 	{
1255 		return tchildren;
1256 	}
1257 	
1258 	
1259 	///
1260 	final @property void pathSeparator(dchar sep) // setter
1261 	{
1262 		pathsep = sep;
1263 	}
1264 	
1265 	/// ditto
1266 	final @property dchar pathSeparator() // getter
1267 	{
1268 		return pathsep;
1269 	}
1270 	
1271 	
1272 	///
1273 	final @property void scrollable(bool byes) // setter
1274 	{
1275 		if(byes)
1276 			_style(_style() & ~TVS_NOSCROLL);
1277 		else
1278 			_style(_style() | TVS_NOSCROLL);
1279 		
1280 		if(created)
1281 			redrawEntire();
1282 	}
1283 	
1284 	/// ditto
1285 	final @property bool scrollable() // getter
1286 	{
1287 		return (_style & TVS_NOSCROLL) == 0;
1288 	}
1289 	
1290 	
1291 	///
1292 	final @property void selectedNode(TreeNode node) // setter
1293 	{
1294 		if(created)
1295 		{
1296 			if(node)
1297 			{
1298 				SendMessageA(hwnd, TVM_SELECTITEM, TVGN_CARET, cast(LPARAM)node.handle);
1299 			}
1300 			else
1301 			{
1302 				// Should the selection be cleared if -node- is null?
1303 				//SendMessageA(hwnd, TVM_SELECTITEM, TVGN_CARET, cast(LPARAM)null);
1304 			}
1305 		}
1306 	}
1307 	
1308 	/// ditto
1309 	final @property TreeNode selectedNode() // getter
1310 	{
1311 		if(created)
1312 		{
1313 			HTREEITEM hnode;
1314 			hnode = cast(HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM, TVGN_CARET, cast(LPARAM)null);
1315 			if(hnode)
1316 				return treeNodeFromHandle(hnode);
1317 		}
1318 		return null;
1319 	}
1320 	
1321 	
1322 	///
1323 	final @property void showLines(bool byes) // setter
1324 	{
1325 		if(byes)
1326 			_style(_style() | TVS_HASLINES);
1327 		else
1328 			_style(_style() & ~TVS_HASLINES);
1329 		
1330 		_crecreate(); // ?
1331 	}
1332 	
1333 	/// ditto
1334 	final @property bool showLines() // getter
1335 	{
1336 		return (_style() & TVS_HASLINES) != 0;
1337 	}
1338 	
1339 	
1340 	///
1341 	final @property void showPlusMinus(bool byes) // setter
1342 	{
1343 		if(byes)
1344 			_style(_style() | TVS_HASBUTTONS);
1345 		else
1346 			_style(_style() & ~TVS_HASBUTTONS);
1347 		
1348 		_crecreate(); // ?
1349 	}
1350 	
1351 	/// ditto
1352 	final @property bool showPlusMinus() // getter
1353 	{
1354 		return (_style() & TVS_HASBUTTONS) != 0;
1355 	}
1356 	
1357 	
1358 	///
1359 	// -showPlusMinus- should be false.
1360 	final @property void singleExpand(bool byes) // setter
1361 	{
1362 		if(byes)
1363 			_style(_style() | TVS_SINGLEEXPAND);
1364 		else
1365 			_style(_style() & ~TVS_SINGLEEXPAND);
1366 		
1367 		_crecreate(); // ?
1368 	}
1369 	
1370 	/// ditto
1371 	final @property bool singleExpand() // getter
1372 	{
1373 		return (_style & TVS_SINGLEEXPAND) != 0;
1374 	}
1375 	
1376 	
1377 	///
1378 	final @property void showRootLines(bool byes) // setter
1379 	{
1380 		if(byes)
1381 			_style(_style() | TVS_LINESATROOT);
1382 		else
1383 			_style(_style() & ~TVS_LINESATROOT);
1384 		
1385 		_crecreate(); // ?
1386 	}
1387 	
1388 	/// ditto
1389 	final @property bool showRootLines() // getter
1390 	{
1391 		return (_style() & TVS_LINESATROOT) != 0;
1392 	}
1393 	
1394 	
1395 	///
1396 	final @property void sorted(bool byes) // setter
1397 	{
1398 		_sort = byes;
1399 	}
1400 	
1401 	/// ditto
1402 	final @property bool sorted() // getter
1403 	{
1404 		return _sort;
1405 	}
1406 	
1407 	
1408 	///
1409 	// First visible node, based on the scrolled position.
1410 	final @property TreeNode topNode() // getter
1411 	{
1412 		if(created)
1413 		{
1414 			HTREEITEM hnode;
1415 			hnode = cast(HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM,
1416 				TVGN_FIRSTVISIBLE, cast(LPARAM)null);
1417 			if(hnode)
1418 				return treeNodeFromHandle(hnode);
1419 		}
1420 		return null;
1421 	}
1422 	
1423 	
1424 	///
1425 	// Number of visible nodes, including partially visible.
1426 	final @property int visibleCount() // getter
1427 	{
1428 		if(!created)
1429 			return 0;
1430 		return cast(int)SendMessageA(hwnd, TVM_GETVISIBLECOUNT, 0, 0);
1431 	}
1432 	
1433 	
1434 	///
1435 	final void beginUpdate()
1436 	{
1437 		SendMessageA(handle, WM_SETREDRAW, false, 0);
1438 	}
1439 	
1440 	/// ditto
1441 	final void endUpdate()
1442 	{
1443 		SendMessageA(handle, WM_SETREDRAW, true, 0);
1444 		invalidate(true); // Show updates.
1445 	}
1446 	
1447 	
1448 	///
1449 	final void collapseAll()
1450 	{
1451 		if(created)
1452 		{
1453 			void collapsing(TreeNodeCollection tchildren)
1454 			{
1455 				foreach(TreeNode node; tchildren._nodes)
1456 				{
1457 					SendMessageA(hwnd, TVM_EXPAND, TVE_COLLAPSE, cast(LPARAM)node.hnode);
1458 					collapsing(node.tchildren);
1459 				}
1460 			}
1461 			
1462 			
1463 			collapsing(tchildren);
1464 		}
1465 	}
1466 	
1467 	
1468 	///
1469 	final void expandAll()
1470 	{
1471 		if(created)
1472 		{
1473 			void expanding(TreeNodeCollection tchildren)
1474 			{
1475 				foreach(TreeNode node; tchildren._nodes)
1476 				{
1477 					SendMessageA(hwnd, TVM_EXPAND, TVE_EXPAND, cast(LPARAM)node.hnode);
1478 					expanding(node.tchildren);
1479 				}
1480 			}
1481 			
1482 			
1483 			expanding(tchildren);
1484 		}
1485 	}
1486 	
1487 	
1488 	///
1489 	final TreeNode getNodeAt(int x, int y)
1490 	{
1491 		if(created)
1492 		{
1493 			TVHITTESTINFO thi;
1494 			HTREEITEM hti;
1495 			thi.pt.x = x;
1496 			thi.pt.y = y;
1497 			hti = cast(HTREEITEM)SendMessageA(hwnd, TVM_HITTEST, 0, cast(LPARAM)&thi);
1498 			if(hti)
1499 			{
1500 				TreeNode result;
1501 				result = treeNodeFromHandle(hti);
1502 				if(result)
1503 				{
1504 					assert(result.tview is this);
1505 					return result;
1506 				}
1507 			}
1508 		}
1509 		return null;
1510 	}
1511 	
1512 	/// ditto
1513 	final TreeNode getNodeAt(Point pt)
1514 	{
1515 		return getNodeAt(pt.x, pt.y);
1516 	}
1517 	
1518 	
1519 	/+
1520 	///
1521 	// TODO: finish.
1522 	final int getNodeCount(bool includeSubNodes)
1523 	{
1524 		int result;
1525 		result = tchildren.length();
1526 		
1527 		if(includeSubNodes)
1528 		{
1529 			// ...
1530 		}
1531 		
1532 		return result;
1533 	}
1534 	+/
1535 	
1536 	
1537 	version(DFL_NO_IMAGELIST)
1538 	{
1539 	}
1540 	else
1541 	{
1542 		///
1543 		final @property void imageList(ImageList imglist) // setter
1544 		{
1545 			if(isHandleCreated)
1546 			{
1547 				prevwproc(TVM_SETIMAGELIST, TVSIL_NORMAL,
1548 					cast(LPARAM)(imglist ? imglist.handle : cast(HIMAGELIST)null));
1549 			}
1550 			
1551 			_imglist = imglist;
1552 		}
1553 		
1554 		/// ditto
1555 		final @property ImageList imageList() // getter
1556 		{
1557 			return _imglist;
1558 		}
1559 		
1560 		
1561 		/+
1562 		///
1563 		// Default image index (if -1 use this).
1564 		final @property void imageIndex(int index) // setter
1565 		{
1566 			_defimgidx = index;
1567 		}
1568 		
1569 		/// ditto
1570 		final @property int imageIndex() // getter
1571 		{
1572 			return _defimgidx;
1573 		}
1574 		+/
1575 		
1576 		
1577 		///
1578 		final @property void selectedImageIndex(int index) // setter
1579 		{
1580 			//assert(index >= 0);
1581 			assert(index >= -1);
1582 			_selimgidx = index;
1583 			
1584 			if(isHandleCreated)
1585 			{
1586 				TreeNode curnode = selectedNode;
1587 				_crecreate();
1588 				if(curnode)
1589 					curnode.ensureVisible();
1590 			}
1591 		}
1592 		
1593 		/// ditto
1594 		final @property int selectedImageIndex() // getter
1595 		{
1596 			return _selimgidx;
1597 		}
1598 	}
1599 	
1600 	
1601 	protected override @property Size defaultSize() // getter
1602 	{
1603 		return Size(120, 100);
1604 	}
1605 	
1606 	
1607 	/+
1608 	override void createHandle()
1609 	{
1610 		if(isHandleCreated)
1611 			return;
1612 		
1613 		createClassHandle(TREEVIEW_CLASSNAME);
1614 		
1615 		onHandleCreated(EventArgs.empty);
1616 	}
1617 	+/
1618 	
1619 	
1620 	protected override void createParams(ref CreateParams cp)
1621 	{
1622 		super.createParams(cp);
1623 		
1624 		cp.className = TREEVIEW_CLASSNAME;
1625 	}
1626 	
1627 	
1628 	protected override void onHandleCreated(EventArgs ea)
1629 	{
1630 		super.onHandleCreated(ea);
1631 		
1632 		prevwproc(CCM_SETVERSION, 5, 0); // Fixes font size issue.
1633 		
1634 		prevwproc(TVM_SETINDENT, ind, 0);
1635 		
1636 		prevwproc(TVM_SETITEMHEIGHT, iheight, 0);
1637 		
1638 		version(DFL_NO_IMAGELIST)
1639 		{
1640 		}
1641 		else
1642 		{
1643 			if(_imglist)
1644 				prevwproc(TVM_SETIMAGELIST, TVSIL_NORMAL, cast(LPARAM)_imglist.handle);
1645 		}
1646 		
1647 		tchildren.doNodes();
1648 	}
1649 	
1650 	
1651 	protected override void onHandleDestroyed(EventArgs ea)
1652 	{
1653 		tchildren._resetHandles();
1654 		
1655 		super.onHandleDestroyed(ea);
1656 	}
1657 	
1658 	
1659 	protected override void wndProc(ref Message m)
1660 	{
1661 		// TODO: support these messages.
1662 		switch(m.msg)
1663 		{
1664 			case TVM_INSERTITEMA:
1665 			case TVM_INSERTITEMW:
1666 				m.result = cast(LRESULT)null;
1667 				return;
1668 			
1669 			case TVM_SETITEMA:
1670 			case TVM_SETITEMW:
1671 				m.result = cast(LRESULT)-1;
1672 				return;
1673 			
1674 			case TVM_DELETEITEM:
1675 				m.result = FALSE;
1676 				return;
1677 			
1678 			case TVM_SETIMAGELIST:
1679 				m.result = cast(LRESULT)null;
1680 				return;
1681 			
1682 			default:
1683 		}
1684 		
1685 		super.wndProc(m);
1686 	}
1687 	
1688 	
1689 	protected override void prevWndProc(ref Message msg)
1690 	{
1691 		//msg.result = CallWindowProcA(treeviewPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam);
1692 		msg.result = dfl.internal.utf.callWindowProc(treeviewPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam);
1693 	}
1694 	
1695 	
1696 	//TreeViewEventHandler afterCollapse;
1697 	Event!(TreeView, TreeViewEventArgs) afterCollapse; ///
1698 	//TreeViewEventHandler afterExpand;
1699 	Event!(TreeView, TreeViewEventArgs) afterExpand; ///
1700 	//TreeViewEventHandler afterSelect;
1701 	Event!(TreeView, TreeViewEventArgs) afterSelect; ///
1702 	//NodeLabelEditEventHandler afterLabelEdit;
1703 	Event!(TreeView, NodeLabelEditEventArgs) afterLabelEdit; ///
1704 	//TreeViewCancelEventHandler beforeCollapse;
1705 	Event!(TreeView, TreeViewCancelEventArgs) beforeCollapse; ///
1706 	//TreeViewCancelEventHandler beforeExpand;
1707 	Event!(TreeView, TreeViewCancelEventArgs) beforeExpand; ///
1708 	//TreeViewCancelEventHandler beforeSelect;
1709 	Event!(TreeView, TreeViewCancelEventArgs) beforeSelect; ///
1710 	//NodeLabelEditEventHandler beforeLabelEdit;
1711 	Event!(TreeView, NodeLabelEditEventArgs) beforeLabelEdit; ///
1712 	
1713 	
1714 	///
1715 	protected void onAfterCollapse(TreeViewEventArgs ea)
1716 	{
1717 		afterCollapse(this, ea);
1718 	}
1719 	
1720 	
1721 	///
1722 	protected void onAfterExpand(TreeViewEventArgs ea)
1723 	{
1724 		afterExpand(this, ea);
1725 	}
1726 	
1727 	
1728 	///
1729 	protected void onAfterSelect(TreeViewEventArgs ea)
1730 	{
1731 		afterSelect(this, ea);
1732 	}
1733 	
1734 	
1735 	///
1736 	protected void onAfterLabelEdit(NodeLabelEditEventArgs ea)
1737 	{
1738 		afterLabelEdit(this, ea);
1739 	}
1740 	
1741 	
1742 	///
1743 	protected void onBeforeCollapse(TreeViewCancelEventArgs ea)
1744 	{
1745 		beforeCollapse(this, ea);
1746 	}
1747 	
1748 	
1749 	///
1750 	protected void onBeforeExpand(TreeViewCancelEventArgs ea)
1751 	{
1752 		beforeExpand(this, ea);
1753 	}
1754 	
1755 	
1756 	///
1757 	protected void onBeforeSelect(TreeViewCancelEventArgs ea)
1758 	{
1759 		beforeSelect(this, ea);
1760 	}
1761 	
1762 	
1763 	///
1764 	protected void onBeforeLabelEdit(NodeLabelEditEventArgs ea)
1765 	{
1766 		beforeLabelEdit(this, ea);
1767 	}
1768 	
1769 	
1770 	protected override void onReflectedMessage(ref Message m) // package
1771 	{
1772 		super.onReflectedMessage(m);
1773 		
1774 		switch(m.msg)
1775 		{
1776 			case WM_NOTIFY:
1777 				{
1778 					NMHDR* nmh;
1779 					NM_TREEVIEW* nmtv;
1780 					TreeViewCancelEventArgs cea;
1781 					
1782 					nmh = cast(NMHDR*)m.lParam;
1783 					assert(nmh.hwndFrom == hwnd);
1784 					
1785 					switch(nmh.code)
1786 					{
1787 						case NM_CUSTOMDRAW:
1788 							{
1789 								NMTVCUSTOMDRAW* tvcd;
1790 								tvcd = cast(NMTVCUSTOMDRAW*)nmh;
1791 								//if(tvcd.nmcd.dwDrawStage & CDDS_ITEM)
1792 								{
1793 									//if(tvcd.nmcd.uItemState & CDIS_SELECTED)
1794 									if((tvcd.nmcd.dwDrawStage & CDDS_ITEM)
1795 										&& (tvcd.nmcd.uItemState & CDIS_SELECTED))
1796 									{
1797 										// Note: might not look good with custom colors.
1798 										tvcd.clrText = SystemColors.highlightText.toRgb();
1799 										tvcd.clrTextBk = SystemColors.highlight.toRgb();
1800 									}
1801 									else
1802 									{
1803 										//tvcd.clrText = foreColor.toRgb();
1804 										tvcd.clrText = foreColor.solidColor(backColor).toRgb();
1805 										tvcd.clrTextBk = backColor.toRgb();
1806 									}
1807 								}
1808 								m.result |= CDRF_NOTIFYITEMDRAW; // | CDRF_NOTIFYITEMERASE;
1809 								
1810 								// This doesn't seem to be doing anything.
1811 								Font fon;
1812 								fon = this.font;
1813 								if(fon)
1814 								{
1815 									SelectObject(tvcd.nmcd.hdc, fon.handle);
1816 									m.result |= CDRF_NEWFONT;
1817 								}
1818 							}
1819 							break;
1820 						
1821 						/+
1822 						case TVN_GETDISPINFOA:
1823 							
1824 							break;
1825 						+/
1826 						
1827 						case TVN_SELCHANGINGW:
1828 							goto sel_changing;
1829 						
1830 						case TVN_SELCHANGINGA:
1831 							if(dfl.internal.utf.useUnicode)
1832 								break;
1833 							sel_changing:
1834 							
1835 							nmtv = cast(NM_TREEVIEW*)nmh;
1836 							switch(nmtv.action)
1837 							{
1838 								case TVC_BYMOUSE:
1839 									cea = new TreeViewCancelEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam,
1840 										false, TreeViewAction.BY_MOUSE);
1841 									onBeforeSelect(cea);
1842 									m.result = cea.cancel;
1843 									break;
1844 								
1845 								case TVC_BYKEYBOARD:
1846 									cea = new TreeViewCancelEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam,
1847 										false, TreeViewAction.BY_KEYBOARD);
1848 									onBeforeSelect(cea);
1849 									m.result = cea.cancel;
1850 									break;
1851 								
1852 								//case TVC_UNKNOWN:
1853 								default:
1854 									cea = new TreeViewCancelEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam,
1855 										false, TreeViewAction.UNKNOWN);
1856 									onBeforeSelect(cea);
1857 									m.result = cea.cancel;
1858 							}
1859 							break;
1860 						
1861 						case TVN_SELCHANGEDW:
1862 							goto sel_changed;
1863 						
1864 						case TVN_SELCHANGEDA:
1865 							if(dfl.internal.utf.useUnicode)
1866 								break;
1867 							sel_changed:
1868 							
1869 							nmtv = cast(NM_TREEVIEW*)nmh;
1870 							switch(nmtv.action)
1871 							{
1872 								case TVC_BYMOUSE:
1873 									onAfterSelect(new TreeViewEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam,
1874 										TreeViewAction.BY_MOUSE));
1875 									break;
1876 								
1877 								case TVC_BYKEYBOARD:
1878 									onAfterSelect(new TreeViewEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam,
1879 										TreeViewAction.BY_KEYBOARD));
1880 									break;
1881 								
1882 								//case TVC_UNKNOWN:
1883 								default:
1884 									onAfterSelect(new TreeViewEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam,
1885 										TreeViewAction.UNKNOWN));
1886 							}
1887 							break;
1888 						
1889 						case TVN_ITEMEXPANDINGW:
1890 							goto item_expanding;
1891 						
1892 						case TVN_ITEMEXPANDINGA:
1893 							if(dfl.internal.utf.useUnicode)
1894 								break;
1895 							item_expanding:
1896 							
1897 							nmtv = cast(NM_TREEVIEW*)nmh;
1898 							switch(nmtv.action)
1899 							{
1900 								case TVE_COLLAPSE:
1901 									cea = new TreeViewCancelEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam,
1902 										false, TreeViewAction.COLLAPSE);
1903 									onBeforeCollapse(cea);
1904 									m.result = cea.cancel;
1905 									break;
1906 								
1907 								case TVE_EXPAND:
1908 									cea = new TreeViewCancelEventArgs(cast(TreeNode)cast(void*)nmtv.itemNew.lParam,
1909 										false, TreeViewAction.EXPAND);
1910 									onBeforeExpand(cea);
1911 									m.result = cea.cancel;
1912 									break;
1913 								
1914 								default:
1915 							}
1916 							break;
1917 						
1918 						case TVN_ITEMEXPANDEDW:
1919 							goto item_expanded;
1920 						
1921 						case TVN_ITEMEXPANDEDA:
1922 							if(dfl.internal.utf.useUnicode)
1923 								break;
1924 							item_expanded:
1925 							
1926 							nmtv = cast(NM_TREEVIEW*)nmh;
1927 							switch(nmtv.action)
1928 							{
1929 								case TVE_COLLAPSE:
1930 									{
1931 										scope TreeViewEventArgs tvea = new TreeViewEventArgs(
1932 											cast(TreeNode)cast(void*)nmtv.itemNew.lParam,
1933 											TreeViewAction.COLLAPSE);
1934 										onAfterCollapse(tvea);
1935 									}
1936 									break;
1937 								
1938 								case TVE_EXPAND:
1939 									{
1940 										scope TreeViewEventArgs tvea = new TreeViewEventArgs(
1941 											cast(TreeNode)cast(void*)nmtv.itemNew.lParam,
1942 											TreeViewAction.EXPAND);
1943 										onAfterExpand(tvea);
1944 									}
1945 									break;
1946 								
1947 								default:
1948 							}
1949 							break;
1950 						
1951 						case TVN_BEGINLABELEDITW:
1952 							goto begin_label_edit;
1953 						
1954 						case TVN_BEGINLABELEDITA:
1955 							if(dfl.internal.utf.useUnicode)
1956 								break;
1957 							begin_label_edit:
1958 							
1959 							{
1960 								TV_DISPINFOA* nmdi;
1961 								nmdi = cast(TV_DISPINFOA*)nmh;
1962 								TreeNode node;
1963 								node = cast(TreeNode)cast(void*)nmdi.item.lParam;
1964 								scope NodeLabelEditEventArgs nleea = new NodeLabelEditEventArgs(node);
1965 								onBeforeLabelEdit(nleea);
1966 								m.result = nleea.cancelEdit;
1967 							}
1968 							break;
1969 						
1970 						case TVN_ENDLABELEDITW:
1971 							{
1972 								Dstring label;
1973 								TV_DISPINFOW* nmdi;
1974 								nmdi = cast(TV_DISPINFOW*)nmh;
1975 								if(nmdi.item.pszText)
1976 								{
1977 									TreeNode node;
1978 									node = cast(TreeNode)cast(void*)nmdi.item.lParam;
1979 									label = fromUnicodez(nmdi.item.pszText);
1980 									scope NodeLabelEditEventArgs nleea = new NodeLabelEditEventArgs(node, label);
1981 									onAfterLabelEdit(nleea);
1982 									if(nleea.cancelEdit)
1983 									{
1984 										m.result = FALSE;
1985 									}
1986 									else
1987 									{
1988 										// TODO: check if correct implementation.
1989 										// Update the node's cached text..
1990 										node.ttext = label;
1991 										
1992 										m.result = TRUE;
1993 									}
1994 								}
1995 							}
1996 							break;
1997 						
1998 						case TVN_ENDLABELEDITA:
1999 							if(dfl.internal.utf.useUnicode)
2000 							{
2001 								break;
2002 							}
2003 							else
2004 							{
2005 								Dstring label;
2006 								TV_DISPINFOA* nmdi;
2007 								nmdi = cast(TV_DISPINFOA*)nmh;
2008 								if(nmdi.item.pszText)
2009 								{
2010 									TreeNode node;
2011 									node = cast(TreeNode)cast(void*)nmdi.item.lParam;
2012 									label = fromAnsiz(nmdi.item.pszText);
2013 									scope NodeLabelEditEventArgs nleea = new NodeLabelEditEventArgs(node, label);
2014 									onAfterLabelEdit(nleea);
2015 									if(nleea.cancelEdit)
2016 									{
2017 										m.result = FALSE;
2018 									}
2019 									else
2020 									{
2021 										// TODO: check if correct implementation.
2022 										// Update the node's cached text..
2023 										node.ttext = label;
2024 										
2025 										m.result = TRUE;
2026 									}
2027 								}
2028 								break;
2029 							}
2030 						
2031 						default:
2032 					}
2033 				}
2034 				break;
2035 			
2036 			default:
2037 		}
2038 	}
2039 	
2040 	
2041 	private:
2042 	TreeNodeCollection tchildren;
2043 	int ind = 19; // Indent.
2044 	dchar pathsep = '\\';
2045 	bool _sort = false;
2046 	int iheight = 16;
2047 	version(DFL_NO_IMAGELIST)
2048 	{
2049 	}
2050 	else
2051 	{
2052 		ImageList _imglist;
2053 		int _selimgidx = -1; //0;
2054 	}
2055 	
2056 	
2057 	TreeNode treeNodeFromHandle(HTREEITEM hnode)
2058 	{
2059 		TV_ITEMA ti;
2060 		ti.mask = TVIF_HANDLE | TVIF_PARAM;
2061 		ti.hItem = hnode;
2062 		if(SendMessageA(hwnd, TVM_GETITEMA, 0, cast(LPARAM)&ti))
2063 		{
2064 			return cast(TreeNode)cast(void*)ti.lParam;
2065 		}
2066 		return null;
2067 	}
2068 	
2069 	package:
2070 	final:
2071 	LRESULT prevwproc(UINT msg, WPARAM wparam, LPARAM lparam)
2072 	{
2073 		//return CallWindowProcA(treeviewPrevWndProc, hwnd, msg, wparam, lparam);
2074 		return dfl.internal.utf.callWindowProc(treeviewPrevWndProc, hwnd, msg, wparam, lparam);
2075 	}
2076 }
2077