win32-clipboard 0.4.4 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGES CHANGED
@@ -1,3 +1,14 @@
1
+ == 0.5.0 - 24-Apr-2009
2
+ * Added the Clipboard.notify_change method, which notifies you if any changes
3
+ have been made to the clipboard.
4
+ * Added support for copying file lists, bitmaps and Windows metafiles.
5
+ * Added the Clipboard.clear alias for Clipboard.empty
6
+ * The previous documentation for the Clipboard.format_available? method was
7
+ misleading. It has been corrected.
8
+ * Added rdoc for modules, classes and constants, and added some inline
9
+ code examples for some methods.
10
+ * Gemspec update for handling a deprecated method.
11
+
1
12
  == 0.4.4 - 26-Aug-2008
2
13
  * Some internal refactoring to ensure that the clipboard is closed on failure
3
14
  in a more robust fashion.
data/Rakefile CHANGED
@@ -1,20 +1,24 @@
1
1
  require 'rake'
2
2
  require 'rake/testtask'
3
3
 
4
- desc "Install the win32-clipboard package (non-gem)"
4
+ desc "Install the win32-clipboard library (non-gem)"
5
5
  task :install do
6
6
  dest = File.join(Config::CONFIG['sitelibdir'], 'win32')
7
7
  Dir.mkdir(dest) unless File.exists? dest
8
8
  cp 'lib/win32/clipboard.rb', dest, :verbose => true
9
9
  end
10
10
 
11
- desc "Install the win32-clipboard package as a gem"
11
+ desc "Install the win32-clipboard library as a gem"
12
12
  task :install_gem do
13
13
  ruby 'win32-clipboard.gemspec'
14
14
  file = Dir["*.gem"].first
15
15
  sh "gem install #{file}"
16
16
  end
17
17
 
18
+ desc "Run the example program"
19
+ task :example do
20
+ sh "ruby -Ilib examples/clipboard_test.rb"
21
+
18
22
  Rake::TestTask.new do |t|
19
23
  t.warning = true
20
24
  t.verbose = true
@@ -1,13 +1,9 @@
1
1
  ##########################################################################
2
2
  # clipboard_test.rb (win32-clipboard)
3
3
  #
4
- # Generic test script for those without TestUnit installed, or for
5
- # general futzing.
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
6
  ##########################################################################
7
- Dir.chdir('..') if File.basename(Dir.pwd) == 'examples'
8
- $LOAD_PATH.unshift(Dir.pwd)
9
- $LOAD_PATH.unshift(Dir.pwd + '/lib')
10
-
11
7
  require "win32/clipboard"
12
8
  require "pp"
13
9
  include Win32
@@ -1,31 +1,72 @@
1
1
  require 'windows/clipboard'
2
2
  require 'windows/memory'
3
3
  require 'windows/error'
4
+ require 'windows/shell'
5
+ require 'windows/library'
6
+ require 'windows/window'
4
7
  require 'windows/msvcrt/buffer'
8
+ require 'windows/gdi/metafile'
9
+ require 'windows/window/message'
10
+ require 'windows/window/classes'
5
11
 
12
+ # The Win32 module serves as a namespace only.
6
13
  module Win32
14
+ # The Clipboard class encapsulates functions that relate to the MS Windows
15
+ # clipboard.
7
16
  class Clipboard
17
+ # The Clipboard::Error class is raised if any of the Win32::Clipboard
18
+ # methods should fail.
8
19
  class Error < StandardError; end
9
20
 
10
21
  include Windows::Clipboard
11
22
  include Windows::Memory
12
23
  include Windows::Error
24
+ include Windows::Shell
25
+ include Windows::Library
26
+ include Windows::Window
13
27
  include Windows::MSVCRT::Buffer
28
+ include Windows::GDI::MetaFile
29
+ include Windows::Window::Message
30
+ include Windows::Window::Classes
14
31
 
15
32
  extend Windows::Clipboard
16
33
  extend Windows::Memory
17
34
  extend Windows::Error
35
+ extend Windows::Shell
36
+ extend Windows::Library
37
+ extend Windows::Window
18
38
  extend Windows::MSVCRT::Buffer
