#NoTrayIcon
#include <Array.au3>
#include <ButtonConstants.au3>
#include <DateTimeConstants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <GuiListView.au3>
#include <ListViewConstants.au3>
#include <WindowsConstants.au3>
#include <GuiStatusBar.au3>
#include <StaticConstants.au3>
#include <WinAPI.au3>
#include <Misc.au3>

Opt('GUIOnEventMode', 1) ; den OnEvent-Modus einschalten
Opt('GUIResizeMode', $GUI_DOCKBORDERS) ; den Standard-Resize-Modus auf DOCKBORDERS (an den Raendern andocken) setzen

#Region globale Variablen deklarieren
Global Const $sTitle = 'Listview-Datenbank-Beispiel' ; Name des Programms
Global Const $sDate = '31.10.2019' ; Datum der Erstellung
Global Const $sVersion = '3.3.0.0' ; Versionsnummer
Global Const $sAuthor = 'Gorathan (Oscar)' ; Programmierer dieser Listview-Datenbank
Global Const $sFileExt = '.lvdb' ; eine ausgedachte Dateiendung fuer die verwendete Datenbankdatei
Global Const $iColCount = 4 ; die Anzahl der Spalten im Listview
Global Const $aHeader[$iColCount] = ['Datum', 'Dateiname', 'Suchbegriffe', 'Dateipfad'] ; Die Überschriften für das Listview
Global Const $aColJustify[$iColCount] = [1, 0, 0, 0] ; die Ausrichtung der einzelnen Spalten (0=links, 1=rechts, 2=zentriert)
Global Const $sLoadDatabaseOnStart = @ScriptDir & '\pcwelt.lvdb' ; Pfad zu der Datenbank, die bei Programmstart geladen werden soll

