The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!perl -w

# Listview with drag and drop capaility
# Authors: Chris Wearn, Jez White, Robert May
# July 2005

use strict;
use warnings;

use Win32::GUI 1.03_03, qw(TPM_LEFTALIGN TPM_TOPALIGN TPM_RIGHTBUTTON
                            ILC_COLOR ILC_COLOR24 ILC_MASK
			    WM_CONTEXTMENU CW_USEDEFAULT
			    LVS_ICON LVS_REPORT LVS_SMALLICON LVS_LIST
			    LVIR_SELECTBOUNDS LVSIL_SMALL);

use Win32::GUI::BitmapInline ();

our $VERSION = "0.01";

my $DEBUG=0;  # set to a false value to turn off debugging output

my $NUM_ITEMS = 20; # set to the number of items you want in the listview

my $dragInfo = {
	dragging       => 0,       # flag to indicate whether we are dragging or not
	cursor_start_x => undef,   # cursor x position in view coordinates at start of move
	cursor_start_y => undef,   # cursor y position in view coordinates at start of move
};

######################################################################
# Cursor for use when dragging
my $curDrag = newCursor Win32::GUI::BitmapInline( q(
AAACAAEAICAAABAACAAwAQAAFgAAACgAAAAgAAAAQAAAAAEAAQAAAAAAAAEAAAAAAAAAAAAAAAAA
AAAAAAAAAAAA////AAAACqoAABVVAAAIAgAAEAEAAAgCAAAQgQAACMIAABWVAAAJqgAAAwAAAEMA
AABmAAAAfgAAAH4AAAB/wAAAf4AAAH8AAAB+AAAAfBAAAHgoAABwbAAAYIIAAEBsAAAAKAAAABAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///gAP//4AD//+f8///n/P//53z//+Yc///mHP//
4CD//2QA//84f///GH///wD///8A////AA///wAf//8AP///AH///wD///8B7///A8f//weD//8P
Af//H4P//z/H////7/////////////////////////////////////8=
) );

######################################################################
# Create Main window
my $mw = Win32::GUI::Window->new(
	-title    => "ListView Drag and Drop Sample",
	-left     => CW_USEDEFAULT,  # let the window manager select the position
	-size     => [500,500],
	-onResize => \&resize
);

######################################################################
# Create ImageLists
my($ilLarge, $ilSmall) = CreateImageLists();

######################################################################
# Create ListView
my $lv = $mw->AddListView(
	-imagelist        => $ilLarge,
	-width            => $mw->ScaleWidth(),
	-height           => $mw->ScaleHeight(),
	-editlabel        => 1,
	-onBeginDrag      => \&beginDrag,
	-onBeginLabelEdit => sub { return 1;},
	-onEndLabelEdit   => sub { $_[0]->SetItemText($_[1],$_[2]) if defined $_[2] ; return 1; },
	-onMouseMove      => \&drag,
	-onMouseUp        => \&endDrag,
	-onMouseDown      => \&preDrag,
);

# Set the small icon imagelist (-imagelist option to AddListView
# sets both imagelists to be the same)
$lv->SetImageList($ilSmall, LVSIL_SMALL);
# Set large icon view (report view is the default)
$lv->View(LVS_ICON);

######################################################################
# Add columns for the 'report' view
$lv->InsertColumn( -text => "Column1", -width => 100);
$lv->InsertColumn( -text => "Column2", -width => 100);
$lv->InsertColumn( -text => "Column3", -width => 100);

######################################################################
# Add items to the listview
# Currently undocumented that -text item can take an array ref
# containing text for multiple columns
for my $i (1..$NUM_ITEMS) {
	$lv->InsertItem( -image => 0, -text  => ["Item ".$i, "Phone", 1024*$i],);
}

######################################################################
# Hook WM_CONTEXTMENU as the procedure to show our context menu
# WM_CONTEXTMENU is better that onMouseRightUp, as it is also
# generated for the keyboard menu key
$lv->Hook(WM_CONTEXTMENU, \&lvPopup);