39
+ extend Windows::GDI::MetaFile
40
+ extend Windows::Window::Message
41
+ extend Windows::Window::Classes
19
42
 
20
- VERSION = '0.4.4'
43
+ # The version of this library
44
+ VERSION = '0.5.0'
21
45
 
22
- # Clipboard data types
23
- TEXT = CF_TEXT
24
- OEMTEXT = CF_OEMTEXT
46
+ # Clipboard formats
47
+
48
+ # Text
49
+ TEXT = CF_TEXT
50
+ OEMTEXT = CF_OEMTEXT
25
51
  UNICODETEXT = CF_UNICODETEXT
26
52
 
27
- # Sets the clipboard contents to the data that you specify. You may
28
- # optionally specify a clipboard format. The default is Clipboard::TEXT.
53
+ # Images
54
+ DIB = CF_DIB
55
+ BITMAP = CF_BITMAP
56
+
57
+ # Metafiles
58
+ ENHMETAFILE = CF_ENHMETAFILE
59
+
60
+ # Files
61
+ HDROP = CF_HDROP
62
+
63
+ # Sets the clipboard contents to the data that you specify. You may
64
+ # optionally specify a clipboard format. The default is Clipboard::TEXT.
65
+ #
66
+ # Example:
67
+ #
68
+ # # Put the string 'hello' on the clipboard
69
+ # Win32::Clipboard.set_data('hello')
29
70
  #
30
71
  def self.set_data(clip_data, format = TEXT)
31
72
  self.open
@@ -59,25 +100,51 @@ module Win32
59
100
  # specified, it will attempt to retrieve the data in that format. The
60
101
  # default is Clipboard::TEXT.
61
102
  #
62
- # If there is no data in the clipboard then an empty string is returned.
103
+ # If there is no data in the clipboard, or data is available but the
104
+ # format doesn't match the data, then an empty string is returned.
105
+ #
106
+ # Examples:
107
+ #
108
+ # # Get some plain text
109
+ # Win32::Clipboard.data # => e.g. 'hello'
110
+ #
111
+ # # Get a list of files copied from the Windows Explorer window
112
+ # Win32::Clipboard.data(Clipboard::HDROP) # => ['foo.rb', 'bar.rb']
113
+ #
114
+ # # Get a bitmap and write it to another file
115
+ # File.open('bitmap_copy', 'wb'){ |fh|
116
+ # fh.write Win32::Clipboard.data(Clipboard::DIB)
117
+ # }
63
118
  #
64
119
  def self.data(format = TEXT)
65
120
  begin
66
121
  self.open
67
- clipdata = GetClipboardData(format) || ""
122
+ if IsClipboardFormatAvailable(format)
123
+ handle = GetClipboardData(format)
124
+ case format
125
+ when TEXT, OEMTEXT, UNICODETEXT
126
+ clip_data = 0.chr * GlobalSize(handle)
127
+ memcpy(clip_data, handle, clip_data.size)
128
+ clip_data.strip!
129
+ when HDROP
130
+ clip_data = get_file_list(handle)
131
+ when ENHMETAFILE
132
+ clip_data = get_metafile_data(handle)
133
+ when DIB, BITMAP
134
+ clip_data = get_image_data(handle)
135
+ else
136
+ raise Error, 'format not supported'
137
+ end
138
+ else
139
+ clip_data = ''
140
+ end
68
141
  ensure
69
142
  self.close
70
143
  end
71
144
 
72
- clipdata
145
+ clip_data
73
146
  end
74
147
 
75
- # Singleton aliases
76
- #
77
- class << self
78
- alias :get_data :data
79
- end
80
-
81
148
  # Empties the contents of the clipboard.
82
149
  #
83
150
  def self.empty
@@ -91,6 +158,13 @@ module Win32
91
158
  self
92
159
  end
93
160
 
161
+ # Singleton aliases
162
+ #
163
+ class << self
164
+ alias :get_data :data
165
+ alias :clear :empty
166
+ end
167
+
94
168
  # Returns the number of different data formats currently on the
95
169
  # clipboard.
96
170
  #
