PasteColor

If you are currently developing portable freeware or planning to do so, use this forum to discuss technical implementation, seek out like-minded developers for partnership, or solicit interested users for beta testing.
Message
Author
User avatar
guinness
Posts: 4118
Joined: Mon Aug 27, 2007 2:00 am
Contact:

Re: PasteColor

#31 Post by guinness »

tproli,

GUISetState("@SW_HIDE") should be GUISetState(@SW_HIDE). Macros shouldn't be encased in quotation marks.

I can't get this to work correctly on Windows 7. I was half way through re-tweaking the code (style wise) but soon realised that a re-code would be better, because looking at it most of the Global variables can be done away with and using the Return command for the functions would be cleaner and easier to manage.

What I had. Do you use SciTE4AutoIt3?

Code: Select all

#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w- 4 -w 5 -w 6 -w 7
;
; AutoIt Version: 3.0
; Language:       English
; Platform:       Win9x/NT
; Author:         Roland Toth (contact@rolandtoth.hu)
;
; Script Function:
;   Converts color string on the clipboard to RGB color format and pastes to Windows' color picker
;
#NoTrayIcon
#include <Array.au3>
#include <WinAPI.au3>
#include <WindowsConstants.au3>

; variables
Global $sProgramName = 'PasteColor', $vColor = ClipGet(), $r, $g, $b, $aArray = 0, $sUserInput = '', $iConfirm = 0

; I use the prefix $ for Global as it makes for easier reading.
Global $hActiveWindow = WinGetHandle('[ACTIVE]', ''), _
		$hActiveHandle = ControlGetHandle($hActiveWindow, '', ControlGetFocus($hActiveWindow)), _
		$iControlID = _WinAPI_GetDlgCtrlID($hActiveHandle)

; functions

Func _ShowError()
	Return MsgBox(17, $sProgramName & ' Error', 'Invalid color!')
EndFunc   ;==>_ShowError

Func _ActivateFocus()
	If ControlFocus($hActiveWindow, '', $hActiveHandle) = 0 Then
		Exit MsgBox(17, $sProgramName & ' Error', 'Cannot find active window!' & @CRLF & @CRLF & 'Exiting...')
	EndIf
EndFunc   ;==>_ActivateFocus

Func _NextControl()
	$iControlID += 1
	ControlFocus($hActiveWindow, '', $iControlID)
EndFunc   ;==>_NextControl

Func _GetText($iControlID)
	Return ControlGetText($hActiveWindow, '', $iControlID)
EndFunc   ;==>_GetText

Func _CopyX($iRepeat)
	If $iRepeat > 4 And $iConfirm = MsgBox(33, $sProgramName, 'You are about to copy ' & $iRepeat & ' items.' & @CRLF & @CRLF & 'Continue?') = 2 Then
		Exit
	EndIf

	_ActivateFocus()

	$aArray = 0
	Local $aArray[$iRepeat]

	$aArray[0] = _GetText($hActiveHandle)

	For $i = 1 To $iRepeat - 1
		_NextControl()
		$aArray[$i] = _GetText('')
	Next

	ClipPut(_ArrayToString($aArray, ','))

	_ActivateFocus()

	Exit
EndFunc   ;==>_CopyX

Func _PasteX($sInput)

	If Not $sInput = '' Then
		$aArray = StringSplit($sInput, ',')
	Else
		$aArray = StringSplit(ClipGet(), ',')
	EndIf

	If $aArray[0] > 4 And $iConfirm = MsgBox(33, $sProgramName, 'You are about to paste ' & $aArray[0] & ' items.' & @CRLF & @CRLF & 'Continue?') = 2 Then
		Exit
	EndIf

	_ActivateFocus()

	ControlSetText($hActiveWindow, '', $hActiveHandle, $aArray[1])

	For $i = 2 To $aArray[0]
		_NextControl()
		ControlSetText($hActiveWindow, '', '', $aArray[$i])
	Next

	_ActivateFocus()

	Exit
EndFunc   ;==>_PasteX

