Activate hotstring when text is pasted from clipboard - clipboard

I have an Autohotkey Hotstring which displays a notification any time a job code is typed with capital letters.
:*B0C:ASSOC::
:*B0C:COORD::
:*B0C:PRACPHYS::
MsgBox Reminder - Set indirect pay to 100
return
While this works fine when manually typing out a job code, I also want these notifications to display when a job code is copy-pasted from the clipboard.
; non-functional pseudo-code
^v:: ; paste
if (pasted text == ASSOC or COORD or PRACPHYS)
MsgBox Reminder - Set indirect pay to 100
return
How can I make my script run whenever a matching string is pasted from the clipboard?

This checks the contents of the clipboard when pasting without interfering with the paste operation
~^v::
if ( clipboard == "ASSOC" || clipboard == "COORD" || clipboard == "PRACPHYS" )
MsgBox Reminder - Set indirect pay to 100
return
Notes
~ key's native function will not be blocked
clipboard contents of the clipboard in text-only format
== case sensitive string comparison
Ref
Autohotkey documentation for Hotkeys

Related

How to use AppleScript with Calculator.app to Automatically select the number of Decimals

Every time I enter a number in Calculator.app I need to go the top menu and select:
View >> Decimal Places >> [0 , 15] And choose the number of decimals.
Is there a way to make an AppleScript that will automatically do so, based on the number I entered?
My input ways are:
by pasting the number
by typing the number
For the pasting part, if you PASTE a number ending in 0, Calculator.app won't show the 0, so it will show one decimal less than the reality. I don't know if this could be also overcome.
Example, if you paste: 12.30 it will show 12.3
if you type: 12.30 it will show 12.30
Building on the addendum to my answer to your other question How can I copy result from Calculator.app to the clipboard using AppleScript, here is a way the trap ⌘V in Calculator and set Calculator > View > Decimal Places to the number of decimal places of the number on the clipboard.
What the example AppleScript code does:
When pressing ⌘V in Calculator the contents of the clipboard is set to a variable and if it contains a decimal point it sets the Calculator > View > Decimal Places menu to the number of decimal places in the number, then keystrokes the number into Calculator. Note that keystroking the number would probably not require you to set the number of decimal places, however I'll leave that up to you to decide.
Example AppleScript code:
set theNumberToType to the clipboard as text
if theNumberToType contains "." then
set theNumberOfDecimalPlaces to count characters ((offset of "." in theNumberToType) + 1) thru -1 of theNumberToType
if theNumberOfDecimalPlaces is greater than 15 then set theNumberOfDecimalPlaces to 15
else
set theNumberOfDecimalPlaces to -1
end if
tell application "Calculator" to activate
tell application "System Events"
if theNumberOfDecimalPlaces is greater than -1 then
click (first menu item of menu 1 of menu item "Decimal Places" of menu 1 of menu bar item "View" of menu bar 1 of process "Calculator" whose name is theNumberOfDecimalPlaces)
end if
delay 0.1
keystroke theNumberToType
end tell
The example AppleScript code above is saved as CalculatorSetDecimalPlaces.applescript in: ~/.hammerspoon/Scripts/, like in my other answer to your other question.
Example Lua code:
-- Create a hotkey used to trap the command v shortcut and disable it.
-- It will then be enabled/disabled as Calculator is focused/unfocused.
-- When enabled and command v is pressed it runs the AppleScript script.
local applicationCalculatorCommandVHotkey = hs.hotkey.bind({"cmd"}, "V", function()
local asFile = "/.hammerspoon/Scripts/CalculatorSetDecimalPlaces.applescript"
local ok, status = hs.osascript.applescriptFromFile(os.getenv("HOME") .. asFile)
if not ok then
msg = "An error occurred running the CalculatorResultToClipboard script."
hs.notify.new({title="Hammerspoon", informativeText=msg}):send()
end
end)
applicationCalculatorCommandVHotkey:disable()
-- Add the following two line respectively before or after the lines
-- applicationCalculatorEnterHotkey:enable()
-- applicationCalculatorEnterHotkey:disable()
-- in either of the two methods presented in my other answer.
applicationCalculatorCommandVHotkey:enable()
applicationCalculatorCommandVHotkey:disable()
Notes:
With the example Lua code, as coded, the behavior of the ⌘V keyboard shortcut is only trapped and modified to trigger the example AppleScript code while Calculator has focus. The ⌘V keyboard shortcut should work normally in all other applications.
The example Lua code above is added to the ~/.hammerspoon/init.lua file from my other answer to your other question.
The example Lua code and API's of Hammerspoon and AppleScript code, shown above, were tested respectively with Hammerspoon and Script Editor under macOS Mojave and macOS Catalina with Language & Region settings in System Preferences set to English (US) — Primary and worked for me without issue1.
1 Assumes necessary and appropriate settings in System Preferences > Security & Privacy > Privacy have been set/addressed as needed.
Update using only Lua without AppleScript
The following example Lua code eliminates the use of AppleScript from both this answer and the code in the other answer to your other related question.
Overwriting the existing code, copy and paste this into your ~/.hammerspoon/init.lua file, save it and run Reload Config from the Hammerspoon menu on the menu bar.
I have performed all the various actions and calculations discussed in the comments and had no issues occur with this new code. Hopefully this will eliminate any issues you were having.
This should also run faster now that it does have to process AppleScript code as well.
Note that as coded it is not automatically reloading the configuration file as that just should not be necessary.
-- Create a hotkey used to trap the enter key and disable it.
-- It will then be enabled/disabled as Calculator is focused/unfocused
-- When enabled and the enter key is pressed it presses = then command C.
local applicationCalculatorEnterHotkey = hs.hotkey.bind({}, "return", function()
-- Press the '=' key to finish the calculation.
hs.eventtap.keyStroke({}, "=")
-- Copy the result to the clipboard.
hs.eventtap.keyStroke({"cmd"}, "C")
end)
applicationCalculatorEnterHotkey:disable()
-- Create a hotkey used to trap the command v shortcut and disable it.
-- It will then be enabled/disabled as Calculator is focused/unfocused.
-- When enabled and command v is pressed it runs the Lua code within.
local applicationCalculatorCommandVHotkey = hs.hotkey.bind({"cmd"}, "V", function()
-- Get the number copied to the clipboard.
local theNumberToType = hs.pasteboard.readString()
-- See if there is a decimal in the number
-- and if so how many decimal places it has.
local l = string.len(theNumberToType)
local s, e = string.find(theNumberToType, "%.")
if s == nil then
theNumberOfDecimalPlaces = -1
elseif l - s > 15 then
theNumberOfDecimalPlaces = 15
else
theNumberOfDecimalPlaces = l - s
end
-- Set the number of decimal places to show.
if theNumberOfDecimalPlaces > -1 then
local calculator = hs.appfinder.appFromName("Calculator")
local vdpn = {"View", "Decimal Places", theNumberOfDecimalPlaces}
calculator:selectMenuItem(vdpn)
end
-- Type the number into Calculator.
hs.eventtap.keyStrokes(theNumberToType)
end)
applicationCalculatorCommandVHotkey:disable()
-- One of two methods of watching Calculator.
--
-- The other is below this one and commented out.
-- Initialize a Calculator window filter.
local CalculatorWindowFilter = hs.window.filter.new("Calculator")
-- Subscribe to when the Calculator window is focused/unfocused.
CalculatorWindowFilter:subscribe(hs.window.filter.windowFocused, function()
-- Enable hotkeys when Calculator is focused.
applicationCalculatorCommandVHotkey:enable()
applicationCalculatorEnterHotkey:enable()
end)
CalculatorWindowFilter:subscribe(hs.window.filter.windowUnfocused, function()
-- Disable hotkeys when Calculator is unfocused.
applicationCalculatorCommandVHotkey:disable()
applicationCalculatorEnterHotkey:disable()
end)
-- Alternate method to wait for Calculator and enable/disable the hotkey.
--
-- Uncomment below method and comment the above method to test between them.
-- The opening '--[[' and closing '--]]' and removed from below added to above.
--[[
function applicationCalculatorWatcher(appName, eventType, appObject)
if (eventType == hs.application.watcher.activated) then
if (appName == "Calculator") then
-- Enable hotkeys when Calculator is activated.
applicationCalculatorCommandVHotkey:enable()
applicationCalculatorEnterHotkey:enable()
end
end
if (eventType == hs.application.watcher.deactivated) then
if (appName == "Calculator") then
-- Disable hotkeys when Calculator is deactivated.
applicationCalculatorCommandVHotkey:disable()
applicationCalculatorEnterHotkey:disable()
end
end
end
appCalculatorWatcher = hs.application.watcher.new(applicationCalculatorWatcher)
appCalculatorWatcher:start()
-- appCalculatorwWatcher:stop()
--]]

