ClipboardMonitor

Summary

HZe 2009-10-17: If you use the clipboard to transfer some of information, sometimes it happens that you copy something and then in the process of coming to the program you want to paste it in, you copy something else and bumm, your first copy is lost.

This tool tries to prevent this, by keeping a history of the clipboard selections.

Additionally, it allows to execute a diff; this make sense if the last two entries of the history are file names.

Code


    ######################################################
    # ClipboardMonitor
    #   by Holger Zeinert
    #
    # This small tool monitors the clipboard and adds every
    # new content to a list. From this list, automatically
    # the last two entries are selected.
    #
    # LIMITATION:
    #   Currently, on UNIX the clipboard CLIPBOARD is
    #   used, not PRIMARY.
    #   Also, it's not tested very much on UNIX.
    #   I use it mainly on Windows.
    #

    set progVersion "Clipboard Monitor v0.24"

    set usage "        $progVersion"
    append usage {
            by Holger Zeinert

        This tool monitors the clipboard and keeps a list
        of snapshots from the content. This keeps track
        of things that were in the clipboard and items
        can be reused later.

        If you select one entry in the list, this entry can be
        exported to the clipboard by pressing the button "export"

        "Clear" resets the list. Please note that this will
        at least enter the current content of the clipboard
        again in the list.

        The content of the list is also stored in a private
        configuration file, so that the current content is
        preserved even if the tool is closed and opened
        again.
        Also be careful when copying passwords over the
        clipboard when this tool is running. Those will
        be visible in plain text! And they are saved
        uncrypted in the history file of the tool.

        If you want to do a diff, use the text editor of your
        choice and go to the first file, copy the file name
        to the clipboard, go to the second file, copy the
        file name to the clipboard. Then, in this tool the
        last two (selected) entries show the two filenames
        Press "diff" to invoke tkdiff.
        You also may select two files from the list to do
        the diff on.
    }

    ######################################################
    # Utility procedures
    #
    proc scan_clp {{update_list 0}} {
        global CONFIG
        if {![catch {set content [clipboard get]}]} {
            if {$content != [lindex $CONFIG(clpList) end]} {
                lappend CONFIG(clpList) $content
                set update_list 1
            }
        }
        if {$update_list} {
            update_listbox
            wm title . "scan_clp: new data"
            after 500
        }
        after 500 scan_clp
        wm title . $::progVersion
    }

    proc exit_handler {} {
        # save current content to private configuration
        write_rc
    }

    proc getrcname {} {
        global env argv0 tcl_platform
        if {[string match "win*" $tcl_platform(platform)]} {
            set filename [file join $env(USERPROFILE) "[file rootname [file tail $argv0]].rc"]
        } else {
            set filename [file join ~ "[file rootname ".[file tail $argv0]"].rc"]
        }
        return $filename
    }

    proc read_rc {} {
        global CONFIG

        catch {
            set fp [open [getrcname] r]
            array set CONFIG [read $fp]
            close $fp
        }
        # set defaults / !! Todo:  needs edit !!
        if {![info exists CONFIG(diff)]} {
            if {[file exists c:/zeinert/tools/tkdiff.bat]} {
                set CONFIG(diff) c:/zeinert/tools/tkdiff.bat
            } else {
              # set CONFIG(diff) tkdiff
                set CONFIG(diff) examdiff
            }
        }
       #set CONFIG(diff) examdiff
        set CONFIG(diff) C:/Programme/Util/ExamDiff/ExamDiff.exe
    }

    proc write_rc {} {
        global CONFIG

        set fp [open [getrcname] w]
        # try to write arrag get a little nicer, so that a human could edit it
        foreach {var value} [array get CONFIG] {
            puts $fp "[list $var]\n[list $value]\n"
        }
        close $fp
    }

    #####################################################
    # GUI handlers
    #
    proc update_listbox {} {
        global lb
        global CONFIG

        set l [llength $CONFIG(clpList)]
        $lb selection clear 0 $l
       #$lb selection set [expr $l-2] end;        # select last 2 entries
        $lb selection set end;                    # select only last entry
        $lb yview end
        return
    }

    proc export_clp {} {
        global lb
        global CONFIG
        set s [$lb curselection]
        if {[llength $s] != 1} {
            tk_messageBox -message "Please select exactly one entry"
        } else {
            clipboard clear
            clipboard append [lindex $CONFIG(clpList) [lindex $s 0]]
        }
        return
    }

    proc process_clp {} {
        global lb
        global CONFIG
        set s [$lb curselection]
        if {[llength $s] != 1} {
            tk_messageBox -message "Please select exactly one entry"
        } else {
            set txt1 [lindex $CONFIG(clpList) [lindex $s 0]]
            set txt2 [string tolower  $txt1]

            clipboard clear
            clipboard append $txt2
        }
        return
    }

 ### ### ### ###

