Added border_fill_middle attribute to ImageDescription.
[redact.git] / lib / redact / part.rb
1 #--
2 # $Id: part.rb 29 2005-04-16 15:59:07Z tilman $
3 #
4 # Copyright (c) 2005 Tilman Sauerbeck (tilman at code-monkey de)
5 #
6 # Permission is hereby granted, free of charge, to any person obtaining
7 # a copy of this software and associated documentation files (the
8 # "Software"), to deal in the Software without restriction, including
9 # without limitation the rights to use, copy, modify, merge, publish,
10 # distribute, sublicense, and/or sell copies of the Software, and to
11 # permit persons to whom the Software is furnished to do so, subject to
12 # the following conditions:
13 #
14 # The above copyright notice and this permission notice shall be
15 # included in all copies or substantial portions of the Software.
16 #
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25 module Redact
26         class Part
27                 TYPE_RECTANGLE = 1
28                 TYPE_TEXT = 2
29                 TYPE_IMAGE = 3
30                 TYPE_SWALLOW = 4
31
32                 include Comparable
33
34                 attr_reader :collection, :id, :name, :dragable, :clip,
35                             :mouse_events, :repeat_events
36
37                 def initialize(collection, id, name)
38                         @collection = collection
39                         @id = id
40
41                         @name = name.to_str.dup.freeze
42                         @type = TYPE_RECTANGLE
43                         @mouse_events = true
44                         @repeat_events = false
45                         @clip = nil
46                         @dragable = Dragable.new(self)
47
48                         @descriptions = Hash.new do |h, k|
49                                 desc, value = k.split("\0")
50                                 value = value.to_f
51
52                                 h[k] = description_class.new(desc, value)
53                         end
54                 end
55
56                 def <=>(b)
57                         @id <=> b.id
58                 end
59
60                 def mouse_events=(val)
61                         @mouse_events = (val == true)
62                 end
63
64                 def repeat_events=(val)
65                         @repeat_events = (val == true)
66                 end
67
68                 def clip=(part)
69                         if part == self
70                                 raise(ArgumentError, "cannot clip part to itself")
71                         elsif !part.nil? && part.collection != @collection
72                                 raise(ArgumentError, "items not in the same collection")
73                         else
74                                 @clip = part
75                         end
76                 end
77
78                 def description(name = "default", value = 0.0) # :yields: desc
79                         d = @descriptions[desc_key(name, value)]
80
81                         block_given? ? (yield d) : d
82                 end
83
84                 protected
85                 def description_class
86                         Description
87                 end
88
89                 def to_eet_name
90                         "Edje_Part"
91                 end
92
93                 def to_eet_properties
94                         other_desc = @descriptions.dup
95                         other_desc.delete(desc_key("default", 0.0))
96
97                         confine_id = @dragable.confine.nil? ?
98                                          -1 : @dragable.confine.id
99
100                         {"name" => [@name],
101                          "id" => [@id],
102                          "type" => [@type, :char],
103                          "effect" => [0, :char],
104                          "mouse_events" => [@mouse_events],
105                          "repeat_events" => [@repeat_events],
106                          "clip_to_id" => [@clip.nil? ? -1 : @clip.id],
107                          "default_desc" => [description("default", 0.0)],
108                          "other_desc" => [other_desc],
109                          "dragable.x" => [@dragable.enabled[0], :char],
110                          "dragable.step_x" => [@dragable.step[0]],
111                          "dragable.count_x" => [@dragable.count[0]],
112                          "dragable.y" => [@dragable.enabled[1], :char],
113                          "dragable.step_y" => [@dragable.step[1]],
114                          "dragable.count_y" => [@dragable.count[1]],
115                          "dragable.counfine_id" => [confine_id]} # not a typo!
116                 end
117
118                 private
119                 def desc_key(name, value)
120                         name + "\0" + value.to_s
121                 end
122         end
123
124         class SwallowPart < Part
125                 def initialize(collection, id, name)
126                         super
127
128                         @type = TYPE_SWALLOW
129                 end
130         end
131
132         class TextPart < Part
133                 attr_accessor :effect
134
135                 def initialize(collection, id, name)
136                         super
137
138                         @type = TYPE_TEXT
139                         @effect = :none
140                 end
141
142                 protected
143                 def description_class
144                         TextDescription
145                 end
146
147                 def to_eet_properties
148                         effect = case @effect
149                         when :none: 0
150                         when :plain: 1
151                         when :outline: 2
152                         when :soft_outline: 3
153                         when :shadow: 4
154                         when :soft_shadow: 5
155                         when :outline_shadow: 6
156                         when :outline_soft_shadow: 7
157                         else
158                                 raise(RedactError, "invalid effect value - #{@effect}")
159                         end
160
161                         super.merge!({"effect" => [effect, :char]})
162                 end
163         end
164
165         class ImagePart < Part
166                 def initialize(collection, id, name)
167                         super
168
169                         @type = TYPE_IMAGE
170                 end
171
172                 protected
173                 def description_class
174                         ImageDescription
175                 end
176         end
177
178         class Dragable
179                 attr_reader :enabled, :step, :count, :confine
180
181                 def initialize(part)
182                         @part= part
183
184                         @enabled = [false, false]
185                         @step = [0, 0]
186                         @count = [0, 0]
187                         @confine = nil
188                 end
189
190                 def confine=(part)
191                         if part == @part
192                                 raise(ArgumentError, "cannot confine part to itself")
193                         elsif !part.nil? && part.collection != @part.collection
194                                 raise(ArgumentError, "items not in the same collection")
195                         else
196                                 @confine = part
197                         end
198                 end
199         end
200
201         class Relation
202                 attr_reader :rel, :to_id, :offset
203
204                 def initialize(rel, offset)
205                         @rel = [rel, rel]
206                         @to_id = [-1, -1]
207                         @offset = [offset, offset]
208                 end
209
210                 def set_rel(x, y)
211                         @rel = [x, y]
212                 end
213
214                 def set_offset(x, y)
215                         @offset = [x, y]
216                 end
217
218                 def to=(part)
219                         @to_id = [].fill(part.nil? ? -1 : part.id, 0..1)
220                 end
221         end
222
223         class Description
224                 attr_reader :rel, :aspect, :step, :visible, :color_class
225                 attr_accessor :aspect_preference
226
227                 def initialize(name = "default", value = 0.0)
228                         @name = name.to_str.dup.freeze
229                         @value = value.freeze
230                         @visible = true
231                         @align = [0.5, 0.5]
232                         @min = [0, 0]
233                         @max = [-1, -1]
234                         @step = [0, 0]
235                         @aspect = [0.0, 0.0]
236                         @aspect_preference = :none
237                         @rel = [Relation.new(0.0, 0), Relation.new(1.0, -1)]
238                         @color = [].fill(255, 0..3)
239                         @color_class = ""
240                 end
241
242                 def visible=(v)
243                         @visible = (v == true)
244                 end
245
246                 def color_class=(v)
247                         @color_class = v.to_str.dup
248                 end
249
250                 def set_step(x = 0, y = 0)
251                         @step = [x, y]
252                 end
253
254                 def set_aspect(x = 0.0, y = 0.0)
255                         @aspect = [x, y]
256                 end
257
258                 def set_align(x = 0.5, y = 0.5)
259                         @align = [x, y]
260                 end
261
262                 def set_size(w, h)
263                         set_min(w, h)
264                         set_max(w, h)
265                 end
266
267                 def set_min(w, h)
268                         @min = [w, h]
269                 end
270
271                 def set_max(w, h)
272                         @max = [w, h]
273                 end
274
275                 def color=(c)
276                         @color = parse_hex_color(c)
277                 end
278
279                 protected
280                 def parse_hex_color(c)
281                         md = c.match(/^#?(([[:xdigit:]]{2}){1,4})$/)
282                         if md.nil?
283                                 raise(ArgumentError, "Argument is not a hex string")
284                         end
285
286                         pairs = md.captures.shift.split(/(..)/).delete_if do |item|
287                                 item == ""
288                         end
289
290                         pairs.push("00") while pairs.length < 3
291                         pairs.push("ff") if pairs.length == 3
292
293                         pairs.map { |p| p.hex }
294                 end
295
296                 def to_eet_name
297                         "Edje_Part_Description"
298                 end
299
300                 def to_eet_properties
301                         asp_pref = case @aspect_preference
302                         when :none: 0
303                         when :vertical: 1
304                         when :horizontal: 2
305                         when :both: 3
306                         else
307                                 raise(RedactError, "invalid aspect preference value - " +
308                                       @aspect_preference.to_s)
309                         end
310
311                         {"state.name" => [@name],
312                          "state.value" => [@value, :double],
313                          "visible" => [@visible],
314                          "align.x" => [@align[0], :double],
315                          "align.y" => [@align[1], :double],
316                          "min.w" => [@min[0]],
317                          "min.h" => [@min[1]],
318                          "max.w" => [@max[0]],
319                          "max.h" => [@max[1]],
320                          "step.x" => [@step[0]],
321                          "step.y" => [@step[1]],
322                          "aspect.min" => [@aspect[0], :double],
323                          "aspect.max" => [@aspect[1], :double],
324                          "aspect.prefer" => [asp_pref, :char],
325                          "rel1.relative_x" => [@rel[0].rel[0], :double],
326                          "rel1.relative_y" => [@rel[0].rel[1], :double],
327                          "rel1.offset_x" => [@rel[0].offset[0]],
328                          "rel1.offset_y" => [@rel[0].offset[1]],
329                          "rel1.id_x" => [@rel[0].to_id[0]],
330                          "rel1.id_y" => [@rel[0].to_id[1]],
331                          "rel2.relative_x" => [@rel[1].rel[0], :double],
332                          "rel2.relative_y" => [@rel[1].rel[1], :double],
333                          "rel2.offset_x" => [@rel[1].offset[0]],
334                          "rel2.offset_y" => [@rel[1].offset[1]],
335                          "rel2.id_x" => [@rel[1].to_id[0]],
336                          "rel2.id_y" => [@rel[1].to_id[1]],
337                          "color_class" => [@color_class],
338                          "color.r" => [@color[0], :char],
339                          "color.g" => [@color[1], :char],
340                          "color.b" => [@color[2], :char],
341                          "color.a" => [@color[3], :char],
342
343                          # image properties
344                          "image.id" => [-1],
345                          "image.tween_list" => [nil],
346                          "border.l" => [0],
347                          "border.r" => [0],
348                          "border.t" => [0],
349                          "border.b" => [0],
350                          "border.no_fill" => [false],
351                          "fill.smooth" => [true],
352                          "fill.pos_rel_x" => [0.0, :double],
353                          "fill.pos_abs_x" => [0],
354                          "fill.rel_x" => [1.0, :double],
355                          "fill.abs_x" => [0],
356                          "fill.pos_rel_y" => [0.0, :double],
357                          "fill.pos_abs_y" => [0],
358                          "fill.rel_y" => [1.0, :double],
359                          "fill.abs_y" => [0],
360
361                          # text properties
362                          "color2.r" => [0, :char],
363                          "color2.g" => [0, :char],
364                          "color2.b" => [0, :char],
365                          "color2.a" => [255, :char],
366                          "color3.r" => [0, :char],
367                          "color3.g" => [0, :char],
368                          "color3.b" => [0, :char],
369                          "color3.a" => [128, :char],
370                          "text.text" => [""],
371                          "text.text_class" => [""],
372                          "text.font" => [""],
373                          "text.size" => [0],
374                          "text.fit_x" => [false],
375                          "text.fit_y" => [false],
376                          "text.min_x" => [0],
377                          "text.min_y" => [0],
378                          "text.align.x" => [0.0, :double],
379                          "text.align.y" => [0.0, :double],
380                          "text.id_source" => [-1],
381                          "text.id_text_source" => [-1]}
382                 end
383         end
384
385         class Tween
386                 def initialize(image)
387                         @id = image.id
388                 end
389
390                 protected
391                 def to_eet_name
392                         "Edje_Part_Image_Id"
393                 end
394         end
395
396         class Tweens < Array
397                 def <<(im)
398                         image = EDJE.image_dir.find { |e| e.filename == im }
399                         if image.nil?
400                                 image = ImageDirectoryEntry.new(im)
401                                 EDJE.image_dir << image
402                         end
403
404                         super(Tween.new(image))
405                 end
406         end
407
408         class ImageDescription < Description
409                 attr_reader :image, :auto_rel, :tweens, :border_fill_middle
410
411                 def initialize(name = "default", value = 0.0)
412                         super
413
414                         @image = nil
415                         @tweens = Tweens.new
416                         @border = [0, 0, 0, 0]
417                         @border_fill_middle = true
418                         @fill_smooth = true
419                         @auto_rel = false
420                 end
421
422                 def border_fill_middle=(var)
423                         @border_fill_middle = (var == true)
424                 end
425
426                 def image=(im)
427                         return if !@image.nil? && im == @image.filename
428
429                         @image = EDJE.image_dir.find { |e| e.filename == im }
430                         if @image.nil?
431                                 @image = ImageDirectoryEntry.new(im)
432                                 EDJE.image_dir << @image
433                         end
434
435                         self.auto_rel = @auto_rel
436                 end
437
438                 def auto_rel=(b)
439                         @auto_rel = b
440
441                         if @auto_rel && !@image.nil?
442                                 off = @rel[0].offset
443
444                                 @rel[1].set_rel(0.0, 0.0)
445                                 @rel[1].set_offset(off[0] + @image.image.width - 1,
446                                                    off[1] + @image.image.height - 1)
447                         end
448                 end
449
450                 def set_border(l = 0, r = 0, t = 0, b = 0)
451                         @border = [r, r, t, b]
452                 end
453
454                 protected
455                 def to_eet_properties
456                         super.merge!(
457                         {"image.id" => [@image.nil? ? -1 : @image.id],
458                          "image.tween_list" => [@tweens],
459                          "border.l" => [@border[0]],
460                          "border.r" => [@border[1]],
461                          "border.t" => [@border[2]],
462                          "border.b" => [@border[3]],
463                          "border.no_fill" => [!@border_fill_middle],
464                          "fill.smooth" => [@fill_smooth],
465                          "fill.pos_rel_x" => [0.0, :double],
466                          "fill.pos_abs_x" => [0],
467                          "fill.rel_x" => [1.0, :double],
468                          "fill.abs_x" => [0],
469                          "fill.pos_rel_y" => [0.0, :double],
470                          "fill.pos_abs_y" => [0],
471                          "fill.rel_y" => [1.0, :double],
472                          "fill.abs_y" => [0]})
473                 end
474         end
475
476         class TextDescription < Description
477                 attr_reader :font, :text, :font_size, :text_class
478
479                 def initialize(name = "default", value = 0.0)
480                         super
481
482                         @outline_color = [0, 0, 0, 255]
483                         @shadow_color = [0, 0, 0, 128]
484                         @text = ""
485                         @text_class = ""
486                         @font = ""
487                         @font_size = 0
488                         @fit = [false, false]
489                         @text_min = [false, false]
490                         @text_align = [0.5, 0.5]
491                         @text_id_source = -1
492                         @text_id_text_source = -1
493                 end
494
495                 def text=(v)
496                         @text = v.to_str.dup
497                 end
498
499                 def font_size=(v)
500                         @font_size = v.to_int
501                 end
502
503                 def text_class=(v)
504                         @text_class = v.to_str.dup
505                 end
506
507                 def set_fit(x = false, y = false)
508                         @fit = [x, y]
509                 end
510
511                 def set_text_min(x = false, y = false)
512                         @text_min = [x, y]
513                 end
514
515                 def set_text_align(x = 0.5, y = 0.5)
516                         @text_align = [x, y]
517                 end
518
519                 def font=(f)
520                         f = f.to_str.strip
521                         md = f.match(/.*\.ttf$/)
522                         unless md.nil?
523                                 found = EDJE.font_dir.find { |font| font.filename == f }
524                                 if found.nil?
525                                         EDJE.font_dir << FontDirectoryEntry.new(f)
526                                 end
527                         end
528
529                         @font = f
530                 end
531
532                 def outline_color=(c)
533                         @outline_color = parse_hex_color(c)
534                 end
535
536                 def shadow_color=(c)
537                         @shadow_color = parse_hex_color(c)
538                 end
539
540                 protected
541                 def to_eet_properties
542                         super.merge!(
543                         {"color2.r" => [@outline_color[0], :char],
544                          "color2.g" => [@outline_color[1], :char],
545                          "color2.b" => [@outline_color[2], :char],
546                          "color2.a" => [@outline_color[3], :char],
547                          "color3.r" => [@shadow_color[0], :char],
548                          "color3.g" => [@shadow_color[1], :char],
549                          "color3.b" => [@shadow_color[2], :char],
550                          "color3.a" => [@shadow_color[3], :char],
551                          "text.text" => [@text],
552                          "text.text_class" => [@text_class],
553                          "text.font" => [@font],
554                          "text.size" => [@font_size],
555                          "text.fit_x" => [@fit[0]],
556                          "text.fit_y" => [@fit[1]],
557                          "text.min_x" => [@text_min[0]],
558                          "text.min_y" => [@text_min[1]],
559                          "text.align.x" => [@text_align[0], :double],
560                          "text.align.y" => [@text_align[1], :double],
561                          "text.id_source" => [@text_id_source],
562                          "text.id_text_source" => [@text_id_text_source]})
563                 end
564         end
565 end