autohotkey assign a text expression to a variable - string

In an autohotkey script I was trying to assign a text string to a variable. Can anyone spot why this doesn't work, it looks just like some provided examples? I don't need the variable anymore, but now I'm just curious what I did wrong...
myvar := % "foo" ; creates global variable myvar, but has no contents
I know you could just say myvar = foo
but leaving quotes off a fixed text string just makes me cringe. I even had it working but didnt' save my file before making 'a few harmless cosmetic edits.' I click on the "H" task
icon near the clock, use menu File / show variables to verify the empty contents...

Okay, so we assign a value to a variable:
eval_this:= "harry"
If you do it this way, you just read the contents of the variable:
msgbox %eval_this% ;=harry
Of course, here, the text is not evaluated - "eval_this" is just text to ahk:
msgbox eval_this ;= eval_this
This method is called a "Forced Expression" - which is what you are looking to do. It tries to read the text string as if it were code. It isn't reading the contents of any variable, it is looking at the text and forcing it to become a variable (it's sort of the same thing, but not really)
msgbox % eval_this ;= harry
This also gets us harry, and you can see how we are reading the variable:
test := eval_this
msgbox %test% ;=harry
Same thing, different approach (forcing the text to become a variable):
test = % eval_this
msgbox %test% ;=harry
Consider this, where we force both text strings into their actual values
eval_this := "harry"
this_too := " and bob"
test = % eval_this this_too
msgbox %test% ;= harry and bob
Okay, now that you've got all that, here is a practical application. We will force the value of the text string to be a variable. Since we've actually defined what "alert" is, then the gosub will call that definition. Pop this into a script and run it:
eval_this := "alert"
gosub % eval_this
exit ;we are finished with the demo, so end here
alert:
msgbox harry
return

Related

Hyperlink to worksheet field by name

I am trying to create a hyperlink from one worksheet(Home) to another worksheet(Events details), finding the target cell by the value.
"Home":
A
[MyLink]
"Events details"
A
["hello there"]
So "MyLink" should point to "hello there" independent of the position of "hello there" in A column.
What I tried: I could create a hyper link to a specific cell with
HYPERLINK("#'Events details'!A2")
I could find a position of "hello there" using:
=MATCH("hello there", 'Events details'!$A:$A, 0)
so I tried putting it together
HYPERLINK("#'Events details'!MATCH("hello there", 'Events details'!$A:$A, 0)")
but it does not seems to work
Try this:
=HYPERLINK("#'Events details'!A"&MATCH("hello there",'Events details'!A:A,),"DisplayText")
The problem with the formula that you tried to use is that excel does not evaluate text strings (stuff between quotes). What I mean by that, can be seen below:
=HYPERLINK("#'Events details'!MATCH("hello there", 'Events details'!$A:$A, 0)")
^------------------------^ ^----------------------------^
Excel first interprets the first part above as text "#'Events details'!MATCH(", followed by a command supposedly called hello there and then another text ", 'Events details'!$A:$A, 0)" because of the way the quotes work.
But this, as you have seen will not give you anything useful.
What you will have to do is concatenate the sheetname, and the cell address (which has the column name and row number). Since you already know that the column name is A, you know that the full cell address should be something like "#'Events details'!A_" where the underscore here is meant to be some number you will retrieve using MATCH. For this first part, your HYPERLINK function becomes:
=HYPERLINK("#'Events details'!A" )
Since MATCH returns the row number, you will have to put it afterwards (notice that the whole MATCH function is outside any quotes):
=HYPERLINK("#'Events details'!A"MATCH("hello there", 'Events details'!$A:$A, 0))
But wait! Excel doesn't understand that the two 'parts' above should become one, so you have to use something to concatenate the two parts, the shorter way being by using the &:
=HYPERLINK("#'Events details'!A"&MATCH("hello there", 'Events details'!$A:$A, 0))
Or you can use the longer function:
=HYPERLINK(CONCATENATE("#'Events details'!A",MATCH("hello there", 'Events details'!$A:$A, 0)))
And of course, you can use the optional second input of the HYPERLINK function to give the link a 'friendly name' as Excel calls it.

Linux application in livecode

Spell Check is a default application in Linux. With the help of that application, can we check the spelling of a text field while users enter data?
Some (or many?) Linux distributions contain a command line utility that is called spell. If you run this with words as parameters, you need to press return a second time, but if you use a file as a paramater, you don't need to press return again. This means that a solution could be:
write the text of a field to a file
run the command line utility from LiveCode's shell function with the file as parameter
parse the result returned by the shell function
Before you try this, open your terminal on Linux and type spell. Press enter to see if the command is recognised. If yes, then the script below should work.
This script writes the text of a field to a file, does a spell check on the file and returns the incorrect words to LiveCode. I haven't tested the script and you may have to tweak it a little.
function spellCheck theText
// works on Linux only
if the platform is "Linux" then
// remove everything that isn't a word
put replaceText(theText,"[^\w]","") into myWords
// write clean data to a temporary file
put the tempName into myTempFile
put myWords into url ("file:" & myTempFile)
// call spell with shell
put "spell" && myTempFile into myShell
// only return the incorrect words
put line 2 to -1 of shell(myShell) into myCorrections
// return the incorrect words to calling handler
return myCorrections
else
// this isn't Linux
return "error"
end if
end spellCheck
//theField is the short name of a field
on checkField theField
// call above function
put spellCheck(the text of fld theField) into myWords
// myWords should now contain the incorrect words
if myWords is not "error" then
lock screen
// parse incorrect words and mark them in the field
repeat with x = 1 to number of words of field theField
if myWord is among the lines of myWords then
// an incorrect word has been found and is marked red
set the textColor of word x of fld theField to red
end if
end repeat
unlock screen
end if
end checkField
Usage: checkField shortNameOfTheField

