﻿#include-once
#include <WinAPISysInternals.au3>
#include <WinAPISysWin.au3>
#include <WindowsConstants.au3>

OnAutoItExitRegister('_WindowMinMax_Exit')

Global $__g_ahMinMax_Gui[0][3] ; 0 = hWnd, 1 = ProcOld, 2 = MinMax-Array[4] (Array in Array)
Global $__g_hMinMax_ProcNew = DllCallbackRegister('_WindowMinMax_Proc', 'int', 'hwnd;uint;wparam;lparam')

Global Const $__g_sMinMax_Version = '1.0.0.0'
Global Const $__g_sMinMax_Date = '2018/07/15 07:00:00'

; #FUNCTION# ====================================================================================================================
; Name ..........: _WindowMinMax_Exit
; Description ...: Wird beim beenden des Scripts zum aufraeumen aufgerufen
; Syntax ........: _WindowMinMax_Exit()
; Parameters ....: None
; Return values .: None
; Author ........: Oscar (www.autoit.de)
; ===============================================================================================================================
Func _WindowMinMax_Exit()
	Local $iCount = UBound($__g_ahMinMax_Gui)
	If $iCount > 0 Then
		For $i = 0 To $iCount - 1
			_WinAPI_SetWindowLong($__g_ahMinMax_Gui[$i][0], $GWL_WNDPROC, $__g_ahMinMax_Gui[$i][1])
		Next
	EndIf
	DllCallbackFree($__g_hMinMax_ProcNew)
EndFunc   ;==>_WindowMinMax_Exit

; #FUNCTION# ====================================================================================================================
; Name ..........: _WindowMinMax_Add
; Description ...: Schaltet fuer das angegebene Fenster die Begrenzung auf die MinMax-Werte ein
; Syntax ........: _WindowMinMax_Add($hWnd, $aMinMax)
; Parameters ....: $hWnd                - das Fensterhandle
;                  $aMinMax             - ein Array mit 4 Werten [MinWidth, MinHeight, MaxWidth, MaxHeight]
; Return values .: Im Erfolgsfall "True", ansonsten "False" und
;                  @error: 1, wenn $hWnd kein Fensterhandle
;                          2, wenn kein Array uebergeben wurde oder wenn es keine 4 Werte enthaelt
; Author ........: Oscar (www.autoit.de)
; ===============================================================================================================================
Func _WindowMinMax_Add($hWnd, $aMinMax)
	If Not IsHWnd($hWnd) Then Return SetError(1, 0, False)
	If Not IsArray($aMinMax) Or UBound($aMinMax) <> 4 Then Return SetError(2, 0, False)
	Local $iCount = UBound($__g_ahMinMax_Gui) + 1
	ReDim $__g_ahMinMax_Gui[$iCount][3]
	$__g_ahMinMax_Gui[$iCount - 1][0] = $hWnd
	$__g_ahMinMax_Gui[$iCount - 1][1] = _WinAPI_SetWindowLong($hWnd, $GWL_WNDPROC, DllCallbackGetPtr($__g_hMinMax_ProcNew))
	$__g_ahMinMax_Gui[$iCount - 1][2] = $aMinMax ; Write "Array in Array"
	Return SetError(0, 0, True)
EndFunc   ;==>_WindowMinMax_Add

; #FUNCTION# ====================================================================================================================
; Name ..........: _WindowMinMax_Remove
; Description ...: Schaltet fuer das angegebene Fenster die Begrenzung auf die MinMax-Werte aus
; Syntax ........: _WindowMinMax_Remove($hWnd)
; Parameters ....: $hWnd                - das Fensterhandle
; Return values .: Im Erfolgsfall "True", ansonsten "False" und
;                  @error: 1, wenn $hWnd kein Fensterhandle
;                          2, wenn im globalen Array keine Eintraege vorhanden sind
;                          3, wenn das Fensterhandle nicht im globalen Array vorhanden ist
; Author ........: Oscar (www.autoit.de)
; ===============================================================================================================================
Func _WindowMinMax_Remove($hWnd)
	Local $iIndex = _WindowMinMax_GetIndex($hWnd)
	If @error Then Return
	_WinAPI_SetWindowLong($__g_ahMinMax_Gui[$iIndex][0], $GWL_WNDPROC, $__g_ahMinMax_Gui[$iIndex][1])
	For $j = 0 To UBound($__g_ahMinMax_Gui, $UBOUND_COLUMNS) - 1
		$__g_ahMinMax_Gui[$iIndex][$j] = $__g_ahMinMax_Gui[UBound($__g_ahMinMax_Gui) - 1][$j]
	Next
	ReDim $__g_ahMinMax_Gui[UBound($__g_ahMinMax_Gui) - 1][3]
	Return SetError(0, 0, True)
EndFunc   ;==>_WindowMinMax_Remove

; #FUNCTION# ====================================================================================================================
; Name ..........: _WindowMinMax_SetMinMax
; Description ...: Uebergibt das neue Array mit den Begrenzungen oder (falls kein Array) schaltet die Begrenzung ab
; Syntax ........: _WindowMinMax_SetMinMax($hWnd, $aMinMax)
; Parameters ....: $hWnd                - das Fensterhandle
;                  $aMinMax             - [optional] ein Array mit 4 Werten [MinWidth, MinHeight, MaxWidth, MaxHeight]
; Return values .: Im Erfolgsfall "True", ansonsten "False" und
;                  @error: 1, wenn $hWnd kein Fensterhandle
;                          2, wenn im globalen Array keine Eintraege vorhanden sind
;                          3, wenn das Fensterhandle nicht im globalen Array vorhanden ist
; Author ........: Oscar (www.autoit.de)
; ===============================================================================================================================
Func _WindowMinMax_SetMinMax($hWnd, $aMinMax = 0)
	If Not IsArray($aMinMax) Or UBound($aMinMax) <> 4 Then Local $aMinMax[4] = [-1, -1, -1, -1]
	Local $iIndex = _WindowMinMax_GetIndex($hWnd)
	If @error Then Return
	$__g_ahMinMax_Gui[$iIndex][2] = $aMinMax ; Write "Array in Array"
	Return SetError(0, 0, True)
