I just noticed that AHK has a built-in "OnClipboardChange" label so there's no need link ^c or ^x type keys to it. I updated the script as follows:
1. I'm using #Right for paste from clipring, and #Up/#Down change clip, #Del to clear it. #Left might have been the more consistent choice, but I thought I might hit it accidentally when pressing the other keys. I also change the cliparray to 15 clips (from 30).
2. I added the OnClipboardChange label so it when new text enters clipboard it's added to the clipring with clipHandle("Save"). However, if you use this script with different hotkeys, you need to add them to the OnClipboardChange label's 'ignore list', as well as other hotkeys that change clipboard content.
3. I included the OnExit label to save clip content before exit, but most people probably don't want those so just remove it, and the ClipSave subroutine, and also the "else if (action = "write")" if you don't need it.
4. I added an "Else If (A_EventInfo = 2)" to the OnClipboardChange label, though it's currently disable. Type 2 events are things like images (non-text), so if you want those you can download the "convert" function (link in script) and add it to your library. It's only meant for images, and I think type 2 clipboard events other things too (not sure), so it might be better to add further restriction to it, for example, only execute convert if printscreen was pressed. Also, I have it set to save type 2 content to 'My Documents' with [time stamp].png title.
Code:
#NoEnv
#SingleInstance, Force
SetBatchLines, -1
SendMode, Input
OnExit, ClipSave
handleClip("clear")
Return
#Right::handleClip("get") ;paste
#Up::handleClip("rollforward")
#Down::handleClip("roll")
#Del::handleClip("clear")
OnClipboardChange:
If A_ThisHotkey In #Right,#Up,#Down ;add your other hotkeys that use clipboard
Return
If (A_EventInfo = 1)
handleClip("save")
;Type 2 events include images, you can get them with the convert function from
;Sean's ScreenCapture.ahk: http://www.autohotkey.com/forum/viewtopic.php?t=18146
;Else If (A_EventInfo = 2) ;add further restriction (e.g. only if PrtSc was pressed)
; Convert(0, A_MyDocuments . "\" . A_Now . ".png") ;customize save location and pic name
Return
handleClip(action)
{
static
if (action = "save")
{
AddNextNum:= AddNextNum < 15 ? AddNextNum+1 : 1
HighestNum+= HighestNum < 15 ? 1 : 0
GetNextNum:= AddNextNum, ClipArray%AddNextNum% := Clipboard
}
else if ((action = "get") || (action = "roll")) && (GetNextNum != 0)
{
if (action = "roll")
Send ^z
Clipboard := ClipArray%GetNextNum%
GetNextNum:= GetNextNum > 1 ? GetNextNum-1 : HighestNum
Send ^v
}
else if ((action = "rollforward") && (GetNextNum != 0))
{
Send ^z
GetNextNum:= GetNextNum < HighestNum ? GetNextNum+1 : 1
Clipboard:= ClipArray%GetNextNum%
Send ^v
}
else if (action = "clear")
GetNextNum:=0, AddNextNum=HighestNum=0
else if (action = "write")
loop, 15
{
If A_Index = 1
FileAppend, `n%A_Now%`n, %A_ScriptDir%\ClipRing.txt
If content:= ClipArray%A_Index%
FileAppend, %content%`n, %A_ScriptDir%\ClipRing.txt
}
}
ClipSave:
handleClip("write")
ExitApp
Problems:
The rolling uses ^z to undo the previous action, then pastes a new clip. The problem is that ^z often undoes more than just the last thing you pasted. At least in Notepad it takes back everything you did in the last 30 sec or so...
I considered alternative for selecting only the last item to have been pasted, but thought the simplicity of ^z outweighed its shortcomings.
I tried cutting out use of clipboard completely, and just send the array content directly to the application with
Code:
SendRaw % ClipArray%GetNextNum%
but the problem is it sends array contents one as a string of characters, which caused SciTE to inject auto-formating (indents, etc) into the process.
I also thought about sending the windows X=(StringLength of last paste) steps left with shiftdown, but it's not exact because of special characters (`n`r etc), so you'd need to return a few steps, but that was getting too complicated when I liked this script in the first place due to its simplicity.
fwiw, here's the type of loop i tried, but it's very rough:
Code:
else if ((action = "rollforward") && (GetNextNum != 0))
{
temp1:= Clipboard, steps:= StrLen(temp1)
Send {ShiftDown}{Left %steps%}{ShiftUp}
loop, % steps
{
Clipboard =
Send ^c
ClipWait, 1
If (temp1 = Clipboard)
Break
Else Send {ShiftDown}{Right}{ShiftUp}
}
Send {Del}
GetNextNum:= GetNextNum < HighestNum ? GetNextNum+1 : 1
Clipboard:= ClipArray%GetNextNum%
Send ^v
}
if anyone knows of an elegant way to walk back one paste only, please post it.