How to detect focus into Microsoft Outlook Search field with AutoHotKey

Question
How do I program AutoHotKey to detect the Microsoft Outlook Search field at the top of the window, so as to only enable hotkeys when outside of that window? That search field looks like this:
Details
I'm intending to add single-character hotkeys similar to what gmail has: https://support.google.com/mail/answer/6594?hl=en#zippy=%2Cactions , notably the # keybinding for deletion. I'm basing my experimentation with AutoHotKey scripts such as this one:
https://github.com/pimlottc-gov/gmailkeys/blob/master/gmailkeys-2013.ahk#L37
That script limits its hotkeys to the main Outlook window using #IfWinActive:
#IfWinActive, - Outlook ahk_class rctrl_renwnd32, NUIDocumentWindow ;for Outlook 2013, uncomment this line
However, if I do not change the above #IfWinActive statement, and then add the # hotkey via:
+3::Send {Delete} ; Means "#" key: Delete selected message(s)
then when running the script, then clicking in the above Search field, then typing #, then of course it sends the Delete key into that field, instead of just passing the # into the search field.
I've hacked around this by rebinding both Ctrl+e and / (the latter being the Gmail binding for searching/filtering) to a temporary input popup where I type in the search expression, and then let AutoHotKey type it into the field. But this of course is a hack:
; Search for some expression
;
; Hack: We have to popup an input dialog box, prompt for the
; search expression, and then when done, type it into the
; underlying search field. This is needed to avoid having other
; single-key bindings get "eaten" when needing to type into the
; Outlook search field, as as of 2021-05-23 I could not find a way
; to detect that specific input field.
;
^e::
/::
; Save current active (Outlook) window so we can return to after prompting for the search expression:
WinGet, winid ,, A ; <-- need to identify window A = active
; Prompt for the search expression:
InputBox, search_expr, Search Expression, Enter the search expression.
; Return to the Outlook window:
WinActivate ahk_id %winid%
; If the user presses Escape or clicks on the Cancel button, do nothing:
if (!ErrorLevel) {
; but if we are doing the search:
; Get into the search field:
Send ^e
; Select all prior text so we can wipe it out:
Send ^a
; ... by typing in all of the expression:
Send %search_expr%
; then do the search:
Send {Enter}
}
return
No matter where I click around in the main Outlook window, Window Spy (app that comes with AutoHotKey), the class always stays the same.
AutoHotkey version: 1.1.33.08
Note that Shift+3 only happens to produce a # on your keyboard layout. It would be more correct to actually use the # key as the hotkey.
Also, the code you're referencing is very legacy AHK. Quite a few things in there that don't belong to modern AHK.
I'd maybe also recommend just doing this with a context sensitive hotkey.
This way you'll retain the # key's native functionality.
The context sensitive hotkey could be done like this:
SetTitleMatchMode, 2
#If, WinActive("A") == WinExist("- Outlook ahk_exe OUTLOOK.EXE") && !SearchBarFocused()
#::SendInput, {Delete}
#If
SearchBarFocused()
{
ControlGetFocus, ctrl
return InStr("RICHEDIT60W1,RichEdit20WPT1", ctrl)
}
I'm also checking if Outlook is actually the active window first. Might be kind of redundant, but makes it so the remap couldn't be active in some other window that could have a control by that name.
I found the solution after some experimentation (including simplification from per 0x464e's answer), and essentially reversing the logic, somewhat, at https://github.com/pimlottc-gov/gmailkeys/blob/master/gmailkeys-2013.ahk#L76 via this:
; Outlook-specific Hotkeys:
;
; Reference material used in this implementation:
; https://autohotkey.com/board/topic/38389-archiving-email-in-outlook-with-a-single-keystroke/
;
; As best I can tell, the window text 'NUIDocumentWindow' is not present
; on any other items except the main window. Also, I look for the phrase
; ' - Outlook' in the title, which will not appear in the title (unless
; a user types this string into the subject of a message or task).
#IfWinActive - Outlook ahk_class rctrl_renwnd32, NUIDocumentWindow
#::
if (SentNormalKeyInOutlookEditControl("#"))
{
return
}
Send {Delete} ; Delete selected message(s)
return
#IfWinActive
SentNormalKeyInOutlookEditControl(normal_key)
{
; Find out which control in Outlook has focus
ControlGetFocus, currentCtrl
; ; set list of controls that should respond to specialKey. Controls are the list of emails and the main (and minor) controls of the reading pane, including controls when viewing certain attachments.
; ; The control 'RichEdit20WPT1' (email subject line) is used extensively for inline editing. Thus it had to be included in bad_ctrl_list.
; ctrlList = Acrobat Preview Window1,AfxWndW5,AfxWndW6,EXCEL71,MsoCommandBar1,OlkPicturePreviewer1,paneClassDC1, RichEdit20WPT2,RichEdit20WPT4,RichEdit20WPT5,RICHEDIT50W1,SUPERGRID1,_WwG1
bad_ctrl_list = RICHEDIT60W1,RichEdit20WPT1
; RICHEDIT60W1 is the search field at the top of the window
if currentCtrl in %bad_ctrl_list%
{
; MsgBox, BAD Control with focus = %currentCtrl% normal_key = %normal_key%
Send %normal_key%
return 1
}
return 0
}
Edit 2021-05-25 06:58:23: Replace legacy "+3" with "#" per 0x464e's answer.
Edit 2021-05-25 08:20:09: Prevent " - Message" windows from receiving the hotkeys (separate message windows, not main window)

2 clipboards ahk script

I'm trying to have 2 clipboards (or more), with an ahk script.
Desired behaviour is working with the standard ^v plus !v so it will be 2 clipboards. ^v being the usual one, !v will be the previous content that ^v had.
If I copy 'text1' on clipboard, I will have this as ^v. If I copy 'text2' I will have 'text2' on clipboard with ^v, plus the old 'text1' available with !v, and so on.
This is so far:
lastclip = %clipboard%
!v::
global lastclip
sendclip(lastclip)
return
ClipWaitChange()
ClipWaitChange()
{
While changed=0 {
global lastclip
lastclip = %clipboard%
Sleep 10
}
OnClipboardChange:
changed = 1
return
}
But it returns a blank clipboard or maybe ^v stops working.
Any hints how to do that?
sendclip is just a custom func to send the clipboard contents by keyboard
I tried put lastclip = %clipboard% inside onclipboardchange but it copies the current changed clipboard, so has to be done before that.
I assume your question is of an academic nature. If not, I recommend one of the many Clipboard Managers already out there.
Here's a solution with comments:
lastClip := ""
bufferedClip := Clipboard
OnClipboardChange("clipChangeHandler")
; Note that the ALT key can change focus, better use WIN+V
#v::
; Since we temporarily switch Clipboard's contents,
; we want to ignore the event it will fire
OnClipboardChange("clipChangeHandler", 0)
; I recommend pasting rather than sending
tmpClip := Clipboard
Clipboard := lastClip
Send, ^v
Clipboard := tmpClip
tmpClip := ""
OnClipboardChange("clipChangeHandler")
return
clipChangeHandler(evtInfo) {
global lastClip, bufferedClip
; Check if Clipboard contains actual text
if(evtInfo = 1) {
lastClip := bufferedClip
bufferedClip := Clipboard
}
}
See also OnClipboardChange in AHK docs.
A few remarks:
The event function checks for the event type being (partially) real text, ignoring pure non-text like images and also empty clipboards
clipBuffer always stores the current clipboard and lastClip contains the previous clipboard. You need the buffer variable, since the event doesn't offer something like a previous clipboard
ALT + v can be problematic when working with windows that have a classic menu bar. Hitting ALT (even before releasing it) will often switch focus away from the current control.
I recommend pasting rather than sending. E.g. when there's large text in the clipboard with thousands of words (maybe even put there by another program or simply by mistake), sending each character one by one can take a long time or may even come with other side effects. If you want to Send regardless, at least use SendRaw so that certain characters won't be mistreated as control sequences (e.g. sending #e will open Windows Explorer).
Always consider security aspects of your application. If you (our your password safe e.g.) empty your clipboard to delete sensitive data, your script will still keep an (unencrypted) copy of it, for every other process to see.

Why is the system beeping when I use AppleScript to set text in BBEdit from TextExpander?

I've written an AppleScript that is designed to fire when TextExpander notices a keystroke. It works fine when run from the AppleScript Editor but under certain conditions it also beeps when run from TextExpander.
Here's the script:
tell front window of application "BBEdit"
if (length of selection) is not 0 then
add prefix and suffix of selection prefix "[" suffix "]"
else
set text of selection to "["
end if
end tell
It's set to fire when I type the [ character, with the idea that if text is selected it will wrap the text in [ and ] but if no text is selected then it should simply type the [ character as normal.
It works perfectly however it's run, but if run from TextExpander and the "else" path is follow (set text of selection to "[") the system beeps. I'm not sure if BBEdit or TextExpander is generating the beep, but there's no beep if I completely remove the "else" section or if it runs with text selected (the "if" path).
The folks at Smile Software (makers of TextExpander) found a perfectly workable solution for me.
Instead of
set text of selection to "["
(which had to be followed by select insertion point after selection in order to deselect the [ anyway), this works perfectly:
return "["
which is a better idea anyway.

Using AppActivate and Sendkeys in VBA shell command

I want to switch back and forth between application with the shell command in VBA.
I am using SendKeys to do stuff in process A then move to process B, then to process A then process B. It works fine for the first iteration. When I use AppActivate to go back to process B, it actually does switch focus back to process B. HOWEVER, it ignores subsequent commands from SendKeys.
Example code:
Sub pastePDF2TXT_v3(pdfName As String, txtName As String)
Dim acrobatID
Dim acrobatInvokeCmd As String
Dim acrobatLocation As String
Dim notepadID
Dim acrobatID2
Dim notepadID2
Debug.Print "here"
acrobatLocation = "C:\Program Files\Adobe\Acrobat 9.0\Acrobat\Acrobat.exe"
acrobatInvokeCmd = acrobatLocation & " " & pdfName
acrobatID = Shell(acrobatInvokeCmd, 1)
AppActivate acrobatID
SendKeys "^a", True '^A selects everything already in the pdf file.
SendKeys "^c", True '^C copies the selection to the clipboard.
notepadID = Shell("NOTEPAD.EXE " & txtName, 1) ' invoke notepad on the text file.
AppActivate notepadID ' set the new app as teh active task.
SendKeys "^a", True '^A selects everything already in the text file.
SendKeys "^v", True '^V pastes the new stuff over the top of the old text file (deleting the old stuff)
SendKeys "%{END}", True ' makre sure last line of text file
SendKeys "{ENTER}", True
AppActivate acrobatID ' NOTE: APPEARS TO WORK UP TO THIS POINT.
SendKeys "{ENTER}", True ' NOTE: SECOND APP IGNORES SUBSEQUENT COMMANDS FROM HERE DOWN.
SendKeys "^a", True '^A selects everything already in the pdf file.
SendKeys "^c", True '^C copies the selection to the clipboard.
SendKeys "%f", True 'alt f, x to exit Notepad.exe
SendKeys "x", True
'acrobatID.Quit
Debug.Print "start second"
AppActivate notepadID ' set the new app as teh active task.
SendKeys "%{END}", True 'Go to end of text file.
SendKeys "^v", True '^V pastes the new stuff at end of file.
SendKeys "{ENTER}", True
SendKeys "^s", True
SendKeys "%f", True 'alt f, x to exit Notepad.exe
SendKeys "x", True
notepadID.Quit
acrobatID.Quit
End Sub
I know this question is old, but I have run into this same problem, but I don't think the answer that was chosen is the correct one.
When you call AppActivate, the script goes on halt, waiting for the target application to terminate. After the application is terminated, the script continues. I see no solution to this issue.
EDIT:
I use a batch-file to call CSCRIPT for the AppActivate command, and I notice you are strictly using a VBS file. Try calling CMD as a new window and pass CSCRIPT //NoLogo //B WScript.CreateObject("WSCript.shell").AppActivate("Window Name") as the argument, and see if that bypasses the issue.
Similarly, I was trying to use AppActivate on a pdf document I had opened with a shell command so that I could use SendKeys on it. It would always generate Run Time Error '5'. My solution, eventually was to NOT use AppActivate at all, in fact opening the document brings it to the forefront anyway. The problem is that the SendKeys statement executes immediately after the shell command but the pdf needs a second or two to open up. My solution is to pause the code for a couple seconds before excuting the sendkeys statement.
I used a time delay curtosy of pootle flump. Check out the thread here:
http://www.dbforums.com/microsoft-access/1219379-time-delay-vba.html
After hours of research on various forums, I could figure out that I wont be able to use Adobe library objects and functions with Adobe Reader. The only viable option left was to use Shell Commands to use "save as text" option available in the Adobe Reader's file menu. The shortcut key is Alt+f+a+x+s. I implemented these in the below code which worked perfectly, although I was required to insert delays in some steps.
Sub SavePDFasText()
Dim AdobeReaderPath As String
Dim PdfFilePath As String
Dim PDFid, NotepdId As Double
AdobeReaderPath = "C:\Program Files\Adobe\Reader 10.0\Reader\AcroRd32.exe"
PdfFilePath = "D:\PowerGenMacro\opm_11.pdf"
PDFid = Shell(AdobeReaderPath & " " & PdfFilePath, vbNormalFocus)
Application.Wait TimeSerial(Hour(Now()), Minute(Now()), Second(Now()) + 5)
'Alt+f+a+x is required to save the pdf as text in the adobe reader
SendKeys "%(FAX)", True
Application.Wait TimeSerial(Hour(Now()), Minute(Now()), Second(Now()) + 2)
SendKeys "%S", True
SendKeys "^q", True
End Sub
This might be more of a comment than an answer, but I don't seem to be allowed to "comment", so...
It's amazing you've gotten as far as you did. Chances are, you're never going to be able to make this work reliably -- period. As soon as you do make it work, something will change about how the Acrobat UI behaves in a future release, or Windows will make another change to the rules for what things can send events to what other things, and then you're hosed again.
What you are probably running into in this case is Windows trying to prevent an application stealing the focus from another when a user is apparently busy interacting with the first one. You see the same kinds of problems trying to do things like have a button in one application write data to a temporary file and open MS Word to edit it. Windows prevents the focus from transferring away from the current app and over to MS Word because you just clicked a button in the current app.
So -- instead of trying to solve this impossible technical issue, let's take a step back and ask what you were originally hoping to accomplish by doing all of this. In that way, perhaps, we can get you where you're trying to go :)
Try
sendkeys "%{tab}"
to switch windows
but then keep only 2 active windows.
or try
appactivate only after sendkeys {Tab} so that text entry box is not selected before switchibg windows cmd.
or try
try appactivate window name, 1
then sleep 2000 °to allow time to bring that window into focus
You forgot to add "DoEvents" between each sendkeys and kind of delay/wait period, to give it some time to execute.

Resources