#include-once

#include <GUIConstants.au3>
#include <StaticConstants.au3>
#Include <Array.au3>
#include <winapi.au3>

; #INDEX# =======================================================================================================================
; Title .........: Handler für Ereignisse durch Maus
; AutoIt Version : geschrieben mit AutoIt v3.3.6.1
; test ..........: WindowsXP Professional, SP3
; Language ......: Deutsch
; Description ...: Erkennt Ereignisse und führt die zugeordneten Funktionen aus.
; Author(s) .....: Viktor Truderung
; Dll ...........: user32.dll
; Version .......: V1.2
; Last Update ...: 30.06.2010
; ------------------------------------------------------------------------------

; # Beschreibung der Verarbeitung der Ereignisse ================================================================================
; ================================================================================
; Mit Hilfe von _GUISetOnEvent kann einer GUI ein event listener hinzugefügt werden. Dabei gelten die Standard-Events und die hier
; zusätzlich angebotenen, s. #CONSTANTS#.
; Mit _GUICtrlSetOnEvent kann einem Control innerhalb einer GUI ein listener zugeordnet werden. Bei Controls dagegen gelten die
; Standard-Events wie bei der GUI nicht. Lediglich die hier angebotenen Aktionen sind möglich.
;
; Ist ein Ereignis ausgelöst, so wird die dem zugeordnete Funktion ausgeführt. In der Regel treten zugleich (in kurzer Zeit)
; mehrere Ereignisse auf, daher werden diese entsprechend der folgenden Auflistung verarbeitet.
;~ #Priorität der Verarbeitung (analog Secondary)
;~ 1. $EVENT_PRIMARY_DOWN
;~ 2. $EVENT_PRIMARY_UP
;~ 3. $EVENT_PRIMARY_CLICK (= $EVENT_PRIMARY_DOWN + $EVENT_PRIMARY_UP)
;~ 4a. $EVENT_PRIMARY_DBLCLICK (= $EVENT_PRIMARY_CLICK + $EVENT_PRIMARY_DOWN) bzw.
;~ 4b. $EVENT_PRIMARY_SECONDARY_FLIP (= $EVENT_PRIMARY_CLICK + $EVENT_SECONDARY_DOWN)
;~ 5. $EVENT_PRIMARY_TRICLICK (= $EVENT_PRIMARY_CLICK + $EVENT_PRIMARY_CLICK + $EVENT_PRIMARY_DOWN)
;
; Ein Event hat eine Lebensdauer, wenn diese überschritten wird, endet, nach Abarbeitung der zuletzt gestarteten Funktion,
; auch die Verarbeitung der events.
; D.h. Dauert die Funktion des eventuell definierten Ereignisses $EVENT_PRIMARY_DOWN zulange, kann $EVENT_PRIMARY_UP, wenn es 
; zu weit zurückliegt, nicht mehr verarbeitet werden.
; Ebenfalls wird die Verarbeitung abgebrochen, wenn in dieser Zeit ein anderes Control den Fokus erhält.
; D.h. Springt bei $EVENT_PRIMARY_DOWN eine Msgbox auf, findet auch kein $EVENT_PRIMARY_UP statt.
; ================================================================================


#region Definitionen
; #CONSTANTS# ===================================================================================================================
; events
Global Const $EVENT_PRIMARY_DOWN = 1
Global Const $EVENT_PRIMARY_UP = 2
Global Const $EVENT_SECONDARY_DOWN = 4
Global Const $EVENT_SECONDARY_UP = 8
Global Const $EVENT_PRIMARY_CLICK = 16
Global Const $EVENT_PRIMARY_DBLCLICK = 32
Global Const $EVENT_PRIMARY_TRICLICK = 64
Global Const $EVENT_SECONDARY_CLICK = 128
Global Const $EVENT_SECONDARY_DBLCLICK = 256
Global Const $EVENT_SECONDARY_TRICLICK = 512
Global Const $EVENT_PRIMARY_SECONDARY_FLIP = 1024
Global Const $EVENT_SECONDARY_PRIMARY_FLIP = 2048

