win 0.3.17 → 0.3.24

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.
@@ -7,7 +7,7 @@ module Win
7
7
  # Below is a table of system-defined message prefixes:
8
8
  #
9
9
  # *Prefix*:: *Message* *category*
10
- # ABM:: Application desktop toolbar
10
+ # ABM:: App desktop toolbar
11
11
  # BM:: Button control
12
12
  # CB:: Combo box control
13
13
  # CBEM:: Extended combo box control
@@ -157,7 +157,7 @@ module Win
157
157
  WM_IME_KEYLAST = 0x010F
158
158
  WM_INITDIALOG = 0x0110
159
159
  WM_COMMAND = 0x0111
160
- # Windows Message Sys Command
160
+ # Windows Message System Command (emitted by Window/System menu)
161
161
  WM_SYSCOMMAND = 0x0112
162
162
  WM_TIMER = 0x0113
163
163
  WM_HSCROLL = 0x0114
@@ -337,22 +337,22 @@ module Win
337
337
 
338
338
  # The MSG structure contains message information from a thread's message queue.
339
339
  #
340
- # typedef struct {
341
- # HWND hwnd;
342
- # UINT message;
343
- # WPARAM wParam;
344
- # LPARAM lParam;
345
- # DWORD time;
346
- # POINT pt;
347
- # } MSG, *PMSG;
348
- #
349
- # hwnd:: Handle to the window whose window procedure receives the message. NULL when the message is a thread message.
350
- # message:: Message identifier. Applications can only use the low word; the high word is reserved by the system.
351
- # wParam:: Additional info about the message. Exact meaning depends on the value of the message member.
352
- # lParam:: Additional info about the message. Exact meaning depends on the value of the message member.
353
- # time:: Specifies the time at which the message was posted.
354
- # pt:: POINT structure - the cursor position, in screen coordinates, when the message was posted.
355
- # (in my definition, it is changed to two longs: x, y - has the same effect, just avoid nested structs)
340
+ # typedef struct {
341
+ # HWND hwnd;
342
+ # UINT message;
343
+ # WPARAM wParam;
344
+ # LPARAM lParam;
345
+ # DWORD time;
346
+ # POINT pt;
347
+ # } MSG, *PMSG;
348
+ #
349
+ # hwnd:: Handle to the window whose window procedure receives the message. NULL when the message is a thread message.
350
+ # message:: Message identifier. Apps can only use the low word; the high word is reserved by the system.
351
+ # wParam:: Additional info about the message. Exact meaning depends on the value of the message member.
352
+ # lParam:: Additional info about the message. Exact meaning depends on the value of the message member.
353
+ # time:: Specifies the time at which the message was posted.
354
+ # pt:: POINT structure - the cursor position, in screen coordinates, when the message was posted.
355
+ # (in my definition, it is changed to two longs: x, y - has the same effect, just avoid nested structs)
356
356
  class Msg < FFI::Struct
357
357
  layout :hwnd, :ulong,
358
358
  :message, :uint,
@@ -364,9 +364,9 @@ module Win
364
364
  end
365
365
 
366
366
  ##
367
- # The SendAsyncProc function is an application-defined callback function used with the SendMessageCallback
367
+ # The SendAsyncProc function is an App-defined callback function used with the SendMessageCallback
368
368
  # function. The system passes the message to the callback function after passing the message to the
369
- # destination window procedure. SendAsyncProc is a placeholder for the application-defined function name.
369
+ # destination window procedure. SendAsyncProc is a placeholder for the App-defined function name.
370
370
  #
371
371
  # [*Syntax*] VOID SendAsyncProc( HWND hwnd, UINT uMsg, ULONG_PTR dwData, LRESULT lResult );
372
372
  #
@@ -374,7 +374,7 @@ module Win
374
374
  # function was called with its hwnd parameter set to HWND_BROADCAST, the system calls the
375
375
  # SendAsyncProc function once for each top-level window.
376
376
  # uMsg:: <in> Specifies the message.
377
- # dwData:: <in> Specifies an application-defined value sent from the SendMessageCallback function.
377
+ # dwData:: <in> Specifies an App-defined value sent from the SendMessageCallback function.
378
378
  # lResult:: <in> Specifies the result of the message processing. This value depends on the message.
379
379
  #
380
380
  # :call-seq:
@@ -386,7 +386,7 @@ module Win
386
386
  # The SendMessageCallback function sends the specified message to a window or windows. It calls the window
387
387
  # procedure for the specified window and returns immediately. After the window procedure processes the message,
388
388
  # the system calls the specified callback function, passing the result of the message processing and an
389
- # application-defined value to the callback function.
389
+ # App-defined value to the callback function.
390
390
  #
391
391
  # [*Syntax*] BOOL SendMessageCallback( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam,
392
392
  # SENDASYNCPROC lpCallBack, ULONG_PTR dwData);
