win32-clipboard 0.6.3 → 0.6.4

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.
data/Rakefile CHANGED
@@ -7,19 +7,16 @@ CLEAN.include('**/*.gem', '**/*.rbc')
7
7
  namespace :gem do
8
8
  desc "Create the win32-clipboard gem"
9
9
  task :create => [:clean] do
10
+ require 'rubygems/package'
10
11
  spec = eval(IO.read('win32-clipboard.gemspec'))
11
- if Gem::VERSION.to_f < 2.0
12
- Gem::Builder.new(spec).build
13
- else
14
- require 'rubygems/package'
15
- Gem::Package.build(spec)
16
- end
12
+ spec.signing_key = File.join(Dir.home, '.ssh', 'gem-private_key.pem')
13
+ Gem::Package.build(spec)
17
14
  end
18
15
 
19
16
  desc "Install the win32-clipboard library"
20
17
  task :install => [:create] do
21
18
  file = Dir["*.gem"].first
22
- sh "gem install #{file}"
19
+ sh "gem install -l #{file}"
23
20
  end
24
21
  end
25
22
 
@@ -0,0 +1,21 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDcDCCAligAwIBAgIBATANBgkqhkiG9w0BAQUFADA/MREwDwYDVQQDDAhkamJl
3
+ cmc5NjEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYDY29t
4
+ MB4XDTE1MDkwMjIwNDkxOFoXDTE2MDkwMTIwNDkxOFowPzERMA8GA1UEAwwIZGpi
5
+ ZXJnOTYxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkWA2Nv
6
+ bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMyTkvXqRp6hLs9eoJOS
7
+ Hmi8kRYbq9Vkf15/hMxJpotYMgJVHHWrmDcC5Dye2PbnXjTkKf266Zw0PtT9h+lI
8
+ S3ts9HO+vaCFSMwFFZmnWJSpQ3CNw2RcHxjWkk9yF7imEM8Kz9ojhiDXzBetdV6M
9
+ gr0lV/alUr7TNVBDngbXEfTWscyXh1qd7xZ4EcOdsDktCe5G45N/o3662tPQvJsi
10
+ FOF0CM/KuBsa/HL1/eoEmF4B3EKIRfTHrQ3hu20Kv3RJ88QM4ec2+0dd97uX693O
11
+ zv6981fyEg+aXLkxrkViM/tz2qR2ZE0jPhHTREPYeMEgptRkTmWSKAuLVWrJEfgl
12
+ DtkCAwEAAaN3MHUwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFEwe
13
+ nn6bfJADmuIDiMSOzedOrL+xMB0GA1UdEQQWMBSBEmRqYmVyZzk2QGdtYWlsLmNv
14
+ bTAdBgNVHRIEFjAUgRJkamJlcmc5NkBnbWFpbC5jb20wDQYJKoZIhvcNAQEFBQAD
15
+ ggEBAHmNOCWoDVD75zHFueY0viwGDVP1BNGFC+yXcb7u2GlK+nEMCORqzURbYPf7
16
+ tL+/hzmePIRz7i30UM//64GI1NLv9jl7nIwjhPpXpf7/lu2I9hOTsvwSumb5UiKC
17
+ /sqBxI3sfj9pr79Wpv4MuikX1XPik7Ncb7NPsJPw06Lvyc3Hkg5X2XpPtLtS+Gr2
18
+ wKJnmzb5rIPS1cmsqv0M9LPWflzfwoZ/SpnmhagP+g05p8bRNKjZSA2iImM/GyYZ
19
+ EJYzxdPOrx2n6NYR3Hk+vHP0U7UBSveI6+qx+ndQYaeyCn+GRX2PKS9h66YF/Q1V
20
+ tGSHgAmcLlkdGgan182qsE/4kKM=
21
+ -----END CERTIFICATE-----
@@ -1,29 +1,29 @@
1
- ##########################################################################
2
- # clipboard_test.rb (win32-clipboard)
3
- #
4
- # Generic test script for those without Test::Unit installed, or for
5
- # general futzing. You can run this example via the 'rake example' task.
6
- ##########################################################################
7
- require "win32/clipboard"
8
- require "pp"
9
- include Win32
10
-
11
- puts "VERSION: " + Clipboard::VERSION
12
-
13
- pp Clipboard.formats
14
- pp Clipboard.data(Clipboard::UNICODETEXT)
15
- pp Clipboard.format_available?(49161)
16
- pp Clipboard.format_name(999999999)
17
- pp Clipboard.format_available?(9999999)
18
-
19
- puts "Data was: [" + Clipboard.data + "]"
20
-
21
- Clipboard.set_data("foobar")
22
-
23
- puts "Data is now: [" + Clipboard.data + "]"
24
-
25
- puts "Number of available formats: " + Clipboard.num_formats.to_s
26
-
27
- Clipboard.empty
28
-
1
+ ##########################################################################
2
+ # clipboard_test.rb (win32-clipboard)
3
+ #
4
+ # Generic test script for those without Test::Unit installed, or for
5
+ # general futzing. You can run this example via the 'rake example' task.
6
+ ##########################################################################
7
+ require "win32/clipboard"
8
+ require "pp"
9
+ include Win32
10
+
11
+ puts "VERSION: " + Clipboard::VERSION
12
+
13
+ pp Clipboard.formats
14
+ pp Clipboard.data(Clipboard::UNICODETEXT)
15
+ pp Clipboard.format_available?(49161)
16
+ pp Clipboard.format_name(999999999)
17
+ pp Clipboard.format_available?(9999999)
18
+
19
+ puts "Data was: [" + Clipboard.data + "]"
20
+
21
+ Clipboard.set_data("foobar")
22
+
23
+ puts "Data is now: [" + Clipboard.data + "]"
24
+
25
+ puts "Number of available formats: " + Clipboard.num_formats.to_s
26
+
27
+ Clipboard.empty
28
+
29
29
  puts "Clipboard emptied"
