Robert,
Thanks a bunch! That does work very nice. I have been gone camping with the family so I haven't even looked at a computer in 3 days. Very well done.

Steve

----- Original Message ----- From: "Robert May" <[EMAIL PROTECTED]>
To: "Steven Lloyd" <[EMAIL PROTECTED]>
Cc: "Jeremy White" <[EMAIL PROTECTED]>; <perl-win32-gui-users@lists.sourceforge.net>
Sent: Friday, August 19, 2005 9:37 AM
Subject: Re: Draggable 'window'


Steven Lloyd wrote:
MouseMove returns the label, and the x, y coordinates of the mouse, right?

Right - but it gives an unsigned value.  This is not usually a problem,
as you don't normally seem mouse move locations outside the client area,
which always has 'small' positive values.  The code below contains a
work-around for this issue, and I will look at Jeremy's solution from
the other related thread.

Not wanting to steal your thunder on this one, but I had a few hours on
a plane this-morning, and I came up with a way of doing the drawing that
doesn't need the Redraw/InvalidateRect calls.  It also deals with not
drawing over windows that overlap the one you hover the mouse over.

I tried to avoid using Win32::API, but in the end needed a couple of
extra calls to get the full functionality that I was looking for.

Regards,
Rob.

#!perl -w
use warnings;
use strict;

use Win32::GUI;
use Win32::GUI::BitmapInline();
use Win32::API;

# Some constants
sub CW_USEDEFAULT()     {0x80000000}
sub R2_NOT()            {6}
sub PS_INSIDEFRAME()    {6}
sub NULL_BRUSH()        {5}
sub GWL_STYLE()         {-16}
sub CWP_SKIPINVISIBLE() {1}

sub CLR_BLUE()          {0xFF0000}

# Using Win32::API, as GetWindowDC not in Win32::GUI
my $GetWindowDC = Win32::API->new("user32", "GetWindowDC", 'N', 'N')
or die "Can't load GetWindowDC: $!";

# Note that the prototype of 'NNNN' is a cheat, and relies on the fact
# that the point structure (2nd argument) that must be passed by value
# can be pushed onto the calling stack as 2 16-bit numbers, rather than
# a single 32-bit struct.  Doing it this way avoids having to use
# Win32::API::Struct to define a POINT struct, and using the prototype
# 'NSN'.
my $ChildWindowFromPointEx = Win32::API->new("user32", "ChildWindowFromPointEx", 'NNNN', 'N')
or die "Can't load ChildWindowFromPointEx: $!";

# Some resources that we'll be using a lot; create them  once:
my $cursor = get_cursor();
my $blank_cursor = get_cursor2();
my $pen = Win32::GUI::Pen->new(
-style => PS_INSIDEFRAME,
-width => 3,
-color => 0,  # pen color is ignored when foreground mix mode is R2_NOT
) or die "Creating Pen";
my $null_brush = Win32::GUI::GetStockObject(NULL_BRUSH);

# The window handle of the window that we're over, and have
# drawn a rectangle around.  Use 0 to indicate no such window.
my $curHwnd = 0;

# Create out main window
my $mw = Win32::GUI::Window->new(
-name => "MainWindow",
-title => "Win32::GUI Spy++",
-left => CW_USEDEFAULT,
-size => [215,140],
-resizable => 0,
) or die "Creating Main Window failed";

# Instruction label
$mw->AddLabel(
-name => "Instructions",
-text => "Drag the target over the window you want information about",
-pos => [5,4],
-size => [160, 32],
) or die "Creating Instruction label failed";

# Target bitmap label
$mw->AddLabel(
-name => "Target",
-pos => [170,4],
-icon => $cursor,
-background => CLR_BLUE,
-notify => 1,
-onMouseDown => \&mouseDown,
-onMouseUp => \&mouseUp,
-onMouseMove => \&mouseMove,
) or die "Creating Target Label failed";

# Label and Textfield to report window handle
$mw->AddTextfield(
-name => 'HWND',
-prompt => [ 'Handle', 40 ],
-pos => [5,40],
-size => [160,20],
-readonly => 1,
) or die "Creating HWND Field failed";