; Einstellungen des Programms sollte man in einem programmeigenen Order im Anwendungsordner des Benutzers (@AppDataDir) erstellen.
; Dort hat man im Normalfall Schreibzugriff und kann eine Inidatei oder so erstellen. Dadurch, dass man den Ordner genauso nennt
; wie das Programm, macht man es dem Anwender leichter dort aufzuraeumen. Soll heissen: existiert das Programm nicht mehr, kann
; auch der Ordner im Anwendungsordner weg.
Global Const $sAppDir = StringFormat('%s\%s\', @AppDataDir, $sTitle) ; Pfad zum Anwendungsordner generieren
If Not FileExists($sAppDir) Then ; wenn der Anwendungsordner noch nicht existiert, dann...
	If Not DirCreate($sAppDir) Then ; wenn der Ordner nicht erstellt werden konnte, dann...
		Exit MsgBox($MB_OK, $sTitle, 'Es konnte kein Verzeichnis im Anwendungsordner erstellt werden!' & @CRLF & 'Das Programm wird beendet.') ; das Programm mit einer Fehlermeldung beenden
	EndIf
EndIf
; Ab hier existiert $sAppDir als Verzeichnis und kann als Teil der Inidatei verwendet werden.
; Als Namen fuer die Inidatei verwenden wir den Programmnamen mit der Erweiterung ".ini",
; damit der Anwender erkennt, dass es sich um eine Datei fuer dieses Programm handelt.
Global Const $sInifile = $sAppDir & $sTitle & '.ini' ; eine Variable mit Pfad und Name der Inidatei
Global Const $iWidthMin = 740 ; die Breite, die das Hauptfenster nicht unterschreiten soll
Global Const $iHeightMin = 480 ; die Hoehe, die das Hauptfenster nicht unterschreiten soll
Global $sInitDir = IniRead($sInifile, 'Config', 'InitDir', @ScriptDir) ; der Ordner, in dem der Benutzer zuletzt eine Datenbank gespeichert hat
Global $iWidth = IniRead($sInifile, 'Config', 'Width', $iWidthMin) ; die Breite des Hauptfensters aus der Inidatei lesen (Standard: 680 pixel)
Global $iHeight = IniRead($sInifile, 'Config', 'Height', $iHeightMin) ; die Hoehe des Hauptfensters aus der Inidatei lesen (Standard: 480 pixel)
Global $iLeft = IniRead($sInifile, 'Config', 'Left', 0) ; die Position (links) des Hauptfensters aus der Inidatei lesen (Standard: Links/Oben)
Global $iTop = IniRead($sInifile, 'Config', 'Top', 0) ; die Position (oben) des Hauptfensters aus der Inidatei lesen (Standard: Links/Oben)
Global $fMaximize = IniRead($sInifile, 'Config', 'Maximize', False) == True ; ob das Hauptfensters maximiert wurde aus der Inidatei lesen (Standard: nein)
Global $aColWidth[$iColCount] = [100, 80, 100, 100] ; die Breite (in Pixel) der einzelnen Spalten des Listviews (Standardwerte)
Global $aColWidthTmp = IniReadSection($sInifile, 'ColWidth') ; nachsehen, ob in der Inidatei andere Werte gespeichert sind
If Not @error And $aColWidthTmp[0][0] = $iColCount Then ; wenn ja, dann...
	For $i = 0 To $iColCount - 1 ; alle Spalten durchgehen
		$aColWidth[$i] = $aColWidthTmp[$i + 1][1] ; die Werte aus der Inidatei uebernehmen
	Next
EndIf
Global $iDataCount = 0 ; Variable fuer die Anzahl der Eintraege in der Datenbank
Global $aDatabase[$iDataCount + 1][$iColCount] ; ein 2D-Array fuer die Datenbank erstellen
Global $fChangeDB = False ; eine Variable zum kennzeichnen, ob die Datenbank geaendert wurde (False = nein)
Global $iInputIndex = -1 ; das Item vom Listview, dass gerade bearbeitet wird (-1 = neuen Eintrag erstellen)
#EndRegion globale Variablen deklarieren

#Region Hauptfenster erstellen
Global $hMainGui = GUICreate(StringFormat('%s - v%-3.3s', $sTitle, $sVersion), $iWidth, $iHeight, $iLeft, $iTop, BitOR($GUI_SS_DEFAULT_GUI, $WS_MAXIMIZEBOX, $WS_SIZEBOX), $WS_EX_WINDOWEDGE) ; Hauptfenster erstellen
GUISetIcon('shell32.dll', 71, $hMainGui) ; das Anzeige-Icon fuer das Hauptfenster aendern
GUISetBkColor(0xCCCCCC, $hMainGui) ; Hintergrundfarbe des Hauptfensters aendern
GUISetOnEvent($GUI_EVENT_CLOSE, '_CloseMainGui') ; diese Funktion wird aufgerufen, wenn man auf das "X" oben rechts klickt oder [ESC] drueckt
GUISetFont(10, 400, 0, 'Arial', $hMainGui) ; einen Zeichensatz fuer das Hauptfenster festlegen

#Region Menue fuer das Hauptfenster erstellen
Global $idFileMenu = GUICtrlCreateMenu('Datei') ; einen Eintrag "Datei" in der Menuezeile erstellen
GUICtrlCreateMenuItem('Neue Datenbank erstellen', $idFileMenu) ; "Neue Datenbank erstellen" als Auswahl fuer das Dateimenue
GUICtrlSetOnEvent(-1, '_CreateDatabase') ; diese Funktion wird aufgerufen, wenn der Menueeintrag ausgewaehlt wird
GUICtrlCreateMenuItem('', $idFileMenu) ; einen Leer-Eintrag (Querstrich) im Dateimenue
GUICtrlCreateMenuItem('Datenbank laden', $idFileMenu) ; "Datenbank laden" als Auswahl fuer das Dateimenue
GUICtrlSetOnEvent(-1, '_LoadDatabase') ; diese Funktion wird aufgerufen, wenn der Menueeintrag ausgewaehlt wird
GUICtrlCreateMenuItem('Datenbank speichern', $idFileMenu) ; "Datenbank speichern" als Auswahl fuer das Dateimenue
GUICtrlSetOnEvent(-1, '_SaveDatabase') ; diese Funktion wird aufgerufen, wenn der Menueeintrag ausgewaehlt wird
GUICtrlCreateMenuItem('', $idFileMenu) ; einen Leer-Eintrag (Querstrich) im Dateimenue
GUICtrlCreateMenuItem('Programm beenden', $idFileMenu) ; "Programm beenden" als Auswahl fuer das Dateimenue
GUICtrlSetOnEvent(-1, '_CloseMainGui') ; diese Funktion wird aufgerufen, wenn der Menueeintrag ausgewaehlt wird
Global $idInfoMenu = GUICtrlCreateMenu('Info') ; einen Eintrag "Info" in der Menuezeile erstellen
GUICtrlCreateMenuItem('Programm-Information', $idInfoMenu) ; "Programm-Information" als Auswahl fuer das Infomenue
GUICtrlSetOnEvent(-1, '_ProgInfo') ; diese Funktion wird aufgerufen, wenn der Menueeintrag ausgewaehlt wird
#EndRegion Menue fuer das Hauptfenster erstellen

#Region Toolbar-Button-Leiste erstellen
Global $iToolbarCount = 7 ; die Anzahl der Toolbar-Buttons festlegen
Global $aToolbarIcons[$iToolbarCount] = [-206, -4, -7, -71, -134, -132, -222] ; die Icon-Nummern aus der "shell32.dll"
Global $aToolbarFunc[$iToolbarCount] = ['_CreateDatabase', '_LoadDatabase', '_SaveDatabase', '_NewItem', '_EditItem', '_DeleteItems', '_ProgInfo'] ; die Funktionen, die beim Mausklick aufgerufen werden
Global $aToolbarTips[$iToolbarCount] = ['Neue Datenbank erstellen', 'Datenbank laden', 'Datenbank speichern', 'Neuen Eintrag erstellen', 'Eintrag bearbeiten', 'Markierte Einträge löschen', 'Programm-Information'] ; die Tooltip-Texte
Global $aidToolbar[$iToolbarCount] ; Variable zum speichern der Control-IDs
For $i = 0 To $iToolbarCount - 1 ; alle Toolbar-Buttons durchgehen
	$aidToolbar[$i] = GUICtrlCreateButton('', 10 + $i * 48, 2, 48, 48, $BS_ICON) ; den Button erstellen
	GUICtrlSetImage(-1, 'shell32.dll', $aToolbarIcons[$i]) ; ihm ein Icon aus der "shell32.dll" zuweisen
	GUICtrlSetOnEvent(-1, $aToolbarFunc[$i]) ; die "Mausklick-Funktion" zuweisen
	GUICtrlSetTip(-1, $aToolbarTips[$i]) ; den Tooltip festlegen
	GUICtrlSetResizing(-1, $GUI_DOCKSIZE + $GUI_DOCKTOP + $GUI_DOCKLEFT) ; und das Resizising festlegen
Next
GUICtrlCreateGroup('Suchbegriff', 360, 1, 330, 46) ; eine Gruppe fuer den Suchbegriff erstellen
GUICtrlSetFont(-1, 9, 400, 2, 'Verdana') ; den Zeichensatz festlegen
GUICtrlSetResizing(-1, $GUI_DOCKSIZE + $GUI_DOCKTOP + $GUI_DOCKLEFT) ; und das Resizising festlegen
Global $idSearch = GUICtrlCreateInput('', 370, 17, 236, 22) ; das Eingabefeld fuer die Suche erstellen
GUICtrlSetResizing(-1, $GUI_DOCKSIZE + $GUI_DOCKTOP + $GUI_DOCKLEFT) ; und das Resizising festlegen
Global $idSearchStart = GUICtrlCreateButton('', 610, 16, 32, 24, BitOR($BS_DEFPUSHBUTTON, $BS_ICON)) ; den Suche-Button erstellen
GUICtrlSetOnEvent(-1, '_SearchItem') ; die "Mausklick-Funktion" zuweisen
GUICtrlSetImage(-1, 'shell32.dll', -23, 0) ; ihm ein Icon aus der "shell32.dll" zuweisen
GUICtrlSetResizing(-1, $GUI_DOCKSIZE + $GUI_DOCKTOP + $GUI_DOCKLEFT) ; und das Resizising festlegen
Global $idSearchStart = GUICtrlCreateButton('', 650, 16, 32, 24, $BS_ICON) ; den Reset-Button erstellen
GUICtrlSetOnEvent(-1, '_SearchReset') ; die "Mausklick-Funktion" zuweisen
GUICtrlSetImage(-1, 'wmploc.dll', -200, 0) ; ihm ein Icon aus der "shell32.dll" zuweisen
GUICtrlSetResizing(-1, $GUI_DOCKSIZE + $GUI_DOCKTOP + $GUI_DOCKLEFT) ; und das Resizising festlegen
GUICtrlCreateGroup('', -99, -99, 1, 1) ; die Gruppe schliessen
GUICtrlSetResizing(-1, $GUI_DOCKSIZE + $GUI_DOCKTOP + $GUI_DOCKLEFT) ; und das Resizising festlegen
#EndRegion Toolbar-Button-Leiste erstellen

#Region Listview fuer die Anzeige der Datenbank
Global $sHeader = _ArrayToString($aHeader) ; aus dem Ueberschriften-Array einen String generieren
Global $idListView = GUICtrlCreateListView($sHeader, 10, 50, $iWidth - 20, $iHeight - 100, $LVS_SHOWSELALWAYS, BitOR($LVS_EX_FULLROWSELECT, $WS_EX_CLIENTEDGE, $LVS_EX_DOUBLEBUFFER)) ; Listview erstellen
GUICtrlSetOnEvent(-1, '_SortListview') ; dieses Event wird nur ausgeloest, wenn auf eine Spalte in der Titelleiste geklickt wird
Global $hListView = GUICtrlGetHandle($idListView) ; das Handle vom Listview wird für die UDF-Listview-Funktionen benoetigt
Global $hLVHeader = _GUICtrlListView_GetHeader($hListView) ; das Handle von der Titelleiste des Listviews (wird beim sortieren benoetigt)
Global $idLVCtx = GUICtrlCreateContextMenu($idListView) ; ein Kontextmenue fuer das Listview erstellen
Global $idLVCopy = GUICtrlCreateMenuItem('Markierte Einträge in die Zwischenablage kopieren', $idLVCtx)
GUICtrlSetOnEvent(-1, '_CopyMarkedItemsToCB')
#EndRegion Listview fuer die Anzeige der Datenbank

#Region Listview fuer die Anzeige der Suche
Global $idSearchLV = GUICtrlCreateListView($sHeader, 10, 50, $iWidth - 20, $iHeight - 100, $LVS_SHOWSELALWAYS) ; Suche-Listview erstellen
GUICtrlSetState(-1, $GUI_HIDE) ; das Suche-Listview erstmal verstecken (wird nur fuer die Suchtreffer benoetigt)
Global $hSearchLV = GUICtrlGetHandle($idSearchLV) ; das Handle vom Listview wird für die UDF-Listview-Funktionen benoetigt
Global $idSearchLVCtx = GUICtrlCreateContextMenu($idSearchLV) ; ein Kontextmenue fuer das Suche-Listview erstellen
Global $idSearchLVCopy = GUICtrlCreateMenuItem('Markierte Einträge in die Zwischenablage kopieren', $idSearchLVCtx)
GUICtrlSetOnEvent(-1, '_CopyMarkedItemsToCB')
#EndRegion Listview fuer die Anzeige der Suche

For $i = 0 To $iColCount - 1 ; Schleife, um alle Spalten durchzugehen
	_GUICtrlListView_SetColumnWidth($hListView, $i, $aColWidth[$i]) ; die Breite und
	_GUICtrlListView_JustifyColumn($hListView, $i, $aColJustify[$i]) ; die Ausrichtung fuer die Spalten im Daten-Listview festlegen
	_GUICtrlListView_SetColumnWidth($hSearchLV, $i, $aColWidth[$i]) ; die Breite und
	_GUICtrlListView_JustifyColumn($hSearchLV, $i, $aColJustify[$i]) ; die Ausrichtung fuer die Spalten im Suche-Listview festlegen
Next

#Region Statuszeile
Global $aParts[2] = [400, -1], $aText[2] = ['Bereit.', 'Einträge = 0'], $ahIcons[3] ; Variablen fuer die Statuszeile
$ahIcons[0] = _WinAPI_LoadShell32Icon(144) ; Haken-Icon aus der "shell32.dll" laden
$ahIcons[1] = _WinAPI_LoadShell32Icon(109) ; Ausrufe-Icon aus der "shell32.dll" laden
$ahIcons[2] = _WinAPI_LoadShell32Icon(166) ; Statistik-Icon aus der "shell32.dll" laden
Global $hStatus = _GUICtrlStatusBar_Create($hMainGui, $aParts, $aText, $SBARS_SIZEGRIP) ; Statuszeile erstellen
_GUICtrlStatusBar_SetIcon($hStatus, 0, $ahIcons[0]) ; Icon fuer die linke Seite der Statuszeile setzen
_GUICtrlStatusBar_SetIcon($hStatus, 1, $ahIcons[2]) ; Icon fuer die rechte Seite der Statuszeile setzen
#EndRegion Statuszeile
#EndRegion Hauptfenster erstellen

#Region Eingabefenster erstellen
Global $hInputGui = GUICreate(StringFormat('%s - Eingabe', $sTitle), 640, 140, -1, -1, BitOR($WS_CAPTION, $WS_POPUPWINDOW), Default, $hMainGui) ; das Eingabefenster erstellen
GUISetIcon('shell32.dll', -101, $hInputGui) ; das Anzeige-Icon fuer das Eingabefenster aendern
GUISetBkColor(0x88DDAA, $hInputGui) ; Hintergrundfarbe des Eingabefensters aendern
GUISetOnEvent($GUI_EVENT_CLOSE, '_CloseInputGui') ; diese Funktion wird aufgerufen, wenn man auf das "X" oben rechts klickt oder [ESC] drueckt
GUISetFont(10, 400, 0, 'Arial', $hInputGui) ; einen Zeichensatz fuer das Eingabefenster festlegen
Global $aidInput[$iColCount], $aidLabel[$iColCount] ; Variablen zum erstellen der Eingabefelder
Global $idApply = GUICtrlCreateButton('übernehmen', 400, 80, 120, 25, $BS_DEFPUSHBUTTON) ; den "uebernehmen"-Button erstellen
GUICtrlSetOnEvent(-1, '_CloseInputGui') ; Funktion, die aufgerufen wird, wenn auf den Button geklickt wird
Global $idBreak = GUICtrlCreateButton('abbrechen', 530, 80, 120, 25) ; den "abbrechen"-Button erstellen
GUICtrlSetOnEvent(-1, '_CloseInputGui') ; Funktion, die aufgerufen wird, wenn auf den Button geklickt wird
#EndRegion Eingabefenster erstellen

GUISetState(@SW_SHOW, $hMainGui) ; das Hauptfenster anzeigen
GUIRegisterMsg($WM_SIZE, '_WM_SIZE') ; Funktion registrieren, die aufgerufen wird, wenn das Fenster in der Groesse veraendert wird
GUIRegisterMsg($WM_NOTIFY, '_WM_NOTIFY') ; Funktion registrieren, um einen Doppelklick auf das Listview auswerten zu koennen
GUIRegisterMsg($WM_GETMINMAXINFO, '_WM_GETMINMAXINFO') ; Funktion registrieren, damit das Hauptfenster nicht kleiner als 680x480 wird
WinMove($hMainGui, '', $iLeft, $iTop, $iWidth, $iHeight) ; das Hauptfenster an die gespeicherten Daten anpassen
If $fMaximize Then WinSetState($hMainGui, '', @SW_MAXIMIZE) ; das Hauptfenster maximieren, wenn das so gespeichert war

If $sLoadDatabaseOnStart <> '' Then _LoadFile($sLoadDatabaseOnStart)

#Region Warteschleife
; Hier koennte man auch eine Endlosschleife "While True; Sleep(1000); WEnd" einfuegen.
; WinWaitClose wartet aber ebenfalls, bis das Fenster geschlossen wird.
; Die Auswertung der Mausklicks erfolgt ueber die aufgerufenen Funktionen (OnEvent-Modus).
WinWaitClose($hMainGui) ; So lange warten, bis das Hauptfenster geschlossen wurde.
Exit ; Programm beenden
#EndRegion Warteschleife

#Region Funktionen, die ueber OnEvent aufgerufen werden
; Diese Funktion wird bei zahlreichen Aktionen des Hauptfensters ausgeführt.
; Hier interessiert uns aber nur der Doppelklick auf das Listview und das aendern der Spaltenbreite.
Func _WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)
	#forceref $hWnd, $iMsg, $wParam ; eine Anweisung an AutoIt, dass diese Variablen hier nicht verwendet werden
	Local $tNMHDR, $hWndFrom, $iCode, $tInfo, $tNMHEADER, $iItem
	$tNMHDR = DllStructCreate($tagNMHDR, $lParam) ; Struktur erstellen
	$hWndFrom = HWnd(DllStructGetData($tNMHDR, 'hWndFrom')) ; das ausloesende Handle ermitteln
	$iCode = DllStructGetData($tNMHDR, 'Code') ; den ausloesenden Code ermitteln
	Switch $hWndFrom ; Welches Gui-Control-Element hat den Notify-Event ausgeloest
		Case $hListView ; wenn es das Listview war, dann...
			If $iDataCount > 0 Then ; den nachfolgenden Code nur abarbeiten, wenn sich Daten im Listview befinden
				Switch $iCode ; mit welchem Code wurde der Event ausgeloest
					Case $NM_DBLCLK ; wenn es ein Doppelklick war, dann...
						If BitAND(WinGetState($hInputGui), 2) Then _CloseInputGui() ; wenn das Eingabefenster geoeffnet ist, dann das Fenster erstmal schliessen, um die Eingabefelder zu loeschen
						$tInfo = DllStructCreate($tagNMITEMACTIVATE, $lParam) ; Struktur erstellen
						$iInputIndex = DllStructGetData($tInfo, 'Index') ; den Index des Listviews ermitteln
						_OpenInputGui() ; und das Eingabefenster oeffnen
				EndSwitch
			EndIf
		Case $hLVHeader ; wenn es die Titelleiste des Listviews war, dann...
			Switch $iCode ; mit welchem Code wurde der Event ausgeloest
				Case $HDN_ITEMCHANGED, $HDN_ITEMCHANGEDW ; wenn die Spaltenbreite geaendert wurde, dann...
					$tNMHEADER = DllStructCreate($tagNMHEADER, $lParam) ; Struktur erstellen
					$iItem = DllStructGetData($tNMHEADER, 'Item') ; die veraenderte Spalte ermitteln
					$aColWidth[$iItem] = _GUICtrlListView_GetColumnWidth($idListView, $iItem) ; die neue Spaltenbreite merken
			EndSwitch
	EndSwitch
	$tNMHEADER = 0 ; die benutzten Strukturen loeschen
	$tNMHDR = 0
	$tInfo = 0
	Return $GUI_RUNDEFMSG ; Anweisung an Windows, dass es mit der normalen Abarbeitung fortfahren kann
