#include-once
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <Constants.au3>
#include <ProgressConstants.au3>
#include <HeaderConstants.au3>
#include <WinAPI.au3>
#include <GuiListView.au3>
#include <GuiEdit.au3>
#include <WinAPIGdi.au3>

Global Const $SW_HIDE = 0
Global Const $SW_SHOW = 5
Global Const $ODT_LISTVIEW = 102
Global Const $ODA_DRAWENTIRE = 0x1
Global Const $ODA_SELECT = 0x2
Global Const $ODA_FOCUS = 0x4
Global Const $ODS_SELECTED = 0x0001

Global $aProgress[1][4], $hListView

GUIRegisterMsg($WM_NOTIFY, "_ListView_Notify")
GUIRegisterMsg($WM_SIZE, "_ListView_Notify")


; #FUNCTION# ======================================================================================
; Function Name:    _ListView_InsertProgressBar()
; Description:      Inserts a progressbar control in the ListView control.
; Syntax.........:  _ListView_InsertProgressBar($sHwnd, $sItemIndex[, $sSubItemIndex])
; Parameter(s):     $sHwnd - Handle to the ListView control.
;                   $sItemIndex - Zero based index of the item at which the progressbar should be inserted.
;                   $sSubItemIndex - [Optional] One based index of the subitem where progressbar should be placed, default is 1.
;
; Return Value(s):  Returns the identifier (controlID) of the new progressbar control.
; Requirement(s):   AutoIt 3.2.10.0 and above
; Note(s):          Optimal amount of progressbar controls is 5-10. Don`t abuse with many amount of progressbar controls.
;
; Author(s):        R.Gilman (a.k.a rasim), arcker
;==================================================================================================
Func _ListView_InsertProgressBar($sHwnd, $sItemIndex, $sSubItemIndex = 0)
    If Not IsHWnd($sHwnd) Then $sHwnd = GUICtrlGetHandle($sHwnd)

    Local $iStyle = _WinAPI_GetWindowLong($sHwnd, $GWL_STYLE)

	If BitAND($iStyle, $WS_CLIPCHILDREN) <> $WS_CLIPCHILDREN Then
		_WinAPI_SetWindowLong($sHwnd, $GWL_STYLE, BitOR($iStyle, $WS_CLIPCHILDREN))
	EndIf

	Local $aRect

	If $sSubItemIndex = 0 Then
		$aRect = _GUICtrlListView_GetItemRect($sHwnd, $sItemIndex, 2)
	Else
		$aRect = _GUICtrlListView_GetSubItemRect($sHwnd, $sItemIndex, $sSubItemIndex)
	EndIf

    $aProgress[0][0] += 1
    ReDim $aProgress[$aProgress[0][0] + 1][4]
    $aProgress[$aProgress[0][0]][0] = _Progress_Create($sHwnd, $aRect[0], $aRect[1], $aRect[2] - $aRect[0], $aRect[3] - $aRect[1])

    $aProgress[$aProgress[0][0]][1] = $sItemIndex
    $aProgress[$aProgress[0][0]][2] = $sSubItemIndex
    $aProgress[$aProgress[0][0]][3] = $sHwnd

    Return $aProgress[$aProgress[0][0]][0]
EndFunc   ;==>_ListView_InsertProgressBar

Func _Progress_Create($hWnd, $iX, $iY, $iWidth = -1, $iHeight = -1, $iStyle = 0, $iExStyle = 0x00000200)
	$iStyle = BitOR($iStyle, $WS_CHILD, $WS_VISIBLE, $WS_TABSTOP, $WS_CHILD, $WS_VISIBLE, $ES_LEFT, $ES_AUTOHSCROLL, $ES_NOHIDESEL)
	Return _WinAPI_CreateWindowEx($iExStyle, "Edit", "", $iStyle, $iX, $iY, $iWidth, $iHeight, $hWnd)
EndFunc   ;==>_Progress_Create

Func _ListView_Notify($hWnd, $Msg, $wParam, $lParam)
	If $Msg = $WM_SIZE Then
		_WinAPI_InvalidateRect($hWnd)
		Return $GUI_RUNDEFMSG
	EndIf

    Local $tNMHEADER, $hWndFrom, $iCode, $i

    $tNMHEADER = DllStructCreate($tagNMHEADER, $lParam)
    $hWndFrom = DllStructGetData($tNMHEADER, "hwndFrom")
	$iCode = DllStructGetData($tNMHEADER, "Code")

	Switch $iCode
		Case $HDN_ITEMCHANGED, $HDN_ITEMCHANGEDW, $LVN_ENDSCROLL
			If $iCode <> $LVN_ENDSCROLL Then $hWndFrom = _WinAPI_GetParent($hWndFrom)

			For $i = 1 To $aProgress[0][0]
				_MoveProgress($hWndFrom, $aProgress[$i][0], $aProgress[$i][1], $aProgress[$i][2])

;~ 				Local $aSubItmRect = _GUICtrlListView_GetSubItemRect($hWndFrom, $aProgress[$i][1], $aProgress[$i][2])
;~ 				; the function above accepts not only subitems (one-based index), but also main item (index=0)
;~ 				; 3. pass the coordinates to a DLL struct
;~ 				Local $iSubItmRect = DllStructCreate("long[4]")
;~ 				DllStructSetData($iSubItmRect, 1, $aSubItmRect[0] + 6, 1) ; +6 is left margin (X)
;~ 				DllStructSetData($iSubItmRect, 1, $aSubItmRect[1] + (-2), 2) ; + (-2) is upper margin (Y)
;~ 				DllStructSetData($iSubItmRect, 1, $aSubItmRect[2], 3)
;~ 				DllStructSetData($iSubItmRect, 1, $aSubItmRect[3], 4)
;~ 				_WinAPI_invalidateRect($hWndFrom, $iSubItmRect)
			Next

			_WinAPI_InvalidateRect($hWndFrom)
	EndSwitch

    Return $GUI_RUNDEFMSG
EndFunc   ;==>_ListView_Notify


Func WM_MEASUREITEM($hWnd, $Msg, $wParam, $lParam)
	Local $tMEASUREITEMS = DllStructCreate("uint cType;uint cID;uint itmID;uint itmW;uint itmH;ulong_ptr itmData", $lParam)
	If DllStructGetData($tMEASUREITEMS, "cType") <> $ODT_LISTVIEW Then Return $GUI_RUNDEFMSG
	DllStructSetData($tMEASUREITEMS, "itmH", 25);row height
	Return 1
EndFunc   ;==>WM_MEASUREITEM

Func WM_DRAWITEM($hWnd, $Msg, $wParam, $lParam)
	Local $tagDRAWITEMSTRUCT, $iBrushColor, $cID, $itmID, $itmAction, $itmState, $hItm, $hDC, $bSelected
	$tagDRAWITEMSTRUCT = DllStructCreate( _
	   "uint cType;" & _
	   "uint cID;" & _
	   "uint itmID;" & _
	   "uint itmAction;" & _
	   "uint itmState;" & _
	   "hwnd hItm;" & _
	   "handle hDC;" & _
	   "long itmRect[4];" & _
	   "ulong_ptr itmData" _
	   , $lParam)
	If DllStructGetData($tagDRAWITEMSTRUCT, "cType") <> $ODT_LISTVIEW Then Return $GUI_RUNDEFMSG
	$cID = DllStructGetData($tagDRAWITEMSTRUCT, "cID")
	$itmID = DllStructGetData($tagDRAWITEMSTRUCT, "itmID")
	$itmAction = DllStructGetData($tagDRAWITEMSTRUCT, "itmAction")
	$itmState = DllStructGetData($tagDRAWITEMSTRUCT, "itmState")
	$hItm = DllStructGetData($tagDRAWITEMSTRUCT, "hItm")
	$hDC = DllStructGetData($tagDRAWITEMSTRUCT, "hDC")
	$bSelected = BitAND($itmState, $ODS_SELECTED)
	Switch $cID ; will look for ControlID, not window handle.
	  Case $hListView
		Switch $itmAction
			Case $ODA_DRAWENTIRE
				; don't forget, this is BGR, not RGB
				If $itmState = $bSelected Then ; item is not selected
					$iBrushColor = 0xCCEECC
				Else ; item is selected
					$iBrushColor = 0xEEDDBB
				EndIf
				Local $aBrush = DllCall("gdi32.dll", "hwnd", "CreateSolidBrush", "int", $iBrushColor)
				Local $aBrushOld = _WinAPI_SelectObject($hDC, $aBrush[0])
				Local $iLeft = DllStructGetData($tagDRAWITEMSTRUCT, "itmRect", 1)
				DllStructSetData($tagDRAWITEMSTRUCT, "itmRect", $iLeft + 1, 1) ; rectangle coordinates for coloring
				; +1 is the left margin
				_WinAPI_FillRect($hDC, DllStructGetPtr($tagDRAWITEMSTRUCT, "itmRect"), $aBrush[0])
				_WinAPI_SelectObject($hDC, $aBrushOld)
				_WinAPI_DeleteObject($aBrush[0])
				; for all columns of the row:
				$local_alignment = $DT_LEFT
				For $i = 0 To _GUICtrlListView_GetColumnCount($hItm) - 1
					; 1. get subitem text:
					Local $iSubItmText = _GUICtrlListView_GetItemText($hItm, $itmID, $i)
					; 2. get subitem coordinates for drawing its respective text
					Local $aSubItmRect = _GUICtrlListView_GetSubItemRect($hItm, $itmID, $i)
					; the function above accepts not only subitems (one-based index), but also main item (index=0)
					; 3. pass the coordinates to a DLL struct
					Local $iSubItmRect = DllStructCreate("long[4]")
					DllStructSetData($iSubItmRect, 1, $aSubItmRect[0] + 6, 1) ; +6 is left margin (X)
					DllStructSetData($iSubItmRect, 1, $aSubItmRect[1] + (-2), 2) ; + (-2) is upper margin (Y)
					DllStructSetData($iSubItmRect, 1, $aSubItmRect[2], 3)
					DllStructSetData($iSubItmRect, 1, $aSubItmRect[3], 4)
					DllCall("user32.dll", "int", "DrawTextW", "hwnd", $hDC, "wstr", $iSubItmText, "int", StringLen($iSubItmText), _
						"ptr", DllStructGetPtr($iSubItmRect), "int", $local_alignment)
				Next

;~ 				_WinAPI_InValidateRect($hListView)

		EndSwitch
	EndSwitch

	Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_DRAWITEM


Func _MoveProgress($hListView, $hProgress, $sItemIndex, $sSubItemIndex)
	Local $aRect

	If $sSubItemIndex = 0 Then
		$aRect = _GUICtrlListView_GetItemRect($hListView, $sItemIndex, 2)
	Else
		$aRect = _GUICtrlListView_GetSubItemRect($hListView, $sItemIndex, $sSubItemIndex)
	EndIf

	If $aRect[1] < 10 Then
        _WinAPI_ShowWindow($hProgress, $SW_HIDE)
    ElseIf $aRect[1] >= 10 Then
        _WinAPI_ShowWindow($hProgress, $SW_SHOW)
    EndIf

    _WinAPI_MoveWindow($hProgress, $aRect[0], $aRect[1], $aRect[2] - $aRect[0], $aRect[3] - $aRect[1], True)
EndFunc   ;==>_MoveProgress
