rubyosa 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +2 -2
- data/README +4 -1
- data/sample/BBEdit_unix_script.rb +19 -0
- data/sample/Finder_show_desktop.rb +3 -0
- data/sample/QT_playall.rb +1 -0
- data/sample/TextEdit_hello_world.rb +19 -0
- data/sample/iChat_image.rb +18 -0
- data/sample/iChat_uptime.rb +6 -2
- data/sample/iTunes_artwork.rb +14 -0
- data/sample/iTunes_control.rb +2 -0
- data/sample/iTunes_fade_volume.rb +1 -0
- data/sample/iTunes_inspect.rb +2 -0
- data/sample/sdef.rb +1 -0
- data/src/lib/rbosa.rb +112 -42
- data/src/rbosa.c +104 -2
- metadata +6 -2
data/AUTHORS
CHANGED
data/README
CHANGED
@@ -33,7 +33,10 @@ The rdoc-osa tool can be used to generate API reference documentation
|
|
33
33
|
for the given application. See its --help flag for more information
|
34
34
|
about how to use it.
|
35
35
|
|
36
|
-
Feel free to send feedback to
|
36
|
+
Feel free to send feedback to rubyosa-discuss@rubyforge.org.
|
37
|
+
|
38
|
+
You can also file bugs, patches and feature requests to the tracker:
|
39
|
+
http://rubyforge.org/tracker/?group_id=1845.
|
37
40
|
|
38
41
|
When reporting a bug, please set the AEDebugSends and AEDebugReceives
|
39
42
|
environment variables to 1 and attach the logs.
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Ask BBEdit to run the uptime(1) command and get the result.
|
2
|
+
|
3
|
+
begin require 'rubygems'; rescue LoadError; end
|
4
|
+
require 'rbosa'
|
5
|
+
|
6
|
+
puts 'Asking for uptime...'
|
7
|
+
|
8
|
+
bbedit = OSA.app('BBEdit')
|
9
|
+
|
10
|
+
bbedit.make(OSA::BBEdit::TextDocument).text = <<EOS
|
11
|
+
#!/bin/sh
|
12
|
+
uptime
|
13
|
+
EOS
|
14
|
+
|
15
|
+
bbedit.run_unix_script
|
16
|
+
|
17
|
+
output_doc = bbedit.text_documents.find { |x| x.name == 'Unix Script Output' }
|
18
|
+
|
19
|
+
puts output_doc.text.get
|
data/sample/QT_playall.rb
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Create new TextEdit documents with a 'Hello World' text.
|
2
|
+
|
3
|
+
begin require 'rubygems'; rescue LoadError; end
|
4
|
+
require 'rbosa'
|
5
|
+
|
6
|
+
textedit = OSA.app('TextEdit')
|
7
|
+
|
8
|
+
# Complex way.
|
9
|
+
textedit.make(OSA::TextEdit::Document, nil, nil, {:ctxt => 'Hello World #1'})
|
10
|
+
|
11
|
+
# Easier way.
|
12
|
+
textedit.make(OSA::TextEdit::Document).text = 'Hello World #2'
|
13
|
+
|
14
|
+
=begin
|
15
|
+
# Easiest way, not implemented for now.
|
16
|
+
document = OSA::TextEdit::Document.new
|
17
|
+
document.text = 'Hello World #3'
|
18
|
+
textedit << document
|
19
|
+
=end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Periodically set your iChat image to one of the default images.
|
2
|
+
|
3
|
+
begin require 'rubygems'; rescue LoadError; end
|
4
|
+
require 'rbosa'
|
5
|
+
|
6
|
+
ichat = OSA.app('iChat')
|
7
|
+
|
8
|
+
old_image = ichat.image
|
9
|
+
trap('INT') { ichat.image = old_image; exit 0 }
|
10
|
+
|
11
|
+
paths = Dir.glob("/Library/User Pictures/**/*.tif")
|
12
|
+
|
13
|
+
while true do
|
14
|
+
paths.each do |path|
|
15
|
+
ichat.image = File.read(path)
|
16
|
+
sleep 2
|
17
|
+
end
|
18
|
+
end
|
data/sample/iChat_uptime.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
# Periodically set your iChat status to the output of uptime(1).
|
2
2
|
|
3
|
+
begin require 'rubygems'; rescue LoadError; end
|
3
4
|
require 'rbosa'
|
4
5
|
|
5
6
|
app = OSA.app('iChat')
|
6
7
|
previous_status_message = app.status_message
|
7
8
|
trap('INT') { app.status_message = previous_status_message; exit 0 }
|
8
|
-
while true
|
9
|
-
|
9
|
+
while true
|
10
|
+
u = `uptime`
|
11
|
+
hours = u.scan(/^\s*(\d+:\d+)\s/).to_s + ' hours'
|
12
|
+
days = u.scan(/\d+\sdays/).to_s
|
13
|
+
app.status_message = "OSX up #{days} #{hours}"
|
10
14
|
sleep 5
|
11
15
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Open the artwork of the current iTunes track in Preview.
|
2
|
+
|
3
|
+
begin require 'rubygems'; rescue LoadError; end
|
4
|
+
require 'rbosa'
|
5
|
+
|
6
|
+
artworks = OSA.app('iTunes').current_track.artworks
|
7
|
+
if artworks.size == 0
|
8
|
+
puts "No artwork for current track."
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
|
12
|
+
fname = '/tmp/foo.' + artworks[0].format.downcase.strip
|
13
|
+
File.open(fname, 'w') { |io| io.write(artworks[0].data) }
|
14
|
+
system("open -a Preview #{fname}")
|
data/sample/iTunes_control.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
# Simple iTunes controller.
|
2
2
|
|
3
|
+
begin require 'rubygems'; rescue LoadError; end
|
3
4
|
require 'rbosa'
|
4
5
|
require 'curses'
|
5
6
|
include Curses
|
6
7
|
|
7
8
|
app = OSA.app('iTunes')
|
9
|
+
OSA.utf8_strings = true
|
8
10
|
|
9
11
|
if app.current_track.nil?
|
10
12
|
# We don't support write access now, so...
|
data/sample/iTunes_inspect.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# Quick inspection of iTunes' sources, playlists and tracks.
|
2
2
|
|
3
|
+
begin require 'rubygems'; rescue LoadError; end
|
3
4
|
require 'rbosa'
|
4
5
|
|
5
6
|
app = OSA.app('iTunes')
|
7
|
+
OSA.utf8_strings = true
|
6
8
|
app.sources.each do |source|
|
7
9
|
puts source.name
|
8
10
|
source.playlists.each do |playlist|
|
data/sample/sdef.rb
CHANGED
data/src/lib/rbosa.rb
CHANGED
@@ -24,11 +24,15 @@
|
|
24
24
|
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
25
25
|
# POSSIBILITY OF SUCH DAMAGE.
|
26
26
|
|
27
|
+
$KCODE = 'u' # we will use UTF-8 strings
|
28
|
+
|
27
29
|
require 'osa'
|
28
30
|
require 'date'
|
31
|
+
require 'uri'
|
32
|
+
require 'iconv'
|
29
33
|
|
30
34
|
# Try to load RubyGems first, libxml-ruby may have been installed by it.
|
31
|
-
begin require 'rubygems' rescue LoadError end
|
35
|
+
begin require 'rubygems'; rescue LoadError; end
|
32
36
|
|
33
37
|
# If libxml-ruby is not present, switch to REXML.
|
34
38
|
USE_LIBXML = begin
|
@@ -69,7 +73,7 @@ end
|
|
69
73
|
|
70
74
|
class String
|
71
75
|
def to_4cc
|
72
|
-
OSA.__four_char_code__(self)
|
76
|
+
OSA.__four_char_code__(Iconv.iconv('MACROMAN', 'UTF-8', self).to_s)
|
73
77
|
end
|
74
78
|
end
|
75
79
|
|
@@ -112,7 +116,8 @@ class OSA::Element
|
|
112
116
|
end
|
113
117
|
|
114
118
|
def self.from_rbobj(requested_type, value, enum_group_codes)
|
115
|
-
|
119
|
+
obj = OSA.convert_to_osa(requested_type, value, enum_group_codes)
|
120
|
+
obj.is_a?(OSA::Element) ? obj : self.__new__(*obj)
|
116
121
|
end
|
117
122
|
end
|
118
123
|
|
@@ -152,6 +157,10 @@ class OSA::ObjectSpecifierList
|
|
152
157
|
end
|
153
158
|
alias_method :size, :length
|
154
159
|
|
160
|
+
def empty?
|
161
|
+
length == 0
|
162
|
+
end
|
163
|
+
|
155
164
|
def [](idx)
|
156
165
|
idx += 1 # AE starts counting at 1.
|
157
166
|
o = obj_spec_with_key(OSA::Element.__new__('long', [idx].pack('l')))
|
@@ -236,7 +245,7 @@ module OSA
|
|
236
245
|
end
|
237
246
|
|
238
247
|
def self.convert_to_ruby(osa_object)
|
239
|
-
osa_type = osa_object.__type__
|
248
|
+
osa_type = osa_object.__type__
|
240
249
|
osa_data = osa_object.__data__(osa_type) if osa_type and osa_type != 'null'
|
241
250
|
if conversion = @conversions_to_ruby[osa_type]
|
242
251
|
args = [osa_data, osa_type, osa_object]
|
@@ -245,12 +254,33 @@ module OSA
|
|
245
254
|
end
|
246
255
|
|
247
256
|
def self.convert_to_osa(requested_type, value, enum_group_codes=nil)
|
257
|
+
if requested_type.nil?
|
258
|
+
case value
|
259
|
+
when OSA::Element
|
260
|
+
return value
|
261
|
+
when String
|
262
|
+
requested_type = 'text'
|
263
|
+
when Array
|
264
|
+
requested_type = 'list'
|
265
|
+
when Hash
|
266
|
+
requested_type = 'record'
|
267
|
+
when Integer
|
268
|
+
requested_type = 'integer'
|
269
|
+
else
|
270
|
+
STDERR.puts "can't determine OSA type for #{value}" if $VERBOSE
|
271
|
+
['null', nil]
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
248
275
|
if conversion = @conversions_to_osa[requested_type]
|
249
276
|
args = [value, requested_type]
|
250
277
|
conversion.call(*args[0..(conversion.arity - 1)])
|
251
278
|
elsif enum_group_codes and enum_group_codes.include?(requested_type)
|
252
279
|
['enum', value.code.to_4cc]
|
253
|
-
|
280
|
+
elsif md = /^list_of_(.+)$/.match(requested_type)
|
281
|
+
ary = value.to_a.map { |x| convert_to_osa(md[1], x, enum_group_codes) }
|
282
|
+
ElementList.__new__(ary)
|
283
|
+
else
|
254
284
|
STDERR.puts "unrecognized type #{requested_type}" if $VERBOSE
|
255
285
|
['null', nil]
|
256
286
|
end
|
@@ -363,12 +393,18 @@ module OSA
|
|
363
393
|
end
|
364
394
|
|
365
395
|
# Creates properties.
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
396
|
+
# Add basic properties that might be missing to the Item class (if any).
|
397
|
+
props = {}
|
398
|
+
element.find('property').each do |x|
|
399
|
+
props[x['name']] = [x['code'], x['type'], x['access'], x['description']]
|
400
|
+
end
|
401
|
+
if klass.name[-6..-1] == '::Item'
|
402
|
+
unless props.has_key?('id')
|
403
|
+
props['id'] = ['ID ', 'integer', 'r', 'the unique ID of the item']
|
404
|
+
end
|
405
|
+
end
|
406
|
+
props.each do |name, pary|
|
407
|
+
code, type, access, description = pary
|
372
408
|
setter = (access == nil or access.include?('w'))
|
373
409
|
|
374
410
|
if type == 'reference'
|
@@ -409,7 +445,7 @@ def #{method_name}
|
|
409
445
|
end
|
410
446
|
EOC
|
411
447
|
end
|
412
|
-
|
448
|
+
|
413
449
|
klass.class_eval(method_code)
|
414
450
|
ptypedoc = if pklass.nil?
|
415
451
|
if mod = enum_group_codes[type]
|
@@ -465,7 +501,7 @@ def #{method_name}
|
|
465
501
|
unless OSA.lazy_events?
|
466
502
|
@app.__send_event__('core', 'getd',
|
467
503
|
[['----', Element.__new_object_specifier__(
|
468
|
-
'#{eklass::CODE}', @app == self ? Element.__new__('null', nil) : self,
|
504
|
+
'#{eklass::CODE}'.to_4cc, @app == self ? Element.__new__('null', nil) : self,
|
469
505
|
'indx', Element.__new__('abso', 'all '.to_4cc))]],
|
470
506
|
true).to_rbobj
|
471
507
|
else
|
@@ -569,6 +605,8 @@ EOC
|
|
569
605
|
p_def << defi
|
570
606
|
end
|
571
607
|
|
608
|
+
code = Iconv.iconv('MACROMAN', 'UTF-8', code).to_s
|
609
|
+
|
572
610
|
method_code = <<EOC
|
573
611
|
def %METHOD_NAME%(#{p_dec.join(', ')})
|
574
612
|
@app.__send_event__('#{code[0..3]}', '#{code[4..-1]}', [#{p_def.join(', ')}], #{result != nil})#{result != nil ? '.to_rbobj' : ''}
|
@@ -663,10 +701,7 @@ EOC
|
|
663
701
|
end
|
664
702
|
|
665
703
|
def self.new_element_code(type, varname, enum_group_codes)
|
666
|
-
|
667
|
-
return "#{varname}.is_a?(OSA::Element) ? #{varname} : ElementList.__new__(#{varname}.to_a.map { |x| #{new_element_code(md[1], 'x', enum_group_codes)} })"
|
668
|
-
end
|
669
|
-
"#{varname}.is_a?(OSA::Element) ? #{varname} : Element.from_rbobj('#{type}', #{varname}, #{enum_group_codes.inspect})"
|
704
|
+
"#{varname}.is_a?(OSA::Element) ? #{varname} : Element.from_rbobj('#{type}', #{varname}, #{enum_group_codes.keys.inspect})"
|
670
705
|
end
|
671
706
|
|
672
707
|
def self.escape_string(string)
|
@@ -674,19 +709,20 @@ EOC
|
|
674
709
|
end
|
675
710
|
|
676
711
|
def self.rubyfy_constant_string(string, upcase=false)
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
string =
|
681
|
-
|
712
|
+
string = string.gsub(/[^\w\s]/, '')
|
713
|
+
first = string[0]
|
714
|
+
if (?a..?z).include?(first)
|
715
|
+
string[0] = first.chr.upcase
|
716
|
+
elsif !(?A..?Z).include?(first)
|
717
|
+
string.insert(0, 'C')
|
682
718
|
end
|
683
719
|
escape_string(upcase ? string.upcase : string.gsub(/\s(.)/) { |s| s[1].chr.upcase })
|
684
720
|
end
|
685
721
|
|
686
|
-
RUBY_RESERVED_KEYWORDS = ['for', 'in']
|
722
|
+
RUBY_RESERVED_KEYWORDS = ['for', 'in', 'class']
|
687
723
|
def self.rubyfy_string(string, handle_ruby_reserved_keywords=false)
|
688
724
|
# Prefix with '_' parameter names to avoid possible collisions with reserved Ruby keywords (for, etc...).
|
689
|
-
if RUBY_RESERVED_KEYWORDS.include?(string)
|
725
|
+
if handle_ruby_reserved_keywords and RUBY_RESERVED_KEYWORDS.include?(string)
|
690
726
|
'_' + string
|
691
727
|
else
|
692
728
|
escape_string(string).downcase
|
@@ -694,17 +730,20 @@ EOC
|
|
694
730
|
end
|
695
731
|
|
696
732
|
def self.rubyfy_method(string, klass, return_type=nil, setter=false)
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
733
|
+
base = rubyfy_string(string)
|
734
|
+
s, i = base.dup, 1
|
735
|
+
loop do
|
736
|
+
if setter
|
737
|
+
# Suffix setters with '='.
|
738
|
+
s << '='
|
739
|
+
elsif return_type == 'boolean'
|
740
|
+
# Suffix predicates with '?'.
|
741
|
+
s << '?'
|
742
|
+
end
|
743
|
+
break unless klass.method_defined?(s)
|
744
|
+
# Suffix with an integer if the class already has a method with such a name.
|
745
|
+
i += 1
|
746
|
+
s = base + i.to_s
|
708
747
|
end
|
709
748
|
return s
|
710
749
|
end
|
@@ -718,15 +757,21 @@ EOC
|
|
718
757
|
end
|
719
758
|
end
|
720
759
|
|
721
|
-
# String, force
|
722
|
-
OSA.add_conversion_to_ruby('TEXT'
|
723
|
-
OSA.
|
760
|
+
# String, for unicode stuff force utf8 type if specified.
|
761
|
+
OSA.add_conversion_to_ruby('TEXT') { |value, type, object| object.__data__('TEXT') }
|
762
|
+
OSA.add_conversion_to_ruby('utxt', 'utf8') { |value, type, object| object.__data__(OSA.utf8_strings ? 'utf8' : 'TEXT') }
|
763
|
+
OSA.add_conversion_to_osa('string', 'text') { |value| ['TEXT', value.to_s] }
|
764
|
+
OSA.add_conversion_to_osa('Unicode text') { |value| [OSA.utf8_strings ? 'utf8' : 'TEXT', value.to_s] }
|
724
765
|
|
725
766
|
# Signed/unsigned integer.
|
726
|
-
OSA.add_conversion_to_ruby('shor', 'long'
|
767
|
+
OSA.add_conversion_to_ruby('shor', 'long') { |value| value.unpack('l').first }
|
768
|
+
OSA.add_conversion_to_ruby('comp') { |value| value.unpack('q').first }
|
727
769
|
OSA.add_conversion_to_ruby('magn') { |value| value.unpack('d').first }
|
728
770
|
OSA.add_conversion_to_osa('integer', 'double integer') { |value| ['magn', [value].pack('l')] }
|
729
771
|
|
772
|
+
# Float
|
773
|
+
OSA.add_conversion_to_ruby('sing') { |value| value.unpack('f').first }
|
774
|
+
|
730
775
|
# Boolean.
|
731
776
|
OSA.add_conversion_to_ruby('bool') { |value| value.unpack('c').first != 0 }
|
732
777
|
OSA.add_conversion_to_osa('boolean') { |value| [(value ? 'true'.to_4cc : 'fals'.to_4cc), nil] }
|
@@ -746,16 +791,41 @@ OSA.add_conversion_to_ruby('list') { |value, type, object|
|
|
746
791
|
# File name.
|
747
792
|
# Let's use the 'furl' type here instead of 'alis', as we don't have a way to produce an alias for a file that does not exist yet.
|
748
793
|
OSA.add_conversion_to_osa('alias', 'file') { |value| ['furl', value.to_s] }
|
794
|
+
OSA.add_conversion_to_ruby('alis') { |value, type, object| URI.parse(object.__data__('furl')).path }
|
749
795
|
|
750
796
|
# Hash.
|
751
|
-
OSA.add_conversion_to_ruby('reco') { |value, type, object| object.is_a?(OSA::ElementRecord) ? object.to_hash :
|
797
|
+
OSA.add_conversion_to_ruby('reco') { |value, type, object| object.is_a?(OSA::ElementRecord) ? object.to_hash : value }
|
798
|
+
OSA.add_conversion_to_osa('record') do |value|
|
799
|
+
if value.is_a?(Hash)
|
800
|
+
value.each { |key, val| value[key] = OSA::Element.from_rbobj(nil, val, nil) }
|
801
|
+
OSA::ElementRecord.__new__(value)
|
802
|
+
else
|
803
|
+
value
|
804
|
+
end
|
805
|
+
end
|
752
806
|
|
753
807
|
# Enumerator.
|
754
|
-
OSA.add_conversion_to_ruby('enum') { |value, type, object| OSA::Enumerator.enum_for_code(object.__data__('TEXT')) or
|
808
|
+
OSA.add_conversion_to_ruby('enum') { |value, type, object| OSA::Enumerator.enum_for_code(object.__data__('TEXT')) or object }
|
755
809
|
|
756
810
|
# Class.
|
757
|
-
OSA.add_conversion_to_osa('type class') { |value| value.is_a?(Class) and value.ancestors.include?(OSA::Element) ? ['type', value::CODE.to_4cc] :
|
811
|
+
OSA.add_conversion_to_osa('type class') { |value| value.is_a?(Class) and value.ancestors.include?(OSA::Element) ? ['type', value::CODE.to_4cc] : value }
|
812
|
+
OSA.add_conversion_to_ruby('type') do |value, type, object|
|
813
|
+
if value == 'msng'
|
814
|
+
# Missing values.
|
815
|
+
nil
|
816
|
+
else
|
817
|
+
hash = object.instance_variable_get(:@app).instance_variable_get(:@classes)
|
818
|
+
hash[value] or value
|
819
|
+
end
|
820
|
+
end
|
758
821
|
|
759
822
|
# QuickDraw Rectangle, aka "bounding rectangle".
|
760
823
|
OSA.add_conversion_to_ruby('qdrt') { |value| value.unpack('S4') }
|
761
824
|
OSA.add_conversion_to_osa('bounding rectangle') { |value| ['qdrt', value.pack('S4')] }
|
825
|
+
|
826
|
+
# Pictures (just return the raw data).
|
827
|
+
OSA.add_conversion_to_ruby('PICT') { |value, type, object| value[222..-1] } # Removing trailing garbage.
|
828
|
+
OSA.add_conversion_to_osa('picture') { |value| ['PICT', value.to_s] }
|
829
|
+
OSA.add_conversion_to_ruby('imaA') { |value, type, object| value }
|
830
|
+
OSA.add_conversion_to_osa('Image') { |value| ['imaA', value.to_s] }
|
831
|
+
OSA.add_conversion_to_osa('TIFF picture') { |value| ['TIFF', value.to_s] }
|
data/src/rbosa.c
CHANGED
@@ -27,6 +27,7 @@
|
|
27
27
|
*/
|
28
28
|
|
29
29
|
#include "rbosa.h"
|
30
|
+
#include <st.h>
|
30
31
|
|
31
32
|
static VALUE mOSA;
|
32
33
|
static VALUE cOSAElement;
|
@@ -194,6 +195,54 @@ rbosa_element_new_os (VALUE self, VALUE desired_class, VALUE container, VALUE ke
|
|
194
195
|
return rbosa_element_make (self, &obj_specifier, Qnil);
|
195
196
|
}
|
196
197
|
|
198
|
+
static void
|
199
|
+
__rbosa_raise_potential_app_error (AEDesc *reply)
|
200
|
+
{
|
201
|
+
OSErr error;
|
202
|
+
AEDesc errorNumDesc;
|
203
|
+
AEDesc errorStringDesc;
|
204
|
+
int errorNum;
|
205
|
+
char exception[128];
|
206
|
+
|
207
|
+
if (AEGetParamDesc (reply, keyErrorNumber, typeInteger, &errorNumDesc) != noErr)
|
208
|
+
return;
|
209
|
+
|
210
|
+
if (AEGetDescData (&errorNumDesc, &errorNum, sizeof errorNum) != noErr
|
211
|
+
|| (errorNum = CFSwapInt32HostToBig (errorNum)) == 0) {
|
212
|
+
|
213
|
+
AEDisposeDesc (&errorNumDesc);
|
214
|
+
return;
|
215
|
+
}
|
216
|
+
|
217
|
+
/* The reply is an application error. */
|
218
|
+
|
219
|
+
exception[0] = '\0';
|
220
|
+
error = AEGetParamDesc (reply, keyErrorString, typeChar, &errorStringDesc);
|
221
|
+
if (error == noErr) {
|
222
|
+
Size size;
|
223
|
+
|
224
|
+
size = AEGetDescDataSize (&errorStringDesc);
|
225
|
+
if (size > 0) {
|
226
|
+
char *msg;
|
227
|
+
|
228
|
+
msg = (char *)malloc (size);
|
229
|
+
if (msg != NULL) {
|
230
|
+
if (AEGetDescData (&errorStringDesc, &msg, size) == noErr)
|
231
|
+
snprintf (exception, sizeof exception, "application returned error %d with message '%s'", errorNum, msg);
|
232
|
+
free (msg);
|
233
|
+
}
|
234
|
+
}
|
235
|
+
AEDisposeDesc (&errorStringDesc);
|
236
|
+
}
|
237
|
+
|
238
|
+
if (exception[0] == '\0')
|
239
|
+
snprintf (exception, sizeof exception, "application returned error %d", errorNum);
|
240
|
+
|
241
|
+
AEDisposeDesc (&errorNumDesc);
|
242
|
+
|
243
|
+
rb_raise (rb_eRuntimeError, exception);
|
244
|
+
}
|
245
|
+
|
197
246
|
static VALUE
|
198
247
|
rbosa_app_send_event (VALUE self, VALUE event_class, VALUE event_id, VALUE params, VALUE need_retval)
|
199
248
|
{
|
@@ -249,6 +298,8 @@ rbosa_app_send_event (VALUE self, VALUE event_class, VALUE event_id, VALUE param
|
|
249
298
|
rb_raise (rb_eRuntimeError, "Cannot send Apple Event '%s%s' : %s (%d)",
|
250
299
|
RVAL2CSTR (event_class), RVAL2CSTR (event_id), GetMacOSStatusErrorString (error), error);
|
251
300
|
|
301
|
+
__rbosa_raise_potential_app_error (&reply);
|
302
|
+
|
252
303
|
if (RTEST (need_retval)) {
|
253
304
|
VALUE rb_reply;
|
254
305
|
AEDesc replyObject;
|
@@ -284,18 +335,24 @@ rbosa_element_data (int argc, VALUE *argv, VALUE self)
|
|
284
335
|
void * data;
|
285
336
|
Size datasize;
|
286
337
|
VALUE retval;
|
338
|
+
bool to_4cc;
|
287
339
|
|
288
340
|
rb_scan_args (argc, argv, "01", &coerce_type);
|
341
|
+
to_4cc = false;
|
289
342
|
|
290
343
|
desc = rbosa_element_aedesc (self);
|
291
344
|
|
292
345
|
if (!NIL_P (coerce_type)) {
|
293
|
-
|
346
|
+
FourCharCode code;
|
347
|
+
|
348
|
+
code = RVAL2FOURCHAR (coerce_type);
|
349
|
+
error = AECoerceDesc (desc, code, &coerced_desc);
|
294
350
|
if (error != noErr)
|
295
351
|
rb_raise (rb_eRuntimeError, "Cannot coerce desc to type %s : %s (%d)",
|
296
352
|
RVAL2CSTR (coerce_type), GetMacOSStatusErrorString (error), error);
|
297
353
|
|
298
354
|
desc = &coerced_desc;
|
355
|
+
to_4cc = code == 'type';
|
299
356
|
}
|
300
357
|
|
301
358
|
datasize = AEGetDescDataSize (desc);
|
@@ -304,7 +361,14 @@ rbosa_element_data (int argc, VALUE *argv, VALUE self)
|
|
304
361
|
rb_fatal ("cannot allocate memory");
|
305
362
|
|
306
363
|
error = AEGetDescData (desc, data, datasize);
|
307
|
-
|
364
|
+
if (error == noErr) {
|
365
|
+
if (to_4cc)
|
366
|
+
*(DescType*)data = CFSwapInt32HostToBig (*(DescType*)data);
|
367
|
+
retval = rb_str_new (data, datasize);
|
368
|
+
}
|
369
|
+
else {
|
370
|
+
retval = Qnil;
|
371
|
+
}
|
308
372
|
|
309
373
|
if (!NIL_P (coerce_type))
|
310
374
|
AEDisposeDesc (&coerced_desc);
|
@@ -482,6 +546,42 @@ rbosa_elementlist_size (VALUE self)
|
|
482
546
|
return INT2FIX (__rbosa_elementlist_count ((AEDescList *)rbosa_element_aedesc (self)));
|
483
547
|
}
|
484
548
|
|
549
|
+
static int
|
550
|
+
__rbosa_elementrecord_set (VALUE key, VALUE value, AEDescList *list)
|
551
|
+
{
|
552
|
+
OSErr error;
|
553
|
+
|
554
|
+
error = AEPutKeyDesc (list, RVAL2FOURCHAR (key), rbosa_element_aedesc (value));
|
555
|
+
if (error != noErr)
|
556
|
+
rb_raise (rb_eRuntimeError, "Cannot set value %p for key %p of record %p: %s (%d)",
|
557
|
+
value, key, list, GetMacOSStatusErrorString (error), error);
|
558
|
+
|
559
|
+
return ST_CONTINUE;
|
560
|
+
}
|
561
|
+
|
562
|
+
static VALUE
|
563
|
+
rbosa_elementrecord_new (int argc, VALUE *argv, VALUE self)
|
564
|
+
{
|
565
|
+
OSErr error;
|
566
|
+
AEDescList list;
|
567
|
+
VALUE hash;
|
568
|
+
|
569
|
+
rb_scan_args (argc, argv, "01", &hash);
|
570
|
+
|
571
|
+
if (!NIL_P (hash))
|
572
|
+
Check_Type (hash, T_HASH);
|
573
|
+
|
574
|
+
error = AECreateList (NULL, 0, true, &list);
|
575
|
+
if (error != noErr)
|
576
|
+
rb_raise (rb_eRuntimeError, "Cannot create Apple Event descriptor list : %s (%d)",
|
577
|
+
GetMacOSStatusErrorString (error), error);
|
578
|
+
|
579
|
+
if (!NIL_P (hash))
|
580
|
+
rb_hash_foreach (hash, __rbosa_elementrecord_set, (VALUE)&list);
|
581
|
+
|
582
|
+
return rbosa_element_make (self, &list, Qnil);
|
583
|
+
}
|
584
|
+
|
485
585
|
static VALUE
|
486
586
|
rbosa_elementrecord_to_a (VALUE self)
|
487
587
|
{
|
@@ -541,6 +641,7 @@ Init_osa (void)
|
|
541
641
|
rb_define_method (cOSAElementList, "add", rbosa_elementlist_add, 1);
|
542
642
|
|
543
643
|
cOSAElementRecord = rb_define_class_under (mOSA, "ElementRecord", cOSAElement);
|
644
|
+
rb_define_singleton_method (cOSAElementRecord, "__new__", rbosa_elementrecord_new, -1);
|
544
645
|
rb_define_method (cOSAElementRecord, "to_a", rbosa_elementrecord_to_a, 0);
|
545
646
|
|
546
647
|
mOSAEventDispatcher = rb_define_module_under (mOSA, "EventDispatcher");
|
@@ -548,4 +649,5 @@ Init_osa (void)
|
|
548
649
|
|
549
650
|
rbosa_define_param ("timeout", INT2NUM (kAEDefaultTimeout));
|
550
651
|
rbosa_define_param ("lazy_events", Qtrue);
|
652
|
+
rbosa_define_param ("utf8_strings", Qfalse);
|
551
653
|
}
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: rubyosa
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date:
|
6
|
+
version: 0.2.0
|
7
|
+
date: 2007-01-02 00:00:00 +01:00
|
8
8
|
summary: A Ruby/AppleEvent bridge.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -45,6 +45,10 @@ files:
|
|
45
45
|
- sample/iTunes_inspect.rb
|
46
46
|
- sample/QT_playall.rb
|
47
47
|
- sample/sdef.rb
|
48
|
+
- sample/BBEdit_unix_script.rb
|
49
|
+
- sample/TextEdit_hello_world.rb
|
50
|
+
- sample/iChat_image.rb
|
51
|
+
- sample/iTunes_artwork.rb
|
48
52
|
test_files: []
|
49
53
|
|
50
54
|
rdoc_options: []
|