@@ -400,7 +400,7 @@ module Win
400
400
  # lpCallBack:: <in> Pointer to a callback function that the system calls after the window procedure processes
401
401
  # the message. For more information, see SendAsyncProc. If hWnd is HWND_BROADCAST, the system calls
402
402
  # SendAsyncProc callback function once for each top-level window.
403
- # dwData:: <in> Specifies an application-defined value to be sent to the callback function pointed to by
403
+ # dwData:: <in> Specifies an App-defined value to be sent to the callback function pointed to by
404
404
  # the lpCallBack parameter.
405
405
  # *Returns*:: Nonzero if the function succeeds, zero if it fails. For extended error info, call GetLastError.
406
406
  # ---
@@ -409,8 +409,8 @@ module Win
409
409
  # SendNotifyMessage, and SendMessageCallback), its message parameters cannot include pointers. Otherwise,
410
410
  # the operation will fail. The functions will return before the receiving thread has had a chance
411
411
  # to process the message and the sender will free the memory before it is used.
412
- # - Applications that need to communicate using HWND_BROADCAST should use the RegisterWindowMessage function
413
- # to obtain a unique message for inter-application communication.
412
+ # - Apps that need to communicate using HWND_BROADCAST should use the RegisterWindowMessage function
413
+ # to obtain a unique message for inter-App communication.
414
414
  # - The system only does marshalling for system messages (those in the range 0 to (WM_USER-1)). To send
415
415
  # messages (>= WM_USER) to another process, you must do custom marshalling.
416
416
  # - The callback function is called only when the thread that called SendMessageCallback also calls GetMessage,
@@ -448,8 +448,8 @@ module Win
448
448
  # - Microsoft Windows Vista and later. When a message is blocked by UIPI the last error, retrieved with
449
449
  # GetLastError, is set to 5 (access denied). Messages in a message queue are retrieved by calls to the
450
450
  # GetMessage or PeekMessage function.
451
- # - Applications that need to communicate using HWND_BROADCAST should use the RegisterWindowMessage function
452
- # to obtain a unique message for inter-application communication.
451
+ # - Apps that need to communicate using HWND_BROADCAST should use the RegisterWindowMessage function
452
+ # to obtain a unique message for inter-App communication.
453
453
  # - The system only does marshalling for system messages (those in the range 0 to (WM_USER-1)). To send other
454
454
  # messages (those >= WM_USER) to another process, you must do custom marshalling.
455
455
  # - If you send a message in the range below WM_USER to the asynchronous message functions (PostMessage,
@@ -492,8 +492,8 @@ module Win
492
492
  # *Remarks*:
493
493
  # - Microsoft Windows Vista and later. When a message is blocked by UIPI the last error, retrieved with
494
494
  # GetLastError, is set to 5 (access denied).
495
- # - Applications that need to communicate using HWND_BROADCAST should use the RegisterWindowMessage function
496
- # to obtain a unique message for inter-application communication.
495
+ # - Apps that need to communicate using HWND_BROADCAST should use the RegisterWindowMessage function
496
+ # to obtain a unique message for inter-App communication.
497
497
  # - The system only does marshalling for system messages (those in the range 0 to (WM_USER-1)). To send other
498
498
  # messages (those >= WM_USER) to another process, you must do custom marshalling.
499
499
  # - If the specified window was created by the calling thread, the window procedure is called immediately as
@@ -549,7 +549,7 @@ module Win
549
549
  # *Warning*
550
550
  # Because the return value can be nonzero, zero, or -1, avoid code like this:
551
551
  # while (GetMessage( lpMsg, hWnd, 0, 0)) ...
552
- # The possibility of a -1 return value means that such code can lead to fatal application errors.
552
+ # The possibility of a -1 return value means that such code can lead to fatal App errors.
553
553
  # Instead, use code like this:
554
554
  # while( (bRet = GetMessage( msg, hWnd, 0, 0 )) != 0)
555
555
  # if (bRet == -1)
@@ -561,12 +561,12 @@ module Win
561
561
  # end
562
562
  # ---
563
563
  # *Remarks*:
564
- # An application typically uses the return value to determine whether to end the main message loop and
564
+ # An App typically uses the return value to determine whether to end the main message loop and
565
565
  # exit the program.
566
566
  #
567
567
  # The GetMessage function retrieves messages associated with the window identified by the hWnd parameter
568
568
  # or any of its children, as specified by the IsChild function, and within the range of message values
569
- # given by the wMsgFilterMin and wMsgFilterMax parameters. Note that an application can only use the low
569
+ # given by the wMsgFilterMin and wMsgFilterMax parameters. Note that an App can only use the low
570
570
  # word in the wMsgFilterMin and wMsgFilterMax parameters; the high word is reserved for the system.
571
571
  # Note that GetMessage always retrieves WM_QUIT messages, no matter which values you specify for
572
572
  # wMsgFilterMin and wMsgFilterMax.