######################################################################
# Some menu definitions, used for the context menus
my $Menus = Win32::GUI::Menu->new(
	"General Menu"       => "General",
	"> &Select All"       => { -name => "SelectAll",   -onClick => sub { select_all  ($lv); }, },
	"> &Deselect All"     => { -name => "DeselectAll", -onClick => sub { deselect_all($lv); }, },
	"> &Arrange"          => { -name => "Arrange",     -onClick => sub { arrange     ($lv); }, },
	"> -"                 => 0,
	"> View"              => "View",
	">> Lar&ge Icons"      => { -name => "Large",       -onClick => sub { change_view ($lv, LVS_ICON); }, },
	">> S&mall Icons"      => { -name => "Small",       -onClick => sub { change_view ($lv, LVS_SMALLICON); }, },
	">> &List"             => { -name => "List",        -onClick => sub { change_view ($lv, LVS_LIST); }, },
	">> &Details"          => { -name => "Details",     -onClick => sub { change_view ($lv, LVS_REPORT); }, },
	"Item Menu"          => "Item",
	"> &Edit Label"       => { -name => "EditLabel",   -onClick => sub { edit_label  ($lv); }, },
	"> &Change Icon"      => { -name => "ChangeIcon",  -onClick => sub { change_icon ($lv); }, },
	"> &Invert Selection" => { -name => "Invert",      -onClick => sub { invert_sel  ($lv); }, },
);

######################################################################
# Show the window and enter the dialog phase
$mw->Show();
Win32::GUI::Dialog();

exit(0);

######################################################################
# Event Handlers
######################################################################

######################################################################
# Called on left mouse down, before starting a drag
# use this to record the drag start position, as by the time we see a
# BeginDrag event, the cursor has moved (4 pixels I think).
sub preDrag
{
	my($control, $x, $y) = @_;
	print "Thinking about dragging? ($x, $y) - client coordinates\n" if $DEBUG;

	#convert cursor position into listview view coordinates
	my ($vox, $voy) = $control->GetOrigin();
	($vox, $voy) = (0,0) if not defined $vox; # GetOrigin returns undef for list and report views
	my ($cvx, $cvy) = ($x + $vox, $y + $voy);
	print "\tCursor at ($cvx, $cvy) - listview view coordinates\n" if $DEBUG;

	# store the potential start cursor position
	$dragInfo->{cursor_start_x} = $cvx;
	$dragInfo->{cursor_start_y} = $cvy;

	return 1;
}

