/lib/CListViewControl.ahk

http://github.com/Skiouros/Macro · AutoHotKey · 901 lines · 568 code · 9 blank · 324 comment · 139 complexity · 15f9f07d4f26f3e0f4345a64088563ac MD5 · raw file

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