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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/CHANGES +112 -105
- data/MANIFEST +31 -12
- data/README +106 -112
- data/Rakefile +4 -7
- data/certs/djberg96_pub.pem +21 -0
- data/examples/clipboard_test.rb +28 -28
- data/lib/win32-clipboard.rb +1 -0
- data/lib/win32/clipboard.rb +447 -447
- data/lib/win32/html_clipboard.rb +232 -232
- data/lib/win32/windows/constants.rb +14 -14
- data/lib/win32/windows/functions.rb +66 -66
- data/lib/win32/windows/structs.rb +43 -43
- data/test/lock.rb +18 -18
- data/test/notify.rb +27 -27
- data/test/test_clipboard.rb +176 -176
- data/test/test_clipboard_chain.rb +137 -137
- data/test/test_html_clipboard.rb +50 -50
- data/test/test_image_clipboard.rb +51 -51
- data/win32-clipboard.gemspec +26 -26
- metadata +37 -13
- metadata.gz.sig +0 -0
data/lib/win32/html_clipboard.rb
CHANGED
@@ -1,232 +1,232 @@
|
|
1
|
-
# The Win32 module serves as a namespace only.
|
2
|
-
#
|
3
|
-
module Win32
|
4
|
-
|
5
|
-
# The HtmlClipboard class is a subclass of Clipboard that explicitly
|
6
|
-
# handles text in HTML format.
|
7
|
-
#
|
8
|
-
class HtmlClipboard < Clipboard
|
9
|
-
|
10
|
-
private
|
11
|
-
|
12
|
-
# Marker block output
|
13
|
-
#--
|
14
|
-
# Version: Version of the clipboard.
|
15
|
-
#
|
16
|
-
# StartHTML: bytecount from the beginning of the clipboard to the
|
17
|
-
# start of the context, or -1 if no context.
|
18
|
-
#
|
19
|
-
# EndHTML: bytecount from the beginning of the clipboard to the end
|
20
|
-
# of the context, or -1 if no context.
|
21
|
-
#
|
22
|
-
# StartFragment: bytecount from the beginning of the clipboard to
|
23
|
-
# the start of the fragment.
|
24
|
-
#
|
25
|
-
# EndFragment: bytecount from the beginning of the clipboard to the
|
26
|
-
# end of the fragment.
|
27
|
-
#
|
28
|
-
# StartSelection: bytecount from the beginning of the clipboard to
|
29
|
-
# the start of the selection.
|
30
|
-
#
|
31
|
-
# EndSelection: bytecount from the beginning of the clipboard to the
|
32
|
-
# end of the selection.
|
33
|
-
#
|
34
|
-
MARKER_BLOCK_OUTPUT =
|
35
|
-
"Version:1.0\r\n" \
|
36
|
-
"StartHTML:%09d\r\n" \
|
37
|
-
"EndHTML:%09d\r\n" \
|
38
|
-
"StartFragment:%09d\r\n" \
|
39
|
-
"EndFragment:%09d\r\n" \
|
40
|
-
"StartSelection:%09d\r\n" \
|
41
|
-
"EndSelection:%09d\r\n" \
|
42
|
-
"SourceURL:%s\r\n"
|
43
|
-
|
44
|
-
# Extended marker block
|
45
|
-
MARKER_BLOCK_EX =
|
46
|
-
'Version:(\S+)\s+' \
|
47
|
-
'StartHTML:(\d+)\s+' \
|
48
|
-
'EndHTML:(\d+)\s+' \
|
49
|
-
'StartFragment:(\d+)\s+' \
|
50
|
-
'EndFragment:(\d+)\s+' \
|
51
|
-
'StartSelection:(\d+)\s+' \
|
52
|
-
'EndSelection:(\d+)\s+' \
|
53
|
-
'SourceURL:(\S+)'
|
54
|
-
|
55
|
-
# Regular expression for extended marker block
|
56
|
-
MARKER_BLOCK_EX_RE = Regexp.new(MARKER_BLOCK_EX, Regexp::MULTILINE) # :nodoc:
|
57
|
-
|
58
|
-
# Standard marker block
|
59
|
-
MARKER_BLOCK =
|
60
|
-
'Version:(\S+)\s+' \
|
61
|
-
'StartHTML:(\d+)\s+' \
|
62
|
-
'EndHTML:(\d+)\s+' \
|
63
|
-
'StartFragment:(\d+)\s+' \
|
64
|
-
'EndFragment:(\d+)\s+' \
|
65
|
-
'SourceURL:(\S+)'
|
66
|
-
|
67
|
-
# Regular expression for the standard marker block
|
68
|
-
MARKER_BLOCK_RE = Regexp.new(MARKER_BLOCK, Regexp::MULTILINE)
|
69
|
-
|
70
|
-
# Default HTML body
|
71
|
-
DEFAULT_HTML_BODY =
|
72
|
-
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\r\n" \
|
73
|
-
"<HTML><BODY><!--StartFragment-->%s<!--EndFragment--></BODY></HTML>"
|
74
|
-
|
75
|
-
public
|
76
|
-
|
77
|
-
# Clipboard format value
|
78
|
-
CF_HTML = RegisterClipboardFormat("HTML Format")
|
79
|
-
|
80
|
-
def initialize # :nodoc:
|
81
|
-
@html = nil
|
82
|
-
@fragment = nil
|
83
|
-
@selection = nil
|
84
|
-
@source = nil
|
85
|
-
@version = nil
|
86
|
-
end
|
87
|
-
|
88
|
-
# Returns a boolean indicating whether or not the clipboard contains
|
89
|
-
# data in HTML format.
|
90
|
-
#
|
91
|
-
def self.html_format?
|
92
|
-
format_available?(CF_HTML)
|
93
|
-
end
|
94
|
-
|
95
|
-
# This method is nearly identical to the Clipboard.data method, but
|
96
|
-
# it decodes the data to preserve the HTML formatting.
|
97
|
-
#
|
98
|
-
def self.data
|
99
|
-
begin
|
100
|
-
self.open
|
101
|
-
if IsClipboardFormatAvailable(CF_HTML)
|
102
|
-
handle = GetClipboardData(CF_HTML)
|
103
|
-
size = GlobalSize(handle)
|
104
|
-
ptr = FFI::Pointer.new(:char, handle)
|
105
|
-
|
106
|
-
clip_data = decode_data(ptr.read_bytes(size))
|
107
|
-
else
|
108
|
-
clip_data = ''
|
109
|
-
end
|
110
|
-
ensure
|
111
|
-
self.close
|
112
|
-
end
|
113
|
-
clip_data
|
114
|
-
end
|
115
|
-
|
116
|
-
# Returns the block marker information for the HTML, or an empty
|
117
|
-
# string if there is no clipboard data.
|
118
|
-
#
|
119
|
-
def self.data_details
|
120
|
-
clip_data = data
|
121
|
-
string = ""
|
122
|
-
unless clip_data.empty?
|
123
|
-
string << "prefix=>>>#{@prefix}<<<END"
|
124
|
-
string << "version=>>>#{@version}<<<END"
|
125
|
-
string << "selection=>>>#{@selection}<<<END"
|
126
|
-
string << "fragment=>>>#{@fragment}<<<END"
|
127
|
-
string << "html=>>>#{@html}<<<END"
|
128
|
-
string << "source=>>>#{@source}<<<END"
|
129
|
-
end
|
130
|
-
string
|
131
|
-
end
|
132
|
-
|
133
|
-
# Put a well-formed fragment of HTML on the clipboard.
|
134
|
-
#
|
135
|
-
# The +selection+, if provided, must be a literal string within a
|
136
|
-
# fragment.
|
137
|
-
#
|
138
|
-
# The +html+ value, if provided, must be a well formed HTML document
|
139
|
-
# that textually contains a fragment and its required markers.
|
140
|
-
#
|
141
|
-
# The +source+, if provided, should include a scheme (file, http, or
|
142
|
-
# https) plus a file name. The default is file:// + __FILE__.
|
143
|
-
#
|
144
|
-
def self.set_data(fragment, selection=nil, html=nil, source=nil)
|
145
|
-
selection ||= fragment
|
146
|
-
html ||= DEFAULT_HTML_BODY % fragment
|
147
|
-
source ||= 'file://' + __FILE__
|
148
|
-
|
149
|
-
fragment_start = html.index(fragment)
|
150
|
-
fragment_end = fragment_start + fragment.length
|
151
|
-
selection_start = html.index(selection)
|
152
|
-
selection_end = selection_start + selection.length
|
153
|
-
|
154
|
-
clip_data = encode_data(
|
155
|
-
html,
|
156
|
-
fragment_start,
|
157
|
-
fragment_end,
|
158
|
-
selection_start,
|
159
|
-
selection_end,
|
160
|
-
source
|
161
|
-
)
|
162
|
-
|
163
|
-
self.open
|
164
|
-
EmptyClipboard()
|
165
|
-
|
166
|
-
# Global Allocate a movable piece of memory.
|
167
|
-
hmem = GlobalAlloc(GHND, clip_data.length + 4)
|
168
|
-
mem = GlobalLock(hmem)
|
169
|
-
mem.write_bytes(clip_data, 0, clip_data.size)
|
170
|
-
|
171
|
-
clip_data2 = fragment.gsub(/<[^>]+?>/,'')
|
172
|
-
hmem2 = GlobalAlloc(GHND, clip_data2.length + 4)
|
173
|
-
mem2 = GlobalLock(hmem2)
|
174
|
-
mem2.write_bytes(clip_data2, 0, clip_data2.size)
|
175
|
-
|
176
|
-
# Set the new data
|
177
|
-
begin
|
178
|
-
if SetClipboardData(CF_HTML, hmem) == 0
|
179
|
-
raise SystemCallError.new('SetClipboardData', FFI.errno)
|
180
|
-
end
|
181
|
-
|
182
|
-
if SetClipboardData(CF_TEXT, hmem2) == 0
|
183
|
-
raise SystemCallError.new('SetClipboardData', FFI.errno)
|
184
|
-
end
|
185
|
-
ensure
|
186
|
-
GlobalFree(hmem)
|
187
|
-
GlobalFree(hmem2)
|
188
|
-
self.close
|
189
|
-
end
|
190
|
-
self
|
191
|
-
end
|
192
|
-
|
193
|
-
private
|
194
|
-
|
195
|
-
# Encode the data markers into the HTML data
|
196
|
-
def self.encode_data(html,frag_start,frag_end,select_start,select_end,src)
|
197
|
-
dummy_prefix = MARKER_BLOCK_OUTPUT % [0,0,0,0,0,0,src]
|
198
|
-
len_prefix = dummy_prefix.length
|
199
|
-
prefix = MARKER_BLOCK_OUTPUT % [len_prefix, html.length + len_prefix,
|
200
|
-
frag_start + len_prefix, frag_end + len_prefix,
|
201
|
-
select_start + len_prefix, select_end + len_prefix, src]
|
202
|
-
prefix + html
|
203
|
-
end
|
204
|
-
|
205
|
-
# Decode the given string to figure out the details of the HTML
|
206
|
-
# that's on the string.
|
207
|
-
#--
|
208
|
-
# Try the extended format first, which has an explicit selection.
|
209
|
-
# If that fails, try the version without a selection.
|
210
|
-
#
|
211
|
-
def self.decode_data(src)
|
212
|
-
if (matches = MARKER_BLOCK_EX_RE.match(src))
|
213
|
-
@prefix = matches[0]
|
214
|
-
@version = matches[1]
|
215
|
-
@html = src[matches[2].to_i ... matches[3].to_i]
|
216
|
-
@fragment = src[matches[4].to_i ... matches[5].to_i]
|
217
|
-
@selection = src[matches[6].to_i ... matches[7].to_i]
|
218
|
-
@source = matches[8]
|
219
|
-
elsif (matches = MARKER_BLOCK_RE.match(src))
|
220
|
-
@prefix = matches[0]
|
221
|
-
@version = matches[1]
|
222
|
-
@html = src[matches[2].to_i ... matches[3].to_i]
|
223
|
-
@fragment = src[matches[4].to_i ... matches[5].to_i]
|
224
|
-
@source = matches[6]
|
225
|
-
@selection = @fragment
|
226
|
-
else
|
227
|
-
raise 'failed to match block markers'
|
228
|
-
end
|
229
|
-
@fragment
|
230
|
-
end
|
231
|
-
end
|
232
|
-
end
|
1
|
+
# The Win32 module serves as a namespace only.
|
2
|
+
#
|
3
|
+
module Win32
|
4
|
+
|
5
|
+
# The HtmlClipboard class is a subclass of Clipboard that explicitly
|
6
|
+
# handles text in HTML format.
|
7
|
+
#
|
8
|
+
class HtmlClipboard < Clipboard
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
# Marker block output
|
13
|
+
#--
|
14
|
+
# Version: Version of the clipboard.
|
15
|
+
#
|
16
|
+
# StartHTML: bytecount from the beginning of the clipboard to the
|
17
|
+
# start of the context, or -1 if no context.
|
18
|
+
#
|
19
|
+
# EndHTML: bytecount from the beginning of the clipboard to the end
|
20
|
+
# of the context, or -1 if no context.
|
21
|
+
#
|
22
|
+
# StartFragment: bytecount from the beginning of the clipboard to
|
23
|
+
# the start of the fragment.
|
24
|
+
#
|
25
|
+
# EndFragment: bytecount from the beginning of the clipboard to the
|
26
|
+
# end of the fragment.
|
27
|
+
#
|
28
|
+
# StartSelection: bytecount from the beginning of the clipboard to
|
29
|
+
# the start of the selection.
|
30
|
+
#
|
31
|
+
# EndSelection: bytecount from the beginning of the clipboard to the
|
32
|
+
# end of the selection.
|
33
|
+
#
|
34
|
+
MARKER_BLOCK_OUTPUT =
|
35
|
+
"Version:1.0\r\n" \
|
36
|
+
"StartHTML:%09d\r\n" \
|
37
|
+
"EndHTML:%09d\r\n" \
|
38
|
+
"StartFragment:%09d\r\n" \
|
39
|
+
"EndFragment:%09d\r\n" \
|
40
|
+
"StartSelection:%09d\r\n" \
|
41
|
+
"EndSelection:%09d\r\n" \
|
42
|
+
"SourceURL:%s\r\n"
|
43
|
+
|
44
|
+
# Extended marker block
|
45
|
+
MARKER_BLOCK_EX =
|
46
|
+
'Version:(\S+)\s+' \
|
47
|
+
'StartHTML:(\d+)\s+' \
|
48
|
+
'EndHTML:(\d+)\s+' \
|
49
|
+
'StartFragment:(\d+)\s+' \
|
50
|
+
'EndFragment:(\d+)\s+' \
|
51
|
+
'StartSelection:(\d+)\s+' \
|
52
|
+
'EndSelection:(\d+)\s+' \
|
53
|
+
'SourceURL:(\S+)'
|
54
|
+
|
55
|
+
# Regular expression for extended marker block
|
56
|
+
MARKER_BLOCK_EX_RE = Regexp.new(MARKER_BLOCK_EX, Regexp::MULTILINE) # :nodoc:
|
57
|
+
|
58
|
+
# Standard marker block
|
59
|
+
MARKER_BLOCK =
|
60
|
+
'Version:(\S+)\s+' \
|
61
|
+
'StartHTML:(\d+)\s+' \
|
62
|
+
'EndHTML:(\d+)\s+' \
|
63
|
+
'StartFragment:(\d+)\s+' \
|
64
|
+
'EndFragment:(\d+)\s+' \
|
65
|
+
'SourceURL:(\S+)'
|
66
|
+
|
67
|
+
# Regular expression for the standard marker block
|
68
|
+
MARKER_BLOCK_RE = Regexp.new(MARKER_BLOCK, Regexp::MULTILINE)
|
69
|
+
|
70
|
+
# Default HTML body
|
71
|
+
DEFAULT_HTML_BODY =
|
72
|
+
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\r\n" \
|
73
|
+
"<HTML><BODY><!--StartFragment-->%s<!--EndFragment--></BODY></HTML>"
|
74
|
+
|
75
|
+
public
|
76
|
+
|
77
|
+
# Clipboard format value
|
78
|
+
CF_HTML = RegisterClipboardFormat("HTML Format")
|
79
|
+
|
80
|
+
def initialize # :nodoc:
|
81
|
+
@html = nil
|
82
|
+
@fragment = nil
|
83
|
+
@selection = nil
|
84
|
+
@source = nil
|
85
|
+
@version = nil
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns a boolean indicating whether or not the clipboard contains
|
89
|
+
# data in HTML format.
|
90
|
+
#
|
91
|
+
def self.html_format?
|
92
|
+
format_available?(CF_HTML)
|
93
|
+
end
|
94
|
+
|
95
|
+
# This method is nearly identical to the Clipboard.data method, but
|
96
|
+
# it decodes the data to preserve the HTML formatting.
|
97
|
+
#
|
98
|
+
def self.data
|
99
|
+
begin
|
100
|
+
self.open
|
101
|
+
if IsClipboardFormatAvailable(CF_HTML)
|
102
|
+
handle = GetClipboardData(CF_HTML)
|
103
|
+
size = GlobalSize(handle)
|
104
|
+
ptr = FFI::Pointer.new(:char, handle)
|
105
|
+
|
106
|
+
clip_data = decode_data(ptr.read_bytes(size))
|
107
|
+
else
|
108
|
+
clip_data = ''
|
109
|
+
end
|
110
|
+
ensure
|
111
|
+
self.close
|
112
|
+
end
|
113
|
+
clip_data
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns the block marker information for the HTML, or an empty
|
117
|
+
# string if there is no clipboard data.
|
118
|
+
#
|
119
|
+
def self.data_details
|
120
|
+
clip_data = data
|
121
|
+
string = ""
|
122
|
+
unless clip_data.empty?
|
123
|
+
string << "prefix=>>>#{@prefix}<<<END"
|
124
|
+
string << "version=>>>#{@version}<<<END"
|
125
|
+
string << "selection=>>>#{@selection}<<<END"
|
126
|
+
string << "fragment=>>>#{@fragment}<<<END"
|
127
|
+
string << "html=>>>#{@html}<<<END"
|
128
|
+
string << "source=>>>#{@source}<<<END"
|
129
|
+
end
|
130
|
+
string
|
131
|
+
end
|
132
|
+
|
133
|
+
# Put a well-formed fragment of HTML on the clipboard.
|
134
|
+
#
|
135
|
+
# The +selection+, if provided, must be a literal string within a
|
136
|
+
# fragment.
|
137
|
+
#
|
138
|
+
# The +html+ value, if provided, must be a well formed HTML document
|
139
|
+
# that textually contains a fragment and its required markers.
|
140
|
+
#
|
141
|
+
# The +source+, if provided, should include a scheme (file, http, or
|
142
|
+
# https) plus a file name. The default is file:// + __FILE__.
|
143
|
+
#
|
144
|
+
def self.set_data(fragment, selection=nil, html=nil, source=nil)
|
145
|
+
selection ||= fragment
|
146
|
+
html ||= DEFAULT_HTML_BODY % fragment
|
147
|
+
source ||= 'file://' + __FILE__
|
148
|
+
|
149
|
+
fragment_start = html.index(fragment)
|
150
|
+
fragment_end = fragment_start + fragment.length
|
151
|
+
selection_start = html.index(selection)
|
152
|
+
selection_end = selection_start + selection.length
|
153
|
+
|
154
|
+
clip_data = encode_data(
|
155
|
+
html,
|
156
|
+
fragment_start,
|
157
|
+
fragment_end,
|
158
|
+
selection_start,
|
159
|
+
selection_end,
|
160
|
+
source
|
161
|
+
)
|
162
|
+
|
163
|
+
self.open
|
164
|
+
EmptyClipboard()
|
165
|
+
|
166
|
+
# Global Allocate a movable piece of memory.
|
167
|
+
hmem = GlobalAlloc(GHND, clip_data.length + 4)
|
168
|
+
mem = GlobalLock(hmem)
|
169
|
+
mem.write_bytes(clip_data, 0, clip_data.size)
|
170
|
+
|
171
|
+
clip_data2 = fragment.gsub(/<[^>]+?>/,'')
|
172
|
+
hmem2 = GlobalAlloc(GHND, clip_data2.length + 4)
|
173
|
+
mem2 = GlobalLock(hmem2)
|
174
|
+
mem2.write_bytes(clip_data2, 0, clip_data2.size)
|
175
|
+
|
176
|
+
# Set the new data
|
177
|
+
begin
|
178
|
+
if SetClipboardData(CF_HTML, hmem) == 0
|
179
|
+
raise SystemCallError.new('SetClipboardData', FFI.errno)
|
180
|
+
end
|
181
|
+
|
182
|
+
if SetClipboardData(CF_TEXT, hmem2) == 0
|
183
|
+
raise SystemCallError.new('SetClipboardData', FFI.errno)
|
184
|
+
end
|
185
|
+
ensure
|
186
|
+
GlobalFree(hmem)
|
187
|
+
GlobalFree(hmem2)
|
188
|
+
self.close
|
189
|
+
end
|
190
|
+
self
|
191
|
+
end
|
192
|
+
|
193
|
+
private
|
194
|
+
|
195
|
+
# Encode the data markers into the HTML data
|
196
|
+
def self.encode_data(html,frag_start,frag_end,select_start,select_end,src)
|
197
|
+
dummy_prefix = MARKER_BLOCK_OUTPUT % [0,0,0,0,0,0,src]
|
198
|
+
len_prefix = dummy_prefix.length
|
199
|
+
prefix = MARKER_BLOCK_OUTPUT % [len_prefix, html.length + len_prefix,
|
200
|
+
frag_start + len_prefix, frag_end + len_prefix,
|
201
|
+
select_start + len_prefix, select_end + len_prefix, src]
|
202
|
+
prefix + html
|
203
|
+
end
|
204
|
+
|
205
|
+
# Decode the given string to figure out the details of the HTML
|
206
|
+
# that's on the string.
|
207
|
+
#--
|
208
|
+
# Try the extended format first, which has an explicit selection.
|
209
|
+
# If that fails, try the version without a selection.
|
210
|
+
#
|
211
|
+
def self.decode_data(src)
|
212
|
+
if (matches = MARKER_BLOCK_EX_RE.match(src))
|
213
|
+
@prefix = matches[0]
|
214
|
+
@version = matches[1]
|
215
|
+
@html = src[matches[2].to_i ... matches[3].to_i]
|
216
|
+
@fragment = src[matches[4].to_i ... matches[5].to_i]
|
217
|
+
@selection = src[matches[6].to_i ... matches[7].to_i]
|
218
|
+
@source = matches[8]
|
219
|
+
elsif (matches = MARKER_BLOCK_RE.match(src))
|
220
|
+
@prefix = matches[0]
|
221
|
+
@version = matches[1]
|
222
|
+
@html = src[matches[2].to_i ... matches[3].to_i]
|
223
|
+
@fragment = src[matches[4].to_i ... matches[5].to_i]
|
224
|
+
@source = matches[6]
|
225
|
+
@selection = @fragment
|
226
|
+
else
|
227
|
+
raise 'failed to match block markers'
|
228
|
+
end
|
229
|
+
@fragment
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|