# Label and Textfield to report window title/text
$mw->AddTextfield(
-name => 'TITLE',
-prompt => [ 'Title', 40 ],
-pos => [5,62],
-size => [160,20],
-readonly => 1,
) or die "Creating TITLE field failed";

# Label and Textfield to report window class
$mw->AddTextfield(
-name => 'CLASS',
-prompt => [ 'Class', 40 ],
-pos => [5,84],
-size => [160,20],
-readonly => 1,
) or die "Creating CLASS field failed";

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

# We're done
exit(0);

######################################################################
# Event handlers
######################################################################

######################################################################
# Target Label Mouse Down handler:
# - Capture the mouse, set the cursor to the target cursor, and remove
#   the icon from the label
sub mouseDown{
  my $label = shift;

  $label->SetCapture();
  Win32::GUI::SetCursor($cursor);
  $label->SetIcon($blank_cursor);

  return;
  }

######################################################################
# Target Label Mouse Up handler:
# - If the label has captured the mouse (should have done in the
#   mouse down handler), then release capture, set the label icon
#   and set the fact that we're no longer over a window
# - Force a redraw of all windows, in case we got any of our
#   drawing wrong.
sub mouseUp{
  return unless Win32::GUI::GetCapture();

  my $label = shift;

  $label->ReleaseCapture();
  $label->SetIcon($cursor);
  $curHwnd = 0;

  # redraw everything to remove any trace that we
  # were here (in case my drawing is wrong somewhere).
  Win32::GUI::InvalidateRect(0,1);

  return;
}

######################################################################
# Target Label Mouse Up handler:
# - If the label has captured the mouse, then find the window the
#   mouse is over.  If it has changed, then undraw any highlight
#   rectangle we have already drawn, draw the highlight rectangle
#   around the new window and update the information fields.
sub mouseMove{
  return unless Win32::GUI::GetCapture();

  my ($label, $cx, $cy) = @_; # x,y in client co-ordinates
  Win32::GUI::SetCursor($cursor);

  # Take into account that client co-ordinates can be negative,
  # convert pos to screen co-ordinates and get handle to window
  # at that position:
  $cx -= 65536 if $cx > 32767;
  $cy -= 65536 if $cy > 32767;
  my ($sx, $sy) = $label->ClientToScreen($cx, $cy);

  my $hwnd=GetWindowFromPoint($sx, $sy);

  # If we moved into a new window, then undraw the rect
  # from the previous window, draw the rect around the
  # new window, and update the textfields:
  if($hwnd != $curHwnd) {
  DrawInvertedRect($curHwnd) if $curHwnd;  # undraw old highlight
  DrawInvertedRect($hwnd);  # draw new highlight
  $curHwnd = $hwnd;         # store the window with the highlight

  # Update the information fields
  $mw->HWND->Text(sprintf("0x%08X", $hwnd));
  $mw->CLASS->Text(Win32::GUI::GetClassName($hwnd));
  $mw->TITLE->Text(Win32::GUI::Text($hwnd));
  }

  return;
}

######################################################################
# Helper Functions
######################################################################

######################################################################
# GetWindowFromPoint
# - returns the handle of the window whose location is at the screen
#   co-ordinates x, y
sub GetWindowFromPoint
{
my ($sx, $sy) = @_;

# Firstly get the window handle at the screen
# co-ordinates given.  This ignores hidden,
# disabled and static text controls.
my $hwnd=Win32::GUI::WindowFromPoint($sx, $sy);

# Convert $x, $y to client co-ordinates of the hwnd
# that has been found, and use ChildWindowFromPointEx
# to find static text controls and disabled
# windows (but ignoring hidden windows, as we can't
# draw on them).  Only use return value if not NULL(0)
# which happens if we're in the non-client region of a window
my ($cx, $cy) = Win32::GUI::ScreenToClient($hwnd, $sx, $sy);
my $chwnd=$ChildWindowFromPointEx->Call($hwnd, $cx, $cy, CWP_SKIPINVISIBLE);
$hwnd = $chwnd if $chwnd;

# If We've got a child window, then walk it's siblings to see
# if there is a visible window higher in the z-order that we
# should be using. This is needed, for example, when a groupbox
# contains controls that have the same parent as the groupbox
# itself.  I.e this copes with multiple controls with the same
# parent occupying the same space - GetChildWindowFromPoint only
# ever gives the lower z-order window. Why?
#
# It would be better to use EnumChildWindows, but we don't have
# that in Win32::GUI, and I don't trust the Win32::API callback
# support.
if((Win32::GUI::GetWindowLong($hwnd, GWL_STYLE) & WS_CHILD) == WS_CHILD) {
my $shwnd = $hwnd;
while($shwnd = Win32::GUI::GetWindow($shwnd, GW_HWNDNEXT)) {
my ($l, $t, $r, $b) = Win32::GUI::GetWindowRect($shwnd);
if( Win32::GUI::IsVisible($shwnd) and
    $sx >= $l and $sx <= $r and
    $sy >= $t and $sy <= $b) {
$hwnd =  $shwnd;
}
}
}

return $hwnd;
}