EndFunc   ;==>_WM_NOTIFY

Func _WM_SIZE($hWnd, $iMsg, $wParam, $lParam) ; diese Funktion wird aufgerufen, wenn die Groesse des Hauptfensters geandert wurde
	#forceref $hWnd, $iMsg, $wParam ; eine Anweisung an AutoIt, dass diese Variablen hier nicht verwendet werden
	Local $iNewWidth = _WinAPI_LoWord($lParam) ; die neue Fensterbreite ermitteln
	_GUICtrlStatusBar_Resize($hStatus) ; die Statuszeile an die Groesse des Hauptfensters anpassen
	$aParts[0] = 400 + Int(($iNewWidth - $iWidthMin) / 2) ; den linken Teil der Statusleiste anpassen
	_GUICtrlStatusBar_SetParts($hStatus, $aParts) ; und die neue Aufteilung setzen
	Return $GUI_RUNDEFMSG ; Anweisung an Windows, dass es mit der normalen Abarbeitung fortfahren kann
EndFunc   ;==>_WM_SIZE

Func _WM_GETMINMAXINFO($hWnd, $iMsg, $wParam, $lParam) ; diese Funktion verhindert, dass das Hauptfenster kleiner als 680x480 wird
	#forceref $iMsg, $wParam ; eine Anweisung an AutoIt, dass diese Variablen hier nicht verwendet werden
	; Die beiden folgenden Konstanten sind in der WinApi-UDF nicht enthalten.
	If Not IsDeclared('SM_CXSIZEFRAME') Then Local Const $SM_CXSIZEFRAME = 32
	If Not IsDeclared('SM_CYSIZEFRAME') Then Local Const $SM_CYSIZEFRAME = 33
	Local $iXmin, $iYmin, $MINMAXINFO = 0
	If $hWnd = $hMainGui Then ; die Mindestgroesse nur fuer das Hauptfenster begrenzen
		$MINMAXINFO = DllStructCreate('long ReservedX;long ReservedY;long MaxSizeX;long MaxSizeY;long MaxPositionX;long MaxPositionY;long MinTrackSizeX;long MinTrackSizeY;long MaxTrackSizeX;long MaxTrackSizeY', $lParam) ; MINMAXINFO-Struktur erstellen
		$iXmin = $iWidthMin + 2 * _WinAPI_GetSystemMetrics($SM_CXSIZEFRAME) ; min X = 680 + 2 * Fensterrahmen
		$iYmin = $iHeightMin + 2 * _WinAPI_GetSystemMetrics($SM_CYSIZEFRAME) ; min Y = 480 + 2 * Fensterrahmen
		$iYmin += _WinAPI_GetSystemMetrics($SM_CYCAPTION) ; min Y += Titelleiste
		$iYmin += _WinAPI_GetSystemMetrics($SM_CYMENUSIZE) ; min Y += Menueleiste
		DllStructSetData($MINMAXINFO, 'MinTrackSizeX', $iXmin) ; Mindestbreite festlegen
		DllStructSetData($MINMAXINFO, 'MinTrackSizeY', $iYmin) ; Mindesthoehe festlegen
	EndIf
	Return $GUI_RUNDEFMSG ; Anweisung an Windows, dass es mit der normalen Abarbeitung fortfahren kann