@@ -590,7 +590,7 @@ module Win
590
590
  # Windows XP: If a top-level window stops responding to messages for more than several seconds, the
591
591
  # system considers the window to be not responding and replaces it with a ghost window that has the same
592
592
  # z-order, location, size, and visual attributes. This allows the user to move it, resize it, or even
593
- # close the application. However, these are the only actions available because the application is
593
+ # close the App. However, these are the only actions available because the App is
594
594
  # actually not responding. When in the debugger mode, the system does not generate a ghost window.
595
595
  # ---
596
596
  # <b>Enhanced (snake_case) API: makes all args optional, returns: *false* if WM_QUIT was posted,
@@ -650,7 +650,7 @@ module Win
650
650
  # *Remarks*:
651
651
  # PeekMessage retrieves messages associated with the window identified by the hWnd parameter or any of
652
652
  # its children as specified by the IsChild function, and within the range of message values given by the
653
- # wMsgFilterMin and wMsgFilterMax parameters. Note that an application can only use the low word in the
653
+ # wMsgFilterMin and wMsgFilterMax parameters. Note that an App can only use the low word in the
654
654
  # wMsgFilterMin and wMsgFilterMax parameters; the high word is reserved for the system.
655
655
  #
656
656
  # Note that PeekMessage always retrieves WM_QUIT messages, no matter which values you specify for
@@ -677,8 +677,8 @@ module Win
677
677
  # Windows XP: If a top-level window stops responding to messages for more than several seconds, the
678
678
  # system considers the window to be not responding and replaces it with a ghost window that has the same
679
679
  # z-order, location, size, and visual attributes. This allows the user to move it, resize it, or even
680
- # close the application. However, these are the only actions available because the application is
681
- # actually not responding. When an application is being debugged, the system does not generate a ghost
680
+ # close the App. However, these are the only actions available because the App is
681
+ # actually not responding. When an App is being debugged, the system does not generate a ghost
682
682
  # window.
683
683
  # ---
684
684
  # <b>Enhanced (snake_case) API: makes all args optional, returns *nil* if no message in queue,
@@ -718,12 +718,12 @@ module Win
718
718
  # TranslateMessage produces WM_CHAR messages only for keys that are mapped to ASCII characters by the
719
719
  # keyboard driver.
720
720
  #
721
- # If applications process virtual-key messages for some other purpose, they should not call
722
- # TranslateMessage. For instance, an application should not call TranslateMessage if the
723
- # TranslateAccelerator function returns a nonzero value. Note that the application is responsible for
724
- # retrieving and dispatching input messages to the dialog box. Most applications use the main message
721
+ # If Apps process virtual-key messages for some other purpose, they should not call
722
+ # TranslateMessage. For instance, an App should not call TranslateMessage if the
723
+ # TranslateAccelerator function returns a nonzero value. Note that the App is responsible for
724
+ # retrieving and dispatching input messages to the dialog box. Most Apps use the main message
725
725
  # loop for this. However, to permit the user to move to and to select controls by using the keyboard,
726
- # the application must call IsDialogMessage. For more information, see Dialog Box Keyboard Interface.
726
+ # the App must call IsDialogMessage. For more information, see Dialog Box Keyboard Interface.
727
727
  # ---
728
728
  # <b>Enhanced (snake_case) API: returns true/false instead of nonzero/zero</b>
729
729
  #
@@ -748,9 +748,9 @@ module Win
748
748
  # message and the lParam parameter of the WM_TIMER message is not NULL, lParam points to a function that
749
749
  # is called instead of the window procedure.
750
750
  #
751
- # Note that the application is responsible for retrieving and dispatching input messages to the dialog
752
- # box. Most applications use the main message loop for this. However, to permit the user to move to and
753
- # to select controls by using the keyboard, the application must call IsDialogMessage. For more
751
+ # Note that the App is responsible for retrieving and dispatching input messages to the dialog
752
+ # box. Most Apps use the main message loop for this. However, to permit the user to move to and
753
+ # to select controls by using the keyboard, the App must call IsDialogMessage. For more
754
754
  # information, see Dialog Box Keyboard Interface.
755
755
  # ---
756
756
  # <b>Enhanced (snake_case) API: </b>
@@ -229,7 +229,7 @@ module Win
229
229
  # :call-seq:
230
230
  # win_handle = find_window( class_name, win_name )
231
231
  #
232
- function :FindWindow, [:pointer, :pointer], :HWND, zeronil: true
232
+ function :FindWindow, [:pointer, :pointer], :HWND, fails: 0
233
233
 
234
234
  ##
235
235
  # Unicode version of FindWindow (strings must be encoded as utf-16LE AND terminate with "\x00\x00")
@@ -237,7 +237,7 @@ module Win
237
237
  # :call-seq:
238
238
  # win_handle = find_window_w( class_name, win_name )
239
239
  #
