rautomation 0.6.3 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. data/.gitignore +34 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +24 -0
  4. data/History.rdoc +21 -0
  5. data/LICENSE +1 -1
  6. data/README.rdoc +8 -7
  7. data/Rakefile +22 -32
  8. data/VERSION +1 -1
  9. data/ext/UiaDll/Release/UiaDll.dll +0 -0
  10. data/ext/UiaDll/UiaDll.suo +0 -0
  11. data/ext/UiaDll/UiaDll/UiaDll.cpp +236 -10
  12. data/lib/rautomation/adapter/autoit/locators.rb +1 -1
  13. data/lib/rautomation/adapter/autoit/text_field.rb +10 -1
  14. data/lib/rautomation/adapter/autoit/window.rb +50 -23
  15. data/lib/rautomation/adapter/helper.rb +6 -6
  16. data/lib/rautomation/adapter/ms_uia.rb +22 -0
  17. data/lib/rautomation/adapter/{win_ffi → ms_uia}/button.rb +3 -2
  18. data/lib/rautomation/adapter/ms_uia/button_helper.rb +25 -0
  19. data/lib/rautomation/adapter/ms_uia/checkbox.rb +27 -0
  20. data/lib/rautomation/adapter/{win_ffi → ms_uia}/constants.rb +20 -5
  21. data/lib/rautomation/adapter/ms_uia/control.rb +180 -0
  22. data/lib/rautomation/adapter/{win_ffi → ms_uia}/functions.rb +24 -26
  23. data/lib/rautomation/adapter/ms_uia/keystroke_converter.rb +122 -0
  24. data/lib/rautomation/adapter/{win_ffi → ms_uia}/label.rb +3 -2
  25. data/lib/rautomation/adapter/ms_uia/list_box.rb +91 -0
  26. data/lib/rautomation/adapter/ms_uia/list_item.rb +49 -0
  27. data/lib/rautomation/adapter/ms_uia/locators.rb +23 -0
  28. data/lib/rautomation/adapter/{win_ffi → ms_uia}/radio.rb +2 -2
  29. data/lib/rautomation/adapter/{win_ffi → ms_uia}/select_list.rb +41 -7
  30. data/lib/rautomation/adapter/ms_uia/table.rb +86 -0
  31. data/lib/rautomation/adapter/{win_ffi → ms_uia}/text_field.rb +5 -4
  32. data/lib/rautomation/adapter/ms_uia/uia_dll.rb +62 -0
  33. data/lib/rautomation/adapter/ms_uia/window.rb +364 -0
  34. data/lib/rautomation/adapter/win_32.rb +21 -0
  35. data/lib/rautomation/adapter/win_32/button.rb +14 -0
  36. data/lib/rautomation/adapter/{win_ffi → win_32}/button_helper.rb +1 -1
  37. data/lib/rautomation/adapter/win_32/checkbox.rb +14 -0
  38. data/lib/rautomation/adapter/win_32/constants.rb +68 -0
  39. data/lib/rautomation/adapter/{win_ffi → win_32}/control.rb +30 -23
  40. data/lib/rautomation/adapter/win_32/functions.rb +313 -0
  41. data/lib/rautomation/adapter/win_32/keys.rb +121 -0
  42. data/lib/rautomation/adapter/win_32/label.rb +10 -0
  43. data/lib/rautomation/adapter/win_32/list_box.rb +40 -0
  44. data/lib/rautomation/adapter/{win_ffi → win_32}/locators.rb +2 -2
  45. data/lib/rautomation/adapter/win_32/radio.rb +11 -0
  46. data/lib/rautomation/adapter/win_32/select_list.rb +97 -0
  47. data/lib/rautomation/adapter/win_32/table.rb +22 -0
  48. data/lib/rautomation/adapter/win_32/text_field.rb +42 -0
  49. data/lib/rautomation/adapter/{win_ffi → win_32}/window.rb +79 -62
  50. data/lib/rautomation/element_collections.rb +9 -1
  51. data/lib/rautomation/window.rb +17 -9
  52. data/rautomation.gemspec +24 -0
  53. data/spec/adapter/autoit/window_spec.rb +71 -0
  54. data/spec/adapter/{win_ffi → ms_uia}/button_spec.rb +1 -1
  55. data/spec/adapter/{win_ffi → ms_uia}/checkbox_spec.rb +9 -3
  56. data/spec/adapter/ms_uia/control_spec.rb +28 -0
  57. data/spec/adapter/ms_uia/keystroke_converter_spec.rb +50 -0
  58. data/spec/adapter/{win_ffi → ms_uia}/label_spec.rb +2 -2
  59. data/spec/adapter/ms_uia/list_item_spec.rb +14 -0
  60. data/spec/adapter/{win_ffi → ms_uia}/listbox_spec.rb +18 -5
  61. data/spec/adapter/{win_ffi → ms_uia}/radio_spec.rb +3 -1
  62. data/spec/adapter/ms_uia/select_list_spec.rb +109 -0
  63. data/spec/adapter/{win_ffi → ms_uia}/table_spec.rb +12 -1
  64. data/spec/adapter/{win_ffi → ms_uia}/text_field_spec.rb +2 -1
  65. data/spec/adapter/ms_uia/window_spec.rb +89 -0
  66. data/spec/adapter/win_32/button_spec.rb +30 -0
  67. data/spec/adapter/win_32/checkbox_spec.rb +48 -0
  68. data/spec/adapter/win_32/label_spec.rb +15 -0
  69. data/spec/adapter/win_32/listbox_spec.rb +42 -0
  70. data/spec/adapter/win_32/radio_spec.rb +32 -0
  71. data/spec/adapter/{win_ffi → win_32}/select_list_spec.rb +16 -16
  72. data/spec/adapter/win_32/table_spec.rb +28 -0
  73. data/spec/adapter/win_32/text_field_spec.rb +24 -0
  74. data/spec/adapter/{win_ffi → win_32}/window_spec.rb +19 -14
  75. data/spec/button_spec.rb +1 -0
  76. data/spec/buttons_spec.rb +4 -4
  77. data/spec/spec_helper.rb +30 -4
  78. data/spec/text_field_spec.rb +6 -7
  79. data/spec/window_spec.rb +12 -0
  80. data/spec/windows_spec.rb +19 -0
  81. metadata +116 -97
  82. data/lib/rautomation/adapter/win_ffi.rb +0 -21
  83. data/lib/rautomation/adapter/win_ffi/checkbox.rb +0 -19
  84. data/lib/rautomation/adapter/win_ffi/keystroke_converter.rb +0 -67
  85. data/lib/rautomation/adapter/win_ffi/list_box.rb +0 -60
  86. data/lib/rautomation/adapter/win_ffi/ms_uia/uia_dll.rb +0 -36
  87. data/lib/rautomation/adapter/win_ffi/table.rb +0 -57
  88. data/spec/adapter/win_ffi/keystroke_converter_spec.rb +0 -47