EndFunc   ;==>_WM_GETMINMAXINFO

Func _CloseMainGui() ; diese Funktion fuehrt zum beenden des Programms (vorher werden noch die Einstellungen gespeichert)
	If $fChangeDB Then ; wenn die Datenbank geaendert, aber noch nicht gespeichert wurde, dann...
		If Not _Confirm('Das Programm wirklich beenden?\nNicht gespeicherte Daten gehen verloren!') Then Return
	EndIf
	Local $sSection = '' ; zum erstellen der Sektion in der Inidatei
	For $i = 0 To $iColCount - 1 ; die Spalten der Datenbank durchgehen
		$sSection &= $i & '=' & $aColWidth[$i] & @LF ; alle Spaltenbreiten zusammenfassen
	Next
	IniWriteSection($sInifile, 'ColWidth', $sSection) ; und in der Inidatei abspeichern
	$fMaximize = BitAND(WinGetState($hMainGui), 32) = 32 ; ermitteln, ob das Hauptfenster maximiert wurde
	IniWrite($sInifile, 'Config', 'Maximize', $fMaximize) ; den Maximiert-Status abspeichern
	Local $aWinPos = WinGetPos($hMainGui) ; die letzte Position des Hauptfensters holen
	If Not $fMaximize And $aWinPos[0] > -32000 Then ; wenn das Hauptfenster weder maximiert noch minimiert war, dann...
		IniWrite($sInifile, 'Config', 'Left', $aWinPos[0]) ; die Position abspeichern
		IniWrite($sInifile, 'Config', 'Top', $aWinPos[1])
		IniWrite($sInifile, 'Config', 'Width', $aWinPos[2]) ; die Fenstergroesse abspeichern
		IniWrite($sInifile, 'Config', 'Height', $aWinPos[3])
	EndIf
	IniWrite($sInifile, 'Config', 'InitDir', $sInitDir) ; den zuletzt ausgewaehlten Pfad abspeichern
	For $hIcon In $ahIcons ; Schleife, um alle geladenen Icons durchzugehen
		_WinAPI_DestroyIcon($hIcon) ; die Icons aus dem Hauptspeicher entfernen
	Next
	GUIDelete($hMainGui) ; und das Hauptfenster schliessen (Programm beenden)
EndFunc   ;==>_CloseMainGui

Func _CloseInputGui() ; hier wird das Eingabefenster versteckt, nachdem die evtl. eingegebenen Daten uebernommen wurden
	If @GUI_CtrlId = $idApply Then ; wenn auf "uebernehmen" geklickt wurde, dann...
		Local $sText = ''
		If $iInputIndex = -1 Then ; wenn es sich um einen neuen Eintrag handelt, dann...
			ReDim $aDatabase[$iDataCount + 1][$i] ; das Datenbank-Array um einen Eintrag vergroessern
			Local $aNewItem[1][$iColCount] ; ein 2D-Array fuer den neuen Eintrag erstellen
			For $i = 0 To $iColCount - 1 ; alle Eingabefelder durchgehen
				$sText = GUICtrlRead($aidInput[$i]) ; den Text auslesen und
				$aNewItem[0][$i] = $sText ; in das Array fuer den neuen Eintrag
				$aDatabase[$iDataCount][$i] = $sText ; und in das Datenbank-Array uebernehmen
			Next
			_GUICtrlListView_AddArray($idListView, $aNewItem) ; den neuen Eintrag ins Listview eintragen
			$fChangeDB = True ; markieren, dass die Datenbank geaendert wurde
			$iDataCount += 1 ; den Zaehler fuer die Datenbank um eins erhoehen
			_GUICtrlStatusBar_SetText($hStatus, 'Einträge = ' & $iDataCount, 1) ; die rechte Statuszeile aktualisieren
		Else ; ansonsten (es handelt sich um einen bearbeiteten Eintrag)
			For $i = 0 To $iColCount - 1 ; alle Eingabefelder durchgehen
				$sText = GUICtrlRead($aidInput[$i]) ; den Text auslesen
				If $sText <> $aDatabase[$iInputIndex][$i] Then ; wenn der Text geaendert wurde, dann...
					_GUICtrlListView_SetItemText($idListView, $iInputIndex, $sText, $i) ; den Text in das Listview
					$aDatabase[$iInputIndex][$i] = $sText ; und in das Datenbank-Array uebernehmen
					$fChangeDB = True ; markieren, dass die Datenbank geaendert wurde
				EndIf
			Next
		EndIf
	EndIf
	; der Rest der Funktion wird sowohl beim "uebernehmen" als auch beim "abbrechen" abgearbeitet
	GUISetState(@SW_HIDE, $hInputGui) ; das Eingabefenster verstecken
	For $i = 0 To $iColCount - 1 ; alle Eingabefelder durchgehen
		GUICtrlDelete($aidInput[$i]) ; die Eingabefelder und
		GUICtrlDelete($aidLabel[$i]) ; die Label dazu loeschen
	Next
	$iInputIndex = -1 ; den Eingabeindex wieder auf "neuer Eintrag" setzen
EndFunc   ;==>_CloseInputGui

Func _OpenInputGui()
	Local $aWinPos = WinGetPos($hMainGui) ; die Position des Hauptfensters holen
	Local $aWinSize = WinGetClientSize($hMainGui) ; die Groesse des Hauptfensters holen
	Local Const $aJustify[3] = [$SS_LEFT, $SS_RIGHT, $SS_CENTER] ; Konstanten zur Ausrichtung
	WinMove($hInputGui, '', $aWinPos[0] + 8, $aWinPos[1] + 160, $aWinSize[0] - 8, 140) ; das Eingabefenster an das Hauptfenster anpassen
	GUISwitch($hInputGui) ; fuer das erstellen der Gui-Elemente auf das Eingabefenster umschalten
	Local $x = 10, $sText = ''
	For $i = 0 To $iColCount - 1 ; alle Eingabefelder durchgehen
		$aidLabel[$i] = GUICtrlCreateLabel($aHeader[$i], $x, 15, $aColWidth[$i] - 8, 20) ; ein Beschriftungslabel erstellen (mit den Header-Angaben)
		If $iInputIndex > -1 Then $sText = _GUICtrlListView_GetItemText($hListView, $iInputIndex, $i) ; wenn das Eingabefenster zum bearbeiten geoeffnet wurde, dann den Text aus dem Listview lesen
		Switch $i ; anhand der Spaltennummer verzweigen
			Case 0 ; wenn es die erste Spalte ist, dann...
				$sText = StringRegExpReplace($sText, '(\d{2})\.(\d{2})\.(\d{4})', '$3/$2/$1') ; das Datum nach yyyy/mm/dd wandeln
				$aidInput[$i] = GUICtrlCreateDate($sText, $x, 40, $aColWidth[$i] - 4, 20) ; ein Datum-Control-Element erstellen
				GUICtrlSetStyle(-1, $DTS_SHORTDATEFORMAT) ; den Stil auf "kurzes Datum" setzen (in deutsch = dd.mm.yyyy)
				; Das Datum-Control-Element erleichert nicht nur dem Anwender die Eingabe, es nimmt dem
				; Programmierer auch das ueberpruefen des Datums auf Gueltigkeit ab. Der Anwender kann dort
				; gar kein falsches Datum eingeben.
			Case Else ; ansonsten (die restlichen Spalten) darf beliebiger Text stehen
				$aidInput[$i] = GUICtrlCreateInput($sText, $x, 40, $aColWidth[$i] - 4, 20, $aJustify[$aColJustify[$i]]) ; ein Input-Control-Element erstellen (mit dem Text)
		EndSwitch
		$x += $aColWidth[$i] ; die X-Position erhoehen, damit das naechste Element neben dem Vorherigen erstellt wird
	Next
	GUICtrlSetPos($idBreak, $aWinSize[0] - 158, 80, 120, 25) ; den "abbrechen"-Button und
	GUICtrlSetPos($idApply, $aWinSize[0] - 288, 80, 120, 25) ; den "uebernehmen"-Button an das Eingabefenster anpassen
	GUICtrlSetState($aidInput[0], $GUI_FOCUS) ; den Eingabefocus auf das erste Eingabefeld setzen
	GUISetState(@SW_SHOW, $hInputGui) ; das Eingabefenster anzeigen