240
- function :FindWindowW, [:pointer, :pointer], :HWND, zeronil: true
240
+ function :FindWindowW, [:pointer, :pointer], :HWND, fails: 0
241
241
 
242
242
  ##
243
243
  # The FindWindowEx function retrieves a handle to a window whose class name and window name match the specified
@@ -284,7 +284,7 @@ module Win
284
284
  # :call-seq:
285
285
  # win_handle = find_window_ex( win_handle, after_child, class_name, win_name )
286
286
  #
287
- function :FindWindowEx, [:HWND, :HWND, :pointer, :pointer], :HWND, zeronil: true
287
+ function :FindWindowEx, [:HWND, :HWND, :pointer, :pointer], :HWND, fails: 0
288
288
 
289
289
  ##
290
290
  # GetWindowText returns the text of the specified window's title bar (if it has one).
@@ -632,7 +632,7 @@ module Win
632
632
  #:call-seq:
633
633
  # win_handle = [get_]foreground_window()
634
634
  #
635
- function :GetForegroundWindow, [], :HWND, zeronil: true
635
+ function :GetForegroundWindow, [], :HWND, fails: 0
636
636
 
637
637
  ##
638
638
  # SetForegroundWindow function puts the thread that created the specified window into the foreground
@@ -705,7 +705,7 @@ module Win
705
705
  #:call-seq:
706
706
  # win_handle = [get_]active_window()
707
707
  #
708
- function :GetActiveWindow, [], :HWND, zeronil: true
708
+ function :GetActiveWindow, [], :HWND, fails: 0
709
709
 
710
710
  ##
711
711
  # The GetWindow function retrieves a handle to a window that has the specified relationship (Z-Order or
@@ -762,7 +762,7 @@ module Win
762
762
  # :call-seq:
763
763
  # window_handle = get_window(h_wnd, u_cmd)
764
764
  #
765
- function :GetWindow, [:HWND, :UINT], :HWND, zeronil: true
765
+ function :GetWindow, [:HWND, :UINT], :HWND, fails: 0
766
766
 
767
767
  ##
768
768
  # The GetParent function retrieves a handle to the specified window's parent OR OWNER.
@@ -788,7 +788,7 @@ module Win
788
788
  # :call-seq:
789
789
  # parent = get_parent(h_wnd)
790
790
  #
791
- function :GetParent, [:HWND], :HWND, zeronil: true
791
+ function :GetParent, [:HWND], :HWND, fails: 0
792
792
 
793
793
  ##
794
794
  # The GetAncestor function retrieves the handle to the ancestor of the specified window.
@@ -810,7 +810,7 @@ module Win
810
810
  # :call-seq:
811
811
  # ancestor = get_ancestor(hwnd, ga_flags)
812
812
  #
813
- function :GetAncestor, [:HWND, :UINT], :HWND, zeronil: true
813
+ function :GetAncestor, [:HWND, :UINT], :HWND, fails: 0
814
814
 
815
815
 
816
816
  # Convenience wrapper methods:
data/lib/win/library.rb CHANGED
@@ -281,7 +281,7 @@ module Win
281
281
  # :camel_only:: If true, no snake_case method is defined
282
282
  # :alias(es):: Provides additional alias(es) for defined method
283
283
  # :boolean:: Forces method to return true/false instead of nonzero/zero
284
- # :zeronil:: Forces method to return nil if function result is zero
284
+ # :fails:: Forces method to return nil if function result is equal to following error code
285
285
  #
286
286
  def function(name, params, returns, options={}, &def_block)
287
287
  snake_name, camel_name, effective_names, aliases = generate_names(name, options)
@@ -394,20 +394,20 @@ module Win
394
394
  end
395
395
 
396
396
  # Generates body for snake_case method according to directives contained in options
397
- # options (:boolean, :zeronil) currently supported
397
+ # options (:boolean, :fails) currently supported
398
398
  #
399
399
  def generate_snake_method_body(api, options, &def_block)
400
400
  if def_block
401
- if options[:zeronil]
402
- ->(*args, &block){ (res = def_block.(api, *args, &block)) != 0 ? res : nil }
401
+ if options[:fails]
402
+ ->(*args, &block){ (res = def_block.(api, *args, &block)) == options[:fails] ? nil: res }
403
403
  elsif options[:boolean]
404
404
  ->(*args, &block){ def_block.(api, *args, &block) != 0 }
405
405
  else
406
406
  ->(*args, &block){ def_block.(api, *args, &block) }
407
407
  end
408
408
  else
409
- if options[:zeronil]
410
- ->(*args, &block){ (res = block ? block[api.call(*args)] : api.call(*args)) != 0 ? res : nil }
409
+ if options[:fails]
410
+ ->(*args, &block){ (res = block ? block[api.call(*args)] : api.call(*args)) == options[:fails] ? nil : res }
411
411
  elsif options[:boolean]