Func _CopyColor()

	_ActivateFocus()

	$r = _GetText($hActiveHandle)
	_NextControl()

	$g = _GetText('')
	_NextControl()

	$b = _GetText('')

	If $vColor = 'copy' Or $vColor = 'rgb' Then
		$vColor = $r & ', ' & $g & ', ' & $b
	ElseIf $vColor = 'hex' Then
		_RGBtoHex('', $r, $g, $b)
	ElseIf $vColor = 'hexx' Then
		_RGBtoHex('#', $r, $g, $b)
	EndIf

	ClipPut($vColor)

	_ActivateFocus()

	Exit
EndFunc   ;==>_CopyColor

Func _RGBtoHex($prefix, $r, $g, $b)
	$vColor = $prefix & Hex($r, 2) & Hex($g, 2) & Hex($b, 2);
EndFunc   ;==>_RGBtoHex

Func _SixDigitColor($vVar)
	$aArray = StringSplit($vVar, '');
	$vColor = ''

	For $count = 1 To StringLen($vVar)
		$vColor &= $aArray[$count] & $aArray[$count]
	Next
EndFunc   ;==>_SixDigitColor

Func _SplitRGB($vVar)
	$aArray = StringSplit($vVar, ',');
	$r = $aArray[1] + 0 ; removing leading zeros
	$g = $aArray[2] + 0
	$b = $aArray[3] + 0
EndFunc   ;==>_SplitRGB

Func _HexToRGB($vVar)
	$vVar = '0x' & $vVar
	$r = BitAND(BitShift($vVar, 16), 0xFF)
	$g = BitAND(BitShift($vVar, 8), 0xFF)
	$b = BitAND($vVar, 0xFF)
EndFunc   ;==>_HexToRGB

Func _PasteColor($r, $g, $b)
	_ActivateFocus()

	ControlSetText($hActiveWindow, '', $hActiveHandle, $r)
	_NextControl()
	ControlSetText($hActiveWindow, '', '', $g)
	_NextControl()
	ControlSetText($hActiveWindow, '', '', $b)

	_ActivateFocus()
EndFunc   ;==>_PasteColor

Func _ValidateInput($vVar)
	If StringRegExp($vVar, '\b((0x0|0x00){1})?([0-9a-fA-F]){6}\b|^(0*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])[, \s]\s*){2}(0*([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])([,]{1})?)\s*$', 0) = 0 Then
		If ($CmdLine[0] > 0 And $CmdLine[1] = '/silent') Then
			_ShowError()
			Exit
		Else
			_ShowError()
			_GetInput($sUserInput)
			_CleanInput($vColor)
			_ValidateInput($vColor)
			Return
		EndIf
	EndIf

	; convert color to Hex or RGB
	If StringInStr($vVar, ',') > 0 Then
		_SplitRGB($vVar)
	Else
		_HexToRGB($vVar)
	EndIf
EndFunc   ;==>_ValidateInput

Func _CleanInput($vVar)
	$vVar = StringStripWS($vVar, 1)
	$vVar = StringStripWS($vVar, 2)

	If StringLeft($vVar, 1) = '#' Then
		$vVar = StringTrimLeft($vVar, 1)
	EndIf

	$vVar = StringReplace($vVar, ' ', ',')
	$vVar = StringReplace($vVar, ',,', ',')
	$vColor = StringReplace($vVar, ',,', ',')

	If StringLen($vColor) = 3 And StringInStr($vColor, ',') = 0 And StringIsXDigit($vColor) Then
		_SixDigitColor($vColor)
	EndIf
EndFunc   ;==>_CleanInput

