1 /**
2  * Widget module.
3  *
4  * License:
5  *     MIT. See LICENSE for full details.
6  */
7 module tkd.widget.treeview;
8 
9 /**
10  * Imports.
11  */
12 import std.algorithm;
13 import std.array;
14 import std.regex;
15 import std.string;
16 import tkd.element.color;
17 import tkd.element.element;
18 import tkd.element.uielement;
19 import tkd.image.image;
20 import tkd.widget.anchorposition;
21 import tkd.widget.common.height;
22 import tkd.widget.common.padding;
23 import tkd.widget.common.xscrollcommand;
24 import tkd.widget.common.yscrollcommand;
25 import tkd.widget.widget;
26 
27 /**
28  * The treeview widget displays a hierarchical collection of items. Each item 
29  * has a textual label, an optional image, and an optional list of data values. 
30  *
31  * There are two varieties of columns. The first is the main tree view column 
32  * that is present all the time. The second are data columns that can be added 
33  * when needed.
34  *
35  * Each tree item has a list of tags, which can be used to associate event 
36  * bindings and control their appearance. Treeview widgets support horizontal 
37  * and vertical scrolling with the standard scroll commands.
38  *
39  * Example:
40  * ---
41  * auto treeView = new TreeView()
42  * 	.setHeading("Text")
43  * 	.addRow(new TreeViewRow(["row1"]))
44  * 	.addRow(new TreeViewRow(["row2"]))
45  * 	.pack();
46  * ---
47  *
48  * Common_Commands:
49  *     These are injected common commands that can also be used with this widget.
50  *     $(P
51  *         $(LINK2 ./common/height.html, Height) $(BR)
52  *         $(LINK2 ./common/padding.html, Padding) $(BR)
53  *         $(LINK2 ./common/xscrollcommand.html, XScrollCommand) $(BR)
54  *         $(LINK2 ./common/yscrollcommand.html, YScrollCommand) $(BR)
55  *     )
56  *
57  * Additional_Events:
58  *     Additional events that can also be bound to using the $(LINK2 ../element/uielement.html#UiElement.bind, bind) method.
59  *     $(P
60  *         <<PrevWindow>>,
61  *         <<TreeviewClose>>
62  *         <<TreeviewOpen>>
63  *         <<TreeviewSelect>>
64  *         <Alt-Key>,
65  *         <B1-Leave>,
66  *         <B1-Motion>,
67  *         <Button-1>,
68  *         <Button-4>,
69  *         <Button-5>,
70  *         <ButtonRelease-1>,
71  *         <Control-Button-1>,
72  *         <Double-Button-1>,
73  *         <Key-Down>,
74  *         <Key-F10>,
75  *         <Key-Left>,
76  *         <Key-Next>,
77  *         <Key-Prior>,
78  *         <Key-Return>,
79  *         <Key-Right>,
80  *         <Key-Tab>,
81  *         <Key-Up>,
82  *         <Key-space>,
83  *         <Leave>,
84  *         <Motion>,
85  *         <Shift-Button-1>,
86  *         <Shift-Button-4>,
87  *         <Shift-Button-5>,
88  *     )
89  *
90  * See_Also:
91  *     $(LINK2 ./widget.html, tkd.widget.widget)
92  */
93 class TreeView : Widget, IXScrollable!(TreeView), IYScrollable!(TreeView)
94 {
95 	/**
96 	 * An array containing all the columns.
97 	 */
98 	private TreeViewColumn[] _columns;
99 
100 	/**
101 	 * Construct the widget.
102 	 *
103 	 * Params:
104 	 *     parent = The parent of this widget.
105 	 *
106 	 * See_Also:
107 	 *     $(LINK2 ../element/uielement.html, tkd.element.uielement) $(BR)
108 	 */
109 	this(UiElement parent = null)
110 	{
111 		super(parent);
112 		this._elementId = "treeview";
113 
114 		this._tk.eval("ttk::treeview %s -selectmode browse", this.id);
115 
116 		// Add the treeview column to the column collection.
117 		this._columns ~= new TreeViewColumn();
118 		this._columns[0].init(this);
119 	}
120 
121 	/**
122 	 * Get the column identifiers of the passed data column indexes.
123 	 *
124 	 * Params:
125 	 *     indexex = The indexes of the data columns.
126 	 *
127 	 * Returns:
128 	 *     A string array containing the columns relating to the indexes.
129 	 */
130 	private string[] getDataColumnIdentifiers(int[] indexes)
131 	{
132 		string[] columns;
133 
134 		for (int x = 1; x < this._columns.length; x++)
135 		{
136 			if (indexes.canFind(x))
137 			{
138 				columns ~= this._columns[x].id;
139 			}
140 		}
141 
142 		return columns;
143 	}
144 
145 	/**
146 	 * Get all column identifiers.
147 	 *
148 	 * Returns:
149 	 *     A string array containing all column identifiers.
150 	 */
151 	private string[] getDataColumnIdentifiers()
152 	{
153 		string[] columns;
154 
155 		for (int x = 1; x < this._columns.length; x++)
156 		{
157 			columns ~= this._columns[x].id;
158 		}
159 
160 		return columns;
161 	}
162 
163 	/**
164 	 * Get the tree view elements that are currently being shown.
165 	 *
166 	 * Returns:
167 	 *     An array cotaining all shown elements.
168 	 */
169 	private string[] getShownElements()
170 	{
171 		this._tk.eval("%s cget -show", this.id);
172 		return this._tk.getResult!(string).split();
173 	}
174 
175 	/**
176 	 * Build the columns found in the column array. This is needed because the 
177 	 * data columns always seem to forget setting if configured piece-meal.
178 	 */
179 	private void buildColumns()
180 	{
181 		this._tk.eval("%s configure -columns { \"%s\" }", this.id, this.getDataColumnIdentifiers().join("\" \""));
182 
183 		for (int x = 1; x < this._columns.length; x++)
184 		{
185 			this.columns[x].init(this);
186 		}
187 	}
188 
189 	/**
190 	 * Convenience method to set the tree column heading text.
191 	 *
192 	 * Params:
193 	 *    title = The title of the column.
194 	 *    anchor = The anchor position of the text.
195 	 *
196 	 * Returns:
197 	 *     This widget to aid method chaining.
198 	 *
199 	 * See_Also:
200 	 *     $(LINK2 ./anchorposition.html, tkd.widget.anchorposition) $(BR)
201 	 */
202 	public auto setHeading(this T)(string title, string anchor = AnchorPosition.west)
203 	{
204 		this._columns[0].setHeading(title, anchor);
205 
206 		return cast(T) this;
207 	}
208 
209 	/**
210 	 * Convenience method to set the tree column heading image.
211 	 *
212 	 * Params:
213 	 *    image = The image to use.
214 	 *
215 	 * Returns:
216 	 *     This widget to aid method chaining.
217 	 */
218 	public auto setHeadingImage(this T)(Image image)
219 	{
220 		this._columns[0].setHeadingImage(image);
221 
222 		return cast(T) this;
223 	}
224 
225 	/**
226 	 * Convenience method to set the tree column command to be executed when 
227 	 * clicking on the heading.
228 	 *
229 	 * Params:
230 	 *    callback = The delegate callback to execute when invoking the command.
231 	 *
232 	 * Returns:
233 	 *     This widget to aid method chaining.
234 	 *
235 	 * Callback_Arguments:
236 	 *     These are the fields within the callback's $(LINK2 
237 	 *     ../element/element.html#CommandArgs, CommandArgs) parameter which 
238 	 *     are populated by this method when the callback is executed. 
239 	 *     $(P
240 	 *         $(PARAM_TABLE
241 	 *             $(PARAM_ROW CommandArgs.element, The tree column.)
242 	 *             $(PARAM_ROW CommandArgs.uniqueData, The internal id of the tree view (An implementation detail that's not very useful).)
243 	 *             $(PARAM_ROW CommandArgs.callback, The callback which was executed.)
244 	 *         )
245 	 *     )
246 	 *
247 	 * See_Also:
248 	 *     $(LINK2 ../element/element.html#CommandCallback, tkd.element.element.CommandCallback)
249 	 */
250 	public auto setHeadingCommand(this T)(CommandCallback callback)
251 	{
252 		this._columns[0].setHeadingCommand(callback);
253 
254 		return cast(T) this;
255 	}
256 
257 	/**
258 	 * Convenience method to remove the tree view column command.
259 	 *
260 	 * Returns:
261 	 *     This widget to aid method chaining.
262 	 */
263 	public auto removeHeadingCommand(this T)()
264 	{
265 		this._columns[0].removeHeadingCommand();
266 
267 		return cast(T) this;
268 	}
269 
270 	/**
271 	 * Convenience method to set the minium width of the tree column.
272 	 *
273 	 * Params:
274 	 *     minWidth = The minimum width in pixels.
275 	 *
276 	 * Returns:
277 	 *     This widget to aid method chaining.
278 	 */
279 	public auto setMinWidth(this T)(int minWidth)
280 	{
281 		this._columns[0].setMinWidth(minWidth);
282 
283 		return cast(T) this;
284 	}
285 
286 	/**
287 	 * Convenience method to enable or disable stretching for the tree column. 
288 	 * This controls how this column react when other columns or the parent 
289 	 * widget is resized.
290 	 *
291 	 * Params:
292 	 *     stretch = true for enabling stretching, false to disable.
293 	 *
294 	 * Returns:
295 	 *     This widget to aid method chaining.
296 	 */
297 	public auto setStretch(this T)(bool stretch)
298 	{
299 		this._columns[0].setStretch(stretch);
300 
301 		return cast(T) this;
302 	}
303 
304 	/**
305 	 * Convenience method to set the width of the tree column.
306 	 *
307 	 * Params:
308 	 *     width = The width in pixels.
309 	 *
310 	 * Returns:
311 	 *     This widget to aid method chaining.
312 	 */
313 	public auto setWidth(this T)(int width)
314 	{
315 		this._columns[0].setWidth(width);
316 
317 		return cast(T) this;
318 	}
319 
320 	/**
321 	 * Add a new column to the tree view.
322 	 *
323 	 * Params:
324 	 *     column = The new column to add.
325 	 *
326 	 * Returns:
327 	 *     This widget to aid method chaining.
328 	 */
329 	public auto addColumn(this T)(TreeViewColumn column)
330 	{
331 		this._columns ~= column;
332 		this.buildColumns();
333 
334 		return cast(T) this;
335 	}
336 
337 	/**
338 	 * Add a row to the tree view.
339 	 *
340 	 * Params:
341 	 *     row = The row to add.
342 	 *
343 	 * Returns:
344 	 *     This widget to aid method chaining.
345 	 */
346 	public auto addRow(this T)(TreeViewRow row)
347 	{
348 		this.appendRows("", [row]);
349 
350 		return cast(T) this;
351 	}
352 
353 	/**
354 	 * Add an array of rowr to the tree view.
355 	 *
356 	 * Params:
357 	 *     rows = The rows to add.
358 	 *
359 	 * Returns:
360 	 *     This widget to aid method chaining.
361 	 */
362 	public auto addRows(this T)(TreeViewRow[] rows)
363 	{
364 		this.appendRows("", rows);
365 
366 		return cast(T) this;
367 	}
368 
369 	/**
370 	 * This method does the actualy work of adding rows to the tree view.
371 	 * All children are recursed and add too.
372 	 *
373 	 * Params:
374 	 *     parentRow = The id of the parent row. Use '{}' for the top level.
375 	 *     rows = The rows to add to the tree view.
376 	 */
377 	private void appendRows(string parentRow, TreeViewRow[] rows)
378 	{
379 		string dataValues;
380 		string tags;
381 
382 		foreach (row; rows)
383 		{
384 			if (row.values.length > 1)
385 			{
386 				dataValues = format("\"%s\"", row.values[1 .. $].join("\" \""));
387 			}
388 
389 			if (row.tags.length)
390 			{
391 				tags = format("\"%s\"", row.tags.join("\" \""));
392 			}
393 
394 			this._tk.eval("%s insert {%s} end -text {%s} -values {%s} -open %s -tags {%s}", this.id, parentRow, row.values[0], dataValues, row.isOpen, tags);
395 
396 			row._rowId = this._tk.getResult!(string);
397 
398 			if (row.children.length)
399 			{
400 				this.appendRows(row.id, row.children);
401 			}
402 		}
403 	}
404 
405 	/**
406 	 * Set the value of a data column, given a row id. Row id's are populated 
407 	 * within a tree view row object once that row has been inserted into the 
408 	 * tree view.
409 	 *
410 	 * Params:
411 	 *     rowId = The row id.
412 	 *     columnIndex = The 0-based data column index.
413 	 *     value = The new value.
414 	 *
415 	 * Returns:
416 	 *     This widget to aid method chaining.
417 	 */
418 	public auto updateDataColumn(this T)(string rowId, uint columnIndex, string value)
419 	{
420 		this._tk.eval("%s set %s %s {%s}", this.id, rowId, columnIndex, value);
421 
422 		return cast(T) this;
423 	}
424 
425 	/**
426 	 * Set image and colors for a specific tag.
427 	 * Use colors from the preset color $(LINK2 ../element/color.html, list) or a web style hex color.
428 	 *
429 	 * Params:
430 	 *     name = The name of the tag.
431 	 *     image = The image to associate to the tag.
432 	 *     foreground = The forground color.
433 	 *     background = The background color.
434 	 *
435 	 * Returns:
436 	 *     This widget to aid method chaining.
437 	 *
438 	 * See_Also:
439 	 *     $(LINK2 ../element/color.html, tkd.widget.color) $(BR)
440 	 */
441 	public auto setTag(this T)(string name, Image image, string foreground = Color.default_, string background = Color.default_)
442 	{
443 		this._tk.eval("%s tag configure %s -image %s -foreground {%s} -background {%s}", this.id, name, image.id, foreground, background);
444 
445 		return cast(T) this;
446 	}
447 
448 	/**
449 	 * Get the columns.
450 	 *
451 	 * Returns:
452 	 *     An array containing all the data columns.
453 	 */
454 	public @property TreeViewColumn[] columns()
455 	{
456 		return this._columns;
457 	}
458 
459 	/**
460 	 * Show all data columns in the event some or all are hidden.
461 	 *
462 	 * Returns:
463 	 *     This widget to aid method chaining.
464 	 */
465 	public auto displayAllDataColumns(this T)()
466 	{
467 		this._tk.eval("%s configure -displaycolumns #all", this.id);
468 
469 		return cast(T) this;
470 	}
471 
472 	/**
473 	 * Show the data columns that relate to the indexes passed.
474 	 *
475 	 * Params:
476 	 *     indexes = The indexes of the data columns to show.
477 	 *
478 	 * Returns:
479 	 *     This widget to aid method chaining.
480 	 */
481 	public auto displayDataColumns(this T)(int[] indexes)
482 	{
483 		this._tk.eval("%s configure -displaycolumns {\"%s\"}", this.id, this.getDataColumnIdentifiers(indexes).join("\" \""));
484 
485 		return cast(T) this;
486 	}
487 
488 	/**
489 	 * Set the selection mode.
490 	 *
491 	 * Params:
492 	 *     mode = The mode of the selection.
493 	 *
494 	 * Returns:
495 	 *     This widget to aid method chaining.
496 	 *
497 	 * See_Also:
498 	 *     $(LINK2 ./treeview.html#TreeViewSelectionMode, tkd.widget.treeview.TreeViewSelectionMode) $(BR)
499 	 */
500 	public auto setSelectionMode(this T)(string mode)
501 	{
502 		this._tk.eval("%s configure -selectmode %s", this.id, mode);
503 
504 		return cast(T) this;
505 	}
506 
507 	/**
508 	 * Hide the headings from all columns.
509 	 *
510 	 * Returns:
511 	 *     This widget to aid method chaining.
512 	 */
513 	public auto hideHeadings(this T)()
514 	{
515 		this._tk.eval("%s configure -show { %s }", this.id, this.getShownElements()
516 			.remove!(x => x == "headings")
517 			.join(" ")
518 		);
519 
520 		return cast(T) this;
521 	}
522 
523 	/**
524 	 * Show the headings for all columns.
525 	 *
526 	 * Returns:
527 	 *     This widget to aid method chaining.
528 	 */
529 	public auto showHeadings(this T)()
530 	{
531 		string[] elements = this.getShownElements();
532 		elements ~= "headings";
533 
534 		this._tk.eval("%s configure -show { %s }", this.id, elements.join(" "));
535 
536 		return cast(T) this;
537 	}
538 
539 	/**
540 	 * Hide the tree view column.
541 	 *
542 	 * Returns:
543 	 *     This widget to aid method chaining.
544 	 */
545 	public auto hideTreeColumn(this T)()
546 	{
547 		this._tk.eval("%s configure -show { %s }", this.id, this.getShownElements()
548 			.remove!(x => x == "tree")
549 			.join(" ")
550 		);
551 
552 		return cast(T) this;
553 	}
554 
555 	/**
556 	 * Show the tree view column.
557 	 *
558 	 * Returns:
559 	 *     This widget to aid method chaining.
560 	 */
561 	public auto showTreeColumn(this T)()
562 	{
563 		string[] elements = this.getShownElements();
564 		elements ~= "tree";
565 
566 		this._tk.eval("%s configure -show { %s }", this.id, elements.join(" "));
567 
568 		return cast(T) this;
569 	}
570 
571 	/**
572 	 * Construct a row object from a row id.
573 	 *
574 	 * Params:
575 	 *     rowId = The id of the row to construct.
576 	 *
577 	 * Returns:
578 	 *     A tree view row.
579 	 */
580 	private TreeViewRow getRowFromId(string rowId)
581 	{
582 		auto row = new TreeViewRow();
583 
584 		this._tk.eval("%s item %s -text", this.id, rowId);
585 		row._values ~= this._tk.getResult!(string);
586 
587 		this._tk.eval("%s item %s -values", this.id, rowId);
588 		auto results = matchAll(this._tk.getResult!(string), "\"(.*?)\"");
589 		foreach (result; results)
590 		{
591 			row._values ~= result.captures[1];
592 		}
593 
594 		this._tk.eval("%s item %s -open", this.id, rowId);
595 		row._isOpen = this._tk.getResult!(bool);
596 
597 		this._tk.eval("%s item %s -tags", this.id, rowId);
598 		results = matchAll(this._tk.getResult!(string), "\"(.*?)\"");
599 		foreach (result; results)
600 		{
601 			row._tags ~= result.captures[1];
602 		}
603 
604 		return row;
605 	}
606 
607 	/**
608 	 * Populate row objects and return them.
609 	 *
610 	 * Params:
611 	 *     rows = An array to append the rows to.
612 	 *     includeChildren = Specifies whether or not to include the children.
613 	 *
614 	 * Returns:
615 	 *     AN array of tree view rows.
616 	 */
617 	private TreeViewRow[] populateRows(string[] rowIds, bool includeChildren)
618 	{
619 		TreeViewRow[] rows;
620 		TreeViewRow currentRow;
621 
622 		foreach (rowId; rowIds)
623 		{
624 			currentRow = this.getRowFromId(rowId);
625 
626 			if (includeChildren)
627 			{
628 				this._tk.eval("%s children %s", this.id, rowId);
629 				currentRow.children = this.populateRows(this._tk.getResult!(string).split(), includeChildren);
630 			}
631 
632 			rows ~= currentRow;
633 		}
634 
635 		return rows;
636 	}
637 
638 	/**
639 	 * Get the row(s) selected in the tree view.
640 	 *
641 	 * Params:
642 	 *     includeChildren = Specifies whether or not to include the children.
643 	 *
644 	 * Returns:
645 	 *     An array containing the selected rows.
646 	 */
647 	public TreeViewRow[] getSelectedRows(bool includeChildren = false)
648 	{
649 		this._tk.eval("%s selection", this.id);
650 		string[] rowIds = this._tk.getResult!(string).split();
651 
652 		return this.populateRows(rowIds, includeChildren);
653 	}
654 
655 	/**
656 	 * Delete all rows in the widget.
657 	 *
658 	 * Returns:
659 	 *     This widget to aid method chaining.
660 	 */
661 	public auto deleteRows()
662 	{
663 		this._tk.eval("%s children {}", this.id);
664 		this._tk.eval("%s delete {%s}", this.id, this._tk.getResult!(string));
665 	}
666 
667 	/**
668 	 * Mixin common commands.
669 	 */
670 	mixin Height;
671 	mixin Padding;
672 	mixin XScrollCommand!(TreeView);
673 	mixin YScrollCommand!(TreeView);
674 }
675 
676 /**
677  * A class representing a column in the tree view.
678  */
679 class TreeViewColumn : Element
680 {
681 	/**
682 	 * The parent of this column.
683 	 */
684 	private TreeView _parent;
685 
686 	/**
687 	 * The title of the heading.
688 	 */
689 	private string _title;
690 
691 	/**
692 	 * The anchor position of the heading title.
693 	 */
694 	private string _anchor = AnchorPosition.west;
695 
696 	/**
697 	 * The image of the heading.
698 	 */
699 	private Image _image;
700 
701 	/**
702 	 * The minimum width of the column.
703 	 */
704 	private int _minWidth = 20;
705 
706 	/**
707 	 * Whether to alter the size of the column when the widget is resized.
708 	 */
709 	private bool _stretch = true;
710 
711 	/**
712 	 * Width of the column.
713 	 */
714 	private int _width = 200;
715 
716 	/**
717 	 * The command associated with the heading.
718 	 */
719 	private CommandCallback _commandCallback;
720 
721 	/**
722 	 * Construct a new column to add the treeview column to the column 
723 	 * collection.
724 	 *
725 	 * '#0' is the built-in Tcl/Tk display id of the tree view column.
726 	 * This allows access to this column even if it wasn't created by us.
727 	 */
728 	private this()
729 	{
730 		super();
731 		this.overrideGeneratedId("#0");
732 	}
733 
734 	/**
735 	 * Construct a new column.
736 	 *
737 	 * Params:
738 	 *     title = The optional title of the heading.
739 	 *     anchor = The anchor position of the heading title.
740 	 */
741 	public this(string title = null, string anchor = AnchorPosition.west)
742 	{
743 		this._elementId = "column";
744 		this.setHeading(title, anchor);
745 	}
746 
747 	/**
748 	 * Initialise the column and attach to a parent.
749 	 *
750 	 * Params:
751 	 *     parent = The parent tree view.
752 	 */
753 	private void init(TreeView parent)
754 	{
755 		this._parent = parent;
756 
757 		this.setHeading(this._title, this._anchor);
758 		this.setHeadingImage(this._image);
759 		this.setHeadingCommand(this._commandCallback);
760 		this.setMinWidth(this._minWidth);
761 		this.setStretch(this._stretch);
762 		this.setWidth(this._width);
763 	}
764 
765 	/**
766 	 * Set the heading title.
767 	 *
768 	 * Params:
769 	 *    title = The title of the column.
770 	 *    anchor = The anchor position of the text.
771 	 *
772 	 * Returns:
773 	 *     This column to aid method chaining.
774 	 *
775 	 * See_Also:
776 	 *     $(LINK2 ./anchorposition.html, tkd.widget.anchorposition) $(BR)
777 	 */
778 	public auto setHeading(this T)(string title, string anchor = AnchorPosition.west)
779 	{
780 		this._title  = title;
781 		this._anchor = anchor;
782 
783 		if (this._parent)
784 		{
785 			this._tk.eval("%s heading %s -text {%s} -anchor %s", this._parent.id, this.id, this._title, this._anchor);
786 		}
787 
788 		return cast(T) this;
789 	}
790 
791 	/**
792 	 * Set the heading image.
793 	 *
794 	 * Params:
795 	 *    image = The image to use.
796 	 *
797 	 * Returns:
798 	 *     This column to aid method chaining.
799 	 */
800 	public auto setHeadingImage(this T)(Image image)
801 	{
802 		this._image = image;
803 
804 		if (this._parent && this._image)
805 		{
806 			this._tk.eval("%s heading %s -text {%s} -anchor %s -image %s", this._parent.id, this.id, this._title, this._anchor, image.id);
807 		}
808 
809 		return cast(T) this;
810 	}
811 
812 	/**
813 	 * Set the column command to be executed when clicking on the heading.
814 	 *
815 	 * Params:
816 	 *    callback = The delegate callback to execute when invoking the command.
817 	 *
818 	 * Returns:
819 	 *     This widget to aid method chaining.
820 	 *
821 	 * Callback_Arguments:
822 	 *     These are the fields within the callback's $(LINK2 
823 	 *     ../element/element.html#CommandArgs, CommandArgs) parameter which 
824 	 *     are populated by this method when the callback is executed. 
825 	 *     $(P
826 	 *         $(PARAM_TABLE
827 	 *             $(PARAM_ROW CommandArgs.element, The column that executed the callback.)
828 	 *             $(PARAM_ROW CommandArgs.uniqueData, The internal id of the tree view (An implementation detail that's not very useful).)
829 	 *             $(PARAM_ROW CommandArgs.callback, The callback which was executed.)
830 	 *         )
831 	 *     )
832 	 *
833 	 * See_Also:
834 	 *     $(LINK2 ../element/element.html#CommandCallback, tkd.element.element.CommandCallback)
835 	 */
836 	public auto setHeadingCommand(this T)(CommandCallback callback)
837 	{
838 		this._commandCallback = callback;
839 
840 		if (this._parent && this._commandCallback)
841 		{
842 			string command = this.createCommand(callback, this._parent.id);
843 			this._tk.eval("%s heading %s -command %s", this._parent.id, this.id, command);
844 		}
845 
846 		return cast(T) this;
847 	}
848 
849 	/**
850 	 * Remove the column command.
851 	 *
852 	 * Returns:
853 	 *     This widget to aid method chaining.
854 	 */
855 	public auto removeHeadingCommand(this T)()
856 	{
857 		if (this._parent && this._commandCallback)
858 		{
859 			this._tk.deleteCommand(this.getCommandName(this._parent.id));
860 			this._tk.eval("%s heading %s -command {}", this._parent.id, this.id);
861 		}
862 
863 		return cast(T) this;
864 	}
865 
866 	/**
867 	 * Set the minium width of the column.
868 	 *
869 	 * Params:
870 	 *     minWidth = The minimum width in pixels.
871 	 *
872 	 * Returns:
873 	 *     This widget to aid method chaining.
874 	 */
875 	public auto setMinWidth(this T)(int minWidth)
876 	{
877 		this._minWidth = minWidth;
878 
879 		if (this._parent)
880 		{
881 			this._tk.eval("%s column %s -minwidth %s", this._parent.id, this.id, this._minWidth);
882 		}
883 
884 		return cast(T) this;
885 	}
886 
887 	/**
888 	 * Enable or disable stretching for the column. This controls how this 
889 	 * column react when other columns or the parent widget is resized.
890 	 *
891 	 * Params:
892 	 *     stretch = true for enabling stretching, false to disable.
893 	 *
894 	 * Returns:
895 	 *     This widget to aid method chaining.
896 	 */
897 	public auto setStretch(this T)(bool stretch)
898 	{
899 		this._stretch = stretch;
900 
901 		if (this._parent)
902 		{
903 			this._tk.eval("%s column %s -stretch %s", this._parent.id, this.id, this._stretch);
904 		}
905 
906 		return cast(T) this;
907 	}
908 
909 	/**
910 	 * Set the width of the column.
911 	 *
912 	 * Params:
913 	 *     width = The width in pixels.
914 	 *
915 	 * Returns:
916 	 *     This widget to aid method chaining.
917 	 */
918 	public auto setWidth(this T)(int width)
919 	{
920 		this._width = width;
921 
922 		if (this._parent)
923 		{
924 			this._tk.eval("%s column %s -width %s", this._parent.id, this.id, this._width);
925 		}
926 
927 		return cast(T) this;
928 	}
929 }
930 
931 /**
932  * A class representing a row in the tree view.
933  */
934 class TreeViewRow
935 {
936 	/**
937 	 * The row id. This is populated by the treeview once the row has been 
938 	 * inserted.
939 	 */
940 	private string _rowId;
941 
942 	/**
943 	 * An array containing the column values.
944 	 */
945 	private string[] _values;
946 
947 	/**
948 	 * Boolean representing if the row was set to be open when created.
949 	 */
950 	private bool _isOpen;
951 
952 	/**
953 	 * An array containing the tags.
954 	 */
955 	private string[] _tags;
956 
957 	/**
958 	 * An array containing the child rows.
959 	 */
960 	public TreeViewRow[] children;
961 
962 	/**
963 	 * Constructor.
964 	 */
965 	private this()
966 	{
967 	}
968 
969 	/**
970 	 * Constructor.
971 	 *
972 	 * Params:
973 	 *     values = The values of the columns.
974 	 *     isOpen = Whether or not to display the row open.
975 	 *     tags = The tags to associate to this row.
976 	 */
977 	public this(string[] values, bool isOpen = false, string[] tags = [])
978 	{
979 		assert(values.length, "There must be at least 1 value in the row.");
980 
981 		this._values = values;
982 		this._isOpen = isOpen;
983 		this._tags   = tags;
984 	}
985 
986 	/**
987 	 * Get the row id. This is populated by the treeview once the row has been 
988 	 * inserted.
989 	 *
990 	 * Returns:
991 	 *     A string containing the row id.
992 	 */
993 	public @property string id()
994 	{
995 		return this._rowId;
996 	}
997 
998 	/**
999 	 * Get the data column values.
1000 	 *
1001 	 * Returns:
1002 	 *     An array containing the data values.
1003 	 */
1004 	public @property string[] values()
1005 	{
1006 		return this._values;
1007 	}
1008 
1009 	/**
1010 	 * Get if the row was open.
1011 	 *
1012 	 * Returns:
1013 	 *     true if the row was set to be open, false if not.
1014 	 */
1015 	public @property bool isOpen()
1016 	{
1017 		return this._isOpen;
1018 	}
1019 
1020 	/**
1021 	 * Get the tags.
1022 	 *
1023 	 * Returns:
1024 	 *     An array of tags assocaited to this row.
1025 	 */
1026 	public @property string[] tags()
1027 	{
1028 		return this._tags;
1029 	}
1030 
1031 	/**
1032 	 * String representation.
1033 	 */
1034 	debug override public string toString()
1035 	{
1036 		return format("Row Id: %s, Values: %s, isOpen: %s, Tags: %s, Children: %s", this.id, this.values, this.isOpen, this.tags, this.children);
1037 	}
1038 }
1039 
1040 /**
1041  * Tree view selection modes.
1042  */
1043 enum TreeViewSelectionMode : string
1044 {
1045 	browse   = "browse",   /// The default mode, allows one selection only.
1046 	extended = "extended", /// Allows multiple selections to be made.
1047 	none     = "none",     /// Disabled all selection.
1048 }