412
412
  ->(*args, &block){ block ? block[api.call(*args)] : api.call(*args) != 0 }
413
413
  else
data/spec/spec_helper.rb CHANGED
@@ -83,7 +83,7 @@ module WinTest
83
83
  IMPOSSIBLE = 'Impossible'
84
84
  CONVERSION_ERROR = /Can.t convert/
85
85
  SLEEP_DELAY = 0.02
86
- APP_PATH = File.join(File.dirname(__FILE__), "test_apps/locknote/LockNote.exe" )
86
+ APP_PATH = File.join(File.dirname(__FILE__), "../misc/locknote/LockNote.exe" )
87
87
  APP_START = cygwin? ? "cmd /c start `cygpath -w #{APP_PATH}`" : 'start "" "' + APP_PATH + '"'
88
88
  WIN_TITLE = 'LockNote - Steganos LockNote'
89
89
  WIN_RECT = [710, 400, 1210, 800]
@@ -123,7 +123,9 @@ module WinTestApp
123
123
  textarea = find_window_ex(handle, 0, TEXTAREA_CLASS, nil)
124
124
  app = "Locknote" # App identifier
125
125
 
126
- eigen_class = class << app; self; end # Extracting app's eigenclass
126
+ eigen_class = class << app;
127
+ self;
128
+ end # Extracting app's eigenclass
127
129
  eigen_class.class_eval do # Defining singleton methods on app
128
130
  define_method(:handle) {handle}
129
131
  define_method(:textarea) {textarea}
@@ -136,7 +138,10 @@ module WinTestApp
136
138
  while @launched_test_app && find_window(nil, WIN_TITLE)
137
139
  shut_window @launched_test_app.handle
138
140
  sleep SLEEP_DELAY
139
- keystroke('N') if find_window(nil, "Steganos Locknote") # Dealing with closing modal dialog
141
+ if dialog = find_window(nil, "Steganos Locknote") # Dealing with closing modal dialog
142
+ set_foreground_window(dialog)
143
+ keystroke('N')
144
+ end
140
145
  end
141
146
  @launched_test_app = nil
142
147
  end
@@ -148,6 +153,23 @@ module WinTestApp
148
153
  close_test_app
149
154
  end
150
155
 
156
+ def test_app_with_dialog(type=:close)
157
+ test_app do |app|
158
+ case type
159
+ when :close
160
+ keystroke('A')
161
+ shut_window app.handle
162
+ sleep 0.01 until dialog = find_window(nil, "Steganos Locknote")
163
+ when :save
164
+ keystroke(VK_ALT, 'F', 'A')
165
+ sleep 0.01 until dialog = find_window(nil, "Save As")
166
+ end
167
+ yield app, dialog
168
+ set_foreground_window(dialog)
169
+ keystroke(VK_ESCAPE)
170
+ end
171
+ end
172
+
151
173
  # Emulates combinations of (any amount of) keys pressed one after another (Ctrl+Alt+P) and then released
152
174
  # *keys should be a sequence of a virtual-key codes. These codes must be a value in the range 1 to 254.
153
175
  # For a complete list, see msdn:Virtual Key Codes.
@@ -6,22 +6,6 @@ module WinGuiDialogTest
6
6
  include WinTestApp
7
7
  include Win::Gui::Dialog
8
8
 
9
- def test_app_with_dialog(type=:close)
10
- test_app do |app|
11
- case type
12
- when :close
13
- keystroke('A')
14
- shut_window app.handle
15
- sleep 0.01 until dialog = find_window(nil, "Steganos Locknote")
16
- when :save
17
- keystroke(VK_ALT, 'F', 'A')
18
- sleep 0.01 until dialog = find_window(nil, "Save As")
19
- end
20
- yield app, dialog
21
- keystroke(VK_ESCAPE)
22
- end
23
- end
24
-
25
9
  describe Win::Gui::Dialog do
26
10
 
27
11
  describe "#message_box" do
@@ -6,22 +6,18 @@ module WinGuiInputTest
6
6
  include WinTestApp
7
7
  include Win::Gui::Input
8
8
 
9
+ # rolling back changes with Ctrl-Z to allow window closing without dialog!
10
+ def rollback_changes(num_changes)
11
+ num_changes.times {keystroke(VK_CONTROL, 'Z'.ord)}
12
+ end
13
+
9
14
  describe Win::Gui::Input, ' defines a set of API functions related to user input' do
10
15
 
11
16
  describe '#keydb_event' do
12
17
  spec{ use{ keybd_event(vkey = 0, bscan = 0, flags = 0, extra_info = 0) }}
13
18
  before(:each){ (@app=launch_test_app)}
14
19
  after(:each) do