AutoHotKey - Assign variable to key (shortcut)

I was wondering.. How can I assign a variable / text to a key (shortcut)?
This is what I am trying to achieve:
myVar = Hello
#F11 = myVar ;myVar ("hello") is stored / bound to the shortcut: `WINDOWS+F11`.
So, whenever I press WINDOWS + F11, it should paste/write the content of myVar.
Is this even possible? If yes, can I do it with multiply keys?
This is the correct syntax:
SendMode Input
myVar := "Hello"
myOtherVar := "World"
#F10::Send, Hello world!
#F11::Send, %myVar%
#F12::Send, %myOtherVar%
You'll notice that the text is only sent when you release the WIN-key. This is intended, and is a special behavior for the WIN-modifier. You can read more about it here.

LibreOffice Calc macro text search

I need help in writing a macro for LibreOffice Calc 3.6.2.2
What I'm trying to do is pass a cell number to function, the function then analyzes the cell's contents (a text string) and return a value based on its content.
My current code:
Function mColor2(mCellAdd)
Dim l(5) as String 'declare list of variables
l(0)="red"
l(1)="blue"
l(2)="yellow"
l(3)="green"
for i=LBound(l) To UBound(l) 'cycle from start to end of list
If InStr(mCellAdd,l(i))<>0 Then
mColor2=l(i)
Else
mColor2="not known"
End If
Next
End Function
But I get only "not known" returned.
I think it's because I don't handle values returned from InStr() properly.
Actually I'm not sure I'm using the right function since I only need to check if the cell's content includes my substring or not...
A screenshot of the results:
Let's say the input string (assuming it is passed as a string?) is "my blue dog". Then your function will run through the loop 5 times (should that be 4?). First it looks for "red", which isn't found, so mColor2 is set to "not known". Then it looks for "blue", which is found, so mColor2 is set to "blue". Then it looks for "yellow", and mColor2 is set back to "not known". Same for "green", and then I don't know what it will look for when i is 4.
In any case, you don't want to reset mColor2 to "not known", just get rid of your Else.
Have you decided what to do if the input string contains more than one of the strings you're looking for? Do you want to exit as soon as you find "red", or continue and remember the last one found?

Excel Select Case?