@@ -8,7 +8,7 @@ module RAutomation
8
8
 
9
9
  def extract(locators)
10
10
  locators = self.class::DEFAULT_LOCATORS.merge(locators) if self.class.const_defined?(:DEFAULT_LOCATORS)
11
- @locators = locators
11
+ @locators = {:index => 0}.merge(locators)
12
12
  @autoit_locators = "[#{@locators.map do |locator, value|
13
13
  locator_key = self.class::LOCATORS[locator] || self.class::LOCATORS[[locator, value.class]]
14
14
  value = value.to_i + 1 if locator == :index # use 0-based indexing
@@ -30,6 +30,10 @@ module RAutomation
30
30
  extract(locators)
31
31
  end
32
32
 
33
+ def hwnd
34
+ Window.autoit.ControlGetHandle(@window.locator_hwnd, "", @autoit_locators).hex
35
+ end
36
+
33
37
  # @see RAutomation::TextField#set
34
38
  def set(text)
35
39
  wait_until do
@@ -53,7 +57,12 @@ module RAutomation
53
57
 
54
58
  # @see RAutomation::TextField#exists?
55
59
  def exists?
56
- not Window.autoit.ControlGetHandle(@window.locator_hwnd, "", @autoit_locators).empty?
60
+ hwnd != 0
61
+ end
62
+
63
+ def hwnd
64
+ handle = Window.autoit.ControlGetHandle(@window.locator_hwnd, "", @autoit_locators)
65
+ handle.to_i(16)
57
66
  end