15
- 3.times do # rolling back changes to allow window closing without dialog!
16
- keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYDOWN, 0)
17
- sleep KEY_DELAY
18
- keybd_event('Z'.ord, 0, KEYEVENTF_KEYDOWN, 0)
19
- sleep KEY_DELAY
20
- keybd_event('Z'.ord, 0, KEYEVENTF_KEYUP, 0)
21
- sleep KEY_DELAY
22
- keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0)
23
- sleep KEY_DELAY
24
- end
20
+ rollback_changes(3)
25
21
  close_test_app
26
22
  end
27
23
 
@@ -0,0 +1,291 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+ require 'win/gui/menu'
3
+
4
+ module WinGuiWindowTest
5
+
6
+ include WinTestApp
7
+ include Win::Gui::Window
8
+
9
+ describe Win::Gui::Menu, ' defines a set of API functions related to menus' do
10
+ context 'non-destructive methods' do
11
+ before(:all)do
12
+ @app = launch_test_app
13
+ @menu = get_menu(@app.handle)
14
+ @file_menu = get_sub_menu(@menu, 0)
15
+ end
16
+ after(:all){ close_test_app if @launched_test_app }
17
+
18
+ describe "#get_menu" do
19
+
20
+ spec{ use{ menu = GetMenu(@app.handle) }}
21
+ spec{ use{ menu = get_menu(@app.handle) }}
22
+
23
+ it "retrieves a handle to the menu assigned to the specified top-level window" do
24
+ menu1 = GetMenu(@app.handle)
25
+ menu2 = get_menu(@app.handle)
26
+ menu1.should be_an Integer
27
+ menu1.should == @menu
28
+ menu1.should == menu2
29
+ end
30
+
31
+ it "returns 0/nil if no menu assigned to the specified top-level window" do
32
+ test_app_with_dialog(:close) do |app, dialog|
33
+ GetMenu(dialog).should == 0
34
+ get_menu(dialog).should == nil
35
+ end
36
+ end
37
+ end # describe get_menu
38
+
39
+ describe "#get_system_menu" do
40
+ spec{ use{ system_menu = GetSystemMenu(any_handle, reset=0) }}
41
+ spec{ use{ system_menu = get_system_menu(any_handle, reset=false) }}
42
+
43
+ it "with reset=0/false(default) allows the application to access the window menu (AKA system menu)" do
44
+ menu1 = GetSystemMenu(@app.handle, reset=0)
45
+ menu2 = get_system_menu(@app.handle, reset=0)
46
+ menu3 = get_system_menu(@app.handle)
47
+ menu1.should be_an Integer
48
+ menu1.should == menu2
49
+ menu1.should == menu3
50
+ end
51
+
52
+ it "with reset=1/true allows the application to reset its window menu to default, returns 0/nil" do
53
+ GetSystemMenu(@app.handle, reset=1).should == 0
54
+ get_system_menu(@app.handle, reset=true).should == nil
55
+ end
56
+ end # describe get_system_menu
57
+
58
+ describe "#get_menu_item_count" do
59
+
60
+ spec{ use{ num_items = GetMenuItemCount(@menu) }}
61
+ spec{ use{ num_items = get_menu_item_count(@menu) }}
62
+
63
+ it "determines the number of items in the specified menu. " do
64
+ GetMenuItemCount(@menu).should == 3
65
+ get_menu_item_count(@menu).should == 3
66
+ end
67
+
68
+ it "returns -1/nil if function fails " do
69
+ GetMenuItemCount(not_a_handle).should == -1
70
+ get_menu_item_count(not_a_handle).should == nil
71
+ end
72
+ end # describe get_menu_item_count
73
+
74
+ describe "#get_menu_item_id" do
75
+ spec{ use{ item_id = GetMenuItemID(@menu, pos=0) }}
76
+ spec{ use{ item_id = get_menu_item_id(@menu, pos=0) }}
77
+
78
+ it "retrieves the menu item identifier of a menu item located at the specified position" do
79
+ GetMenuItemID(@file_menu, pos=0).should == ID_FILE_SAVE_AS
80
+ get_menu_item_id(@file_menu, pos=0).should == ID_FILE_SAVE_AS
81
+ end
82
+
83
+ it "returns -1/nil if no menu item at given position" do
84
+ GetMenuItemID(@menu, pos=4).should == -1
85
+ get_menu_item_id(@menu, pos=4).should == nil
86
+ end
87
+
88
+ it "returns -1/nil if given menu item is in fact a sub-menu" do
89
+ GetMenuItemID(@menu, pos=0).should == -1
90
+ get_menu_item_id(@menu, pos=1).should == nil
91
+ end
92
+ end # describe get_menu_item_id
93
+
94
+ describe "#get_sub_menu" do
95
+ spec{ use{ sub_menu = GetSubMenu(@menu, pos=0) }}
96
+ spec{ use{ sub_menu = get_sub_menu(@menu, pos=0) }}
97
+
98
+ it "retrieves a handle to the drop-down menu or submenu activated by the specified menu item" do
99
+ sub_menu1 = GetSubMenu(@menu, pos=0)
100
+ sub_menu2 = get_sub_menu(@menu, pos=0)
101
+ sub_menu1.should be_an Integer
102
+ sub_menu1.should == @file_menu
103
+ sub_menu1.should == sub_menu2
104
+ end
105
+
106
+ it "returns 0/nil if unable to find submenu activated by the specified menu item" do
107
+ GetSubMenu(@file_menu, pos=0).should == 0
108
+ get_sub_menu(@file_menu, pos=0).should == nil
109
+ end
110
+ end # describe get_sub_menu
111
+
112
+ describe "#is_menu" do
113
+ before(:all){ @menu = get_menu(@app.handle) }
114
+
115
+ spec{ use{ success = IsMenu(@menu) }}
116
+ spec{ use{ success = menu?(@menu) }}
117
+
118
+ it "determines whether a given handle is a menu handle " do
119
+ IsMenu(@menu).should == 1
120
+ is_menu(@menu).should == true
121
+ menu?(@menu).should == true
122
+ menu?(@file_menu).should == true
123
+ IsMenu(not_a_handle).should == 0
124
+ is_menu(not_a_handle).should == false
125
+ menu?(not_a_handle).should == false
126
+ end
127
+ end # describe is_menu
128
+
129
+ describe "#set_menu" do
130
+ spec{ use{ success = SetMenu(window_handle=0, menu_handle=0) }}
131
+ spec{ use{ success = set_menu(window_handle=0, menu_handle=0) }}
132
+
133
+ it "assigns/removes menu of the specified top-level window" do
134
+ SetMenu(@app.handle, menu_handle=0)
135
+ get_menu(@app.handle).should == nil
136
+ SetMenu(@app.handle, @menu)
137
+ menu(@app.handle).should == @menu
138
+ set_menu(@app.handle)
139
+ menu(@app.handle).should == nil
140
+ set_menu(@app.handle, @menu)
141
+ menu(@app.handle).should == @menu
142
+ end
143
+
144
+ it "snake_case api with nil, zero or omitted menu_handle removes menu" do
145
+ [[@app.handle, 0], [@app.handle, nil], [@app.handle]].each do |args|
146
+ set_menu(*args)
147
+ menu(@app.handle).should == nil
148
+ set_menu(@app.handle, @menu)
149
+ end
150
+ end
151
+ end # describe set_menu
152
+
153
+ describe "#create_menu" do
154
+ after(:each){ destroy_menu(@new_menu) }
155
+
156
+ spec{ use{ @new_menu = CreateMenu() }}
157
+ spec{ use{ @new_menu = create_menu() }}
158
+
159
+ it "original api creates a menu. The menu is initially empty, but it can be filled with menu items" do
160
+ @new_menu = CreateMenu()
161
+ menu?(@new_menu).should == true
162
+ end
163
+
164
+ it "snake_case api creates a menu. The menu is initially empty." do
165
+ @new_menu = create_menu()
166
+ menu?(@new_menu).should == true
167
+ end
168
+ end # describe create_menu
169
+
170
+ context 'functions related to menu item manipulation' do
171
+ before(:each)do
172
+ @new_menu = create_menu()
173
+ @sub_menu = create_menu()
174
+ @text = FFI::MemoryPointer.from_string("Appended Item Text")
175
+ end
176
+ after(:each){ destroy_menu(@new_menu) }
177
+
178
+ describe "#append_menu" do
179
+ spec{ use{ success = AppendMenu(menu_handle=0, flags=0, id_new_item=0, lp_new_item=nil) }}
180
+ spec{ use{ success = append_menu(menu_handle=0, flags=0, id_new_item=0, lp_new_item=nil) }}
181
+
182
+ it "appends a new item to the end of the specified menu bar, drop-down or context menu, returns 1/true " do
183
+ AppendMenu(@new_menu, flags=MF_STRING, 333, @text).should == 1
184
+ append_menu(@new_menu, flags=MF_STRING, 333, @text).should == true
185
+ menu_item_count(@new_menu).should == 2
186
+ menu_item_id(@new_menu, pos=0).should == 333
187
+ end
188
+
189
+ it "appends a submenu to the end of the specified menu bar, drop-down or context menu, returns 1/true " do
190
+ AppendMenu(@new_menu, MF_STRING | MF_POPUP, @sub_menu, @text).should == 1
191
+ append_menu(@new_menu, MF_STRING | MF_POPUP, @sub_menu, @text).should == true
192
+ menu_item_count(@new_menu).should == 2
193
+ get_sub_menu(@new_menu, pos=0).should == @sub_menu
194
+ get_sub_menu(@new_menu, pos=1).should == @sub_menu
195
+ end
196
+
197
+ it "returns 0/false if unable to appends a new item to the end of the specified menu" do
198
+ AppendMenu(0, flags=MF_STRING, 333, @text).should == 0
199
+ append_menu(0, flags=MF_STRING, 333, @text).should == false
200
+ end
201
+ end # describe append_menu
202
+
203
+ describe "#insert_menu" do
204
+ before(:each)do
205
+ append_menu(@new_menu, MF_STRING, ID_FILE_SAVE_AS, FFI::MemoryPointer.from_string("Appended Item Text"))
206
+ end
207
+
208
+ spec{ use{ success = InsertMenu(menu_handle=0, position=0, flags=0, id_new_item=0, lp_new_item=nil) }}
209
+ spec{ use{ success = insert_menu(menu_handle=0, position=0, flags=0, id_new_item=0, lp_new_item=nil) }}
210
+
211
+ it "inserts a new menu item into a menu, moving other items down the menu, returns 0/true" do
212
+ InsertMenu(@new_menu, 0, MF_STRING | MF_BYPOSITION, 1, @text).should == 1
213
+ insert_menu(@new_menu, 0, MF_STRING | MF_BYPOSITION, 0, @text).should == true
214
+ menu_item_count(@new_menu).should == 3
215
+ menu_item_id(@new_menu, pos=0).should == 0
216
+ menu_item_id(@new_menu, pos=1).should == 1
217
+ end
218
+
219
+ it "returns 0/false if unable to appends a new item to the end of the specified menu" do
220
+ InsertMenu(0, 0, flags=MF_STRING, 333, @text).should == 0
221
+ insert_menu(0, 0, flags=MF_STRING, 333, @text).should == false
222
+ end
223
+ end # describe insert_menu
224
+
225
+ describe "#delete_menu" do
226
+ before(:each)do
227
+ append_menu(@new_menu, MF_STRING, 0, FFI::MemoryPointer.from_string("Item 0"))
228
+ append_menu(@new_menu, MF_STRING, 1, FFI::MemoryPointer.from_string("Item 1"))
229
+ append_menu(@new_menu, MF_POPUP | MF_STRING, @sub_menu, FFI::MemoryPointer.from_string("Sub 1"))
230
+ end
231
+
232
+ spec{ use{ success = DeleteMenu(menu_handle=0, position=0, flags=0) }}
233
+ spec{ use{ success = delete_menu(menu_handle=0, position=0, flags=0) }}
234
+
235
+ it "deletes an item from the specified menu, returns 1/true" do
236
+ DeleteMenu(@new_menu, position=0, flags=MF_BYPOSITION).should == 1
237
+ menu_item_count(@new_menu).should == 2
238
+ delete_menu(@new_menu, position=0, flags=MF_BYPOSITION).should == true
239
+ menu_item_count(@new_menu).should == 1
240
+ end
241
+
242
+ it "returns 0/false if unable to delete an item from the specified menu" do
243
+ DeleteMenu(@new_menu, position=5, flags=MF_BYPOSITION).should == 0
244
+ menu_item_count(@new_menu).should == 3
245
+ delete_menu(0, position=0, flags=MF_BYPOSITION).should == false
246
+ end
247
+
248
+ it "destroys the handle to submenu and frees the memory if given menu item opens a submenu" do
249
+ delete_menu(@new_menu, position=2, flags=MF_BYPOSITION).should == true
250
+ menu_item_count(@new_menu).should == 2
251
+ menu?(@sub_menu).should == false
252
+ end
253
+ end # describe delete_menu
254
+ end # functions related to menu item manipulation
255
+ end # context 'non-destructive methods'
256
+
257
+ context 'destructive methods' do
258
+ before(:each)do
259
+ @app = launch_test_app
260
+ @menu = get_menu(@app.handle)
261
+ @file_menu = get_sub_menu(@menu, 0)
262
+ end
263
+ after(:each){ close_test_app if @launched_test_app }
264
+
265
+ describe "#destroy_menu" do
266
+ spec{ use{ success = DestroyMenu(menu_handle=0) }}
267
+ spec{ use{ success = destroy_menu(menu_handle=0) }}
268
+
269
+ it "original api destroys the specified menu and frees any memory that the menu occupies, returns 1" do
270
+ DestroyMenu(@menu).should == 1
271
+ menu?(@menu).should == false
272
+ end
273
+
274
+ it "snake_case api destroys the specified menu and frees any memory that the menu occupies, returns true" do
275
+ destroy_menu(@menu).should == true
276
+ menu?(@menu).should == false
277
+ end
278
+
279
+ it "returns 0/false if function was not successful " do
280
+ destroy_menu(h_menu=0).should == false
281
+ DestroyMenu(0).should == 0
282
+ end
283
+ end # describe destroy_menu
284
+
285
+ end # context 'destructive methods' do
286
+
287
+ end # describe Win::Gui::Menu, ' defines a set of API functions related to menus'
288
+
289
+ # describe Win::Gui::Menu, ' defines convenience/service methods on top of Windows API' do
290
+ # end # Win::Gui::Menu, ' defines convenience/service methods on top of Windows API'
291
+ end