EndFunc   ;==>_OpenInputGui

Func _CopyMarkedItemsToCB()
	Local $hLV, $sSelIndices, $aIndices, $sClip
	Switch @GUI_CtrlId
		Case $idLVCopy
			$hLV = $hListView
		Case $idSearchLVCopy
			$hLV = $hSearchLV
	EndSwitch
	$sSelIndices = _GUICtrlListView_GetSelectedIndices($hLV)
	If $sSelIndices = '' Then Return
	$aIndices = StringSplit($sSelIndices, '|', 2)
	For $i = 0 To UBound($aIndices) - 1
		$sClip &= _GUICtrlListView_GetItemTextString($hLV, $aIndices[$i]) & @CRLF
	Next
	ClipPut($sClip)
EndFunc   ;==>_CopyMarkedItemsToCB

Func _SortListview()
	; Hinweis zu der hier gewaehlten Vorgehensweise!
	; Das Datenbank-Array zu sortieren und das Listview anzupassen geht schneller, als wenn man das Listview sortieren laesst!
	_GUICtrlStatusBar_SetIcon($hStatus, 0, $ahIcons[1]) ; Icon fuer die linke Seite der Statuszeile setzen
	_GUICtrlStatusBar_SetText($hStatus, 'Sortiere Datenbank! Bitte warten...', 0) ; Hinweis in der Statuszeile
	GUISetCursor(15, $GUI_CURSOR_OVERRIDE, $hMainGui) ; und den Mauscursor als rotierenden Zeiger anzeigen
	Local $iSortCol = GUICtrlGetState($idListView) ; auslesen, auf welche Spalte geklickt wurde
	Local $iSortArrow = _GUICtrlHeader_GetItemFlags($hLVHeader, $iSortCol) ; einen evtl. vorhandenen Sortierpfeil auslesen
	Local $iDescending ; Variable fuer die Sortierrichtung
	For $i = 0 To $iColCount - 1 ; alle Headerspalten durchgehen
		_GUICtrlHeader_SetItemFlags($hLVHeader, $i, 0) ; und den Pfeil entfernen
	Next
	If BitAND($iSortArrow, 4) Then ; wenn der Sortierpfeil nach unten gezeigt hat, dann...
		_GUICtrlHeader_SetItemFlags($hLVHeader, $iSortCol, 8) ; den Pfeil nach oben setzen
		$iDescending = 0 ; das Array aufsteigend sortieren
	Else ; ansonsten (Pfeil zeigte nach oben oder es war kein Pfeil vorhanden)
		_GUICtrlHeader_SetItemFlags($hLVHeader, $iSortCol, 4) ; den Pfeil nach unten setzen
		$iDescending = 1 ; das Array absteigend sortieren
	EndIf
	If $iSortCol = 0 Then _SwitchDateForSort($aDatabase, $iSortCol) ; in Spalte 0 steht das Datum, deshalb nach yyyy.mm.dd wandeln
	_ArraySort($aDatabase, $iDescending, 0, 0, $iSortCol) ; die Datenbank sortieren
	If $iSortCol = 0 Then _SwitchDateForList($aDatabase, $iSortCol) ; nach dem Sortieren das Datum wieder nach dd.mm.yyyy wandeln
	For $i = 0 To $iDataCount - 1 ; alle Datenbank-Eintraege (Zeilen) durchgehen
		For $j = 0 To $iColCount - 1 ; alle Datenbank-Eintraege (Spalten) durchgehen
			_GUICtrlListView_SetItemText($idListView, $i, $aDatabase[$i][$j], $j) ; und das Listview entsprechend der Neusortierung anpassen
		Next
	Next
	GUISetCursor(2, $GUI_CURSOR_OVERRIDE, $hMainGui) ; den Mauszeiger wieder zuruecksetzen
	_GUICtrlStatusBar_SetText($hStatus, 'Bereit.', 0) ; die linke Statuszeile aktualisieren
	_GUICtrlStatusBar_SetIcon($hStatus, 0, $ahIcons[0]) ; das Icon setzen
EndFunc   ;==>_SortListview

Func _SwitchDateForSort(ByRef $array, $iSubItem) ; Funktion, um eine Spalte, die ein Datum enthaelt, nach yyyy.mm.dd zu wandeln
	For $i = 0 To UBound($array) - 1
		$array[$i][$iSubItem] = StringRegExpReplace($array[$i][$iSubItem], '(\d{2})\.(\d{2})\.(\d{4})', '$3.$2.$1')
	Next
EndFunc   ;==>_SwitchDateForSort

Func _SwitchDateForList(ByRef $array, $iSubItem) ; Funktion, um eine Spalte, die ein Datum enthaelt, wieder nach dd.mm.yyyy zu wandeln
	For $i = 0 To UBound($array) - 1
		$array[$i][$iSubItem] = StringRegExpReplace($array[$i][$iSubItem], '(\d{4})\.(\d{2})\.(\d{2})', '$3.$2.$1')
	Next
EndFunc   ;==>_SwitchDateForList

Func _CreateDatabase()
	If $iDataCount = 0 Then Return ; wenn keine Eintraege im Listview vorhanden sind, dann die Funktion verlassen
	Local $sMsg = StringFormat('Wirklich eine neue Datenbank erstellen?\nAlle bisherigen Daten gehen verloren!')
	If Not _Confirm($sMsg) Then Return ; wenn es sich der Anwender anders ueberlegt hat, dann die Funktion verlassen
	_GUICtrlStatusBar_SetIcon($hStatus, 0, $ahIcons[1]) ; Icon fuer die linke Seite der Statuszeile setzen
	_GUICtrlStatusBar_SetText($hStatus, 'Erstelle neue Datenbank! Bitte warten...', 0)
	GUISetCursor(15, $GUI_CURSOR_OVERRIDE, $hMainGui) ; und den Mauscursor als rotierenden Zeiger anzeigen
	$iDataCount = 0 ; den Datenzaehler auf Null setzen
	ReDim $aDatabase[$iDataCount + 1][$iColCount] ; das Datenbank-Array anpassen
	$fChangeDB = False ; und die Datenbank auf "nicht geaendert" setzen
	_GUICtrlListView_DeleteAllItems($idListView) ; alle Eintraege aus dem Listview entfernen
	_GUICtrlStatusBar_SetText($hStatus, 'Bereit.', 0) ; die linke Statuszeile aktualisieren
	_GUICtrlStatusBar_SetIcon($hStatus, 0, $ahIcons[0]) ; das Icon setzen
	GUISetCursor(2, $GUI_CURSOR_OVERRIDE, $hMainGui) ; den Mauszeiger wieder zuruecksetzen
	_GUICtrlStatusBar_SetText($hStatus, 'Einträge = ' & $iDataCount, 1) ; die rechte Statuszeile aktualisieren
EndFunc   ;==>_CreateDatabase

Func _LoadDatabase() ; Funktion zum laden der Datenbankdatei (das laden der Daten geht relativ schnell, aber das anzeigen im Listview dauert rund 10mal so lange)
	Local $sPath = FileOpenDialog($sTitle, $sInitDir, 'Listview Datenbank (*' & $sFileExt & ')', $FD_FILEMUSTEXIST, '', $hMainGui)
	If Not FileExists($sPath) Then Return ; wenn die Datei nicht existiert, dann die Funktion verlassen
	$sInitDir = StringRegExpReplace($sPath, '(.+\\).+', '$1') ; den zuletzt ausgewaehlten Ordner merken
	_LoadFile($sPath)
EndFunc   ;==>_LoadDatabase

