view subtly/gtk.rb @ 36:a672f0142d27

Added command repetition
author unexist
date Fri, 22 Jan 2010 18:32:18 +0100
parents 12b8961eecc0
children 6838c7123945
line wrap: on
line source
##
# @package subtly
#
# @file Gtk bindings
# @author Christoph Kappel <unexist@dorfelite.net>
# @version $Id: subtly/gtk.rb,v 36 1264181538.0-3600 unexist $
#
# This program can be distributed under the terms of the GNU GPL.
##

require "ffi"
require "subtly/gthread"
require "subtly/gobject"

module Subtly
  module Gtk
    class Runner # {{{
      extend FFI::Library

      ffi_lib("libgtk-x11-2.0")

      ## initialize
      # Create runner
      ##

      def initialize
        Subtly::GThread.ffi_init(nil)
        Subtly::Gtk::Runner.ffi_init(nil, nil)
      end

      ## run
      # Start runner
      ##

      def run
        Subtly::Gtk::Runner.ffi_run
      end

      private

      ## init
      # Init Gtk system
      #
      # @param  [Pointer, #read]  Parent widget
      ##

      attach_function(:ffi_init,                   
        :gtk_init, [ :pointer, :pointer ], :void
      )

      ## run
      # Run Gtk main loop
      ##

      attach_function(:ffi_run,
        :gtk_main, [ ], :void
      )

      ## quit
      # Quit Gtk main loop
      ##

      attach_function(:ffi_quit,
        :gtk_main_quit, [ ], :void
      )
    end # }}}

    class Widget # {{{
      attr_accessor :widget

      extend FFI::Library

      ffi_lib("libgtk-x11-2.0")

      # Widget states
      enum(:state,
      [
        :normal, 0,
        :active,
        :prelight,
        :selected,
        :insensitive
      ])

      ## foreground
      # Set foreground color of widget
      #
      # @param  [String, #read]  color  Color value
      ##

      def foreground=(color)
        col = Subtly::Gdk::Color.new

        Subtly::Gdk.ffi_parse_color(color, col.pointer)
        Subtly::Gtk::Widget.ffi_set_fg(@widget, :normal, col.pointer)
      end

      ## background
      # Set background color of widget
      #
      # @param  [String, #read]  color  Color value
      ##

      def background=(color)
        col = Subtly::Gdk::Color.new

        Subtly::Gdk.ffi_parse_color(color, col.pointer)
        Subtly::Gtk::Widget.ffi_set_bg(@widget, :normal, col.pointer)      
      end

      ## connect
      # Connect callback to signal
      #
      # @param  [String,  #read]  signal    Signal name
      # @param  [Proc,    #read]  callback  Callback proc
      # @param  [Pointer, #read]  data      Userdata
      ##

      def connect(signal, callback, data)
        case callback.arity
          when 2 then
            Subtly::GObject.ffi_signal_connect2(@widget, signal, callback, data, nil, 0)
          when 3 then
            Subtly::GObject.ffi_signal_connect3(@widget, signal, callback, data, nil, 0)
          when 4 then
            Subtly::GObject.ffi_signal_connect4(@widget, signal, callback, data, nil, 0)
        end
      end

      ## alignment
      # Set alignment of widget
      #
      # @param  [Array, #read]  array  Alignment array for x/y
      ##

      def alignment=(array)
        Subtly::Gtk::Widget.ffi_set_alignment(@widget, array[0], array[1])
      end

      ## focusable
      # Set if widget is focusable
      #
      # @param  [Bool, #read]  value  New value
      ##

      def focusable=(value)
        Subtly::Gtk::Widget.ffi_set_can_focus(@widget, value)
      end

      ## editable
      # Set if widget is editable
      #
      # @param  [Bool, #read]  value  New value
      ##

      def editable=(value)
        Subtly::Gtk::Widget.ffi_set_can_edit(@widget, value)
      end

      ## to_wid
      # Get real widget from object
      # @return [Pointer] Real widget
      ##

      def to_wid
        @widget
      end

      private
      
      ## ffi_set_fg
      # Set foreground color of widget
      #
      # @param  [Pointer, #read]  widget  A #GtkWidget
      # @param  [State,   #read]  state   Widget state
      # @param  [Pointer, #read]  color   A #GdkColor
      ##

      attach_function(:ffi_set_fg,
        :gtk_widget_modify_fg, [ :pointer, :state, :pointer ], :void
      )

      ## ffi_set_fg
      # Set background color of widget
      #
      # @param  [Pointer, #read]  widget  A #GtkWidget
      # @param  [State,   #read]  state   Widget state
      # @param  [Pointer, #read]  color   A #GdkColor
      ##

      attach_function(:ffi_set_bg,
        :gtk_widget_modify_bg, [ :pointer, :state, :pointer ], :void
      )

      ## ffi_set_alignment
      # Set background color of widget
      #
      # @param  [Pointer, #read]  misc    A #GtkMisc
      # @param  [Float,   #read]  xalign  X alignment
      # @param  [Float,   #read]  yalign  Y alignment
      ##

      attach_function(:ffi_set_alignment,
        :gtk_misc_set_alignment, [ :pointer, :float, :float ], :void
      )

      ## ffi_set_can_focus
      # Set if widget can focus
      #
      # @param  [Pointer, #read]  widget     A #GtkWidget
      # @param  [Book,    #read]  can_focus  Set focusable
      ##

      attach_function(:ffi_set_can_focus,
        :gtk_widget_set_can_focus, [ :pointer, :bool ], :void
       )

      ## ffi_set_can_edit
      # Set if widget is editable
      #
      # @param  [Pointer, #read]  editable     A #GtkWidget
      # @param  [Book,    #read]  is_editable  Set editable
      ##

      attach_function(:ffi_set_can_edit,
        :gtk_editable_set_editable, [ :pointer, :bool ], :void
       )
    end # }}}

    class Container < Subtly::Gtk::Widget # {{{
      extend FFI::Library

      ## show_all
      # Show all widgets
      ##

      def show_all
        Subtly::Gtk::Container.ffi_show_all(@widget)
      end

      ## add
      # Add widget to container
      #
      # @param  [Widget, #read]  widget  A #Widget
      ##

      def add(widget)
        Subtly::Gtk::Container.ffi_add(@widget, widget.to_wid)
      end

      private

      ## ffi_show_all
      # Show all child widgets
      #
      # @param  [Pointer, #read]  widget  A #GtkWdget
      ##

      attach_function(:ffi_show_all,
        :gtk_widget_show_all, [ :pointer ], :void
      )

      ## ffi_add
      # Add widget to container
      #
      # @param  [Pointer, #read]  container  A #GtkContainer
      # @param  [Pointer, #read]  widget     A #GtkWidget
      ##

      attach_function(:ffi_add,
        :gtk_container_add, [ :pointer, :pointer ], :void
      )
    end # }}}

    class Window < Subtly::Gtk::Container # {{{
      extend FFI::Library

      ## initialize
      # Create a new widget
      #
      # @return [Widget]  New widget
      ##

      def initialize
        @widget = Subtly::Gtk::Window.ffi_new(0)
      end

      ## title=
      # Set window title
      #
      # @param  [String, #read]  title  Window title
      ##

      def title=(str)
        Subtly::Gtk::Window.ffi_set_title(@widget, str)
      end

      ## klass=
      # Set window class
      #
      # @param  [Array, #read]  array  Klass array
      ##

      def klass=(array)
        Subtly::Gtk::Window.ffi_set_klass(@widget, array[0], array[1])
      end

      ## size=
      # Set window size
      #
      # @param  [Array, #read]  array  Size array
      ##

      def size=(array)
        Subtly::Gtk::Window.ffi_set_size(@widget, array[0], array[1])
      end

      private

      ## ffi_new
      # Create new window
      #
      # @param  [Fixnum, #read]  type  A #GtkWindowType
      #
      # @return [Pointer] A new widget
      ##
     
      attach_function(:ffi_new,
        :gtk_window_new, [ :int ], :pointer
      )

      ## ffi_set_title
      # Set window title
      #
      # @param  [Pointer, #read]  window  A #GtkWindow
      # @param  [String,  #read]  title   Window title
      ##

      attach_function(:ffi_set_title,
        :gtk_window_set_title, [:pointer, :string], :void
      )
      
      ## ffi_set_klass
      # Set window instance and class
      #
      # @param  [Pointer, #read]  window         A #GtkWindow
      # @param  [String,  #read]  wmclass_name   Instance name
      # @param  [String,  #read]  wmclass_class  Class name
      ##

      attach_function(:ffi_set_klass,
        :gtk_window_set_wmclass, [:pointer, :string, :string], :void
      )

      ## ffi_set_size
      # Set default size of widget
      #
      # @param  [Pointer, #read]  window  A #GtkWindow
      # @param  [Fixnum,  #read]  width   Width of the widget
      # @param  [Fixnum,  #read]  height  Height of the widget
      ##

      attach_function(:ffi_set_size,
        :gtk_window_set_default_size, [ :pointer, :int, :int ], :void
      )      
    end # }}}

    class Scrolled < Subtly::Gtk::Container # {{{
      extend FFI::Library

      ## initialize
      # Create a new scrolled window
      #
      # @return  [Widget] Scrolled window
      ##

      def initialize
        @widget = Subtly::Gtk::Scrolled.ffi_new(nil, nil)
      end

      ## vadjust=
      # Set vadjustment position of scrolled window
      #
      # @param [Float, #read]  pos  Value for adjustment
      ##

      def vadjust=(pos)
        vadjust = Subtly::Gtk::Scrolled.ffi_get_vadjustment(@widget)

        # Collect bounds
        upper = Subtly::Gtk::Scrolled.ffi_get_vadjust_lower(vadjust)
        lower = Subtly::Gtk::Scrolled.ffi_get_vadjust_upper(vadjust)
        page  = Subtly::Gtk::Scrolled.ffi_get_vadjust_page_size(vadjust)

        # Calculation
        value = lower * pos

        puts Subtly::Gtk::Scrolled.ffi_get_vadjust_value(vadjust)

        Subtly::Gtk::Scrolled.ffi_set_vadjust_value(vadjust, value.to_f)
      end

      ## vadjust
      # Get vadjustment position of scrolled window
      #
      # @return [Pointer] The #GtkAdjustment
      ##

      def vadjust
        vadjust = Subtly::Gtk::Scrolled.ffi_get_vadjustment(@widget)

        # Collect bounds
        pos   = Subtly::Gtk::Scrolled.ffi_get_vadjust_value(vadjust)
        upper = Subtly::Gtk::Scrolled.ffi_get_vadjust_lower(vadjust)
        lower = Subtly::Gtk::Scrolled.ffi_get_vadjust_upper(vadjust)
        page  = Subtly::Gtk::Scrolled.ffi_get_vadjust_page_size(vadjust)

        # Calculation
        real    = lower - page #< Substract page size
        percent = pos * 100 / real

        # FIXME: Better way to do this?
        (percent / 100).to_s[0..3].to_f
      end

      private

      ## ffi_new
      # Create scrolled window
      #
      # @param  [Pointer, #read]  hadjustment  A #GtkAdjustment
      # @param  [Pointer, #read]  vadjustment  A #GtkAdjustment
      #
      # @return [Pointer] The new widget
      ##
      
      attach_function(:ffi_new,
        :gtk_scrolled_window_new, [ :pointer, :pointer ], :pointer
      )

      ## ffi_get_vadjustment
      # Get vertical adjustment
      #
      # @param  [Pointer, #read]  scrolled_window A #GtkScrolledWindow
      #
      # @return [Pointer] A #GtkAdjustment
      ##

      attach_function(:ffi_get_vadjustment,
        :gtk_scrolled_window_get_vadjustment, [ :pointer ], :pointer
      )

      ## ffi_set_vadjust_value
      # Set vertical adjustment value
      #
      # @param  [Pointer, #read]  adjustment  A #GtkAdjustment
      # @param  [Float,   #read]  value       The new value
      #
      # @return [Pointer] A #GtkAdjustment
      ##

      attach_function(:ffi_set_vadjust_value,
        :gtk_adjustment_set_value, [ :pointer, :float ], :void
      )

      ## ffi_get_vadjust_value
      # Get vertical adjustment value
      #
      # @param  [Pointer, #read]  adjustment  A #GtkAdjustment
      #
      # @return [Float] The vadjustment value
      ##

      attach_function(:ffi_get_vadjust_value,
        :gtk_adjustment_get_value, [ :pointer ], :float
      )

      ## ffi_get_vadjust_lower
      # Get vertical adjustment lower value
      #
      # @param  [Pointer, #read]  adjustment  A #GtkAdjustment
      #
      # @return [Float] The vadjustment lower value
      ##

      attach_function(:ffi_get_vadjust_lower,
        :gtk_adjustment_get_lower, [ :pointer ], :float
      )

      ## ffi_get_vadjust_upper
      # Get vertical adjustment upper value
      #
      # @param  [Pointer, #read]  adjustment  A #GtkAdjustment
      #
      # @return [Float] The vadjustment upper value
      ##
 
      attach_function(:ffi_get_vadjust_upper,
        :gtk_adjustment_get_upper, [ :pointer ], :float
      )

      ## ffi_get_vadjust_page_size
      # Get vertical adjustment page size
      #
      # @param  [Pointer, #read]  adjustment  A #GtkAdjustment
      #
      # @return [Float] The vadjustment page size
      ##
      #
      attach_function(:ffi_get_vadjust_page_size,
        :gtk_adjustment_get_page_size, [ :pointer ], :float
      )
    end # }}}

    class Box < Subtly::Gtk::Widget # {{{
      extend FFI::Library

      ## initialize
      # Create a new box
      #
      # @param  [Symbol, #read]  type  Type of box
      # @param  [Bool,   #read]  homogeneous  Homogeneous or not
      # @param  [Bool,   #read]  spacing      Spacing
      # @return [Object] New box
      ##

      def initialize(type, homogenous, spacing)
        if(:vbox == type)
          @widget = Subtly::Gtk::Box.ffi_vbox_new(homogenous, spacing)
        else
          @widget = Subtly::Gtk::Box.ffi_hbox_new(homogenous, spacing)
        end
      end

      ## add
      # Add widget to box
      #
      # @param  [Widget, #read]  widget   A #Widget
      # @param  [Bool,   #read]  expand   Expand widget
      # @param  [Bool,   #read]  padding  Pad widget
      ##

      def add(widget, expand, fill, padding)
        Subtly::Gtk::Box.ffi_add(@widget, widget.to_wid, expand, fill, padding)
      end

      private

      ## ffi_vbox_new
      # Create vbox widget
      #
      # @param  [Bool,   #read]  homogenous  Homogeneous or not
      # @param  [Fixnum, #read]  spacing     Default space between children
      # @return [Pointer] Return the new widget
      ##

      attach_function(:ffi_vbox_new,
        :gtk_vbox_new, [ :bool, :int ], :pointer
      )
      
      ## ffi_hbox_new
      # Create hbox widget
      #
      # @param  [Bool,   #read]  homogeneous  Homogeneous or not
      # @param  [Fixnum, #read]  spacing      Default space between children
      # @return [Pointer] Return the new widget
      ##

      attach_function(:ffi_hbox_new,
        :gtk_hbox_new, [ :bool, :int ], :pointer
      )

      ## ffi_add
      # Add widget to box
      #
      # @param  [Pointer, #read]  box      Box widget
      # @param  [Pointer, #read]  child    Widget to add
      # @param  [Bool,    #read]  expand   Expand widget
      # @param  [Bool,    #read]  fill     Fill widget
      # @param  [Fixnum,  #read]  padding  Padding of children
      ##

      attach_function(:ffi_add,
        :gtk_box_pack_start, [ :pointer, :pointer, :bool, :bool, :int ], :void
      )      
    end # }}}

    class Label < Subtly::Gtk::Widget# {{{

      ## initialize
      # Create new label
      #
      # @param  [Bool, #read]  single      Single line?
      # @param  [Bool, #read]  selectable  Selectable?
      #
      # @return  [Widget]  New adjustment
      ##

      def initialize(single, selectable)
        @widget = Subtly::Gtk::Label.ffi_new("Subtly")

        Subtly::Gtk::Label.ffi_single_line(@widget, single)
        Subtly::Gtk::Label.ffi_selectable(@widget,  selectable)
      end

      ## text=
      # Set text of label
      #
      # @param  [String, #read]  str  String for label
      ##

      def text=(str)
        Subtly::Gtk::Label.ffi_set_text(@widget, str)
      end

      ## markup=
      # Set markup of label
      #
      # @param  [String, #read]  str  Markup for label
      ##

      def markup=(str)
        Subtly::Gtk::Label.ffi_set_markup(@widget, str)
      end

      private

      ## ffi_new
      # Create label widget
      #
      # @param  [String, #read]  String to display
      # @return [Pointer] Return the new widget
      ##

      attach_function(:ffi_new,
        :gtk_label_new, [ :string ], :pointer
      )

      ## ffi_set_text
      # Set widget label text
      #
      # @param  [Pointer, #read]  Widget to set text
      # @param  [String,  #read]  String to set
      ##

      attach_function(:ffi_set_text,
        :gtk_label_set_text, [ :pointer, :string ], :void
      )

      ## ffi_set_markup
      # Set widget label markup
      #
      # @param  [Pointer, #read]  label  A #GtkLabel
      # @param  [String,  #read]  str    String to set
      ##

      attach_function(:ffi_set_markup,
        :gtk_label_set_markup, [ :pointer, :string ], :void
      )

      ## ffi_single_line
      # Set single line mode
      #
      # @param  [Pointer, #read]  Widget to set text
      # @param  [Bool,    #read]  Set single line mode
      ##

      attach_function(:ffi_single_line,
        :gtk_label_set_single_line_mode, [ :pointer, :bool ], :void
      )

      ## ffi_selectable
      # Set selected
      #
      # @param  [Pointer, #read]  Widget to set text
      # @param  [Bool,    #read]  Set selectable
      ##

      attach_function(:ffi_selectable,
        :gtk_label_set_selectable , [ :pointer, :bool ], :void
      )
    end # }}}

    class Entry < Subtly::Gtk::Widget # {{{
      ## initialize
      # Creaze new entry
      #
      # @param  [Bool, #read]  frame  Has frame?
      #
      # @return [Widget] New entry
      ##

      def initialize(frame)
        @widget = Subtly::Gtk::Entry.ffi_new

        Subtly::Gtk::Entry.ffi_set_frame(@widget, frame)
      end

      ## text
      # Get text of entry
      #
      # @return [String] String of entry
      ##

      def text
        Subtly::Gtk::Entry.ffi_get_text(@widget)
      end

      ## text=
      # Set text of entry
      #
      # @param  [String, #read]  str  String to set
      ##

      def text=(str)
        Subtly::Gtk::Entry.ffi_set_text(@widget, str)
      end

      ## empty?
      # Is entry empty?
      #
      # @return [Bool] Empty or not
      ##

      def empty?
        0 < Subtly::Gtk::Entry.ffi_get_length(@widget) ? false : true
      end

      ## <<
      # Append text to entry
      #
      # @param  [String, #read]  str  String to append
      ##

      def <<(str)
        Subtly::Gtk::Entry.ffi_append_text(@widget, str)
      end

      ## length=
      # Set max length of widget
      #
      # @param  [Fixnum, #read]  max  Max length
      ##

      def length=(max)
        Subtly::Gtk::Entry.ffi_set_length(@widget, max)
      end

      ## width=
      # Set width in chars of widget
      #
      # @param  [Pointer, #read]  entry    A #GtkEntry
      # @param  [Fixnum,  #read]  n_chars  Width in chars
      ##

      def width=(num)
        Subtly::Gtk::Entry.ffi_set_width_chars(@widget, num)
      end

      private

      ## ffi_new
      # Create entry widget
      # 
      # @return [Pointer] Mew widget
      ##

      attach_function(:ffi_new,
        :gtk_entry_new, [ ], :pointer
      )

      ## ffi_set_frame
      # Set widget frame
      #
      # @param  [Pointer, #read]  entry    A #GtkEntry
      # @param  [Bool,    #read]  setting  Set frame
      ##

      attach_function(:ffi_set_frame,
        :gtk_entry_set_has_frame, [ :pointer, :bool ], :void
      )

      ## ffi_get_text
      # Set widget entry text
      #
      # @param  [Pointer, #read]  entry  A #GtkEntry
      #
      # @return [String] Entry text
      ##

      attach_function(:ffi_get_text,
        :gtk_entry_get_text, [ :pointer ], :string
      )

      ## ffi_set_text
      # Set widget entry text
      #
      # @param  [Pointer, #read]  widget  A #GtkEntry
      # @param  [String,  #read]  text    String to set
      ##

      attach_function(:ffi_set_text,
        :gtk_entry_set_text, [ :pointer, :string ], :void
      )

      ## ffi_get_length
      # Get entry text length
      #
      # @param  [Pointer, #read]  widget  A #GtkEntry
      #
      # @return [Uin16] Length of text
      ##

      attach_function(:ffi_get_length,
        :gtk_entry_get_text_length, [ :pointer ], :uint16
      )

      ## ffi_set_length
      # Set entry text length
      #
      # @param  [Pointer, #read]  entry  A #GtkEntry
      # @param  [Uint16r, #read]  max    Maximum length of entry
      ##

      attach_function(:ffi_set_length,
        :gtk_entry_set_max_length, [ :pointer, :uint16 ], :void
      )

      ## ffi_append_text
      # Append text to entry
      #
      # @param  [Pointer, #read]  widget  A #GtkEntry
      # @param  [String,  #read]  text    Text to append
      ##

      attach_function(:ffi_append_text,
        :gtk_entry_append_text, [ :pointer, :string ], :void
      )

      ## ffi_set_width_chars
      # Set width in chars of widget
      #
      # @param  [Pointer, #read]  widget   A #GtkEntry
      # @param  [Fixnum,  #read]  n_chars  Width in chars
      ##

      attach_function(:ffi_set_width_chars,
        :gtk_entry_set_width_chars, [ :pointer, :int ], :void
      )
    end # }}}

    class List < Subtly::Gtk::Widget # {{{
      ## initialize
      # Creaze new entry
      #
      # @param  [Bool, #read]  frame  Has frame?
      #
      # @return [Widget] New entry
      ##

      def initialize
      end

      private

      ## ffi_new
      # Create entry widget
      # 
      # @return [Pointer] Mew widget
      ##

      attach_function(:ffi_new,
        :gtk_entry_new, [ ], :pointer
      )
    end # }}}

    class Clipboard # {{{

      extend FFI::Library

      ffi_lib("libgtk-x11-2.0")

      ## self.text=
      # Set content of clipboard
      #
      # @param [String, #read]  str  A string
      ##

      def self.text=(str)
        clip = Subtly::Gtk::Clipboard.ffi_get_cliboard(
          Subtly::Gdk::Selections[:primary]
        )

        Subtly::Gtk::Clipboard.ffi_set_text(clip, str, str.size)
        Subtly::Gtk::Clipboard.ffi_store(clip)
      end

      ## self.text
      # Get content of clipboard
      #
      # @return [String]  A string
      ##

      def self.text
        clip = Subtly::Gtk::Clipboard.ffi_get_cliboard(
          Subtly::Gdk::Selections[:primary]
        )

        Subtly::Gtk::Clipboard.ffi_get_text(clip)
      end

      private

      ## ffi_get_cliboard
      # Get the clipboard object for given selection
      #
      # @param  [Fixnum, #read]  selection  Clipboard identifier
      #
      # @return [Pointer]  Clipboard for selection
      ##

      attach_function(:ffi_get_cliboard,
        :gtk_clipboard_get, [ :int ], :pointer
      )

      ## ffi_set_text
      # Set content of clipboard
      #
      # @param [Pointer, #read]  clipboard  A #GtlClipboard
      # @param [String,  #read]  text       A UTF-8 string
      # @param [Fixnum,  #read]  len        Length of text
      ##

      attach_function(:ffi_set_text,
        :gtk_clipboard_set_text, [ :pointer, :string, :int ], :void
      )

      ## ffi_get_text
      # Get content of clipboard
      #
      # @param [Pointer, #read]  clipboard  A #GtlClipboard
      #
      # @return [String]  A UTF-8 string
      ##

      attach_function(:ffi_get_text,
        :gtk_clipboard_wait_for_text, [ :pointer ], :string
      )

      ## ffi_store
      # Store the current clipboard
      #
      # @param [Pointer, #read]  clipboard  A #GtlClipboard
      ##

      attach_function(:ffi_store,
        :gtk_clipboard_store, [ :pointer ], :void
      )
    end # }}}
  end
end