58
67
  end
59
68
  end
@@ -30,9 +30,9 @@ module RAutomation
30
30
  # @private
31
31
  # Special-cased locators
32
32
  LOCATORS = {
33
- [:title, Regexp] => :regexptitle,
34
- :index => :instance,
35
- :hwnd => :handle
33
+ [:title, Regexp] => :regexptitle,
34
+ :index => :instance,
35
+ :hwnd => :handle
36
36
  }
37
37
 
38
38
  # Creates the window object.
@@ -44,7 +44,8 @@ module RAutomation
44
44
  # @option locators [String, Fixnum] :hwnd Window handle in decimal format
45
45
  # @option locators [String, Fixnum] :index 0-based index to specify n-th window if all other criteria match
46
46
  # @see RAutomation::Window#initialize
47
- def initialize(locators)
47
+ def initialize(container, locators)
48
+ @container = container
48
49
  @hwnd = locators[:hwnd]
49
50
  @locator_index = locators.delete(:index) if locators[:index] && locators.size == 1
50
51
  @locator_pid = locators.delete(:pid).to_i if locators[:pid]
@@ -57,22 +58,22 @@ module RAutomation
57
58
  # @see RAutomation::Window#hwnd
58
59
  def hwnd
59
60
  @hwnd ||= begin
60
- locators = @autoit_locators
61
- if @locator_index || @locator_pid
62
- # @todo Come up with some better solution for this case
63
- locators = "[regexptitle:]" # match all, needed for the case when only :index or :pid is used
64
- end
65
- windows = @@autoit.WinList(locators, @locator_text).pop.compact.
66
- map {|handle| self.class.new(:hwnd => handle.hex)}
67
- windows.delete_if {|window| !window.visible?}
68
-
69
- if @locator_pid
70
- window = windows.find {|win| win.pid == @locator_pid}
71
- else
72
- window = windows[@locator_index || 0]
73
- end
74
- window ? window.hwnd : nil
75
- end
61
+ locators = @autoit_locators
62
+ if @locator_index || @locator_pid
63
+ # @todo Come up with some better solution for this case
64
+ locators = "[regexptitle:]" # match all, needed for the case when only :index or :pid is used
65
+ end
66
+ windows = @@autoit.WinList(locators, @locator_text).pop.compact.
67
+ map { |handle| RAutomation::Window.new(:hwnd => handle.hex) }
68
+ windows.delete_if { |window| !window.visible? }
69
+
70
+ if @locator_pid
71
+ window = windows.find { |win| win.pid == @locator_pid }
72
+ else
73
+ window = windows[@locator_index || 0]
74
+ end
75
+ window ? window.hwnd : nil
76
+ end
76
77
  end
77
78
 
78
79
  # @see RAutomation::Window#pid
@@ -85,6 +86,11 @@ module RAutomation
85
86
  @@autoit.WinGetTitle(locator_hwnd)
86
87
  end
87
88
 
89
+ # @see RAutomation::Window#class_names
90
+ def class_names
91
+ @@autoit.WinGetClassList(locator_hwnd).split("\n").sort
92
+ end
93
+
88
94
  # @see RAutomation::Window#activate
89
95
  def activate
90
96
  @@autoit.WinWait(locator_hwnd, "", 1)
@@ -145,7 +151,7 @@ module RAutomation
145
151
  activate
146
152
  active?
147
153
  end
148
- @@autoit.Send(keys)
154
+ @@autoit.Send(keys[0])
149
155
  end
150
156
 
151
157
  # @see RAutomation::Window#close