Func _LoadFile($sPath)
	_GUICtrlStatusBar_SetIcon($hStatus, 0, $ahIcons[1]) ; Icon fuer die linke Seite der Statuszeile setzen
	_GUICtrlStatusBar_SetText($hStatus, 'Lade Datenbank! Bitte warten...', 0)
	GUISetCursor(15, $GUI_CURSOR_OVERRIDE, $hMainGui) ; und den Mauscursor als rotierenden Zeiger anzeigen
	Sleep(50)
	Local $hFile = FileOpen($sPath, $FO_READ) ; die Datenbankdatei zum lesen oeffnen
	If $hFile = -1 Then Return _LoadReturn() ; wenn dabei ein Fehler aufgetreten ist, dann die Funktion verlassen
	Local $sFile = FileRead($hFile) ; die Datei komplett in den Hauptspeicher laden
	FileClose($hFile) ; und die geoeffnete Datei wieder schliessen
	If StringRight($sFile, 2) = @CRLF Then $sFile = StringTrimRight($sFile, 2) ; ein evtl. vorhandenes @CRLF am Ende entfernen
	If $sFile = '' Then Return _LoadReturn() ; wenn die Datei leer ist, dann die Funktion verlassen
	Local $aRow = StringSplit($sFile, @CRLF, $STR_ENTIRESPLIT + $STR_NOCOUNT) ; die Datei am Zeilenende splitten (erzeugt ein Array mit den Zeilen des Listviews)
	Local $aCol
	$iDataCount = UBound($aRow) ; die Anzahl der Zeilen ermitteln
	ReDim $aDatabase[$iDataCount][$iColCount] ; das Datenbank-Array entsprechend anpassen
	For $i = 0 To $iDataCount - 1 ; das Zeilen-Array durchgehen
		$aCol = StringSplit($aRow[$i], '|', $STR_NOCOUNT) ; die Zeile in die einzelnen Spalten splitten (erzeugt ein Array mit den Spalten des Listviews)
		If UBound($aCol) = $iColCount Then ; wenn die Spaltenanzahl mit der Spaltenanzahl des Listviews uebereinstimmt, dann...
			For $j = 0 To $iColCount - 1 ; das Spalten-Array durchgehen
				$aDatabase[$i][$j] = $aCol[$j] ; die Daten in das Datenbank-Array speichern
			Next
		EndIf
	Next
	_GUICtrlListView_DeleteAllItems($idListView) ; alle Eintraege aus dem Listview entfernen
	_GUICtrlListView_AddArray($idListView, $aDatabase) ; das Datenbank-Array im Listview anzeigen
	_LoadReturn()
EndFunc   ;==>_LoadFile

Func _LoadReturn()
	GUISetCursor(2, $GUI_CURSOR_OVERRIDE, $hMainGui) ; den Mauszeiger wieder zuruecksetzen
	_GUICtrlStatusBar_SetText($hStatus, 'Bereit.', 0) ; die linke Statuszeile aktualisieren
	_GUICtrlStatusBar_SetIcon($hStatus, 0, $ahIcons[0]) ; das Icon setzen
	_GUICtrlStatusBar_SetText($hStatus, 'Einträge = ' & $iDataCount, 1) ; die rechte Statuszeile aktualisieren
EndFunc   ;==>_LoadReturn

Func _SaveDatabase() ; Funktion zum speichern der Datenbank (das speichern geht sehr schnell, weil die Daten im Array vorliegen)
	If $iDataCount = 0 Then Return ; wenn keine Eintraege im Listview vorhanden sind, dann die Funktion verlassen
	Local $sPath = FileSaveDialog($sTitle, $sInitDir, 'Listview Datenbank (*' & $sFileExt & ')', $FD_PROMPTOVERWRITE, '', $hMainGui)
	If @error Then Return ; wenn der Anwender auf "Abbrechen" geklickt hat, dann die Funktion verlassen
	$sInitDir = StringRegExpReplace($sPath, '(.+\\).+', '$1') ; den zuletzt ausgewaehlten Ordner merken
	If StringRight($sPath, 5) <> $sFileExt Then $sPath &= $sFileExt ; wenn die ausgewaehlte Datei nicht die ".lvdb"-Dateiendung besitzt, dann diese hinzufuegen
	If FileExists($sPath) Then ; wenn die Datei bereits existiert, dann ein Backup der alten Datenbankdatei erstellen
		Local $sBKFile = StringFormat('%s\bak\db_%04d_%02d_%02d__%02d_%02d_%02d%s', @ScriptDir, @YEAR, @MON, @MDAY, @HOUR, @MIN, @SEC, $sFileExt) ; Pfad und Dateiname fuer das Backup generieren
		FileMove($sPath, $sBKFile, $FC_CREATEPATH + $FC_OVERWRITE) ; die alte Datei umbenennen und verschieben
	EndIf
	Local $hFile = FileOpen($sPath, $FO_CREATEPATH + $FO_OVERWRITE) ; die Datenbankdatei zum ueberschreiben oeffnen
	If $hFile = -1 Then Return ; wenn dabei ein Fehler aufgetreten ist, dann die Funktion verlassen
	_GUICtrlStatusBar_SetIcon($hStatus, 0, $ahIcons[1]) ; Icon fuer die linke Seite der Statuszeile setzen
	_GUICtrlStatusBar_SetText($hStatus, 'Speichere Datenbank! Bitte warten...', 0)
	GUISetCursor(15, $GUI_CURSOR_OVERRIDE, $hMainGui) ; und den Mauscursor als rotierenden Zeiger anzeigen
	Local $sLine = ''
	For $i = 0 To $iDataCount - 1 ; alle Zeilen der Datenbank durchgehen
		For $j = 0 To $iColCount - 1 ; alle Spalten der Datenbank durchgehen
			$sLine &= $aDatabase[$i][$j] & '|' ; die Daten mit Trennzeichen '|' in eine Zeile zusammensetzen
		Next
		FileWriteLine($hFile, StringTrimRight($sLine, 1)) ; die Zeile in die Datei schreiben (vorher das letzte Trennzeichen entfernen)
		$sLine = '' ; den Variableninhalt loeschen fuer die naechste Zeile
	Next
	FileClose($hFile) ; und die geoeffnete Datei wieder schliessen
	GUISetCursor(2, $GUI_CURSOR_OVERRIDE, $hMainGui) ; den Mauszeiger wieder zuruecksetzen
	$fChangeDB = False ; Datenbank wurde gespeichert, also Datenbankstatus auf "nicht geaendert" setzen
	_GUICtrlStatusBar_SetText($hStatus, 'Bereit.', 0) ; die Statuszeile aktualisieren
	_GUICtrlStatusBar_SetIcon($hStatus, 0, $ahIcons[0]) ; das Icon setzen
EndFunc   ;==>_SaveDatabase

Func _NewItem()
	$iInputIndex = -1 ; kennzeichnen, dass es sich um einen neuen Eintrag handelt
	_OpenInputGui() ; und das Eingabefenster oeffnen
EndFunc   ;==>_NewItem

Func _EditItem()
	Local $iSelItem = ControlListView($hMainGui, '', $idListView, 'GetSelected', 0) ; den Index des ersten markierten Eintrags aus dem Listview holen
	If $iSelItem = '' Then Return ; wenn Leerstring (kein Eintrag markiert), dann Funktion verlassen
	$iInputIndex = $iSelItem ; den Index fuer das Eingabefenster festlegen
	_OpenInputGui() ; und das Eingabefenster oeffnen
EndFunc   ;==>_EditItem

