#!ruby -Ks

    ##
    ##  xbase.rb
    ##
    ##  supporting library for conversion from xBASE to Apollo.
    ##
    ##  xBase からの移植を主として支援するライブラリ。
    ##

require "phi"

module Phi

    ##
    ##  get 用の Edit クラス
    ##
    ##  保存先のデータを持つ。
    ##  変更があれば保存先に保存する。
    ##  逆向きの処理（保存先に変更があったときに表示を変える）は
    ##  自動では出来ない。？？。
    ##
##  class EditXB < Edit
  class EditXB < MaskEdit
    attr :hash,  true
    attr :key,   true
    attr :dirty, true

    def initialize( *args )
      self.on_change = proc {
        if @hash[@key].to_s != self.text
          @hash[@key] = self.text
          @dirty = true
        end
      }
    end

    def reload
      self.text = @hash[@key].to_s
      @dirty = false
    end
  end

  class FormXB < Phi::Form  ## form for xbase conversion
    attr :cell_height
    attr :cell_width
    attr :default_top
    attr :default_left
    attr :prev_label    ##  get で prev_label に focus_control をセットする。
    attr :labels
    attr :edits         ##  [edit:EditXB]

        ##
        ##  フォームの大きさには手をつけない。
        ##  ８０字ｘ２５文字にしたい場合には for_xbase を使う。
        ##  FormXB.new.for_xbase
        ##
        ##  外国語版の font.name は何を指定するのかな？。
        ##
    def initialize( *args )
      self.font.name = "ＭＳ 明朝"
      self.load_cell_size
      @default_top  = 0     ##  pixel単位。
      @default_left = 0     ##  pixel単位。
      @labels = []          ##  名なしでもリストにあれば消えない。
      @edits  = []
    end

    def load_cell_size
      size = self.canvas.text_extent("W")
      @cell_height = size.cy += 4
      @cell_width  = size.cx += 2
      return size
    end

    def at(row,col=0, *args)
      load_cell_size
      @default_top  = @cell_height * row
      @default_left = @cell_width  * col
      if args != []
        say( *args )
      end
    end

    def say( *args )
      msg = args.shift
      label = Label.new( self )
      label.caption = msg
      label.top  = @default_top
      label.left = @default_left
      @default_left += label.width
      @labels.push( label )
      @prev_label = label
      if args != []
        get( *args )
      end
    end

        ##
        ##  Form#get
        ##
        ##  Hash 型の変数の表示、編集を行う。
        ##
        ##  直前に say で作成した Label の focus_control をセットするので、
        ##  say "名前(&N)" ; get hash, "NAME"
        ##  のようにショートカットを指定すると、
        ##  ショートカット（ALT+N）で編集窓がアクティブになる。
        ##
        ##  作成した edit を返すので、独自のイベントハンドラなどを設定可能。
        ##
        ##  Hash 型というのは obj[key]、obj[key]= メソッドがあればよい。
        ##
        ##  DataSet でもかまわない。DataSet[FieldName]= がある。
        ##
        ##  dataset = Table.new( "TableName" ).open
        ##  form.at(10,5) ; form.get( dataset , "FieldName" )
        ##
        ##  しかし、DataSet 直接では扱いにくいので
        ##  DataSet#mem_store でハッシュ変数にコピーしたものを
        ##  使うのが望ましい。
        ##
        ##  dataset = Table.new( "TableName" ).open
        ##  mem = dataset.mem_store
        ##  form.at(10,5) ; form.get( mem , "FIELDNAME" )
        ##
        ##  Form のメソッドの中では「form.」は不要になるので
        ##  次のように、ほとんど dBASE そっくりに書くことができる。
        ##
        ##  【dBASE 】
        ## @   04,00   say "YourRef = "   get        YourRefNo
        ##  【FormXB】
        ## at   4,00 ; say "YourRef = " ; get @mem, "YOURREFNO"
        ##
        ##  または、at()、say() は引数が多ければ get をコールするので、
        ## at   4,00 ,     "YourRef = " ,     @mem, "YOURREFNO"
        ##
    def get( hash, key, mask=nil, length=nil )
      if mask.is_a? Integer
        length = mask 
        mask  = nil
      end
      edit = EditXB.new( self )
      edit.hash = hash
      edit.key  = key
      edit.top  = @default_top
      edit.left = @default_left
      edit.text = hash[key].to_s
      edit.auto_size = false
      edit.height = @cell_height
      if mask
        edit.edit_mask = mask 
        length = edit.max_length+1
      end
      edit.width = length * @cell_width if length

      @default_left += edit.width
      @edits.push( edit )

      @prev_label.focus_control = edit if @prev_label
      @prev_label = nil
      return edit
    end

        ##
        ##  get のデータを読み直す。
        ##
    def get_reload
      @edits.each{ |edit| edit.reload }
      self
    end

        ##
        ##  変更のあった最初の Edit を返す。
        ##
    def get_dirty_edit
      @edits.each{|edit| return edit if edit.dirty }
      return nil
    end

        ##
        ##  get のデータを無効にする。
        ##
        ##  強制廃棄はできるのカナ？。
        ##  hide しておけばそのうち消える。
        ##
        ##  controls から強制排除した場合どうなるか？。
        ##  puts form.controls
        ##  やってみてから文句を言おう。
        ##
    def get_clear
      @labels.each{|label|
        label.visible = false
        controls.delete( label )
      }
      @edits.each{|edit|
        edit.visible = false
        controls.delete( edit )
      }
      @labels = []
      @edits  = []
      self
    end

    def for_xbase( cols=80, lines=25 )
      self.client_width  = self.cell_width  * cols
      self.client_height = self.cell_height * lines
      self
    end
  end

