conversion logic, v1 = -> v1 := -> v2, two-way compatibility

Discuss the future of the AutoHotkey language
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: conversion logic, v1 = -> v1 := -> v2, two-way compatibility

05 Jan 2018, 18:59

GENERAL REPORT ON CONVERSION ISSUES
- See also the 3 posts above.

GENERAL: VAR = VALUE -> VAR := VALUE
- You have to be careful with numbers with leading/trailing zeros e.g.:

Code: Select all

var = 06
;goes to:
var := "06"
;not:
var := 06
- However, it has been suggested that when you do var = value, to var := "value", to always assume it's a string.

GENERAL: BLANK PARAMETERS: COMMANDS -> FUNCTIONS
- With commands, blank parameters and omitted parameters look identical.
- So parameters that I perceived as being omissible, were in fact compulsory, e.g. FileAppend (Text), RegWrite (Value). (Note: RegWrite's Value parameter is stated as compulsory but appears to be omissible.)
blanks in ternary operator - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 56#p189956

GENERAL: CONVERTING FUNCTIONS
- Let's say you have a script, and you've converted some lines, but not others. It will be easy to identify which lines are AHK v2 ready, and which lines still need to be converted.
- An exception is functions where the functionality has changed i.e. InStr/SubStr/RegExMatch/RegExReplace.
- E.g.

Code: Select all

InStr(vPath, "\", 0, 1) ;not ambiguous: works the same in AHK v1/v2
InStr(vPath, "\", 0, -1) ;ambiguous: works differently in AHK v1/v2
InStr(vPath, "\", 0, vPos) ;ambiguous: works differently in AHK v1/v2 if vPos is non-positive
- It is like dates: 10/01/2017 is ambiguous (10 Jan or 1 Oct), but 20/01/2017 is not.
- So, if you want to be sure how the code is supposed to work for ambiguous cases of StartingPos, you should leave a comment (i.e. to say that it was written for AHK v1/v2). Although there could be multiple functions with ambiguities on the same line making this less feasible.
- What I do instead, is, whenever InStr is ambiguous, I replace it with JEE_InStr, which has the AHK v2 behaviour in both AHK v1/v2.
- I fully support the changes to InStr/SubStr/RegExMatch/RegExReplace. But I would warn that any *changes* to functions, that aren't simply extending the functionality, can cause a lot of complications and awkwardness. And that this should not be underestimated.
- A possible solution is to convert instances with a non-positive StartingPos, to (subtract 1 and) use InStr2 (AHK v2 behaviour on AHK v1/v2), and any instances that have a variable for StartingPos to use InStr1 temporarily (AHK v1 behaviour on AHK v1/v2) (which then have to be manually converted to use InStr2).
- I would also suggest a mode for AHK v1, where, if set, the 4 functions behave like they do in AHK v2. So that people can write more forwards compatible code that won't have to be converted manually later, and so that code can be more easily reused between AHK v1/v2, and because the StartingPos values are more logical/easier to use (more newbie-friendly). This is not an argument primarily inspired by two-way compatibility, the JEE_InStr idea can achieve that.

GENERAL: FUNCTIONS WITHIN FUNCTIONS
- When converting from commands to functions, some things become clear, like the benefit of not having subcommands.
- One other thing worth considering, is avoiding, where possible, functions within functions. These are the sorts of things you discover while playing around in AHK v2.

Code: Select all

;e.g. proposal:
DateAdd(DateTime, Time, TimeUnits, Format)
;versus:
FormatTime(DateAdd(DateTime, Time, TimeUnits), Format)
GENERAL: CONTROLXXX FUNCTION NAMES
- I made some comments here, at the bottom of this post:
why was LoopParse (no space) removed? - Page 2 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 42#p190742

GENERAL: XXXGETLIST FUNCTIONS
- Window list: WinGet-List -> WinGetList.
- Control list: WinGet-ControlList -> WinGetControls/WinGetControlsHwnd.
- Item list (items within control): - ControlGet-List -> ControlGetList (perhaps ControlGetItems).
- Re. counting items:
- WinGet-Count -> WinGetCount.
- WinGet-ControlList: There is no 'window get control count' option (unless you retrieve the pseudo-array).
- ControlGet-List has a count option.

CONTROLGETLIST (CONTROLGETITEMS?): STRING -> ARRAY
- Although returning an array as the default, instead of an LF-delimited string, makes sense (e.g. theoretically an item could contains LFs). It can be slow to build a string from an array, and to search for an item within an array. Thus an option to return a string may be sensible.
jeeswg's benchmark tests - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 29#p192029
- Also, this suggests the need for a StrJoin function, which many have already talked about. Here's *a* version, one question is whether a capacity parameter should be present or not (I would suppose not, and, perhaps AHK can roughly estimate the lengths of all strings in an array, and so the parameter wouldn't be necessary).

Code: Select all

JEE_StrJoin(vSep, oArray*)
{
	vOutput := ""
	VarSetCapacity(vOutput, 1000000*2)
	Loop, % oArray.MaxIndex()-1
		vOutput .= oArray[A_Index] vSep
	vOutput .= oArray[oArray.MaxIndex()]
	return vOutput
}
CONTROLSETTAB
- Pretty awkward to convert. (This is not a complaint, just an interesting curio.)

Code: Select all

;before:
Control, TabLeft, 3, SysTabControl321, A
Control, TabRight, 3, SysTabControl321, A
;after:
ControlSetTab(-3+ControlGetTab("SysTabControl321", "A"), "SysTabControl321", "A")
ControlSetTab(3+ControlGetTab("SysTabControl321", "A"), "SysTabControl321", "A")
DEREF: SHOULD IT BE KEPT?
- I have had a lot of problems in my scripts relating to Deref not being available. One use is to handle entries of the form '%dir%\MyFile.txt' loaded from ini files, or from the selected text.
- I think that this function, (including possible options to change the deref character(s), and support for environment variables, and perhaps for objects with keys, that I've previously mentioned,) would be very useful.
- I think it would be good to have Deref available and backport it to AHK v1 (which currently has it as a subcommand of Transform).
- This function cannot be recreated as a custom user-defined function.

FILEINSTALL
- I don't personally use FileInstall, but the FileInstall function does seem like a good candidate for being backported to AHK v1. Perhaps the only function that would *need* backporting, other than Deref.
FileInstall
https://autohotkey.com/docs/commands/FileInstall.htm
Extract file that was added with fileinstall - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 35#p188235

PIXELGETCOLOR/PIXELSEARCH
- If these two functions allowed you to specify 'BGR', it would make automatic conversion a lot easier. Also RGB and BGR both appear commonly with regard to image scripts.
- The silliest (most nuanced) part of my converter so far, checks whether the pixel colour is the same in RGB and BGR, if it can't be sure, then it cannot safely convert that line, and so it omits it from the conversion, and adds it to the error list.

Code: Select all

JEE_ColRGBIsBGR(vNum)
{
	if RegExMatch(vNum, "^0x\d{6}$")
		return (SubStr(vNum, 3, 2) = SubStr(vNum, 7, 2))
	else if RegExMatch(vNum, "^\d+$")
		return ((vNum && 0xFF0000 >> 16) = (vNum && 0xFF))
	else
		return 0
}
PROGRESS/SPLASHIMAGE: RECREATING THEM
- These are due to be removed from AHK v2.
- I am going to try to recreate these 2 commands as custom functions.
- AHK v2 doesn't let you specify the class of a GUI window, so there's at least that aspect that cannot be recreated currently, and so some of my scripts, that depend on the class name to identify the Progress windows, would have to be altered.
- I do feel that there will be other obstacles, perhaps some of them insurmountable, in trying to exactly recreate these commands with no loss of functionality, for example, to avoid making a script #Persistent.
- I am not sure, but I think that there are subtle optimisations and advantages that AHK GUI-like notification commands have, that a custom user-defined function can't reproduce.
- I have found these commands very useful when sharing scripts on forums, and doing diagnostic image/text/font tests. Once you have to manually include a custom function inside a script, and copy and paste it every time you want to use it on the forum, it's no longer newbie-friendly. Also, even if I did copy and paste the recreated Progress function, it might be exceedingly long and complicated.

REGEXMATCH: PSEUDO-ARRAY -> ARRAY
- RegExMatch will return an object in AHK v2, never a pseudo-array.
- However, the RegExMatch object is read-only, and so you can't edit the array items.
- I attempted to create a function that approximately recreates it, partly to understand everything that's in the object. I also provided an example script to recreate part of the object.
RegExMatch
https://autohotkey.com/docs/commands/Re ... atchObject
RegEx: edit RegEx match object - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=40679

STRINGSPLIT -> STRSPLIT: PSEUDO-ARRAY -> ARRAY
- There are two options re. conversion with StringSplit. Create a pseudo-array manually, or replace the pseudo-array with an array (via StrSplit).
- Although I would recommend converting to StrSplit, and using a proper array, it can be very time-consuming and awkward to make all of the necessary adjustments, and this would have to be done manually. So for one of my big scripts with around 100 uses of StringSplit, I converted it to still use a pseudo-array, until I have the time to manually review the (very old) code.
- StringSplit can be replaced automatically (without the need for manual conversion to StrSplit) like so:

Code: Select all

;before
StringSplit, vPseudoArray, vText, % vSep
;after:
Loop, Parse, vText, % vSep ;string split
	vPseudoArray%A_Index% := A_LoopField, vPseudoArray0 := A_Index

;more generally:
;before
StringSplit, vPseudoArray, vText, % vSep
MsgBox, % vPseudoArray0
MsgBox, % vPseudoArray1
;after (replace with StrSplit):
oArray := StrSplit(vText, vSep)
MsgBox, % oArray.Length()
MsgBox, % oArray.1
;after (keep using pseudo-array):
Loop, Parse, vText, % vSep ;string split
	vPseudoArray%A_Index% := A_LoopField, vPseudoArray0 := A_Index
- I would leave the comment 'string split' to indicate what is going on, and as a reminder to review the code at a future date.
- The following function could be useful for some circumstances:

Code: Select all

; ;e.g.
; vText := "abc,def,ghi,jkl,mno,pqr,stu,vwx,yz"
; v0 := JEE_StrSplitToVars(vText, ",",, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20)
; MsgBox, % v0 " " v1 " " v2

; ;e.g.
; vText := "abcdefghijklmnopqrstuvwxyz"
; v0 := JEE_StrSplitToVars(vText,,, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20)
; MsgBox, % v0 " " v1 " " v2

JEE_StrSplitToVars(vText, vDelims:="", vOmitChars:="", ByRef v1:="", ByRef v2:="", ByRef v3:="", ByRef v4:="", ByRef v5:="", ByRef v6:="", ByRef v7:="", ByRef v8:="", ByRef v9:="", ByRef v10:="", ByRef v11:="", ByRef v12:="", ByRef v13:="", ByRef v14:="", ByRef v15:="", ByRef v16:="", ByRef v17:="", ByRef v18:="", ByRef v19:="", ByRef v20:="")
{
	global vJeeStrSplitToVarsWarn
	oArray := StrSplit(vText, vDelims, vOmitChars)
	if (oArray.Length() > 20) && vJeeStrSplitToVarsWarn
		MsgBox, % A_ThisFunc ": warning:`r`n" "item count (" oArray.Length() ") exceeds limit (20)"
	Loop, % (oArray.Length() > 20) ? 20 : oArray.Length()
		v%A_Index% := oArray[A_Index]
	return oArray.Length()
}
WINSETSTYLE
- The use of + and ^ presents a subtle problem in the move to function-style parameters.

Code: Select all

;before:
WinSet, Style, 0x1000000, % "ahk_id " hWnd
WinSet, Style, +0x1000000, % "ahk_id " hWnd
WinSet, Style, -0x1000000, % "ahk_id " hWnd
WinSet, Style, ^0x1000000, % "ahk_id " hWnd
;after:
WinSetStyle(0x1000000, "ahk_id " hWnd)
WinSetStyle("+" 0x1000000, "ahk_id " hWnd)
WinSetStyle(-0x1000000, "ahk_id " hWnd)
WinSetStyle("^" 0x1000000, "ahk_id " hWnd)
- It is mentioned in these two links:
WinSet
https://lexikos.github.io/v2/docs/comma ... .htm#Style
Control Functions
https://lexikos.github.io/v2/docs/comma ... m#SetStyle

GENERAL COMMENTS
- It's curious, instead of just manually fixing a particular code line, intellectualising it, in order to automate its conversion.
- Combining registry keys and subkeys is a bit awkward, after having already experienced most conversion problems, I suddenly found the need to 'clean' a function parameter, e.g. "a" "b" "c" -> "abc" (e.g. "HKEY_CLASSES_ROOT" "\" "txtfile" -> "HKEY_CLASSES_ROOT\txtfile").
- Some changes really make the simplicity clearer:

Code: Select all

;before:
ControlGetFocus, vCtlClassNN, A
;after:
vCtlClassNN := ControlGetFocus("A")
- I also like how the parameters in RegWrite and WinMove have been rearranged to always be consistent. And I now really like the change from "" to `", which I wasn't initially sure about.
- I have already had this problem a few times, that of 'unconverting', e.g. to share an AHK v2 script in AHK v1 form on the forum, I will have to do work on an 'unconverter'. Luckily, my work on two-way compatibility has made this easier.
- For using AHK v1 and AHK v2 simultaneously for different scripts, an /inc switch, and also allowing A_AhkPath with #Include, would be incredibly useful.
- Perhaps every function that lacks a return value at present, should return 1 if there are no errors. And WinWait/WinWaitActive, could return an hWnd.
- 'GuiCtrlFromHwnd', 'Ctl'? 'Ctrl' seems a bit odd, and more reminiscent of keyboard keys.
- There has been mention that ahk_dhw/ahk_mode wouldn't break existing scripts so could be added at a later date, but I've always found the use of things like Last Found Window/A_StringCaseSense/A_DetectHiddenWindows/A_TitleMatchMode most unsatisfactory, and a huge amount of my code could be improved by WinWait/WinWaitActive returning an hWnd, ahk_dhw s/c(ontains)/(e)x(act)/e(nds)/r and ahk_mode vh/v/h being available, and a case sensitive StrReplace parameter being available (although in that case, RegExReplace could be used instead). There is also this issue:
DetectHiddenWindows bug? - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 14&t=30010
- I am reminded that, if feasible, having a way to redefine a built-in function, whilst having access to the original built-in function, could have advantages, allowing users to backport certain functionality manually. Otherwise, AHK users are completely dependent on changes to the source code.
- I would consider having ControlGetChoice as part of ControlGetList (aka ControlGetItems). Or as part of 'LBGetItems'/'CBGetItems', if they were separate functions. I am not against ControlChoose/ControlChooseString being one function per se, however, realistically this will add '+0' workaround awkwardness to yet another function. Sometimes the item strings are integers. (Note: I am quite neutral, and don't have strong opinions re. the topics in this bullet point.)
- It's interesting how ControlGet and Control have been combined in the AHK v2 help.
Control Functions
https://lexikos.github.io/v2/docs/commands/Control.htm
- What happened to the colour table from the Progress/SplashImage page?
Progress/SplashImage
https://autohotkey.com/docs/commands/Pr ... ect_Colors
Color Names
https://lexikos.github.io/v2/docs/misc/Colors.htm
- I have posted comments about parsing issues here:
parsing AutoHotkey scripts - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=40471
- Thanks for reading.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: conversion logic, v1 = -> v1 := -> v2, two-way compatibility

07 Jan 2018, 04:57

USING THE SAME LIBRARIES WITH BOTH AHK V1 AND AHK V2 SCRIPTS

- I've been thinking about the worst-case scenario, what if no changes are made, to the source code, that will deliberately or inadvertently make two-way compatibility easier.
Here is a potential approach (untested):
- AHK v1/AHK v2 are in completely different folders.
- The original library functions are in the AHK v2 Lib folder.
- The cloned library functions are in the AHK v1 Lib folder.
- When AHK v2 library scripts are saved, or AHK v1 scripts are run, the datestamps of the AHK v2 library scripts are checked, if a date has changed, then the AHK v2 script is converted/copied, 'converticopied', for use with AHK v1.
- This special conversion/copying is made as simple as possible.
- Loop lines have '% ' added, to force expression. I.e. lines that start with Loop that aren't within continuation sections.
- Any instances of `" are replaced with "". A potential false positive is an escaped grave e.g. "``". The simplest workaround is to replace "``" with Chr(96). There will be far fewer Chr(96)s needed than Chr(34)s. At present I'm using Chr(34)s to store double quotes, so I could just stick with that approach, and avoid converting double quotes.
- I haven't tested this all out yet, but it seems plausible.
- Perhaps now you can see why I'm tempted to hold out to see if the situation will get a little easier, before plunging into a particular strategy.
- These curious workarounds are preferable to maintaining separate/diverging versions of 40 libraries.
- Btw if I ever need/want to add something to a library that is AHK v2 only this is absolutely fine and doable, e.g. I can make addendum libraries resulting in something like 'MyClipboardLib', with the core functions, and 'MyClipboardLib+', with the additional AHK v2-only functions. Furthermore scripts can potentially become AHK v2 only, but it helps if libraries remain two-way compatible for the foreseeable future. Cheers.

- [EDIT:] I'm reminded of a fundamental problem with #Include, it is optimised to specify files relative to the path of the *script*, and not to the path of the *AHK exe*.
There is this:
Functions
https://autohotkey.com/docs/Functions.htm#lib
path-to-the-currently-running-AutoHotkey.exe\Lib\ ; Standard library.
- It's currently difficult however, AFAIK, for multiple exes to have both *shared* and *unique* (i.e. exe settings) library scripts at the same time.
- Hence a suggestion of something like:

Code: Select all

#Include, %A_AhkPath% settings1.ahk
#Include, %A_AhkPath% settings2.ahk
;or:
#Include, %A_AhkPath% Dir\settings1.ahk
#Include, %A_AhkPath% Dir\settings2.ahk
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
coffee
Posts: 133
Joined: 01 Apr 2017, 07:55

Re: conversion logic, v1 = -> v1 := -> v2, two-way compatibility

09 Jan 2018, 21:29

jeeswg wrote: - [EDIT:] I'm reminded of a fundamental problem with #Include, it is optimised to specify files relative to the path of the *script*, and not to the path of the *AHK exe*.
There is this:
Functions
https://autohotkey.com/docs/Functions.htm#lib
path-to-the-currently-running-AutoHotkey.exe\Lib\ ; Standard library.
- It's currently difficult however, AFAIK, for multiple exes to have both *shared* and *unique* (i.e. exe settings) library scripts at the same time.
- Hence a suggestion of something like:

Code: Select all

#Include, %A_AhkPath% settings1.ahk
#Include, %A_AhkPath% settings2.ahk
;or:
#Include, %A_AhkPath% Dir\settings1.ahk
#Include, %A_AhkPath% Dir\settings2.ahk
For manual includes, if you don't specify a folder then yes, it's relative to script dir. If you specify an allowed directory with the % signs, or a directory path, manual includes are relative to that directory.
Directory: Specify a directory instead of a file to change the working directory used by all subsequent occurrences of #Include and FileInstall. Note: Changing the working directory in this way does not affect the script's initial working directory when it starts running (A_WorkingDir). To change that, use SetWorkingDir at the top of the script.
https://autohotkey.com/docs/commands/_Include.htm

Unless i'm misinterpreting, why a_ahkpath? that's a full path to the executable file... why not a_ahkdir? You can just do

Code: Select all

#include %a_ahkdir%
#include file_in_ahkdir.ahk
;or
#include dir_in_ahkdir\file.ahk

In script.cpp @ isdirective function, look for

Code: Select all

		StrReplace(parameter, _T("%A_ScriptDir%"), mFileDir, SCS_INSENSITIVE, 1, space_remaining); // v1.0.35.11.  Caller has ensured string is writable.
		StrReplace(parameter, _T("%A_LineFile%"), Line::sSourceFile[mCurrFileIndex], SCS_INSENSITIVE, 1, space_remaining); // v1.1.11: Support A_LineFile to allow paths relative to current file regardless of working dir; e.g. %A_LineFile%\..\fileinsamedir.ahk.
		
Add under it

Code: Select all

		StrReplace(parameter, _T("%A_AhkDir%"), mOurEXEDir, SCS_INSENSITIVE, 1, space_remaining);
Compile.

For the function autofind, you just tweak the FindFuncInLibrary function in script.cpp

For example, for a v2 executable, adding a "Lib v2" lookup, before "Lib", in the same directory of "Lib" (documents\autohotkey)
Look for

Code: Select all

	#define FUNC_LIB_EXT EXT_AUTOHOTKEY
	#define FUNC_LIB_EXT_LENGTH (_countof(FUNC_LIB_EXT) - 1)
	#define FUNC_LOCAL_LIB _T("\\Lib\\") // Needs leading and trailing backslash.
	#define FUNC_LOCAL_LIB_LENGTH (_countof(FUNC_LOCAL_LIB) - 1)
	#define FUNC_USER_LIB _T("\\AutoHotkey\\Lib\\") // Needs leading and trailing backslash.
	#define FUNC_USER_LIB_LENGTH (_countof(FUNC_USER_LIB) - 1)
	#define FUNC_STD_LIB _T("Lib\\") // Needs trailing but not leading backslash.
	#define FUNC_STD_LIB_LENGTH (_countof(FUNC_STD_LIB) - 1)
Add under

Code: Select all

	#define FUNC_USER_LIB_V2 _T("\\AutoHotkey\\Lib v2\\") // Needs leading and trailing backslash.
	#define FUNC_USER_LIB_V2_LENGTH (_countof(FUNC_USER_LIB_V2) - 1)
Increase #define FUNC_LIB_COUNT by 1

Add before user library look up (// DETERMINE PATH TO "USER" LIBRARY:)

Code: Select all

		this_lib++; // For convenience and maintainability.
		this_lib->length = BIV_MyDocuments(this_lib->path, _T(""));
		if (this_lib->length < MAX_PATH-FUNC_USER_LIB_V2_LENGTH)
		{
			_tcscpy(this_lib->path + this_lib->length, FUNC_USER_LIB_V2);
			this_lib->length += FUNC_USER_LIB_V2_LENGTH;
		}
		else // Insufficient room to build the path name.
		{
			*this_lib->path = '\0'; // Mark this library as disabled.
			this_lib->length = 0;   //
		}
Which is the same code for the user library just using the changed definitions.
As dirty and quick as it can get.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: conversion logic, v1 = -> v1 := -> v2, two-way compatibility

09 Jan 2018, 22:06

What I want is something like this:

Code: Select all

;libraries shared by multiple exe files:
;note: there is no A_AhkDir variable at present
;note: using A_AhkDir may not be necessary since you can do:
;path-to-the-currently-running-AutoHotkey.exe\Lib\ ; Standard library.
#Include %A_AhkDir%\Lib\Lib1.ahk
#Include %A_AhkDir%\Lib\Lib2.ahk
#Include %A_AhkDir%\Lib\Lib3.ahk

;initial setting libraries used by individual exe files:
;note: there are no A_AhkDir or A_AhkNameNoExt variables at present
#Include %A_AhkDir%\%A_AhkNameNoExt% Settings.ahk

;an alternative that includes '.exe', but isn't too bad
;#Include %A_AhkPath% Settings.ahk
So you are saying that it's relatively straightforward to implement? Are you sure it's that simple to implement? See lexikos's comment 'More variables supported = larger code', made here:
A_UserName to be allowed in #include - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 13&t=15926

Do you want to submit to GitHub, to add in support for A_Desktop/A_UserName/A_AhkPath to #Include. Or to add A_AhkDir to AutoHotkey? A lot of people have asked for #Include to support more variables.

Btw you also commented there about junction links. Could you expand on your comments, I couldn't work out how to implement a junction link solution.

- [EDIT:] I think you added some comments re. searching for an AHK v2 Lib since I posted. Having AHK look for a folder called 'Lib v2' is a nice idea, although I tend to look for pure, general solutions, e.g. add support for a few more variables to #Include, and add an /inc command-line switch.
- Thanks again for sharing your C++ expertise. I will be doing more work in C++, and thus I should be able to propose base code in C++ for my wish list ideas. It doesn't mean that they'll be implemented, but I can at least do that.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
coffee
Posts: 133
Joined: 01 Apr 2017, 07:55

Re: conversion logic, v1 = -> v1 := -> v2, two-way compatibility

09 Jan 2018, 22:24

jeeswg wrote:What I want is something like this:

Code: Select all

;libraries shared by multiple exe files:
;note: there is no A_AhkDir variable at present
;note: using A_AhkDir may not be necessary since you can do:
;path-to-the-currently-running-AutoHotkey.exe\Lib\ ; Standard library.
#Include %A_AhkDir%\Lib\Lib1.ahk
#Include %A_AhkDir%\Lib\Lib2.ahk
#Include %A_AhkDir%\Lib\Lib3.ahk

;initial setting libraries used by individual exe files:
;note: there are no A_AhkDir or A_AhkNameNoExt variables at present
#Include %A_AhkDir%\%A_AhkNameNoExt% Settings.ahk

;an alternative that includes '.exe', but isn't too bad
;#Include %A_AhkPath% Settings.ahk
So you are saying that it's relatively straightforward to implement? Are you sure it's that simple to implement? See lexikos's comment 'More variables supported = larger code', made here:
A_UserName to be allowed in #include - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 13&t=15926

Do you want to submit to GitHub, to add in support for A_Desktop/A_UserName/A_AhkPath to #Include. Or to add A_AhkDir to AutoHotkey? A lot of people have asked for #Include to support more variables.

Btw you also commented there about junction links. Could you expand on your comments, I couldn't work out how to implement a junction link solution.
Well, it is what he said, str replace after str replace. It's true. What I posted does an extra str replace for a_ahkdir and the autofind is just a copy of the user lib look up.
the a_ variables in include dont use the same functions as the a_variables in script body. Basically, a_ahkdir not existing for the script to use, does not prevent you from using a_ahkdir in #include. The variable holding exe dir already exists in the script class.
The a_Ahkdir BIV can probably be copied from autohotkey_h's source. As it is implemented there.

For A_Desktop or others, it's a little bit different.
You edit the isdirective function, just a little bit lower than what I posted, after the a_appdatacommon replacement, adding

Code: Select all

		if (tcscasestr(parameter, _T("%A_Desktop%")))
		{
			BIV_SpecialFolderPath(buf, _T("A_Desktop"));
			StrReplace(parameter, _T("%A_Desktop%"), buf, SCS_INSENSITIVE, 1, space_remaining);
		}

		// #include %A_MyDocuments%
		if (tcscasestr(parameter, _T("%A_MyDocuments%")))
		{
			BIV_SpecialFolderPath(buf, _T("A_MyDocuments"));
			StrReplace(parameter, _T("%A_MyDocuments%"), buf, SCS_INSENSITIVE, 1, space_remaining);
		}

		// #include %A_UserProfile%
		if (tcscasestr(parameter, _T("%A_UserProfile%")))
		{
			BIV_SpecialFolderPath(buf, _T("A_UserProfile"));
			StrReplace(parameter, _T("%A_UserProfile%"), buf, SCS_INSENSITIVE, 1, space_remaining);
		}
Bunch of str replaces, in the purist sense of the word, it may not be "good maintainable code", as you are repeating yourself. If you decide to change implementation on one, then you probably will have to change the others manually. I'm pretty sure that can be done in a more speedier manner by shortcircuiting each replacement since they are absolute paths, i.e they are mutually exclusive for #include. Probably making the rest "else ifs" instead of "ifs" is the fastest way to achieve it. I just followed the pattern, i'll try else ifs later. You can also redo that entire block inside a loop using an array of the variables to replace maybe? *shrugs*

Then you have to go into the BIV_SpecialFolderPath in script2.cpp and add inside the switch

Code: Select all

	case 'M': // A_MyDocuments(Common) -> C:\Users\Username\Documents
		aFolder = aVarName[13] ? CSIDL_COMMON_DOCUMENTS : CSIDL_MYDOCUMENTS;
		break;
	case 'U': // A_UserProfile -> C:\Users\Username
		aFolder = CSIDL_PROFILE;
		break;
As for whatever it is you are trying to do with the includes, I wouldn't use the same folder for autohotkey executables versioning. It will fuck up ahk2exe and is not organized.


I'm not really doing any expertising here, i'm just copy pasting what's already in the code. It's just that, opening the solution in visual studio makes the code more visualizable (may not be a valid word) as you can jump into definitions/declarations, view call hierarchies, find all references, etc. Unlike opening it with a text editor.

edit:

Code: Select all

;note: using A_AhkDir may not be necessary since you can do:
;path-to-the-currently-running-AutoHotkey.exe\Lib\ ; Standard library.
"#include path-to-directory" does not change the behavior of autoincludes, the standard library is only for auto includes or pulling with < > and it doesnt exist either in user or local. You can pull from a subfolder in one of the libraries if you use <subfolder\file> but you can't choose from which library to do it (fringe/testing case).
I've only used that very very few times, for testing stuff only, and it wasn't for the lib folder, but for another folder inside ahk dir when ahk dir is not script dir, or a file in a subfolder inside the standard library that also existed in the same directory tree format in the user library. They are simply QOL, you can always type the path statically.

By default "#include file.ahk" is relative to scriptdir. If I want a file in ahk's dir (even in ahk dir root), then it's just QOL using a_ahkdir over c:\parent folder\autohotkey <insert variant/version/suffix>\subfolder-or-file.ahk
If i want a guarantee that the pull will be from the standard library for a given executable, #include %a_ahkdir%\lib\file.ahk when file.ahk can exist in the other two.
For a_ahkdir they are such fringe cases, that can be fixed by moving files around during normal use, that doesnt really warrant a permanent addition. But this is in no one else's binary so I dont give a shit.
I also a use a mini preparser that selects the executable to use at runtime based on what the script has specified, for whatever I was using it then, i'm sure it was easier than typing full paths for whatever I was trying to test.
In the same usage, if <file> is pulling from local, and you want to pull from user, %a_mydocuments%\autohotkey\lib\file.ahk. I mean, the real world use cases are spotty almost always if you maintain an organized library, which is why I understand the hesitation of adding a bunch of variables for a < 1% scenario.

edit2:
CSIDL_PROFILE in the switch statement is for the user's folder full path. Not just the username. I don't see username in here as it is obviously not a folder, could just be a matter of grabbing it from the string.

Code: Select all

#define CSIDL_DESKTOP                   0x0000        // <desktop>
#define CSIDL_INTERNET                  0x0001        // Internet Explorer (icon on desktop)
#define CSIDL_PROGRAMS                  0x0002        // Start Menu\Programs
#define CSIDL_CONTROLS                  0x0003        // My Computer\Control Panel
#define CSIDL_PRINTERS                  0x0004        // My Computer\Printers
#define CSIDL_PERSONAL                  0x0005        // My Documents
#define CSIDL_FAVORITES                 0x0006        // <user name>\Favorites
#define CSIDL_STARTUP                   0x0007        // Start Menu\Programs\Startup
#define CSIDL_RECENT                    0x0008        // <user name>\Recent
#define CSIDL_SENDTO                    0x0009        // <user name>\SendTo
#define CSIDL_BITBUCKET                 0x000a        // <desktop>\Recycle Bin
#define CSIDL_STARTMENU                 0x000b        // <user name>\Start Menu
#define CSIDL_MYDOCUMENTS               CSIDL_PERSONAL //  Personal was just a silly name for My Documents
#define CSIDL_MYMUSIC                   0x000d        // "My Music" folder
#define CSIDL_MYVIDEO                   0x000e        // "My Videos" folder
#define CSIDL_DESKTOPDIRECTORY          0x0010        // <user name>\Desktop
#define CSIDL_DRIVES                    0x0011        // My Computer
#define CSIDL_NETWORK                   0x0012        // Network Neighborhood (My Network Places)
#define CSIDL_NETHOOD                   0x0013        // <user name>\nethood
#define CSIDL_FONTS                     0x0014        // windows\fonts
#define CSIDL_TEMPLATES                 0x0015
#define CSIDL_COMMON_STARTMENU          0x0016        // All Users\Start Menu
#define CSIDL_COMMON_PROGRAMS           0X0017        // All Users\Start Menu\Programs
#define CSIDL_COMMON_STARTUP            0x0018        // All Users\Startup
#define CSIDL_COMMON_DESKTOPDIRECTORY   0x0019        // All Users\Desktop
#define CSIDL_APPDATA                   0x001a        // <user name>\Application Data
#define CSIDL_PRINTHOOD                 0x001b        // <user name>\PrintHood

#ifndef CSIDL_LOCAL_APPDATA
#define CSIDL_LOCAL_APPDATA             0x001c        // <user name>\Local Settings\Applicaiton Data (non roaming)
#endif // CSIDL_LOCAL_APPDATA

#define CSIDL_ALTSTARTUP                0x001d        // non localized startup
#define CSIDL_COMMON_ALTSTARTUP         0x001e        // non localized common startup
#define CSIDL_COMMON_FAVORITES          0x001f

#ifndef _SHFOLDER_H_
#define CSIDL_INTERNET_CACHE            0x0020
#define CSIDL_COOKIES                   0x0021
#define CSIDL_HISTORY                   0x0022
#define CSIDL_COMMON_APPDATA            0x0023        // All Users\Application Data
#define CSIDL_WINDOWS                   0x0024        // GetWindowsDirectory()
#define CSIDL_SYSTEM                    0x0025        // GetSystemDirectory()
#define CSIDL_PROGRAM_FILES             0x0026        // C:\Program Files
#define CSIDL_MYPICTURES                0x0027        // C:\Program Files\My Pictures
#endif // _SHFOLDER_H_

#define CSIDL_PROFILE                   0x0028        // USERPROFILE
#define CSIDL_SYSTEMX86                 0x0029        // x86 system directory on RISC
#define CSIDL_PROGRAM_FILESX86          0x002a        // x86 C:\Program Files on RISC

#ifndef _SHFOLDER_H_
#define CSIDL_PROGRAM_FILES_COMMON      0x002b        // C:\Program Files\Common
#endif // _SHFOLDER_H_

#define CSIDL_PROGRAM_FILES_COMMONX86   0x002c        // x86 Program Files\Common on RISC
#define CSIDL_COMMON_TEMPLATES          0x002d        // All Users\Templates

#ifndef _SHFOLDER_H_
#define CSIDL_COMMON_DOCUMENTS          0x002e        // All Users\Documents
#define CSIDL_COMMON_ADMINTOOLS         0x002f        // All Users\Start Menu\Programs\Administrative Tools
#define CSIDL_ADMINTOOLS                0x0030        // <user name>\Start Menu\Programs\Administrative Tools
#endif // _SHFOLDER_H_

#define CSIDL_CONNECTIONS               0x0031        // Network and Dial-up Connections
#define CSIDL_COMMON_MUSIC              0x0035        // All Users\My Music
#define CSIDL_COMMON_PICTURES           0x0036        // All Users\My Pictures
#define CSIDL_COMMON_VIDEO              0x0037        // All Users\My Video
#define CSIDL_RESOURCES                 0x0038        // Resource Direcotry

#ifndef _SHFOLDER_H_
#define CSIDL_RESOURCES_LOCALIZED       0x0039        // Localized Resource Direcotry
#endif // _SHFOLDER_H_

#define CSIDL_COMMON_OEM_LINKS          0x003a        // Links to All Users OEM specific apps
#define CSIDL_CDBURN_AREA               0x003b        // USERPROFILE\Local Settings\Application Data\Microsoft\CD Burning
// unused                               0x003c
#define CSIDL_COMPUTERSNEARME           0x003d        // Computers Near Me (computered from Workgroup membership)

#ifndef _SHFOLDER_H_
#define CSIDL_FLAG_CREATE               0x8000        // combine with CSIDL_ value to force folder creation in SHGetFolderPath()
#endif // _SHFOLDER_H_

#define CSIDL_FLAG_DONT_VERIFY          0x4000        // combine with CSIDL_ value to return an unverified folder path
#define CSIDL_FLAG_DONT_UNEXPAND        0x2000        // combine with CSIDL_ value to avoid unexpanding environment variables
#if (NTDDI_VERSION >= NTDDI_WINXP)
#define CSIDL_FLAG_NO_ALIAS             0x1000        // combine with CSIDL_ value to insure non-alias versions of the pidl
#define CSIDL_FLAG_PER_USER_INIT        0x0800        // combine with CSIDL_ value to indicate per-user init (eg. upgrade)
#endif  // NTDDI_WINXP
#define CSIDL_FLAG_MASK                 0xFF00        // mask for all possible flag values

SHSTDAPI SHGetSpecialFolderLocation(__reserved HWND hwnd, __in int csidl, __deref_out PIDLIST_ABSOLUTE *ppidl);

SHSTDAPI_(PIDLIST_ABSOLUTE) SHCloneSpecialIDList(__reserved HWND hwnd, __in int csidl, __in BOOL fCreate);
__success(return != 0)
SHSTDAPI_(BOOL) SHGetSpecialFolderPathA(__reserved HWND hwnd, __out_ecount(MAX_PATH) LPSTR pszPath, __in int csidl, __in BOOL fCreate);
__success(return != 0)
SHSTDAPI_(BOOL) SHGetSpecialFolderPathW(__reserved HWND hwnd, __out_ecount(MAX_PATH) LPWSTR pszPath, __in int csidl, __in BOOL fCreate);
#ifdef UNICODE
#define SHGetSpecialFolderPath  SHGetSpecialFolderPathW
#else
#define SHGetSpecialFolderPath  SHGetSpecialFolderPathA
#endif // !UNICODE

#if (NTDDI_VERSION >= NTDDI_WIN2K)
SHSTDAPI_(void) SHFlushSFCache(void);

typedef enum {
    SHGFP_TYPE_CURRENT  = 0,   // current value for user, verify it exists
    SHGFP_TYPE_DEFAULT  = 1,   // default value, may not exist
} SHGFP_TYPE;

SHFOLDERAPI SHGetFolderPathA(__reserved HWND hwnd, __in int csidl, __in_opt HANDLE hToken, __in DWORD dwFlags, __out_ecount(MAX_PATH) LPSTR pszPath);
SHFOLDERAPI SHGetFolderPathW(__reserved HWND hwnd, __in int csidl, __in_opt HANDLE hToken, __in DWORD dwFlags, __out_ecount(MAX_PATH) LPWSTR pszPath);
#ifdef UNICODE
#define SHGetFolderPath  SHGetFolderPathW
#else
#define SHGetFolderPath  SHGetFolderPathA
#endif // !UNICODE
SHSTDAPI SHGetFolderLocation(__reserved HWND hwnd, __in int csidl, __in_opt HANDLE hToken, __in DWORD dwFlags, __deref_out PIDLIST_ABSOLUTE *ppidl);
#endif  // NTDDI_WIN2K

#if (NTDDI_VERSION >= NTDDI_WINXP)
SHSTDAPI SHSetFolderPathA(__in int csidl, __in_opt HANDLE hToken, __in DWORD dwFlags, __in LPCSTR pszPath);
SHSTDAPI SHSetFolderPathW(__in int csidl, __in_opt HANDLE hToken, __in DWORD dwFlags, __in LPCWSTR pszPath);
#ifdef UNICODE
#define SHSetFolderPath  SHSetFolderPathW
#else
#define SHSetFolderPath  SHSetFolderPathA
#endif // !UNICODE
SHSTDAPI SHGetFolderPathAndSubDirA(__reserved HWND hwnd, __in int csidl, __in_opt HANDLE hToken, __in DWORD dwFlags, __in_opt LPCSTR pszSubDir, __out_ecount(MAX_PATH) LPSTR pszPath);
SHSTDAPI SHGetFolderPathAndSubDirW(__reserved HWND hwnd, __in int csidl, __in_opt HANDLE hToken, __in DWORD dwFlags, __in_opt LPCWSTR pszSubDir, __out_ecount(MAX_PATH) LPWSTR pszPath);
#ifdef UNICODE
#define SHGetFolderPathAndSubDir  SHGetFolderPathAndSubDirW
#else
#define SHGetFolderPathAndSubDir  SHGetFolderPathAndSubDirA
#endif // !UNICODE
#endif // NTDDI_VISTA
User avatar
derz00
Posts: 497
Joined: 02 Feb 2016, 17:54
Location: Middle of the round cube
Contact:

Re: conversion logic, v1 = -> v1 := -> v2, two-way compatibility

10 Jan 2018, 07:54

Looks like Lexikos is working on it: v1.1.28.00 is beta-release, and has support for all built-in variables in #include. https://autohotkey.com/boards/viewtopic ... 24&t=42628
try it and see
...
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: conversion logic, v1 = -> v1 := -> v2, two-way compatibility

18 Jan 2018, 11:17

Yes, this is pretty special. Force-local in the Christmas updates, and now, since 10th Jan, #Include %A_XXX%. AutoHotkey now has the two main features that I felt it really needed. I was sat staring at the screen for two days. I had no idea that these updates were coming. Cheers.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: conversion logic, v1 = -> v1 := -> v2, two-way compatibility

23 Jan 2018, 04:30

AUXILIARY FUNCTIONS: PIXELGETCOLOR / PIXELSEARCH

v2-changes
https://autohotkey.com/v2/v2-changes.htm
PixelSearch and PixelGetColor use RGB values instead of BGR, for consistency with other functions.
- To convert such lines using built-in functionality only, you would need a 'BGR to RGB' function, unless BGR parameters were added to the PixelXXX functions.

AUXILIARY FUNCTIONS: CONTROLXXX

v2-changes
https://autohotkey.com/v2/v2-changes.htm
ControlMove, ControlGetPos and ControlClick now use client coordinates
- To convert such lines using built-in functionality only, you would need separate window-mode functions (or some other more fiddly solution), unless something like A_CoordModeControl := "Window" was added to AHK v2.

VAR++ / VAR-- / VAR += N / VAR -= N

- In the past, incrementing/decrementing an empty string, was equivalent to incrementing/decrementing 0. But no longer since AHK v2.0-a084-72186a7 (alpha). It could be difficult to spot instances of this, although this change seems like the right thing to do (tough but right).
- Link:
AutoHotkey v2 alpha (UPDATES) - Page 2 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 10#p195810

Code: Select all

;AHK v1
var := ""
var++
MsgBox, % var ;1

var := ""
var += 5
MsgBox, % var ;5

;AHK v2
var := ""
var++
MsgBox(var) ;blank

;AHK v2
var := ""
var += 5
MsgBox(var) ;blank
- Some ideas for recreating the existing var++ functionality, that treats a blank string as 0.

Code: Select all

var := Trim("0" var) + 1 ;wouldn't work on negative numbers

var := Format("{:01}", var) + 1 ;padding a blank string with a leading zero, turns it into 0

(!var) ? (var := 0) : 0
var++

(!var) ? (var := 1) : var++

(var = "") ? (var := 0) : 0
var++

(var = "") ? (var := 1) : var++
- For most situations just add var := 0, to make a script work as before, but for some hotkey subroutines, you don't want the value reset to 0 when you run it, hence, the workarounds can be useful.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: conversion logic, v1 = -> v1 := -> v2, two-way compatibility

26 Jan 2018, 20:07

CONVERTING COMMANDS THAT I DON'T USE

I'm nearing completing my converter script, still to do: [EDIT: All done.]
- make sure all commands are supported (i.e. commands that I don't use) (DONE)
- (check that all ControlXXX/DriveXXX/ProcessXXX/WinXXX subcommands are fully supported)
- tidy converter script, ready to present
- test converter script on my main function libraries
- (update AHK v2 functions for AHK v1)
- (update general conversion advice list)

- After writing code to convert the functions I don't use, I would say:
- StringXXX. Make it absolutely obvious that StringGetPos, StringMid, StringReplace and StringSplit are deprecated. They are a barrel of fun to convert. [EDIT: Done. 'Deprecated' warnings have been added to the documentation for multiple functions.]
- OnExit. The OnExit command supports a label, but the new OnExit function does not. So this would have to be converted manually.
- SysGet/MonitorGetXXX. I wasn't sure if any of the MonitorGetXXX functions outputted arrays. In fact, they don't. MonitorGet/MonitorGetWorkArea each output 4 ByRef variables, similarly to WinGetPos. Which is a good solution.
- FileReadLine. This will be removed, I have a curious replacement for it below. I.e. a converter script should make it possible to convert anything, wherever possible (although certain command conversions may be turned off by default).
- WinGetActiveStats. This will be removed, it is a curious command, as you need two functions to replace it, see an example below.
- GetKeyState. It was awkward to convert from the GetKeyState command to the GetKeyState function, without using any temporary variables. I have made an attempt below. Hacker's delight.
- ... Since any scripts using the GetKeyState command will expect D/U/(blank), my conversion script should retrieve 1/0/(blank) as the function does, and convert it to D/U/(blank). The trick is to pass the 1/0/(blank) to an array, which then outputs D/U/(blank) accordingly.
- ... I would recommend that users convert the GetKeyState command to the GetKeyState function manually, and remove all references to 'D' or 'U'.
- Transform. The command is curious, and it has a lot of functionality duplicated elsewhere, but it is reasonably straightforward to convert.

Code: Select all

;converter before/after examples

FileReadLine, OutputVar, Filename, LineNum
OutputVar := StrSplit(FileRead("Filename"), "`n", "`r")[LineNum] ;file read line

GetKeyState, OutputVar, KeyName, Mode
OutputVar := {0:"U",1:"D"}[GetKeyState("KeyName", "Mode")]

SysGet, Var, Monitor, N
MonitorGet("N", VarLeft, VarTop, VarRight, VarBottom)

SysGet, Var, MonitorWorkArea, N
MonitorGetWorkArea("N", VarLeft, VarTop, VarRight, VarBottom)

WinGetActiveStats, Title, Width, Height, X, Y
Title := WinGetTitle("A"), WinGetPos(X, Y, Width, Height, "A")
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: conversion logic, v1 = -> v1 := -> v2, two-way compatibility

02 Mar 2018, 07:44

CONVERTER COMPLETED

AHK v1 to AHK v2 converter - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 37&t=36754

MOUSECLICK REVISITED

- I've improved my ideas from here slightly by introducing Swap0/Swap1:
conversion logic, v1 = -> v1 := -> v2, two-way compatibility - Page 5 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 47#p180247
- I would suggest MouseClick (or Click) and MouseDrag (or MouseClickDrag).
- I don't like the 'clever' parameters in Click. I find '2' to mean double-click, but '2,3' to mean single-click at (2,3) to be confusing.
- I would have a writable variable A_MouseSwapButtons (or another name) that has a default value equal to SM_SWAPBUTTON (1 or 0). SM_SWAPBUTTON corresponds to:
Control Panel\Hardware and Sound, Mouse,
'Switch primary and secondary buttons'

Code: Select all

;options:
;C2 (click n times) [perhaps also: 2 (click n times)]
;D|U|Down|Up
;M|R|X1|X2|Middle|Right
;Rel|Relative
;S0 (speed) (min. delay: 0, max. delay: 100)
;Swap ;temporarily override A_MouseSwapButtons (force swap)
;Swap0 ;temporarily override A_MouseSwapButtons (force no swap)
;Swap1 ;temporarily override A_MouseSwapButtons (force swap)
;WU|WD|WL|WR|WheelUp|WheelDown|WheelLeft|WheelRight

JEE_MouseClick(vPosX:="", vPosY:="", vOpt:="")
{
	if (A_MouseSwapButtons = "")
		SysGet, vDoSwap, 23 ;SM_SWAPBUTTON := 23
	else
		vDoSwap := A_MouseSwapButtons
	Loop, Parse, vOpt, % " `t"
	{
		if (A_LoopField ~= "i)^(M|R|X1|X2|WU|WD|WL|WR|Middle|Right|WheelUp|WheelDown|WheelLeft|WheelRight)$")
			vWhichButton := A_LoopField
		else if (A_LoopField ~= "i)^S\d+$")
			vSpeed := SubStr(A_LoopField, 2)
		else if (A_LoopField = "Rel") || (A_LoopField = "Relative")
			vRel := "R"
		else if (SubStr(A_LoopField, 1, 1) = "C")
			vClickCount := SubStr(A_LoopField, 2)
		else if (A_LoopField ~= "^\d+$")
			vClickCount := A_LoopField
		else if (A_LoopField ~= "i)^(D|U|Down|Up)$")
			vEvent := A_LoopField
		else if (A_LoopField = "Swap") || (A_LoopField = "Swap1")
			vDoSwap := 1
		else if (A_LoopField = "Swap1")
			vDoSwap := 0
	}
	if (vClickCount = 0)
	{
		MouseMove, % vPosX, % vPosY, % vSpeed, % vRel
		return
	}
	if vDoSwap
		if (vWhichButton = "")
			vWhichButton := "R"
		else if (SubStr(vWhichButton, 1, 1) = "R")
			vWhichButton := "L"
	MouseClick, % vWhichButton, % vPosX, % vPosY, % vClickCount, % vSpeed, % vEvent, % vRel
}
My expectation would be something like this, to allow mathematics to be performed on the coordinates, and then a space-separated list of options. 'D' for destination.

Code: Select all

MouseClick(11, 22, "R Rel")
MouseDrag(11, 22, 33, 44, vOpt)
Send("{Click X11 Y22 R Rel}")
Send("{Click X11 Y22 DX33 DY44 " vOpt "}")
Options with approximate likelihood of usage:
WhichButton Rel Speed ClickCount Event Swap0/Swap1

There could also be options to temporarily override:
SendMode Input|Play|Event|InputThenPlay
e.g. SMI/SMP/SME/SMIP and/or A_SendModeMouse. Perhaps a 'Normal'/'Basic' mode (or another name) is needed. [EDIT:] Reading through the documentation, I think that 'Event' *is* the normal/basic mode in AHK v1, so 'Normal'/'Basic' modes wouldn't be needed.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: conversion logic, v1 = -> v1 := -> v2, two-way compatibility

01 May 2018, 09:48

THREAD COMMAND
- Maybe 'ThreadBlockTimers'?
- I'd be tempted to replace 'Thread' and most of the settings commands with writable 'A_' variables.
- Otherwise IMO 3 having separate ThreadXXX functions is preferable to having 1 Thread function, as double quotes are avoided, and it looks neater.
- [EDIT:] Re. Thread/Critical, an option to cache/discard hotkeys pressed while a subroutine was running, would be useful.

VARSETCAPACITY
- (Essentially 'length' is the size of something, 'capacity' is the size of something plus the size of extra available unused space.)
- This is an idea:

Code: Select all

VarSetSize(Var, Size, FillByte) ;[bytes] ['VarSetLen' isn't so great]
VarSetCapacity(Var, Capacity, FillByte) ;[bytes] [as it is now] [already exists]
StrSetLen(Var, Len, FillChar) ;[chars]
StrSetCapacity(Var, Capacity, FillChar) ;[chars]

VarGetSize(Var) [or 'SizeOf']
VarGetCapacity(Var)
StrLen(Var) ;[already exists]
StrGetCapacity(Var)
- One question is whether all of these would overwrite any existing data. If so, then StrSetLen could cause people to accidentally lose their data.
- [EDIT:] I would suppose that the data would be maintained by default, necessitating a 4th Options parameter to keep/discard data, with keep as the default.

OBJECT IDEAS
- I've tried to collect/anticipate the solutions to the core problems regarding objects that general users face.
objects/object classes: new features from a newbie-friendly perspective - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 83#p214283

ORIGINAL AUTOHOTKEY V2 DISCUSSION THREAD
- Interesting. I enjoyed infogulch's comments in particular.
AutoHotkey v2 Alpha Release - Scripts and Functions - AutoHotkey Community
https://autohotkey.com/board/topic/6531 ... a-release/
Last edited by jeeswg on 15 Nov 2018, 07:01, edited 2 times in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
guest3456
Posts: 3454
Joined: 09 Oct 2013, 10:31

Re: conversion logic, v1 = -> v1 := -> v2, two-way compatibility

01 May 2018, 10:01

for function name changes such as RegisterCallback -> CallbackCreate, we used this solution in the GDIP v2 lib:

https://github.com/mmikeww/AHKv2-Gdip/b ... 2819-L2820

User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: conversion logic, v1 = -> v1 := -> v2, two-way compatibility

02 Aug 2018, 19:08

- I've been considering using the double-deref hack in my function libraries to make loops forwards compatible. 'Write once, run anywhere' to coin a phrase. (It's a little verbose however, requiring 4 extra lines prior to the file loop in the example below. In case anyone has any suggestions.)
- I also present 'is version 1' checks for InStr/RegExMatch/RegExReplace/SubStr. I.e. to check the behaviour of the function itself, is more reliable than depending on A_AhkVersion.

Code: Select all

q:: ;some two-way compatibility checks
;e.g. check the behaviour of functions and of %var% within Loop
vList := "InStr,RegExMatch,RegExReplace,SubStr,var"
vOutput := ""
for _, vCmd in StrSplit(vList, ",")
	vOutput .= JEE_IsV1(vCmd) "`t" vCmd "`r`n"
;MsgBox, % vOutput
MsgBox(vOutput)

;e.g. file loop
vFilePtn := A_ScriptFullPath, vMode := "F"
vIsV1 := JEE_IsV1("var")
vFileX := vIsV1 ? vFilePtn : "vFilePtn"
vModeX := vIsV1 ? vMode : "vMode"
Loop Files, %vFileX%, %vModeX%
{
	vPath := A_LoopFileFullPath
	break
}
;MsgBox, % vPath
MsgBox(vPath)
return

JEE_IsV1(vCmd)
{
	if (vCmd = "InStr")
		return InStr(1, 1,, 0)
	else if (vCmd = "RegExMatch")
		return RegExMatch(10, 1,, -1)
	else if (vCmd = "RegExReplace")
		return !RegExReplace(10, 1,,,, -1)
	else if (vCmd = "SubStr")
		return !!SubStr(1, 0)
	else if (vCmd = "var")
	{
		vDeref1 := "vDeref2", vDeref2 := ""
		if %vDeref1%
			return 1
		else
			return 0
	}
}
- Here are some small ideas for 'Loop' and 'if var is type' that would make it easier to write forwards compatible code in AHK v1:

Code: Select all

;==================================================

;before
Loop % vCount

;after [no new functionality needed]
while (A_Index <= vCount)

;==================================================

;before
Loop Parse, vText, `n, `r

;after [requires a 'for loop' to support A_LoopField]
for vKey, A_LoopField in StrSplit(vText, "`n", "`r")

;==================================================

;before
Loop Reg, % vRegPattern, V

;after [allow " in the Mode parameter in AHK v1, the quotes would be ignored]
vDeref1 := "vDeref2", vDeref2 := "", vIsV1 := 0
if %vDeref1%
	vIsV1 := 1
vRegX := vIsV1 ? vRegPattern : "vRegPattern"
Loop Reg, %vRegX%, "V"

;==================================================

;before
Loop Files, % vFilePattern, F

;after [allow " in the Mode parameter in AHK v1, the quotes would be ignored]
vDeref1 := "vDeref2", vDeref2 := "", vIsV1 := 0
if %vDeref1%
	vIsV1 := 1
vFileX := vIsV1 ? vFilePattern : "vFilePattern"
Loop Files, %vFileX%, "F"

;==================================================

;before
if var is % vType

;after [two-way compatible if use parentheses]
if (var is vType)

;==================================================
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
guest3456
Posts: 3454
Joined: 09 Oct 2013, 10:31

Re: conversion logic, v1 = -> v1 := -> v2, two-way compatibility

02 Aug 2018, 21:11

jeeswg wrote: - I also present 'is version 1' checks for InStr/RegExMatch/RegExReplace/SubStr. I.e. to check the behaviour of the function itself, is more reliable than depending on A_AhkVersion.
why is it more reliable?

jeeswg wrote:

Code: Select all

;before
if var is % vType

;after [two-way compatible if use parentheses]
if (var is vType)
eh, no.
in v1, if var is type is an if-command, and so parens wouldn't work because is isn't an expression operator in v1:

Code: Select all

var := "hello"

if (var is integer)
   msgbox true
else
   msgbox false
we do it this way in GDIPv2:
https://github.com/mmikeww/AHKv2-Gdip/b ... .ahk#L2773

User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: conversion logic, v1 = -> v1 := -> v2, two-way compatibility

02 Aug 2018, 21:41

- It's possible that the user might replace the function with a custom function.
- Also, in the future, there *might* be an 'AHK v2' mode for AHK v1. Where the function behaviour could change, or always be 'AHK v2' style within a script.
- (Then there's the question of reliably checking A_AhkVersion. It's unlikely to go wrong, but this method is a pure one.)

- Good point re. 'is', thanks, I see that contains/in/is are only listed for AHK v2.
Variables and Expressions - Definition & Usage | AutoHotkey
https://autohotkey.com/docs/Variables.htm#Operators
Variables and Expressions - Definition & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/Varia ... #Operators
- (I'm aware of the 'breaking backwards compatibility' argument, but reserving those 3 words in AHK v1 seems justified, the current behaviour breaks the principle of least astonishment and leads to bugs. How many people use those as variable names, and should they!? As long as no silent errors are created.)
- Well I'd said I'd be happy with function version of contains/in/is in AHK v1/v2, (easier to implement,) and that still stands.

- [EDIT:] I was suggesting that if (var is type) be possible in AHK v1, and that contains/in/is operators be introduced to AHK v1. Btw AHK v1 already distinguishes between if var1 = LiteralText (old-style) and if (var1 = var2) (new-style), this applies to 7 operators: = != <> < > <= >=. In AHK v2 both are new-style.
Last edited by jeeswg on 15 Nov 2018, 07:11, edited 3 times in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: conversion logic, v1 = -> v1 := -> v2, two-way compatibility

08 Oct 2018, 22:59

- I mentioned some ideas in MOUSECLICK REVISITED above.
- Working on 'AHK v2 functions for AHK v1', I noticed how complex ControlClick is.
- I might suggest changing it to this, using the space-separated Options as described for MouseClick above.
- By default it would click the middle of the control/window.

Code: Select all

;before:
ControlClick Control-or-Pos, WinTitle, WinText, WhichButton, ClickCount, Options, ExcludeTitle, ExcludeText

;after:
ControlClick Options, Control, WinTitle, WinText, ExcludeTitle, ExcludeText

;plus:
WinClick Options, WinTitle, WinText, ExcludeTitle, ExcludeText
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: conversion logic, v1 = -> v1 := -> v2, two-way compatibility

27 Oct 2018, 14:52

CONTROLCLICK / CONTROLGETPOS / CONTROLMOVE

- A key conversion obstacle is ControlClick/ControlGetPos/ControlMove.
- In AHK v1 you specify coordinates relative to the top-left corner of the window.
- In AHK v2 you specify coordinates relative to the top-left corner of the client area (which excludes the title bar/menu bar/borders etc).
- You can't therefore automate conversion as the coordinates could be wrong.
- One idea could be an A_CoordModeControl variable. To choose window/client coordinates.
- However, I think that a better solution is this, AHK v2 remains as it is, but custom functions are made available with the AHK v1 behaviour. That way you can automate the conversion with AHK v1-like functions, and change to the AHK v2 functions (and change the coordinates) at a more convenient time.
- I'll be adding these to the 'AHK v2 functions for AHK v1' expansion pack. I'll do something for ControlClick when it's more clear what the function will be like.
- (Btw it might be helpful if the AHK v2 documentation stated what should happen if ControlGetPos/ControlMove are applied to windows instead of controls.)

Code: Select all

;==================================================

;works like ControlGetPos (AHK v1) (on AHK v1/v2)
JEE_ControlGetPos1(ByRef vCtlX:="", ByRef vCtlY:="", ByRef vCtlW:="", ByRef vCtlH:="", vControl:="", vWinTitle:="", vWinText:="", vExcludeTitle:="", vExcludeText:="")
{
	local
	hCtl := ControlGetHwnd(vControl, vWinTitle, vWinText, vExcludeTitle, vExcludeText)
	hWnd := DllCall("user32\GetParent", Ptr,hCtl, Ptr)
	WinGetPos(vWinX, vWinY,,, "ahk_id " hWnd)
	WinGetPos(vCtlX, vCtlY, vCtlW, vCtlH, "ahk_id " hCtl)
	vCtlX -= vWinX, vCtlY -= vWinY
}

;==================================================

;works like ControlMove (AHK v1) (on AHK v1/v2)
JEE_ControlMove1(vCtlX:="", vCtlY:="", vCtlW:="", vCtlH:="", vControl:="", vWinTitle:="", vWinText:="", vExcludeTitle:="", vExcludeText:="")
{
	local
	hCtl := ControlGetHwnd(vControl, vWinTitle, vWinText, vExcludeTitle, vExcludeText)
	hWnd := DllCall("user32\GetParent", Ptr,hCtl, Ptr)
	WinGetPos(vWinX, vWinY,,, "ahk_id " hWnd)
	WinGetClientPos(vCWinX, vCWinY,,, "ahk_id " hWnd)
	WinMove(vCtlX+vWinX-vCWinX, vCtlY+vWinY-vCWinY, vCtlW, vCtlH, "ahk_id " hCtl)
}

;==================================================
- There are some related functions here:
convert coordinates between Client/Screen/Window modes - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 37#p246337
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: conversion logic, v1 = -> v1 := -> v2, two-way compatibility

09 Jun 2019, 01:27

4 EDITS TO AHK V2: SCRIPTS THAT WORK IN BOTH AHK V1/V2

As a result of wanting to test GUI object backports, I made 4 edits to the AHK v2 source code, that allowed AHK v2 to run both AHK v1/v2 code. (Note: where the AHK v1 code is written in a forwards compatible way and uses custom backport functions.)

RELEVANT COMMITS

Links to relevant code for the 4 edits:

[Loop/Loop XXX: allow '% Expression'][redundant if Loop (expression)/LoopXXX are agreed for AHK v1/v2]
Changed commands to accept expressions by default. · Lexikos/AutoHotkey_L@4f8d55d · GitHub
https://github.com/Lexikos/AutoHotkey_L/commit/4f8d55dfddb6135c1d32584cb42ccefca0260e9d

[ControlXXX: allow omitting the 'Control' parameter][redundant if 'Control' is made omissible again for all ControlXXX functions in AHK v2]
Changed usage of Control parameter. · Lexikos/AutoHotkey_L@01f61cb · GitHub
https://github.com/Lexikos/AutoHotkey_L/commit/01f61cbd676026e29b15e8043cefc2260b288a9a

[if (a = b): restore the numeric comparison of string variables][redundant if the behaviour is restored in AHK v2]
Revised handling of types (integer/float/string). · Lexikos/AutoHotkey_L@275216e · GitHub
https://github.com/Lexikos/AutoHotkey_L/commit/275216e20724568ba7afbe6f2cc29297fd162be2

[#IfWin: restore #IfWinActive][redundant if #If WinXXX() is optimised in AHK v1]
Removed #IfWin and optimized #If Win(). · Lexikos/AutoHotkey_L@e4a5493 · GitHub
https://github.com/Lexikos/AutoHotkey_L/commit/e4a5493749362b11c83ff71a9695782e72f82c65

I will post more specific C++ code in a separate thread.
[EDIT:]
AHK v2: 4 mods to enable two-way compatibility - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=75&t=69595

ERROR CHECKING

It's great having AHK v2 error checking for AHK v1 scripts. The error checking has perfected my scripts in terms of forwards compatibility, and has revealed two examples of '~' when '~=' was intended.

I was already aware that 'obj.item(key) := value' was not allowed in AHK v2, but the error checking helped discover instances.

Clarification of those two curios revealed through error checking:

Code: Select all

obj.item(key) := value ;before (allowed in AHK v1, but not allowed in AHK v2)
obj.item[key] := value ;after

if (var ~ "LiteralRegExNeedle") ;before (typo)
if (var ~= "LiteralRegExNeedle") ;after

PRIORITIES

This exercise means that I feel we can delay some AHK v2 changes (e.g. finalising Loop). But equally, it has helped solidify certain priorities:
- AHK v2: fix (restore) comparison of variables containing numeric-looking strings
- AHK v1/v2: A_ variables: A_AhkVersionMain/A_OSVersionMain (or other names) [e.g. 1.1/6.1] [or alternatively an '/inc' command-line switch to specify files for inclusion] [maybe A_AhkVersion1/2/3/4 or equivalent]
- AHK v1/v2: 'global A_XXX' to not cause a crash, and a VarIsInit/IsInit function [to facilitate backporting A_ variables] [note: VarIsInit to report 2/A/B/something else for built-in variables]

FLAG AHK V1 CODE IN AHK V2

I'd recommend doing the following to AHK v1 code that is not forwards compatible, to prevent it going undetected in AHK v2:

Code: Select all

;use '% ' in Loop and with contains/in/is:
Loop % count
if var contains % "abc,def,ghi"
if var in % "abc,def,ghi"
if var is % "number"

;use an initial comma with commands:
Run, % vTarget
I.e. write two-way compatible code where possible. Where code is not two-way compatible, write it in a way that would trigger an error in the other version.
Code that can go undetected: changes to A_LoopFileXXX/A_LoopRegXXX, InStr/RegExMatch/RegExReplace/SubStr offsets, RegExMatch variable/object mode, StrPut.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA

Return to “AutoHotkey Development”

Who is online

Users browsing this forum: No registered users and 32 guests