win32-clipboard 0.6.3 → 0.6.4

Sign up to get free protection for your applications and to get access to all the features.
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')