@@ -135,6 +209,10 @@ module Win32
135
209
 
136
210
  # Returns a hash of all the current formats, with the format number as
137
211
  # the key and the format name as the value for that key.
212
+ #
213
+ # Example:
214
+ #
215
+ # Win32::Clipboard.formats # => {1 => nil, 49335 => "HTML Format"}
138
216
  #
139
217
  def self.formats
140
218
  formats = {}
@@ -167,22 +245,168 @@ module Win32
167
245
  # through 0xFFFF.
168
246
  #
169
247
  def self.register_format(format)
248
+ raise TypeError unless format.is_a?(String)
249
+
170
250
  if RegisterClipboardFormat(format) == 0
171
251
  error = "RegisterClipboardFormat() call failed: " + get_last_error
172
- raise Error, error
252
+ raise Error, error
253
+ end
254
+ end
255
+
256
+ # Sets up a notification loop that will call the provided block whenever
257
+ # there's a change to the clipboard.
258
+ #
259
+ # Example:
260
+ #
261
+ # Win32::Clipboard.notify_change{
262
+ # puts "There's been a change in the clipboard contents!"
263
+ # }
264
+ #--
265
+ # We skip the first notification because the very act of attaching the
266
+ # new window causes it to trigger once.
267
+ #
268
+ def self.notify_change(&block)
269
+ name = 'ruby-clipboard-' + Time.now.to_s
270
+ handle = CreateWindow('static', name, 0, 0, 0, 0, 0, 0, 0, 0, 0)
271
+
272
+ @first_notify = true
273
+
274
+ wnd_proc = Win32::API::Callback.new('LLLL', 'L') do |hwnd, umsg, wparam, lparam|
275
+ case umsg
276
+ when WM_DRAWCLIPBOARD
277
+ yield unless @first_notify
278
+ next_viewer = GetWindowLongPtr(hwnd, GWL_USERDATA)
279
+ if next_viewer != 0
280
+ PostMessage(next_viewer, umsg, wparam, lparam)
281
+ end
282
+ rv = 0
283
+ when WM_CHANGECBCHAIN
284
+ yield unless @first_notify
285
+ next_viewer = lparam if next_viewer == wparam
286
+ if next_viewer != 0
287
+ PostMessage(next_viewer, umsg, wparam, lparam)
288
+ end
289
+ rv = 0
290
+ else
291
+ rv = DefWindowProc(hwnd, umsg, wparam, lparam)
292
+ end
293
+
294
+ @first_notify = false
295
+
296
+ rv
297
+ end
298
+
299
+ old_wnd_proc = SetWindowLongPtr(handle, GWL_WNDPROC, wnd_proc.address)
300
+ next_viewer = SetClipboardViewer(handle)
301
+
302
+ SetWindowLongPtr(handle, GWL_USERDATA, next_viewer)
303
+
304
+ msg = 0.chr * 100
305
+
306
+ while true
307
+ while PeekMessage(msg, handle, 0, 0, 1)
308
+ TranslateMessage(msg)
309
+ DispatchMessage(msg)
310
+ end
311
+ sleep 0.1
173
312
  end
174
313
  end
175
314
 
176
315
  private
177
316
 
317
+ # Opens the clipboard and prevents other applications from modifying
318
+ # the clipboard content until it is closed.
319
+ #
178
320
  def self.open
179
- if 0 == OpenClipboard(0)
321
+ if 0 == OpenClipboard(nil)
180
322
  raise Error, "OpenClipboard() failed: " + get_last_error
181
323
  end
182
324
  end
183
325
 
326
+ # Close the clipboard
327
+ #
184
328
  def self.close
185
329
  CloseClipboard()
186
330
  end