@@ -156,13 +162,13 @@ module RAutomation
156
162
 
157
163
  # @see Button#initialize
158
164
  # @see RAutomation::Window#button
159
- def button(locator)
165
+ def button(locator={})
160
166
  Button.new(self, locator)
161
167
  end
162
168
 
163
169
  # @see TextField#initialize
164
170
  # @see RAutomation::Window#text_field
165
- def text_field(locator)
171
+ def text_field(locator={})
166
172
  TextField.new(self, locator)
167
173
  end
168
174
 
@@ -174,6 +180,27 @@ module RAutomation
174
180
  @@autoit.send(name, *args)
175
181
  end
176
182
 
183
+ # AutoIt adapter specific API methods
184
+ def move_mouse(x_coord, y_coord)
185
+ @@autoit.MouseMove(x_coord, y_coord)
186
+ end
187
+
188
+ def mouse_position
189
+ [@@autoit.MouseGetPosX, @@autoit.MouseGetPosY]
190
+ end
191
+
192
+ def click_mouse(button = "left")
193
+ @@autoit.MouseClick(button)
194
+ end
195
+
196
+ def press_mouse(button = "left")
197
+ @@autoit.MouseDown(button)
198
+ end
199
+
200
+ def release_mouse(button = "left")
201
+ @@autoit.MouseUp(button)
202
+ end
203
+
177
204
  # @private
178
205
  def locator_hwnd
179
206
  "[HANDLE:#{hwnd.to_i.to_s(16)}]"
@@ -1,7 +1,8 @@
1
1
  module RAutomation
2
2
  module Adapter
3
3
  autoload :Autoit, File.dirname(__FILE__) + "/autoit.rb"
4
- autoload :WinFfi, File.dirname(__FILE__) + "/win_ffi.rb"
4
+ autoload :MsUia, File.dirname(__FILE__) + "/ms_uia.rb"
5
+ autoload :Win32, File.dirname(__FILE__) + "/win_32.rb"
5
6
 
6
7
  module Helper
7
8
  extend self
@@ -9,11 +10,10 @@ module RAutomation
9
10
  # @private
10
11
  # Retrieves default {Adapter} for the current platform.
11
12
  def default_adapter
12
- case RUBY_PLATFORM
13
- when /mswin|msys|mingw32/
14
- :win_ffi
15
- else
16
- raise "unsupported platform for RAutomation: #{RUBY_PLATFORM}"
13
+ if ENV['OS'] == 'Windows_NT'
14
+ :win_32
15
+ else
16
+ raise "unsupported platform for RAutomation: #{RUBY_PLATFORM}"
17
17
  end
18
18
  end
19
19
  end
@@ -0,0 +1,22 @@
1
+ begin
2
+ gem "ffi"
3
+ rescue Gem::LoadError
4
+ raise Gem::LoadError, "Unable to load FFI gem. Install it with:\n\tgem install ffi"
5
+ end
6
+ require "ffi"
7
+ require File.dirname(__FILE__) + "/ms_uia/constants"
8
+ require File.dirname(__FILE__) + "/ms_uia/keystroke_converter"
9
+ require File.dirname(__FILE__) + "/ms_uia/functions"
10
+ require File.dirname(__FILE__) + "/ms_uia/locators"
11
+ require File.dirname(__FILE__) + "/ms_uia/window"
12
+ require File.dirname(__FILE__) + "/ms_uia/button_helper"
13
+ require File.dirname(__FILE__) + "/ms_uia/control"
14
+ require File.dirname(__FILE__) + "/ms_uia/button"
15
+ require File.dirname(__FILE__) + "/ms_uia/checkbox"
16
+ require File.dirname(__FILE__) + "/ms_uia/radio"
17
+ require File.dirname(__FILE__) + "/ms_uia/text_field"
18
+ require File.dirname(__FILE__) + "/ms_uia/select_list"
19
+ require File.dirname(__FILE__) + "/ms_uia/table"
20
+ require File.dirname(__FILE__) + "/ms_uia/label"
21
+ require File.dirname(__FILE__) + "/ms_uia/list_box"
22
+ require File.dirname(__FILE__) + "/ms_uia/list_item"
@@ -1,6 +1,6 @@
1
1
  module RAutomation