Func _GetInput($vVar)
	Local $hWnd = WinGetHandle(AutoItWinGetTitle()) ; Internal AutoIt Window >> http://www.autoitscript.com/forum/topic/133648-autoitwingettitleautoitwinsettitle-an-example-of-usage/
	WinSetState($hWnd, '', @SW_SHOW)
	WinSetOnTop($hWnd, '', 1)
	WinSetState($hWnd, '', @SW_HIDE)

	If $CmdLine[0] = 0 Then
		$sUserInput = InputBox($sProgramName, 'Enter color:', StringLeft($vVar, 100), ' M100', Default, 132, Default, Default, Default, $hWnd)

		;            Msgbox(0, 'Color', 'Input is: ' & $vColor)
		;            Msgbox(0, 'Color', 'Window is: ' & $hActiveWindow)
		;            Msgbox(0, 'Color', 'Handle is: ' & $hActiveHandle)
		;            Msgbox(0, 'Color', 'ControlID is: ' & $iControlID)
		;            Exit

		; If an error occurred Exit the application.
		If @error Then
			Exit
		EndIf
		$vColor = $sUserInput
	EndIf

	If $vColor = 'copy' Or $vColor = 'rgb' Or $vColor = 'hex' Or $vColor = 'hexx' Then
		_CopyColor()
		Return
	EndIf

	If StringLeft($vColor, 1) = 'c' And StringIsDigit(StringTrimLeft($vColor, 1)) And StringTrimLeft($vColor, 1) > 0 Then
		_CopyX(StringTrimLeft($vColor, 1))
		Return
	EndIf

	If StringLeft($vColor, 1) = 'p' Then
		_PasteX(StringTrimLeft($vColor, 1))
		Return
	EndIf
EndFunc   ;==>_GetInput

_GetInput($vColor)
_CleanInput($vColor)
_ValidateInput($vColor)
_PasteColor($r, $g, $b)
Exit

User avatar
tproli
Posts: 1172
Joined: Sat Sep 09, 2006 10:14 am
Location: Hungary
Contact:

Re: PasteColor

#32 Post by tproli »

Thanks, much appreciated.

The main issue is that on Win7 the active window is not saved to the "hActiveWindow" variable. I tried to solve it by hardcoding the title and it works fine, but another problem is that the color picker's title can be different if launched from different applications, for example "Edit Colors", "Colors", not to mention non-English versions of Windows.

I guess a solution is to save active window under the mouse to a variable somehow.

Here is the updated source:
http://rolandtoth.hu/files/PasteColor1.zip

User avatar
guinness
Posts: 4118
Joined: Mon Aug 27, 2007 2:00 am
Contact:

Re: PasteColor

#33 Post by guinness »

So I used WinList and the REGEXPTITLE as the title parameter and then added a couple of little tweaks here and there.

Before I re-post could you provide me with an example of the expected input and output, because on Windows 7 it's not finding the controls of the color picker correctly.

User avatar
tproli
Posts: 1172
Joined: Sat Sep 09, 2006 10:14 am
Location: Hungary
Contact:

Re: PasteColor

#34 Post by tproli »

Expected inputs and outputs can be for example:
(output is the rgb values that are pasted to the color picker, comma separated)

Code: Select all

