Right Click an Element

346 views
Skip to first unread message

Alan Baird

unread,
Jun 10, 2008, 2:33:13 PM6/10/08
to watir-...@googlegroups.com
All -

I have posted a snippet of code to assist Watir users that might need to
right-click on an element on the screen. I would appreciate any
feedback you could give me about the method that I am using.

Ideally there should be a way to do this without resorting to AutoIt but
I couldn't find a way. If anyone knows how to use some windows API
method to click a mouse coordinate on a screen and/or how to figure out
the offset between an IE window an it's client area (as reported by
ole_object.getBoundingClientRect) I would really appreciate it.

http://wiki.openqa.org/display/WTR/Right+Click+an+Element

Thanks for your consideration,
Alan


Paul Rogers

unread,
Jun 10, 2008, 3:45:42 PM6/10/08
to watir-...@googlegroups.com
I think you can do

ie.button(:id , 'watever').fire_event('onContextMenu')

and then use autoit to send the key presses to select the menu item

Paul

Alan Baird

unread,
Jun 10, 2008, 4:05:18 PM6/10/08
to watir-...@googlegroups.com
I tried that and couldn't get it to work. Part of the reason I gave up
though is that I thought the HTML page had to define a Javascript event
for onContextMenu. Maybe that's not right but for whatever reason, the
command doesn't error out but also doesn't appear to bring up the
context menu.

Here is what I've tried:
$ie = Watir::IE.attach(:url, /SessionLog/)
$autoit = WIN32OLE.new('AutoItX3.Control')
$autoit.AutoItSetOption("WinTitleMatchMode", 2)
$autoit.AutoItSetOption("MouseCoordMode", 2)
window_title = "SessionLog?"
$autoit.WinActivate(window_title)

myobject = $ie.link(:text => 'Request', :index => 1)
myobject.focus
myobject.fire_event('onContextmenu')
$autoit.Send "{DOWN}{DOWN}{DOWN}{ENTER}"

Thanks for your help,
Alan

Paul Rogers

unread,
Jun 10, 2008, 4:12:55 PM6/10/08
to watir-...@googlegroups.com
you're right - I just tried this in IE7 on XP and the onContextMenu
event didnt do anything ( Im sure it used to) I took a quick look msdn
and couldnt see anything else that might help.

Paul

Bill Agee

unread,
Jun 10, 2008, 5:02:11 PM6/10/08
to watir-...@googlegroups.com
If you have the coordinates of the element, then you might be able to use SendInput to generate a right click at those coords.  See the docs for the function:
 
 
Note that you have to manually build and pack arrays of input data to mimic the structs SendInput expects as args.  I'm not aware of a friendlier way to do it...someone needs to write a nice wrapper around SendInput. :)
 
I've never used SendInput for mouse input, but I have some code laying around that uses it for keyboard input...let me know if you're interested and I'll paste it somewhere.
 
There's also the older (and maybe easier to use?) mouse_input function, but as its MSDN page says, it's been superseded by SendInput:
 

Alan Baird

unread,
Jun 11, 2008, 12:27:17 PM6/11/08
to watir-...@googlegroups.com
>I've never used SendInput for mouse input, but I have some code laying
>around that uses it for keyboard input...let me know if you're
interested >and I'll paste it somewhere.

That would be nice. Reading the explanation for this makes my head
hurt, any help in deciphering would be nice.

Alan

Bill Agee

unread,
Jun 14, 2008, 5:40:52 AM6/14/08
to watir-...@googlegroups.com
OK, finally got a few minutes to look at generating mouse input using the Windows API.  I got some example code working - I'll paste it below.  Hopefully this will be useful in some way or another. :)
 
The examples below are just toy programs; if code like this were to be used it would be nice to have it be in a general purpose input module.
 
And in the end Autoit may turn out to be the easiest way to accomplish this, but I must confess the idea of a pure Ruby approach is alluring. :)
 
Here's how to generate a right click at the cursor's current coordinates using mouse_event...not too harsh compared to SendInput.  I believe this is what the existing Ruby GuiTest modules use:
 
----
require 'Win32API'

Mouse_event = Win32API.new('user32','mouse_event','LLLLL','V')
MOUSEEVENTF_RIGHTDOWN = 0x0008
MOUSEEVENTF_RIGHTUP = 0x0010

Mouse_event.call(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0)
Mouse_event.call(MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0)
----

And here's a similar script that uses SendInput instead.  For the parts that get a little cryptic, I tried adding some comments:

----

require 'Win32API'

SendInput = Win32API.new("user32", "SendInput", 'IPI', 'I')

