http://qs321.pair.com?node_id=346620
Category: Win32 Stuff
Author/Contact Info Chady
Description:

I love the idea of Klipper in KDE, and I always lack that functionality in Windows; you copy some text, move around to another application, select a new text to replace and accidentally hit Ctrl+C instead of Ctrl+V. So your old text is gone, and you have to find it back and copy again.

This is an attempt to make a small tray application to keep your latest 5 clipboard copies and let you choose one of them.

Requirements:

  • required perl 5.8.0 or higher.
  • Win32::GUI.
  • Win32::Clipboard blocks, so I had to put this in threads.
  • We need to share data between threads, thus threads::shared.
  • Problem: we cannot splice the array, so I'm deleting the array elements manually.

Bugs:

  • uses a lot of memory. Not recommended if you copy around huge amounts of data.
  • Can only handle text.

I'm using ActivPerl 5.8.3 and run this with ``wperl clip.pl'' from a startup script. You will also need an icon of some sort.

Please post you comments, suggestions.

Update: Screenshot

#!perl

# copyright (c) Chady Kassouf 2004
# This program is free software, it is distributed  under 
# the sames terms as Perl itself.


use strict;

use threads;
use threads::shared;
use Win32::Clipboard;
use Win32::GUI;

my @board : shared;
push @board, '--empty--' for 0 .. 4;

my @boxes;
my $CLIP = Win32::Clipboard();

my $thread = threads->create("monitor");

my $main = Win32::GUI::Window->new(
    -name    =>    'main',
    -text    =>    'Win32 Multiple Clipboard',
    -width    =>    200,
    -height    =>    150
);

my $icon = Win32::GUI::Icon->new('Note.ico');
my $tray = $main->AddNotifyIcon(
    -name    =>    'tray',
    -icon    =>    $icon,
    -tip    =>    'Win32 Multiple Clipboard'
);


foreach (0 .. 4) {
    my $label = $board[$_];
    $label =~ s/^(.{10})(.*)$/$1... /;
    $boxes[$_] = $main->AddRadioButton(
        -name => "board$_",
                -text => $label,
                -pos  => [ 10, 10 + 20 * $_ ],
    );
    $boxes[$_]->{-width} = 150;
}


$main->Show();
set_clip(1);
Win32::GUI::Dialog();

##

sub set_clip {
    my $which = shift;
    $boxes[$_]->Checked(0) for 0..4;
    $boxes[$_]->{-text} = $board[$_] for 0..4;
    if ($which) {
        $which--;
        my $d = $board[$which];
        $CLIP->Set($d);
        $main->Disable();
        $main->Hide();    
    }    
}

# there must be a better way for this.
sub board0_Click {set_clip(1);1;}
sub board1_Click {set_clip(2);1;}
sub board2_Click {set_clip(3);1;}
sub board3_Click {set_clip(4);1;}
sub board4_Click {set_clip(5);1;}

sub tray_Click {

    # move the window near the task bar
    my $desk = Win32::GUI::GetDesktopWindow();

    my $dw = Win32::GUI::Width($desk);
    my $dh = Win32::GUI::Height($desk);
    my $x = $dw - $main->Width();
    # the taskbar is 27 pixels on Xp (no Luna)
    my $y = $dh - $main->Height() - 27;
    $main->Move($x, $y);
    
    $main->Enable();
    $main->Show();
    set_clip(0);
    1;
}

# this runs in another thread because it blo
sub monitor {
    
    {
        print "[MONITOR]: Waiting for clipboard.\n";
        $CLIP->WaitForChange();
        print "[MONITOR]: Clipboard changed.\n";
        my $data = $CLIP->Get();
        last if $data eq '___EXIT___';
        # skip empty and same requests
        redo if ($data eq $board[0] || $data eq '--empty--');

        unshift @board, $data;
        # splice @board, 5;
        delete $board[$_] for 5 .. $#board + 1;
        redo;
    }
}


sub main_Minimize {
    $main->Disable();
    $main->Hide();
    1;
}


sub main_Terminate {
    $CLIP->Set('___EXIT___');
    $thread->join();
    $CLIP->Set($board[0]); # make sure we can start next time.
    -1;
}