Skip to content

Instantly share code, notes, and snippets.

@jadient
Last active March 26, 2022 04:59
Show Gist options
  • Save jadient/1b1478f59f6308c2ea451bdb35f964fb to your computer and use it in GitHub Desktop.
Save jadient/1b1478f59f6308c2ea451bdb35f964fb to your computer and use it in GitHub Desktop.
Simple Clipboard History for Windows
;; ClipHist4 - ClipBoard History using AutoHotkey
;;
;; From:
;; https://tinyurl.com/cliphist4
;;
;; Based on:
;; https://www.autohotkey.com/board/topic/87650-clipboard-history-menu-decrementing-numbered-circular-buckets/
;; with customizations and modernization
;;
;; About ClipHist4:
;; - Stores up to 10 items of clipboard history
;; - Popup menu for clipboard history, controllable with keyboard or mouse
;; - Hotkeys for the 3 most recent clipboard items
;; - Displays tooltip on copy to clipboard
;; - Compatible with emacs and putty
;;
;; Limitations:
;; - Text-only
;;
;; Hotkeys:
;; Ctrl-1 to paste the most recent clipboard item
;; Ctrl-2 to paste previous item on clipboad
;; Ctrl-3 to paste item before that
;; Ctrl-4 to show clipboard history as a popup menu
;;
;; Clipboard history management:
;; - if copied text is already in the history, it will be moved to the top of the history
;; rather than duplicating the item
;; - on menu selection via mouse, if the shift key is down
;; the selected item is moved to the top of the list
;;
^1:: PasteClipItem(1) ;; send current (top) clipboard item
^2:: PasteClipItem(2) ;; send previous clipboard item
^3:: PasteClipItem(3) ;; send the one before that
^4:: ShowClipHistMenu() ;; who can remember past 3? show a menu!
ShowClipHistMenu()
{
global ClipList
;; remove previous menu
Menu, ClipboardHistory, Add
Menu, ClipboardHistory, DeleteAll
;; create new menu items
Loop % ClipList.Length()
{
;; clean the text for use in the menu
MenuText := StrReplace(ClipList[A_Index], "`n", "|")
MenuText := RegExReplace(MenuText, "[[:blank:]]+", " ")
MenuText := SubStr(MenuText, 1, 64)
;; 0x00b7 - unicode middle dot: https://unicodeplus.com/U+00B7
MenuLabel := "&" . Chr(64+A_Index) . " " . Chr(0x00b7) . " "
;; label menu items starting with 'A' for keyboard access
Menu, ClipboardHistory, Add, % MenuLabel . MenuText, PasteClipFromMenu
}
;; a hint of blue for menu background: https://tinyurl.com/d0e0f0
Menu, ClipboardHistory, Color, d0e0f0
;; show menu near current cursor position
Menu, ClipboardHistory, Show, % A_CaretX, % A_CaretY + 16
}
HandleClipboardChange(Type)
{
If (Type != 1)
return
global ClipList
;; replace CRLF by LF
Text := StrReplace(Clipboard, "`r`n", "`n")
;; check if text is already in the history list
ClipListIndex := HasVal(ClipList, Text)
if (ClipListIndex > 0)
ClipList.RemoveAt(ClipListIndex)
;; add item to the top of the history list
ClipList.InsertAt(1, Text)
ShowTooltip(ClipList[1])
;; enforce max number of items
ClipList.RemoveAt(ClipList.Max+1)
}
;; from https://tinyurl.com/HasVal
HasVal(haystack, needle)
{
for index, value in haystack
if (value == needle)
return index
return 0
}
ShowTooltip(Text)
{
;; show no more than the first 80 characters
Tooltip % SubStr(Text, 1, 80) . "..."
; display tooltip for 1 second
Sleep 1000
Tooltip
}
PasteClipFromMenu(ItemName, ItemPos, MenuName)
{
global ClipList
PasteText(ClipList[ItemPos])
;; move item top top of list, if shift key is down
if (ItemPos > 1 && GetKeyState("Shift"))
{
ClipList.InsertAt(1, ClipList[ItemPos])
ClipList.RemoveAt(ItemPos+1)
}
}
PasteClipItem(index)
{
global ClipList
PasteText(ClipList[index])
}
PasteText(Text)
{
;; faster method, but it's a hack:
;;
;; (1) save current contents of clipboard
;; (2) put text into the clipboard
;; (3) send hotkey that will paste the text
;; (4) restore the clipboard
;;
;; Our own clipboard handler must be disabled during these operations.
;; If anyone else is listening to the clipboard, this will confuse them.
;;
;; if this doesn't work, replace with this:
;; SetKeyDelay,0
;; SendRaw % Text
;;
If WinActive("ahk_class Emacs") ;; any Emacs users out there?
PasteKey := "^y" ;; send C-y for emacs "yank"
Else
PasteKey := "+{Ins}" ;; same as ^v, but +{Ins} also works with putty
ClipSaved := ClipboardAll ;; save the clipboard
;; disable our clipboard listener
OnClipboardChange("HandleClipboardChange", 0)
Clipboard := Text ;; put text in the clipboard
Sleep 100 ;; delay (to increase reliability - rdpclip)
Send % PasteKey ;; send keystroke to paste
Clipboard := ClipSaved ;; restore the clipboard
Sleep 100 ;; delay (to increase reliability)
;; restore our clipboard listener
OnClipboardChange("HandleClipboardChange", 1)
}
;; the OnClipboardChange label is deprecated, but it's quite useful here
OnClipboardChange:
global ClipList
if (IsObject(ClipList) )
;; our registered clipboard handler will be triggered
return
;; first time here, perform initialization
ClipList := []
;; max number of items in history
ClipList.Max := 10
;; handle the clipboard event, just this one time
HandleClipboardChange(A_EventInfo)
;; next clipboard event will be handled using the newer mechanism
OnClipboardChange("HandleClipboardChange")
return
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment