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