EndFunc   ;==>_WindowMinMax_SetMinMax

; #FUNCTION# ====================================================================================================================
; Name ..........: _WindowMinMax_GetIndex
; Description ...: Wird nur intern verwendet, um anhand des Fensterhandle den Array-Index zu ermitteln
; Syntax ........: _WindowMinMax_GetIndex($hWnd)
; Parameters ....: $hWnd                - das Fensterhandle
; Return values .: Im Erfolgsfall den Array-Index, ansonsten
;                  @error: 1, wenn $hWnd kein Fensterhandle
;                          2, wenn im globalen Array keine Eintraege vorhanden sind
;                          3, wenn das Fensterhandle nicht im globalen Array vorhanden ist
; Author ........: Oscar (www.autoit.de)
; ===============================================================================================================================
Func _WindowMinMax_GetIndex($hWnd)
	If Not IsHWnd($hWnd) Then Return SetError(1)
	Local $iCount = UBound($__g_ahMinMax_Gui)
	If $iCount = 0 Then Return SetError(2)
	For $iIndex = 0 To $iCount - 1
		If $__g_ahMinMax_Gui[$iIndex][0] = $hWnd Then Return $iIndex
	Next
	Return SetError(3)
EndFunc   ;==>_WindowMinMax_GetIndex

; #FUNCTION# ====================================================================================================================
; Name ..........: _WindowMinMax_Proc
; Description ...: Die neue WindowProc, die dem Fenster bei "_WindowMinMax_Add" zugewiesen wird.
; Syntax ........: _WindowMinMax_Proc($hWnd, $iMsg, $wParam, $lParam)
; Return values .: None
; Author ........: Oscar (www.autoit.de)
; ===============================================================================================================================
Func _WindowMinMax_Proc($hWnd, $iMsg, $wParam, $lParam)
	Local $iIndex, $hOldProc, $aMinMax, $tMinMaxInfo, $tWindowInfo, $iWindowStyle, $iCYCaption, $iMinW, $iMinH, $iMaxW, $iMaxH
	Local Const $tagMINMAXINFO = 'struct;' & _
			'struct;long Reserved[2];endstruct;' & _     ; POINT Reserved, do not use!
			'struct;long MaxSize[2];endstruct;' & _      ; POINT MaxSize, the maximized width and height of the window
			'struct;long MaxPosition[2];endstruct;' & _  ; POINT MaxPosition, the position (left, top) of the maximized window
			'struct;long MinTrackSize[2];endstruct;' & _ ; POINT MinTrackSize, the minimum tracking width and height
			'struct;long MaxTrackSize[2];endstruct;' & _ ; POINT MaxTrackSize, the maximum tracking width and height
			'endstruct'
	$iIndex = _WindowMinMax_GetIndex($hWnd) ; Get Index of global Array
	$hOldProc = $__g_ahMinMax_Gui[$iIndex][1]
	Switch $iMsg
		Case $WM_DESTROY
			_WindowMinMax_Remove($hWnd)
		Case $WM_GETMINMAXINFO ; When the size or position of the window is about to change.
			$aMinMax = $__g_ahMinMax_Gui[$iIndex][2] ; Read "Array in Array" (values of the client area)
			$tMinMaxInfo = DllStructCreate($tagMINMAXINFO, $lParam) ; Get MINMAXINFO-Struktur
			$tWindowInfo = _WinAPI_GetWindowInfo($hWnd) ; Get WINDOWINFO-Struktur
			$iWindowStyle = DllStructGetData($tWindowInfo, 'Style')
			$iCYCaption = BitAND($iWindowStyle, $WS_CAPTION) ? _WinAPI_GetSystemMetrics($SM_CYCAPTION) : 0
			$iMinW = $aMinMax[0] + DllStructGetData($tWindowInfo, 'cxWindowBorders') * 2
			$iMinH = $aMinMax[1] + DllStructGetData($tWindowInfo, 'cyWindowBorders') * 2 + $iCYCaption
			$iMaxW = $aMinMax[2] + DllStructGetData($tWindowInfo, 'cxWindowBorders') * 2
			$iMaxH = $aMinMax[3] + DllStructGetData($tWindowInfo, 'cyWindowBorders') * 2 + $iCYCaption
			If $aMinMax[0] >= 0 Then DllStructSetData($tMinMaxInfo, 'MinTrackSize', $iMinW, 1) ; Set min width
			If $aMinMax[1] >= 0 Then DllStructSetData($tMinMaxInfo, 'MinTrackSize', $iMinH, 2) ; Set min height
			If $aMinMax[2] >= 0 Then DllStructSetData($tMinMaxInfo, 'MaxTrackSize', $iMaxW, 1) ; Set max width
			If $aMinMax[3] >= 0 Then DllStructSetData($tMinMaxInfo, 'MaxTrackSize', $iMaxH, 2) ; Set max height
	EndSwitch
	Return DllCall('user32.dll', 'lresult', 'CallWindowProc', 'ptr', $hOldProc, 'hwnd', $hWnd, 'uint', $iMsg, 'wparam', $wParam, 'lparam', $lParam)[0]
	; Bug in "_WinAPI_CallWindowProc" (https://autoit.de/index.php?thread/86028-mehrere-udfs-mit-windowproc-verketten/&postID=690695#post690695)
EndFunc   ;==>_WindowMinMax_Proc