; standard events
;~ Global Const $GUI_EVENT_CLOSE = -3
;~ Global Const $GUI_EVENT_MINIMIZE = -4
;~ Global Const $GUI_EVENT_RESTORE = -5
;~ Global Const $GUI_EVENT_MAXIMIZE = -6
;~ Global Const $GUI_EVENT_PRIMARYDOWN = -7
;~ Global Const $GUI_EVENT_PRIMARYUP = -8
;~ Global Const $GUI_EVENT_SECONDARYDOWN = -9
;~ Global Const $GUI_EVENT_SECONDARYUP = -10
;~ Global Const $GUI_EVENT_MOUSEMOVE = -11
;~ Global Const $GUI_EVENT_RESIZED = -12
;~ Global Const $GUI_EVENT_DROPPED = -13

; #VARIABLES# ===================================================================================================================
Local $pos, $oldPos
Local $clickspeed = RegRead("HKEY_CURRENT_USER\Control Panel\Mouse", "DoubleClickSpeed")
Local $EventFunctionContainer[1]
Local $currentEvent = 0
Local $start
#endregion Definition

; #CURRENT# =====================================================================================================================
; _GUISetOnEvent
; _GUICtrlSetOnEvent
; ===============================================================================================================================

; #INTERNAL_USE_ONLY# ===========================================================================================================
; __registerGUI
; __primaryDown
; __primaryUp
; __doEvent
; __quit
; __isStandardEvent
; __isLegalEvent
; __fitEvent
; __addEventControl
; __getControlFunction
; __isMouseOverControl
; ===============================================================================================================================

#region Funktionen
Func _GUISetOnEvent($event, $function, $winhandle)
	if not IsHWnd($winhandle) then $winhandle = WinGetHandle($winhandle)
	if not IsHWnd($winhandle) then __quit("Falscher Parameter in _GUISetOnEvent($event, $function, $winhandle)"&@CRLF)

	$event = __fitEvent($event)
	If __isStandardEvent($event) Then
		GUISetOnEvent($event, $function, $winhandle)
	ElseIf __isLegalEvent($event) Then
		__registerGUI($winhandle)
		__addEventControl($EventFunctionContainer, $event&"_"&$winhandle&"_"&$winhandle&","&$function)
	Else
		__quit("Ereignis unbekannt: "&$event&@CRLF)
	EndIf
EndFunc

Func _GUICtrlSetOnEvent($event, $function, $winhandle, $ctrlhandle)
	if not IsHWnd($winhandle) then $winhandle = WinGetHandle($winhandle)
	if not IsHWnd($ctrlhandle) then $ctrlhandle = ControlGetHandle($winhandle,"",$ctrlhandle)
	if not IsHWnd($winhandle) Or Not IsHWnd($ctrlhandle) then __quit("Falscher Parameter in _GUICtrlSetOnEvent($event, $function, $winhandle, $ctrlhandle)"&@CRLF)

	if __isStandardEvent($event) Then $event = __fitEvent($event)
	If __isLegalEvent($event) Then
		__registerGUI($winhandle)
		__addEventControl($EventFunctionContainer, $event&"_"&$winhandle&"_"&$ctrlhandle&","&$function)
	Else
		__quit("Das Ereignis "&$event&" steht für das Control: [CLASS:"&_WINAPI_GETCLASSNAME($ctrlhandle)&", NAME:"&ControlGetText($winhandle,"",$ctrlhandle)&"] nicht zur Verfügung!"&@CRLF)
	EndIf
EndFunc
#endregion

#region Interne Funktionen
Func __registerGUI($winHandle)
 	GUISetOnEvent($GUI_EVENT_PRIMARYDOWN, '__primaryDown', $winhandle)
 	GUISetOnEvent($GUI_EVENT_PRIMARYUP, '__primaryUp', $winhandle)
 	GUISetOnEvent($GUI_EVENT_SECONDARYDOWN, '__secondaryDown', $winhandle)
 	GUISetOnEvent($GUI_EVENT_SECONDARYUP, '__secondaryUp', $winhandle)
