RUIC 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +201 -0
  4. data/bin/ruic +52 -0
  5. data/gui/MetaData.xml +387 -0
  6. data/gui/TODO +2 -0
  7. data/gui/appattributesmodel.rb +51 -0
  8. data/gui/appelementsmodel.rb +126 -0
  9. data/gui/launch.rb +20 -0
  10. data/gui/makefile +14 -0
  11. data/gui/resources.qrc +37 -0
  12. data/gui/resources/images/Objects-Behavior-Normal.png +0 -0
  13. data/gui/resources/images/Objects-Camera-Normal.png +0 -0
  14. data/gui/resources/images/Objects-Component-Normal.png +0 -0
  15. data/gui/resources/images/Objects-Effect-Normal.png +0 -0
  16. data/gui/resources/images/Objects-Group-Normal.png +0 -0
  17. data/gui/resources/images/Objects-Image-Normal.png +0 -0
  18. data/gui/resources/images/Objects-Layer-Normal.png +0 -0
  19. data/gui/resources/images/Objects-Light-Normal.png +0 -0
  20. data/gui/resources/images/Objects-Material-Normal.png +0 -0
  21. data/gui/resources/images/Objects-Model-Normal.png +0 -0
  22. data/gui/resources/images/Objects-Music-Normal.png +0 -0
  23. data/gui/resources/images/Objects-Property-Normal.png +0 -0
  24. data/gui/resources/images/Objects-References-Normal.png +0 -0
  25. data/gui/resources/images/Objects-Scene-Normal.png +0 -0
  26. data/gui/resources/images/Objects-Sound-Normal.png +0 -0
  27. data/gui/resources/images/Objects-Text-Normal.png +0 -0
  28. data/gui/resources/images/Objects-Unknown-Normal.png +0 -0
  29. data/gui/resources/images/Objects-Video-Normal.png +0 -0
  30. data/gui/resources/images/SCXML.ico +0 -0
  31. data/gui/resources/images/TSCXML.ico +0 -0
  32. data/gui/resources/images/UIA.ico +0 -0
  33. data/gui/resources/images/clipboard.png +0 -0
  34. data/gui/resources/images/console_arrow.png +0 -0
  35. data/gui/resources/images/cross.png +0 -0
  36. data/gui/resources/images/currentline.png +0 -0
  37. data/gui/resources/images/disk-black.png +0 -0
  38. data/gui/resources/images/disks-black.png +0 -0
  39. data/gui/resources/images/document--plus.png +0 -0
  40. data/gui/resources/images/document-copy.png +0 -0
  41. data/gui/resources/images/executable-actions-delete-active.png +0 -0
  42. data/gui/resources/images/executable-actions-delete-idle.png +0 -0
  43. data/gui/resources/images/executable-actions-enter-badge.png +0 -0
  44. data/gui/resources/images/executable-actions-exit-badge.png +0 -0
  45. data/gui/resources/images/folder-horizontal--plus.png +0 -0
  46. data/gui/resources/images/folder-horizontal-open.png +0 -0
  47. data/gui/resources/images/gear.png +0 -0
  48. data/gui/resources/images/invalid_breakpoint.png +0 -0
  49. data/gui/resources/images/scissors-blue.png +0 -0
  50. data/gui/resources/images/slide-16-active.png +0 -0
  51. data/gui/resources/images/slide-16-master.png +0 -0
  52. data/gui/resources/images/slide-16-normal.png +0 -0
  53. data/gui/resources/images/slide-32-active.png +0 -0
  54. data/gui/resources/images/slide-32-master.png +0 -0
  55. data/gui/resources/images/slide-32-normal.png +0 -0
  56. data/gui/resources/images/studio_architect32.ico +0 -0
  57. data/gui/resources/images/studio_architect32.png +0 -0
  58. data/gui/resources/images/textfile_icon.png +0 -0
  59. data/gui/resources/style/checkbox.png +0 -0
  60. data/gui/resources/style/dark.qss +459 -0
  61. data/gui/resources/style/down_arrow.png +0 -0
  62. data/gui/resources/style/handle.png +0 -0
  63. data/gui/window.rb +90 -0
  64. data/gui/window.ui +753 -0
  65. data/lib/ruic.rb +59 -0
  66. data/lib/ruic/application.rb +129 -0
  67. data/lib/ruic/asset_classes.rb +448 -0
  68. data/lib/ruic/behaviors.rb +31 -0
  69. data/lib/ruic/interfaces.rb +36 -0
  70. data/lib/ruic/presentation.rb +354 -0
  71. data/lib/ruic/statemachine.rb +111 -0
  72. data/lib/ruic/version.rb +3 -0
  73. data/ruic.gemspec +23 -0
  74. data/test/MetaData.xml +387 -0
  75. data/test/customclasses.ruic +21 -0
  76. data/test/filtering.ruic +39 -0
  77. data/test/nonmaster.ruic +21 -0
  78. data/test/projects/CustomClasses/Brush Strokes.effect +38 -0
  79. data/test/projects/CustomClasses/CustomClasses.uia +6 -0
  80. data/test/projects/CustomClasses/CustomClasses.uip +34 -0
  81. data/test/projects/CustomClasses/copper.material +194 -0
  82. data/test/projects/CustomClasses/maps/UV-Checker.png +0 -0
  83. data/test/projects/CustomClasses/maps/effects/brushnoise.dds +0 -0
  84. data/test/projects/CustomClasses/maps/materials/spherical_checker.png +0 -0
  85. data/test/projects/ReferencedMaterials/ReferencedMaterials.uia +6 -0
  86. data/test/projects/ReferencedMaterials/ReferencedMaterials.uip +39 -0
  87. data/test/properties.ruic +81 -0
  88. data/test/referencematerials.ruic +53 -0
  89. data/test/usage.ruic +20 -0
  90. metadata +166 -0