Func _DeleteItems()
	; Das loeschen mehrerer Eintraege aus dem Listview kann bei einer umfangreichen Datenbank
	; viel Zeit in Anspruch nehmen. "_GUICtrlListView_DeleteItemsSelected" ist nicht immer die
	; erste Wahl. Ab einer bestimmten Grenze geht es schneller alle Eintraege aus dem Listview
	; zu loeschen und sie komplett aus dem Datenbank-Array neu zu erstellen.
	; Ich habe die Grenze hier mal bei 500 zu loeschenden Eintraegen gesetzt. Diese Grenze basiert
	; auf einigen Tests, die ich durchgefuehrt habe. Es kann aber sein, dass sie auf anderen
	; Rechner-Konfigurationen hoeher oder niedriger liegt.
	Local $sSelItems = ControlListView($hMainGui, '', $idListView, 'GetSelected', 1) ; die Indizes aller markierten Eintraege aus dem Listview holen
	If $sSelItems = '' Then Return ; wenn Leerstring (kein Eintrag markiert), dann die Funktion verlassen
	If Not _Confirm('Sollen die markierten Einträge wirklich gelöscht werden?') Then Return ; wenn es sich der Anwender anders ueberlegt hat, dann die Funktion verlassen
	_GUICtrlStatusBar_SetIcon($hStatus, 0, $ahIcons[1]) ; Icon fuer die linke Seite der Statuszeile setzen
	_GUICtrlStatusBar_SetText($hStatus, 'Lösche markierte Einträge! Bitte warten...', 0)
	GUISetCursor(15, $GUI_CURSOR_OVERRIDE, $hMainGui) ; und den Mauscursor als rotierenden Zeiger anzeigen
	Local $aSelItems = StringSplit($sSelItems, '|') ; den String aufsplitten (wird ein Array)
	_ArrayDelete($aDatabase, $aSelItems) ; die markierten Eintraege aus dem Datenbank-Array loeschen
	If $aSelItems[0] < 500 Then ; wenn weniger als 500 Eintraege aus dem Listview entfernt werden sollen, dann...
		_GUICtrlListView_DeleteItemsSelected($idListView) ; geht das so schneller
	Else ; ansonsten (bei mehr als 500 Eintraegen) ist es schneller alle Eintraege zu loeschen und sie neu zu erstellen
		_GUICtrlListView_DeleteAllItems($idListView) ; alle Eintraege aus dem Listview entfernen
		_GUICtrlListView_AddArray($idListView, $aDatabase) ; das Datenbank-Array im Listview anzeigen
	EndIf
	$iDataCount = UBound($aDatabase) ; die Anzahl der Eintraege korrigieren
	_GUICtrlStatusBar_SetText($hStatus, 'Einträge = ' & $iDataCount, 1) ; die rechte Statuszeile aktualisieren
	$fChangeDB = True ; markieren, dass die Datenbank geaendert wurde
	GUISetCursor(2, $GUI_CURSOR_OVERRIDE, $hMainGui) ; den Mauszeiger wieder zuruecksetzen
	_GUICtrlStatusBar_SetText($hStatus, 'Bereit.', 0) ; die Statuszeile aktualisieren
	_GUICtrlStatusBar_SetIcon($hStatus, 0, $ahIcons[0]) ; das Icon setzen
EndFunc   ;==>_DeleteItems

Func _Confirm($sMsg) ; Diese Funktion fragt den Anwender, ob er eine bestimmte Aktion wirklich durchfuehren will
	; Ich verwende sie beim loeschen der markierten Eintraege, beim erstellen einer neuen Datenbank
	; und beim beenden des Programms
	Local $iOldEventMode = Opt('GUIOnEventMode', 0) ; auf den MessageLoop-Modus umschalten (vorherigen Status merken)
	Local $aWinPos = WinGetPos($hMainGui) ; die Position des Hauptfensters holen
	Local $x = $aWinPos[0] + $aWinPos[2] / 2 - 200 ; horizontal auf den Koordinaten des Hauptfenster zentrieren
	Local $y = $aWinPos[1] + $aWinPos[3] / 2 - 60 ; vertikal auf den Koordinaten des Hauptfenster zentrieren
	Local $hConfirmGui = GUICreate(StringFormat('%s - Nachfrage', $sTitle), 400, 120, $x, $y, BitOR($WS_CAPTION, $WS_POPUPWINDOW), Default, $hMainGui) ; das Nachfragefenster erstellen
	GUISetBkColor(0xF0C0A0, $hConfirmGui) ; die Hintergrundfarbe fuer das Nachfragefenster setzen
	GUISetIcon('shell32.dll', -24, $hConfirmGui) ; Fenstericon setzen
	GUISetFont(12, 400, 0, 'Verdana', $hConfirmGui) ; den Standard-Zeichensatz fuer das Nachfragefenster setzen
	GUICtrlCreateGroup('', 5, 5, 385, 70) ; Gruppe erstellen
	GUICtrlCreateLabel(StringFormat($sMsg), 20, 25, 360, 40) ; ein Textfeld erstellen
	GUICtrlCreateGroup('', -99, -99, 0, 0) ; die Gruppe schliessen
	Local $idYes = GUICtrlCreateButton('Ja', 260, 85, 60, 25) ; den "Ja"-Button erstellen
	Local $idNo = GUICtrlCreateButton('Nein', 325, 85, 60, 25, $BS_DEFPUSHBUTTON) ; den "Nein"-Button erstellen
	GUISetState(@SW_DISABLE, $hMainGui) ; das Hauptfenster deaktivieren
	GUISetState(@SW_DISABLE, $hInputGui) ; das Eingabefenster deaktivieren
	GUISetState(@SW_SHOW, $hConfirmGui) ; das Nachfragefenster anzeigen
	Local $fReturn = False ; die Standard-Rueckgabe (False), wenn "Nein" angeklickt oder das Fenster geschlossen wird
	While True ; eine Endlosschleife
		Switch GUIGetMsg() ; hier wird der MessageLoop-Modus abgefragt
			Case $idNo, $GUI_EVENT_CLOSE ; wenn das Nachfragefenster geschlossen wird, dann...
				ExitLoop ; die Endlosschleife verlassen
			Case $idYes ; wenn auf "Ja" geklickt wurde, dann...
				$fReturn = True ; den Rueckgabewert auf True setzen
				ExitLoop ; und die Endlosschleife verlassen
		EndSwitch
	WEnd
	GUISetState(@SW_ENABLE, $hInputGui) ; das Eingabefenster wieder aktivieren
	GUISetState(@SW_ENABLE, $hMainGui) ; das Hauptfenster wieder aktivieren
	GUIDelete($hConfirmGui) ; das Nachfragefenster loeschen
	Opt('GUIOnEventMode', $iOldEventMode) ; und den alten EventModus wiederherstellen
	Return $fReturn
EndFunc   ;==>_Confirm

Func _SearchDBArray($sSearch) ; hier wird das Datenbank-Array durchsucht und die Zeilen der Suchtreffer als Array zurueckgegeben
	Local $sIndizes = '', $error = 1, $aRet, $aSearch, $iCheck, $iSum
	Local $sWord, $iCase = 0, $iTmp
	$aSearch = StringRegExp($sSearch, '((?:\+|\-)[^+-]+)', 3) ; die Eingabe in Plus- und Minus-Suchbegriffe trennen
	If @error Then Return SetError($error, 0, $aRet) ; wenn kein Suchbegriff gefunden, entsprechendes zurueckgeben
;~ 	_ArrayDisplay($aSearch)
	For $i = 0 To $iDataCount - 1 ; alle Zeilen der Datenbank durchsuchen
		$iCheck = 0
		$iSum = 0
		For $k = 0 To UBound($aSearch) - 1 ; alle Suchbegriffe durchgehen
			$sWord = StringRegExpReplace(StringMid($aSearch[$k], 2), '"(.+)"', '$1')
			$iCase = @extended
			$iSum += 1
			Switch StringLeft($aSearch[$k], 1)
				Case '+' ; bei einem Plus-Suchbegriff (muss in der Zeile vorhanden sein)
					For $j = 0 To $iColCount - 1 ; alle Spalten der Datenbank durchsuchen
						If StringInStr($aDatabase[$i][$j], $sWord, $iCase) Then ; wenn Suchbegriff gefunden
							$iCheck += 1 ; dann: ok!
							ExitLoop ; die uebrigen Spalten brauchen dann nicht mehr durchsucht werden
						EndIf
					Next
				Case '-' ; bei einem Minus-Suchbegriff (darf in der Zeile nicht vorhanden sein)
					$iTmp = 0
					For $j = 0 To $iColCount - 1 ; alle Spalten der Datenbank durchsuchen
						If StringInStr($aDatabase[$i][$j], $sWord, $iCase) Then $iTmp += 1
					Next
					If $iTmp = 0 Then $iCheck += 1 ; wenn Suchbegriff nicht vorhanden, dann: ok!
			EndSwitch
		Next
		If $iCheck = $iSum Then ; wenn alle Suchbegriffe zutreffend sind
			$sIndizes &= $i & '|' ; den Zeilen-Index in den Ergebnisstring aufnehmen
			$error = 0 ; die error-Variable auf Null setzen, weil etwas gefunden wurde
		EndIf
	Next
	$aRet = StringSplit(StringTrimRight($sIndizes, 1), '|', 2) ; den String mit den Indizes in ein Array splitten
	Return SetError($error, 0, $aRet) ; und an die aufrufende Funktion zurueckgeben
