Accept an optional argument that's the height of the Embrace window.
[embrace.git] / bin / embrace
index f9d00c047afd82af3d6c8d2d9b0a498c01fc61e1..79d57c892ae374d3498066c19d2e5072e936c11e 100755 (executable)
@@ -27,29 +27,9 @@ require "embrace/imap"
 PKG_NAME = "embrace"
 DATADIR = "/usr/local/share/#{PKG_NAME}/"
 
-class Evas::EvasObject
-       def move_relative(obj, x, y)
-               # FIXME investigate whether there's an easier way
-               move(*obj.geometry[0..1].zip([x, y]).map { |(a, b)| a + b })
-       end
-
-       def center(obj)
-               a = geometry
-               b = obj.geometry
-
-               move_relative(obj, (b[2] / 2) - (a[2] / 2),
-                                  (b[3] / 2) - (a[3] / 2))
-       end
-
-       def alpha=(alpha)
-               set_color(*(get_color[0..-2] << alpha))
-       end
-end
-
 module Embrace
        VERSION = "0.0.1"
        ICON_FILE = DATADIR + "l33t_MAI_envelope.png"
-       MAX_ICONS = 11
 
        class ZeroToOneAnimator < Ecore::Animator
                def initialize(duration)
@@ -81,11 +61,22 @@ module Embrace
        # an animator that runs for the specified number of seconds,
        # and yields values between 0 and 255
        class AlphaAnimator < ZeroToOneAnimator
-               def initialize(duration, *objects)
+               def initialize(duration, object)
                        super(duration) do |v|
-                               objects.each { |o| o.alpha = (255 * v).to_i }
+                               a = compute_alpha(v)
+                               object.set_color(a, a, a, a)
                        end
                end
+
+               def compute_alpha(v)
+                       (255 * v).to_i
+               end
+       end
+
+       class InverseAlphaAnimator < AlphaAnimator
+               def compute_alpha(v)
+                       super((1.0 - v).abs)
+               end
        end
 
        class MoveAnimator < ZeroToOneAnimator
@@ -105,11 +96,23 @@ module Embrace
        end
 
        class MailboxIcon < Evas::Smart
+               class FadeOutFinishedEvent < Ecore::Event
+                       attr_reader :icon
+
+                       def initialize(icon)
+                               super()
+
+                               @icon = icon
+                       end
+               end
+
                attr_accessor :slot
 
                def initialize(evas, label)
                        super(evas)
 
+                       self.name = label
+
                        @slot = nil
                        @alpha_anim = nil
 
@@ -117,40 +120,53 @@ module Embrace
                        @label = Evas::Text.new(evas)
 
                        @objects = [@img, @label]
+                       @objects.each { |o| add_member(o) }
 
-                       @img.set_color(255, 255, 255, 0)
-                       @label.set_color(255, 255, 255, 0)
-                       @label.set_color(255, 0, 0, 0)
+                       set_color(0, 0, 0, 0)
 
                        @img.set_file(ICON_FILE)
                        @img.set_fill(0, 0, *@img.get_size)
 
-                       @label.text = label
+                       @label.text = name
                        @label.set_font("VeraBd", 10)
 
+                       a = @label.geometry
+                       b = *@img.get_size
+
+                       @label_offset_x = (b[0] / 2) - (a[2] / 2)
+                       @label_offset_y = (b[1] / 2) - (a[3] / 2)
+
                        resize(*@img.get_size)
                end
 
-               def label
-                       @label.text
+               def fade_in
+                       show
+
+                       @alpha_anim ||= AlphaAnimator.new(2, self)
+                       @alpha_anim.on_finished { @alpha_anim = nil }
+               end
+
+               def fade_out
+                       @alpha_anim ||= InverseAlphaAnimator.new(2, self)
+                       @alpha_anim.on_finished do
+                               @alpha_anim = nil
+                               FadeOutFinishedEvent.raise(self)
+                       end
                end
 
                # smart callbacks
-               def on_show
+               def smart_show
                        @objects.each { |o| o.show }
-
-                       @alpha_anim ||= AlphaAnimator.new(2, @img, @label)
-                       @alpha_anim.on_finished { @alpha_anim = nil }
                end
 
-               def on_hide
+               def smart_hide
                        @objects.each { |o| o.hide }
 
                        @alpha_anim && @alpha_anim.delete
                        @alpha_anim = nil
                end
 
-               def on_delete
+               def smart_delete
                        @objects.each { |o| o.delete }
                        @objects.clear
 
@@ -159,49 +175,22 @@ module Embrace
                        @img = @label = @alpha_anim = nil
                end
 
-               def on_layer_set(layer)
-                       @objects.each { |o| o.layer = layer }
-               end
+               def smart_move(x, y)
+                       @img.move(x, y)
 
-               def on_stack_above(other)
-                       @objects.each { |o| o.stack_above = other }
+                       # center the label on the image
+                       @label.move(x + @label_offset_x,
+                                   y + @label_offset_y)
                end
 
-               def on_stack_below(other)
-                       @objects.each { |o| o.stack_below = other }
-               end
-
-               def on_move(x, y)
-                       @objects.each { |o| o.move(x, y) }
-
-                       @label.center(self)
-               end
-
-               def on_resize(w, h)
+               def smart_resize(w, h)
                        @img.resize(w, h)
                end
-       end
-
-       class FixedSizeArray < Array
-               def initialize(siz)
-                       super
-               end
 
-               def each
-                       super { |item| yield item unless item.nil? }
+               def smart_color_set(r, g, b, a)
+                       @img.set_color(r, g, b, a)
+                       @label.set_color(r, 0, 0, a)
                end
-
-               def delete_at(i)
-                       self[i] = nil
-               end
-
-               undef :push
-               undef :<<
-               undef :unshift
-
-               undef :pop
-               undef :shift
-               undef :delete
        end
 
        class Container < Evas::Smart
@@ -209,33 +198,46 @@ module Embrace
                class ContainerFullError < ContainerError; end
                class ContainerLockedError < ContainerError; end
 
-               include Enumerable
-
                def initialize(evas)
                        super
 
                        @bg = Evas::Rectangle.new(evas)
-                       @bg.set_color(0, 0, 0, 255)
+                       @bg.set_color(0, 0, 0, 8)
+
+                       add_member(@bg)
 
-                       @icons = FixedSizeArray.new(MAX_ICONS)
-                       @about_to_add = []
+                       @icons = []
                        @animators = []
 
-                       @lock_count = 0
+                       @about_to_add = 0
+                       @add_lock_count = 0
+
+                       @handlers = [
+                               Ecore::EventHandler.new(MailboxIcon::FadeOutFinishedEvent,
+                                                       &method(:on_icon_fade_out_finished))
+                       ]
                end
 
-               def each
-                       @icons.each { |i| yield i unless i.nil? }
+               def can_add?
+                       !slots_left.zero? && @add_lock_count.zero?
+               end
+
+               def can_delete?
+                       @about_to_add.zero? && @add_lock_count.zero?
                end
 
                def <<(i)
                        Kernel.raise(ContainerFullError) if slots_left.zero?
-                       Kernel.raise(ContainerLockedError) if @lock_count > 0
+                       Kernel.raise(ContainerLockedError) unless @add_lock_count.zero?
+
+                       geo = geometry
+
+                       i.move(geo[0],
+                              geo[1] + geo[3] % Main.instance.icon_height)
 
-                       i.move_relative(self, 0, 0)
                        i.slot = next_slot
                        i.clip = self
-                       i.show
+                       i.fade_in
 
                        # check whether we need to need to move this icon
                        if slots_left == 1
@@ -245,7 +247,7 @@ module Embrace
 
                        movement = Main.instance.icon_height * (slots_left - 1)
 
-                       @about_to_add << i
+                       @about_to_add += 1
 
                        move_time = 0.25 * slots_left
                        @animators << MoveAnimator.new(move_time, movement, i)
@@ -254,15 +256,11 @@ module Embrace
                                @animators.delete(ani)
                                @icons[i.slot] = i
 
-                               # FIXME check whether we can always shift the array instead
-                               #puts "really added #{i.label} now (slot #{i.slot})"
-                               @about_to_add.delete(i)
+                               @about_to_add -= 1
                        end
                end
 
                def delete(icon)
-                       # icons that are placed above the one that's deleted need
-                       # to be moved
                        i = @icons.index(icon)
                        return (block_given? ? yield : nil) if i.nil?
 
@@ -270,66 +268,67 @@ module Embrace
                end
 
                def delete_at(i)
-                       @icons[i].delete
+                       Kernel.raise(ContainerLockedError) unless @about_to_add.zero?
+                       Kernel.raise(ContainerLockedError) unless @add_lock_count.zero?
+
+                       @add_lock_count += 1
+                       @icons[i].fade_out
+               end
+
+               def on_icon_fade_out_finished(ev)
+                       i = @icons.index(ev.icon)
+                       ev.icon.delete
                        @icons.delete_at(i)
 
-                       ar = @icons[i..-1].reject { |i| i.nil? }
-                       return if ar.nil?
+                       # icons that are placed above the one that's deleted need
+                       # to be moved. check whether are there any first
+                       if i == @icons.length
+                               @add_lock_count -= 1
+                               return
+                       end
 
-                       @lock_count += 1
+                       ar = @icons[i..-1]
 
                        @animators << MoveAnimator.new(2, Main.instance.icon_height, *ar)
                        @animators.last.on_finished do |ani|
                                @animators.delete(ani)
-                               @lock_count -= 1
+                               @add_lock_count -= 1
                        end
                end
 
-               def length
-                       @icons.nitems
-               end
-
                # smart callbacks
-               def on_show
+               def smart_show
                        @bg.show
                end
 
-               def on_hide
+               def smart_hide
                        @bg.hide
                end
 
-               def on_delete
+               def smart_delete
                        @bg.delete
                        @bg = nil
                end
 
-               def on_layer_set(layer)
-                       @bg.layer = layer
-               end
-
-               def on_stack_above(other)
-                       @bg.stack_above = other
-               end
-
-               def on_stack_below(other)
-                       @bg.stack_below = other
-               end
-
-               def on_move(x, y)
+               def smart_move(x, y)
                        @bg.move(x, y)
                end
 
-               def on_resize(w, h)
+               def smart_resize(w, h)
                        @bg.resize(w, h)
                end
 
                private
+               def max_icons
+                       geometry.pop / Main.instance.icon_height
+               end
+
                def slots_left
-                       MAX_ICONS - @icons.nitems - @about_to_add.length
+                       max_icons - @icons.nitems - @about_to_add
                end
 
                def next_slot
-                       @icons.nitems + @about_to_add.length
+                       @icons.nitems + @about_to_add
                end
        end
 
@@ -341,19 +340,28 @@ module Embrace
                def initialize
                        super
 
-                       self.title = "blah"
+                       self.has_alpha = true
+                       self.title = "Embrace"
                        self.borderless = true
 
                        @icon_dim = IO.read(ICON_FILE, 8, 16).unpack("NN")
 
-                       self.on_resize { @container.resize(*geometry[2, 3]) }
+                       on_resize { @container.resize(*geometry[2, 3]) }
 
                        @container = Container.new(evas)
                        @container.move(0, 0)
                        @container.layer = -1
                        @container.show
 
-                       size = [@icon_dim.first, icon_height * MAX_ICONS]
+                       size = [@icon_dim.first]
+
+                       arg = ARGV.shift
+                       if arg.nil?
+                               size << icon_height * 11
+                       else
+                               size << arg.to_i
+                       end
+
                        resize(*size)
                        set_size_min(*size)
                        set_size_max(*size)
@@ -367,6 +375,9 @@ module Embrace
                                                        &method(:on_finished))
                        ]
 
+                       s = File.expand_path("~/.e/apps/embrace/config.yaml")
+                       @config = YAML.load(File.read(s))
+
                        @server = nil
                        @timer = Ecore::Timer.new(30, &method(:on_timer))
                        on_timer
@@ -381,38 +392,10 @@ module Embrace
                end
 
                private
-               def add_icon(name)
-                       @container << MailboxIcon.new(evas, name)
-               end
-
                def on_timer
                        return unless @server.nil?
 
-                       mboxes = %w{
-                               INBOX
-                               Lists.ba-2005
-                               Lists.blackbox-devel
-                               Lists.clc-devel
-                               Lists.crux
-                               Lists.cruxcon
-                               Lists.dri-devel
-                               Lists.dri-users
-                               Lists.enlightenment-cvs
-                               Lists.enlightenment-devel
-                               Lists.hobix
-                               Lists.mesa3d-dev
-                               Lists.ruby-core
-                               Lists.rubygems-devel
-                               Lists.vim-ruby-devel
-                               Lists.xmms2-devel
-                               Lists.xorg
-                       }
-
-                       s = File.expand_path("~/.e/apps/embrace/config.yaml")
-
-                       File.open(s) do |f|
-                               @server = IMAP::Session.new(YAML.load(f), mboxes)
-                       end
+                       @server = IMAP::Session.new(@config)
 
                        true
                end
@@ -425,24 +408,12 @@ module Embrace
                                lbl = md.captures.first
                        end
 
-                       found = @container.find { |i| i.label == lbl }
-
-                       begin
-                               if ev.count.zero?
-                                       unless found.nil?
-                                               #puts "removing icon #{lbl}"
-                                               @container.delete(found)
-                                       else
-                                               #puts "count == 0, but icon not found (#{lbl})"
-                                       end
-                               elsif found.nil?
-                                       #puts "adding icon #{lbl}"
-                                       add_icon(lbl)
-                               else
-                                       #puts "count > 0, but already there (#{lbl})"
-                               end
-                       rescue Exception => e
-                               puts e.message
+                       found = evas.find_object(lbl)
+
+                       if ev.count.zero? && !found.nil? && @container.can_delete?
+                               @container.delete(found)
+                       elsif !ev.count.zero? && found.nil? && @container.can_add?
+                               @container << MailboxIcon.new(evas, lbl)
                        end
 
                        false