# Copyright (c) 2003-2005 Ruby-GNOME2 Project Team
# This program is licenced under the same licence as Ruby-GNOME2.
#
# $Id: images.rb,v 1.5 2006/04/08 12:30:03 mutoh Exp $
=begin
= Images

Gtk::Image is used to display an image; the image can be in a number of formats.
Typically, you load an image into a Gdk::Pixbuf, then display the pixbuf.

This demo code shows some of the more obscure cases, in the simple
case a call to Gtk::Image.new is all you need.

If you want to put image data in your program as a String variable,
use the make-inline-pixbuf program that comes with GTK+.
This way you won't need to depend on loading external files, your
application binary can be self-contained.
=end
require 'common'

module Demo
  class Images < BasicWindow
    def initialize
      @pixbuf_loader = nil
      @load_timeout = 0
      @image_stream = nil

      super('Images')
      signal_connect('destroy') do
        cleanup_callback
      end

      self.border_width = 8

      vbox = Gtk::VBox.new(false, 8)
      vbox.border_width = 8
      add(vbox)

      label = Gtk::Label.new
      label.set_markup('<u>Image loaded from a file</u>')
      vbox.pack_start(label, :expand => false, :fill => false, :padding => 0)

      frame = Gtk::Frame.new
      frame.shadow_type = :in

      # The alignment keeps the frame from growing when users resize
      # the window
      align = Gtk::Alignment.new(0.5, 0.5, 0, 0)
      align.add(frame)
      vbox.pack_start(align, :expand => false, :fill => false, :padding => 0)

      # demo_find_file looks in the the current directory first,
      # so you can run gtk-demo without installing GTK, then looks
      # in the location where the file is installed.
      pixbuf = nil
      begin
        filename = Demo.find_file('gtk-logo-rgb.gif')
        pixbuf = Gdk::Pixbuf.new(filename)
      rescue
        # This code shows off error handling. You can just use
        # Gtk::Image.new instead if you don't want to report
        # errors to the user. If the file doesn't load when using
        # Gtk::Image.new, a 'missing image' icon will
        # be displayed instead.
        dialog = Gtk::MessageDialog.new(self,
                                        Gtk::Dialog::DESTROY_WITH_PARENT,
                                        Gtk::MessageDialog::ERROR,
                                        Gtk::MessageDialog::BUTTONS_CLOSE,
                                        "Unable to open image file 'gtk-logo-rgb.gif': #{$1}")

        dialog.signal_connect('response') do |widget, data|
          widget.destroy
        end
        dialog.show
      end

      image = Gtk::Image.new(pixbuf)
      frame.add(image)


      # Animation
      label = Gtk::Label.new
      label.set_markup('<u>Animation loaded from a file</u>')
      vbox.pack_start(label, :expand => false, :fill => false, :padding => 0)

      frame = Gtk::Frame.new
      frame.shadow_type = :in

      # The alignment keeps the frame from growing when users resize
      # the window
      align = Gtk::Alignment.new(0.5, 0.5, 0, 0)
      align.add(frame)
      vbox.pack_start(align, :expand => false, :fill => false, :padding => 0)

      filename = Demo.find_file('floppybuddy.gif')
      image = Gtk::Image.new(filename)
      frame.add(image)


      # Progressive
      label = Gtk::Label.new
      label.set_markup('<u>Progressive image loading</u>')
      vbox.pack_start(label, :expand => false, :fill => false, :padding => 0)

      frame = Gtk::Frame.new(nil)
      frame.shadow_type = :in

      # The alignment keeps the frame from growing when users resize
      # the window
      align = Gtk::Alignment.new(0.5, 0.5, 0, 0)
      align.add(frame)
      vbox.pack_start(align, :expand => false, :fill => false, :padding => 0)

      # Create an empty image for now; the progressive loader
      # will create the pixbuf and fill it in.
      image = Gtk::Image.new
      frame.add(image)

      start_progressive_loading(image)

      # Sensitivity control
      button = Gtk::ToggleButton.new('_Insensitive', true)
      vbox.pack_start(button, :expand => false, :fill => false, :padding => 0)

      button.signal_connect('toggled') do |widget|
        vbox.children.each do |widget|
          if widget != button
            widget.sensitive = ! button.active?
          end
        end
      end
    end

    def start_progressive_loading(image)
      @load_timeout = Gtk.timeout_add(150) do
        progressive_timeout(image)
      end
    end

    def progressive_timeout(image)
      if @image_stream
        buf = @image_stream.read(256)

        @pixbuf_loader.write(buf)

        if @image_stream.eof?
          @image_stream.close
          @image_stream = nil

          @pixbuf_loader.close
          @pixbuf_loader = nil
        end
      else
        filename = Demo.find_file('alphatest.png')
        @image_stream = File.open(filename, 'rb')

        if @pixbuf_loader != nil
          @pixbuf_loader.close
          @pixbuf_loader = nil
        end

        @pixbuf_loader = Gdk::PixbufLoader.new

        @pixbuf_loader.signal_connect('area_prepared') do |loader|
          pixbuf = loader.pixbuf

          # Avoid displaying random memory contents, since the pixbuf
          # isn't filled in yet.
          pixbuf.fill!(0xaaaaaaff)

          image.pixbuf = pixbuf
        end

        @pixbuf_loader.signal_connect('area_updated') do
          # We know the pixbuf inside the Gtk::Image has changed, but the image
          # itself doesn't know this; so queue a redraw.  If we wanted to be
          # really efficient, we could use a drawing area or something
          # instead of a Gtk::Image, so we could control the exact position of
          # the pixbuf on the display, then we could queue a draw for only
          # the updated area of the image.
          image.queue_draw
        end
      end

      # leave timeout installed
      return true
    end

    def cleanup_callback
      @pixbuf_loader.close if @pixbuf_loader
      @pixbuf_loader = nil
      if @load_timeout != 0
        Gtk.timeout_remove(@load_timeout)
      end
    end
  end
end