2
2
  module Adapter
3
- module WinFfi
3
+ module MsUia
4
4
  class Button < Control
5
5
  include WaitHelper
6
6
  include Locators
@@ -8,13 +8,14 @@ module RAutomation
8
8
  # Default locators used for searching buttons.
9
9
  DEFAULT_LOCATORS = {:class => /button/i}
10
10
 
11
+ #todo - replace with UIA version
11
12
  # @see RAutomation::Button#value
12
13
  def value
13
14
  Functions.control_value(Functions.control_hwnd(@window.hwnd, @locators))
14
15
  end
15
16
 
16
17
  def exist?
17
- @locators[:id].nil? ? super : super && matches_type(Constants::UIA_BUTTON_CONTROL_TYPE)
18
+ super && matches_type?(Constants::UIA_BUTTON_CONTROL_TYPE)
18
19
  end
19
20
 
20
21
  alias_method :exists?, :exist?
@@ -0,0 +1,25 @@
1
+ module RAutomation
2
+ module Adapter
3
+ module MsUia
4
+ module ButtonHelper
5
+
6
+ #todo - replace with UIA version
7
+ def set?
8
+ control_hwnd = Functions.control_hwnd(@window.hwnd, @locators)
9
+ Functions.control_set? control_hwnd
10
+ end
11
+
12
+ # @todo call a windows function to do this without clicking
13
+ def clear
14
+ click {!set?} if set?
15
+ end
16
+
17
+ # @todo call a windows function to do this without clicking
18
+ def set
19
+ click {set?} unless set?
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,27 @@
1
+ module RAutomation
2
+ module Adapter
3
+ module MsUia
4
+ class Checkbox < Control
5
+ include WaitHelper
6
+ include Locators
7
+ include ButtonHelper
8
+
9
+ def value
10
+ hwnd = Functions.control_hwnd(@window.hwnd, @locators)
11
+ checkbox = UiaDll::element_from_handle(hwnd)
12
+
13
+ checkbox_value = FFI::MemoryPointer.new :char, UiaDll::get_name(checkbox, nil) + 1
14
+ UiaDll::get_name(checkbox, checkbox_value)
15
+ checkbox_value.read_string
16
+ end
17
+
18
+ def exist?
19
+ super && matches_type?(Constants::UIA_CHECKBOX_CONTROL_TYPE)
20
+ end
21
+
22
+ alias_method :exists?, :exist?
23
+
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,7 +1,6 @@
1
1
  module RAutomation
2
2
  module Adapter
3
- module WinFfi
4
- # @private
3
+ module MsUia
5
4
  module Constants
6
5
  WM_GETTEXT = 0xD
7
6
  WM_SETTEXT = 0xC
@@ -50,6 +49,8 @@ module RAutomation
50
49
  VK_NUMLOCK = 0x90
51
50
  VK_DELETE = 0x2E
52
51
  VK_INSERT = 0x2D
52
+ VK_NEXT = 0x22
53
+ VK_PRIOR = 0x21
53
54
 
54
55
  # GetWindow constants
55
56
  GW_ENABLEDPOPUP = 6
@@ -65,11 +66,23 @@ module RAutomation
65
66
 
66
67
  # Combobox
67
68
  CB_GETCOUNT = 0x0146
69
+ CB_GETTOPINDEX = 0x015b
68
70
  CB_GETLBTEXTLEN = 0x0149
69
71
  CB_GETLBTEXT = 0x0148
70
72
  CB_GETCURSEL = 0x0147
73
+ CB_GETDROPPEDCONTROLRECT = 0x0152
74
+ CB_GETITEMHEIGHT = 0x0154
71
75
  CB_ERR = -1
72
76
  CB_SETCURSEL = 0x14E
