﻿#include <Array.au3>
#include <APIShellExConstants.au3>
#include <Date.au3>
#include <EditConstants.au3>
#include <FileConstants.au3>
#include <GUIConstantsEx.au3>
#include <GuiImageList.au3>
#include <GuiListView.au3>
#include <GuiTreeView.au3>
#include <ListViewConstants.au3>
#include <MsgBoxConstants.au3>
#include <StructureConstants.au3>
#include <TreeViewConstants.au3>
#include <WinAPIConv.au3>
#include <WinAPIFiles.au3>
#include <WinAPIIcons.au3>
#include <WinAPILocale.au3>
#include <WinAPIRes.au3>
#include <WinAPIShellEx.au3>
#include <WinAPIShPath.au3>
#include <WindowsConstants.au3>


Opt('GUIOnEventMode', 1)

Global $bLargeIcons = True ; ob grosse oder kleine Icons benutzt werden sollen
Global $bLargeIcons = False ; ob grosse oder kleine Icons benutzt werden sollen
_WinAPI_FileIconInit() ; System-Imagelist initialisieren
Global $hSystemImgList = _WinAPI_ShellGetImageList(Not $bLargeIcons) ; System-Imagelist holen
Global $hBusyCur = _WinAPI_LoadCursorFromFile(@WindowsDir & '\Cursors\aero_busy_xl.ani') ; Busy-Animation-Cursor laden


Global $hMainGui = GUICreate('Test', 320, 240)
GUISetOnEvent($GUI_EVENT_CLOSE, '_CloseMainGui')

Global $idSelectFiles = GUICtrlCreateButton('Select files or folder', 60, 20, 180, 25)
GUICtrlSetOnEvent(-1, '_OpenSelectGui')

Global $hSelectGui = GUICreate('Select files or folder', 800, 490, -1, -1, BitOR($WS_POPUPWINDOW, $WS_CAPTION, $WS_SIZEBOX), Default, $hMainGui)
GUISetOnEvent($GUI_EVENT_CLOSE, '_CloseSelectGui')

Global $idPath = GUICtrlCreateInput('', 5, 5, 790, 25, BitOR($ES_LEFT, $ES_AUTOHSCROLL, $ES_READONLY))
GUICtrlSetFont(-1, 11 + $bLargeIcons, 400, 0, 'Verdana')
GUICtrlSetBkColor(-1, 0xFFFFFF)
GUICtrlSetResizing(-1, $GUI_DOCKTOP + $GUI_DOCKHEIGHT + $GUI_DOCKLEFT + $GUI_DOCKRIGHT)

Global $idTreeview = GUICtrlCreateTreeView(5, 35, 260, 410, $GUI_SS_DEFAULT_TREEVIEW, $WS_EX_CLIENTEDGE)
GUICtrlSetFont(-1, 10 + $bLargeIcons, 400, 0, 'Verdana')
GUICtrlSetResizing(-1, $GUI_DOCKBOTTOM + $GUI_DOCKTOP + $GUI_DOCKLEFT)
_GUICtrlTreeView_SetNormalImageList($idTreeview, $hSystemImgList)

Global $idListview = GUICtrlCreateListView('Dateiname|Größe|Datum|Attr.', 275, 35, 520, 410, $LVS_SHOWSELALWAYS)
GUICtrlSetFont(-1, 9 + $bLargeIcons * 3, 400, 0, 'Arial')
GUICtrlSetResizing(-1, $GUI_DOCKBOTTOM + $GUI_DOCKTOP + $GUI_DOCKRIGHT)
_GUICtrlListView_SetImageList($idListview, $hSystemImgList, 1)

Global $aColWidth[4] = [180, 100 + $bLargeIcons * 40, 125 + $bLargeIcons * 50, 50], $aColJust[4] = [0, 1, 1, 1]
For $i = 0 To 3
	_GUICtrlListView_SetColumnWidth($idListview, $i, $aColWidth[$i])
	_GUICtrlListView_JustifyColumn($idListview, $i, $aColJust[$i])
Next