@@ -0,0 +1 @@
1
+ require_relative 'win32/clipboard'
@@ -1,447 +1,447 @@
1
- require File.join(File.dirname(__FILE__), 'windows', 'constants')
2
- require File.join(File.dirname(__FILE__), 'windows', 'structs')
3
- require File.join(File.dirname(__FILE__), 'windows', 'functions')
4
-
5
- # The Win32 module serves as a namespace only.
6
- module Win32
7
-
8
- # The Clipboard class encapsulates functions that relate to the MS Windows clipboard.
9
- class Clipboard
10
-
11
- include Windows::Constants
12
- include Windows::Structs
13
- include Windows::Functions
14
-
15
- extend Windows::Functions
16
- extend Windows::Structs
17
-
18
- # The version of this library
19
- VERSION = '0.6.3'
20
-
21
- # Clipboard formats
22
-
23
- # Text
24
-
25
- TEXT = 1
26
- OEMTEXT = 7
27
- UNICODETEXT = 13
28
-
29
- # Images
30
-
31
- DIB = 8
32
- BITMAP = 2
33
-
34
- # Metafiles
35
-
36
- ENHMETAFILE = 14
37
-
38
- # Files
39
-
40
- HDROP = 15
41
-
42
- # Empties the contents of the clipboard.
43
- #
44
- def self.empty
45
- begin
46
- open
47
- clear_if_already_opened
48
- ensure
49
- close
50
- end
51
- end
52
-
53
- class << self
54
- alias clear empty
55
- end
56
-
57
- # Returns the number of different data formats currently on the clipboard.
58
- #
59
- def self.num_formats
60
- CountClipboardFormats() || 0
61
- end
62
-
63
- # Returns whether or not +format+ (an integer) is currently available.
64
- #
65
- def self.format_available?(format)
66
- IsClipboardFormatAvailable(format)
67
- end
68
-
69
- # Returns the data currently in the clipboard. If +format+ is
70
- # specified, it will attempt to retrieve the data in that format. The
71
- # default is Clipboard::TEXT.
72
- #
73
- # If there is no data in the clipboard, or data is available but the
74
- # format doesn't match the data, then an empty string is returned.
75
- #
76
- # Examples:
77
- #
78
- # # Get some plain text
79
- # Win32::Clipboard.data # => e.g. 'hello'
80
- #
81
- # # Get a list of files copied from the Windows Explorer window
82
- # Win32::Clipboard.data(Clipboard::HDROP) # => ['foo.rb', 'bar.rb']
83
- #
84
- # # Get a bitmap and write it to another file
85
- # File.open('bitmap_copy', 'wb'){ |fh|
86
- # fh.write Win32::Clipboard.data(Clipboard::DIB)
87
- # }
88
- #
89
- def self.data(format = TEXT)
90
- begin
91
- open
92
-
93
- if IsClipboardFormatAvailable(format)
94
- handle = GetClipboardData(format)
95
-
96
- case format
97
- when UNICODETEXT
98
- size = GlobalSize(handle)
99
- ptr = GlobalLock(handle)
100
- clip_data = ptr.read_bytes(size).force_encoding('UTF-16LE').encode('UTF-8').strip
101
- when TEXT, OEMTEXT
102
- size = GlobalSize(handle)
103
- ptr = GlobalLock(handle)
104
-
105
- clip_data = ptr.read_bytes(size).strip
106
-
107
- unless clip_data.ascii_only?
108
- clip_data.force_encoding('BINARY')
109
- end
110
- when HDROP
111
- clip_data = get_file_list(handle)
112
- when ENHMETAFILE
113
- clip_data = get_metafile_data(handle)
114
- when DIB, BITMAP
115
- clip_data = get_image_data(handle)
116
- else
117
- raise "format '#{format}' not supported"
118
- end
119
- else
120
- clip_data = ''
121
- end
122
- ensure
123
- close
124
- end
125
-
126
- clip_data
127
- end
128
-
129
- class << self
130
- alias get_data data
131
- end
132
-
133
- # Sets the clipboard contents to the data that you specify. You may
134
- # optionally specify a clipboard format. The default is Clipboard::TEXT.
135
- #
136
- # Example:
137
- #
138
- # # Put the string 'hello' on the clipboard
139
- # Win32::Clipboard.set_data('hello')
140
- #
141
- # # Put the image test.bmp on the clipboard
142
- # f = File.open("test.bmp", "rb")
143
- # str = f.read
144
- # Clipboard.set_data(str, Win32::Clipboard::DIB)
145
- #
146
- def self.set_data(clip_data, format = TEXT)
147
- begin
148
- open
149
- clear_if_already_opened
150
-
151
- # NULL terminate text or strip header of bitmap file
152
- case format
153
- when UNICODETEXT
154
- clip_data.encode!('UTF-16LE')
155
- clip_data << "\0".encode('UTF-16LE')
156
- buf = clip_data
157
- extra = 4
158
- when TEXT, OEMTEXT
159
- clip_data << "\0"
160
- buf = clip_data
161
- extra = 4
162
- when BITMAP, DIB
163
- buf = clip_data[BITMAP_FILE_HEADER_SIZE..clip_data.length]
164
- extra = 0
165
- end
166
-
167
- # Global Allocate a movable piece of memory.
168
- hmem = GlobalAlloc(GHND, buf.bytesize + extra)
169
- mptr = GlobalLock(hmem)
170
-
171
- mptr.write_bytes(buf, 0, buf.bytesize)
172
-
173
- # Set the new data
174
- if SetClipboardData(format, hmem) == 0
175
- raise SystemCallError.new('SetClipboardData', FFI.errno)
176
- end
177
- ensure
178
- GlobalFree(hmem)
179
- close
180
- end
181
- end
182
-
183
- # Returns a hash of all the current formats, with the format number as
184
- # the key and the format name as the value for that key.
185
- #
186
- # Example:
187
- #
188
- # Win32::Clipboard.formats # => {1 => nil, 49335 => "HTML Format"}
189
- #
190
- def self.formats
191
- formats = {}
192
- format = 0
193
-
194
- begin
195
- self.open
196
- while 0 != (format = EnumClipboardFormats(format))
197
- buf = FFI::MemoryPointer.new(:char, 80)
198
- GetClipboardFormatName(format, buf, buf.size)
199
- string = buf.read_string
200
- formats[format] = string.empty? ? nil : string
201
- end
202
- ensure
203
- self.close
204
- end
205
-
206
- formats
207
- end
208
-
209
- # Returns the corresponding name for the given +format_num+, or nil
210
- # if it does not exist. You cannot specify any of the predefined
211
- # clipboard formats (or nil is returned), only registered formats.
212
- #
213
- def self.format_name(format_num)
214
- val = nil
215
- buf = FFI::MemoryPointer.new(:char, 80)
216
-
217
- begin
218
- open
219
-
220
- if GetClipboardFormatName(format_num, buf, buf.size) != 0
221
- val = buf.read_string
222
- end
223
- ensure
224
- close
225
- end
226
-
227
- val
228
- end
229
-
230
- # Registers the given +format+ (a String) as a clipboard format, which
231
- # can then be used as a valid clipboard format. Returns the integer
232
- # value of the registered format.
233
- #
234
- # If a registered format with the specified name already exists, a new
235
- # format is not registered and the return value identifies the existing
236
- # format. This enables more than one application to copy and paste data
237
- # using the same registered clipboard format. Note that the format name
238
- # comparison is case-insensitive.
239
- #
240
- # Registered clipboard formats are identified by values in the range 0xC000
241
- # through 0xFFFF.
242
- #
243
- def self.register_format(format)
244
- format_value = RegisterClipboardFormat(format)
245
-
246
- if format_value == 0
247
- raise SystemCallError.new('RegisterClipboardFormat', FFI.errno)
248
- end
249
-
250
- format_value
251
- end
252
-
253
- # Sets up a notification loop that will call the provided block whenever
254
- # there's a change to the clipboard.
255
- #
256
- # Example:
257
- #
258
- # Win32::Clipboard.notify_change{
259
- # puts "There's been a change in the clipboard contents!"
260
- # }
261
- #--
262
- # We skip the first notification because the very act of attaching the
263
- # new window causes it to trigger once.
264
- #
265
- def self.notify_change(&block)
266
- name = 'ruby-clipboard-' + Time.now.to_s
267
- handle = CreateWindowEx(0, 'static', name, 0, 0, 0, 0, 0, 0, 0, 0, nil)
268
-
269
- next_viewer = SetClipboardViewer(handle)
270
-
271
- if next_viewer.nil?
272
- raise SystemCallError.new('SetClipboardViewer', FFI.errno)
273
- end
274
-
275
- SetWindowLongPtr(handle, GWL_USERDATA, next_viewer)
276
-
277
- ObjectSpace.define_finalizer(self, proc{self.close_window(handle)})
278
-
279
- @first_notify = true
280
-
281
- wnd_proc = FFI::Function.new(:uintptr_t, [:uintptr_t, :uint, :uintptr_t, :uintptr_t]) do |hwnd, umsg, wparam, lparam|
282
- case umsg
283
- when WM_DESTROY
284
- next_viewer = GetWindowLongPtr(hwnd, GWL_USERDATA)
285
- ChangeClipboardChain(hwnd, next_viewer)
286
- PostQuitMessage(0)
287
- rv = 0
288
- when WM_DRAWCLIPBOARD
289
- yield unless @first_notify
290
- next_viewer = GetWindowLongPtr(hwnd, GWL_USERDATA)
291
- if next_viewer != 0
292
- PostMessage(next_viewer, umsg, wparam, lparam)
293
- end
294
- rv = 0
295
- when WM_CHANGECBCHAIN
296
- next_viewer = GetWindowLongPtr(hwnd, GWL_USERDATA)
297
- next_viewer = lparam if next_viewer == wparam
298
- SetWindowLongPtr(hwnd, GWL_USERDATA, next_viewer)
299
- if next_viewer != 0
300
- PostMessage(next_viewer, umsg, wparam, lparam)
301
- end
302
- rv = 0
303
- else
304
- rv = DefWindowProc(hwnd, umsg, wparam, lparam)
305
- end
306
-
307
- @first_notify = false
308
-
309
- rv
310
- end
311
-
312
- if SetWindowLongPtr(handle, GWL_WNDPROC, wnd_proc.address) == 0
313
- raise SystemCallError.new('SetWindowLongPtr', FFI.errno)
314
- end
315
-
316
- msg = FFI::MemoryPointer.new(:char, 100)
317
-
318
- while true
319
- while PeekMessage(msg, handle, 0, 0, 1)
320
- TranslateMessage(msg)
321
- DispatchMessage(msg)
322
- end
323
- sleep 0.1
324
- end
325
- end
326
-
327
- private
328
-
329
- # sizeof(BITMAPFILEHEADER)
330
-
331
- BITMAP_FILE_HEADER_SIZE = 14
332
-
333
- # sizeof(BITMAPINFOHEADER)
334
-
335
- BITMAP_INFO_HEADER_SIZE = 40
336
-
337
- # Opens the clipboard and prevents other applications from modifying
338
- # the clipboard content until it is closed.
339
- #
340
- def self.open
341
- unless OpenClipboard(0)
342
- raise SystemCallError.new('OpenClipboard', FFI.errno)
343
- end
344
- end
345
-
346
- # Close the clipboard
347
- #
348
- def self.close
349
- unless CloseClipboard()
350
- raise SystemCallError.new('CloseClipboard', FFI.errno)
351
- end
352
- end
353
-
354
- # Clear the clipboard that is already opened
355
- #
356
- def self.clear_if_already_opened
357
- unless EmptyClipboard()
358
- raise SystemCallError.new('EmptyClipboard', FFI.errno)
359
- end
360
- end
361
-
362
- # Get data for enhanced metadata files
363
- #
364
- def self.get_metafile_data(handle)
365
- buf_size = GetEnhMetaFileBits(handle, 0, nil)
366
- buf = FFI::MemoryPointer.new(:char, buf_size)
367
- GetEnhMetaFileBits(handle, buf.size, buf)
368
- buf.read_string
369
- end
370
-
371
- # Get data for bitmap files
372
- #
373
- def self.get_image_data(handle)
374
- buf = nil
375
-
376
- begin
377
- ptr = GlobalLock(handle)
378
- buf_size = GlobalSize(handle)
379
-
380
- bmi = BITMAPINFO.new(ptr)
381
-
382
- file_size = buf_size + BITMAP_FILE_HEADER_SIZE
383
-
384
- # Calculate the header size
385
- case bmi[:bmiHeader][:biBitCount]
386
- when 1
387
- table_size = 2
388
- when 4
389
- table_size = 16
390
- when 8
391
- table_size = 256
392
- when 16, 32
393
- if bmi[:bmiHeader][:biCompression] == BI_RGB
394
- table_size = bmi[:bmiHeader][:biClrUsed]
395
- elsif bmi[:bmiHeader][:biCompression] == BI_BITFIELDS
396
- table_size = bmi[:bmiHeader][:biClrUsed]
397
- if bmi[:bmiHeader][:biSize] == BITMAP_INFO_HEADER_SIZE
398
- # Add bit fields size
399
- table_size += 3
400
- end
401
- else
402
- raise "invalid bit/compression combination"
403
- end
404
- when 24
405
- table_size = bmi[:bmiHeader][:biClrUsed]
406
- else
407
- raise "invalid bit count"
408
- end # case
409
-
410
- # offset = sizeof(BITMAPFILEHEADER)
411
- # + sizeof(BITMAPINFOHEADER or BITMAPV4HEADER or BITMAPV5HEADER)
412
- # + (color table size)
413
- offset = BITMAP_FILE_HEADER_SIZE + bmi[:bmiHeader][:biSize] + (table_size * 4)
414
-
415
- buf = "\x42\x4D" + [file_size].pack('L') + 0.chr * 4 + [offset].pack('L') + ptr.read_bytes(buf_size)
416
- ensure
417
- GlobalUnlock(handle) if handle
418
- end
419
-
420
- buf
421
- end
422
-
423
- # Get and return an array of file names that have been copied.
424
- #
425
- def self.get_file_list(handle)
426
- array = []
427
- count = DragQueryFileA(handle, 0xFFFFFFFF, nil, 0)
428
-
429
- 0.upto(count - 1){ |i|
430
- length = DragQueryFileA(handle, i, nil, 0) + 1
431
- buf = FFI::MemoryPointer.new(:char, length)
432
- DragQueryFileA(handle, i, buf, buf.size)
433
- array << buf.read_string
434
- }
435
-
436
- array
437
- end
438
-
439
- # Close a window.(for self.notify_change)
440
- #
441
- def self.close_window(handle)
442
- SendMessage(handle, WM_CLOSE, 0, 0)
443
- end
444
- end
445
- end
446
-
447
- require File.join(File.dirname(__FILE__), 'html_clipboard')
1
+ require_relative 'windows/constants'
2
+ require_relative 'windows/structs'
3
+ require_relative 'windows/functions'
4
+
5
+ # The Win32 module serves as a namespace only.
6
+ module Win32
7
+
8
+ # The Clipboard class encapsulates functions that relate to the MS Windows clipboard.
9
+ class Clipboard
10
+
11
+ include Windows::Constants
12
+ include Windows::Structs
13
+ include Windows::Functions
14
+
15
+ extend Windows::Functions
16
+ extend Windows::Structs
17
+
18
+ # The version of the win32-clipboard library.
19
+ VERSION = '0.6.4'
20
+
21
+ # Clipboard formats
22
+
23
+ # Text
24
+
25
+ TEXT = 1
26
+ OEMTEXT = 7
27
+ UNICODETEXT = 13
28
+
29
+ # Images
30
+
31
+ DIB = 8
32
+ BITMAP = 2
33
+
34
+ # Metafiles
35
+
36
+ ENHMETAFILE = 14
37
+
38
+ # Files
39
+
40
+ HDROP = 15
41
+
42
+ # Empties the contents of the clipboard.
43
+ #
44
+ def self.empty
45
+ begin
46
+ open
47
+ clear_if_already_opened
48
+ ensure
49
+ close
50
+ end
51
+ end
52
+
53
+ class << self
54
+ alias clear empty
55
+ end
56
+
57
+ # Returns the number of different data formats currently on the clipboard.
58
+ #
59
+ def self.num_formats
60
+ CountClipboardFormats() || 0
61
+ end
62
+
63
+ # Returns whether or not +format+ (an integer) is currently available.
64
+ #
65
+ def self.format_available?(format)
66
+ IsClipboardFormatAvailable(format)
67
+ end
68
+
69
+ # Returns the data currently in the clipboard. If +format+ is
70
+ # specified, it will attempt to retrieve the data in that format. The
71
+ # default is Clipboard::TEXT.
72
+ #
73
+ # If there is no data in the clipboard, or data is available but the
74
+ # format doesn't match the data, then an empty string is returned.
75
+ #
76
+ # Examples:
77
+ #
78
+ # # Get some plain text
79
+ # Win32::Clipboard.data # => e.g. 'hello'
80
+ #
81
+ # # Get a list of files copied from the Windows Explorer window
82
+ # Win32::Clipboard.data(Clipboard::HDROP) # => ['foo.rb', 'bar.rb']
83
+ #
84
+ # # Get a bitmap and write it to another file
85
+ # File.open('bitmap_copy', 'wb'){ |fh|
86
+ # fh.write Win32::Clipboard.data(Clipboard::DIB)
87
+ # }
88
+ #
89
+ def self.data(format = TEXT)
90
+ begin
91
+ open
92
+
93
+ if IsClipboardFormatAvailable(format)
94
+ handle = GetClipboardData(format)
95
+
96
+ case format
97
+ when UNICODETEXT
98
+ size = GlobalSize(handle)
99
+ ptr = GlobalLock(handle)
100
+ clip_data = ptr.read_bytes(size).force_encoding('UTF-16LE').encode('UTF-8').strip
101
+ when TEXT, OEMTEXT
102
+ size = GlobalSize(handle)
103
+ ptr = GlobalLock(handle)
104
+
105
+ clip_data = ptr.read_bytes(size).strip
106
+
107
+ unless clip_data.ascii_only?
108
+ clip_data.force_encoding('BINARY')
109
+ end
110
+ when HDROP
111
+ clip_data = get_file_list(handle)
112
+ when ENHMETAFILE
113
+ clip_data = get_metafile_data(handle)
114
+ when DIB, BITMAP
115
+ clip_data = get_image_data(handle)
116
+ else
117
+ raise "format '#{format}' not supported"
118
+ end
119
+ else
120
+ clip_data = ''
121
+ end
122
+ ensure
123
+ close
124
+ end
125
+
126
+ clip_data
127
+ end
128
+
129
+ class << self
130
+ alias get_data data
131
+ end
132
+
133
+ # Sets the clipboard contents to the data that you specify. You may
134
+ # optionally specify a clipboard format. The default is Clipboard::TEXT.
135
+ #
136
+ # Example:
137
+ #
138
+ # # Put the string 'hello' on the clipboard
139
+ # Win32::Clipboard.set_data('hello')
140
+ #
141
+ # # Put the image test.bmp on the clipboard
142
+ # f = File.open("test.bmp", "rb")
143
+ # str = f.read
144
+ # Clipboard.set_data(str, Win32::Clipboard::DIB)
145
+ #
146
+ def self.set_data(clip_data, format = TEXT)
147
+ begin
148
+ open
149
+ clear_if_already_opened
150
+
151
+ # NULL terminate text or strip header of bitmap file
152
+ case format
153
+ when UNICODETEXT
154
+ clip_data.encode!('UTF-16LE')
155
+ clip_data << "\0".encode('UTF-16LE')
156
+ buf = clip_data
157
+ extra = 4
158
+ when TEXT, OEMTEXT
159
+ clip_data << "\0"
160
+ buf = clip_data
161
+ extra = 4
162
+ when BITMAP, DIB
163
+ buf = clip_data[BITMAP_FILE_HEADER_SIZE..clip_data.length]
164
+ extra = 0
165
+ end
166
+
167
+ # Global Allocate a movable piece of memory.
168
+ hmem = GlobalAlloc(GHND, buf.bytesize + extra)
169
+ mptr = GlobalLock(hmem)
170
+
171
+ mptr.write_bytes(buf, 0, buf.bytesize)
172
+
173
+ # Set the new data
174
+ if SetClipboardData(format, hmem) == 0
175
+ raise SystemCallError.new('SetClipboardData', FFI.errno)
176
+ end
177
+ ensure
178
+ GlobalFree(hmem)
179
+ close
180
+ end
181
+ end
182
+
183
+ # Returns a hash of all the current formats, with the format number as
184
+ # the key and the format name as the value for that key.
185
+ #
186
+ # Example:
187
+ #
188
+ # Win32::Clipboard.formats # => {1 => nil, 49335 => "HTML Format"}
189
+ #
190
+ def self.formats
191
+ formats = {}
192
+ format = 0
193
+
194
+ begin
195
+ self.open
196
+ while 0 != (format = EnumClipboardFormats(format))
197
+ buf = FFI::MemoryPointer.new(:char, 80)
198
+ GetClipboardFormatName(format, buf, buf.size)
199
+ string = buf.read_string
200
+ formats[format] = string.empty? ? nil : string
201
+ end
202
+ ensure
203
+ self.close
204
+ end
205
+
206
+ formats
207
+ end
208
+
209
+ # Returns the corresponding name for the given +format_num+, or nil
210
+ # if it does not exist. You cannot specify any of the predefined
211
+ # clipboard formats (or nil is returned), only registered formats.
212
+ #
213
+ def self.format_name(format_num)
214
+ val = nil
215
+ buf = FFI::MemoryPointer.new(:char, 80)
216
+
217
+ begin
218
+ open
219
+
220
+ if GetClipboardFormatName(format_num, buf, buf.size) != 0
221
+ val = buf.read_string
222
+ end
223
+ ensure
224
+ close
225
+ end
226
+
227
+ val
228
+ end
229
+
230
+ # Registers the given +format+ (a String) as a clipboard format, which
231
+ # can then be used as a valid clipboard format. Returns the integer
232
+ # value of the registered format.
233
+ #
234
+ # If a registered format with the specified name already exists, a new
235
+ # format is not registered and the return value identifies the existing
236
+ # format. This enables more than one application to copy and paste data
237
+ # using the same registered clipboard format. Note that the format name
238
+ # comparison is case-insensitive.
239
+ #
240
+ # Registered clipboard formats are identified by values in the range 0xC000
241
+ # through 0xFFFF.
242
+ #
243
+ def self.register_format(format)
244
+ format_value = RegisterClipboardFormat(format)
245
+
246
+ if format_value == 0
247
+ raise SystemCallError.new('RegisterClipboardFormat', FFI.errno)
248
+ end
249
+
250
+ format_value
251
+ end
252
+
253
+ # Sets up a notification loop that will call the provided block whenever
254
+ # there's a change to the clipboard.
255
+ #
256
+ # Example:
257
+ #
258
+ # Win32::Clipboard.notify_change{
259
+ # puts "There's been a change in the clipboard contents!"
260
+ # }
261
+ #--
262
+ # We skip the first notification because the very act of attaching the
263
+ # new window causes it to trigger once.
264
+ #
265
+ def self.notify_change(&block)
266
+ name = 'ruby-clipboard-' + Time.now.to_s
267
+ handle = CreateWindowEx(0, 'static', name, 0, 0, 0, 0, 0, 0, 0, 0, nil)
268
+
269
+ next_viewer = SetClipboardViewer(handle)
270
+
271
+ if next_viewer.nil?
272
+ raise SystemCallError.new('SetClipboardViewer', FFI.errno)
273
+ end
274
+
275
+ SetWindowLongPtr(handle, GWL_USERDATA, next_viewer)
276
+
277
+ ObjectSpace.define_finalizer(self, proc{self.close_window(handle)})
278
+
279
+ @first_notify = true
280
+
281
+ wnd_proc = FFI::Function.new(:uintptr_t, [:uintptr_t, :uint, :uintptr_t, :uintptr_t]) do |hwnd, umsg, wparam, lparam|
282
+ case umsg
283
+ when WM_DESTROY
284
+ next_viewer = GetWindowLongPtr(hwnd, GWL_USERDATA)
285
+ ChangeClipboardChain(hwnd, next_viewer)
286
+ PostQuitMessage(0)
287
+ rv = 0
288
+ when WM_DRAWCLIPBOARD
289
+ yield unless @first_notify
290
+ next_viewer = GetWindowLongPtr(hwnd, GWL_USERDATA)
291
+ if next_viewer != 0
292
+ PostMessage(next_viewer, umsg, wparam, lparam)
293
+ end
294
+ rv = 0
295
+ when WM_CHANGECBCHAIN
296
+ next_viewer = GetWindowLongPtr(hwnd, GWL_USERDATA)
297
+ next_viewer = lparam if next_viewer == wparam
298
+ SetWindowLongPtr(hwnd, GWL_USERDATA, next_viewer)
299
+ if next_viewer != 0
300
+ PostMessage(next_viewer, umsg, wparam, lparam)
301
+ end
302
+ rv = 0
303
+ else
304
+ rv = DefWindowProc(hwnd, umsg, wparam, lparam)
305
+ end
306
+
307
+ @first_notify = false
308
+
309
+ rv
310
+ end
311
+
312
+ if SetWindowLongPtr(handle, GWL_WNDPROC, wnd_proc.address) == 0
313
+ raise SystemCallError.new('SetWindowLongPtr', FFI.errno)
314
+ end
315
+
316
+ msg = FFI::MemoryPointer.new(:char, 100)
317
+
318
+ while true
319
+ while PeekMessage(msg, handle, 0, 0, 1)
320
+ TranslateMessage(msg)
321
+ DispatchMessage(msg)
322
+ end
323
+ sleep 0.1
324
+ end
325
+ end
326
+
327
+ private
328
+
329
+ # sizeof(BITMAPFILEHEADER)
330
+
331
+ BITMAP_FILE_HEADER_SIZE = 14
332
+
333
+ # sizeof(BITMAPINFOHEADER)
334
+
335
+ BITMAP_INFO_HEADER_SIZE = 40
336
+
337
+ # Opens the clipboard and prevents other applications from modifying
338
+ # the clipboard content until it is closed.
339
+ #
340
+ def self.open
341
+ unless OpenClipboard(0)
342
+ raise SystemCallError.new('OpenClipboard', FFI.errno)
343
+ end
344
+ end
345
+
346
+ # Close the clipboard
347
+ #
348
+ def self.close
349
+ unless CloseClipboard()
350
+ raise SystemCallError.new('CloseClipboard', FFI.errno)
351
+ end
352
+ end
353
+
354
+ # Clear the clipboard that is already opened
355
+ #
356
+ def self.clear_if_already_opened
357
+ unless EmptyClipboard()
358
+ raise SystemCallError.new('EmptyClipboard', FFI.errno)
359
+ end
360
+ end
361
+
362
+ # Get data for enhanced metadata files
363
+ #
364
+ def self.get_metafile_data(handle)
365
+ buf_size = GetEnhMetaFileBits(handle, 0, nil)
366
+ buf = FFI::MemoryPointer.new(:char, buf_size)
367
+ GetEnhMetaFileBits(handle, buf.size, buf)
368
+ buf.read_string
369
+ end
370
+
371
+ # Get data for bitmap files
372
+ #
373
+ def self.get_image_data(handle)
374
+ buf = nil
375
+
376
+ begin
377
+ ptr = GlobalLock(handle)
378
+ buf_size = GlobalSize(handle)
379
+
380
+ bmi = BITMAPINFO.new(ptr)
381
+
382
+ file_size = buf_size + BITMAP_FILE_HEADER_SIZE
383
+
384
+ # Calculate the header size
385
+ case bmi[:bmiHeader][:biBitCount]
386
+ when 1
387
+ table_size = 2
388
+ when 4
389
+ table_size = 16
390
+ when 8
391
+ table_size = 256
392
+ when 16, 32
393
+ if bmi[:bmiHeader][:biCompression] == BI_RGB
394
+ table_size = bmi[:bmiHeader][:biClrUsed]
395
+ elsif bmi[:bmiHeader][:biCompression] == BI_BITFIELDS
396
+ table_size = bmi[:bmiHeader][:biClrUsed]
397
+ if bmi[:bmiHeader][:biSize] == BITMAP_INFO_HEADER_SIZE
398
+ # Add bit fields size
399
+ table_size += 3
400
+ end
401
+ else
402
+ raise "invalid bit/compression combination"
403
+ end
404
+ when 24
405
+ table_size = bmi[:bmiHeader][:biClrUsed]
406
+ else
407
+ raise "invalid bit count"
408
+ end # case
409
+
410
+ # offset = sizeof(BITMAPFILEHEADER)
411
+ # + sizeof(BITMAPINFOHEADER or BITMAPV4HEADER or BITMAPV5HEADER)
412
+ # + (color table size)
413
+ offset = BITMAP_FILE_HEADER_SIZE + bmi[:bmiHeader][:biSize] + (table_size * 4)
414
+
415
+ buf = "\x42\x4D" + [file_size].pack('L') + 0.chr * 4 + [offset].pack('L') + ptr.read_bytes(buf_size)
416
+ ensure
417
+ GlobalUnlock(handle) if handle
418
+ end
419
+
420
+ buf
421
+ end
422
+
423
+ # Get and return an array of file names that have been copied.
424
+ #
425
+ def self.get_file_list(handle)
426
+ array = []
427
+ count = DragQueryFileA(handle, 0xFFFFFFFF, nil, 0)
428
+
429
+ 0.upto(count - 1){ |i|
430
+ length = DragQueryFileA(handle, i, nil, 0) + 1
431
+ buf = FFI::MemoryPointer.new(:char, length)
432
+ DragQueryFileA(handle, i, buf, buf.size)
433
+ array << buf.read_string
434
+ }
435
+
436
+ array
437
+ end
438
+
439
+ # Close a window.(for self.notify_change)
440
+ #
441
+ def self.close_window(handle)
442
+ SendMessage(handle, WM_CLOSE, 0, 0)
443
+ end
444
+ end
445
+ end
446
+
447
+ require File.join(File.dirname(__FILE__), 'html_clipboard')