EndFunc	
	
Func __primaryDown()
	$temp = Opt("MouseCoordMode", 2) ; relative Mauskoordinaten zur Anwendungsfläche
	$pos = MouseGetPos()
	Opt("MouseCoordMode", $temp) ; vorhergehenden Zustand wiederherstellen
	if TimerDiff($start) > $clickspeed Or $pos[0] <> $oldpos[0] Or $pos[1] <> $oldpos[1] Then
		$currentEvent = $EVENT_PRIMARY_DOWN
		$start =  TimerInit()
		$oldpos = $pos
	ElseIf BitAND($currentEvent, $EVENT_SECONDARY_CLICK) Then
		$currentEvent = $EVENT_SECONDARY_PRIMARY_FLIP
		$start = 0
	ElseIf BitAND($currentEvent, $EVENT_PRIMARY_DBLCLICK) Then
		$currentEvent = $EVENT_PRIMARY_TRICLICK
		$start = 0		
	ElseIf BitAND($currentEvent, $EVENT_PRIMARY_CLICK) Then
		$currentEvent = $EVENT_PRIMARY_DBLCLICK
		$start = TimerInit()
	Else
		Return
	EndIf

	__doEvent($currentEvent)
EndFunc

Func __primaryUp()
	$currentEvent = BitOR($currentEvent, $EVENT_PRIMARY_UP)
	If BitAND($currentEvent, $EVENT_PRIMARY_DOWN) Then 
		$currentEvent = BitXOR($EVENT_PRIMARY_DOWN, BitOR($currentEvent, $EVENT_PRIMARY_CLICK))
		__doEvent($EVENT_PRIMARY_UP)
		__doEvent($EVENT_PRIMARY_CLICK)
	else
		__doEvent($EVENT_PRIMARY_UP)
	EndIf
EndFunc

Func __secondaryDown()
	$temp = Opt("MouseCoordMode", 2) ; relative Mauskoordinaten zur Anwendungsfläche
	$pos = MouseGetPos()
	Opt("MouseCoordMode", $temp) ; vorhergehenden Zustand wiederherstellen
	if TimerDiff($start) > $clickspeed Or $pos[0] <> $oldpos[0] Or $pos[1] <> $oldpos[1] Then
		$currentEvent = $EVENT_SECONDARY_DOWN
		$start =  TimerInit()
		$oldpos = $pos
	ElseIf BitAND($currentEvent, $EVENT_PRIMARY_CLICK) Then
		$currentEvent = $EVENT_PRIMARY_SECONDARY_FLIP
		$start = 0
	ElseIf BitAND($currentEvent, $EVENT_SECONDARY_DBLCLICK) Then
		$currentEvent = $EVENT_SECONDARY_TRICLICK
		$start = 0		
	ElseIf BitAND($currentEvent, $EVENT_SECONDARY_CLICK) Then
		$currentEvent = $EVENT_SECONDARY_DBLCLICK
		$start = TimerInit()
	Else
		Return
	EndIf

	__doEvent($currentEvent)
EndFunc

Func __secondaryUp()
	$currentEvent = BitOR($currentEvent, $EVENT_SECONDARY_UP)
	If BitAND($currentEvent, $EVENT_SECONDARY_DOWN) Then 
		$currentEvent = BitXOR($EVENT_SECONDARY_DOWN, BitOR($currentEvent, $EVENT_SECONDARY_CLICK))
		__doEvent($EVENT_SECONDARY_UP)
		__doEvent($EVENT_SECONDARY_CLICK)
	else
		__doEvent($EVENT_SECONDARY_UP)
	EndIf
EndFunc

Func __doEvent($event)
	$function = __getControlFunction($event&"_"&@GUI_WinHandle, $pos)
	If $function <> "" Then call($function)
EndFunc

Func __quit($meldung = "")
	if $meldung <> "" Then
		ConsoleWriteError($meldung&@CRLF)
		Exit
	Else
		Exit
	EndIf
EndFunc