Global $idOkButton = GUICtrlCreateButton('Ok', 620, 455, 70, 22)
GUICtrlSetFont(-1, 11, 400, 0, 'Arial')
GUICtrlSetOnEvent(-1, '_SelectOk')
GUICtrlSetResizing(-1, $GUI_DOCKBOTTOM + $GUI_DOCKHEIGHT + $GUI_DOCKWIDTH + $GUI_DOCKRIGHT)
Global $idCancelButton = GUICtrlCreateButton('Cancel', 700, 455, 90, 22)
GUICtrlSetFont(-1, 11, 400, 0, 'Arial')
GUICtrlSetOnEvent(-1, '_CloseSelectGui')
GUICtrlSetResizing(-1, $GUI_DOCKBOTTOM + $GUI_DOCKHEIGHT + $GUI_DOCKWIDTH + $GUI_DOCKRIGHT)

GUISetState(@SW_SHOW, $hMainGui)
GUIRegisterMsg($WM_NOTIFY, '_WM_NOTIFY')
_GUICtrlTreeView_CreateDriveItems($idTreeview, $bLargeIcons)

While Sleep(1000)
WEnd

Func _CloseMainGui()
	_GUIImageList_Destroy($hSystemImgList)
	If $hBusyCur Then _WinAPI_DestroyCursor($hBusyCur)
	GUIDelete($hMainGui)
	Exit
EndFunc   ;==>_CloseMainGui

Func _OpenSelectGui()
	GUISetState(@SW_SHOW, $hSelectGui)
EndFunc   ;==>_OpenSelectGui

Func _CloseSelectGui()
	GUISetState(@SW_HIDE, $hSelectGui)
EndFunc   ;==>_CloseSelectGui

Func _SelectOk()
	Local $sSelected, $aSelIndices, $sPath = GUICtrlRead($idPath)
	Local $sSepChar = Opt('GUIDataSeparatorChar')
	GUISetState(@SW_HIDE, $hSelectGui)
	$sSelected = ControlListView($hSelectGui, '', $idListview, 'GetSelected', 1)
	If $sSelected <> '' Then ; wenn der Benutzer Dateien ausgewaehlt hat
		$aSelIndices = StringSplit($sSelected, $sSepChar, 2)
		Local $aOut[UBound($aSelIndices)]
		For $i = 0 To UBound($aSelIndices) - 1
			$aOut[$i] = $sPath & _GUICtrlListView_GetItemText(GUICtrlGetHandle($idListview), $aSelIndices[$i])
		Next
	Else ; keine Dateien ausgewaehlt, dann nur das Verzeichnis zurueckgeben
		Local $aOut[1] = [$sPath]
	EndIf
	_ArrayDisplay($aOut)
EndFunc   ;==>_SelectOk