######################################################################
# DrawInvertedRect
# - Draws a rectangle around a window.  Uses a NULL brush to avoid
#   painting over the window content;  use a pen with style
#   PS_INSIDEFRAME to adjust the rectangle edge to  be drawn entirely
#   inside the rectangle boundaries; use a foreground mix mode
#   (SetROP2) of R2_NOT, so that drawing the same rectangle a second
#   time, undoes the draw.
sub DrawInvertedRect
{
my $hwnd = shift or return 0;

# Get window position in screen co-ordinates
my($l, $t, $r, $b) = Win32::GUI::GetWindowRect($hwnd);

# Get a handle to the WINDOW DC (the whole window, not just
# the client area, so that we can draw in the non-client
# area for window where we need to)
my $hdc = $GetWindowDC->Call($hwnd);
# Set the foreground mix mode
my $oldROP = Win32::GUI::DC::ROP2($hdc, R2_NOT);
# Set the pen and brush
my $oldPen = Win32::GUI::DC::SelectObject($hdc, $pen);
my $oldBrush = Win32::GUI::DC::SelectObject($hdc, $null_brush);

# Draw the rectangle
Win32::GUI::DC::Rectangle($hdc, 0, 0, $r-$l, $b-$t);

# Restore the forgeround mix mode, pen and brush, and relese
# the window DC
Win32::GUI::DC::ROP2($hdc, $oldROP);
Win32::GUI::DC::SelectObject($hdc, $oldPen);
Win32::GUI::DC::SelectObject($hdc, $oldBrush);
Win32::GUI::DC::ReleaseDC($hwnd, $hdc);

return 1;
}

######################################################################
# get_cursor
# - returns a Win32::GUI::Cursor object for the target cursor/icon
sub get_cursor
{
return Win32::GUI::BitmapInline->newCursor( q(
AAACAAEAICAAAA8AEAAwAQAAFgAAACgAAAAgAAAAQAAAAAEAAQAAAAAAAAEAAAAAAAAAAAAAAAAA
AAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHwAAAGDAAACbIAABQFAAApsoA
AUAFAAECgQACoAqAAqgqgAIBAIACqCqAAqAKgAECgQABQAUAAKbKAABQFAAAJsgAABgwAAAHwAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////////////////////////g////g
D///wAf//4gj//8YMf/+ODj//nx8//wMYH/8A4B//AOAf/wDgH/8DGB//nx8//44OP//GDH//4gj
///AB///4A////g///////////////////////////////////////8=
) );
}

######################################################################
# get_cursor2
# - returns a Win32::GUI::Cursor object for a blank (transparent)
#   cursor/icon, used to replae the target icon in the TARGET label
#   while we're draggin the target around
sub get_cursor2
{
return Win32::GUI::BitmapInline->newCursor( q(
AAABAAEAICAAAAEAAQAwAQAAFgAAACgAAAAgAAAAQAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////8=
) );
}




--
No virus found in this incoming message.
Checked by AVG Anti-Virus.
Version: 7.0.338 / Virus Database: 267.10.13/78 - Release Date: 8/19/2005




Reply via email to