@@ -0,0 +1,59 @@
1
+ #encoding:utf-8
2
+ class RUIC; end
3
+ module UIC; end
4
+
5
+ require 'nokogiri'
6
+ require_relative 'ruic/version'
7
+ require_relative 'ruic/asset_classes'
8
+ require_relative 'ruic/interfaces'
9
+ require_relative 'ruic/application'
10
+ require_relative 'ruic/behaviors'
11
+ require_relative 'ruic/statemachine'
12
+ require_relative 'ruic/presentation'
13
+
14
+ class RUIC
15
+ DEFAULTMETADATA = 'C:/Program Files (x86)/NVIDIA Corporation/UI Composer 8.0/res/DataModelMetadata/en-us/MetaData.xml'
16
+ def self.run(ruic_path)
17
+ script = File.read(ruic_path,encoding:'utf-8')
18
+ Dir.chdir(File.dirname(ruic_path)) do
19
+ self.new.instance_eval(script,ruic_path)
20
+ end
21
+ end
22
+ def initialize( metadata=DEFAULTMETADATA )
23
+ @metadata = metadata
24
+ @apps = {}
25
+ end
26
+ def metadata(path)
27
+ @metadata = path
28
+ end
29
+ def uia(path)
30
+ meta = UIC.Meta @metadata
31
+ name = @apps.empty? ? :app : :"app#{@apps.length+1}"
32
+ @apps[name] = UIC.App(meta,path)
33
+ end
34
+ def method_missing(name,*a)
35
+ @apps[name] || super
36
+ end
37
+ def assert(condition=:CONDITIONNOTSUPPLIED,msg=nil,&block)
38
+ if block && condition==:CONDITIONNOTSUPPLIED || condition.is_a?(String)
39
+ msg = condition.is_a?(String) ? condition : yield
40
+ condition = msg.is_a?(String) ? eval(msg,block.binding) : msg
41
+ end
42
+ unless condition
43
+ file, line, _ = caller.first.split(':')
44
+ puts "#{msg && "#{msg} : "}assertion failed (#{file} line #{line})"
45
+ exit 1
46
+ end
47
+ end
48
+ def show(*a); puts *a.map(&:to_s); end
49
+ end
50
+
51
+ def RUIC(file_path=nil,&block)
52
+ if block
53
+ Dir.chdir(File.dirname($0)) do
54
+ RUIC.new.instance_eval(&block)
55
+ end
56
+ else
57
+ RUIC.run(file_path)
58
+ end
59
+ end
@@ -0,0 +1,129 @@
1
+ class UIC::Application
2
+ include UIC::FileBacked
3
+
4
+ attr_reader :metadata
5
+ def initialize(metadata,uia_path)
6
+ @metadata = metadata
7
+ self.file = uia_path
8
+ load_from_file if file_found?
9
+ end
10
+
11
+ def load_from_file
12
+ self.doc = Nokogiri.XML(File.read(file,encoding:'utf-8'))
13
+ @assets = @doc.search('assets *').map do |el|
14
+ case el.name
15
+ when 'behavior' then UIC::Application::Behavior
16
+ when 'statemachine' then UIC::Application::StateMachine
17
+ when 'presentation' then UIC::Application::Presentation
18
+ when 'renderplugin' then UIC::Application::RenderPlugin
19
+ end.new(self,el)
20
+ end.group_by{ |asset| asset.el.name }
21
+ end
22
+
23
+ def errors?
24
+ !errors.empty?
25
+ end
26
+
27
+ def errors
28
+ file_found? ? assets.flat_map(&:errors) : ["File not found: '#{file}'"]
29
+ end
30
+
31
+ def [](asset_id_or_path)
32
+ all = assets
33
+ if asset_id_or_path.start_with?('#')
34
+ id = asset_id_or_path[1..-1]
35
+ all.find{ |asset| asset.id==id }
36
+ else
37
+ full_path = File.expand_path(asset_id_or_path,File.dirname(file))
38
+ all.find{ |asset| asset.file==full_path }
39
+ end
40
+ end
41
+
42
+ def unused_files
43
+ referenced_files - directory_files
44
+ end
45
+
46
+ def referenced_files
47
+ # TODO: state machines can reference external scripts
48
+ # TODO: behaviors can reference external scripts
49
+ assets.map{ |asset| path_to(asset.src) }
50
+ + presentations.flat_map{ |pres| pres.presentation.referenced_files }
51
+ end
52
+
53
+ def assets
54
+ @assets.values.inject(:+)
55
+ end
56
+
57
+ def main_presentation
58
+ initial_id = @doc.at('assets')['initial']
59
+ presos = presentations
60
+ presos.find{ |pres| pres.id==initial_id } || presos.first
61
+ end
62
+
63
+ def main_presentation=(presentation)
64
+ # TODO: set to Presentation or PresentationAsset
65
+ # TODO: create a unique ID if none exists
66
+ @doc.at('assets')['initial'] = presentation.id
67
+ end
68
+
69
+ def image_usage
70
+ Hash[
71
+ (presentations + statemachines)
72
+ .map(&:image_usage)
73
+ .inject{ |h1,h2| h1.merge(h2){ |path,els1,els2| [*els1,*els2] } }
74
+ .sort_by do |path,assets|
75
+ parts = path.downcase.split '/'
76
+ [ parts.length, parts ]
77
+ end
78
+ ].tap{ |h| h.extend(UIC::PresentableHash) }
79
+ end
80
+
81
+ def image_paths
82
+ image_usage.keys
83
+ end
84
+
85
+ def presentations
86
+ @assets['presentation'] ||= []
87
+ end
88
+
89
+ def behaviors
90
+ @assets['behavior'] ||= []
91
+ end
92
+
93
+ def statemachines
94
+ @assets['statemachine'] ||= []
95
+ end
96
+
97
+ def renderplugins
98
+ @assets['renderplugin'] ||= []
99
+ end
100
+
101
+ def save_all!
102
+ # save!
103
+ presentations.each(&:save!)
104
+ end
105
+
106
+ def at(path)
107
+ parts = path.split(':')
108
+ preso = parts.length==2 ? self["##{parts.first}"] : main_presentation
109
+ raise "Cannot find presentation for #{id}" unless preso
110
+ preso.at(parts.last)
111
+ end
112
+ alias_method :/, :at
113
+
114
+ def xml
115
+ @doc.to_xml
116
+ end
117
+ end
118
+
119
+ class << UIC
120
+ def Application(metadata,uia_path)
121
+ UIC::Application.new( metadata, uia_path )
122
+ end
123
+ alias_method :App, :Application
124
+ end
125
+
126
+ __END__
127
+ <?xml version="1.0" encoding="UTF-8" ?>
128
+ <application xmlns="http://nvidia.com/uicomposer"><assets/></application>
129
+
@@ -0,0 +1,448 @@
1
+ #encoding: utf-8
2
+ require 'set'
3
+ $depth = 0
4
+ class UIC::Asset
5
+ class Root
6
+ @properties = {}
7
+ class << self
8
+ attr_reader :name
9
+ def properties
10
+ (ancestors[1].respond_to?(:properties) ? ancestors[1].properties : {}).merge(@properties)
11
+ end
12
+
13
+ def each
14
+ (@by_name.values - [self]).each{ |klass| yield klass }
15
+ end
16
+ include Enumerable
17
+
18
+ def inspect
19
+ "<#{@name}>"
20
+ end
21
+ end
22
+
23
+ def properties
24
+ self.class.properties
25
+ end
26
+
27
+ def at(sub_path)
28
+ presentation.at(sub_path,@el)
29
+ end
30
+ alias_method :/, :at
31
+
32
+ attr_accessor :presentation, :el
33
+ def initialize( presentation, element )
34
+ @presentation = presentation
35
+ @el = element
36
+ end
37
+
38
+ def type
39
+ self.class.name
40
+ # self.class.name.split('::').last
41
+ end
42
+
43
+ def parent
44
+ presentation.parent_asset(@el)
45
+ end
46
+
47
+ def children
48
+ presentation.child_assets(@el)
49
+ end
50
+
51
+ def find(criteria={},&block)
52
+ criteria[:under] ||= self
53
+ presentation.find(criteria,&block)
54
+ end
55
+
56
+ # Find the owning component (even if you are a component)
57
+ def component
58
+ presentation.owning_component(@el)
59
+ end
60
+
61
+ def component?
62
+ @el.name == 'Component'
63
+ end
64
+
65
+ def master?
66
+ presentation.master?(@el)
67
+ end
68
+
69
+ def slide?
70
+ false
71
+ end
72
+
73
+ def has_slide?(slide_name_or_index)
74
+ presentation.has_slide?(@el,slide_name_or_index)
75
+ end
76
+
77
+ def slides
78
+ presentation.slides_for(@el)
79
+ end
80
+
81
+ def on_slide(slide_name_or_index)
82
+ if has_slide?(slide_name_or_index)
83
+ UIC::SlideValues.new( self, slide_name_or_index )
84
+ end
85
+ end
86
+
87
+ def path
88
+ @path ||= @presentation.path_to(@el)
89
+ end
90
+
91
+ def name
92
+ properties['name'].get( self, presentation.slide_index(@el) )
93
+ end
94
+
95
+ def name=( new_name )
96
+ properties['name'].set( self, new_name, presentation.slide_index(@el) )
97
+ end
98
+
99
+ # Get the value(s) of an attribute
100
+ def [](attribute_name, slide_name_or_index=nil)
101
+ if property = properties[attribute_name]
102
+ if slide_name_or_index
103
+ property.get( self, slide_name_or_index ) if has_slide?(slide_name_or_index)
104
+ else
105
+ UIC::ValuesPerSlide.new(@presentation,self,property)
106
+ end
107
+ end
108
+ end
109
+
110
+ # Set the value of an attribute, either across all slides, or on a particular slide
111
+ # el['foo'] = 42
112
+ # el['foo',0] = 42
113
+ def []=( attribute_name, slide_name_or_index=nil, new_value )
114
+ if property = properties[attribute_name] then
115
+ property.set(self,new_value,slide_name_or_index)
116
+ end
117
+ end
118
+
119
+ def to_xml
120
+ @el.to_xml
121
+ end
122
+ def inspect
123
+ "<asset #{@el.name}##{@el['id']}>"
124
+ end
125
+
126
+ def to_s
127
+ "<#{type} #{path}>"
128
+ end
129
+
130
+ def ==(other)
131
+ (self.class==other.class) && (el==other.el)
132
+ end
133
+ alias_method :eql?, :==
134
+ end
135
+
136
+ attr_reader :by_name
137
+
138
+ HIER = {}
139
+ %w[Asset Slide Scene].each{ |s| HIER[s] = 'Root' }
140
+ %w[Node Behavior Effect Image Layer MaterialBase RenderPlugin].each{ |s| HIER[s]='Asset' }
141
+ %w[Camera Component Group Light Model Text].each{ |s| HIER[s]='Node' }
142
+ %w[Material ReferencedMaterial].each{ |s| HIER[s]='MaterialBase' }
143
+
144
+ def initialize(xml)
145
+ @by_name = {'Root'=>Root}
146
+
147
+ doc = Nokogiri.XML(xml)
148
+ hack_in_slide_names!(doc)
149
+
150
+ HIER.each do |class_name,parent_class_name|
151
+ parent_class = @by_name[parent_class_name]
152
+ el = doc.root.at(class_name)
153
+ @by_name[class_name] = create_class(el,parent_class,el.name)
154
+ UIC::Asset.const_set( el.name, @by_name[class_name] ) # give the class instance a name by pointing a constant to it :/
155
+ end
156
+
157
+ # Extend well-known classes with script interfaces after they are created
158
+ @by_name['State'] = @by_name['Slide']
159
+ @by_name['Slide'].instance_eval do
160
+ attr_accessor :index, :name
161
+ define_method :inspect do
162
+ "<slide ##{index} of #{@el['component'] || @el.parent['component']}>"
163
+ end
164
+ define_method(:slide?){ true }
165
+ end
166
+
167
+ refmat = @by_name['ReferencedMaterial']
168
+ @by_name['MaterialBase'].instance_eval do
169
+ define_method :replace_with_referenced_material do
170
+ type=='ReferencedMaterial' ? self : presentation.replace_asset( self, 'ReferencedMaterial', name:name )
171
+ end
172
+ end
173
+ end
174
+
175
+ # Creates a class from MetaData.xml with accessors for the <Property> listed.
176
+ # Instances of the class are associated with a presentation and know how to
177
+ # get/set values in that XML based on value types, slides, defaults.
178
+ # Also used to create classes from effects, materials, and behavior preambles.
179
+ def create_class(el,parent_class,name='CustomAsset')
180
+ Class.new(parent_class) do
181
+ @name = name
182
+ @properties = Hash[ el.css("Property").map do |e|
183
+ type = e['type'] || (e['list'] ? 'String' : 'Float')
184
+ type = "Float" if type=="float"
185
+ property = UIC::Property.const_get(type).new(e)
186
+ [ property.name, UIC::Property.const_get(type).new(e) ]
187
+ end ]
188
+ end
189
+ end
190
+
191
+ def new_instance(presentation,el)
192
+ @by_name[el.name].new(presentation,el)
193
+ end
194
+
195
+ def hack_in_slide_names!(doc)
196
+ doc.at('Slide') << '<Property name="name" formalName="Name" type="String" default="Slide" hidden="True" />'
197
+ end
198
+ end
199
+
200
+ def UIC.Meta(metadata_path)
201
+ UIC::Asset.new(File.read(metadata_path,encoding:'utf-8'))
202
+ end
203
+
204
+ class UIC::Property
205
+ class << self; attr_accessor :default; end
206
+ def initialize(el); @el = el; end
207
+ def name; @name||=@el['name']; end
208
+ def type; @type||=@el['type']; end
209
+ def formal; @formal||=@el['formalName'] || @el['name']; end
210
+ def min; @el['min']; end
211
+ def max; @el['max']; end
212
+ def description; @desc||=@el['description']; end
213
+ def default; @def ||= (@el['default'] || self.class.default); end
214
+ def get(asset,slide)
215
+ if asset.slide? || asset.has_slide?(slide)
216
+ asset.presentation.get_attribute(asset.el,name,slide) || default
217
+ end
218
+ end
219
+ def set(asset,new_value,slide_name_or_index)
220
+ asset.presentation.set_attribute(asset.el,name,slide_name_or_index,new_value)
221
+ end
222
+ def inspect
223
+ "<#{type} '#{name}'>"
224
+ end
225
+
226
+ class String < self
227
+ self.default = ''
228
+ end
229
+ MultiLineString = String
230
+
231
+ class Float < self
232
+ self.default = 0.0
233
+ def get(asset,slide); super.to_f; end
234
+ end
235
+ class Long < self
236
+ self.default = 0
237
+ def get(asset,slide); super.to_i; end
238
+ end
239
+ class Boolean < self
240
+ self.default = false
241
+ def get(asset,slide); super=='True'; end
242
+ def set(asset,new_value,slide_name_or_index)
243
+ super( asset, new_value ? 'True' : 'False', slide_name_or_index )
244
+ end
245
+ end
246
+ class Vector < self
247
+ self.default = '0 0 0'
248
+ def get(asset,slide)
249
+ VectorValue.new(asset,self,slide,super)
250
+ end
251
+ def set(asset,new_value,slide_name_or_index)
252
+ new_value = new_value.join(' ') if new_value.is_a?(Array)
253
+ super( asset, new_value, slide_name_or_index )
254
+ end
255
+ end
256
+ Rotation = Vector
257
+ Color = Vector
258
+ class Image < self
259
+ self.default = nil
260
+ def get(asset,slide)
261
+ if idref = super
262
+ result = asset.presentation.asset_by_id( idref[1..-1] )
263
+ slide ? result.on_slide( slide ) : result
264
+ end
265
+ end
266
+ def set(asset,new_value,slide)
267
+ raise "Setting image attributes not yet supported"
268
+ end
269
+ end
270
+ class Texture < String
271
+ def get(asset,slide)
272
+ if path=super
273
+ path.empty? ? nil : path
274
+ end
275
+ end
276
+ end
277
+
278
+ class ObjectRef < self
279
+ self.default = nil
280
+ def get(asset,slide)
281
+ ref = super
282
+ type = :absolute
283
+ obj = nil
284
+ unless ref=='' || ref.nil?
285
+ type = ref[0]=='#' ? :absolute : :path
286
+ ref = type==:absolute ? asset.presentation.asset_by_id( ref[1..-1] ) : asset.presentation.at( ref, asset.el )
287
+ end
288
+ ObjectReference.new(asset,self,slide,ref,type)
289
+ end
290
+ def set(asset,new_object,slide)
291
+ get(asset,slide).object = new_object
292
+ end
293
+ end
294
+
295
+ class ObjectReference
296
+ attr_reader :object, :type
297
+ def initialize(asset,property,slide,object=nil,type=nil)
298
+ @asset = asset
299
+ @name = property.name
300
+ @slide = slide
301
+ @object = object
302
+ @type = type
303
+ end
304
+ def object=(new_object)
305
+ raise "ObjectRef must be set to an asset (not a #{new_object.class.name})" unless new_object.is_a?(UIC::Asset::Root)
306
+ @object = new_object
307
+ write_value!
308
+ end
309
+ def type=(new_type)
310
+ raise "ObjectRef types must be either :absolute or :path (not #{new_type.inspect})" unless [:absolute,:path].include?(new_type)
311
+ @type = new_type
312
+ write_value!
313
+ end
314
+ private
315
+ def write_value!
316
+ path = case @object
317
+ when NilClass then ""
318
+ else case @type
319
+ when :absolute then "##{@object.el['id']}"
320
+ when :path then @asset.presentation.path_to( @object.el, @asset.el ).sub(/^[^:.]+:/,'')
321
+ # when :root then @asset.presentation.path_to( @object.el ).sub(/^[^:.]+:/,'')
322
+ end
323
+ end
324
+ @asset.presentation.set_attribute( @asset.el, @name, @slide, path )
325
+ end
326
+ end
327
+
328
+ Import = String #TODO: a real class
329
+ Mesh = String #TODO: a real class
330
+ Renderable = String #TODO: a real class
331
+ Font = String #TODO: a real class
332
+ FontSize = Long
333
+
334
+ StringListOrInt = String #TODO: a real class
335
+
336
+ class VectorValue
337
+ attr_reader :x, :y, :z
338
+ def initialize(asset,property,slide,str)
339
+ @asset = asset
340
+ @property = property
341
+ @slide = slide
342
+ @x, @y, @z = str.split(/\s+/).map(&:to_f)
343
+ end
344
+ def setall
345
+ @property.set( @asset, to_s, @slide )
346
+ end
347
+ def x=(n); @x=n; setall end
348
+ def y=(n); @y=n; setall end
349
+ def z=(n); @z=n; setall end
350
+ alias_method :r, :x
351
+ alias_method :g, :y
352
+ alias_method :b, :z
353
+ alias_method :r=, :x=
354
+ alias_method :g=, :y=
355
+ alias_method :b=, :z=
356
+ def inspect
357
+ "<#{@asset.path}.#{@property.name}: #{self}>"
358
+ end
359
+ def to_s
360
+ to_a.join(' ')
361
+ end
362
+ def to_a
363
+ [x,y,z]
364
+ end
365
+ end
366
+ end
367
+
368
+ class UIC::SlideCollection
369
+ include Enumerable
370
+ attr_reader :length
371
+ def initialize(slides)
372
+ @length = slides.length-1
373
+ @slides = slides
374
+ @lookup = {}
375
+ slides.each do |s|
376
+ @lookup[s.index] = s
377
+ @lookup[s.name] = s
378
+ end
379
+ end
380
+ def each
381
+ @slides.each{ |s| yield(s) }
382
+ end
383
+ def [](index_or_name)
384
+ @lookup[ index_or_name ]
385
+ end
386
+ def inspect
387
+ "[ #{@slides.map(&:inspect).join ', '} ]"
388
+ end
389
+ def to_ary
390
+ @slides
391
+ end
392
+ end
393
+
394
+ class UIC::ValuesPerSlide
395
+ def initialize(presentation,asset,property)
396
+ raise unless presentation.is_a?(UIC::Presentation)
397
+ raise unless asset.is_a?(UIC::Asset::Root)
398
+ raise unless property.is_a?(UIC::Property)
399
+ @preso = presentation
400
+ @asset = asset
401
+ @el = asset.el
402
+ @property = property
403
+ end
404
+ def value
405
+ values.first
406
+ end
407
+ def [](slide_name_or_index)
408
+ @property.get( @asset, slide_name_or_index )
409
+ end
410
+ def []=(slide_name_or_index,new_value)
411
+ @property.set( @asset, new_value, slide_name_or_index )
412
+ end
413
+ def linked?
414
+ @preso.attribute_linked?(@el,@property.name)
415
+ end
416
+ def unlink
417
+ @preso.unlink_attribute( @el, @property.name )
418
+ end
419
+ def link
420
+ @preso.link_attribute( @el, @property.name )
421
+ end
422
+ def values
423
+ @asset.slides.map{ |s| self[s.name] }
424
+ end
425
+ def inspect
426
+ "<Values of '#{@asset.name}.#{@property.name}' across slides>"
427
+ end
428
+ alias_method :to_s, :inspect
429
+ end
430
+
431
+ class UIC::SlideValues
432
+ def initialize( asset, slide )
433
+ @asset = asset
434
+ @slide = slide
435
+ end
436
+ def [](attribute_name)
437
+ @asset[attribute_name,@slide]
438
+ end
439
+ def []=( attribute_name, new_value )
440
+ @asset[attribute_name,@slide] = new_value
441
+ end
442
+ def method_missing( name, *args, &blk )
443
+ asset.send(name,*args,&blk)
444
+ end
445
+ def inspect
446
+ "<#{@asset.inspect} on slide #{@slide.inspect}>"
447
+ end
448
+ end