##  class Component
##
##        ##
##        ##  designate position based on charactors.
##        ##
##        ##  文字単位での位置指定
##        ##
##        ##  @ 17,0 say "message:" get variable
##        ##
##        ##  Label.new(form, :l01 , "message:" )   .at(17,0)
##        ##  Edit .new(form, :e01 , variable.to_s ).at(17,10)
##        ##
##        ##  →c:\apollo\doc\phi\canvas.pi
##        ##
##    def at(row,col=0)
##      size = parent.canvas.text_extent("W")
##      self.left = col * size.cx
##      self.top  = row * size.cy
##      self
##    end
##  end

end

module RDB
  module DataSet_ex

    ##
    ##  DBXL のメモリ変数に近い処理。
    ##
    ##  mem_store でデータセットからハッシュにコピーする。
    ##  mem_replace でハッシュからデータセットにコピーする。
    ##
    ##  通常は内部の Hash である @mem を使用するが、
    ##  引数で指定すれば外部のハッシュを使用するようになる。
    ##  外部ハッシュを指定した場合には内部ハッシュは変更されない。
    ##
    ##  mem_store、mem_replace ともにコピー後のハッシュを返す。
    ##  mem = dataset.mem_store
    ##  としておけば、アクセスが簡単になる。
    ##
    ##  mem_replace を実行する前には DataSet#edit などが必要。
    ##
    ##  Hash のキーは大文字・小文字を区別するので注意。
    ##  「全部大文字」になる模様。
    ##
    ##  mem_store はハッシュをクリアする。クリアしないもの、欲しい？
    ##
    ##
    attr :mem
    def initialize( *args )
      @mem = {}
    end
    def mem_store( mem = @mem )
      mem.clear
      fields.each{|f| mem[f.field_name] = f.value }
      mem
    end
    def mem_replace( mem = @mem )
      fields.each{|f| f.value = mem[f.field_name] }
      mem
    end

  end
end

if $0 == __FILE__

  form = Phi::FormXB.new.for_xbase
  form.position = Phi::PO_SCREEN_CENTER

  hash = {}
  hash["key1"] = "test_value(1)"
  hash["key2"] = "test_value(2)"
  hash["key3"] = "test_value(3)"

  button = Phi::Button.new form , :button , "get_clear"
  button.on_click = proc{ form.get_clear }

    ##  正式な記述法
  form.at(13,00) ; form.say(' test1(&A) ') ; form.get( hash , "key1" )

    ##  合成記述法（at+say+get を一度に書ける）
  form.at(14,00 ,           ' test2(&B) ' ,            hash , "key2" ,16)
  form.say(                 ' test3(&C) ' ,            hash , "key3" , "cc")

  form.on_click = proc { p hash }

  form.show
  Phi.mainloop

end