77
+ CB_SELECTSTRING = 0x14D
78
+ CB_SETEDITSEL = 0x142
79
+ CB_SETTOPINDEX = 0x015c
80
+
81
+ #ListBox
82
+ LB_SETTOPINDEX = 0x0197
83
+ LB_GETITEMHEIGHT = 0x01A1
84
+ LB_GETITEMRECT = 0x0198
85
+ LB_GETTOPINDEX = 0x018E
73
86
 
74
87
  # listview
75
88
  LVM_FIRST = 0x1000
@@ -78,15 +91,17 @@ module RAutomation
78
91
  # UI Automation control type IDs
79
92
  UIA_LIST_CONTROL_TYPE = 50008
80
93
  UIA_LIST_ITEM_CONTROL_TYPE = 50007
94
+ UIA_DATA_ITEM_CONTROL_TYPE =50029
81
95
  UIA_CHECKBOX_CONTROL_TYPE = 50002
82
96
  UIA_BUTTON_CONTROL_TYPE = 50000
83
- UIA_LABEL_CONTROL_TYPE = 50020
97
+ UIA_TEXT_CONTROL_TYPE = 50020
84
98
  UIA_RADIO_BUTTON_CONTROL_TYPE = 50013
85
99
  UIA_COMBOBOX_CONTROL_TYPE = 50003
86
100
  UIA_EDIT_CONTROL_TYPE = 50004
87
101
  UIA_HEADER_CONTROL_TYPE = 50034
88
- UIA_HEADER_ITEM_CONTROL_TYPE = 50035
89
-
102
+ UIA_HEADER_ITEM_CONTROL_TYPE = 50035
103
+ UIA_WINDOW_CONTROL_TYPE = 50032
104
+ UIA_PANE_CONTROL_TYPE = 50033
90
105
 
91
106
  end
92
107
  end