######################################################################
# Called when a selected set of items is starting to be dragged
sub beginDrag
{
	my ($control, $item) = @_;

	my $dix;        # left corner of drag image
	my $diy;        # top corner of drag image
	my $dimagelist; # the drag image we create

	print "BeginDrag Event received for item $item (zero-based index)\n" if $DEBUG;

	# we're going to need to do a number of client to listview view coordinate
	# conversions, so get the listview origin here, once
	my ($vox, $voy) = $control->GetOrigin();
	($vox, $voy) = (0,0) if not defined $vox; # GetOrigin returns undef for list and report views

	# create the drag image
	for my $i ($control->SelectedItems()) {

		# Get the position of the image that will be created by CreateDragImage
		# NOTE: This is the top left of the rect around the icon and label, NOT
		# the top left of the item rect as returned by GetItemPosition
		# LVIR_SELECTBOUNDS returns the union of LVIR_ICON and LVIR_LABEL.
		# GetItemRect returns it's value in client coordinates, not listview
		# view coordinates
		my ($ix, $iy, undef, undef) = $control->GetItemRect($i, LVIR_SELECTBOUNDS);
		print "\tIcon for item $i at ($ix, $iy) - client coordinates\n" if $DEBUG;
		# convert to listview coordinates
	  ($ix, $iy) = ($ix + $vox, $iy + $voy);
		print "\tIcon for item $i at ($ix, $iy) - listview view coordinates\n" if $DEBUG;

		# create a temporary drag image for the item
		# NOTE: scoping of $tmp_imagelist is important so that destructors get called
		# NOTE: it really doesn't matter where we create this temporary image, as we never show it
		my $tmp_imagelist = $control->CreateDragImage($i, $ix, $iy);

		if(defined $dimagelist) {
			# Merge the existing drag image with the image for the item we are adding in this loop
			# NOTE: re-assinging a new imagelist to $dimagelist causes the ref count of the old one to go
			# to zero, so it's destructor gets called impicitly.
			$dimagelist = Win32::GUI::ImageList::Merge($dimagelist, 0, 0, $ix - $dix, $iy - $diy, $tmp_imagelist);

			# maintain the top left corner of the drag image (view coordinates)
			if($ix < $dix) { $dix = $ix; }
			if($iy < $diy) { $diy = $iy; }
		}
		else {
			# initialise the drag image
			$dimagelist = $tmp_imagelist;

			# initialise the top left corner of the drag image (view coordinates)
			$dix = $ix;
			$diy = $iy;
		}
	}

	# Temporarily change the cursor to the drag cursor
	# NOTE: Using SetCursor, the image gets changed immediatly
	# without having to move cursor again ... but we must repeat
	# the call on each mouse event, or it changes back (which is
	# what we want on mouseup, of course)
	Win32::GUI::SetCursor($curDrag);
	# Restrict cursor to listview client area while dragging
	# NOTE: could capture mouse (SetCapture), and then handle
	# not moving the icon if mouseup is outside client area
	my ($cl, $ct, $cr, $cb) = $control->GetClientRect();
	my ($sl, $st) = $control->ClientToScreen($cl, $ct);
	my ($sr, $sb) = $control->ClientToScreen($cr, $cb);
	Win32::GUI::ClipCursor($sl, $st, $sr, $sb);

	# Get the Cursor position
	my ($csx, $csy) = Win32::GUI::GetCursorPos();
	print "\tCursor at ($csx, $csy) - screen coordinates\n" if $DEBUG;
	my ($ccx, $ccy) = $control->ScreenToClient($csx, $csy);
	print "\tCursor at ($ccx, $ccy) - client coordinates\n" if $DEBUG;

	# Convert cursor position into listview view coordinates
	my ($cvx, $cvy) = ($ccx + $vox, $ccy + $voy);
	print "\tCursor at ($cvx, $cvy) - listview view coordinates\n" if $DEBUG;

	# Offset of cursor from where we draw the image. The Hotspot.
	my $offset_hx = $dragInfo->{cursor_start_x} - $dix;
	my $offset_hy = $dragInfo->{cursor_start_y} - $diy;
	print "\tHotspot offset ($offset_hx, $offset_hy)\n" if $DEBUG;

	# Create the temporary drag image
	Win32::GUI::ImageList::BeginDrag($dimagelist, 0, $offset_hx, $offset_hy);
	# Start dragging: lock updates to window and display drag image
	# NOTE: window coordinates not client coordinates
	Win32::GUI::ImageList::DragEnter( $control, $control->ClientToWindow($ccx, $ccy));

	# We're dragging
	$dragInfo->{dragging} = 1;

	return 0; # we processed the message, so don't pass it on
}

######################################################################
# Called on mouse move events - when dragging, move the drag image
sub drag
{
	my($control, $x, $y) = @_;

	# If dragging ...
	if($dragInfo->{dragging}) {
		# Maintain the drag cursor
		Win32::GUI::SetCursor($curDrag);

		# Move the drag image 
		# NOTE: window coordinates not client coorinates
		Win32::GUI::ImageList::DragMove($control->ClientToWindow($x, $y));

		return 0; # we processed the message, so don't pass it on
	}

	return 1; # we didn't process the message, so pass to default handler
}

######################################################################
# Called on mouse up events - when dragging, remove the drag image
# and move the dragged items to their new positions
sub endDrag
{
	my($control, $x, $y) = @_;

	# If dragging ...
	if($dragInfo->{dragging}) {

		# Convert client coordinates of mouse to view coordinates, and
		# calculate offset from initial cursor position
		my ($vox, $voy) = $control->GetOrigin();
		($vox, $voy) = (0,0) if not defined $vox; # GetOrigin returns undef for list and report views
		my $move_x = $vox + $x - $dragInfo->{cursor_start_x};
		my $move_y = $voy + $y - $dragInfo->{cursor_start_y};

		# re-position each selected item
		for my $i ($control->SelectedItems()) {
			my($ox, $oy) = $control->GetItemPosition($i);
			$control->ItemPosition($i, $ox + $move_x, $oy + $move_y);
		}

		print "Ending Drag ($x, $y) - client coordinates\n" if $DEBUG;
		# unlock window and hide drag image
		Win32::GUI::ImageList::DragLeave($control);
		# end the drag, destroying the temporary drag image
		Win32::GUI::ImageList::EndDrag();

		# free the cursor from the client area
		Win32::GUI::ClipCursor();

		$dragInfo->{dragging} = 0;  # not dragging any more

		return 0; # we processed the message, so don't pass it on
	}

	return 1; # we didn't process the message, so pass to default handler
}

