Distinguish source of the clipboard change - autohotkey

I wanted to log clipboard content whenever it changes, so I created this code:
#Persistent
FileEncoding, UTF-8
OnClipboardChange("ClipChanged")
return
ClipChanged(Type) {
now := A_Now
FormatTime, time, now, dd.MM.yyyy HH:mm:ss
FileAppend, % Format("{} '{}'`n", time, Clipboard), D:\clipboard_log.txt
}
Now, I would like to ignore scenarios when clipboard has been changed by me. I want to log clipboard changes made only by external applications. I came up with an idea:
#Persistent
FileEncoding, UTF-8
OnClipboardChange("ClipChanged")
global clipChangedByMe := false
return
ClipChanged(Type) {
if (clipChangedByMe)
{
clipChangedByMe := false
}
else
{
now := A_Now
FormatTime, time, now, dd.MM.yyyy HH:mm:ss
FileAppend, % Format("{} '{}'`n", time, Clipboard), D:\clipboard_log.txt
}
}
^c::
clipChangedByMe := true
Clipboard := ClipboardAll
return
For some reason, ClipChanged event is not fired after line: Clipboard := ClipboardAll. Whats wrong there?

At the first glance I see a couple of issues here. If you want the original function of Ctrl + C preserved you have to use the tilde prefix: ~^c. Secondly, both Clipboard and ClipboardAll are built in variables and you are assigning one to the other for some reason?
Update: Well, the following seems to work:
Global clipChangedByMe := false
OnClipboardChange("ClipChanged")
~^c::clipChangedByMe := true
ClipChanged() {
If (clipChangedByMe)
clipChangedByMe := false
else {
FormatTime, time,, dd.MM.yyyy HH:mm:ss
FileAppend, % Format("{}`n{}`n`n", time, Clipboard), D:\clipboard.log
}
}

Related

When autohotkey inserts a text string into a file, I need the name and path of that file. Can AHK give it to me?