331
+
332
+ # Get data for enhanced metadata files
333
+ #
334
+ def self.get_metafile_data(handle)
335
+ buf_size = GetEnhMetaFileBits(handle, 0, nil)
336
+ buf = 0.chr * buf_size
337
+ GetEnhMetaFileBits(handle, buf_size, buf)
338
+ buf
339
+ end
340
+
341
+ # Get data for bitmap files
342
+ #
343
+ def self.get_image_data(handle)
344
+ buf = nil
345
+ bmi = 0.chr * 44 # BITMAPINFO
346
+
347
+ begin
348
+ address = GlobalLock(handle)
349
+ buf_size = GlobalSize(handle)
350
+
351
+ memcpy(bmi, address, bmi.length)
352
+
353
+ size = bmi[0,4].unpack('L').first # biSize
354
+ bit_count = bmi[14,2].unpack('S').first # biBitCount
355
+ compression = bmi[16,4].unpack('L').first # biCompression
356
+ size_image = bmi[20,4].unpack('L').first # biSizeImage
357
+ clr_used = bmi[32,4].unpack('L').first # biClrUsed
358
+
359
+ size_image = buf_size + 16 if size_image == 0
360
+
361
+ # Calculate the header size
362
+ case bit_count
363
+ when 1
364
+ table_size = 2
365
+ when 4
366
+ table_size = 16
367
+ when 8
368
+ table_size = 256
369
+ when 16, 32
370
+ if compression == 0
371
+ table_size = clr_used
372
+ elsif compression == 3
373
+ table_size = 3
374
+ else
375
+ raise Error, "invalid bit/compression combination"
376
+ end
377
+ when 24
378
+ table_size = clr_used
379
+ else
380
+ raise Error, "invalid bit count"
381
+ end # case
382
+
383
+ offset = 0x36 + (table_size * 4)
384
+ buf = 0.chr * buf_size
385
+
386
+ memcpy(buf, address, buf.size)
387
+
388
+ buf = "\x42\x4D" + [size_image].pack('L') + 0.chr * 4 + [offset].pack('L') + buf
389
+ ensure
390
+ GlobalUnlock(handle)
391
+ end
392
+
393
+ buf
394
+ end
395
+
396
+ # Get and return an array of file names that have been copied.
397
+ #
398
+ def self.get_file_list(handle)
399
+ array = []
400
+ count = DragQueryFile(handle, 0xFFFFFFFF, nil, 0)
401
+
402
+ 0.upto(count - 1){ |i|
403
+ length = DragQueryFile(handle, i, nil, 0) + 1
404
+ buf = 0.chr * length
405
+ DragQueryFile(handle, i, buf, buf.length)
406
+ array << buf.strip
407
+ }
408
+
409
+ array
410
+ end
187
411
  end
188
- end
412
+ end
@@ -7,34 +7,48 @@
7
7
  #
8
8
  # You should run this test case via the 'rake test' task.
9
9
  ###########################################################################
10
+ require 'rubygems'
11
+ gem 'test-unit'
12
+
10
13
  require 'win32/clipboard'
11
14
  require 'test/unit'
12
15
  include Win32
13
16
 
14
17
  class TC_Win32_ClipBoard < Test::Unit::TestCase
15
18
  def test_version
16
- assert_equal('0.4.4', Clipboard::VERSION)
19
+ assert_equal('0.5.0', Clipboard::VERSION)
17
20
  end
18
21
 
19
22
  def test_data
20
23
  assert_respond_to(Clipboard, :data)
21
24
  assert_nothing_raised{ Clipboard.data }
22
- assert_kind_of(String, Clipboard.data, 'bad data type')
23
- assert_raises(NameError){ Clipboard.data(CF_FOO) }
25
+ assert_kind_of(String, Clipboard.data)
26
+ end
27
+
28
+ def test_data_expected_errors
29
+ assert_raise(TypeError){ Clipboard.data('test') }
30
+ assert_raise(NameError){ Clipboard.data(CF_FOO) }
24
31
  end
25
32
 
26
33
  def test_get_data_alias
27
34
  assert_respond_to(Clipboard, :get_data)
28
- assert_equal(true, Clipboard.method(:data) == Clipboard.method(:get_data))
35
+ assert_true(Clipboard.method(:data) == Clipboard.method(:get_data))
29
36
  end
30
37
 
31
38
  def test_set_data
32
39
  assert_respond_to(Clipboard, :set_data)
33
40
  assert_nothing_raised{ Clipboard.set_data("foo") }
