1 // Written by Christopher E. Miller
2 // See the included license.txt for copyright and license details.
3 
4 
5 ///
6 module dfl.listview;
7 
8 private import dfl.internal.dlib, dfl.internal.clib;
9 
10 private import dfl.base, dfl.control, dfl.internal.winapi, dfl.application;
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 _initListview();
23 
24 
25 ///
26 enum ListViewAlignment: ubyte
27 {
28 	TOP, ///
29 	DEFAULT, /// ditto
30 	LEFT, /// ditto
31 	SNAP_TO_GRID, /// ditto
32 }
33 
34 
35 private union CallText
36 {
37 	Dstringz ansi;
38 	Dwstringz unicode;
39 }
40 
41 
42 private CallText getCallText(Dstring text)
43 {
44 	CallText result;
45 	if(text is null)
46 	{
47 		if(useUnicode)
48 			result.unicode = null;
49 		else
50 			result.ansi = null;
51 	}
52 	else
53 	{
54 		if(useUnicode)
55 			result.unicode = toUnicodez(text);
56 		else
57 			result.ansi = toAnsiz(text);
58 	}
59 	return result;
60 }
61 
62 
63 package union LvColumn
64 {
65 	LV_COLUMNW lvcw;
66 	LV_COLUMNA lvca;
67 	struct
68 	{
69 		UINT mask;
70 		int fmt;
71 		int cx;
72 		private void* pszText;
73 		int cchTextMax;
74 		int iSubItem;
75 	}
76 }
77 
78 
79 ///
80 class ListViewSubItem: DObject
81 {
82 	///
83 	this()
84 	{
85 		Application.ppin(cast(void*)this);
86 	}
87 	
88 	/// ditto
89 	this(Dstring thisSubItemText)
90 	{
91 		this();
92 		
93 		settextin(thisSubItemText);
94 	}
95 	
96 	/// ditto
97 	this(ListViewItem owner, Dstring thisSubItemText)
98 	{
99 		this();
100 		
101 		settextin(thisSubItemText);
102 		if(owner)
103 		{
104 			this._item = owner;
105 			owner.subItems.add(this);
106 		}
107 	}
108 	
109 	/+
110 	this(Object obj) // package
111 	{
112 		this(getObjectString(obj));
113 	}
114 	+/
115 	
116 	
117 	package final void settextin(Dstring newText)
118 	{
119 		calltxt = getCallText(newText);
120 		_txt = newText;
121 	}
122 	
123 	
124 	override Dstring toString()
125 	{
126 		return text;
127 	}
128 	
129 	
130 	override Dequ opEquals(Object o)
131 	{
132 		return text == getObjectString(o);
133 	}
134 	
135 	
136 	Dequ opEquals(Dstring val)
137 	{
138 		return text == val;
139 	}
140 	
141 	
142 	override int opCmp(Object o)
143 	{
144 		return stringICmp(text, getObjectString(o));
145 	}
146 	
147 	
148 	int opCmp(Dstring val)
149 	{
150 		return stringICmp(text, val);
151 	}
152 	
153 	
154 	///
155 	final @property void text(Dstring newText) // setter
156 	{
157 		settextin(newText);
158 		
159 		if(_item && _item.lview && _item.lview.created)
160 		{
161 			int ii, subi;
162 			ii = _item.lview.items.indexOf(_item);
163 			assert(-1 != ii);
164 			subi = _item.subItems.indexOf(this);
165 			assert(-1 != subi);
166 			_item.lview.updateItemText(ii, newText, subi + 1); // Sub items really start at 1 in the list view.
167 		}
168 	}
169 	
170 	/// ditto
171 	final @property Dstring text() // getter
172 	{
173 		return _txt;
174 	}
175 	
176 	
177 	private:
178 	package ListViewItem _item;
179 	Dstring _txt;
180 	package CallText calltxt;
181 }
182 
183 
184 ///
185 class ListViewItem: DObject
186 {
187 	///
188 	static class ListViewSubItemCollection
189 	{
190 		protected this(ListViewItem owner)
191 		in
192 		{
193 			assert(!owner.isubs);
194 		}
195 		body
196 		{
197 			_item = owner;
198 		}
199 		
200 		
201 		private:
202 		
203 		ListViewItem _item;
204 		package ListViewSubItem[] _subs;
205 		
206 		
207 		void _adding(size_t idx, ListViewSubItem val)
208 		{
209 			if(val._item)
210 				throw new DflException("ListViewSubItem already belongs to a ListViewItem");
211 		}
212 		
213 		
214 		public:
215 		
216 		mixin ListWrapArray!(ListViewSubItem, _subs,
217 			_adding, _blankListCallback!(ListViewSubItem),
218 			_blankListCallback!(ListViewSubItem), _blankListCallback!(ListViewSubItem),
219 			true, false, false);
220 	}
221 	
222 	
223 	///
224 	this()
225 	{
226 		Application.ppin(cast(void*)this);
227 		
228 		isubs = new ListViewSubItemCollection(this);
229 	}
230 	
231 	/// ditto
232 	this(Dstring text)
233 	{
234 		this();
235 		
236 		settextin(text);
237 	}
238 	
239 	
240 	private final void _setcheckstate(int thisindex, bool bchecked)
241 	{
242 		if(lview && lview.created)
243 		{
244 			LV_ITEMA li;
245 			li.stateMask = LVIS_STATEIMAGEMASK;
246 			li.state = cast(LPARAM)(bchecked ? 2 : 1) << 12;
247 			lview.prevwproc(LVM_SETITEMSTATE, cast(WPARAM)thisindex, cast(LPARAM)&li);
248 		}
249 	}
250 	
251 	
252 	private final bool _getcheckstate(int thisindex)
253 	{
254 		if(lview && lview.created)
255 		{
256 			if((lview.prevwproc(LVM_GETITEMSTATE, cast(WPARAM)thisindex, LVIS_STATEIMAGEMASK) >> 12) - 1)
257 				return true;
258 		}
259 		return false;
260 	}
261 	
262 	
263 	///
264 	final @property void checked(bool byes) // setter
265 	{
266 		return _setcheckstate(index, byes);
267 	}
268 	
269 	/// ditto
270 	final @property bool checked() // getter
271 	{
272 		return _getcheckstate(index);
273 	}
274 	
275 	
276 	package final void settextin(Dstring newText)
277 	{
278 		calltxt = getCallText(newText);
279 		_txt = newText;
280 	}
281 	
282 	
283 	override Dstring toString()
284 	{
285 		return text;
286 	}
287 	
288 	
289 	override Dequ opEquals(Object o)
290 	{
291 		return text == getObjectString(o);
292 	}
293 	
294 	
295 	Dequ opEquals(Dstring val)
296 	{
297 		return text == val;
298 	}
299 	
300 	
301 	override int opCmp(Object o)
302 	{
303 		return stringICmp(text, getObjectString(o));
304 	}
305 	
306 	
307 	int opCmp(Dstring val)
308 	{
309 		return stringICmp(text, val);
310 	}
311 	
312 	
313 	///
314 	final @property Rect bounds() // getter
315 	{
316 		if(lview)
317 		{
318 			int i = index;
319 			assert(-1 != i);
320 			return lview.getItemRect(i);
321 		}
322 		return Rect(0, 0, 0, 0);
323 	}
324 	
325 	
326 	///
327 	final @property int index() // getter
328 	{
329 		if(lview)
330 			return lview.litems.indexOf(this);
331 		return -1;
332 	}
333 	
334 	
335 	///
336 	final @property void text(Dstring newText) // setter
337 	{
338 		settextin(newText);
339 		
340 		if(lview && lview.created)
341 			lview.updateItemText(this, newText);
342 	}
343 	
344 	/// ditto
345 	final @property Dstring text() // getter
346 	{
347 		return _txt;
348 	}
349 	
350 	
351 	///
352 	final @property void selected(bool byes) // setter
353 	{
354 		if(lview && lview.created)
355 		{
356 			LV_ITEMA li;
357 			li.stateMask = LVIS_SELECTED;
358 			if(byes)
359 				li.state = LVIS_SELECTED;
360 			lview.prevwproc(LVM_SETITEMSTATE, cast(WPARAM)index, cast(LPARAM)&li);
361 		}
362 	}
363 	
364 	/// ditto
365 	final @property bool selected() // getter
366 	{
367 		if(lview && lview.created)
368 		{
369 			if(lview.prevwproc(LVM_GETITEMSTATE, cast(WPARAM)index, LVIS_SELECTED))
370 				return true;
371 		}
372 		return false;
373 	}
374 	
375 	
376 	///
377 	final @property ListView listView() // getter
378 	{
379 		return lview;
380 	}
381 	
382 	
383 	///
384 	final @property void tag(Object obj) // setter
385 	{
386 		_tag = obj;
387 	}
388 	
389 	/// ditto
390 	final @property Object tag() // getter
391 	{
392 		return _tag;
393 	}
394 	
395 	
396 	final void beginEdit()
397 	{
398 		if(lview && lview.created)
399 		{
400 			if(dfl.internal.utf.useUnicode)
401 			{
402 				lview.prevwproc(LVM_EDITLABELW, index, 0);
403 			}
404 			else
405 			{
406 				lview.prevwproc(LVM_EDITLABELA, index, 0);
407 			}
408 		}
409 	}
410 	
411 	
412 	///
413 	final @property ListViewSubItemCollection subItems() // getter
414 	{
415 		return isubs;
416 	}
417 	
418 	
419 	version(DFL_NO_IMAGELIST)
420 	{
421 	}
422 	else
423 	{
424 		///
425 		final @property void imageIndex(int index) // setter
426 		{
427 			this._imgidx = index;
428 			
429 			if(lview && lview.created)
430 				lview.updateItem(this);
431 		}
432 		
433 		/// ditto
434 		final @property int imageIndex() // getter
435 		{
436 			return _imgidx;
437 		}
438 	}
439 	
440 	
441 	private:
442 	package ListView lview = null;
443 	Object _tag = null;
444 	package ListViewSubItemCollection isubs = null;
445 	version(DFL_NO_IMAGELIST)
446 	{
447 	}
448 	else
449 	{
450 		int _imgidx = -1;
451 	}
452 	Dstring _txt;
453 	package CallText calltxt;
454 }
455 
456 
457 ///
458 class ColumnHeader: DObject
459 {
460 	///
461 	this(Dstring text)
462 	{
463 		this();
464 		
465 		this._txt = text;
466 	}
467 	
468 	/// ditto
469 	this()
470 	{
471 		Application.ppin(cast(void*)this);
472 	}
473 	
474 	
475 	///
476 	final @property ListView listView() // getter
477 	{
478 		return lview;
479 	}
480 	
481 	
482 	///
483 	final @property void text(Dstring newText) // setter
484 	{
485 		_txt = newText;
486 		
487 		if(lview && lview.created)
488 		{
489 			lview.updateColumnText(this, newText);
490 		}
491 	}
492 	
493 	/// ditto
494 	final @property Dstring text() // getter
495 	{
496 		return _txt;
497 	}
498 	
499 	
500 	override Dstring toString()
501 	{
502 		return text;
503 	}
504 	
505 	
506 	override Dequ opEquals(Object o)
507 	{
508 		return text == getObjectString(o);
509 	}
510 	
511 	
512 	Dequ opEquals(Dstring val)
513 	{
514 		return text == val;
515 	}
516 	
517 	
518 	override int opCmp(Object o)
519 	{
520 		return stringICmp(text, getObjectString(o));
521 	}
522 	
523 	
524 	int opCmp(Dstring val)
525 	{
526 		return stringICmp(text, val);
527 	}
528 	
529 	
530 	///
531 	final @property int index() // getter
532 	{
533 		if(lview)
534 			lview.cols.indexOf(this);
535 		return -1;
536 	}
537 	
538 	
539 	///
540 	final @property void textAlign(HorizontalAlignment halign) // setter
541 	{
542 		_align = halign;
543 		
544 		if(lview && lview.created)
545 		{
546 			lview.updateColumnAlign(this, halign);
547 		}
548 	}
549 	
550 	/// ditto
551 	final @property HorizontalAlignment textAlign() // getter
552 	{
553 		return _align;
554 	}
555 	
556 	
557 	///
558 	final @property void width(int w) // setter
559 	{
560 		_width = w;
561 		
562 		if(lview && lview.created)
563 		{
564 			lview.updateColumnWidth(this, w);
565 		}
566 	}
567 	
568 	/// ditto
569 	final @property int width() // getter
570 	{
571 		if(lview && lview.created)
572 		{
573 			int xx;
574 			xx = lview.getColumnWidth(this);
575 			if(-1 != xx)
576 				_width = xx;
577 		}
578 		return _width;
579 	}
580 	
581 	
582 	private:
583 	package ListView lview;
584 	Dstring _txt;
585 	int _width;
586 	HorizontalAlignment _align;
587 }
588 
589 
590 ///
591 class LabelEditEventArgs: EventArgs
592 {
593 	///
594 	this(ListViewItem item, Dstring label)
595 	{
596 		_item = item;
597 		_label = label;
598 	}
599 	
600 	/// ditto
601 	this(ListViewItem node)
602 	{
603 		_item = item;
604 	}
605 	
606 	
607 	///
608 	final @property ListViewItem item() // getter
609 	{
610 		return _item;
611 	}
612 	
613 	
614 	///
615 	final @property Dstring label() // getter
616 	{
617 		return _label;
618 	}
619 	
620 	
621 	///
622 	final @property void cancelEdit(bool byes) // setter
623 	{
624 		_cancel = byes;
625 	}
626 	
627 	/// ditto
628 	final @property bool cancelEdit() // getter
629 	{
630 		return _cancel;
631 	}
632 	
633 	
634 	private:
635 	ListViewItem _item;
636 	Dstring _label;
637 	bool _cancel = false;
638 }
639 
640 
641 /+
642 class ItemCheckEventArgs: EventArgs
643 {
644 	this(int index, CheckState newCheckState, CheckState oldCheckState)
645 	{
646 		this._idx = index;
647 		this._ncs = newCheckState;
648 		this._ocs = oldCheckState;
649 	}
650 	
651 	
652 	final @property CheckState currentValue() // getter
653 	{
654 		return _ocs;
655 	}
656 	
657 	
658 	/+
659 	final @property void newValue(CheckState cs) // setter
660 	{
661 		_ncs = cs;
662 	}
663 	+/
664 	
665 	
666 	final @property CheckState newValue() // getter
667 	{
668 		return _ncs;
669 	}
670 	
671 	
672 	private:
673 	int _idx;
674 	CheckState _ncs, _ocs;
675 }
676 +/
677 
678 
679 class ItemCheckedEventArgs: EventArgs
680 {
681 	this(ListViewItem item)
682 	{
683 		this._item = item;
684 	}
685 	
686 	
687 	final @property ListViewItem item() // getter
688 	{
689 		return this._item;
690 	}
691 	
692 	
693 	private:
694 	ListViewItem _item;
695 }
696 
697 
698 ///
699 class ListView: ControlSuperClass // docmain
700 {
701 	///
702 	static class ListViewItemCollection
703 	{
704 		protected this(ListView lv)
705 		in
706 		{
707 			assert(lv.litems is null);
708 		}
709 		body
710 		{
711 			this.lv = lv;
712 		}
713 		
714 		
715 		void add(ListViewItem item)
716 		{
717 			int ii = -1; // Insert index.
718 			
719 			switch(lv.sorting)
720 			{
721 				case SortOrder.NONE: // Add to end.
722 					ii = _items.length;
723 					break;
724 				
725 				case SortOrder.ASCENDING: // Insertion sort.
726 					for(ii = 0; ii != _items.length; ii++)
727 					{
728 						assert(lv._sortproc);
729 						//if(item < _items[ii])
730 						if(lv._sortproc(item, _items[ii]) < 0)
731 							break;
732 					}
733 					break;
734 				
735 				case SortOrder.DESCENDING: // Insertion sort.
736 					for(ii = 0; ii != _items.length; ii++)
737 					{
738 						assert(lv._sortproc);
739 						//if(item >= _items[ii])
740 						if(lv._sortproc(item, _items[ii]) >= 0)
741 							break;
742 					}
743 					break;
744 				
745 				default:
746 					assert(0);
747 			}
748 			
749 			assert(-1 != ii);
750 			insert(ii, item);
751 		}
752 		
753 		void add(Dstring text)
754 		{
755 			return add(new ListViewItem(text));
756 		}
757 		
758 		
759 		// addRange must have special case in case of sorting.
760 		
761 		void addRange(ListViewItem[] range)
762 		{
763 			foreach(ListViewItem item; range)
764 			{
765 				add(item);
766 			}
767 		}
768 		
769 		/+
770 		void addRange(Object[] range)
771 		{
772 			foreach(Object o; range)
773 			{
774 				add(o);
775 			}
776 		}
777 		+/
778 		
779 		void addRange(Dstring[] range)
780 		{
781 			foreach(Dstring s; range)
782 			{
783 				add(s);
784 			}
785 		}
786 		
787 		
788 		private:
789 		
790 		ListView lv;
791 		package ListViewItem[] _items;
792 		
793 		
794 		package final @property bool created() // getter
795 		{
796 			return lv && lv.created();
797 		}
798 		
799 		
800 		package final void doListItems() // DMD 0.125: this member is not accessible when private.
801 		in
802 		{
803 			assert(created);
804 		}
805 		body
806 		{
807 			int ii;
808 			foreach(int i, ListViewItem item; _items)
809 			{
810 				ii = lv._ins(i, item);
811 				//assert(-1 != ii);
812 				assert(i == ii);
813 				
814 				/+
815 				// Add sub items.
816 				foreach(int subi, ListViewSubItem subItem; item.isubs._subs)
817 				{
818 					lv._ins(i, subItem, subi + 1); // Sub items really start at 1 in the list view.
819 				}
820 				+/
821 			}
822 		}
823 		
824 		
825 		void verifyNoParent(ListViewItem item)
826 		{
827 			if(item.lview)
828 				throw new DflException("ListViewItem already belongs to a ListView");
829 		}
830 		
831 		
832 		void _adding(size_t idx, ListViewItem val)
833 		{
834 			verifyNoParent(val);
835 		}
836 		
837 		
838 		void _added(size_t idx, ListViewItem val)
839 		{
840 			val.lview = lv;
841 			
842 			int i;
843 			if(created)
844 			{
845 				i = lv._ins(idx, val);
846 				assert(-1 != i);
847 			}
848 		}
849 		
850 		
851 		void _removed(size_t idx, ListViewItem val)
852 		{
853 			if(size_t.max == idx) // Clear all.
854 			{
855 				if(created)
856 				{
857 					lv.prevwproc(LVM_DELETEALLITEMS, 0, 0);
858 				}
859 			}
860 			else
861 			{
862 				if(created)
863 				{
864 					lv.prevwproc(LVM_DELETEITEM, cast(WPARAM)idx, 0);
865 				}
866 			}
867 		}
868 		
869 		
870 		public:
871 		
872 		mixin ListWrapArray!(ListViewItem, _items,
873 			_adding, _added,
874 			_blankListCallback!(ListViewItem), _removed,
875 			true, false, false);
876 	}
877 	
878 	
879 	///
880 	static class ColumnHeaderCollection
881 	{
882 		protected this(ListView owner)
883 		in
884 		{
885 			assert(!owner.cols);
886 		}
887 		body
888 		{
889 			lv = owner;
890 		}
891 		
892 		
893 		private:
894 		ListView lv;
895 		ColumnHeader[] _headers;
896 		
897 		
898 		package final @property bool created() // getter
899 		{
900 			return lv && lv.created();
901 		}
902 		
903 		
904 		void verifyNoParent(ColumnHeader header)
905 		{
906 			if(header.lview)
907 				throw new DflException("ColumnHeader already belongs to a ListView");
908 		}
909 		
910 		
911 		package final void doListHeaders() // DMD 0.125: this member is not accessible when private.
912 		in
913 		{
914 			assert(created);
915 		}
916 		body
917 		{
918 			int ii;
919 			foreach(int i, ColumnHeader header; _headers)
920 			{
921 				ii = lv._ins(i, header);
922 				assert(-1 != ii);
923 				//assert(i == ii);
924 			}
925 		}
926 		
927 		
928 		void _adding(size_t idx, ColumnHeader val)
929 		{
930 			verifyNoParent(val);
931 		}
932 		
933 		
934 		void _added(size_t idx, ColumnHeader val)
935 		{
936 			val.lview = lv;
937 			
938 			int i;
939 			if(created)
940 			{
941 				i = lv._ins(idx, val);
942 				assert(-1 != i);
943 			}
944 		}
945 		
946 		
947 		void _removed(size_t idx, ColumnHeader val)
948 		{
949 			if(size_t.max == idx) // Clear all.
950 			{
951 			}
952 			else
953 			{
954 				if(created)
955 				{
956 					lv.prevwproc(LVM_DELETECOLUMN, cast(WPARAM)idx, 0);
957 				}
958 			}
959 		}
960 		
961 		
962 		public:
963 		
964 		mixin ListWrapArray!(ColumnHeader, _headers,
965 			_adding, _added,
966 			_blankListCallback!(ColumnHeader), _removed,
967 			true, false, false,
968 			true); // CLEAR_EACH
969 	}
970 	
971 	
972 	///
973 	static class SelectedIndexCollection
974 	{
975 		deprecated alias length count;
976 		
977 		@property int length() // getter
978 		{
979 			if(!lview.created)
980 				return 0;
981 			
982 			int result = 0;
983 			foreach(int onidx; this)
984 			{
985 				result++;
986 			}
987 			return result;
988 		}
989 		
990 		
991 		int opIndex(int idx)
992 		{
993 			foreach(int onidx; this)
994 			{
995 				if(!idx)
996 					return onidx;
997 				idx--;
998 			}
999 			
1000 			// If it's not found it's out of bounds and bad things happen.
1001 			assert(0);
1002 		}
1003 		
1004 		
1005 		bool contains(int idx)
1006 		{
1007 			return indexOf(idx) != -1;
1008 		}
1009 		
1010 		
1011 		int indexOf(int idx)
1012 		{
1013 			int i = 0;
1014 			foreach(int onidx; this)
1015 			{
1016 				if(onidx == idx)
1017 					return i;
1018 				i++;
1019 			}
1020 			return -1;
1021 		}
1022 		
1023 		
1024 		int opApply(int delegate(ref int) dg)
1025 		{
1026 			if(!lview.created)
1027 				return 0;
1028 			
1029 			int result = 0;
1030 			int idx = -1;
1031 			for(;;)
1032 			{
1033 				idx = cast(int)lview.prevwproc(LVM_GETNEXTITEM, cast(WPARAM)idx, MAKELPARAM(cast(UINT)LVNI_SELECTED, 0));
1034 				if(-1 == idx) // Done.
1035 					break;
1036 				int dgidx = idx; // Prevent ref.
1037 				result = dg(dgidx);
1038 				if(result)
1039 					break;
1040 			}
1041 			return result;
1042 		}
1043 		
1044 		mixin OpApplyAddIndex!(opApply, int);
1045 		
1046 		
1047 		protected this(ListView lv)
1048 		{
1049 			lview = lv;
1050 		}
1051 		
1052 		
1053 		package:
1054 		ListView lview;
1055 	}
1056 	
1057 	
1058 	deprecated alias SelectedItemCollection SelectedListViewItemCollection;
1059 	
1060 	///
1061 	static class SelectedItemCollection
1062 	{
1063 		deprecated alias length count;
1064 		
1065 		@property int length() // getter
1066 		{
1067 			if(!lview.created)
1068 				return 0;
1069 			
1070 			int result = 0;
1071 			foreach(ListViewItem onitem; this)
1072 			{
1073 				result++;
1074 			}
1075 			return result;
1076 		}
1077 		
1078 		
1079 		ListViewItem opIndex(int idx)
1080 		{
1081 			foreach(ListViewItem onitem; this)
1082 			{
1083 				if(!idx)
1084 					return onitem;
1085 				idx--;
1086 			}
1087 			
1088 			// If it's not found it's out of bounds and bad things happen.
1089 			assert(0);
1090 		}
1091 		
1092 		
1093 		bool contains(ListViewItem item)
1094 		{
1095 			return indexOf(item) != -1;
1096 		}
1097 		
1098 		
1099 		int indexOf(ListViewItem item)
1100 		{
1101 			int i = 0;
1102 			foreach(ListViewItem onitem; this)
1103 			{
1104 				if(onitem == item) // Not using is.
1105 					return i;
1106 				i++;
1107 			}
1108 			return -1;
1109 		}
1110 		
1111 		
1112 		int opApply(int delegate(ref ListViewItem) dg)
1113 		{
1114 			if(!lview.created)
1115 				return 0;
1116 			
1117 			int result = 0;
1118 			int idx = -1;
1119 			for(;;)
1120 			{
1121 				idx = cast(int)lview.prevwproc(LVM_GETNEXTITEM, cast(WPARAM)idx, MAKELPARAM(cast(UINT)LVNI_SELECTED, 0));
1122 				if(-1 == idx) // Done.
1123 					break;
1124 				ListViewItem litem = lview.litems._items[idx]; // Prevent ref.
1125 				result = dg(litem);
1126 				if(result)
1127 					break;
1128 			}
1129 			return result;
1130 		}
1131 		
1132 		mixin OpApplyAddIndex!(opApply, ListViewItem);
1133 		
1134 		
1135 		protected this(ListView lv)
1136 		{
1137 			lview = lv;
1138 		}
1139 		
1140 		
1141 		package:
1142 		ListView lview;
1143 	}
1144 	
1145 	
1146 	///
1147 	static class CheckedIndexCollection
1148 	{
1149 		deprecated alias length count;
1150 		
1151 		@property int length() // getter
1152 		{
1153 			if(!lview.created)
1154 				return 0;
1155 			
1156 			int result = 0;
1157 			foreach(int onidx; this)
1158 			{
1159 				result++;
1160 			}
1161 			return result;
1162 		}
1163 		
1164 		
1165 		int opIndex(int idx)
1166 		{
1167 			foreach(int onidx; this)
1168 			{
1169 				if(!idx)
1170 					return onidx;
1171 				idx--;
1172 			}
1173 			
1174 			// If it's not found it's out of bounds and bad things happen.
1175 			assert(0);
1176 		}
1177 		
1178 		
1179 		bool contains(int idx)
1180 		{
1181 			return indexOf(idx) != -1;
1182 		}
1183 		
1184 		
1185 		int indexOf(int idx)
1186 		{
1187 			int i = 0;
1188 			foreach(int onidx; this)
1189 			{
1190 				if(onidx == idx)
1191 					return i;
1192 				i++;
1193 			}
1194 			return -1;
1195 		}
1196 		
1197 		
1198 		int opApply(int delegate(ref int) dg)
1199 		{
1200 			if(!lview.created)
1201 				return 0;
1202 			
1203 			int result = 0;
1204 			foreach(ref size_t i, ref ListViewItem lvitem; lview.items)
1205 			{
1206 				if(lvitem._getcheckstate(i))
1207 				{
1208 					int dgidx = i; // Prevent ref.
1209 					result = dg(dgidx);
1210 					if(result)
1211 						break;
1212 				}
1213 			}
1214 			return result;
1215 		}
1216 		
1217 		mixin OpApplyAddIndex!(opApply, int);
1218 		
1219 		
1220 		protected this(ListView lv)
1221 		{
1222 			lview = lv;
1223 		}
1224 		
1225 		
1226 		package:
1227 		ListView lview;
1228 	}
1229 	
1230 	
1231 	this()
1232 	{
1233 		_initListview();
1234 		
1235 		litems = new ListViewItemCollection(this);
1236 		cols = new ColumnHeaderCollection(this);
1237 		selidxcollection = new SelectedIndexCollection(this);
1238 		selobjcollection = new SelectedItemCollection(this);
1239 		checkedis = new CheckedIndexCollection(this);
1240 		
1241 		wstyle |= WS_TABSTOP | LVS_ALIGNTOP | LVS_AUTOARRANGE | LVS_SHAREIMAGELISTS;
1242 		wexstyle |= WS_EX_CLIENTEDGE;
1243 		ctrlStyle |= ControlStyles.SELECTABLE;
1244 		wclassStyle = listviewClassStyle;
1245 	}
1246 	
1247 	
1248 	///
1249 	final @property void activation(ItemActivation ia) // setter
1250 	{
1251 		switch(ia)
1252 		{
1253 			case ItemActivation.STANDARD:
1254 				_lvexstyle(LVS_EX_ONECLICKACTIVATE | LVS_EX_TWOCLICKACTIVATE, 0);
1255 				break;
1256 			
1257 			case ItemActivation.ONE_CLICK:
1258 				_lvexstyle(LVS_EX_ONECLICKACTIVATE | LVS_EX_TWOCLICKACTIVATE, LVS_EX_ONECLICKACTIVATE);
1259 				break;
1260 			
1261 			case ItemActivation.TWO_CLICK:
1262 				_lvexstyle(LVS_EX_ONECLICKACTIVATE | LVS_EX_TWOCLICKACTIVATE, LVS_EX_TWOCLICKACTIVATE);
1263 				break;
1264 			
1265 			default:
1266 				assert(0);
1267 		}
1268 	}
1269 	
1270 	/// ditto
1271 	final @property ItemActivation activation() // getter
1272 	{
1273 		DWORD lvex;
1274 		lvex = _lvexstyle();
1275 		if(lvex & LVS_EX_ONECLICKACTIVATE)
1276 			return ItemActivation.ONE_CLICK;
1277 		if(lvex & LVS_EX_TWOCLICKACTIVATE)
1278 			return ItemActivation.TWO_CLICK;
1279 		return ItemActivation.STANDARD;
1280 	}
1281 	
1282 	
1283 	/+
1284 	///
1285 	final void alignment(ListViewAlignment lva)
1286 	{
1287 		// TODO
1288 		
1289 		switch(lva)
1290 		{
1291 			case ListViewAlignment.TOP:
1292 				_style((_style() & ~(LVS_ALIGNLEFT | foo)) | LVS_ALIGNTOP);
1293 				break;
1294 			
1295 			default:
1296 				assert(0);
1297 		}
1298 	}
1299 	
1300 	/// ditto
1301 	final @property ListViewAlignment alignment() // getter
1302 	{
1303 		// TODO
1304 	}
1305 	+/
1306 	
1307 	
1308 	///
1309 	final @property void allowColumnReorder(bool byes) // setter
1310 	{
1311 		_lvexstyle(LVS_EX_HEADERDRAGDROP, byes ? LVS_EX_HEADERDRAGDROP : 0);
1312 	}
1313 	
1314 	/// ditto
1315 	final @property bool allowColumnReorder() // getter
1316 	{
1317 		return (_lvexstyle() & LVS_EX_HEADERDRAGDROP) == LVS_EX_HEADERDRAGDROP;
1318 	}
1319 	
1320 	
1321 	///
1322 	final @property void autoArrange(bool byes) // setter
1323 	{
1324 		if(byes)
1325 			_style(_style() | LVS_AUTOARRANGE);
1326 		else
1327 			_style(_style() & ~LVS_AUTOARRANGE);
1328 		
1329 		//_crecreate(); // ?
1330 	}
1331 	
1332 	/// ditto
1333 	final @property bool autoArrange() // getter
1334 	{
1335 		return (_style() & LVS_AUTOARRANGE) == LVS_AUTOARRANGE;
1336 	}
1337 	
1338 	
1339 	override @property void backColor(Color c) // setter
1340 	{
1341 		if(created)
1342 		{
1343 			COLORREF cref;
1344 			if(Color.empty == c)
1345 				cref = CLR_NONE;
1346 			else
1347 				cref = c.toRgb();
1348 			prevwproc(LVM_SETBKCOLOR, 0, cast(LPARAM)cref);
1349 			prevwproc(LVM_SETTEXTBKCOLOR, 0, cast(LPARAM)cref);
1350 		}
1351 		
1352 		super.backColor = c;
1353 	}
1354 	
1355 	
1356 	override @property Color backColor() // getter
1357 	{
1358 		if(Color.empty == backc)
1359 			return defaultBackColor;
1360 		return backc;
1361 	}
1362 	
1363 	
1364 	///
1365 	final @property void borderStyle(BorderStyle bs) // setter
1366 	{
1367 		final switch(bs)
1368 		{
1369 			case BorderStyle.FIXED_3D:
1370 				_style(_style() & ~WS_BORDER);
1371 				_exStyle(_exStyle() | WS_EX_CLIENTEDGE);
1372 				break;
1373 				
1374 			case BorderStyle.FIXED_SINGLE:
1375 				_exStyle(_exStyle() & ~WS_EX_CLIENTEDGE);
1376 				_style(_style() | WS_BORDER);
1377 				break;
1378 				
1379 			case BorderStyle.NONE:
1380 				_style(_style() & ~WS_BORDER);
1381 				_exStyle(_exStyle() & ~WS_EX_CLIENTEDGE);
1382 				break;
1383 		}
1384 		
1385 		if(created)
1386 		{
1387 			redrawEntire();
1388 		}
1389 	}
1390 	
1391 	/// ditto
1392 	final @property BorderStyle borderStyle() // getter
1393 	{
1394 		if(_exStyle() & WS_EX_CLIENTEDGE)
1395 			return BorderStyle.FIXED_3D;
1396 		else if(_style() & WS_BORDER)
1397 			return BorderStyle.FIXED_SINGLE;
1398 		return BorderStyle.NONE;
1399 	}
1400 	
1401 	
1402 	///
1403 	final @property void checkBoxes(bool byes) // setter
1404 	{
1405 		_lvexstyle(LVS_EX_CHECKBOXES, byes ? LVS_EX_CHECKBOXES : 0);
1406 	}
1407 	
1408 	/// ditto
1409 	final @property bool checkBoxes() // getter
1410 	{
1411 		return (_lvexstyle() & LVS_EX_CHECKBOXES) == LVS_EX_CHECKBOXES;
1412 	}
1413 	
1414 	
1415 	///
1416 	// ListView.CheckedIndexCollection
1417 	final @property CheckedIndexCollection checkedIndices() // getter
1418 	{
1419 		return checkedis;
1420 	}
1421 	
1422 	
1423 	/+
1424 	///
1425 	// ListView.CheckedListViewItemCollection
1426 	final @property CheckedListViewItemCollection checkedItems() // getter
1427 	{
1428 		// TODO
1429 	}
1430 	+/
1431 	
1432 	
1433 	///
1434 	final @property ColumnHeaderCollection columns() // getter
1435 	{
1436 		return cols;
1437 	}
1438 	
1439 	
1440 	///
1441 	// Extra.
1442 	final @property int focusedIndex() // getter
1443 	{
1444 		if(!created)
1445 			return -1;
1446 		return cast(int)prevwproc(LVM_GETNEXTITEM, cast(WPARAM)-1, MAKELPARAM(cast(UINT)LVNI_FOCUSED, 0));
1447 	}
1448 	
1449 	
1450 	///
1451 	final @property ListViewItem focusedItem() // getter
1452 	{
1453 		int i;
1454 		i = focusedIndex;
1455 		if(-1 == i)
1456 			return null;
1457 		return litems._items[i];
1458 	}
1459 	
1460 	
1461 	override @property void foreColor(Color c) // setter
1462 	{
1463 		if(created)
1464 			prevwproc(LVM_SETTEXTCOLOR, 0, cast(LPARAM)c.toRgb());
1465 		
1466 		super.foreColor = c;
1467 	}
1468 	
1469 	
1470 	override @property Color foreColor() // getter
1471 	{
1472 		if(Color.empty == forec)
1473 			return defaultForeColor;
1474 		return forec;
1475 	}
1476 	
1477 	
1478 	///
1479 	final @property void fullRowSelect(bool byes) // setter
1480 	{
1481 		_lvexstyle(LVS_EX_FULLROWSELECT, byes ? LVS_EX_FULLROWSELECT : 0);
1482 	}
1483 	
1484 	/// ditto
1485 	final @property bool fullRowSelect() // getter
1486 	{
1487 		return (_lvexstyle() & LVS_EX_FULLROWSELECT) == LVS_EX_FULLROWSELECT;
1488 	}
1489 	
1490 	
1491 	///
1492 	final @property void gridLines(bool byes) // setter
1493 	{
1494 		_lvexstyle(LVS_EX_GRIDLINES, byes ? LVS_EX_GRIDLINES : 0);
1495 	}
1496 	
1497 	/// ditto
1498 	final @property bool gridLines() // getter
1499 	{
1500 		return (_lvexstyle() & LVS_EX_GRIDLINES) == LVS_EX_GRIDLINES;
1501 	}
1502 	
1503 	
1504 	/+
1505 	///
1506 	final @property void headerStyle(ColumnHeaderStyle chs) // setter
1507 	{
1508 		// TODO: LVS_NOCOLUMNHEADER ... default is clickable.
1509 	}
1510 	
1511 	/// ditto
1512 	final @property ColumnHeaderStyle headerStyle() // getter
1513 	{
1514 		// TODO
1515 	}
1516 	+/
1517 	
1518 	
1519 	///
1520 	final @property void hideSelection(bool byes) // setter
1521 	{
1522 		if(byes)
1523 			_style(_style() & ~LVS_SHOWSELALWAYS);
1524 		else
1525 			_style(_style() | LVS_SHOWSELALWAYS);
1526 	}
1527 	
1528 	/// ditto
1529 	final @property bool hideSelection() // getter
1530 	{
1531 		return (_style() & LVS_SHOWSELALWAYS) != LVS_SHOWSELALWAYS;
1532 	}
1533 	
1534 	
1535 	///
1536 	final @property void hoverSelection(bool byes) // setter
1537 	{
1538 		_lvexstyle(LVS_EX_TRACKSELECT, byes ? LVS_EX_TRACKSELECT : 0);
1539 	}
1540 	
1541 	/// ditto
1542 	final @property bool hoverSelection() // getter
1543 	{
1544 		return (_lvexstyle() & LVS_EX_TRACKSELECT) == LVS_EX_TRACKSELECT;
1545 	}
1546 	
1547 	
1548 	///
1549 	final @property ListViewItemCollection items() // getter
1550 	{
1551 		return litems;
1552 	}
1553 	
1554 	
1555 	///
1556 	// Simple as addRow("item", "sub item1", "sub item2", "etc");
1557 	// rowstrings[0] is the item and rowstrings[1 .. rowstrings.length] are its sub items.
1558 	//final void addRow(Dstring[] rowstrings ...)
1559 	final ListViewItem addRow(Dstring[] rowstrings ...)
1560 	{
1561 		if(rowstrings.length)
1562 		{
1563 			ListViewItem item;
1564 			item = new ListViewItem(rowstrings[0]);
1565 			if(rowstrings.length > 1)
1566 				item.subItems.addRange(rowstrings[1 .. rowstrings.length]);
1567 			items.add(item);
1568 			return item;
1569 		}
1570 		assert(0);
1571 	}
1572 	
1573 	
1574 	///
1575 	final @property void labelEdit(bool byes) // setter
1576 	{
1577 		if(byes)
1578 			_style(_style() | LVS_EDITLABELS);
1579 		else
1580 			_style(_style() & ~LVS_EDITLABELS);
1581 	}
1582 	
1583 	/// ditto
1584 	final @property bool labelEdit() // getter
1585 	{
1586 		return (_style() & LVS_EDITLABELS) == LVS_EDITLABELS;
1587 	}
1588 	
1589 	
1590 	///
1591 	final @property void labelWrap(bool byes) // setter
1592 	{
1593 		if(byes)
1594 			_style(_style() & ~LVS_NOLABELWRAP);
1595 		else
1596 			_style(_style() | LVS_NOLABELWRAP);
1597 	}
1598 	
1599 	/// ditto
1600 	final @property bool labelWrap() // getter
1601 	{
1602 		return (_style() & LVS_NOLABELWRAP) != LVS_NOLABELWRAP;
1603 	}
1604 	
1605 	
1606 	///
1607 	final @property void multiSelect(bool byes) // setter
1608 	{
1609 		if(byes)
1610 		{
1611 			_style(_style() & ~LVS_SINGLESEL);
1612 		}
1613 		else
1614 		{
1615 			_style(_style() | LVS_SINGLESEL);
1616 			
1617 			if(selectedItems.length > 1)
1618 				selectedItems[0].selected = true; // Clear all but first selected.
1619 		}
1620 	}
1621 	
1622 	/// ditto
1623 	final @property bool multiSelect() // getter
1624 	{
1625 		return (_style() & LVS_SINGLESEL) != LVS_SINGLESEL;
1626 	}
1627 	
1628 	
1629 	///
1630 	// Note: scrollable=false is not compatible with the list or details(report) styles(views).
1631 	// See Knowledge Base Article Q137520.
1632 	final @property void scrollable(bool byes) // setter
1633 	{
1634 		if(byes)
1635 			_style(_style() & ~LVS_NOSCROLL);
1636 		else
1637 			_style(_style() | LVS_NOSCROLL);
1638 		
1639 		_crecreate();
1640 	}
1641 	
1642 	/// ditto
1643 	final @property bool scrollable() // getter
1644 	{
1645 		return (_style() & LVS_NOSCROLL) != LVS_NOSCROLL;
1646 	}
1647 	
1648 	
1649 	///
1650 	final @property SelectedIndexCollection selectedIndices() // getter
1651 	{
1652 		return selidxcollection;
1653 	}
1654 	
1655 	
1656 	///
1657 	final @property SelectedItemCollection selectedItems() // getter
1658 	{
1659 		return selobjcollection;
1660 	}
1661 	
1662 	
1663 	///
1664 	final @property void view(View v) // setter
1665 	{
1666 		switch(v)
1667 		{
1668 			case View.LARGE_ICON:
1669 				_style(_style() & ~(LVS_SMALLICON | LVS_LIST | LVS_REPORT));
1670 				break;
1671 			
1672 			case View.SMALL_ICON:
1673 				_style((_style() & ~(LVS_LIST | LVS_REPORT)) | LVS_SMALLICON);
1674 				break;
1675 			
1676 			case View.LIST:
1677 				_style((_style() & ~(LVS_SMALLICON | LVS_REPORT)) | LVS_LIST);
1678 				break;
1679 			
1680 			case View.DETAILS:
1681 				_style((_style() & ~(LVS_SMALLICON | LVS_LIST)) | LVS_REPORT);
1682 				break;
1683 			
1684 			default:
1685 				assert(0);
1686 		}
1687 		
1688 		if(created)
1689 			redrawEntire();
1690 	}
1691 	
1692 	/// ditto
1693 	final @property View view() // getter
1694 	{
1695 		LONG st;
1696 		st = _style();
1697 		if(st & LVS_SMALLICON)
1698 			return View.SMALL_ICON;
1699 		if(st & LVS_LIST)
1700 			return View.LIST;
1701 		if(st & LVS_REPORT)
1702 			return View.DETAILS;
1703 		return View.LARGE_ICON;
1704 	}
1705 	
1706 	
1707 	///
1708 	final @property void sorting(SortOrder so) // setter
1709 	{
1710 		if(so == _sortorder)
1711 			return;
1712 		
1713 		switch(so)
1714 		{
1715 			case SortOrder.NONE:
1716 				_sortproc = null;
1717 				break;
1718 			
1719 			case SortOrder.ASCENDING:
1720 			case SortOrder.DESCENDING:
1721 				if(!_sortproc)
1722 					_sortproc = &_defsortproc;
1723 				break;
1724 			
1725 			default:
1726 				assert(0);
1727 		}
1728 		
1729 		_sortorder = so;
1730 		
1731 		sort();
1732 	}
1733 	
1734 	/// ditto
1735 	final @property SortOrder sorting() // getter
1736 	{
1737 		return _sortorder;
1738 	}
1739 	
1740 	
1741 	///
1742 	final void sort()
1743 	{
1744 		if(SortOrder.NONE != _sortorder)
1745 		{
1746 			assert(_sortproc);
1747 			ListViewItem[] sitems = items._items;
1748 			if(sitems.length > 1)
1749 			{
1750 				sitems = sitems.dup; // So exception won't damage anything.
1751 				// Stupid bubble sort. At least it's a "stable sort".
1752 				bool swp;
1753 				auto sortmax = sitems.length - 1;
1754 				size_t iw;
1755 				do
1756 				{
1757 					swp = false;
1758 					for(iw = 0; iw != sortmax; iw++)
1759 					{
1760 						//if(sitems[iw] > sitems[iw + 1])
1761 						if(_sortproc(sitems[iw], sitems[iw + 1]) > 0)
1762 						{
1763 							swp = true;
1764 							ListViewItem lvis = sitems[iw];
1765 							sitems[iw] = sitems[iw + 1];
1766 							sitems[iw + 1] = lvis;
1767 						}
1768 					}
1769 				}
1770 				while(swp);
1771 				
1772 				if(created)
1773 				{
1774 					beginUpdate();
1775 					SendMessageA(handle, LVM_DELETEALLITEMS, 0, 0); // Note: this sends LVN_DELETEALLITEMS.
1776 					foreach(idx, lvi; sitems)
1777 					{
1778 						_ins(idx, lvi);
1779 					}
1780 					endUpdate();
1781 				}
1782 				
1783 				items._items = sitems;
1784 			}
1785 		}
1786 	}
1787 	
1788 	
1789 	///
1790 	final @property void sorter(int delegate(ListViewItem, ListViewItem) sortproc) // setter
1791 	{
1792 		if(sortproc == this._sortproc)
1793 			return;
1794 		
1795 		if(!sortproc)
1796 		{
1797 			this._sortproc = null;
1798 			sorting = SortOrder.NONE;
1799 			return;
1800 		}
1801 		
1802 		this._sortproc = sortproc;
1803 		
1804 		if(SortOrder.NONE == sorting)
1805 			sorting = SortOrder.ASCENDING;
1806 		sort();
1807 	}
1808 	
1809 	/// ditto
1810 	final int delegate(ListViewItem, ListViewItem) sorter() @property // getter
1811 	{
1812 		return _sortproc;
1813 	}
1814 	
1815 	
1816 	/+
1817 	///
1818 	// Gets the first visible item.
1819 	final @property ListViewItem topItem() // getter
1820 	{
1821 		if(!created)
1822 			return null;
1823 		// TODO: LVM_GETTOPINDEX
1824 	}
1825 	+/
1826 	
1827 	
1828 	///
1829 	final @property void arrangeIcons()
1830 	{
1831 		if(created)
1832 		//	SendMessageA(hwnd, LVM_ARRANGE, LVA_DEFAULT, 0);
1833 			prevwproc(LVM_ARRANGE, LVA_DEFAULT, 0);
1834 	}
1835 	
1836 	/// ditto
1837 	final void arrangeIcons(ListViewAlignment a)
1838 	{
1839 		if(created)
1840 		{
1841 			switch(a)
1842 			{
1843 				case ListViewAlignment.TOP:
1844 					//SendMessageA(hwnd, LVM_ARRANGE, LVA_ALIGNTOP, 0);
1845 					prevwproc(LVM_ARRANGE, LVA_ALIGNTOP, 0);
1846 					break;
1847 				
1848 				case ListViewAlignment.DEFAULT:
1849 					//SendMessageA(hwnd, LVM_ARRANGE, LVA_DEFAULT, 0);
1850 					prevwproc(LVM_ARRANGE, LVA_DEFAULT, 0);
1851 					break;
1852 				
1853 				case ListViewAlignment.LEFT:
1854 					//SendMessageA(hwnd, LVM_ARRANGE, LVA_ALIGNLEFT, 0);
1855 					prevwproc(LVM_ARRANGE, LVA_ALIGNLEFT, 0);
1856 					break;
1857 				
1858 				case ListViewAlignment.SNAP_TO_GRID:
1859 					//SendMessageA(hwnd, LVM_ARRANGE, LVA_SNAPTOGRID, 0);
1860 					prevwproc(LVM_ARRANGE, LVA_SNAPTOGRID, 0);
1861 					break;
1862 				
1863 				default:
1864 					assert(0);
1865 			}
1866 		}
1867 	}
1868 	
1869 	
1870 	///
1871 	final void beginUpdate()
1872 	{
1873 		SendMessageA(handle, WM_SETREDRAW, false, 0);
1874 	}
1875 	
1876 	/// ditto
1877 	final void endUpdate()
1878 	{
1879 		SendMessageA(handle, WM_SETREDRAW, true, 0);
1880 		invalidate(true); // Show updates.
1881 	}
1882 	
1883 	
1884 	///
1885 	final void clear()
1886 	{
1887 		litems.clear();
1888 	}
1889 	
1890 	
1891 	///
1892 	final void ensureVisible(int index)
1893 	{
1894 		// Can only be visible if it's created. Check if correct implementation.
1895 		createControl();
1896 		
1897 		//if(created)
1898 		//	SendMessageA(hwnd, LVM_ENSUREVISIBLE, cast(WPARAM)index, FALSE);
1899 			prevwproc(LVM_ENSUREVISIBLE, cast(WPARAM)index, FALSE);
1900 	}
1901 	
1902 	
1903 	/+
1904 	///
1905 	// Returns null if no item is at this location.
1906 	final ListViewItem getItemAt(int x, int y)
1907 	{
1908 		// LVM_FINDITEM LVFI_NEARESTXY ? since it's nearest, need to see if it's really at that location.
1909 		// TODO
1910 	}
1911 	+/
1912 	
1913 	
1914 	///
1915 	final Rect getItemRect(int index)
1916 	{
1917 		if(created)
1918 		{
1919 			RECT rect;
1920 			rect.left = LVIR_BOUNDS;
1921 			if(prevwproc(LVM_GETITEMRECT, cast(WPARAM)index, cast(LPARAM)&rect))
1922 				return Rect(&rect);
1923 		}
1924 		return Rect(0, 0, 0, 0);
1925 	}
1926 	
1927 	/// ditto
1928 	final Rect getItemRect(int index, ItemBoundsPortion ibp)
1929 	{
1930 		if(created)
1931 		{
1932 			RECT rect;
1933 			switch(ibp)
1934 			{
1935 				case ItemBoundsPortion.ENTIRE:
1936 					rect.left = LVIR_BOUNDS;
1937 					break;
1938 				
1939 				case ItemBoundsPortion.ICON:
1940 					rect.left = LVIR_ICON;
1941 					break;
1942 				
1943 				case ItemBoundsPortion.ITEM_ONLY:
1944 					rect.left = LVIR_SELECTBOUNDS; // ?
1945 					break;
1946 				
1947 				case ItemBoundsPortion.LABEL:
1948 					rect.left = LVIR_LABEL;
1949 					break;
1950 				
1951 				default:
1952 					assert(0);
1953 			}
1954 			if(prevwproc(LVM_GETITEMRECT, cast(WPARAM)index, cast(LPARAM)&rect))
1955 				return Rect(&rect);
1956 		}
1957 		return Rect(0, 0, 0, 0);
1958 	}
1959 	
1960 	
1961 	version(DFL_NO_IMAGELIST)
1962 	{
1963 	}
1964 	else
1965 	{
1966 		///
1967 		final @property void largeImageList(ImageList imglist) // setter
1968 		{
1969 			if(isHandleCreated)
1970 			{
1971 				prevwproc(LVM_SETIMAGELIST, LVSIL_NORMAL,
1972 					cast(LPARAM)(imglist ? imglist.handle : cast(HIMAGELIST)null));
1973 			}
1974 			
1975 			_lgimglist = imglist;
1976 		}
1977 		
1978 		/// ditto
1979 		final @property ImageList largeImageList() // getter
1980 		{
1981 			return _lgimglist;
1982 		}
1983 		
1984 		
1985 		///
1986 		final @property void smallImageList(ImageList imglist) // setter
1987 		{
1988 			if(isHandleCreated)
1989 			{
1990 				prevwproc(LVM_SETIMAGELIST, LVSIL_SMALL,
1991 					cast(LPARAM)(imglist ? imglist.handle : cast(HIMAGELIST)null));
1992 			}
1993 			
1994 			_smimglist = imglist;
1995 		}
1996 		
1997 		/// ditto
1998 		final @property ImageList smallImageList() // getter
1999 		{
2000 			return _smimglist;
2001 		}
2002 		
2003 		
2004 		/+
2005 		///
2006 		final @property void stateImageList(ImageList imglist) // setter
2007 		{
2008 			if(isHandleCreated)
2009 			{
2010 				prevwproc(LVM_SETIMAGELIST, LVSIL_STATE,
2011 					cast(LPARAM)(imglist ? imglist.handle : cast(HIMAGELIST)null));
2012 			}
2013 			
2014 			_stimglist = imglist;
2015 		}
2016 		
2017 		/// ditto
2018 		final @property ImageList stateImageList() // getter
2019 		{
2020 			return _stimglist;
2021 		}
2022 		+/
2023 	}
2024 	
2025 	
2026 	// TODO:
2027 	//  itemActivate, itemDrag
2028 	//CancelEventHandler selectedIndexChanging; // ?
2029 	
2030 	Event!(ListView, ColumnClickEventArgs) columnClick; ///
2031 	Event!(ListView, LabelEditEventArgs) afterLabelEdit; ///
2032 	Event!(ListView, LabelEditEventArgs) beforeLabelEdit; ///
2033 	//Event!(ListView, ItemCheckEventArgs) itemCheck; ///
2034 	Event!(ListView, ItemCheckedEventArgs) itemChecked; ///
2035 	Event!(ListView, EventArgs) selectedIndexChanged; ///
2036 	
2037 	
2038 	///
2039 	protected void onColumnClick(ColumnClickEventArgs ea)
2040 	{
2041 		columnClick(this, ea);
2042 	}
2043 	
2044 	
2045 	///
2046 	protected void onAfterLabelEdit(LabelEditEventArgs ea)
2047 	{
2048 		afterLabelEdit(this, ea);
2049 	}
2050 	
2051 	
2052 	///
2053 	protected void onBeforeLabelEdit(LabelEditEventArgs ea)
2054 	{
2055 		beforeLabelEdit(this, ea);
2056 	}
2057 	
2058 	
2059 	/+
2060 	protected void onItemCheck(ItemCheckEventArgs ea)
2061 	{
2062 		itemCheck(this, ea);
2063 	}
2064 	+/
2065 	
2066 	
2067 	///
2068 	protected void onItemChecked(ItemCheckedEventArgs ea)
2069 	{
2070 		itemChecked(this, ea);
2071 	}
2072 	
2073 	
2074 	///
2075 	protected void onSelectedIndexChanged(EventArgs ea)
2076 	{
2077 		selectedIndexChanged(this, ea);
2078 	}
2079 	
2080 	
2081 	protected override @property Size defaultSize() // getter
2082 	{
2083 		return Size(120, 95);
2084 	}
2085 	
2086 	
2087 	static @property Color defaultBackColor() // getter
2088 	{
2089 		return SystemColors.window;
2090 	}
2091 	
2092 	
2093 	static @property Color defaultForeColor() // getter
2094 	{
2095 		return SystemColors.windowText;
2096 	}
2097 	
2098 	
2099 	protected override void createParams(ref CreateParams cp)
2100 	{
2101 		super.createParams(cp);
2102 		
2103 		cp.className = LISTVIEW_CLASSNAME;
2104 	}
2105 	
2106 	
2107 	protected override void prevWndProc(ref Message msg)
2108 	{
2109 		switch(msg.msg)
2110 		{
2111 			case WM_MOUSEHOVER:
2112 				if(!hoverSelection)
2113 					return;
2114 				break;
2115 			
2116 			default:
2117 		}
2118 		
2119 		//msg.result = CallWindowProcA(listviewPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam);
2120 		msg.result = dfl.internal.utf.callWindowProc(listviewPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam);
2121 	}
2122 	
2123 	
2124 	protected override void wndProc(ref Message m)
2125 	{
2126 		// TODO: support the listview messages.
2127 		
2128 		switch(m.msg)
2129 		{
2130 			/+
2131 			case WM_PAINT:
2132 				// This seems to be the only way to display columns correctly.
2133 				prevWndProc(m);
2134 				return;
2135 			+/
2136 			
2137 			case LVM_ARRANGE:
2138 				m.result = FALSE;
2139 				return;
2140 			
2141 			case LVM_DELETEALLITEMS:
2142 				litems.clear();
2143 				m.result = TRUE;
2144 				return;
2145 			
2146 			case LVM_DELETECOLUMN:
2147 				cols.removeAt(cast(int)m.wParam);
2148 				m.result = TRUE;
2149 				return;
2150 			
2151 			case LVM_DELETEITEM:
2152 				litems.removeAt(cast(int)m.wParam);
2153 				m.result = TRUE;
2154 				return;
2155 			
2156 			case LVM_INSERTCOLUMNA:
2157 			case LVM_INSERTCOLUMNW:
2158 				m.result = -1;
2159 				return;
2160 			
2161 			case LVM_INSERTITEMA:
2162 			case LVM_INSERTITEMW:
2163 				m.result = -1;
2164 				return;
2165 			
2166 			case LVM_SETBKCOLOR:
2167 				backColor = Color.fromRgb(cast(COLORREF)m.lParam);
2168 				m.result = TRUE;
2169 				return;
2170 			
2171 			case LVM_SETCALLBACKMASK:
2172 				m.result = FALSE;
2173 				return;
2174 			
2175 			case LVM_SETCOLUMNA:
2176 			case LVM_SETCOLUMNW:
2177 				m.result = FALSE;
2178 				return;
2179 			
2180 			case LVM_SETCOLUMNWIDTH:
2181 				return;
2182 			
2183 			case LVM_SETIMAGELIST:
2184 				m.result = cast(LRESULT)null;
2185 				return;
2186 			
2187 			case LVM_SETITEMA:
2188 				m.result = FALSE;
2189 				return;
2190 			
2191 			case LVM_SETITEMSTATE:
2192 				m.result = FALSE;
2193 				return;
2194 			
2195 			case LVM_SETITEMTEXTA:
2196 			case LVM_SETITEMTEXTW:
2197 				m.result = FALSE;
2198 				return;
2199 			
2200 			//case LVM_SETTEXTBKCOLOR:
2201 			
2202 			case LVM_SETTEXTCOLOR:
2203 				foreColor = Color.fromRgb(cast(COLORREF)m.lParam);
2204 				m.result = TRUE;
2205 				return;
2206 			
2207 			case LVM_SORTITEMS:
2208 				m.result = FALSE;
2209 				return;
2210 			
2211 			default:
2212 		}
2213 		super.wndProc(m);
2214 	}
2215 	
2216 	
2217 	protected override void onHandleCreated(EventArgs ea)
2218 	{
2219 		super.onHandleCreated(ea);
2220 		
2221 		//SendMessageA(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, wlvexstyle, wlvexstyle);
2222 		prevwproc(LVM_SETEXTENDEDLISTVIEWSTYLE, 0, wlvexstyle); // wparam=0 sets all.
2223 		
2224 		Color color;
2225 		COLORREF cref;
2226 		
2227 		color = backColor;
2228 		if(Color.empty == color)
2229 			cref = CLR_NONE;
2230 		else
2231 			cref = color.toRgb();
2232 		prevwproc(LVM_SETBKCOLOR, 0, cast(LPARAM)cref);
2233 		prevwproc(LVM_SETTEXTBKCOLOR, 0, cast(LPARAM)cref);
2234 		
2235 		//prevwproc(LVM_SETTEXTCOLOR, 0, foreColor.toRgb()); // DMD 0.125: cast(Control )(this).foreColor() is not an lvalue
2236 		color = foreColor;
2237 		prevwproc(LVM_SETTEXTCOLOR, 0, cast(LPARAM)color.toRgb());
2238 		
2239 		version(DFL_NO_IMAGELIST)
2240 		{
2241 		}
2242 		else
2243 		{
2244 			if(_lgimglist)
2245 				prevwproc(LVM_SETIMAGELIST, LVSIL_NORMAL, cast(LPARAM)_lgimglist.handle);
2246 			if(_smimglist)
2247 				prevwproc(LVM_SETIMAGELIST, LVSIL_SMALL, cast(LPARAM)_smimglist.handle);
2248 			//if(_stimglist)
2249 			//	prevwproc(LVM_SETIMAGELIST, LVSIL_STATE, cast(LPARAM)_stimglist.handle);
2250 		}
2251 		
2252 		cols.doListHeaders();
2253 		litems.doListItems();
2254 		
2255 		recalcEntire(); // Fix frame.
2256 	}
2257 	
2258 	
2259 	protected override void onReflectedMessage(ref Message m)
2260 	{
2261 		super.onReflectedMessage(m);
2262 		
2263 		switch(m.msg)
2264 		{
2265 			case WM_NOTIFY:
2266 				{
2267 					NMHDR* nmh;
2268 					nmh = cast(NMHDR*)m.lParam;
2269 					switch(nmh.code)
2270 					{
2271 						case LVN_GETDISPINFOA:
2272 							if(dfl.internal.utf.useUnicode)
2273 							{
2274 								break;
2275 							}
2276 							else
2277 							{
2278 								LV_DISPINFOA* lvdi;
2279 								lvdi = cast(LV_DISPINFOA*)nmh;
2280 								
2281 								// Note: might want to verify it's a valid ListViewItem.
2282 								
2283 								ListViewItem item;
2284 								item = cast(ListViewItem)cast(void*)lvdi.item.lParam;
2285 								
2286 								if(!lvdi.item.iSubItem) // Item.
2287 								{
2288 									version(DFL_NO_IMAGELIST)
2289 									{
2290 									}
2291 									else
2292 									{
2293 										if(lvdi.item.mask & LVIF_IMAGE)
2294 											lvdi.item.iImage = item._imgidx;
2295 									}
2296 									
2297 									if(lvdi.item.mask & LVIF_TEXT)
2298 										lvdi.item.pszText = cast(typeof(lvdi.item.pszText))item.calltxt.ansi;
2299 								}
2300 								else // Sub item.
2301 								{
2302 									if(lvdi.item.mask & LVIF_TEXT)
2303 									{
2304 										if(lvdi.item.iSubItem <= item.subItems.length)
2305 											lvdi.item.pszText = cast(typeof(lvdi.item.pszText))item.subItems[lvdi.item.iSubItem - 1].calltxt.ansi;
2306 									}
2307 								}
2308 								break;
2309 							}
2310 						
2311 						case LVN_GETDISPINFOW:
2312 							{
2313 								Dstring text;
2314 								LV_DISPINFOW* lvdi;
2315 								lvdi = cast(LV_DISPINFOW*)nmh;
2316 								
2317 								// Note: might want to verify it's a valid ListViewItem.
2318 								
2319 								ListViewItem item;
2320 								item = cast(ListViewItem)cast(void*)lvdi.item.lParam;
2321 								
2322 								if(!lvdi.item.iSubItem) // Item.
2323 								{
2324 									version(DFL_NO_IMAGELIST)
2325 									{
2326 									}
2327 									else
2328 									{
2329 										if(lvdi.item.mask & LVIF_IMAGE)
2330 											lvdi.item.iImage = item._imgidx;
2331 									}
2332 									
2333 									if(lvdi.item.mask & LVIF_TEXT)
2334 										lvdi.item.pszText = cast(typeof(lvdi.item.pszText))item.calltxt.unicode;
2335 								}
2336 								else // Sub item.
2337 								{
2338 									if(lvdi.item.mask & LVIF_TEXT)
2339 									{
2340 										if(lvdi.item.iSubItem <= item.subItems.length)
2341 											lvdi.item.pszText = cast(typeof(lvdi.item.pszText))item.subItems[lvdi.item.iSubItem - 1].calltxt.unicode;
2342 									}
2343 								}
2344 							}
2345 							break;
2346 						
2347 						/+
2348 						case LVN_ITEMCHANGING:
2349 							{
2350 								auto nmlv = cast(NM_LISTVIEW*)nmh;
2351 								if(-1 != nmlv.iItem)
2352 								{
2353 									UINT stchg = nmlv.uNewState ^ nmlv.uOldState;
2354 									if(stchg & (3 << 12))
2355 									{
2356 										// Note: not tested.
2357 										scope ItemCheckEventArgs ea = new ItemCheckEventArgs(nmlv.iItem,
2358 											(((nmlv.uNewState >> 12) & 3) - 1) ? CheckState.CHECKED : CheckState.UNCHECKED,
2359 											(((nmlv.uOldState >> 12) & 3) - 1) ? CheckState.CHECKED : CheckState.UNCHECKED);
2360 										onItemCheck(ea);
2361 									}
2362 								}
2363 							}
2364 							break;
2365 						+/
2366 						
2367 						case LVN_ITEMCHANGED:
2368 							{
2369 								auto nmlv = cast(NM_LISTVIEW*)nmh;
2370 								if(-1 != nmlv.iItem)
2371 								{
2372 									if(nmlv.uChanged & LVIF_STATE)
2373 									{
2374 										UINT stchg = nmlv.uNewState ^ nmlv.uOldState;
2375 										
2376 										//if(stchg & LVIS_SELECTED)
2377 										{
2378 											// Only fire for the selected one; don't fire twice for old/new.
2379 											if(nmlv.uNewState & LVIS_SELECTED)
2380 											{
2381 												onSelectedIndexChanged(EventArgs.empty);
2382 											}
2383 										}
2384 										
2385 										if(stchg & (3 << 12))
2386 										{
2387 											scope ItemCheckedEventArgs ea = new ItemCheckedEventArgs(items[nmlv.iItem]);
2388 											onItemChecked(ea);
2389 										}
2390 									}
2391 								}
2392 							}
2393 							break;
2394 						
2395 						case LVN_COLUMNCLICK:
2396 							{
2397 								auto nmlv = cast(NM_LISTVIEW*)nmh;
2398 								scope ccea = new ColumnClickEventArgs(nmlv.iSubItem);
2399 								onColumnClick(ccea);
2400 							}
2401 							break;
2402 						
2403 						case LVN_BEGINLABELEDITW:
2404 							goto begin_label_edit;
2405 						
2406 						case LVN_BEGINLABELEDITA:
2407 							if(dfl.internal.utf.useUnicode)
2408 								break;
2409 							begin_label_edit:
2410 							
2411 							{
2412 								LV_DISPINFOA* nmdi;
2413 								nmdi = cast(LV_DISPINFOA*)nmh;
2414 								if(nmdi.item.iSubItem)
2415 								{
2416 									m.result = TRUE;
2417 									break;
2418 								}
2419 								ListViewItem lvitem;
2420 								lvitem = cast(ListViewItem)cast(void*)nmdi.item.lParam;
2421 								scope LabelEditEventArgs leea = new LabelEditEventArgs(lvitem);
2422 								onBeforeLabelEdit(leea);
2423 								m.result = leea.cancelEdit;
2424 							}
2425 							break;
2426 						
2427 						case LVN_ENDLABELEDITW:
2428 							{
2429 								Dstring label;
2430 								LV_DISPINFOW* nmdi;
2431 								nmdi = cast(LV_DISPINFOW*)nmh;
2432 								if(nmdi.item.pszText)
2433 								{
2434 									ListViewItem lvitem;
2435 									lvitem = cast(ListViewItem)cast(void*)nmdi.item.lParam;
2436 									if(nmdi.item.iSubItem)
2437 									{
2438 										m.result = FALSE;
2439 										break;
2440 									}
2441 									label = fromUnicodez(nmdi.item.pszText);
2442 									scope LabelEditEventArgs nleea = new LabelEditEventArgs(lvitem, label);
2443 									onAfterLabelEdit(nleea);
2444 									if(nleea.cancelEdit)
2445 									{
2446 										m.result = FALSE;
2447 									}
2448 									else
2449 									{
2450 										// TODO: check if correct implementation.
2451 										// Update the lvitem's cached text..
2452 										lvitem.settextin(label);
2453 										
2454 										m.result = TRUE;
2455 									}
2456 								}
2457 							}
2458 							break;
2459 						
2460 						case LVN_ENDLABELEDITA:
2461 							if(dfl.internal.utf.useUnicode)
2462 							{
2463 								break;
2464 							}
2465 							else
2466 							{
2467 								Dstring label;
2468 								LV_DISPINFOA* nmdi;
2469 								nmdi = cast(LV_DISPINFOA*)nmh;
2470 								if(nmdi.item.pszText)
2471 								{
2472 									ListViewItem lvitem;
2473 									lvitem = cast(ListViewItem)cast(void*)nmdi.item.lParam;
2474 									if(nmdi.item.iSubItem)
2475 									{
2476 										m.result = FALSE;
2477 										break;
2478 									}
2479 									label = fromAnsiz(nmdi.item.pszText);
2480 									scope LabelEditEventArgs nleea = new LabelEditEventArgs(lvitem, label);
2481 									onAfterLabelEdit(nleea);
2482 									if(nleea.cancelEdit)
2483 									{
2484 										m.result = FALSE;
2485 									}
2486 									else
2487 									{
2488 										// TODO: check if correct implementation.
2489 										// Update the lvitem's cached text..
2490 										lvitem.settextin(label);
2491 										
2492 										m.result = TRUE;
2493 									}
2494 								}
2495 								break;
2496 							}
2497 						
2498 						default:
2499 					}
2500 				}
2501 				break;
2502 			
2503 			default:
2504 		}
2505 	}
2506 	
2507 	
2508 	private:
2509 	DWORD wlvexstyle = 0;
2510 	ListViewItemCollection litems;
2511 	ColumnHeaderCollection cols;
2512 	SelectedIndexCollection selidxcollection;
2513 	SelectedItemCollection selobjcollection;
2514 	SortOrder _sortorder = SortOrder.NONE;
2515 	CheckedIndexCollection checkedis;
2516 	int delegate(ListViewItem, ListViewItem) _sortproc;
2517 	version(DFL_NO_IMAGELIST)
2518 	{
2519 	}
2520 	else
2521 	{
2522 		ImageList _lgimglist, _smimglist;
2523 		//ImageList _stimglist;
2524 	}
2525 	
2526 	
2527 	int _defsortproc(ListViewItem a, ListViewItem b)
2528 	{
2529 		return a.opCmp(b);
2530 	}
2531 	
2532 	
2533 	DWORD _lvexstyle()
2534 	{
2535 		//if(created)
2536 		//	wlvexstyle = cast(DWORD)SendMessageA(hwnd, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
2537 		//	wlvexstyle = cast(DWORD)prevwproc(LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
2538 		return wlvexstyle;
2539 	}
2540 	
2541 	
2542 	void _lvexstyle(DWORD flags)
2543 	{
2544 		DWORD _b4;
2545 		_b4 = wlvexstyle;
2546 		
2547 		wlvexstyle = flags;
2548 		if(created)
2549 		{
2550 			// hwnd, msg, mask, flags
2551 			//SendMessageA(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, flags ^ _b4, wlvexstyle);
2552 			prevwproc(LVM_SETEXTENDEDLISTVIEWSTYLE, flags ^ _b4, wlvexstyle);
2553 			//redrawEntire(); // Need to recalc the frame ?
2554 		}
2555 	}
2556 	
2557 	
2558 	void _lvexstyle(DWORD mask, DWORD flags)
2559 	in
2560 	{
2561 		assert(mask);
2562 	}
2563 	body
2564 	{
2565 		wlvexstyle = (wlvexstyle & ~mask) | (flags & mask);
2566 		if(created)
2567 		{
2568 			// hwnd, msg, mask, flags
2569 			//SendMessageA(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, mask, flags);
2570 			prevwproc(LVM_SETEXTENDEDLISTVIEWSTYLE, mask, flags);
2571 			//redrawEntire(); // Need to recalc the frame ?
2572 		}
2573 	}
2574 	
2575 	
2576 	// If -subItemIndex- is 0 it's an item not a sub item.
2577 	// Returns the insertion index or -1 on failure.
2578 	package final LRESULT _ins(int index, LPARAM lparam, Dstring itemText, int subItemIndex, int imageIndex = -1)
2579 	in
2580 	{
2581 		assert(created);
2582 	}
2583 	body
2584 	{
2585 		/+
2586 		cprintf("^ Insert item:  index=%d, lparam=0x%X, text='%.*s', subItemIndex=%d\n",
2587 			index, lparam, itemText.length > 20 ? 20 : itemText.length, cast(char*)itemText, subItemIndex);
2588 		+/
2589 		
2590 		LV_ITEMA lvi;
2591 		lvi.mask = LVIF_TEXT | LVIF_PARAM;
2592 		version(DFL_NO_IMAGELIST)
2593 		{
2594 		}
2595 		else
2596 		{
2597 			//if(-1 != imageIndex)
2598 			if(!subItemIndex)
2599 				lvi.mask |= LVIF_IMAGE;
2600 			//lvi.iImage = imageIndex;
2601 			lvi.iImage = I_IMAGECALLBACK;
2602 		}
2603 		lvi.iItem = index;
2604 		lvi.iSubItem = subItemIndex;
2605 		//lvi.pszText = toStringz(itemText);
2606 		lvi.pszText = LPSTR_TEXTCALLBACKA;
2607 		lvi.lParam = lparam;
2608 		return prevwproc(LVM_INSERTITEMA, 0, cast(LPARAM)&lvi);
2609 	}
2610 	
2611 	
2612 	package final LRESULT _ins(int index, ListViewItem item)
2613 	{
2614 		//return _ins(index, cast(LPARAM)cast(void*)item, item.text, 0);
2615 		version(DFL_NO_IMAGELIST)
2616 		{
2617 			return _ins(index, cast(LPARAM)cast(void*)item, item.text, 0, -1);
2618 		}
2619 		else
2620 		{
2621 			return _ins(index, cast(LPARAM)cast(void*)item, item.text, 0, item._imgidx);
2622 		}
2623 	}
2624 	
2625 	
2626 	package final LRESULT _ins(int index, ListViewSubItem subItem, int subItemIndex)
2627 	in
2628 	{
2629 		assert(subItemIndex > 0);
2630 	}
2631 	body
2632 	{
2633 		return _ins(index, cast(LPARAM)cast(void*)subItem, subItem.text, subItemIndex);
2634 	}
2635 	
2636 	
2637 	package final LRESULT _ins(int index, ColumnHeader header)
2638 	{
2639 		// TODO: column inserted at index 0 can only be left aligned, so will need to
2640 		// insert a dummy column to change the alignment, then delete the dummy column.
2641 		
2642 		//LV_COLUMNA lvc;
2643 		LvColumn lvc;
2644 		lvc.mask = LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH;
2645 		switch(header.textAlign)
2646 		{
2647 			case HorizontalAlignment.RIGHT:
2648 				lvc.fmt = LVCFMT_RIGHT;
2649 				break;
2650 			
2651 			case HorizontalAlignment.CENTER:
2652 				lvc.fmt = LVCFMT_CENTER;
2653 				break;
2654 			
2655 			default:
2656 				lvc.fmt = LVCFMT_LEFT;
2657 		}
2658 		lvc.cx = header.width;
2659 		lvc.iSubItem = index; // iSubItem is probably only used when retrieving column info.
2660 		if(dfl.internal.utf.useUnicode)
2661 		{
2662 			lvc.lvcw.pszText = cast(typeof(lvc.lvcw.pszText))dfl.internal.utf.toUnicodez(header.text);
2663 			return prevwproc(LVM_INSERTCOLUMNW, cast(WPARAM)index, cast(LPARAM)&lvc.lvcw);
2664 		}
2665 		else
2666 		{
2667 			lvc.lvca.pszText = cast(typeof(lvc.lvca.pszText))dfl.internal.utf.toAnsiz(header.text);
2668 			return prevwproc(LVM_INSERTCOLUMNA, cast(WPARAM)index, cast(LPARAM)&lvc.lvca);
2669 		}
2670 	}
2671 	
2672 	
2673 	// If -subItemIndex- is 0 it's an item not a sub item.
2674 	// Returns FALSE on failure.
2675 	LRESULT updateItem(int index)
2676 	in
2677 	{
2678 		assert(created);
2679 	}
2680 	body
2681 	{
2682 		return prevwproc(LVM_REDRAWITEMS, cast(WPARAM)index, cast(LPARAM)index);
2683 	}
2684 	
2685 	LRESULT updateItem(ListViewItem item)
2686 	{
2687 		int index;
2688 		index = item.index;
2689 		assert(-1 != index);
2690 		return updateItem(index);
2691 	}
2692 	
2693 	
2694 	LRESULT updateItemText(int index, Dstring newText, int subItemIndex = 0)
2695 	{
2696 		return updateItem(index);
2697 	}
2698 	
2699 	LRESULT updateItemText(ListViewItem item, Dstring newText, int subItemIndex = 0)
2700 	{
2701 		return updateItem(item);
2702 	}
2703 	
2704 	
2705 	LRESULT updateColumnText(int colIndex, Dstring newText)
2706 	{
2707 		//LV_COLUMNA lvc;
2708 		LvColumn lvc;
2709 		
2710 		lvc.mask = LVCF_TEXT;
2711 		if(dfl.internal.utf.useUnicode)
2712 		{
2713 			lvc.lvcw.pszText = cast(typeof(lvc.lvcw.pszText))dfl.internal.utf.toUnicodez(newText);
2714 			return prevwproc(LVM_SETCOLUMNW, cast(WPARAM)colIndex, cast(LPARAM)&lvc.lvcw);
2715 		}
2716 		else
2717 		{
2718 			lvc.lvca.pszText = cast(typeof(lvc.lvca.pszText))dfl.internal.utf.toAnsiz(newText);
2719 			return prevwproc(LVM_SETCOLUMNA, cast(WPARAM)colIndex, cast(LPARAM)&lvc.lvca);
2720 		}
2721 	}
2722 	
2723 	
2724 	LRESULT updateColumnText(ColumnHeader col, Dstring newText)
2725 	{
2726 		int colIndex;
2727 		colIndex = columns.indexOf(col);
2728 		assert(-1 != colIndex);
2729 		return updateColumnText(colIndex, newText);
2730 	}
2731 	
2732 	
2733 	LRESULT updateColumnAlign(int colIndex, HorizontalAlignment halign)
2734 	{
2735 		LV_COLUMNA lvc;
2736 		lvc.mask = LVCF_FMT;
2737 		switch(halign)
2738 		{
2739 			case HorizontalAlignment.RIGHT:
2740 				lvc.fmt = LVCFMT_RIGHT;
2741 				break;
2742 			
2743 			case HorizontalAlignment.CENTER:
2744 				lvc.fmt = LVCFMT_CENTER;
2745 				break;
2746 			
2747 			default:
2748 				lvc.fmt = LVCFMT_LEFT;
2749 		}
2750 		return prevwproc(LVM_SETCOLUMNA, cast(WPARAM)colIndex, cast(LPARAM)&lvc);
2751 	}
2752 	
2753 	
2754 	LRESULT updateColumnAlign(ColumnHeader col, HorizontalAlignment halign)
2755 	{
2756 		int colIndex;
2757 		colIndex = columns.indexOf(col);
2758 		assert(-1 != colIndex);
2759 		return updateColumnAlign(colIndex, halign);
2760 	}
2761 	
2762 	
2763 	LRESULT updateColumnWidth(int colIndex, int w)
2764 	{
2765 		LV_COLUMNA lvc;
2766 		lvc.mask = LVCF_WIDTH;
2767 		lvc.cx = w;
2768 		return prevwproc(LVM_SETCOLUMNA, cast(WPARAM)colIndex, cast(LPARAM)&lvc);
2769 	}
2770 	
2771 	
2772 	LRESULT updateColumnWidth(ColumnHeader col, int w)
2773 	{
2774 		int colIndex;
2775 		colIndex = columns.indexOf(col);
2776 		assert(-1 != colIndex);
2777 		return updateColumnWidth(colIndex, w);
2778 	}
2779 	
2780 	
2781 	int getColumnWidth(int colIndex)
2782 	{
2783 		LV_COLUMNA lvc;
2784 		lvc.mask = LVCF_WIDTH;
2785 		lvc.cx = -1;
2786 		prevwproc(LVM_GETCOLUMNA, cast(WPARAM)colIndex, cast(LPARAM)&lvc);
2787 		return lvc.cx;
2788 	}
2789 	
2790 	
2791 	int getColumnWidth(ColumnHeader col)
2792 	{
2793 		int colIndex;
2794 		colIndex = columns.indexOf(col);
2795 		assert(-1 != colIndex);
2796 		return getColumnWidth(colIndex);
2797 	}
2798 	
2799 	
2800 	package:
2801 	final:
2802 	LRESULT prevwproc(UINT msg, WPARAM wparam, LPARAM lparam)
2803 	{
2804 		//return CallWindowProcA(listviewPrevWndProc, hwnd, msg, wparam, lparam);
2805 		return dfl.internal.utf.callWindowProc(listviewPrevWndProc, hwnd, msg, wparam, lparam);
2806 	}
2807 }
2808