#367 -> 51, 102, 119
367 -> 51, 102, 119
51, 102, 119 -> 51, 102, 119
51,102,119 -> 51, 102, 119
#938951 -> 147, 137, 81
#938951 -> 147, 137, 81
copy -> [copies rgb color to clipboard, result format: 51, 102, 119]
rgb -> [copies rgb color to clipboard, result format: 51, 102, 119]
hex -> [copies hex color to clipboard without the hash mark, result format: 856874]
hex -> [copies hex color to clipboard with the hash mark, result format: #856874]
c5 -> copies 5 control's value (no color conversion)
p 10,20,30,40,50 -> 10, 20, 30, 40, 50 (pastes whatever is on the clipboard, no color conversion)
The idea is that the first input field (control) is where the cursor is in where PasteColor starts, and then jump to the next 2 (or more in case of cN or pN) fields. So one can put the cursor elsewhere, e.g. the "Hue" field and copy 6 numbers.

Do you think that altering the activation method would do the trick? That is, running PasteColor in the background and adding a hotkey to use? Perhaps doing so would prevent stealing focus on Win7.

I will also experiment with WinList, if it can reliably return the last active window then it would be great.

User avatar
tproli
Posts: 1172
Joined: Sat Sep 09, 2006 10:14 am
Location: Hungary
Contact:

Re: PasteColor

#35 Post by tproli »

I found this:

http://stackoverflow.com/questions/1123 ... se-pointer

This returns the window title under the mouse and works on Win7 too. It is also not fail-safe because it is possible to click in the Red control and move the mouse out of the window, thus resulting a wrong window title.

User avatar
guinness
Posts: 4118
Joined: Mon Aug 27, 2007 2:00 am
Contact:

Re: PasteColor

#36 Post by guinness »

tproli wrote:I found this:

http://stackoverflow.com/questions/1123 ... se-pointer

This returns the window title under the mouse and works on Win7 too. It is also not fail-safe because it is possible to click in the Red control and move the mouse out of the window, thus resulting a wrong window title.
I have a similar example in my snippets folder which uses AutoIt UDF functions instead.

Code: Select all

#include <Misc.au3>
#include <WinAPI.au3>
#include <WindowsConstants.au3>

HotKeySet('{ESC}', '_Exit')

Example()

Func Example()
	Local $hWnd = 0, $hWnd_Previous = 0, $tGetMousePos = 0 ; Declaring variables in the loop will slow it down.

	While 1
		$tGetMousePos = _WinAPI_GetMousePos()
		$hWnd = _WinAPI_GetAncestor(_WinAPI_WindowFromPoint($tGetMousePos), $GA_ROOT)
		If $hWnd <> $hWnd_Previous Then
			$hWnd_Previous = $hWnd
			ConsoleWrite($hWnd & @CRLF)
			TrayTip('', $hWnd, 2)
		EndIf
		Sleep(20)
	WEnd
EndFunc   ;==>Example

Func _Exit()
	Exit MsgBox(4096, '', 'Exiting...', 1)
EndFunc   ;==>_Exit
I will have a look on the weekend.

User avatar
guinness
Posts: 4118
Joined: Mon Aug 27, 2007 2:00 am
Contact:

Re: PasteColor

#37 Post by guinness »

I hope you don't mind but I decided to start from scratch to give you an idea of how I would try it >>
Start the example and and select the Edit Colors button in MSPaint. Then hover your mouse over the hue control and voila the GUI will show.

Code: Select all

#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w- 4 -w 5 -w 6 -w 7

#include <GUIConstantsEx.au3>
#include <WinAPI.au3>
#include <WindowsConstants.au3>

; Start paint for the purposes of the example.
_RunPaint()
_Main()

Func _Main() ; By guinness.
	Local $hWnd = 0, $hWnd_Child = 0, $iControlID_Previous = 0, $iControlID = 0, $iShowGUI = 0, $tGetMousePos = 0, $sClassName = '', $sTitle = '' ; Declaring variables in the loop will slow it down.

	Local $hGUI = GUICreate('PasteColor', 300, 30, -1, -1, BitXOR($GUI_SS_DEFAULT_GUI, $WS_MINIMIZEBOX))
	GUICtrlCreateInput('', 5, 5, 290, 20)

	GUISetState(@SW_HIDE, $hGUI)
	WinSetOnTop($hGUI, '', 1)

	Local $iEnter = GUICtrlCreateDummy()
	Local $aHotKeys[1][2] = [['{ENTER}', $iEnter]]
	GUISetAccelerators($aHotKeys)

	While 1
		$tGetMousePos = _WinAPI_GetMousePos() ; Get the mouse coordinates.
		$hWnd_Child = _WinAPI_WindowFromPoint($tGetMousePos) ; Handle under the mouse.
		$iControlID = _WinAPI_GetDlgCtrlID($hWnd_Child) ; Control ID.
		$sClassName = _WinAPI_GetClassName($hWnd_Child) ; ClassName.
		$hWnd = _WinAPI_GetAncestor($hWnd_Child, $GA_ROOT) ; Main handle.
		$sTitle = WinGetTitle($hWnd) ; Window title.
		If StringRegExp($sTitle, '(?:Edit\s)?Colors', 0) Then
			If $iShowGUI = 0 Then
				$iShowGUI = 1
				GUISetState(@SW_SHOW, $hGUI)
			EndIf
			If $iControlID <> $iControlID_Previous And $sClassName = 'Edit' Then ; If the ControlID is different then display data in ConsoleWrite.
				$iControlID_Previous = $iControlID
				ConsoleWrite($sTitle & ' >> ' & $iControlID & ' >> ' & $sClassName & @CRLF)
			EndIf
		Else
			$iShowGUI = 0
		EndIf

		Switch GUIGetMsg()
			Case $GUI_EVENT_CLOSE
				ExitLoop

			Case $iEnter ; Hit the enter key when input has focus.
;~ 				ControlSetText($hWnd_Child, '', $iControlID, '999')
				MsgBox(4096, '', 'ToDo Function.')

		EndSwitch
	WEnd
EndFunc   ;==>_Main

Func _RunPaint()
	Local $iPID = ProcessExists('mspaint.exe')
	If $iPID = 0 Then
		$iPID = Run('mspaint.exe', @SystemDir, @SW_SHOW)
	EndIf
	Local $hWnd = WinWait('[CLASS:MSPaintApp]', '', 5)
	WinActivate($hWnd)
	Return $iPID
EndFunc   ;==>_RunPaint

User avatar
guinness
Posts: 4118
Joined: Mon Aug 27, 2007 2:00 am
Contact:

Re: PasteColor

#38 Post by guinness »

This works. If I hover over the hue edit then enter 99 in the input box and select the ENTER key or use the OK button, it fills the hue edit control with 99.

Code: Select all

#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w- 4 -w 5 -w 6 -w 7

#include <GUIConstantsEx.au3>
#include <WinAPI.au3>
#include <WindowsConstants.au3>

; Start paint for the purposes of the example.
_RunPaint()
_Main()

Func _Main() ; By guinness.
	Local $hWnd = 0, $hWnd_Use = 0, $iControlID = 0, $iControlID_Previous = 0, $iControlID_Use = 0, $iShowGUI = 0, $tGetMousePos = 0, $sClassName = '', $sClassName_Use = '', $sTitle = '' ; Declaring variables in the loop will slow it down.

	Local $hGUI = GUICreate('PasteColor', 325, 30, -1, -1, BitXOR($GUI_SS_DEFAULT_GUI, $WS_MINIMIZEBOX))
	Local $iColor = GUICtrlCreateInput('', 5, 5, 250, 20)
	Local $iOK = GUICtrlCreateButton('&OK', 255, 5, 65, 20)

	GUISetState(@SW_HIDE, $hGUI)
	WinSetOnTop($hGUI, '', 1)

	Local $iEnter = GUICtrlCreateDummy()
	Local $aHotKeys[1][2] = [['{ENTER}', $iEnter]]
	GUISetAccelerators($aHotKeys)

	While 1
		$tGetMousePos = _WinAPI_GetMousePos() ; Get the mouse coordinates.
		$hWnd = _WinAPI_WindowFromPoint($tGetMousePos) ; Handle under the mouse.
		$iControlID = _WinAPI_GetDlgCtrlID($hWnd) ; Control ID.
		$sClassName = _WinAPI_GetClassName($hWnd) ; ClassName.
		$hWnd = _WinAPI_GetAncestor($hWnd, $GA_ROOT) ; Main handle.
		$sTitle = WinGetTitle($hWnd) ; Window title.
		If StringRegExp($sTitle, '(?:Edit\s)?Colors', 0) Then
			If $iShowGUI = 0 Then
				$iShowGUI = 1
				GUISetState(@SW_SHOW, $hGUI)
			EndIf
			If $iControlID <> $iControlID_Previous And $sClassName = 'Edit' Then ; If the ControlID is different then display data in ConsoleWrite.
				$iControlID_Previous = $iControlID
				$hWnd_Use = $hWnd
				$iControlID_Use = $iControlID
				$sClassName_Use = $sClassName
				ConsoleWrite($sTitle & ' >> ' & $iControlID & ' >> ' & $sClassName & @CRLF)
			EndIf
		Else
			$iShowGUI = 0
		EndIf

		Switch GUIGetMsg()
			Case $GUI_EVENT_CLOSE
				ExitLoop

			Case $iEnter, $iOK
				ControlSetText($hWnd_Use, '', $iControlID_Use, GUICtrlRead($iColor))
				MsgBox(4096, '', $sTitle & @CRLF & $iControlID_Use & @CRLF & $sClassName_Use & @CRLF & @CRLF)

		EndSwitch
	WEnd
EndFunc   ;==>_Main

Func _RunPaint()
	Local $iPID = ProcessExists('mspaint.exe')
	If $iPID = 0 Then
		$iPID = Run('mspaint.exe', @SystemDir, @SW_SHOW)
	EndIf
	Local $hWnd = WinWait('[CLASS:MSPaintApp]', '', 5)
	WinActivate($hWnd)
	Return $iPID
EndFunc   ;==>_RunPaint

User avatar
tproli
Posts: 1172
Joined: Sat Sep 09, 2006 10:14 am
Location: Hungary
Contact:

Re: PasteColor

#39 Post by tproli »

Thanks. Yes, the main issue is the activation, getting the active window and the active control. It worked fine on XP but not on Win7. But when I hard-coding the window title string PasteColor worked on Win7 too (well, occassionally couldn't find the control but it was very rare).

Getting the window under the mouse is the best solution so far, but fails if the mouse is NOT over the active window.

The hard-coded "Colors" in the regexp works only on English Windows so for example I also had to modify it. Because of this it cannot be used as it fails elsewhere.

I will check your script more thoroughly later, perhaps these are enough for a 7-compatible edition.

User avatar
guinness
Posts: 4118
Joined: Mon Aug 27, 2007 2:00 am
Contact:

Re: PasteColor

#40 Post by guinness »

Getting the window under the mouse is the best solution so far, but fails if the mouse is NOT over the active window.

I wouldn't say it fails because it's down to the Windows API WindowFromPoint, so it's actually Windows that is 'at fault'.

I will look at this next week as I've spent a couple of hours today researching this.

User avatar
tproli
Posts: 1172
Joined: Sat Sep 09, 2006 10:14 am
Location: Hungary
Contact:

Re: PasteColor

#41 Post by tproli »

I mean that "active window" and "window under the mouse" can be two different things. For example, move the color picker, then release it and move the mouse off the window. Not color picker is active but window under the cursor is not the color picker.

In these situations it fails.

User avatar
tproli
Posts: 1172
Joined: Sat Sep 09, 2006 10:14 am
Location: Hungary
Contact:

Re: PasteColor

#42 Post by tproli »

I think I will return to the good ol' key send method (TAB key), and perhaps with an ini setting read from an ini file. Currently I'm testing on Win7 and so far so good but of course it is much slower.

Is detecting WinXP reliable in AutoIt? If yes, I would make the controlID method the default on XP as it worked fine there.

User avatar
guinness
Posts: 4118
Joined: Mon Aug 27, 2007 2:00 am
Contact:

Re: PasteColor

#43 Post by guinness »

tproli wrote:Is detecting WinXP reliable in AutoIt? If yes, I would make the controlID method the default on XP as it worked fine there.
Er...of course it is. @OSVersion would be the recommended macro.

Code: Select all

#include <Constants.au3>

Local $aCommand[2] = ['ANY_OTHER_STRING', 'XP_STRING']
MsgBox($MB_SYSTEMMODAL, '', $aCommand[@OSVersion == 'WIN_XP'])
And Send can have many issues, most notable is focus from another window.

User avatar
tproli
Posts: 1172
Joined: Sat Sep 09, 2006 10:14 am
Location: Hungary
Contact:

Re: PasteColor

#44 Post by tproli »

Thanks for the help.

Yes, I know its drawbacks but Send actually works on Win7 while ControlFocus fails as we discussed earlier.

User avatar
guinness
Posts: 4118
Joined: Mon Aug 27, 2007 2:00 am
Contact:

Re: PasteColor

#45 Post by guinness »

OK, no problem. I'm working on a new version of SciTE Jump, but you know where I am if you need help with AutoIt.

Post Reply