i want to create the "cases" formula for excel to simulate Select case behavior (with multiple arguments and else optional).
If A1 and A2 are excel cells, this is the goal:
A1 Case: A2 Formula: A2 Result
5 cases({A1>5,"greather than 5"}, {A1<5, "less than 5"},{else,"equal to 5"}) equal to 5
Hi cases({A1="","there is nothing"},{else,A1}) Hi
1024 cases({5<A1<=10,10},{11<=A1<100,100},{A1>100,1000}) 1000
12 cases({A1=1 to 9, "digit"}, {A1=11|22|33|44|55|66|77|88|99, "11 multiple"}) (empty)
60 cases({A1=1 to 49|51 to 99,"not 50"}) not 50
If it could, It must accept excel formulas or vba code, to make an operation over the cell before take a case, i.g.
cases({len(A1)<7, "too short"},{else,"good length"})
If it could, it must accept to or more cells to evaluate, i.g.
if A2=A3=A4=A5=1 and A1=2, A6="one", A7="two"
cases(A1!=A2|A3|A4|A5, A6}, {else,A7}) will produce "two"
By the way, | means or, != means different
Any help?
I'm grateful.
What I could write was this:
Public Function arr(ParamArray args()) 'Your function, thanks
arr = args
End Function
Public Function cases(arg, arg2) 'I don't know how to do it better
With Application.WorksheetFunction
cases = .Choose(.Match(True, arg, 0), arg2)
End With
End Function
I call the function in this way
=cases(arr(A1>5, A1<5, A1=5),arr( "gt 5", "lt 5", "eq 5"))
And i can't get the goal, it just works for the first condition, A1>5.
I fixed it using a for, but i think it's not elegant like your suggestion:
Function selectCases(cases, actions)
For i = 1 To UBound(cases)
If cases(i) = True Then
selectCases = actions(i)
Exit Function
End If
Next
End Function
When i call the function:
=selectCases(arr(A1>5, A1<5, A1=5),arr( "gt 5", "lt 5", "eq 5"))
It works.
Thanks for all.
After work a little, finally i get a excel select case, closer what i want at first.
Function cases(ParamArray casesList())
'Check all arguments in list by pairs (case, action),
'case is 2n element
'action is 2n+1 element
'if 2n element is not a test or case, then it's like the "otherwise action"
For i = 0 To UBound(casesList) Step 2
'if case checks
If casesList(i) = True Then
'then take action
cases = casesList(i + 1)
Exit Function
ElseIf casesList(i) <> False Then
'when the element is not a case (a boolean value),
'then take the element.
'It works like else sentence
cases = casesList(i)
Exit Function
End If
Next
End Function
When A1=5 and I call:
=cases(A1>5, "gt 5",A1<5, "lt 5","eq 5")
It can be read in this way: When A1 greater than 5, then choose "gt 5", but when A1 less than 5, then choose "lt 5", otherwise choose "eq 5". After run it, It matches with "eq 5"
Thank you, it was exciting and truly educative!
O.K., there's no way at all to do exactly what you want. You can't use anything other than Excel syntax within a formula, so stuff like 'A1 = 1 to 9' is just impossible.
You could write a pretty elaborate VBA routine that took strings or something and parsed them, but that really amounts to designing and implementing a complete little language. And your "code" wouldn't play well with Excel. For example, if you called something like
=cases("{A1="""",""there is nothing""},{else,A1}")
(note the escaped quotes), Excel wouldn't update your A1 reference when it moved or the formula got copied. So let's discard the whole "syntax" option.
However, it turns out you can get much of the behavior I think you actually want with regular Excel formulas plus one tiny VBA UDF. First the UDF:
Public Function arr(ParamArray args())
arr = args
End Function
This lets us create an array from a set of arguments. Since the arguments can be expressions instead of just constants, we can call it from a formula like this:
=arr(A1=42, A1=99)
and get back an array of boolean values.
With that small UDF, you can now use regular formulas to "select cases". They would look like this:
=CHOOSE(MATCH(TRUE, arr(A1>5, A1<5, A1=5), 0), "gt 5", "lt 5", "eq 5")
What's going on is that 'arr' returns a boolean array, 'MATCH' finds the position of the first TRUE, and 'CHOOSE' returns the corresponding "case".
You can emulate an "else" clause by wrapping the whole thing in 'IFERROR':
=IFERROR(CHOOSE(MATCH(TRUE, arr(A1>5, A1<5), 0), "gt 5", "lt 5"), "eq 5")
If that is too verbose for you, you can always write another VBA UDF that would bring the MATCH, CHOOSE, etc. inside, and call it like this:
=cases(arr(A1>5, A1<5, A1=5), "gt 5", "lt 5", "eq 5")
That's not far off from your proposed syntax, and much, much simpler.
EDIT:
I see you've already come up with a (good) solution that is closer to what you really want, but I thought I'd add this anyway, since my statement above about bringing MATCH, CHOOSE, etc. inside the UDF made it look easier thatn it really is.
So, here is a 'cases' UDF:
Public Function cases(caseCondResults, ParamArray caseValues())
On Error GoTo EH
Dim resOfMatch
resOfMatch = Application.Match(True, caseCondResults, 0)
If IsError(resOfMatch) Then
cases = resOfMatch
Else
Call assign(cases, caseValues(LBound(caseValues) + resOfMatch - 1))
End If
Exit Function
EH:
cases = CVErr(xlValue)
End Function
It uses a little helper routine, 'assign':
Public Sub assign(ByRef lhs, rhs)
If IsObject(rhs) Then
Set lhs = rhs
Else
lhs = rhs
End If
End Sub
The 'assign' routine just makes it easier to deal with the fact that users can call UDFs with either values or range references. Since we want our 'cases' UDF to work like Excel's 'CHOOSE', we'd like to return back references when necessary.
Basically, within the new 'cases' UDF, we do the "choose" part ourselves by indexing into the param array of case values. I slapped an error handler on there so basic stuff like a mismatch between case condition results and case values will result in a return value of #VALUE!. You would probably add more checks in a real function, like making sure the condition results were booleans, etc.
I'm glad you reached an even better solution for yourself, though! This has been interesting.
MORE ABOUT 'assign':
In response to your comment, here is more about why that is part of my answer. VBA uses a different syntax for assigning an object to a variable than it does for assigning a plain value. Look at the VBA help or see this stackoverflow question and others like it: What does the keyword Set actually do in VBA?
This matters because, when you call a VBA function from an Excel formula, the parameters can be objects of type Range, in addition to numbers, strings, booleans, errors, and arrays. (See Can an Excel VBA UDF called from the worksheet ever be passed an instance of any Excel VBA object model class other than 'Range'?)
Range references are what you describe using Excel syntax like A1:Q42. When you pass one to an Excel UDF as a parameter, it shows up as a Range object. If you want to return a Range object from the UDF, you have to do it explicitly with the VBA 'Set' keyword. If you don't use 'Set', Excel will instead take the value contained within the Range and return that. Most of the time this doesn't matter, but sometimes you want the actual range, like when you've got a named formula that must evaluate to a range because it's used as the source for a validation list.

Resources