######################################################################
# main window resize handler
sub resize
{
	my $self = shift;

	$lv->Resize($self->ScaleWidth(), $self->ScaleHeight());

	return 1;
}

######################################################################
# WM_CONTEXTMENU handler: Called on right mouse up, keyboard context
# menu key and shift-F10
sub lvPopup
{
  my ($self, $wparam, $lparam, $type, $msgcode) = @_;

	# if it isn't the event we are expecting, ignore it
  return if($type != 0);
  return if($msgcode != WM_CONTEXTMENU);

	print "Context menu called\n" if $DEBUG;

  my $hwnd = $wparam; # handle to the window clicked in

  # don't process if it's not in our window (i.e. over a child window)
  return if($hwnd != $self->{-handle});

  my $x = $lparam & 0xFFFF;  # in screen coordinates
  my $y = $lparam >> 16;     # in screen coordinates
  # convert to signed values to cope with multiple monitors
  # where screen coordinates can be negtive
  if($x > 32767) { $x -= 65536; }
  if($y > 32767) { $y -= 65536; }

	my $menu;  # which menu we'll display

  if(($x == -1) and ($y == -1)) {
    # One of Shift-F10 or VK_APP pressed

    # Our decision what to do ...
    # if no selected items, we'll display general menu at 0,0
    # if selected items, ensure first selected item is
    # visible and draw menu centered on it.

    my $selected_items = $self->GetSelectedCount();

    if($selected_items == 0) {
    	$x=0;
    	$y=0;
    	$menu=$Menus->{General};
		}
		else {
			my $item = ($self->SelectedItems())[0];
			$self->EnsureVisible($item);
			# get top left of icon, in view coordinates
			($x, $y) = $self->GetItemPosition($item);
			# convert view coordinates to client coordinates
			my ($vox, $voy) = $self->GetOrigin();
			($x, $y) = ($x - $vox, $y - $voy);
			# offset the menu from the top left of the icon
			$x += 20;
			$y += 20;
			$menu=$Menus->{Item};
		}
  }
  else {
    # if mouse over an item, select that item (other items
    # get deselected) and display item menu.  If not over
    # an item, show general menu
		($x, $y) = $self->ScreenToClient($x, $y);
    my $item = $self->HitTest($x, $y);
    if($item == -1) {
    	$menu=$Menus->{General};
		}
		else {
			$menu=$Menus->{Item};
		}
	}

	# disable the edit name item if more than one item selected
	if($self->GetSelectedCount() > 1) {
		$Menus->{EditLabel}->Enabled(0);
	}
	else {
		$Menus->{EditLabel}->Enabled(1);
	}


  # Display and process the menu (screen co-ordinates)
  $mw->TrackPopupMenu(
    $menu,
	  $self->ClientToScreen($x, $y),
    TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON, # right-click menus should allow right selection
  );

  return 0; # don't pass on as we handled it
}

######################################################################
# Menu item processing functions
######################################################################

######################################################################
# Selects all items in the listview
sub select_all
{
	my $self = shift;
	print "Got Popup Menu SELECT ALL command\n" if $DEBUG;
	$self->SelectAll();
	return 0;
}

######################################################################
# Deselects all items in the listview
sub deselect_all
{
	my $self = shift;
	print "Got Popup Menu DESELECT ALL command\n" if $DEBUG;
	$self->DeselectAll();
	return 0;
}


######################################################################
# Arrange all the icons
sub arrange
{
	my $self = shift;
	print "Got Popup Menu ARRANGE command\n" if $DEBUG;
	$self->Arrange();
	return 0;
}

######################################################################
# Edit the text label
# See also BeginEdit and EndEdit listview events
sub edit_label
{
	my $self = shift;

	my $item = ($self->SelectedItems())[0];
	print "Got Popup Menu EDIT LABEL command for Item $item\n" if $DEBUG;
	
	$self->EditLabel($item);
	return 0;
}

######################################################################
# Change the icon
sub change_icon
{
	my $self = shift;

	for my $item ($self->SelectedItems()) {
		print "Got Popup Menu CHANGE ICON command for Item $item\n" if $DEBUG;
	
		my %info = $self->GetItem($item);

		my $image = $info{-image};

		$self->ChangeItem(
			-item		=> $item,
			-image 	=> not $image,
		);
		$self->Update($item);
	}

	return 0;
}