I use autohotkey to insert a text string into a text file.
How can I store the path and the name of that file in a new variable?
Let's say I use this code for inserting a date/time stamp:
::iii:: ; insert a date time stamp
send, ID%A_YYYY%.%A_MM%.%A_DD%.%A_Hour%.%A_Min%.%A_Sec%
return
How can I modify my code to store the path and name of the file I am tagging?
Something like this? :
::iii:: ; insert a date time stamp
send, ID%A_YYYY%.%A_MM%.%A_DD%.%A_Hour%.%A_Min%.%A_Sec%
path = <code for extracting path>
filename = <code for extracting filename>
return
Try
::iii:: ; insert a date time stamp
SendInput, ID%A_YYYY%.%A_MM%.%A_DD%.%A_Hour%.%A_Min%.%A_Sec%{Enter}
SendInput, % GetFilePath_notepad() "`n"
SendInput, % GetFileName_notepad() "`n"
return
GetFilePath_notepad(){
If !WinActive("ahk_class Notepad")
{
MsgBox, Notepad isn't active
return
}
; https://autohotkey.com/docs/commands/ComObjGet.htm
Path := ""
WinGet pid, PID, A
wmi := ComObjGet("winmgmts:")
queryEnum := wmi.ExecQuery(""
. "Select * from Win32_Process where ProcessId=" . pid)
._NewEnum()
If queryEnum[process]
{
Pos := InStr(process.CommandLine, .exe,, 1)
Path := SubStr(process.CommandLine, Pos+6)
}
else
MsgBox, Process not found!
wmi := queryEnum := process := ""
If (Path != "")
return %Path%
else
MsgBox, Path not found!
}
GetFileName_notepad(){
If !WinActive("ahk_class Notepad")
{
MsgBox, Notepad isn't active
return
}
WinGetTitle, WinTitle, A
If (SubStr(WinTitle, -9) = " - Notepad")
FileName := SubStr(WinTitle, 1, -10)
If (SubStr(WinTitle, -8) = " - Editor")
FileName := SubStr(WinTitle, 1, -9)
If (SubStr(FileName, 1, 1) = "*")
FileName := SubStr(FileName, 2)
return %FileName%
}
EDIT:
Insteaf of
SendInput, % GetFilePath_notepad() "`n"
SendInput, % GetFileName_notepad() "`n"
you can use
FilePath := GetFilePath_notepad()
SendInput, %FilePath%{Enter}
FileName := GetFileName_notepad()
SendInput, %FileName%{Enter}
SendInput is faster and more reliable as Send
You can use the WinGet command to get the full path of the currently active window:
WinGet, path, ProcessPath, A
path is the variable in which you store the result of the command.
ProcessPath is a parameter of the command that tells it which information you want to extract, in this case the process path.
A means that you want to get the information of the currently active window.
To get the title of the currently active window you use the WinGetActiveTitle command:
WinGetActiveTitle, thetitle
StringTrimRight is used to remove the " - Notepad" part from the window title.
You can test your code with this:
::iii:: ; insert a date time stamp
send, ID%A_YYYY%.%A_MM%.%A_DD%.%A_Hour%.%A_Min%.%A_Sec%
WinGet, path, ProcessPath, A
WinGetActiveTitle, thetitle
StringTrimRight, thetitle, thetitle ,10
Msgbox, path=%path% `ntitle=%thetitle%
return

MATLAB auto completing start and end statements of code body?

Is it possible in MATLAB to automatically insert start and end of a code body ?
For example: classdef and end; function and end; methods and end.
classdef Circle
properties
radius
end
methods
function dia = FindDia(obj)
dia = [obj.radius]*2;
end
function %no automatic insertion of 'end'
end
end
Since I work regularly in so many different editors, I don't rely on any one editor's features. It's a pain to learn them all and keep the configuration for all these editors in sync, on all my machines. Often, it's useful to have the same feature set I'm used to, in editors not even meant for code (like MS Word, or here on Stack Overflow).
Therefore, I use AutoHotkey for this kind of thing (and Autokey on Linux).
For functions and classes, I use a paste function to paste a specific template file, depending on whether I'm at work or at home, and which project I'm working on. A small GUI then asks me for the function or class name, which it then populates the template with. I can share that too if you want.
Below are a few AutoHotkey functions and hotstrings I use for including the end keyword. Note that this might all seem overly complex just to put an end there, and in this case, it probably is. But the clipCopy, clipPaste and getIndent functions have proven their usefulness in the rest of my programming snippets, and I hope they might be for you too.
I've thrown in the error functions as well, just because.
; Copy selected text
; More robust than C-c/C-x/C-v; see
; https://autohotkey.com/board/topic/111817-robust-copy-and-paste-routine-function/
clipCopy(dontRestoreClipboard := 0)
{
prevClipboard := Clipboard
Clipboard := ""
copyKey := "vk43sc02E" ; Copy
SendInput, {Shift Down}{Shift Up}{Ctrl Down}{%copyKey% Down}
ClipWait, 0.25, 1
SendInput, {%copyKey% Up}{Ctrl Up}
str := Clipboard
if (dontRestoreClipboard == 0)
Clipboard := prevClipboard
return str
}
clipPaste(ByRef txt, dontBackupClipboard := 0)
{
if (txt != "")
{
prevClipboard := ""
pasteKey := "vk56sc02F" ; Paste
if (dontBackupClipboard == 0) {
prevClipboard := Clipboard
Clipboard := ""
}
Clipboard := txt
ClipWait, 1.00, 1
; Start pressing paste key
SendInput, {Shift Down}{Shift Up}{Ctrl Down}{%pasteKey% Down}
; Wait until clipboard is ready
startTime := A_TickCount
while (DllCall("GetOpenClipboardWindow") && (A_TickCount - startTime < 1000)) {
Sleep, 50
}
; Release paste key
SendInput, {%pasteKey% Up}{Ctrl Up}
; TODO: a manual delay still seems necessary...this vlaue needs to be this large, to also have
; correct behavior in superslow apps like MS Office, Outlook, etc. Sadly, the SetTimer approach
; does not seem to work (doesn't correctly restore the clipboard); to be investigated.
Sleep 333
; Put previous clipboard content back onto the clipboard
Clipboard := prevClipboard
}
}
; Get current indentation level in an editor-independent way
getIndent(dontRestoreClipboard := 0)
{
; Select text from cursor to start of line
SendInput, +{Home}
indent := clipCopy(dontRestoreClipboard)
numsp := 0
if (indent != "")
indent := RegExReplace(indent, ".", " ", numsp)
; Undo selection (this is tricky; different editors often have
; different behavior for Home/End keys while text is selected
SendInput, {Right}{Left}{Home}
; NOTE: don't use {End}, because we might be in the middle of a sentence
; Use the "right" key, repeatedly
Loop, %numsp% {
SendInput, {Right}
}
return indent
}
mBasic(str)
{
current_indent := getIndent()
SendInput, %str% (){Enter}+{Home}%current_indent%{Space 4}{Enter}+{Home}%current_indent%end{Up 2}{End}{Left}
}
mErrfcn(str)
{
current_indent := getIndent()
spaces := RegExReplace(str, ".", " ")
clipPaste(str . "([mfilename ':default_errorID'],...`r`n" . current_indent . spaces . " 'Default error string.');")
return current_indent
}
; MATLAB Hotstrings for basic control structures
:o:mfor::
mBasic("for")
return
:o:mpar::
mBasic("parfor")
return
:o:mwhile::
mBasic("while")
return
:o:spmd::
mBasic("spmd")
return
:o:mif::
mBasic("if")
return
; ...etc.
; error, warning, assert
:o:merr::
:o:merror::
mErrfcn("error")
SendInput, {Up}{End}{Left 21}
return
:o:mwarn::
:o:mwarning::
mErrfcn("warning")
SendInput, {Up}{End}{Left 21}
return
_mlAssert()
{
current_indent := mErrfcn("assert")
SendInput, {Up}{End}{Left 34}true,...{Enter}+{Home}%current_indent%{Space 7}{Up}{End}{Left 8}
}
:o:massert::
_mlAssert()
return

AutoHotkey - Rename image by desired name only by selecting it and pressing shortcut

I have a quite organized workflow and I have an image that always needs to have the same name. It's always a PNG (Portable Network Graphics) and no matter if it's on Desktop or in a folder.
So i just want to select the image and with a one letter shortcut (for example "L") rename it (regardless it's previous name) to "LAYOUT"
F2::
ClipSaved := ClipboardAll ; save the entire clipboard to the variable ClipSaved
clipboard := "" ; empty clipboard
Send, ^c ; copy the selected file
ClipWait, 1 ; wait for the clipboard to contain data
if (!ErrorLevel) ; If NOT ErrorLevel clipwait found data on the clipboard
{
clipboard := clipboard ; convert to text (= copy the path of the selected file)
Sleep, 300
; MsgBox, %clipboard% ; display the path
if (SubStr(clipboard, -2) != "png")
{
MsgBox, No PNG-file selected
clipboard := ClipSaved
return
}
; otherwise:
SplitPath, clipboard, name, dir
FileMove, %clipboard%, %dir%\LAYOUT.png
Sleep, 100
clipboard := ClipSaved ; restore original clipboard
}
else
{
MsgBox, No file selected
clipboard := ClipSaved
}
return

autohotkey looping tooltip and confirm selection with enter

I want to accomplish an apparently easy task with autohotkey: when certain hotstring is detected, then show a tooltip (in this case, with the current date). While the tooltip is displayed I want to react to UP and DOWN key-presses showing the next and previous item on an array respectively. Then when the Enter key is pressed I want to confirm the "selection" and paste that tooltip text. Here is the current code, which looks too big for a task that is so simple.
; ------------------------ Date tooltip
::#curdate::
EnteringDate := True
DateSeparator := [".","/","-"]
SelectedSep := 1
GoSub, ShowToolTip
return
ShowToolTip:
Sep := DateSeparator[SelectedSep]
FormatTime, Time,, dd%Sep%MM%Sep%yyyy ; dd MM yyyy is day month year
ToolTip, %Time%
return
#If EnteringDate
Up::
SelectedSep := cycle(SelectedSep,DateSeparator.MaxIndex(),1)
GoSub, ShowToolTip
return
Down::
SelectedSep := cycle(SelectedSep,DateSeparator.MaxIndex(),-1)
GoSub, ShowToolTip
return
Enter::
EnteringDate := False
SendInput, %Time%
ToolTip ; Clear the tool tip
return
#If ; end entering date
cycle(value,maxValue,increment:=1){
value += increment
if value not between 1 and %maxValue%
value := increment<0 ? maxValue : 1
return value
}
::#curdate::
i:=0,DateSep:= [".","/","-"],EnteringDate:=1
return
#If EnteringDate
Up::
ToolTip
,% DateSep[i:=i<DateSep.MaxIndex()?++i:1]
return
Down::
ToolTip
,% DateSep[i:=i>1?--i:DateSep.MaxIndex()]
return
Enter::
EnteringDate:=0,Sep := DateSep[i]
FormatTime, Time,, dd%Sep%MM%Sep%yyyy
SendInput, %Time%
ToolTip
return
#If
Esc::ExitApp
::#curdate::
i:=0,DateSep:= [".","/","-"],EnteringDate:=1
SendLevel,1
Send,{up}
return
#If EnteringDate
Up::
DateSep[i:=i<DateSep.MaxIndex()?++i:1]
Sep:=DateSep[i]
FormatTime, Time,, dd%Sep%MM%Sep%yyyy
ToolTip,% Time
return
Down::
DateSep[i:=i>1?--i:DateSep.MaxIndex()]
Sep:=DateSep[i]
FormatTime, Time,,dd%Sep%MM%Sep%yyyy
ToolTip,% Time
return
Enter::
EnteringDate:=0
SendInput, % Time
ToolTip
return
#If

Get String Value of passed ByRef Variable

Say I call a function that uses a byref modifier. Once in the function, I want to fetch the string value of the passed variable.
myvar := "george"
passit(myvar)
passit(byref whatvar){
msgbox % %whatvar% ;I should see a messagebox reporting the string "myvar"
}
Getting the string value of the variable works fine if I'm not passing the variable byref.
Maybe I'm going about this the wrong way. I want the function to know the string name for the variable being referenced.
This approch uses the buildin ListLines-Command to access the needed metadata.
The command ListLines opens the main window of the current script and displays the last executed script lines.
Content looks like this:
Script lines most recently executed (oldest first). Press [F5] to refresh. The seconds elapsed between a line and the one after it is in parentheses to the right (if not 0). The bottommost line's elapsed time is the number of seconds since it executed.
---- D:\Eigene_Dateien\ahk scripts\test3.ahk
002: myvar := "george"
003: passit(myvar)
007: MsgBox,GetOriginalVariableNameForSingleArgumentOfCurrentCall(A_ThisFunc)
012: lines := ListLines()
Press [F5] to refresh.
This data can be parsed to extract the wanted information (what is passed to 'passit').
One problem here is, that there is no buildin programmatically way of access this info.
The function ListLines overrides temporary User32.ShowWindow and User32.SetForgroundWindow to return simply true, so the buildin command ListLines can be used without displaying its window (Might produce problems with multithreaded scripts). From this 'hidden' window its text is received and cleaned up. Function is written by Lexikos (http://www.autohotkey.com/board/topic/20925-listvars/#entry156570 http://www.autohotkey.com/board/topic/58110-printing-listlines-into-a-file/#entry365156).
GetOriginalVariableNameForSingleArgumentOfCurrentCall extracts the variable name with a regular expression, which searches the first call to the passed function above the current call (call to GetOriginalVariableNameForSingleArgumentOfCurrentCall).
myvar := "george"
passit(myvar)
return
passit(whatvar){
msgbox % GetOriginalVariableNameForSingleArgumentOfCurrentCall(A_ThisFunc)
}
GetOriginalVariableNameForSingleArgumentOfCurrentCall(callerFuncName)
{
lines := ListLines()
pattern = O)%callerFuncName%\((.*?)\).*?%A_ThisFunc%\(.*?\)
RegExMatch(lines, pattern, match)
return match[1]
}
; Originally written by Lexikos / Copy of ListGlobalVars http://www.autohotkey.com/board/topic/20925-listvars/#entry156570
; with modifications from here http://www.autohotkey.com/board/topic/58110-printing-listlines-into-a-file/#entry365156
; Tested/Modified for AHK Unicode 64bit v1.1.14.03
ListLines()
{
static hwndEdit, pSFW, pSW, bkpSFW, bkpSW
ListLines Off
if !hwndEdit
{
dhw := A_DetectHiddenWindows
DetectHiddenWindows, On
Process, Exist
ControlGet, hwndEdit, Hwnd,, Edit1, ahk_class AutoHotkey ahk_pid %ErrorLevel%
DetectHiddenWindows, %dhw%
astr := A_IsUnicode ? "astr":"str"
ptr := A_PtrSize=8 ? "ptr":"uint"
hmod := DllCall("GetModuleHandle", "str", "user32.dll")
pSFW := DllCall("GetProcAddress", ptr, hmod, astr, "SetForegroundWindow")
pSW := DllCall("GetProcAddress", ptr, hmod, astr, "ShowWindow")
DllCall("VirtualProtect", ptr, pSFW, ptr, 8, "uint", 0x40, "uint*", 0)
DllCall("VirtualProtect", ptr, pSW, ptr, 8, "uint", 0x40, "uint*", 0)
bkpSFW := NumGet(pSFW+0, 0, "int64")
bkpSW := NumGet(pSW+0, 0, "int64")
}
if (A_PtrSize=8) {
NumPut(0x0000C300000001B8, pSFW+0, 0, "int64") ; return TRUE
NumPut(0x0000C300000001B8, pSW+0, 0, "int64") ; return TRUE
} else {
NumPut(0x0004C200000001B8, pSFW+0, 0, "int64") ; return TRUE
NumPut(0x0008C200000001B8, pSW+0, 0, "int64") ; return TRUE
}
ListLines
NumPut(bkpSFW, pSFW+0, 0, "int64")
NumPut(bkpSW, pSW+0, 0, "int64")
; Retrieve ListLines text and strip out some unnecessary stuff:
ControlGetText, ListLinesText,, ahk_id %hwndEdit%
RegExMatch(ListLinesText, ".*`r`n`r`n\K[\s\S]*(?=`r`n`r`n.*$)", ListLinesText)
StringReplace, ListLinesText, ListLinesText, `r`n, `n, All
ListLines On
return ListLinesText ; This line appears in ListLines if we're called more than once.
}
The closest to what you would want...? This reminds of a question I had.
See http://ahkscript.org/boards/viewtopic.php?f=14&t=3651
myvar:="test"
passit("myvar") ; display value, and change it
msgbox % "myvar = " myvar
passit(byref whatvar){ ; no more need for byref here...
msgbox % whatvar " = " (%whatvar%)
%whatvar%:="blah" ; edit globalvar "hack"
}