41
+ end
42
+
43
+ def test_set_data_unicode
34
44
  assert_nothing_raised{
35
45
  Clipboard.set_data('Ηελλας', Clipboard::UNICODETEXT)
36
46
  }
37
- assert_raises(NameError){ Clipboard.set_data('foo', CF_FOO) }
47
+ end
48
+
49
+ def test_set_data_expected_errors
50
+ assert_raise(ArgumentError){ Clipboard.set_data }
51
+ assert_raise(NameError){ Clipboard.set_data('foo', CF_FOO) }
38
52
  end
39
53
 
40
54
  def test_set_and_get_ascii
@@ -54,18 +68,28 @@ class TC_Win32_ClipBoard < Test::Unit::TestCase
54
68
  assert_nothing_raised{ Clipboard.empty }
55
69
  end
56
70
 
71
+ def test_clear_alias
72
+ assert_respond_to(Clipboard, :clear)
73
+ assert_true(Clipboard.method(:clear) == Clipboard.method(:empty))
74
+ end
75
+
57
76
  def test_num_formats
58
77
  assert_respond_to(Clipboard, :num_formats)
59
78
  assert_nothing_raised{ Clipboard.num_formats }
60
79
  assert_kind_of(Fixnum, Clipboard.num_formats)
61
80
  end
62
81
 
63
- # This TypeError check causes a segfault when using Win32API in 1.8.4 or
64
- # earlier.
82
+ def test_num_formats_expected_errors
83
+ assert_raise(ArgumentError){ Clipboard.num_formats(true) }
84
+ end
85
+
65
86
  def test_register_format
66
- assert_respond_to(Clipboard,:register_format)
87
+ assert_respond_to(Clipboard, :register_format)
67
88
  assert_nothing_raised{ Clipboard.register_format('foo') }
68
- #assert_raises(TypeError){ Clipboard.register_format(1) }
89
+ end
90
+
91
+ def test_register_format_expected_errors
92
+ assert_raises(TypeError){ Clipboard.register_format(1) }
69
93
  end
70
94
 
71
95
  def test_formats
@@ -74,21 +98,37 @@ class TC_Win32_ClipBoard < Test::Unit::TestCase
74
98
  assert_kind_of(Hash, Clipboard.formats)
75
99
  end
76
100
 
101
+ def test_formats_expected_errors
102
+ assert_raise(ArgumentError){ Clipboard.formats(true) }
103
+ end
104
+
77
105
  def test_format_available
78
106
  assert_respond_to(Clipboard, :format_available?)
79
107
  assert_nothing_raised{ Clipboard.format_available?(1) }
108
+ assert_boolean(Clipboard.format_available?(1))
80
109
  end
81
110
 
82
111
  def test_format_name
83
112
  assert_respond_to(Clipboard, :format_name)
84
113
  assert_nothing_raised{ Clipboard.format_name(1) }
85
114
  assert_nil(Clipboard.format_name(9999999))
86
- assert_raises(TypeError){ Clipboard.format_name('foo') }
115
+ end
116
+
117
+ def test_format_name_expected_errors
118
+ assert_raise(TypeError){ Clipboard.format_name('foo') }
119
+ end
120
+
121
+ def test_notify_change
122
+ assert_respond_to(Clipboard, :notify_change)
87
123
  end
88
124
 
89
125
  def test_constants
90
126
  assert_not_nil(Clipboard::TEXT)
91
127
  assert_not_nil(Clipboard::OEMTEXT)
92
128
  assert_not_nil(Clipboard::UNICODETEXT)
129
+ assert_not_nil(Clipboard::BITMAP)
130
+ assert_not_nil(Clipboard::DIB)
131
+ assert_not_nil(Clipboard::HDROP)
132
+ assert_not_nil(Clipboard::ENHMETAFILE)
93
133
  end
94
134
  end
@@ -1,27 +1,32 @@
1
1
  require "rubygems"
2
2
 
3
3
  spec = Gem::Specification.new do |gem|