if 0 {
# Patterns to look for:
# Input1 - Forum
 Post subject: Re: Problem.
New postPosted: 2016-01-13 14:15
Reply with quote
...

# Input2 - IP/RIPE:
 RIPE NCC Network Coordination
    RIPE Database (Whois)
    Website
Your IP address is: 89.246.201.51 
...

# Input3 - CDex :
CDex version  :1.79
Disk ID       :A90C9D0C
Volume ID     :1326472
OS Version    :Windows 
...
}
 ### ### ### ###

    proc filter_clp {} {
        global lb
        global CONFIG
        set s [$lb curselection]
        if {[llength $s] != 1} {
            tk_messageBox -message "Please select exactly one entry"
        } else {
            clipboard clear
            set txt [lindex $CONFIG(clpList) [lindex $s 0]]
    # IP-Filter:
            set p1  [string first "Your IP addr" $txt];  # RIPE.net
            if {$p1 >= 0} {
              set p1  [string first ": "        $txt $p1]
              set p2  [string first "\n"        $txt $p1]
             #if {$p2 < 0} { set p2 $p1; incr p2 16 }
              if {$p2 < 0} { set p2 $p1; incr p2 2; set p2 [string first " " $txt $p2] }
              if {$p2 < 0} { set p2  [string length $txt] }
           ## wm title . "IP: $p1 $p2"; after 1111;  ## debug
              incr p1
              incr p2 -1
              set ip [string range $txt $p1 $p2]
              set cs [clock seconds]
             #set td [clock format $cs  -format "%Y-%m-%d  %H:%M:%S" ]
              set td [clock format $cs  -format "%Y-%m-%d %H:%M" ]
             #clipboard append "IP=$ip"
              clipboard append "$td   $ip "
                return
            }
    # CDex-Filter:
            set p1  [string first "Disk ID"   $txt];     # freedb-Disk-ID
            if {$p1 >= 0} {
              set p1  [string first ":"         $txt $p1]
              set p2  [string first "\n"        $txt $p1]
           ## wm title . "CDex $p1 $p2"; after 1111;  ## debug
              incr p1
              incr p2 -1
              set id1 [string range $txt $p1 $p2]

              set p1  [string first "Volume ID" $txt];     # CD-Volume-ID
              set p1  [string first ":"         $txt $p1]
              set p2  [string first "\n"        $txt $p1]
              incr p1
              incr p2 -1
              set id2 [string range $txt $p1 $p2]

              clipboard append "DiskID=$id1 V-Id=$id2"
                return
            }
    # Forum-Filter:
            set p1  [string first "Post subject:" $txt];   # Forum
            if {$p1 >= 0} {
              set p1  [string first ":"         $txt $p1]
              set p2  [string first "\n"        $txt $p1]
           ## wm title . "Forum $p1 $p2"; after 1111;  ## debug
              incr p1
              incr p2 -1
              set txS [string range $txt $p1 $p2]

              set p1  [string first "Posted:"   $txt];
              set p1  [string first ":"         $txt $p1]
              set p2  [string first "\n"        $txt $p1]
              incr p1
              incr p2 -1
              set txD [string range $txt $p1 $p2]

              clipboard append "Date=$txD  Subject=$txS"
              return
            }
    # ...add other filters here...

            wm title . "filter: no match"
            after 500
        }
        return
    }

    proc diff {} {
        global lb
        global CONFIG

        set s [$lb curselection]
        if {[llength $s] < 2} {
            tk_messageBox -message "please select two files"
        } else {
            set s1 [lindex $s end]
            set s2 [lindex $s end-1]
            set f1 [lindex $CONFIG(clpList) $s1]
            set f2 [lindex $CONFIG(clpList) $s2]
         # Todo: process exitcodes>0 that don't indicate errors
            if {[catch {
                exec $CONFIG(diff) $f1 $f2
            } msg]} {
                tk_messageBox -title "Error" -icon info \
                          -message "could not execute diff tool '$CONFIG(diff)'\n \
                    $msg\n\
                    Please check installation or change [getrcname]\n\
                    to specify the diff tool of your choice."
            }
        }
        return
    }

    proc finish {} {
        exit_handler
        exit
    }

    proc build_gui {} {
        global lb

        frame .l
        set lb .l.lb
        listbox $lb -listvar CONFIG(clpList) -selectmode extended -height 7 -width 50 -yscrollcommand ".l.sb set"
        scrollbar .l.sb  -command "$lb yview"
        pack $lb   -expand 1 -fill both -side left
        pack .l.sb -fill y -side left
        pack .l    -expand 1 -fill both -padx 5 -pady 5

        frame .b
        button .b.clear   -text Clear   -command {set CONFIG(clpList) {}}
        button .b.export  -text Export  -command {export_clp}
        button .b.process -text Process -command {process_clp}
        button .b.filter  -text Filter  -command {filter_clp}
        button .b.diff    -text Diff    -command  diff
        button .b.exit    -text Exit    -command  finish
        button .b.help    -text Help    -command {
            toplevel .help
            label  .help.l -text $usage -justify left -width 60
            pack   .help.l -expand 1
            button .help.b -command {destroy .help} -text "    OK    "
            pack   .help.b -expand 1 -padx 3 -pady 3
        }

        pack .b.exit .b.help .b.diff .b.filter .b.process .b.export .b.clear \
             -side right -padx 3 -pady 3 -ipadx 10
           # -side right -padx 5 -pady 5 -ipadx 15
        pack .b -fill x

        wm protocol . WM_DELETE_WINDOW finish
        wm title . $::progVersion

        # set minsize to the size of the window after 1sec.
        # this freezes the automatically set windows size as minimum.
        after 1000 {catch {wm minsize . [lindex [split [wm geometry .] x+] 0] [lindex [split [wm geometry .] x+] 1]}}
    }

    #########################################################
    # main
    #
    build_gui
    read_rc
    scan_clp 1

Comments

HJG 2016-01-25 - I added a new command "filter". It searches thru the text of the selected clip for certain keywords, and returns some extracted text-snippets to the clipboard.

Currently, the proc filter_clp is looking for 3 different patterns, let's call them Forum, IP, and CDex.

When a block of text from a forum-posting gets copied to the clipboard, that might look like

Post subject: Re: Problem.
New postPosted: 2016-01-13 14:15
Reply with quote
...

Now the filter scans that text and exports back to the clipboard:

 Date= 2016-01-13 14:15  Subject= Re: Problem.

wm title is used for brief status-messages.

I also tried to make the diff-portion of ClipboardMonitor work with ExamDiff .
(There is also a free version available on that website)
So far, this was only partly successful: that diff-program runs, but returns with an errorcode.
I guess Examdiff uses exitcode 0=no diffs found, 1=some diffs found, 2=error, or something like that.

HJG 2016-02-04: Added another new command, "Process".
For now, it just converts the text of the selected entry to lowercase before exporting it to the clipboard, but I have plans to expand this functionality.

See also: