#-- # $Id: part.rb 70 2005-09-21 15:56:55Z tilman $ # # Copyright (c) 2005 Tilman Sauerbeck (tilman at code-monkey de) # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. module Redact class Part TYPE_RECTANGLE = 1 TYPE_TEXT = 2 TYPE_IMAGE = 3 TYPE_SWALLOW = 4 include Comparable attr_reader :collection, :id, :name, :dragable, :clip, :mouse_events, :repeat_events def initialize(collection, id, name) @collection = collection @id = id @name = name.to_str.dup.freeze @type = TYPE_RECTANGLE @mouse_events = true @repeat_events = false @clip = nil @dragable = Dragable.new(self) @descriptions = Hash.new do |h, k| desc, value = k.split("\0") value = value.to_f h[k] = description_class.new(desc, value) end end def <=>(b) @id <=> b.id end def mouse_events=(val) @mouse_events = (val == true) end def repeat_events=(val) @repeat_events = (val == true) end def clip=(part) if part == self raise(ArgumentError, "cannot clip part to itself") elsif !part.nil? && part.collection != @collection raise(ArgumentError, "items not in the same collection") else @clip = part end end def description(name = "default", value = 0.0) # :yields: desc d = @descriptions[desc_key(name, value)] block_given? ? (yield d) : d end protected def description_class Description end def to_eet_name "Edje_Part" end def to_eet_properties other_desc = @descriptions.dup other_desc.delete(desc_key("default", 0.0)) confine_id = @dragable.confine.nil? ? -1 : @dragable.confine.id {"name" => [@name], "id" => [@id], "type" => [@type, :char], "effect" => [0, :char], "mouse_events" => [@mouse_events], "repeat_events" => [@repeat_events], "clip_to_id" => [@clip.nil? ? -1 : @clip.id], "default_desc" => [description("default", 0.0)], "other_desc" => [other_desc], "dragable.x" => [@dragable.enabled[0], :char], "dragable.step_x" => [@dragable.step[0]], "dragable.count_x" => [@dragable.count[0]], "dragable.y" => [@dragable.enabled[1], :char], "dragable.step_y" => [@dragable.step[1]], "dragable.count_y" => [@dragable.count[1]], "dragable.events_id" => [-1], "dragable.counfine_id" => [confine_id]} # not a typo! end private def desc_key(name, value) name + "\0" + value.to_s end end class SwallowPart < Part def initialize(collection, id, name) super @type = TYPE_SWALLOW end end class TextPart < Part attr_accessor :effect def initialize(collection, id, name) super @type = TYPE_TEXT @effect = :none end protected def description_class TextDescription end def to_eet_properties effect = case @effect when :none: 0 when :plain: 1 when :outline: 2 when :soft_outline: 3 when :shadow: 4 when :soft_shadow: 5 when :outline_shadow: 6 when :outline_soft_shadow: 7 else raise(RedactError, "invalid effect value - #{@effect}") end super.merge!({"effect" => [effect, :char]}) end end class ImagePart < Part def initialize(collection, id, name) super @type = TYPE_IMAGE end protected def description_class ImageDescription end end class Dragable attr_reader :enabled, :step, :count, :confine def initialize(part) @part= part @enabled = [false, false] @step = [0, 0] @count = [0, 0] @confine = nil end def confine=(part) if part == @part raise(ArgumentError, "cannot confine part to itself") elsif !part.nil? && part.collection != @part.collection raise(ArgumentError, "items not in the same collection") else @confine = part end end end class Relation attr_reader :rel, :to_id, :offset def initialize(rel, offset) @rel = [rel.to_f, rel.to_f] @to_id = [-1, -1] @offset = [offset, offset] end def set_rel(x, y) @rel = [x.to_f, y.to_f] end def set_offset(x, y) @offset = [x, y] end def to=(part) self.set_to(part) end def set_to(part_x, part_y = part_x) @to_id = [part_x.nil? ? -1 : part_x.id, part_y.nil? ? -1 : part_y.id] end end class Description attr_reader :rel, :aspect, :step, :visible, :color_class attr_accessor :aspect_preference def initialize(name = "default", value = 0.0) @name = name.to_str.dup.freeze @value = value.freeze @visible = true @align = [0.5, 0.5] @min = [0, 0] @max = [-1, -1] @step = [0, 0] @aspect = [0.0, 0.0] @aspect_preference = :none @rel = [Relation.new(0.0, 0), Relation.new(1.0, -1)] @color = [].fill(255, 0..3) @color_class = "" end def inherit(other) unless other.is_a?(Description) raise(ArgumentError, "Cannot inherit from description") end prot = ["@name", "@value"] (instance_variables - prot).each do |v| n = other.instance_variable_get(v.intern) n = n.dup rescue n instance_variable_set(v.intern, n) end end def visible=(v) @visible = (v == true) end def color_class=(v) @color_class = v.to_str.dup end def set_step(x = 0, y = 0) @step = [x, y] end def set_aspect(x = 0.0, y = 0.0) @aspect = [x, y] end def set_align(x = 0.5, y = 0.5) @align = [x, y] end def set_size(w, h) set_min(w, h) set_max(w, h) end def set_min(w, h) @min = [w, h] end def set_max(w, h) @max = [w, h] end def color=(c) @color = parse_hex_color(c) end protected def parse_hex_color(c) md = c.match(/^#?(([[:xdigit:]]{2}){1,4})$/) if md.nil? raise(ArgumentError, "Argument is not a hex string") end pairs = md.captures.shift.split(/(..)/).delete_if do |item| item == "" end pairs.push("00") while pairs.length < 3 pairs.push("ff") if pairs.length == 3 pairs.map { |p| p.hex } end def to_eet_name "Edje_Part_Description" end def to_eet_properties asp_pref = case @aspect_preference when :none: 0 when :vertical: 1 when :horizontal: 2 when :both: 3 else raise(RedactError, "invalid aspect preference value - " + @aspect_preference.to_s) end {"state.name" => [@name], "state.value" => [@value, :double], "visible" => [@visible], "align.x" => [@align[0], :double], "align.y" => [@align[1], :double], "min.w" => [@min[0]], "min.h" => [@min[1]], "max.w" => [@max[0]], "max.h" => [@max[1]], "step.x" => [@step[0]], "step.y" => [@step[1]], "aspect.min" => [@aspect[0], :double], "aspect.max" => [@aspect[1], :double], "aspect.prefer" => [asp_pref, :char], "rel1.relative_x" => [@rel[0].rel[0], :double], "rel1.relative_y" => [@rel[0].rel[1], :double], "rel1.offset_x" => [@rel[0].offset[0]], "rel1.offset_y" => [@rel[0].offset[1]], "rel1.id_x" => [@rel[0].to_id[0]], "rel1.id_y" => [@rel[0].to_id[1]], "rel2.relative_x" => [@rel[1].rel[0], :double], "rel2.relative_y" => [@rel[1].rel[1], :double], "rel2.offset_x" => [@rel[1].offset[0]], "rel2.offset_y" => [@rel[1].offset[1]], "rel2.id_x" => [@rel[1].to_id[0]], "rel2.id_y" => [@rel[1].to_id[1]], "color_class" => [@color_class], "color.r" => [@color[0], :char], "color.g" => [@color[1], :char], "color.b" => [@color[2], :char], "color.a" => [@color[3], :char], # image properties "image.id" => [-1], "image.tween_list" => [nil], "border.l" => [0], "border.r" => [0], "border.t" => [0], "border.b" => [0], "border.no_fill" => [false], "fill.smooth" => [true], "fill.pos_rel_x" => [0.0, :double], "fill.pos_abs_x" => [0], "fill.rel_x" => [1.0, :double], "fill.abs_x" => [0], "fill.pos_rel_y" => [0.0, :double], "fill.pos_abs_y" => [0], "fill.rel_y" => [1.0, :double], "fill.abs_y" => [0], # text properties "color2.r" => [0, :char], "color2.g" => [0, :char], "color2.b" => [0, :char], "color2.a" => [255, :char], "color3.r" => [0, :char], "color3.g" => [0, :char], "color3.b" => [0, :char], "color3.a" => [128, :char], "text.text" => [""], "text.text_class" => [""], "text.font" => [""], "text.size" => [0], "text.fit_x" => [false], "text.fit_y" => [false], "text.min_x" => [0], "text.min_y" => [0], "text.align.x" => [0.0, :double], "text.align.y" => [0.0, :double], "text.id_source" => [-1], "text.id_text_source" => [-1]} end end class Tween def initialize(image) @id = image.id end protected def to_eet_name "Edje_Part_Image_Id" end end class Tweens < Array def <<(im) im2 = find_image(im.to_str.strip) raise(RedactError, "cannot find image - #{im}") if im2.nil? image = EDJE.image_dir.find { |e| e.filename == im2 } if image.nil? image = ImageDirectoryEntry.new(im, im2) EDJE.image_dir << image end super(Tween.new(image)) end private def find_image(file) [".", OPTIONS.image_dir].each do |d| f2 = File.join(d, file) return Pathname.new(f2).cleanpath.to_s if File.file?(f2) end nil end end class ImageDescription < Description attr_reader :image, :auto_rel, :tweens, :border_fill_middle, :fill_smooth, :fill_pos_rel, :fill_pos_abs, :fill_rel, :fill_abs def initialize(name = "default", value = 0.0) super @image = nil @tweens = Tweens.new @border = [0, 0, 0, 0] @border_fill_middle = true @fill_smooth = true @fill_pos_rel = [0.0, 0.0] @fill_pos_abs = [0, 0] @fill_rel = [1.0, 1.0] @fill_abs = [0, 0] @auto_rel = false end def border_fill_middle=(var) @border_fill_middle = (var == true) end def image=(im) im2 = find_image(im.to_str.strip) raise(RedactError, "cannot find image - #{im}") if im2.nil? return if !@image.nil? && im2 == @image.filename @image = EDJE.image_dir.find { |e| e.filename == im2 } if @image.nil? @image = ImageDirectoryEntry.new(im, im2) EDJE.image_dir << @image end self.auto_rel = @auto_rel end def auto_rel=(b) @auto_rel = b if @auto_rel && !@image.nil? off = @rel[0].offset @rel[1].set_rel(0.0, 0.0) @rel[1].set_offset(off[0] + @image.image.width - 1, off[1] + @image.image.height - 1) end end def set_border(l = 0, r = 0, t = 0, b = 0) @border = [l, r, t, b] end def fill_smooth=(v) @fill_smooth = (v == true) end def fill_pos_rel=(x, y) @fill_pos_rel = [x.to_f, y.to_f] end def fill_pos_abs=(x, y) @fill_pos_abs = [x.to_i, y.to_i] end def fill_rel=(x, y) @fill_rel = [x.to_f, y.to_f] end def fill_abs=(x, y) @fill_abs = [x.to_i, y.to_i] end protected def to_eet_properties super.merge!( {"image.id" => [@image.nil? ? -1 : @image.id], "image.tween_list" => [@tweens], "border.l" => [@border[0]], "border.r" => [@border[1]], "border.t" => [@border[2]], "border.b" => [@border[3]], "border.no_fill" => [!@border_fill_middle], "fill.smooth" => [@fill_smooth], "fill.pos_rel_x" => [@fill_pos_rel[0], :double], "fill.pos_abs_x" => [@fill_pos_abs[0]], "fill.rel_x" => [@fill_rel[0], :double], "fill.abs_x" => [@fill_abs[0]], "fill.pos_rel_y" => [@fill_pos_rel[1], :double], "fill.pos_abs_y" => [@fill_pos_abs[1]], "fill.rel_y" => [@fill_rel[1], :double], "fill.abs_y" => [@fill_abs[1]]}) end private def find_image(file) [".", OPTIONS.image_dir].each do |d| f2 = File.join(d, file) return Pathname.new(f2).cleanpath.to_s if File.file?(f2) end nil end end class TextDescription < Description attr_reader :font, :text, :font_size, :text_class def initialize(name = "default", value = 0.0) super @outline_color = [0, 0, 0, 255] @shadow_color = [0, 0, 0, 128] @text = "" @text_class = "" @font = "" @font_size = 0 @fit = [false, false] @text_min = [false, false] @text_align = [0.5, 0.5] @text_id_source = -1 @text_id_text_source = -1 end def text=(v) @text = v.to_str.dup end def font_size=(v) @font_size = v.to_int end def text_class=(v) @text_class = v.to_str.dup end def set_fit(x = false, y = false) @fit = [x, y] end def set_text_min(x = false, y = false) @text_min = [x, y] end def set_text_align(x = 0.5, y = 0.5) @text_align = [x, y] end def font=(f) f = f.to_str.strip md = f.match(/.*\.ttf$/) unless md.nil? f2 = find_font(f) raise(RedactError, "cannot find font - #{f}") if f2.nil? found = EDJE.font_dir.find { |font| font.filename == f2 } if found.nil? EDJE.font_dir << FontDirectoryEntry.new(f, f2) @font = EDJE.font_dir.last.alias else @font = found.alias end else @font = f end end def outline_color=(c) @outline_color = parse_hex_color(c) end def shadow_color=(c) @shadow_color = parse_hex_color(c) end protected def to_eet_properties super.merge!( {"color2.r" => [@outline_color[0], :char], "color2.g" => [@outline_color[1], :char], "color2.b" => [@outline_color[2], :char], "color2.a" => [@outline_color[3], :char], "color3.r" => [@shadow_color[0], :char], "color3.g" => [@shadow_color[1], :char], "color3.b" => [@shadow_color[2], :char], "color3.a" => [@shadow_color[3], :char], "text.text" => [@text], "text.text_class" => [@text_class], "text.font" => [@font], "text.size" => [@font_size], "text.fit_x" => [@fit[0]], "text.fit_y" => [@fit[1]], "text.min_x" => [@text_min[0]], "text.min_y" => [@text_min[1]], "text.align.x" => [@text_align[0], :double], "text.align.y" => [@text_align[1], :double], "text.id_source" => [@text_id_source], "text.id_text_source" => [@text_id_text_source]}) end private def find_font(file) [".", OPTIONS.font_dir].each do |d| f2 = File.join(d, file) return Pathname.new(f2).cleanpath.to_s if File.file?(f2) end nil end end end