Func _WM_NOTIFY($hWnd, $iMsg, $iwParam, $ilParam)
	#forceref $hWnd, $iMsg, $iwParam
	Local $tNMTREEVIEW, $tNMHDR, $hItem, $hWndFrom, $hChild, $hOldCur, $sFolder
	Local $sSepChar = Opt('GUIDataSeparatorChar')
	$tNMTREEVIEW = DllStructCreate($tagNMTREEVIEW, $ilParam)
	$hWndFrom = DllStructGetData($tNMTREEVIEW, 'hWndFrom')
	Switch $hWndFrom
		Case GUICtrlGetHandle($idTreeview)
			Switch DllStructGetData($tNMTREEVIEW, 'Code')
				Case $TVN_SELCHANGEDA, $TVN_SELCHANGEDW
					$hItem = DllStructGetData($tNMTREEVIEW, 'NewhItem')
					If $hItem Then
						$sFolder = StringReplace(_GUICtrlTreeView_GetTree($hWndFrom, $hItem), $sSepChar, '\')
						If StringRight($sFolder, 1) <> '\' Then $sFolder &= '\'
						GUICtrlSetData($idPath, $sFolder)
						_GUICtrlListView_CreateFilesItem($idListview, $sFolder)
					EndIf
				Case $TVN_ITEMEXPANDINGA, $TVN_ITEMEXPANDINGW
					Switch DllStructGetData($tNMTREEVIEW, 'Action')
						Case $TVE_EXPAND
							$hItem = DllStructGetData($tNMTREEVIEW, 'NewhItem')
							$hChild = _GUICtrlTreeView_GetFirstChild($hWndFrom, $hItem)
							$hOldCur = _WinAPI_SetCursor($hBusyCur ? $hBusyCur : 9)
							While $hChild
								$sPath = StringReplace(_GUICtrlTreeView_GetTree($hWndFrom, $hChild), $sSepChar, '\')
								_GUICtrlTreeView_CreateFolderItems($idTreeview, $hChild, $sPath, $bLargeIcons)
								$hChild = _GUICtrlTreeView_GetNextChild($hWndFrom, $hChild)
							WEnd
							_WinAPI_SetCursor($hOldCur)
					EndSwitch
			EndSwitch
	EndSwitch
	Return $GUI_RUNDEFMSG
EndFunc   ;==>_WM_NOTIFY

Func _GUICtrlTreeView_CreateDriveItems(ByRef $idTreeview, $bLargeIcons)
	Local $aDrives = DriveGetDrive('ALL'), $iIndex, $hItem, $hOldCur
	$hOldCur = _WinAPI_SetCursor($hBusyCur ? $hBusyCur : 9)
	_GUICtrlTreeView_BeginUpdate($idTreeview)
	For $i = 1 To $aDrives[0]
		$iIndex = _GUIImageList_GetFileIconIndex($aDrives[$i], $bLargeIcons, False)
		$hItem = _GUICtrlTreeView_Add($idTreeview, 0, StringUpper($aDrives[$i]), $iIndex, $iIndex)
		_GUICtrlTreeView_CreateFolderItems($idTreeview, $hItem, $aDrives[$i], $bLargeIcons)
	Next
	_GUICtrlTreeView_EndUpdate($idTreeview)
	_WinAPI_SetCursor($hOldCur)
EndFunc   ;==>_GUICtrlTreeView_CreateDriveItems

Func _GUICtrlTreeView_CreateFolderItems(ByRef $idTreeview, $hItem, $sFolder, $bLargeIcons)
	Local $iIndex, $aFolder
	If StringRight($sFolder, 1) <> '\' Then $sFolder &= '\'
	$aFolder = _WinAPI_EnumFiles($sFolder, 2) ; nur Verzeichnisse einlesen
	If Not @error Then
		For $i = 1 To $aFolder[0][0]
			If Not BitAND($aFolder[$i][6], $FILE_ATTRIBUTE_REPARSE_POINT) Then
				$iIndex = _GUIImageList_GetFileIconIndex($sFolder & $aFolder[$i][0], $bLargeIcons, True)
				_GUICtrlTreeView_AddChild($idTreeview, $hItem, $aFolder[$i][0], $iIndex, $iIndex)
			EndIf
		Next
	EndIf
EndFunc   ;==>_GUICtrlTreeView_CreateFolderItems

Func _GUIImageList_GetFileIconIndex($sFilePath, $bLargeIcons = False, $bForceLoadFromDisk = False)
	Local $tFileInfo = DllStructCreate($tagSHFILEINFO)
	Local $iFlags = BitOR($SHGFI_SYSICONINDEX, _
			$bLargeIcons ? $SHGFI_LARGEICON : $SHGFI_SMALLICON, _
			$bForceLoadFromDisk ? 0 : $SHGFI_USEFILEATTRIBUTES)
	Local $ret = _WinAPI_ShellGetFileInfo($sFilePath, $iFlags, $FILE_ATTRIBUTE_NORMAL, $tFileInfo)
	If $ret Then Return DllStructGetData($tFileInfo, 'iIcon')
	Return SetError(1, 0, -1)
EndFunc   ;==>_GUIImageList_GetFileIconIndex

Func _GUICtrlListView_CreateFilesItem(ByRef $idListview, $sFolder)
	Local $aFiles, $iIndex, $hOldCur, $tNUMBERFMT
	If StringRight($sFolder, 1) <> '\' Then $sFolder &= '\'
	_GUICtrlListView_BeginUpdate($idListview)
	_GUICtrlListView_DeleteAllItems($idListview)
	$tNUMBERFMT = _WinAPI_CreateNumberFormatInfo(0, 1, 3, '', '.', 1)
	$aFiles = _WinAPI_EnumFiles($sFolder, 1) ; nur Dateien einlesen
	If Not @error Then
		$hOldCur = _WinAPI_SetCursor($hBusyCur ? $hBusyCur : 9)
		Local $aNewItems[$aFiles[0][0]][5] ; 'IconIndex|Dateiname|Groesse|Datum|Attr'
		For $i = 1 To $aFiles[0][0]
			$iIndex = _GUIImageList_GetFileIconIndex($sFolder & $aFiles[$i][0], $bLargeIcons, False)
			$aNewItems[$i - 1][0] = $iIndex
			$aNewItems[$i - 1][1] = $aFiles[$i][0]
			$aNewItems[$i - 1][2] = _WinAPI_GetNumberFormat(0, $aFiles[$i][4], $tNUMBERFMT)
			$aNewItems[$i - 1][3] = _DateTimeFormat(_GetDateTime($aFiles[$i][3]), 0)
			$aNewItems[$i - 1][4] = _GetAttrChars($aFiles[$i][6])
		Next
		; bei der Standard-Funktion von "_GUICtrlListView_AddArray" kann man kein IconIndex uebergeben
		_GUICtrlListView_AddArrayEx($idListview, $aNewItems) ; deswegen diese, von mir geaenderte, Version
		_WinAPI_SetCursor($hOldCur)
	EndIf
	_GUICtrlListView_EndUpdate($idListview)
EndFunc   ;==>_GUICtrlListView_CreateFilesItem

Func _GetAttrChars($iAttr)
	Local Const $aFA[][2] = [[$FILE_ATTRIBUTE_READONLY, 'R'], [$FILE_ATTRIBUTE_ARCHIVE, 'A'], _
			[$FILE_ATTRIBUTE_SYSTEM, 'S'], [$FILE_ATTRIBUTE_HIDDEN, 'H']]
	Local $sAttr = ''
	For $i = 0 To UBound($aFA) - 1
		$sAttr &= BitAND($iAttr, $aFA[$i][0]) ? $aFA[$i][1] : '-'
	Next
	Return $sAttr
EndFunc   ;==>_GetAttrChars

Func _GetDateTime($iDWord)
	Local $tFILETIME = DllStructCreate($tagFILETIME), $tLOCAL
	DllStructSetData($tFILETIME, 1, _WinAPI_LoDWord($iDWord))
	DllStructSetData($tFILETIME, 2, _WinAPI_HiDWord($iDWord))
	$tLOCAL = _Date_Time_FileTimeToLocalFileTime($tFILETIME)
	Return _Date_Time_FileTimeToStr($tLOCAL, 1)
EndFunc   ;==>_GetDateTime

; Bei dieser Version von _GUICtrlListView_AddArray kann man auch den Image-Index angeben
Func _GUICtrlListView_AddArrayEx($hWnd, ByRef $aItems)
	Local $fUnicode, $tItem, $pItem, $tBuffer, $pBuffer, $iLastItem
	$fUnicode = _GUICtrlListView_GetUnicodeFormat($hWnd)
	$tItem = DllStructCreate($tagLVITEM)
	$pItem = DllStructGetPtr($tItem)
	If $fUnicode Then
		$tBuffer = DllStructCreate('wchar Text[4096]')
	Else
		$tBuffer = DllStructCreate('char Text[4096]')
	EndIf
	$pBuffer = DllStructGetPtr($tBuffer)
	DllStructSetData($tItem, 'Mask', BitOR($LVIF_TEXT, $LVIF_IMAGE)) ; <- $LVIF_IMAGE hinzugefügt
	DllStructSetData($tItem, 'Text', $pBuffer)
	DllStructSetData($tItem, 'TextMax', 4096)
	$iLastItem = _GUICtrlListView_GetItemCount($hWnd)
	For $i = 0 To UBound($aItems) - 1
		DllStructSetData($tItem, 'Item', $i + $iLastItem)
		DllStructSetData($tItem, 'SubItem', 0)
		DllStructSetData($tItem, 'Image', $aItems[$i][0]) ; <- Den Image-Index als Element 0 im Array
		DllStructSetData($tBuffer, 'Text', $aItems[$i][1])
		If $fUnicode Then
			GUICtrlSendMsg($hWnd, $LVM_INSERTITEMW, 0, $pItem)
		Else
			GUICtrlSendMsg($hWnd, $LVM_INSERTITEMA, 0, $pItem)
		EndIf
		For $j = 2 To UBound($aItems, 2) - 1
			DllStructSetData($tItem, 'SubItem', $j - 1)
			DllStructSetData($tBuffer, 'Text', $aItems[$i][$j])
			If $fUnicode Then
				GUICtrlSendMsg($hWnd, $LVM_SETITEMW, 0, $pItem)
			Else
				GUICtrlSendMsg($hWnd, $LVM_SETITEMA, 0, $pItem)
			EndIf
		Next
	Next
	$tItem = 0
	$tBuffer = 0
EndFunc   ;==>_GUICtrlListView_AddArrayEx