4
- gem.name = "win32-clipboard"
5
- gem.version = "0.4.4"
6
- gem.authors = ["Daniel J. Berger", "Park Heesob"]
7
- gem.email = "djberg96@gmail.com"
8
- gem.homepage = "http://www.rubyforge.org/projects/win32utils"
9
- gem.platform = Gem::Platform::RUBY
10
- gem.summary = "A library for interacting with the Windows clipboard"
11
- gem.description = "A library for interacting with the Windows clipboard"
12
- gem.test_file = "test/test_clipboard.rb"
13
- gem.has_rdoc = true
14
- gem.extra_rdoc_files = ['CHANGES', 'README', 'MANIFEST']
15
- gem.rubyforge_project = "win32utils"
16
- gem.add_dependency("windows-pr", ">= 0.8.1")
17
-
18
- files = Dir["doc/*"] + Dir["examples/*"] + Dir["lib/win32/*"]
19
- files += Dir["test/*"] + Dir["[A-Z]*"]
20
- files.delete_if{ |item| item.include?("CVS") }
21
- gem.files = files
4
+ gem.name = 'win32-clipboard'
5
+ gem.version = '0.5.0'
6
+ gem.authors = ['Daniel J. Berger', 'Park Heesob']
7
+ gem.email = 'djberg96@gmail.com'
8
+ gem.homepage = 'http://www.rubyforge.org/projects/win32utils'
9
+ gem.platform = Gem::Platform::RUBY
10
+ gem.summary = 'A library for interacting with the Windows clipboard'
11
+ gem.test_file = 'test/test_clipboard.rb'
12
+ gem.has_rdoc = true
13
+ gem.extra_rdoc_files = ['CHANGES', 'README', 'MANIFEST']
14
+ gem.rubyforge_project = 'win32utils'
15
+
16
+ gem.add_dependency('windows-pr', '>= 1.0.3')
17
+
18
+ gem.description = "The win32-clipboard library provides an interface for
19
+ interacting with the Windows clipboard. It supports the ability to read
20
+ and write text, images, files, and Windows metafiles.".gsub(/\s+/, ' ')
21
+
22
+ files = Dir['doc/*'] + Dir['examples/*'] + Dir['lib/win32/*']
23
+ files += Dir['test/*'] + Dir['[A-Z]*']
24
+ files.delete_if{ |item| item.include?('CVS') }
25
+
26
+ gem.files = files
22
27
  end
23
28
 
24
29
  if $0 == __FILE__
25
- Gem.manage_gems
30
+ Gem.manage_gems if Gem::RubyGemsVersion.to_f < 1.0
26
31
  Gem::Builder.new(spec).build
27
32
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: win32-clipboard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.4
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel J. Berger
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2008-08-26 00:00:00 -06:00
13
+ date: 2009-04-24 00:00:00 -06:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -21,9 +21,9 @@ dependencies:
21
21
  requirements:
22
22
  - - ">="
23
23
  - !ruby/object:Gem::Version
24
- version: 0.8.1
24
+ version: 1.0.3
25
25
  version:
26
- description: A library for interacting with the Windows clipboard
26
+ description: The win32-clipboard library provides an interface for interacting with the Windows clipboard. It supports the ability to read and write text, images, files, and Windows metafiles.
27
27
  email: djberg96@gmail.com
28
28
  executables: []
29
29
 
@@ -38,15 +38,14 @@ files:
38
38
  - lib/win32/clipboard.rb
39
39
  - test/test_clipboard.rb
40
40
  - CHANGES
41
- - examples
42
- - lib
43
41
  - MANIFEST
44
42
  - Rakefile
45
43
  - README
46
- - test
47
44
  - win32-clipboard.gemspec
48
45
  has_rdoc: true
49
46
  homepage: http://www.rubyforge.org/projects/win32utils
47
+ licenses: []
48
+
50
49
  post_install_message:
51
50
  rdoc_options: []
52
51
 
@@ -67,9 +66,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
67
66
  requirements: []
68
67
 
69
68
  rubyforge_project: win32utils
70
- rubygems_version: 1.2.0
69
+ rubygems_version: 1.3.2
71
70
  signing_key:
72
- specification_version: 2
71
+ specification_version: 3
73
72
  summary: A library for interacting with the Windows clipboard
74
73
  test_files:
75
74
  - test/test_clipboard.rb