@@ -0,0 +1,180 @@
1
+ module RAutomation
2
+ module Adapter
3
+ module MsUia
4
+ class Control
5
+ include WaitHelper
6
+ include Locators
7
+
8
+ # Creates the control object.
9
+ # @note this method is not meant to be accessed directly
10
+ # @param [RAutomation::Window] window this button belongs to.
11
+ # @param [Hash] locators for searching the button.
12
+ # @option locators [String, Regexp] :value Value (text) of the button
13
+ # @option locators [String, Regexp] :class Internal class name of the button
14
+ # @option locators [String, Fixnum] :id Internal ID of the button
15
+ # @option locators [String, Fixnum] :index 0-based index to specify n-th button if all other criteria match
16
+ # @see RAutomation::Window#button
17
+ def initialize(window, locators)
18
+ @window = window
19
+ extract(locators)
20
+ end
21
+
22
+ #todo - replace with UIA version
23
+ def hwnd
24
+ Functions.control_hwnd(@window.hwnd, @locators)
25
+ end
26
+
27
+ def uia_element
28
+ # puts "finding element with #{@locators.inspect}"
29
+
30
+ case
31
+ #Causes a bug for some reason
32
+ # when @locators[:value]
33
+ # uia_window = UiaDll::element_from_handle(@window.hwnd)
34
+ # begin
35
+ # uia_window.read_pointer
36
+ # rescue FFI::NullPointerError => e
37
+ # raise UnknownElementException, "Window with handle #{@window.hwnd} does not exist"
38
+ # end
39
+ # uia_control = UiaDll::find_child_by_name(uia_window, @locators[:value].to_s)
40
+ # begin
41
+ # uia_control.read_pointer
42
+ # rescue FFI::NullPointerError => e
43
+ # raise UnknownElementException, "#{@locators[:value]} does not exist"
44
+ # end
45
+ when @locators[:focus]
46
+ uia_control = UiaDll::get_focused_element
47
+ begin
48
+ uia_control.read_pointer
49
+ rescue FFI::NullPointerError => e
50
+ raise UnknownElementException, "Focused element does not exist"
51
+ end
52
+ when @locators[:id]
53
+ uia_window = UiaDll::element_from_handle(@window.hwnd)
54
+ uia_control = UiaDll::find_child_by_id(uia_window, @locators[:id].to_s)
55
+ begin
56
+ uia_control.read_pointer
57
+ rescue FFI::NullPointerError => e
58
+ raise UnknownElementException, "#{@locators[:id]} does not exist"
59
+ end
60
+ when @locators[:point]
61
+ uia_control = UiaDll::element_from_point(@locators[:point][0], @locators[:point][1])
62
+ begin
63
+ uia_control.read_pointer
64
+ rescue FFI::NullPointerError => e
65
+ raise UnknownElementException, "#{@locators[:point]} does not exist"
66
+ end
67
+ else
68
+ handle= hwnd
69
+ raise UnknownElementException, "Element with #{@locators.inspect} does not exist" if (handle == 0) or (handle == nil)
70
+ uia_control = UiaDll::element_from_handle(handle)
71
+ end
72
+ uia_control
73
+ end
74
+
75
+
76
+ #todo - replace with UIA version
77
+ def click
78
+ assert_enabled
79
+ clicked = false
80
+ wait_until do
81
+ @window.activate
82
+ @window.active? &&
83
+ Functions.set_control_focus(hwnd) &&
84
+ Functions.control_click(hwnd) &&
85
+ clicked = true # is clicked at least once
86
+
87
+ block_given? ? yield : clicked && !exist?
88
+ end
89
+ end
90
+
91
+ def exist?
92
+ begin
93
+ !!uia_element
94
+ rescue UnknownElementException
95
+ false
96
+ end
97
+ end
98
+
99
+ def enabled?
100
+ !disabled?
101
+ end
102
+
103
+ #todo - replace with UIA version
104
+ def disabled?
105
+ Functions.unavailable?(hwnd)
106
+ end
107
+
108
+ #todo - replace with UIA version
109
+ def has_focus?
110
+ Functions.has_focus?(hwnd)
111
+ end
112
+
113
+ def set_focus
114
+ assert_enabled
115
+ uia_control = UiaDll::element_from_handle(hwnd)
116
+ UiaDll::set_focus(uia_control)
117
+ end
118
+
119
+ def bounding_rectangle
120
+ control = uia_element
121
+
122
+ boundary = FFI::MemoryPointer.new :long, 4
123
+ UiaDll::bounding_rectangle(control, boundary)
124
+
125
+ boundary.read_array_of_long(4)
126
+ end
127
+
128
+ def visible?
129
+ element = UiaDll::element_from_handle(hwnd)
130
+
131
+ off_screen = FFI::MemoryPointer.new :int
132
+
133
+ if UiaDll::is_offscreen(element, off_screen) == 0
134
+ fail "Could not check element"
135
+ end
136
+
137
+ # puts "return #{off_screen.read_int}"
138
+ if off_screen.read_int == 0
139
+ return true
140
+ end
141
+ false
142
+ end
143
+
144
+ def matches_type?(clazz)
145
+ get_current_control_type == clazz
146
+ end
147
+
148
+ def get_current_control_type
149
+ UiaDll::current_control_type(uia_element)
150
+ end
151
+
152
+ def new_pid
153
+ UiaDll::current_process_id(uia_element)
154
+ end
155
+
156
+ def control_name
157
+ uia_control = uia_element
158
+ element_name = FFI::MemoryPointer.new :char, UiaDll::get_name(uia_control, nil) + 1
159
+
160
+ UiaDll::get_name(uia_control, element_name)
161
+ element_name.read_string
162
+ end
163
+
164
+ def control_class
165
+ uia_control = uia_element
166
+ element_class = FFI::MemoryPointer.new :char, UiaDll::get_class_name(uia_control, nil) + 1
167
+
168
+ UiaDll::get_class_name(uia_control, element_class)
169
+ element_class.read_string
170
+ end
171
+
172
+ alias_method :exists?, :exist?
173
+
174
+ def assert_enabled
175
+ raise "Cannot interact with disabled control #{@locators.inspect} on window #{@window.locators.inspect}!" if disabled?
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end