######################################################################
# Invert the selection
sub invert_sel
{
	my $self = shift;
	print "Got Popup Menu INVERT SELECCTION command\n" if $DEBUG;
	my @items = $self->SelectedItems();
	$self->SelectAll();
	for my $item (@items) {
		$self->Deselect($item);
	}
	return 0;
}

######################################################################
# change the view type
sub change_view
{
	my $self = shift;
	my $type = shift;

	$self->View($type); # set view type
	$self->Arrange();
	return 0;
}

######################################################################
# Helper function to convert client coordinates to window
# coordinates
# Takes a 2 element list in client coordinates as input, and returns
# a 2 element list in window coordinates
sub Win32::GUI::ClientToWindow
{
	my $win = shift;
	my $x = shift;
	my $y = shift;

	my ($wl, $wt, undef, undef) = $win->GetWindowRect();
	my ($cl, $ct, undef, undef) = $win->GetAbsClientRect();

	return ($x + $cl - $wl,
	        $y + $ct - $wt);
}

######################################################################
# Function to create and return the imagelists that we will use

sub CreateImageLists
{
	my $bmpMobile = new Win32::GUI::BitmapInline( q(
	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
	) );

	my $bmpMobileMask = new Win32::GUI::BitmapInline( q(
	Qk2+AAAAAAAAAD4AAAAoAAAAIAAAACAAAAABAAEAAAAAAIAAAAAjCwAAIwsAAAIAAAACAAAAAAAA
	AP///wD//////+A////gB///4AP//8AD///AA///wAP//8AD///AA///wAP//8AD///AA///wAP/
	/8AD///AA///wAP//8AD///AA///wAP//8AD///AA///wAP//8AD///AA///wAP//8AD///AB///
	4f///+H////h////4f////H//w==
	) );

	my $bmpMobileSmall = new Win32::GUI::BitmapInline( q(
	Qk02AwAAAAAAADYAAAAoAAAAEAAAABAAAAABABgAAAAAAAADAAATCwAAEwsAAAAAAAAAAAAAAAAH
	AxMaAAAGAAAEAAUIBxQWAAEBAQUGBQABEwIGFQADHAEKDwAHAgAGAAAHAAAAAAQLAAEHETc5IE5P
	FUZEAB8dKFJRFzk4MEdJFyYpOEFFIisvKjg+AAIIAA4VAAAAAAAGFTs9J3t3GId/DIN6D4R7JI6H
	FXVvLIB+MH98Ln16NoWCKHd0CEZGAAMGAAAAAAIJFjw+j/PtQ83CDKOZAJGIdP/9Gp+cgPv5JZqZ
	if77IJGND3lzAE9MAAIGAAAABQAEKEFDr///be7nOtDKACMfM83MABETUNveACMmRsHDYNDQMJGP
	ACwsBQ4SAAAABAADJDc6vf/+lv//WfDtKc/OdP//RNXdZu76Q8rShf//Vs3PLn+CDTY5AAAEAAAA
	AA0MIzg62///nfH2YP/9ABQVUOLuABEjfvX/AAoZQd3iB4qNP3Z/OkVNAgAGAAAAAAQBAAgIFy42
	x///Z+3xT/X6cP//WOj6dv//RNHgeP//B3mAGz5LAAAHAAAEAAAAAAUBAA0MAAAIHUBNp/n/AAsW
	U///ABQdMPT+ABAbX9blADZFAAANCAcRAAEGAAAAAAsGED89ADEyAD1Fwf//ju38QtrlRv3/Itnj
	OdPeFXSDB0RSDEFLBC0wAAIEAAAAGTs6jM/KKKugAIJ8ADc/K3B/TqOzJoWVGXuNHnuKAC88JoGI
	AIB6MauhBzg2AAAAESoswv//WObZBqSYFIKCAEBJADZEADtLCktaACs2NpacHJmXROnca/fqADQx
	AAAANEJI0vn7r///fPbuc/z0effyrP//rfn/nPD1m///a//5W/vvFJ+UiurkGDc6AAAAAAEIJDg9
	BiwwneHgoP//k/32uv//rfHwk9/eqP//OMK2AHVpAFBLCTo8AAAGAAAAAAAHAAAHBwAGSUBKDxoe
	LkNFITg6JENCG0NCAC4sB0I+EkNBAAYLAAAHAAAHAAAAAAAHAQAKHgAGKQAGIAAIFgEJAgABAAMD
	AAUDAAQBABESAAADDgAGEQAJAwIMAAAA
	) );

	my $bmpMobileMaskSmall = new Win32::GUI::BitmapInline( q(
	Qk1+AAAAAAAAAD4AAAAoAAAAEAAAABAAAAABAAEAAAAAAEAAAAAjCwAAIwsAAAIAAAACAAAA////
	AAAAAAAAAAAAP/wAAH/8AAB//AAAf/wAAH/8AAB//AAAH/gAAB/wAAB/+AAAf/wAAH/8AAB//AAA
	H/wAAAAAAAAAAAAA
	) );

	my $bmpPhone = new Win32::GUI::BitmapInline( q(
	Qk02DAAAAAAAADYAAAAoAAAAIAAAACAAAAABABgAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAA////
	////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////
	////////////////8/Pzy8vLy8vL1tbW+fn5////////////////////////////////////////
	////////////////////////////////////////////////////////////8/Pzy8vLp6etTU1/
	TU1tYWF0ubm61tbW+fn5////////////////////////////////////////////////////////
	////////////////////////////8/Pzy8vLrKyxVlaIR0ecCwvVCwuMHBx7Tk5ybGx8vLy91tbW
	+fn5////////////////////////////////////////////////////////////////////8/Pz
	y8vLp6etTU2FOzugAAD/AAD/AADjAACRAACEAACEEBB+RERsYWF0ubm61tbW+fn5////////////
	////////////////////////////////////////8/Pzy8vLrKyxVlaIR0ecCwvtCAjxAAD/AAD/
	AADjAACRAACEAACEAgKDCQmAHBx7Tk5ybGx8vLy91tbW+fn5////////////////////////////
	////////8/Pzy8vLp6etTU2FOzugAAD/AAD/AAD/AAD/AAD/DAz8KyvcBgaQAACEAACEAACEAACE
	AACEAACEEBB+RERsYWF0ubm61tbW+fn5////////////////////////y8vLrKyxVlaIR0ecCwvt
	CAjxAAD/AAD/AAD/DAz8ODj1XV3vsLDHGRmNAACEAACEAACEAACEAACEAACEAgKDCQmAHBx7Tk5y
	bGx8vLy91tbW+fn5////////////////Xl5qPj6cAAD/AAD/AAD/AAD/AAD/DAz8ODj1ZGTu///W
	8vLYmprQFhagAACEAACEAACEAACEAACEAACEAACEAACEAACEBASCJiZ2Wlplb29vurq6y8vL1tbW
	////////T0+BEhLiAAD/AAD/AAD/DAz8ODj1XV3v4uLa29vbxsbeoKDkFhb4AwPfAACYAACGAACE
	AACEAACEAACEAACEAACEBASCISF4VVVtTk6BTk5/VlZyVlZwbGx81tbW+fn5TU2FCwvtAAD/DAz8
	ODj1ZGTu///W8vLYxsbfmprlAAD/AAD/AAD/AAD/AADpAACjAACEAACEAACEAACEAACEAACEFRV8
	UlJuOzugCAjxAADjAACRAACEEBB+YWF0ubm6TU2FFxfrODj1XV3v4uLa29vbxsbeoKDkHBz6FRX1
	AADdAADVAAD/AAD/AAD7AADgAACYAACGAACEAACEAACEBASCISF4NzeJCAjrAQHkBATMEhKIFRV8
	EhJ9ISF6YGBsTU2FQ0Pk///W8vLYxsbfmprlAAD/AAD4AADdDQ2/ODh/KytiAADpAAD7AAD/AAD/
	AADpAACjAACEAACEAACEFRV8TU1qCwuUAADUAACmEBCTRERwTU1qQEBtJiZ2WlplYWGFR0fNxsbe
	oKDkHBz6FRX1AADdDAzBOTmRVFRxra20lZWjAACdAADMAAD/AAD/AAD7AADgAACYAACGAACEFRV8
	UVFoGRmEAAC7AADpAgLYCQmMCwuAFhZ7SEhqX19k1dXVbGyMEBDkAgL7AAD/DAznODh/V1dbyMjA
	qamvLS2Bi4ubODh/KytiAADpAAD7AAD/AAD/AADpAACjAACEFRV8Xl5kPj6JAAD/AAD/AADjAACR
	AACEAACEFRV8V1dm+fn5xsbHWlqRGxvWAAD/JSX1qanSioq7PT2HhISVPj6DYGBpra20lZWjAACd
	AADMAAD/AAD/AAD7AADgAACYFRV/T09pEhKYAAD/AAD/AADpAACjAACEAACEFRV8V1dm////////
	1tbWbGyLEBDkAgL7AAD/DAznODh/V1dbyMjAqamvLS2Bi4ubODh/KytiAADpAAD7AAD/AAD/AADp
	FRWcTU1qCwuaAAD/AAD/DAz8MjLhODiWKyuSFRV8V1dm////////+fn5xsbHWlqRGxvWAAD/JSX1
	qanSioq7PT2HhISVPj6DYGBpqam9g4O/AAC+AAD1AAD/AAD4AADgFRW9TU1qCwuaAAD/DAz8UVHx
	nZ3iqanVkJDHQUGKXl5o////////////////1tbWbGyLEBDkAgL7AAD/DAznODh/V1dbxsbJmprN
	AAC0AAD0AAD/BAT3EBDkAgLfAACEFRV8TU1qCwuaAAD/ODj1xsbfHBz6AAD/MDDvv7+nfHx6////
	////////////7e3toKCjSEiLFxfYAAD/JSX1qanSiorBHBy5FRXvAAD0BAT1FRXcIyPFUFCQLy/N
	AACEFRV8TU1wCwutDAz8UVHxmprlFRX7BAT3LS3Fjo6M09PT////////////8/Pzra2seXl+VFR4
	VlZyEBDkAgL7AAD/AAD/AAD4AADmBATiHx/DTU1qUVFohYV8wcG6MDCQJSV7OzuKFRXsV1fwioro
	AAD/BAT3LCy/gICO////////////////////0dHRhIR8sLC9NjakaWlHNzeQFRXZAAD/AAD/AADm
	AACdExOLPDx5CwuAGRmBbGyRxcWzo6OjT099QECoUlLmioroJSX4BAT3JyfGgICO2dnZ////////
	////////////y8vLfX12rq6lTU2FOzuEEBB+ODiGEBDkDg74KyvcBgaQFRV8QUFzAACfAACzEBDk
	UFCQdHSBYGCixsbfmprlBAT3ExPgLCy/gICO////////////////////////////y8vLbm5rcXGa
	CwvtCAjbAgKXFRWCNzeQR0fQvLzLQ0OXISF+NTWcAADxDAzxOjrxW1vhsrLSi4vcISHyKCjcKirD
	Y2OJgICO2dnZ////////////////////////////1tbWhYWAqam9Kyv3Bgb9AADpAACjEBB+Y2N/
	3Ny8vLyvSEieOjrpODj1V1fwxsbfmprlBAT3ExPgLCy/a2t8iIiI6enp////////////////////
	////////////////+fn5xMTEj4+NoaHMOjrxAAD7DAzeOjqVVFSQlpaJg4OdWVnjnp7jqanjj4/g
	MTHXKyvYKirDY2OJgICO09PT6enp/Pz8////////////////////////////////////////////
	1tbWhYWAoaG4EBDkLS3zxsbfxsbfxsbfxsbfn5/eKSncFRXcLCy/dHR0dHR0iIiI6enp////////
	////////////////////////////////////////////////////+fn5xMTEgoKEWlqRJibOMTHX
	MTHXMTHXMTHXPz+/ZmaIa2uBgICO5OTk5OTk6enp/Pz8////////////////////////////////
	////////////////////////////////////////1tbWfX19dHR0dHR0dHR0dHR0iIiI6enp////
	////////////////////////////////////////////////////////////////////////////
	////////////////+fn55ubm5OTk5OTk5OTk5OTk6enp/Pz8////////////////////////////
	////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////
	) );

	my $bmpPhoneMask = new Win32::GUI::BitmapInline( q(
	Qk2+AAAAAAAAAD4AAAAoAAAAIAAAACAAAAABAAEAAAAAAIAAAAATCwAAEwsAAAIAAAACAAAAAAAA
	AP///wD///////P////A////AD///AAH//AAA//AAAB/AAAAHwAAAAMAAAABAAAAAAAAAAAAAAAA
	AAAAAAAAAACAAAAAwAAAAOAAAADwAAAA+AAAAPAAAAPgAAAD4AAAD+AAAB/gAAB/8AAA//gAB//8
	AD///wH//////////////////w==
	) );

	my $bmpPhoneSmall = new Win32::GUI::BitmapInline( q(
	Qk02AwAAAAAAADYAAAAoAAAAEAAAABAAAAABABgAAAAAAAADAAATCwAAEwsAAAAAAAAAAAAA//v/
	/v3/8fX2+v//4ezq9P//8v//8///6ff29P//9v//7vb2+/7//fz/+vn///7///v//fr/7+//8PL/
	9Pv/8fv/7f3/5/f/7fz/7ff/8/v/8vb/9Pf/+/v/+Pf///7///r/T0hpOjNgVk6DSkV8SEp6NDlm
	R0x5Q0d3QT52S0Z9VE59UU1wamp8+/7//f//6+b7h3yuemuva1ytaV6ud3K3dHWydHWyXFufeXDB
	dGe7dWmxRj9yJyc/+v3//f//+vb/saPjh3TLrZn8iHrYhoHMjo7OiYjKkIzYl4rslIPqrp/7c2mv
	WVp87vL9/f//8vH/pprce2nEiXbdh3ja39v/e3q+0NH/hIHR1Mf/eGjTiHrahn7FUlR29v3/+///
	7vP/9O//in7Kb2K+iH3ZtbH9k5PZsbH3rar5ppv7joHji4PWODZxsLvR8/7/+v//5e738/T/rqnm
	amGxd27E2dT/nJrmzsv/v7v/z8b/e3PKfHi/ZWmZ5vL/8///+v7/8v7/8/f/6en/ranraWOywrv/
	jYTepZ75l5Hqp6L3ammzQEJ87PX/3uz/8f3/+vz/Ki9IMzhZaWmXz83/kY7TxsD/pZ/xxr//zcn/
	zMr/dne7g4W/foO0Rkx1JClI6+r+i4XGrajlKCVdkI/Hp6jgdXawhYXBkZDOamqqY2OjXFudq6nv
	ZmKugHrMR0KHbGiLTD6WenC9Qzx/lZLK0tX/P0NsoKfO7/P/3+T/e3y0YF+jsav8bmPDdGbQTECY
	k422fGrHfm7FU0eTQDp17O7/4Of/5fH/7PX/6vL/2tv/qqbtfXLOcGHKi3nqLR55sazTmI/SfXK7
	q6DpTkaFGhdIU1dzbniKbneLYWWIZmKdXlajkITejIHefnTOS0SH9vX/8fr/sbDalo/MmI3TiX/B
	h4O0R0dvT054Pjtycmi0h33Qg3rKe3i2pq/RzNbn9v7/6fzt8/j/8+7/7eH/7uL/zcX/4Nv/19L/
	7OT/0MP/2Mr/4tv/7/H/3vD38f/79P/8
	) );

	my $bmpPhoneMaskSmall = new Win32::GUI::BitmapInline( q(
	Qk1+AAAAAAAAAD4AAAAoAAAAEAAAABAAAAABAAEAAAAAAEAAAAAjCwAAIwsAAAIAAAACAAAAAAAA
	AP///wD//wAA//8AAIADAACAAwAAgAMAAIADAADAAwAAwAcAAMAPAAAAAQAAAAAAAAAAAAAAAAAA
	AAEAAIABAADgAwAA
	) );

	# Cretae the NORMAL imagelist (large icons)
	my $ilLarge = Win32::GUI::ImageList->new(32, 32, ILC_COLOR|ILC_COLOR24|ILC_MASK, 2, 0);
	$ilLarge->Add($bmpMobile, $bmpMobileMask);
	$ilLarge->Add($bmpPhone, $bmpPhoneMask);

	# Cretae the SMALL imagelist (small icons)
	my $ilSmall= Win32::GUI::ImageList->new(16, 16, ILC_COLOR|ILC_COLOR24|ILC_MASK, 2, 0);
	$ilSmall->Add($bmpMobileSmall, $bmpMobileMaskSmall);
	$ilSmall->Add($bmpPhoneSmall, $bmpPhoneMaskSmall);

	return ($ilLarge, $ilSmall);
}