EndFunc   ;==>_SearchDBArray

Func _SearchItem()
	If $iDataCount = 0 Then Return ; wenn keine Eintraege im Listview vorhanden sind, dann die Funktion verlassen
	Local $sSearch = GUICtrlRead($idSearch) ; den Suchbegriff auslesen
	If $sSearch = '' Then ; wenn ein Leerstring uebergeben wurde, dann...
		_SearchReset() ; Suche zuruecksetzen
		Return ; Funktion verlassen
	EndIf
	_GUICtrlListView_DeleteAllItems($idSearchLV) ; alle Eintraege aus dem Suche-Listview entfernen
	GUICtrlSetState($idListView, $GUI_HIDE) ; das normale Listview verstecken
	GUICtrlSetState($idSearchLV, $GUI_SHOW) ; und das Suche-Listview anzeigen
	Local $aFind = _SearchDBArray($sSearch) ; ein Array mit den Indizes zu den Suchtreffern erstellen
	If @error Then ; wenn ein Fehler (kein Suchtreffer) aufgetreten ist, dann den Anwender informieren
		GUICtrlSetBkColor($idSearchLV, 0xFFBBBB) ; die Hintergrundfarbe fuer das Suche-Listview auf rot setzen
		_GUICtrlStatusBar_SetIcon($hStatus, 0, $ahIcons[1]) ; Icon fuer die linke Seite der Statuszeile setzen
		_GUICtrlStatusBar_SetText($hStatus, 'Kein Eintrag zu dem Suchbegriff gefunden!', 0)
	Else ; ansonsten (Suchtreffer vorhanden)
		GUICtrlSetBkColor($idSearchLV, 0xDDFFDD) ; die Hintergrundfarbe fuer das Suche-Listview auf gruen setzen
		Local $iFoundCount = UBound($aFind) ; die Anzahl der Suchtreffer speichern
		Local $aFindData[$iFoundCount][$iColCount] ; ein 2D-Array fuer die, zu dem Suchbegriff, passenden Daten erstellen
		For $i = 0 To $iFoundCount - 1 ; das Array mit den Indizes zu den Suchtreffern durchgehen
			For $j = 0 To $iColCount - 1 ; Schleife fuer die Spalten der Datenbank
				$aFindData[$i][$j] = $aDatabase[$aFind[$i]][$j] ; die Daten aus der Datenbank ins "gefunden"-Array aufnehmen
			Next
		Next
		Local $sText = StringFormat('%d Eintr%s gefunden!', $iFoundCount, $iFoundCount = 1 ? 'ag' : 'äge')
		_GUICtrlListView_AddArray($idSearchLV, $aFindData) ; das "gefunden"-Array im Suche-Listview anzeigen
		_GUICtrlStatusBar_SetIcon($hStatus, 0, $ahIcons[0]) ; Icon fuer die linke Seite der Statuszeile setzen
		_GUICtrlStatusBar_SetText($hStatus, $sText, 0) ; die Anzahl der Suchtreffer anzeigen
	EndIf
EndFunc   ;==>_SearchItem

Func _SearchReset()
	GUICtrlSetState($idSearchLV, $GUI_HIDE) ; das Suche-Listview verstecken
	GUICtrlSetState($idListView, $GUI_SHOW) ; und das normale Listview wieder anzeigen
	_GUICtrlStatusBar_SetText($hStatus, 'Bereit.', 0) ; die Statuszeile aktualisieren
	_GUICtrlStatusBar_SetIcon($hStatus, 0, $ahIcons[0]) ; das Icon setzen
EndFunc   ;==>_SearchReset

Func _ProgInfo() ; Funktion, um die Programminformationen auszugeben
	; Natuerlich haette ich hier auch einfach eine MsgBox benutzen koennen, um die Informationen auszugeben,
	; aber weil sich das Hauptfenster ja auch auf einem anderen Monitor (bei Multi-Monitor-Betrieb) befinden
	; kann, habe ich mir gedacht, dass ich ein eigenes Infofenster erstelle und dieses ueber dem Hauptfenster
	; zentriere.
	; So nebenbei lernt man hier, wie man im Hauptfenster den OnEvent-Modus benutzt, in dieser Funktion aber
	; den MessageLoop-Modus (GuiGetMsg) verwendet. Man kann also den Modus innerhalb des Programms umschalten,
	; muss aber am Ende der Funktion darauf achten, den alten Modus wiederherzustellen.
	; Ausserdem sollte man beachten, dass man die anderen Fenster deaktiviert, weil der Anwender sonst mit den
	; vielen Fenstern durcheinander kommen kann.
	Local $iOldEventMode = Opt('GUIOnEventMode', 0) ; auf den MessageLoop-Modus umschalten (vorherigen Status merken)
	Local $aWinPos = WinGetPos($hMainGui) ; die Position des Hauptfensters holen
	Local $x = $aWinPos[0] + $aWinPos[2] / 2 - 200 ; horizontal auf den Koordinaten des Hauptfenster zentrieren
	Local $y = $aWinPos[1] + $aWinPos[3] / 2 - 95 ; vertikal auf den Koordinaten des Hauptfenster zentrieren
	Local $hInfoGui = GUICreate(StringFormat('%s - Info', $sTitle), 420, 185, $x, $y, BitOR($WS_CAPTION, $WS_POPUPWINDOW), Default, $hMainGui) ; das Infofenster erstellen
	GUISetBkColor(0xF0C0A0, $hInfoGui) ; die Hintergrundfarbe fuer das Infofenster setzen
	GUISetIcon('shell32.dll', -222, $hInfoGui) ; Fenstericon setzen
	GUISetFont(14, 400, 2, 'Verdana', $hInfoGui) ; den Standard-Zeichensatz fuer das Infofenster setzen
	Local $sMsg = StringFormat('Version: %s\nDatum:  %s\nAutor:    %s', $sVersion, $sDate, $sAuthor)
	GUICtrlCreateGroup('', 10, 5, 400, 135) ; eine Gruppe erstellen
	GUICtrlCreateLabel($sMsg, 25, 25, 320, 70) ; ein Textfeld erstellen
	GUICtrlCreateIcon('shell32.dll', -15, 370, 95, 32, 32) ; das Homepage-Icon erstellen
	GUICtrlCreateLabel('Homepage:', 25, 100, 110, 30) ; ein Textfeld erstellen
	Local $idLink = GUICtrlCreateLabel('forum.technik-hobby.de', 140, 100, 220, 30) ; ein Textfeld als Link erstellen
	GUICtrlSetFont(-1, 14, 400, 6, 'Verdana') ; Zeichensatz fuer das Textfeld setzen (hier zusaetzlich unterstrichen)
	GUICtrlSetColor(-1, 0x0000FF) ; die Textfarbe setzen
	GUICtrlSetCursor(-1, 0) ; und den Mauscursor beim "ueberfahren" auf das Handsymbol setzen
	GUICtrlCreateGroup('', -99, -99, 1, 1) ; die Gruppe schliessen
	Local $idOk = GUICtrlCreateButton('Ok', 170, 150, 80, 28) ; einen "Ok"-Button erstellen
	GUISetState(@SW_DISABLE, $hMainGui) ; das Hauptfenster deaktivieren
	GUISetState(@SW_DISABLE, $hInputGui) ; das Eingabefenster deaktivieren
	GUISetState(@SW_SHOW, $hInfoGui) ; das Infofenster anzeigen
	While True ; eine Endlosschleife
		Switch GUIGetMsg() ; hier wird der MessageLoop-Modus abgefragt
			Case $idOk, $GUI_EVENT_CLOSE ; wenn das Infofenster geschlossen wird, dann...
				ExitLoop ; die Endlosschleife verlassen
			Case $idLink ; wenn der Link angeklickt wird, dann...
				ShellExecute('https://forum.technik-hobby.de/index.php') ; diese URL aufrufen
		EndSwitch
	WEnd
	GUISetState(@SW_ENABLE, $hInputGui) ; das Eingabefenster wieder aktivieren
	GUISetState(@SW_ENABLE, $hMainGui) ; das Hauptfenster wieder aktivieren
	GUIDelete($hInfoGui) ; das Infofenster loeschen
	Opt('GUIOnEventMode', $iOldEventMode) ; und den alten EventModus wiederherstellen
EndFunc   ;==>_ProgInfo
#EndRegion Funktionen, die ueber OnEvent aufgerufen werden