Func __isStandardEvent($event)
	Switch $event
		Case $GUI_EVENT_CLOSE
			Return True
		Case $GUI_EVENT_MINIMIZE
			Return True
		Case $GUI_EVENT_RESTORE
			Return True
		Case $GUI_EVENT_MAXIMIZE
			Return True
		Case $GUI_EVENT_PRIMARYDOWN
			Return True
		Case $GUI_EVENT_PRIMARYUP
			Return True
		Case $GUI_EVENT_SECONDARYDOWN
			Return True
		Case $GUI_EVENT_SECONDARYUP
			Return True
		Case $GUI_EVENT_MOUSEMOVE
			Return True
		Case $GUI_EVENT_RESIZED
			Return True
		Case $GUI_EVENT_DROPPED
			Return True
		Case Else
			Return False
	EndSwitch
EndFunc

Func __isLegalEvent($event)
	Switch $event
		Case $EVENT_PRIMARY_DOWN
			Return True
		Case $EVENT_PRIMARY_UP
			Return True
		Case $EVENT_SECONDARY_DOWN
			Return True
		Case $EVENT_SECONDARY_UP
			Return True
		Case $EVENT_PRIMARY_CLICK
			Return True
		Case $EVENT_PRIMARY_DBLCLICK
			Return True
		Case $EVENT_PRIMARY_TRICLICK
			Return True
		Case $EVENT_SECONDARY_CLICK
			Return True
		Case $EVENT_SECONDARY_DBLCLICK
			Return True
		Case $EVENT_SECONDARY_TRICLICK
			Return True
		Case $EVENT_PRIMARY_SECONDARY_FLIP
			Return True		
		Case $EVENT_SECONDARY_PRIMARY_FLIP
			Return True		
		Case Else
			Return False
	EndSwitch
EndFunc

Func __fitEvent($event)
	Switch $event
		Case $GUI_EVENT_PRIMARYDOWN 
			Return $EVENT_PRIMARY_DOWN
		Case $GUI_EVENT_PRIMARYUP
			Return $EVENT_PRIMARY_UP
		Case $GUI_EVENT_SECONDARYDOWN
			Return $EVENT_SECONDARY_DOWN
		Case $GUI_EVENT_SECONDARYUP
			Return $EVENT_SECONDARY_UP
		Case Else
			Return $event
	EndSwitch
EndFunc

; $array = search aus $EventFunctionContainer[event-winhandle-ctrlhandle,function]
; $id = event-winhandle bzw. event-winhandle-ctrlhandle
Func __addEventControl(ByRef $array, $id)
	if _ArraySearch($array, $id&",", 0, 0, 0, 1) > -1 Then Return False
	_ArrayAdd($array, $id)
	$index = _ArraySearch($array, "")	; Durch Array-Erstellung entstandenes ""-Element herauslöschen
	If $index > -1 then _ArrayDelete($array, $index)
	Return True
EndFunc

; $id = event-winhandle bzw. event-winhandle-ctrlhandle
func __getControlFunction($id, $pos)
	for $i = 0 To UBound($EventFunctionContainer) - 1
		if $id = StringLeft($EventFunctionContainer[$i],StringLen($id)) then
			$element = StringSplit($EventFunctionContainer[$i], ",",2)
			$element2 = StringSplit($element[0], "_",2)
			$temp = Opt("MouseCoordMode", 2) ; relative Mauskoordinaten zur Anwendungsfläche
			if __isMouseOverControl(ControlGetPos(HWnd($element2[1]),"",HWnd($element2[2])), $pos) then Return $element[1]
			Opt("MouseCoordMode", $temp) ; vorhergehenden Zustand wiederherstellen
		EndIf
	Next
	Return ""
EndFunc

Func __isMouseOverControl($CtrlPos, $mousePos)
	If $CtrlPos[0] <= $mousePos[0] And $CtrlPos[1] <= $mousePos[1] And $CtrlPos[0] + $CtrlPos[2] > $mousePos[0] And $CtrlPos[1] + $CtrlPos[3] > $mousePos[1] Then Return True
	Return False
EndFunc

func m($message)
	msgbox(4096,"",$message)
EndFunc
#endregion