PageRenderTime 35ms CodeModel.GetById 1ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 1ms

/lib/CListViewControl.ahk

http://github.com/Skiouros/Macro
AutoHotKey | 901 lines | 568 code | 9 blank | 324 comment | 139 complexity | 15f9f07d4f26f3e0f4345a64088563ac MD5 | raw file
  1/*
  2Class: CListViewControl
  3A ListView control. Additionally to its features present in AHK it can use sorting-independent indexing for synchronizing its fields with an array.
  4
  5This control extends <CControl>. All basic properties and functions are implemented and documented in this class.
  6*/
  7Class CListViewControl Extends CControl
  8{
  9	__New(Name, ByRef Options, Text, GUINum)
 10	{
 11		;~ global CGUI		
 12		Events := ["Click", "RightClick", "ItemActivated", "MouseLeave", "EditingStart", "FocusReceived", "FocusLost", "ItemSelected", "ItemDeselected", "ItemFocused", "ItemDefocused", "ItemChecked", " ItemUnChecked", "SelectionChanged", "CheckedChanged", "FocusedChanged", "KeyPress", "Marquee", "ScrollingStart", "ScrollingEnd"]
 13		if(!InStr(Options, "AltSubmit")) ;Automagically add AltSubmit when necessary
 14		{
 15			for index, function in Events
 16			{
 17				if(IsFunc(CGUI.GUIList[GUINum][Name "_" function]))
 18				{
 19					Options .= " AltSubmit"
 20					break
 21				}
 22			}
 23		}
 24		base.__New(Name, Options, Text, GUINum)
 25		this._.Insert("ControlStyles", {ReadOnly : -0x200, Header : -0x4000, NoSortHdr : 0x8000, AlwaysShowSelection : 0x8, Multi : -0x4, Sort : 0x10, SortDescending : 0x20})
 26		this._.Insert("ControlExStyles", {Checked : 0x4, FullRowSelect : 0x20, Grid : 0x1, AllowHeaderReordering : 0x10, HotTrack : 0x8})
 27		this._.Insert("Events", ["DoubleClick", "DoubleRightClick", "ColumnClick", "EditingEnd", "Click", "RightClick", "ItemActivate", "EditingStart", "KeyPress", "FocusReceived", "FocusLost", "Marquee", "ScrollingStart", "ScrollingEnd", "ItemSelected", "ItemDeselected", "ItemFocused", "ItemDefocused", "ItemChecked", "ItemUnChecked", "SelectionChanged", "CheckedChanged", "FocusedChanged"])
 28		this._.Insert("Messages", {0x004E : "Notify"}) ;This control uses WM_NOTIFY with NM_SETFOCUS and NM_KILLFOCUS
 29		this.Type := "ListView"
 30	}
 31	
 32	PostCreate()
 33	{
 34		Base.PostCreate()
 35		this._.Insert("ImageListManager", new this.CImageListManager(this.GUINum, this.hwnd))
 36		this._.Insert("Items", new this.CItems(this.GUINum, this.hwnd))
 37	}
 38	/*
 39	Function: ModifyCol
 40	Modifies a column. See AHK help on LV_ModifyCol for details.
 41	*/
 42	ModifyCol(ColumnNumber="", Options="", ColumnTitle="")
 43	{
 44		;~ global CGUI
 45		if(CGUI.GUIList[this.GUINum].IsDestroyed)
 46			return
 47		Gui, % this.GUINum ":Default"
 48		Gui, ListView, % this.ClassNN
 49		LV_ModifyCol(ColumnNumber, Options, ColumnTitle)
 50	}
 51	/*
 52	Function: InsertCol
 53	Inserts a column. See AHK help on LV_InsertCol for details.
 54	*/
 55	InsertCol(ColumnNumber, Options="", ColumnTitle="")
 56	{
 57		;~ global CGUI
 58		if(CGUI.GUIList[this.GUINum].IsDestroyed)
 59			return
 60		Gui, % this.GUINum ":Default"
 61		Gui, ListView, % this.ClassNN
 62		LV_InsertCol(ColumnNumber, Options, ColumnTitle)
 63	}
 64	/*
 65	Function: DeleteCol
 66	Deletes a column. See AHK help on LV_DeleteCol for details.
 67	*/
 68	DeleteCol(ColumnNumber)
 69	{
 70		;~ global CGUI
 71		if(CGUI.GUIList[this.GUINum].IsDestroyed)
 72			return
 73		Gui, % this.GUINum ":Default"
 74		Gui, ListView, % this.ClassNN
 75		LV_DeleteCol(ColumnNumber)
 76	}
 77	/*
 78	Variable: Items
 79	An array of all ListView rows. See <CListViewControl.CItems>.
 80	
 81	Variable: SelectedItem
 82	Contains the (first) selected row.
 83	
 84	Variable: SelectedItems
 85	Contains all selected rows.
 86	
 87	Variable: SelectedIndex
 88	Contains the index of the (first) selected row.
 89	
 90	Variable: SelectedIndices
 91	Contains all indices of the selected rows.
 92	
 93	Variable: CheckedItem
 94	Contains the (first) checked row.
 95	
 96	Variable: CheckedItems
 97	Contains all checked rows.
 98	
 99	Variable: CheckedIndex
100	Contains the index of the (first) checked row.
101	
102	Variable: CheckedIndices
103	Contains all indices of the checked rows.
104	
105	Variable: FocusedItem
106	Contains the focused row.
107	
108	Variable: FocusedIndex
109	Contains the index of the focused row.
110	
111	Variable: IndependentSorting
112	This setting is off by default. In this case, indexing the rows behaves like AHK ListViews usually do. 
113	If it is enabled however, the row indexing will be independent of the current sorting. 
114	That means that the first row can actually be displayed as the second, third,... or last row on the GUI. 
115	This feature is very useful if you need to synchronize an array with the data in the ListView 
116	because the index of the array can then be directly mapped to the ListView row index. 
117	This would not be possible if this option was off and the ListView gets sorted differently.
118	*/
119	__Get(Name, Params*)
120	{
121		;~ global CGUI
122		if(Name != "GUINum" && !CGUI.GUIList[this.GUINum].IsDestroyed)
123		{
124			DetectHidden := A_DetectHiddenWindows
125			DetectHiddenWindows, On
126			if(Name = "Items")
127				Value := this._.Items
128			else if(Name = "SelectedIndices" || Name = "SelectedItems" || Name = "CheckedIndices" || Name = "CheckedItems")
129			{
130				Gui, % this.GUINum ":Default"
131				Gui, ListView, % this.ClassNN
132				Value := []
133				Loop % this.Items.Count
134					if(LV_GetNext(A_Index - 1, InStr(Name, "Checked") ? "Checked" : "") = A_Index)
135					{
136						Index := (this._.Items.IndependentSorting ? this.CItems.CRow.GetUnsortedIndex(A_Index, this.hwnd) : A_Index)
137						Value.Insert(InStr(Name, "Indices") ? Index : this._.Items[Index]) ;new this.CItems.CRow(this.CItems.GetSortedIndex(A_Index, this.hwnd), this.GUINum, this.Name))
138					}
139			}
140			else if(Name = "SelectedIndex" || Name = "SelectedItem" || Name = "CheckedIndex" || Name = "CheckedItem")
141			{
142				Gui, % this.GUINum ":Default"
143				Gui, ListView, % this.ClassNN
144				Loop % this.Items.Count
145					if(LV_GetNext(A_Index - 1, InStr(Name, "Checked") ? "Checked" : "") = A_Index)
146					{
147						Index := (this._.Items.IndependentSorting ? this.CItems.CRow.GetUnsortedIndex(A_Index, this.hwnd) : A_Index)
148						Value := InStr(Name, "Index") ? Index : this._.Items[Index] ;new this.CItems.CRow(this.CItems.GetSortedIndex(A_Index, this.hwnd), this.GUINum, this.Name))
149						break
150					}
151			}
152			else if(Name = "FocusedItem" || Name = "FocusedIndex")
153			{
154				Gui, % this.GUINum ":Default"
155				Gui, ListView, % this.ClassNN
156				Value := LV_GetNext(0, "Focused")
157				if(this._.Items.IndependentSorting)
158					Value := this.CItems.CRow.GetUnsortedIndex(Value, this.hwnd)
159				if(Name = "FocusedItem")
160					Value := this._.Items[Value] ;new this.CItems.CRow(Value, this.GUINum, this.Name)
161			}
162			else if(Name = "IndependentSorting")
163				Value := this._.Items.IndependentSorting
164			Loop % Params.MaxIndex()
165				if(IsObject(Value)) ;Fix unlucky multi parameter __GET
166					Value := Value[Params[A_Index]]
167			if(!DetectHidden)
168				DetectHiddenWindows, Off
169			if(Value != "")
170				return Value
171		}
172	}
173	__Set(Name, Params*)
174	{
175		;~ global CGUI
176		if(!CGUI.GUIList[this.GUINum].IsDestroyed)
177		{
178			DetectHidden := A_DetectHiddenWindows
179			DetectHiddenWindows, On
180			Handled := true
181			Value := Params[Params.MaxIndex()]
182			Params.Remove(Params.MaxIndex())
183			if(Name = "SelectedIndices" || Name = "CheckedIndices")
184			{
185				Gui, % this.GUINum ":Default"
186				Gui, ListView, % this.ClassNN
187				Indices := Value
188				if(!IsObject(Value))
189				{
190					Indices := Array()
191					Loop, Parse, Value,|
192						if A_LoopField is Integer
193							Indices.Insert(A_LoopField)
194				}
195				LV_Modify(0, Name = "SelectedIndices" ? "-Select" : "-Check")
196				Loop % Indices.MaxIndex()
197					if(Indices[A_Index] > 0)
198						LV_Modify(this._.Items.IndependentSorting ? this.CItems.CRow.GetSortedIndex(Indices[A_Index], this.hwnd) : Indices[A_Index], Name = "SelectedIndices" ? "Select" : "Check")
199				if(Name = "SelectedIndices")
200				{
201					if(LV_GetCount("Selected") = 1)
202					{
203						this.ProcessSubControlState(this._.PreviouslySelectedItem, this.SelectedItem)
204						this._.PreviouslySelectedItem := this.SelectedItem
205					}
206					else
207					{
208						this.ProcessSubControlState(this._.PreviouslySelectedItem, "")
209						this._.PreviouslySelectedItem := ""
210					}
211				}
212			}
213			else if(Name = "SelectedIndex" || Name = "CheckedIndex")
214			{
215				Gui, % this.GUINum ":Default"
216				Gui, ListView, % this.ClassNN
217				LV_Modify(0, Name = "SelectedIndex" ? "-Select" : "-Check")
218				if(Value > 0)
219				{
220					LV_Modify(this._.Items.IndependentSorting ? this.CItems.CRow.GetSortedIndex(Value, this.hwnd) : Value, Name = "SelectedIndex" ? "Select" : "Check")
221					if(Name = "SelectedIndex")
222					{
223						if(LV_GetCount("Selected") = 1)
224						{
225							this.ProcessSubControlState(this._.PreviouslySelectedItem, this.SelectedItem)
226							this._.PreviouslySelectedItem := this.SelectedItem
227						}
228						else
229						{
230							this.ProcessSubControlState(this._.PreviouslySelectedItem, "")
231							this._.PreviouslySelectedItem := ""
232						}
233					}
234				}
235			}
236			else if(Name = "FocusedIndex")
237			{
238				Gui, % this.GUINum ":Default"
239				Gui, ListView, % this.ClassNN
240				LV_Modify(this._.Items.IndependentSorting ? this.CItems.CRow.GetSortedIndex(Value, this.hwnd) : Value, "Focused")
241			}
242			else if(Name = "Items" && IsObject(Value) && IsObject(this._.Items) && Params.MaxIndex() > 0)
243			{
244				Items := this._.Items
245				Items[Params*] := Value
246			}
247			else if(Name = "IndependentSorting")
248				this._.Items.IndependentSorting := Value
249			else if(Name = "Items")
250				Value := 0
251			else
252				Handled := false
253			if(!DetectHidden)
254				DetectHiddenWindows, Off
255			if(Handled)
256				return Value
257		}
258	}
259	
260	/*
261	Class: CListViewControl.CItems
262	An array of all ListView rows.
263	*/
264	Class CItems
265	{
266		__New(GUINum, hwnd)
267		{
268			this._Insert("_", {})
269			this._.GUINum := GUINum
270			this._.hwnd := hwnd
271		}
272		_NewEnum()
273		{
274			;~ global CEnumerator
275			return new CEnumerator(this)
276		}
277		/*
278		Function: MaxIndex
279		Returns the number of rows.
280		*/
281		MaxIndex()
282		{
283			;~ global CGUI
284			GUI := CGUI.GUIList[this._.GUINum]
285			if(GUI.IsDestroyed)
286				return
287			Control := GUI.Controls[this._.hwnd]
288			Gui, % this._.GUINum ":Default"
289			Gui, ListView, % Control.ClassNN
290			return LV_GetCount()
291		}
292		/*
293		Function: Add
294		Adds a row.
295		
296		Parameters:
297			Options - Options for the new row. See AHK documentation on LV_Add().
298			Fields - Any additional parameters are used as cell text.
299		*/
300		Add(Options, Fields*)
301		{
302			;~ global CGUI
303			GUI := CGUI.GUIList[this._.GUINum]
304			if(GUI.IsDestroyed)
305				return
306			Control := GUI.Controls[this._.hwnd]
307			Gui, % Control.GUINum ":Default"
308			Gui, ListView, % Control.ClassNN
309			SortedIndex := LV_Add(Options, Fields*)
310			UnsortedIndex := LV_GetCount()
311			this._.Insert(UnsortedIndex, new this.CRow(SortedIndex, UnsortedIndex, this._.GUINum, Control.hwnd))
312			if(InStr(Options, "Select"))
313			{
314				if(LV_GetCount("Selected") = 1)
315				{
316					Control.ProcessSubControlState(Control._.PreviouslySelectedItem, Control.SelectedItem)
317					Control._.PreviouslySelectedItem := Control.SelectedItem
318				}
319				else
320				{
321					Control.ProcessSubControlState(Control._.PreviouslySelectedItem, "")
322					Control._.PreviouslySelectedItem := ""
323				}
324			}
325		}
326		/*
327		Function: Insert
328		Inserts a row.
329		
330		Parameters:
331			RowNumber - Index before which the row is inserted.
332			Options - Options for the new row. See AHK documentation on LV_Add().
333			Fields - Any additional parameters are used as cell text.
334		*/
335		Insert(RowNumber, Options, Fields*)
336		{
337			;~ global CGUI
338			GUI := CGUI.GUIList[this._.GUINum]
339			if(GUI.IsDestroyed)
340				return
341			Control := GUI.Controls[this._.hwnd]
342			Gui, % Control.GUINum ":Default"
343			Gui, ListView, % Control.ClassNN
344			SortedIndex := this.IndependentSorting ? this.CRow.GetSortedIndex(RowNumber, Control.hwnd) : RowNumber ;If independent sorting is off, the RowNumber parameter of this function is interpreted as sorted index
345			if(SortedIndex = -1 || SortedIndex > LV_GetCount())
346				SortedIndex := LV_GetCount() + 1
347			
348			UnsortedIndex := this.CRow.GetUnsortedIndex(SortedIndex, Control.hwnd)
349			
350			;move all unsorted indices >= the insertion point up by one to make place for the insertion
351			Loop % LV_GetCount() - UnsortedIndex + 1
352			{
353				index := LV_GetCount() - A_Index + 1 ;loop backwards
354				sIndex := this.CRow.GetSortedIndex(index, Control.hwnd) - 1
355				this.CRow.SetUnsortedIndex(sIndex, index + 1, Control.hwnd)
356			}
357			
358			SortedIndex := LV_Insert(SortedIndex, Options, Fields*)
359			this._.Insert(UnsortedIndex, new this.CRow(SortedIndex, UnsortedIndex, this._.GUINum, Control.hwnd))
360			if(InStr(Options, "Select"))
361			{
362				if(LV_GetCount("Selected") = 1)
363				{
364					Control.ProcessSubControlState(Control._.PreviouslySelectedItem, Control.SelectedItem)
365					Control._.PreviouslySelectedItem := Control.SelectedItem
366				}
367				else
368				{
369					Control.ProcessSubControlState(Control._.PreviouslySelectedItem, "")
370					Control._.PreviouslySelectedItem := ""
371				}
372			}
373		}	
374		
375		/*
376		Function: Modify
377		Modifies a row.
378		
379		Parameters:
380			RowNumber - Index of the row which should be modified.
381			Options - Options for the modified row. See AHK documentation on LV_Modify().
382			Fields - Any additional parameters are used as cell text.
383		*/
384		Modify(RowNumber, Options, Fields*)
385		{
386			;~ global CGUI
387			GUI := CGUI.GUIList[this._.GUINum]
388			if(GUI.IsDestroyed)
389				return
390			Control := GUI.Controls[this._.hwnd]
391			Gui, % Control.GUINum ":Default"
392			Gui, ListView, % Control.ClassNN
393			SortedIndex := this.IndependentSorting ? this.CRow.GetSortedIndex(RowNumber, Control.hwnd) : RowNumber ;If independent sorting is off, the RowNumber parameter of this function is interpreted as sorted index
394			LV_Modify(SortedIndex, Options, Fields*)
395			if(InStr(Options, "Select"))
396			{
397				if(LV_GetCount("Selected") = 1)
398				{
399					Control.ProcessSubControlState(Control._.PreviouslySelectedItem, Control.SelectedItem)
400					Control._.PreviouslySelectedItem := Control.SelectedItem
401				}
402				else
403				{
404					Control.ProcessSubControlState(Control._.PreviouslySelectedItem, "")
405					Control._.PreviouslySelectedItem := ""
406				}
407			}
408		}
409		
410		/*
411		Function: Clear
412		Clears the ListView by deleting all rows.
413		*/
414		Clear()
415		{
416			Loop % this.MaxIndex()
417				this.Delete(1)
418		}
419		/*
420		Function: Delete
421		Deletes a row.
422		
423		Parameters:
424			RowNumber - Index of the row which should be deleted.
425		*/
426		Delete(RowNumber)
427		{
428			;~ global CGUI
429			GUI := CGUI.GUIList[this._.GUINum]
430			if(GUI.IsDestroyed)
431				return
432			Control := GUI.Controls[this._.hwnd]
433			Gui, % Control.GUINum ":Default"
434			Gui, ListView, % Control.ClassNN
435			WasSelected := Control.Items[RowNumber].Selected
436			SortedIndex := this.IndependentSorting ? this.CRow.GetSortedIndex(RowNumber, Control.hwnd) : RowNumber ;If independent sorting is off, the RowNumber parameter of this function is interpreted as sorted index
437			UnsortedIndex := this.CRow.GetUnsortedIndex(SortedIndex, Control.hwnd)
438			;Decrease the unsorted indices after the deletion index by one
439			Loop % LV_GetCount() - UnsortedIndex
440				this.CRow.SetUnsortedIndex(this.CRow.GetSortedIndex(UnsortedIndex + A_Index, Control.hwnd), UnsortedIndex + A_Index - 1, Control.hwnd)
441			LV_Delete(SortedIndex)
442			if(WasSelected)
443			{
444				if(LV_GetCount("Selected") = 1)
445				{
446					Control.ProcessSubControlState(Control._.PreviouslySelectedItem, Control.SelectedItem)
447					Control._.PreviouslySelectedItem := Control.SelectedItem
448				}
449				else
450				{
451					Control.ProcessSubControlState(Control._.PreviouslySelectedItem, "")
452					Control._.PreviouslySelectedItem := ""
453				}
454			}
455		}
456		/*
457		Variable: 1,2,3,4,...
458		Rows can be accessed by their index, e.g. this.ListView.Items[1][2] accesses the text of the first row and second column.
459		
460		Variable: Count
461		The number of rows.
462		*/
463		__Get(Name)
464		{
465			;~ global CGUI
466			if(Name != "_")
467			{
468				GUI := CGUI.GUIList[this._.GUINum]
469				if(GUI.IsDestroyed)
470					return
471				Control := GUI.Controls[this._.hwnd]
472				if Name is Integer
473				{
474					if(Name > 0 && Name <= this.Count)
475						return this._[this.IndependentSorting ? Name : this.CRow.GetUnsortedIndex(Name, Control.hwnd)]
476				}
477				else if(Name = "Count")
478					return this.MaxIndex()
479			}
480		}
481		__Set(Name, Params*)
482		{
483			;~ global CGUI
484			GUI := CGUI.GUIList[this._.GUINum]
485			if(GUI.IsDestroyed)
486				return
487			Value := Params[Params.MaxIndex()]
488			Params.Remove(Params.MaxIndex())
489			if Name is Integer
490			{
491				if(!Params.MaxIndex()) ;Setting a row directly is not allowed
492					return
493				else ;Set a column or other row property
494				{			
495					Row := this[Name]
496					Row[Params*] := Value
497					return
498				}
499			}
500		}
501		
502		/*
503		Class: CListViewControl.CItems.CRow
504		A single row of a ListView control.
505		CRow uses the unsorted row numbers internally, but it can switch to sorted row numbers depending on the setting of the ListView.
506		*/
507		Class CRow
508		{
509			__New(SortedIndex, UnsortedIndex, GUINum, hwnd)
510			{
511				;~ global CGUI
512				this.Insert("_", {})				
513				this._.RowNumber := UnsortedIndex
514				this._.GUINum := GUINum
515				this._.hwnd := hwnd
516				GUI := CGUI.GUIList[GUINum]
517				if(GUI.IsDestroyed)
518					return
519				Control := GUI.Controls[hwnd]				
520				;Store the real unsorted index in the custom property lParam field of the list view item so it can be reidentified later
521				this.SetUnsortedIndex(SortedIndex, UnsortedIndex, Control.hwnd)
522				this.SetIcon("")
523				this._.Insert("Controls", {})
524			}
525			/*
526			Function: AddControl
527			Adds a control to this item that will be visible/enabled only when this item is selected. The parameters correspond to the Add() function of CGUI.
528			
529			Parameters:
530				Type - The type of the control.
531				Name - The name of the control.
532				Options - Options used for creating the control.
533				Text - The text of the control.
534				UseEnabledState - If true, the control will be enabled/disabled instead of visible/hidden.
535			*/
536			AddControl(type, Name, Options, Text, UseEnabledState = 0)
537			{
538				;~ global CGUI
539				GUI := CGUI.GUIList[this._.GUINum]
540				if(!this.Selected)
541					Options .= UseEnabledState ? " Disabled" : " Hidden"
542				Control := GUI.AddControl(type, Name, Options, Text, this._.Controls)
543				Control._.UseEnabledState := UseEnabledState
544				Control.hParentControl := this._.hwnd
545				return Control
546			}
547			/*
548			typedef struct {
549			  UINT   mask;
550			  int    iItem;
551			  int    iSubItem;
552			  UINT   state;
553			  UINT   stateMask;
554			  LPTSTR pszText;
555			  int    cchTextMax;
556			  int    iImage;
557			  LPARAM lParam;
558			#if (_WIN32_IE >= 0x0300)
559			  int    iIndent;
560			#endif 
561			#if (_WIN32_WINNT >= 0x0501)
562			  int    iGroupId;
563			  UINT   cColumns;
564			  UINT   puColumns;
565			#endif 
566			#if (_WIN32_WINNT >= 0x0600)
567			  int    piColFmt;
568			  int    iGroup;
569			#endif 
570			} LVITEM, *LPLVITEM;
571			*/
572			SetUnsortedIndex(SortedIndex, lParam, hwnd)
573			{
574				;~ if(!this.IndependentSorting)
575					;~ return
576				VarSetCapacity(LVITEM, 13*4 + 2 * A_PtrSize, 0)
577				mask := 0x4   ; LVIF_PARAM := 0x4
578				NumPut(mask, LVITEM, 0, "UInt") 
579				NumPut(SortedIndex - 1, LVITEM, 4, "Int")   ; iItem 
580				NumPut(lParam, LVITEM, 7*4 + A_PtrSize, "PTR")
581				;~ string := this.hex(LVITEM,  "UINT|INT|INT|UINT|UINT|PTR|INT|INT|PTR|INT|INT|UINT|UINT|INT|INT")
582				SendMessage, (A_IsUnicode ? 0x1000 + 76 : 0x1000 + 6), 0, &LVITEM,, % "ahk_id " hwnd ;LVM_SETITEM
583				;~ result := errorlevel
584				;~ result := DllCall("SendMessage", "PTR", hwnd, "UInt", LVM_SETITEM := (A_IsUnicode ? 0x1000 + 76 : 0x1000 + 6), "PTR", 0, "PTRP", LVITEM, "PTR")
585				;~ lParam2 := this.GetUnsortedIndex(RowNumber, hwnd)
586				return ErrorLevel
587			}
588			;Returns the sorted index (by which AHK usually accesses listviews) by searching for a custom index that is independent of sorting
589			/*
590			typedef struct tagLVFINDINFO {
591			  UINT    flags; 4
592			  LPCTSTR psz; 4-8
593			  LPARAM  lParam; 4- 8
594			  POINT   pt; 8
595			  UINT    vkDirection; 4
596			} LVFINDINFO, *LPFINDINFO;
597			*/
598			GetSortedIndex(UnsortedIndex, hwnd)
599			{
600				;~ if(!this.IndependentSorting)
601					;~ return UnsortedIndex
602				;Create the LVFINDINFO structure
603				VarSetCapacity(LVFINDINFO, 4*4 + 2 * A_PtrSize, 0)
604				mask := 0x1   ; LVFI_PARAM := 0x1
605				NumPut(mask, LVFINDINFO, 0, "UInt") 
606				NumPut(UnsortedIndex, LVFINDINFO, 4 + A_PtrSize, "PTR")
607				;~ string := hex(LVFINDINFO,  "UINT|INT|INT|UINT|UINT|PTR|INT|INT|PTR|INT|INT|UINT|UINT|INT|INT")
608				SendMessage, (A_IsUnicode ? 0x1000 + 83 : 0x1000 + 13), -1, &LVFINDINFO,, % "ahk_id " hwnd ;LVM_FINDITEM
609				;~ MsgReply := ErrorLevel > 0x7FFFFFFF ? -(~ErrorLevel) - 1 : ErrorLevel
610				;~ result := DllCall("SendMessage", "PTR", hwnd, "UInt", LVM_FINDITEM := (A_IsUnicode ? 0x1000 + 83 : 0x1000 + 13), "PTR", -1, "UIntP", LVITEM, "PTR") + 1
611				return ErrorLevel + 1
612			}
613			GetUnsortedIndex(SortedIndex, hwnd)
614			{
615				;~ if(!this.IndependentSorting)
616					;~ return SortedIndex
617				VarSetCapacity(LVITEM, 13*4 + 2 * A_PtrSize, 0)
618				mask := 0x4   ; LVIF_PARAM := 0x4
619				NumPut(mask, LVITEM, 0, "UInt") 
620				NumPut(SortedIndex - 1, LVITEM, 4, "Int")   ; iItem 
621				;~ NumPut(lParam, LVITEM, 7*4 + A_PtrSize, "PTR")
622				;~ string := this.hex(LVITEM,  "UINT|INT|INT|UINT|UINT|PTR|INT|INT|PTR|INT|INT|UINT|UINT|INT|INT")
623				SendMessage, (A_IsUnicode ? 0x1000 + 75 : 0x1000 + 5), 0, &LVITEM,,% "ahk_id " hwnd ;LVM_GETITEM
624				;~ result := errorlevel
625				;~ result := DllCall("SendMessage", "PTR", hwnd, "UInt", LVM_GETITEM := (A_IsUnicode ? 0x1000 + 75 : 0x1000 + 5), "PTR", 0, "PTRP", LVITEM, "PTR")
626				;~ string := this.hex(LVITEM,  "UINT|INT|INT|UINT|UINT|PTR|INT|INT|PTR|INT|INT|UINT|UINT|INT|INT")
627				UnsortedIndex := NumGet(LVITEM, 7*4 + A_PtrSize, "PTR")
628				return UnsortedIndex
629			}
630			_NewEnum()
631			{
632				;~ global CEnumerator
633				return new CEnumerator(this)
634			}
635			/*
636			Function: MaxIndex
637			Returns the number of columns.
638			*/
639			MaxIndex()
640			{				
641				;~ global CGUI
642				GUI := CGUI.GUIList[this._.GUINum]
643				if(GUI.IsDestroyed)
644					return
645				Control := GUI.Controls[this._.hwnd]
646				Gui, % Control.GUINum ":Default"
647				Gui, ListView, % Control.ClassNN
648				Return LV_GetCount("Column")
649			}
650			/*
651			Function: SetIcon
652			Sets the icon of a ListView row
653			
654			Parameters:
655				Filename - The filename of the file containing the icon.
656				IconNumberOrTransparencyColor - The icon number or the transparency color if the used file has no transparency support.
657			*/
658			SetIcon(Filename, IconNumberOrTransparencyColor = 1)
659			{
660				;~ global CGUI
661				GUI := CGUI.GUIList[this._.GUINum]
662				if(GUI.IsDestroyed)
663					return
664				Control := GUI.Controls[this._.hwnd]
665				Control._.ImageListManager.SetIcon(this.GetSortedIndex(this._.RowNumber, Control.hwnd), Filename, IconNumberOrTransparencyColor)
666				this._.Icon := Filename
667				this._.IconNumber := IconNumberOrTransparencyColor
668			}
669			/*
670			Variable: 1,2,3,4,...
671			Columns can be accessed by their index, e.g. this.ListView.Items[1][2] accesses the text of the first row and second column.
672			
673			Variable: Text
674			The text of the first column of this row.
675			
676			Variable: Count
677			The number of columns.
678			
679			Variable: Checked
680			True if the row is checked.
681			
682			Variable: Selected
683			True if the row is selected.
684			
685			Variable: Focused
686			True if the row is foucsed.
687			
688			Variable: Icon
689			The filename of the file containing the icon for the current row.
690			
691			Variable: IconNumber
692			The number of the icon in a multi-icon file.
693			*/
694			__Get(Name)
695			{
696				;~ global CGUI				
697				GUI := CGUI.GUIList[this._.GUINum]
698				if(!GUI.IsDestroyed)
699				{
700					Control := GUI.Controls[this._.hwnd]
701					if Name is Integer
702					{
703						if(Name > 0 && Name <= this.Count) ;Setting default listview is already done by this.Count __Get
704						{
705							LV_GetText(value, this.GetSortedIndex(this._.RowNumber, Control.hwnd), Name)
706							return value
707						}
708					}
709					else if(Name = "Text")
710						return this[1]
711					else if(Name = "Count")
712					{
713						Gui, % Control.GUINum ":Default"
714						Gui, ListView, % Control.ClassNN
715						Return LV_GetCount("Column")
716					}
717					else if Name in Checked,Focused,Selected
718					{
719						Value := {Checked : "Checked", Focused : "Focused", Selected : ""}[Name]
720						Gui, % Control.GUINum ":Default"
721						Gui, ListView, % Control.ClassNN
722						return this.GetUnsortedIndex(LV_GetNext(this.GetSortedIndex(this._.RowNumber, Control.hwnd) - 1, Value), Control.hwnd) = this._.RowNumber
723					}
724					else if(Name = "Icon" || Name = "IconNumber")
725						return this._[Name]
726					else if(Name = "Controls")
727						return this._.Controls
728				}
729			}
730			__Set(Name, Value)
731			{				
732				;~ global CGUI
733				GUI := CGUI.GUIList[this._.GUINum]
734				if(!GUI.IsDestroyed)
735				{
736					Control := GUI.Controls[this._.hwnd]
737					if Name is Integer
738					{
739						if(Name <= this.Count) ;Setting default listview is already done by this.Count __Get
740							LV_Modify(this.GetSortedIndex(this._.RowNumber, Control.hwnd), "Col" Name, Value)
741						return Value
742					}
743					else if(Key := {Checked : "Check", Focused : "Focus", "Selected" : ""}[Name])
744					{
745						Gui, % Control.GUINum ":Default"
746						Gui, ListView, % Control.ClassNN
747						LV_Modify(this.GetSortedIndex(this._.RowNumber, Control.hwnd), (Value = 0 ? "-" : "") Key)
748						if(Name = "Selected")
749						{							
750							if(LV_GetCount("Selected") = 1)
751							{
752								Control.ProcessSubControlState(Control._.PreviouslySelectedItem, Control.SelectedItem)
753								Control._.PreviouslySelectedItem := Control.SelectedItem
754							}
755							else
756							{
757								Control.ProcessSubControlState(Control._.PreviouslySelectedItem, "")
758								Control._.PreviouslySelectedItem := ""
759							}
760						}
761						return Value
762					}
763					else if(Name = "Icon")
764					{
765						this.SetIcon(Value, this._.HasKey("IconNumber") ? this._.IconNumber : 1)
766						return Value
767					}
768					else if(Name = "IconNumber")
769					{
770						this._.IconNumber := Value
771						if(this._.Icon)
772							this.SetIcon(this._.Icon, Value)
773						return Value
774					}
775				}
776			}
777		}
778	}
779	
780	/*
781	Event: Introduction
782	To handle control events you need to create a function with this naming scheme in your window class: ControlName_EventName(params)
783	The parameters depend on the event and there may not be params at all in some cases.
784	Additionally it is required to create a label with this naming scheme: GUIName_ControlName
785	GUIName is the name of the window class that extends CGUI. The label simply needs to call CGUI.HandleEvent(). 
786	For better readability labels may be chained since they all execute the same code.
787	Instead of using ControlName_EventName() you may also call <CControl.RegisterEvent> on a control instance to register a different event function name.
788	
789	Event: Click(RowItem)
790	Invoked when the user clicked on the control.
791	
792	Event: DoubleClick(RowItem)
793	Invoked when the user double-clicked on the control.
794	
795	Event: RightClick(RowItem)
796	Invoked when the user right-clicked on the control.
797	
798	Event: DoubleRightClick(RowItem)
799	Invoked when the user double-right-clicked on the control.
800	
801	Event: ColumnClick(ColumnIndex)
802	Invoked when the user clicked on a column header.
803	
804	Event: EditingStart(RowItem)
805	Invoked when the user started editing the first cell of a row.
806	
807	Event: EditingEnd(RowItem)
808	Invoked when the user finished editing a cell.
809	
810	Event: ItemActivate(RowItem)
811	Invoked when a row was activated.
812	
813	Event: KeyPress(KeyCode)
814	Invoked when the user pressed a key while the control had focus.
815	
816	Event: MouseLeave()
817	Invoked when the mouse leaves the control boundaries.
818	
819	Event: FocusReceived()
820	Invoked when the control receives the focus.
821	
822	Event: FocusLost()
823	Invoked when the control loses the focus.
824	
825	Event: Marquee()
826	Invoked when the mouse gets moved over the control.
827	
828	Event: ScrollingStart()
829	Invoked when the user starts scrolling the control.
830	
831	Event: ScrollingEnd()
832	Invoked when the user ends scrolling the control.
833	
834	Event: ItemSelected(RowItem)
835	Invoked when the user selects an item.
836	
837	Event: ItemDeselected(RowItem)
838	Invoked when the user deselects an item.
839	
840	Event: SelectionChanged(RowItem)
841	Invoked when the selected item(s) has/have changed.
842	
843	Event: ItemFocused(RowItem)
844	Invoked when a row gets focused.
845	
846	Event: ItemDefocused(RowItem)
847	Invoked when a row loses the focus.
848	
849	Event: FocusedChanged(RowItem)
850	Invoked when the row focus has changed.
851	
852	Event: ItemChecked(RowItem)
853	Invoked when the user checks a row.
854	
855	Event: ItemUnchecked(RowItem)
856	Invoked when the user unchecks a row.	
857	
858	Event: CheckedChanged(RowItem)
859	Invoked when the checked row(s) has/have changed.
860	*/
861	HandleEvent(Event)
862	{
863		Row := this._.Items.IndependentSorting ? this.CItems.CRow.GetUnsortedIndex(Event.EventInfo, this.hwnd) : Event.EventInfo
864		if(Event.GUIEvent == "E")
865			this.CallEvent("EditingStart", Row)
866		else if(EventName := {DoubleClick : "DoubleClick", R : "DoubleRightClick", ColClick : "ColumnClick", e : "EditingEnd", Normal : "Click", RightClick : "RightClick",  A : "ItemActivate", K : "KeyPress"}[Event.GUIEvent])
867			this.CallEvent(EventName, Row)
868		else if(Event.GUIEvent == "F")
869			this.CallEvent("FocusReceived")
870		else if(Event.GUIEvent == "S")
871			this.CallEvent("ScrollingStart")
872		else if(EventName :=  {C : "MouseLeave", f : "FocusLost", M : "Marquee", s : "ScrollingEnd"}[Event.GUIEvent])
873			this.CallEvent(EventName)
874		else if(Event.GUIEvent = "I")
875		{
876			if(InStr(Event.Errorlevel, "S")) ;Process sub control state
877			{
878				if(LV_GetCount("Selected") = 1)
879					this.ProcessSubControlState(this._.PreviouslySelectedItem, this.SelectedItem)
880				else
881					this.ProcessSubControlState(this._.PreviouslySelectedItem, "")
882			}
883			Mapping := { Sa : "ItemSelected", sb : "ItemDeselected", Fa : "ItemFocused", fb : "ItemDefocused", Ca : "ItemChecked", cb : "ItemUnChecked"} ;Case insensitivity strikes back!
884			for EventIndex, Function in Mapping
885				if(InStr(Event.Errorlevel, SubStr(EventIndex, 1, 1), true))
886				{
887					this.CallEvent(Function, Row)
888					break
889				}
890			if(EventName :=  {S : "SelectionChanged", C : "CheckedChanged", F : "FocusedChanged"}[Event.Errorlevel])
891				this.CallEvent(EventName, Row)
892			if(InStr(Event.Errorlevel, "S")) ;Process sub control state
893			{
894				if(LV_GetCount("Selected") = 1)
895					this._.PreviouslySelectedItem := this.SelectedItem
896				else
897					this._.PreviouslySelectedItem := ""
898			}
899		}
900	}
901}