wet-winobj 0.1-mswin32

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.
@@ -0,0 +1,537 @@
1
+ =begin license
2
+ Copyright (c) 2005, Qantom Software
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6
+
7
+ Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8
+ Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9
+ Neither the name of Qantom Software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
10
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
11
+
12
+ (based on BSD Open Source License)
13
+ =end
14
+ require 'timeout'
15
+ require 'wet/constants/testconstants.rb'
16
+ require 'wet/winobjects/WinUtils.rb'
17
+ require 'wet/utils/CompileUtils.rb'
18
+ require 'wet/exceptions/RuntimeExceptions.rb'
19
+ require 'wet/exceptions/CompileExceptions.rb'
20
+
21
+ module Wet
22
+ module Winobjects
23
+ =begin rdoc
24
+ Base class for all Win32 objects. All UI objects are considered
25
+ to be some sort of a window
26
+ =end
27
+ class Window
28
+
29
+ #Order _DOES_ matter. If you change the order in the following
30
+ #array, bad things _WILL_ happen
31
+ @@WinClasses = [ "WinEdit", "WinLabel", "WinComboBox", \
32
+ "WinCheckbox", "WinRadio", "WinButton"]
33
+
34
+
35
+ =begin rdoc
36
+ Constructor that accepts the window handle. Typically
37
+ you dont have to directly instantiate this class. Window.create()
38
+ will instantiate the right type of window for you.
39
+ =end
40
+ def initialize(hwnd)
41
+ @hwnd = hwnd
42
+ if @hwnd != -1
43
+ find_styles()
44
+ # It would have been nice to do a lazy loading - find the
45
+ #children when required. But when the children() method
46
+ # is called in quick succession, you hit the 'too many
47
+ # callbacks' problem. So do an early loading
48
+ @children = find_children()
49
+ end
50
+
51
+ end
52
+
53
+
54
+ =begin rdoc
55
+ Does this object represent a valid Win32 element that is being
56
+ displayed? Return true if yes, false otherwise
57
+ =end
58
+ def exists?()
59
+ return @hwnd != -1
60
+ end
61
+
62
+ =begin rdoc
63
+ Given a window's hwnd, this class level method
64
+ creates an appropriate WinObject
65
+
66
+ You usually dont have to call this method directly You
67
+ should use one of the helper methods like
68
+ Container(..).win_edit(...)
69
+ =end
70
+ def Window.create(hwnd)
71
+
72
+ tmp_element = Window.new(hwnd)
73
+ elem = nil
74
+ if hwnd != -1
75
+ @@WinClasses.each do |wc|
76
+ eval_string = "#{wc}::candidate_match?(tmp_element)"
77
+ is_match = eval eval_string
78
+ if is_match
79
+ str_cls = "#{wc}.new(#{hwnd})"
80
+ elem = eval str_cls
81
+ break
82
+ end
83
+ end
84
+ end
85
+
86
+ if elem == nil
87
+ elem = tmp_element
88
+ end
89
+ return elem
90
+ end
91
+
92
+ =begin rdoc
93
+ Returns a string representing the element's text. The text property
94
+ has a different meaning depending on the type of the object. For,
95
+ for a parent window the text is the title; For a button, the text is the
96
+ value shown on the button, etc.
97
+ =end
98
+ def text()
99
+ assert_object_exists
100
+ return window_caption(@hwnd)
101
+ end
102
+
103
+ =begin rdoc
104
+ Get the windows handle for this element
105
+ =end
106
+ def hwnd()
107
+ return @hwnd
108
+ end
109
+
110
+ =begin rdoc
111
+ Get the string representing the win32 class name for this element
112
+ =end
113
+ def win_class()
114
+ assert_object_exists
115
+ return class_name(@hwnd)
116
+ end
117
+
118
+
119
+ =begin rdoc
120
+ Emulate a click on this element
121
+ =end
122
+ def click()
123
+ assert_object_exists
124
+ return do_click(@hwnd)
125
+ end
126
+
127
+ =begin rdoc
128
+ Find all the child_objects of this window. If the optional
129
+ reqd_class parameter is non-nil, then only the WinElements
130
+ that are of the type of 'reqd_class' are returned.
131
+
132
+ parameters = reqd_class (optional) - Should be an instance object
133
+ of the Class class
134
+ return An array of WinElements that match the 'reqd_class'
135
+ criteria. If the reqd_class is nil, then all the children of this window
136
+ are returned.
137
+ =end
138
+ def child_objects(reqd_class = nil)
139
+ assert_object_exists
140
+ reqd_obj = []
141
+ if reqd_class == nil
142
+ reqd_obj = @children
143
+ else
144
+ @children.each do |c|
145
+ if c.kind_of?(reqd_class)
146
+ reqd_obj << c
147
+ end
148
+ end
149
+ end
150
+ return reqd_obj
151
+ end
152
+
153
+ =begin rdoc
154
+ Check if the win element exists. If it doesn't then a
155
+ ObjectNotFoundException is rasied
156
+ =end
157
+ def assert_object_exists()
158
+ if @hwnd == -1
159
+ raise ObjectNotFoundException.new("cannot find #{win_class}")
160
+ end
161
+ end
162
+
163
+ =begin rdoc
164
+ Is the current window a parent object? Return true
165
+ if yes, false if not.
166
+ =end
167
+ def is_parent()
168
+ assert_object_exists
169
+ return !@is_child
170
+ end
171
+
172
+ =begin rdoc
173
+ Does this element have a tab stop? If an element has a
174
+ tab stop, then when you keep pressing the tab button, at
175
+ some stage the focus will stop at this control. If this property
176
+ is not set, then the element wont receive focus when you keep
177
+ pressing tab.
178
+ =end
179
+ def is_tab_stop()
180
+ assert_object_exists
181
+ return @is_tab_stop
182
+ end
183
+
184
+ =begin rdoc
185
+ Get if the passed style has been defined for the Window or
186
+ not. The following styles are understood :
187
+ =end
188
+ def is_style(style)
189
+ assert_object_exists
190
+ find_styles()
191
+ eval_string = "@is_#{style}"
192
+ ret_val = eval eval_string
193
+ return ret_val
194
+ end
195
+
196
+ =begin rdoc
197
+ Is this control being used to display text? if yes,
198
+ what is it
199
+ This method is to be overriden by controls that intend
200
+ to display text
201
+ =end
202
+ def display_text()
203
+ assert_object_exists
204
+ return ""
205
+ end
206
+
207
+ def to_s()
208
+ str = "Window(class:="
209
+ str = str + win_class()
210
+ str = str + ")"
211
+ return str
212
+ end
213
+
214
+ ## The following methods are interfaces to the scripting world
215
+ ## The different window objects are retreived in a style such that
216
+ ## the script looks object oriented
217
+ =begin rdoc
218
+ Get a Label element that is a child of the current window.
219
+ Label's are the Static text elements that are usually used either to
220
+ identify another control or simply display some text.
221
+
222
+ The props parameter is a hashtable of expected properties' name / value.
223
+
224
+ In the current version the following properties can be used:
225
+ 1) 'text' - The caption of the label.
226
+ The value for text can either be a literal string or a regex.
227
+
228
+ eg:-
229
+ If you say myWindow.win_label("text" => "Are you sure"), the the method
230
+ will return the WinLabel element whose caption is 'Are you sure'. If no such
231
+ element is found, the method returns an 'empty' WinLabel
232
+ =end
233
+ def win_label(props)
234
+ assert_object_exists
235
+ return find_element(props, "WinLabel", ["text"])
236
+ end
237
+
238
+ =begin rdoc
239
+ Get a WinEdit element that is a child of the current window.
240
+ WinEdits are nothing but TextFields
241
+
242
+ The props parameter is a hashtable of expected properties' name / value.
243
+
244
+ In the current version the following properties can be used:
245
+ 1) 'label' - The caption of the WinLabel element that is associated with this
246
+ control. Most WinEdit elements have a Static element beside the Edit that
247
+ describes what should be entered in the Edit box. This static element is
248
+ considered to be the label for this WinEdit
249
+ The value for label can either be a literal string or a regex.
250
+
251
+ eg:-
252
+ If you say myWindow.win_edit("label" => "&Enter your name"), then the method
253
+ will return the WinEdit element whose label 'Enter your name'. If no such
254
+ element is found, the method returns an 'empty' WinEdit
255
+ =end
256
+ def win_edit(props)
257
+ assert_object_exists
258
+ return find_element(props, "WinEdit", ["label"])
259
+ end
260
+
261
+
262
+ =begin rdoc
263
+ Get a WinCombobox element that is a child of the current window.
264
+
265
+ The props parameter is a hashtable of expected properties' name / value.
266
+
267
+ In the current version the following properties can be used:
268
+ 1) 'label' - The caption of the WinLabel element that is associated with this
269
+ control. Most WinComboboxes elements have a Static element beside thecontrol that
270
+ describes what should be entered in the Combo box. This static element is
271
+ considered to be the label for this control
272
+ The value for label can either be a literal string or a regex.
273
+
274
+ eg:-
275
+ If you say myWindow.win_combobox("label" => "&select your age"), then the method
276
+ will return the WinCombobox element whose label 'select your age'. If no such
277
+ element is found, the method returns an 'empty' WinEdit
278
+ =end
279
+ def win_combobox(props)
280
+ assert_object_exists
281
+ return find_element(props, "WinComboBox", ["label"])
282
+ end
283
+
284
+ =begin rdoc
285
+ Get a WinCheckbox element that is a child of the current window.
286
+
287
+ The props parameter is a hashtable of expected properties' name / value.
288
+
289
+ In the current version the following properties can be used:
290
+ 1) 'text' - The text displayed for the WinCheckbox
291
+ The value for text can either be a literal string or a regex.
292
+
293
+ eg:-
294
+ If you say myWindow.win_checkbox("label" => "I agree"), then the method
295
+ will return the WinCheckbox element whose text is 'I agree'. If no such
296
+ element is found, the method returns an 'empty' WinCheckbox
297
+ =end
298
+ def win_checkbox(props)
299
+ assert_object_exists
300
+ return find_element(props, "WinCheckbox", ["text"])
301
+ end
302
+
303
+ =begin rdoc
304
+ Get a WinRadio element that is a child of the current window.
305
+
306
+ The props parameter is a hashtable of expected properties' name / value.
307
+
308
+ In the current version the following properties can be used:
309
+ 1) 'text' - The text displayed for the WinRadio
310
+ The value for text can either be a literal string or a regex.
311
+
312
+ eg:-
313
+ If you say myWindow.win_radio("text" => "10 to 100"), then the method
314
+ will return the WinRadio element whose text is '10 to 100'. If no such
315
+ element is found, the method returns an 'empty' WinRadio
316
+ =end
317
+ def win_radio(props)
318
+ assert_object_exists
319
+ return find_element(props, "WinRadio", ["text"])
320
+ end
321
+
322
+
323
+ =begin rdoc
324
+ Get a WinButton element that is a child of the current window.
325
+
326
+ The props parameter is a hashtable of expected properties' name / value.
327
+
328
+ In the current version the following properties can be used:
329
+ 1) 'text' - The text displayed for the WinButton
330
+ The value for text can either be a literal string or a regex.
331
+
332
+ eg:-
333
+ If you say myWindow.win_button("text" => "OK"), then the method
334
+ will return the WinButton element whose text is 'OK'. If no such
335
+ element is found, the method returns an 'empty' WinButton
336
+ =end
337
+ def win_button(props)
338
+ assert_object_exists
339
+ return find_element(props, "WinButton", ["text"])
340
+ end
341
+
342
+ =begin rdoc
343
+ =end
344
+ def debug()
345
+ puts "#{to_s()}\n----- Styles ------------"
346
+
347
+ puts "------Window type---------------"
348
+ ["overlapped", "popup", "child"].each do |s|
349
+ puts "#{s} = #{is_style(s)}"
350
+ end
351
+
352
+
353
+ puts "----- Window visual properties----"
354
+ [ "border", "dlg_frame", "maximizable", "minimizable", \
355
+ "v_scroll", "h_scroll", "sysmenu", "sizable" ].each do |s|
356
+ puts "#{s} = #{is_style(s)}"
357
+ end
358
+
359
+ puts "------ current window state-------"
360
+ ["maximized", "minimized", "visible", "disabled"].each do |s|
361
+ puts "#{s} = #{is_style(s)}"
362
+ end
363
+
364
+ puts "------- other properties --------"
365
+ ["tab_stop"].each do |s|
366
+ puts "#{s} = #{is_style(s)}"
367
+ end
368
+
369
+ puts " --------------- End Style -------------------- "
370
+ end
371
+
372
+ protected
373
+ =begin rdoc
374
+ If this element has another static text as its label,
375
+ that needs to be set
376
+ =end
377
+ def set_label(l)
378
+ assert_object_exists
379
+ @label_element = l
380
+ end
381
+
382
+ =begin rdoc
383
+ What is the label for the element? This is not valid
384
+ for all types of element. Classes for whom this matter should
385
+ override this method.
386
+ =end
387
+ def label()
388
+ assert_object_exists
389
+ lbl = nil
390
+ if @label_element != nil
391
+ lbl = @label_element.text
392
+ end
393
+ return lbl
394
+ end
395
+
396
+ protected
397
+ def get_win_style()
398
+ find_styles()
399
+ return @control_style
400
+ end
401
+
402
+ private
403
+ def find_styles()
404
+
405
+ control_style = get_window_style(@hwnd)
406
+ @control_style = control_style
407
+ #Styles that the determine the type of wiindow
408
+ @is_overlapped = control_style & WS_OVERLAPPED == WS_OVERLAPPED
409
+ @is_popup = control_style & WS_POPUP == WS_POPUP
410
+ @is_child = control_style & WS_CHILD == WS_CHILD
411
+
412
+ #Styles to determine the physical appearance of the window.
413
+ @is_border = control_style & WS_BORDER == WS_BORDER
414
+ @is_dlg_frame = control_style & WS_DLGFRAME == WS_DLGFRAME
415
+ @is_v_scroll = control_style & WS_VSCROLL == WS_VSCROLL
416
+ @is_h_scroll = control_style & WS_HSCROLL == WS_HSCROLL
417
+ @is_maximizable = control_style & WS_MAXIMIZEBOX == WS_MAXIMIZEBOX
418
+ @is_minimizable = control_style & WS_MINIMIZEBOX == WS_MINIMIZEBOX
419
+ @is_sysmenu = control_style & WS_SYSMENU == WS_SYSMENU
420
+ @is_sizable = control_style & WS_THICKFRAME == WS_THICKFRAME
421
+
422
+
423
+ #Styles that determine the current state of the window
424
+ @is_minimized = control_style & WS_MINIMIZE == WS_MINIMIZE
425
+ @is_maximized = control_style & WS_MAXIMIZE == WS_MAXIMIZE
426
+ @is_visible = control_style & WS_VISIBLE == WS_VISIBLE
427
+ @is_disabled = control_style & WS_DISABLED == WS_DISABLED
428
+
429
+ # Does circulating through the tab button, stop at this control at any stage?
430
+ @is_tab_stop = control_style & WS_TABSTOP == WS_TABSTOP
431
+
432
+
433
+ end
434
+
435
+ def find_children()
436
+ hwnd = @hwnd
437
+ elems = children(hwnd)
438
+ mark_for_deletion=[]
439
+ elems.each do |c|
440
+ win_class = class_name(c)
441
+ if TextUtils.compare_text(win_class, /^Combo/)
442
+ gc_count=0
443
+ gc = children(c)
444
+ gc_count = gc.length
445
+ gc.each do |c2|
446
+ mark_for_deletion << c2
447
+ end
448
+ end
449
+ end
450
+
451
+
452
+ mark_for_deletion.each do |m|
453
+ elems.delete(m)
454
+ end
455
+
456
+ all_children=[]
457
+ all_labelled = []
458
+
459
+ #Even though it may take slightly longer, it
460
+ #definitely is much less buggier to loop through
461
+ #the elements multiple times
462
+ elems.each do |c|
463
+ elem = Window::create(c)
464
+ #keep track of those windows are labels for others
465
+
466
+ if elem.instance_of?(WinLabel)
467
+ labelled = elem.labelled_control()
468
+ if labelled != nil && labelled.exists?()
469
+ all_labelled << labelled
470
+ end
471
+ end
472
+ end
473
+
474
+ elems.each do |c|
475
+ mark_for_delete = []
476
+ elem = Window::create(c)
477
+ elem_to_add = nil
478
+
479
+ all_labelled.each do |l|
480
+ if l.hwnd == c
481
+ mark_for_delete << l
482
+ if elem_to_add == nil
483
+ elem_to_add = l
484
+ break
485
+ end
486
+ end
487
+ end
488
+
489
+ mark_for_delete.each do |m|
490
+ all_labelled.delete(m)
491
+ end
492
+
493
+ if elem_to_add == nil
494
+ elem_to_add = elem
495
+ end
496
+ all_children << elem_to_add
497
+ end
498
+ return all_children
499
+ end
500
+
501
+ ## Helper method used by the accessors.
502
+ def find_element(props, reqd_class, prop_names)
503
+ #prop_names = ["text"]
504
+ #reqd_class = "WinLabel"
505
+ #in this version only one property is allowd
506
+ def_prop = prop_names[0]
507
+
508
+ CompileUtils.assert_argument_types(Hash, props.class)
509
+ CompileUtils.assert_argument_types(Array, prop_names.class)
510
+
511
+ found_elem = eval "#{reqd_class}.new(-1)"
512
+
513
+ if props.length == 1 && props[def_prop] != nil
514
+ exp_text = props[def_prop]
515
+
516
+ reqd_elems = child_objects(found_elem.class)
517
+ reqd_elems.each do |e|
518
+ eval_string = "e.#{def_prop}"
519
+ act_text = eval eval_string
520
+ if TextUtils.compare_text(act_text, exp_text)
521
+ found_elem = e
522
+ break
523
+ else
524
+ #puts "reqd=#{exp_text}; act=#{act_text}"
525
+ end
526
+ end
527
+ else
528
+ raise UnsupportedOperationException.new("Limited search options available now")
529
+ end
530
+ return found_elem
531
+ end
532
+
533
+ end
534
+ end
535
+ end
536
+
537
+