INPUT_MOUSE = 0
MOUSEEVENTF_LEFTDOWN = 0x0002
MOUSEEVENTF_LEFTUP = 0x0004
MOUSEEVENTF_RIGHTDOWN = 0x0008
MOUSEEVENTF_RIGHTUP = 0x0010

# Takes an array of packed input structures and passes them on to SendInput.
# Taken from the Ruby dl2 SendInput example in Takaaki Tateishi's dlcookbook:
# http://www.koders.com/ruby/fid5ECCEACCE986D7D1452DB90275B010C51F8EDD33.aspx
def send_input(inputs)
  n = inputs.size
  ptr = inputs.collect {|i| i.to_s}.join # flatten arrays into a single string
  SendInput.call(n, ptr, inputs[0].size)
end

# Takes a mouse input bit flag, and returns a packed string containing the flag.
# SendInput will accept the string as an INPUT structure.
#
# The list of flags is here:
# http://msdn.microsoft.com/en-us/library/ms646273(VS.85).aspx
def create_mouse_input(mouse_flag)
  mi = Array.new(7, 0)
  mi[0] = INPUT_MOUSE
  mi[4] = mouse_flag
  mi.pack('LLLLLLL') # Pack array into a binary sequence usable to SendInput
end

rightdown = create_mouse_input(MOUSEEVENTF_RIGHTDOWN)
rightup = create_mouse_input(MOUSEEVENTF_RIGHTUP)

p send_input([rightdown, rightup])

----

 

Bill Agee

unread,
Jun 20, 2008, 12:51:57 PM6/20/08
to watir-...@googlegroups.com
Hey Alan,
 
Based on your questions, I finished some example code based on yours, which removes the magic number dependency and uses the Windows API to drive the mouse.  Keyboard input is still done with autoit (via IE#send_keys), for now. :)
 
The code adds a method named right_click to the Element class; it moves the mouse cursor to the absolute coords of the element and then sends a right click to the current cursor location:
 
 

 
On 6/10/08, Alan Baird <alan....@riskmetrics.com> wrote:

Alan Baird

unread,
Jun 20, 2008, 4:38:48 PM6/20/08
to watir-...@googlegroups.com

Bill

I downloaded your example and it runs fine on my computer.  However, when I use it in conjunction with my program I get the following error:

>ruby right-click_BillAgee.rb

right-click_BillAgee.rb:22:in `method_missing': unknown property or method `parentWindow' (WIN32OLERuntimeError)

    HRESULT error code:0x80020006

      Unknown name.     from right-click_BillAgee.rb:22:in `left_edge_absolute'

        from right-click_BillAgee.rb:26:in `right_click'

        from right-click_BillAgee.rb:75:in `main'

        from right-click_BillAgee.rb:82

>Exit code: 1

The only thing I did was access my page from your program, I have posted the relevant modification below.  The main difference is that my link is in a table, see the code below:

  # Open google index page, and send a right click to the logo image

  ie = Watir::IE.attach(:url, /SessionLog?/)

  ie.table(:index, 1).link(:text => 'Request', :index => 1).right_click

  # Then, bring up the properties menu (works with IE6, at least)

  ie.send_keys("{DOWN}{DOWN}{DOWN}{ENTER}")

Maybe container.document.parentWindow doesnt work inside a table?

Alan

From: watir-...@googlegroups.com [mailto:watir-...@googlegroups.com] On Behalf Of Bill Agee
Sent: Friday, June 20, 2008 11:52 AM
To: watir-...@googlegroups.com
Subject: [wtr-general] Re: Right Click an Element

<br

Bill Agee

unread,
Jun 20, 2008, 5:01:28 PM6/20/08
to watir-...@googlegroups.com
Hey Alan,
 
I think I see the problem - you called it, for elements inside tables, their immediate container is a Table object instead of an IE object.
 
For the first table on the google index page, I found that invoking the container method again on the Table object returns the IE object we need, but this isn't a good solution:
 
ie.table(:index, 1).links[1].container.container
 
I wonder if there's some easy way to get the root container of an element (which is presumably always a Watir::IE instance).  Anyone know?
 
Thanks
Bill

Bill Agee

unread,
Jun 20, 2008, 5:11:27 PM6/20/08
to watir-...@googlegroups.com
I think I found a fix - there's an accessor called "page_container" that always seems to return an IE instance. :)  I just changed the pastie code - the change was to turn both uses of "container" into "page_container".

Alan Baird

unread,
Jun 20, 2008, 6:37:24 PM6/20/08
to watir-...@googlegroups.com

Bill –

 

This worked for me.  I will update the wiki accordingly.

 

Thanks again!

Alan

 


Reply all
Reply to author
Forward
0 new messages