# A data-oriented editable table control.
class Wx::Grid
  # The following extensions are all to prevent crashes associated with
  # garbage collection of Grid-related classes; they do not extend the
  # public functionality of the class in any way. 
  # 
  # GridTableBase, and the GridCellRenderers and GridCellEditors pose a
  # problem for ruby's GC, in that Wx makes them owned by the grid once
  # they have been set for a cell or group of cells. However, because
  # they are SWIG directors we cannot allow the ruby object they are
  # originally associated with to be swept by GC, as C++ method calls
  # are routed to ruby method calls.
  #
  # The code below stores Ruby redefines all methods that may
  # potentially set a GridTableBase, Editor or Renderer, and stores a
  # reference to them in an instance variable, so they are not disposed
  # of up when GC sweeps.
  
  # These all need to be set up as private methods which default to an
  # array. This can't be done in initialize b/c that may not be called
  # when a Grid is loaded from XRC
  %w| __cell_editors   __col_editors   __row_editors
      __cell_renderers __col_renderers __row_renderers |.each do | meth |
    define_method(meth) do
      instance_variable_get("@#{meth}") || 
        instance_variable_set("@#{meth}", [])
    end
    private meth
  end

  # Set a grid table base to provide data
  alias :__set_table :set_table
  def set_table(table, sel_mode = Wx::Grid::GridSelectCells)
    __set_table(table, sel_mode)
    @__grid_table = table
  end

  # store default editor
  wx_set_default_editor = self.instance_method(:set_default_editor)
  define_method(:set_default_editor) do | editr |
    wx_set_default_editor.bind(self).call(editr)
    @__default_editor = editr
  end

  # store default renderer
  wx_set_default_renderer = self.instance_method(:set_default_renderer)
  define_method(:set_default_renderer) do | rendr |
    wx_set_default_renderer.bind(self).call(rendr)
    @__default_renderer = rendr
   end

  # store cell editors
  wx_set_cell_editor = self.instance_method(:set_cell_editor)
  define_method(:set_cell_editor) do | row, col, editr |
    wx_set_cell_editor.bind(self).call(row, col, editr)
    __cell_editors[row] ||= []
    __cell_editors[row][col] = editr
   end

  # store cell renderer
  wx_set_cell_renderer = self.instance_method(:set_cell_renderer)
  define_method(:set_cell_renderer) do | row, col, rendr |
    wx_set_cell_renderer.bind(self).call(row, col, rendr)
    __cell_renderers[row] ||= []
    __cell_renderers[row][col] = rendr
  end

  # Store an editor and/or renderer for a whole column
  wx_set_col_attr = self.instance_method(:set_col_attr)
  define_method(:set_col_attr) do | col, attr |
    wx_set_col_attr.bind(self).call(col, attr)
    if attr.has_editor
      __col_editors[col] = attr.get_editor(self, 0, col)
    end
    if attr.has_renderer
      __col_renderers[col] = attr.get_renderer(self, 0, col)
    end
  end  

  # Store an editor and/or renderer for a whole row
  wx_set_row_attr = self.instance_method(:set_row_attr)
  define_method(:set_row_attr) do | row, attr |
    wx_set_row_attr.bind(self).call(row, attr)
    if attr.has_editor
      __row_editors[row] = attr.get_editor(self, row, 0)
    end
    if attr.has_renderer
      __row_renderers[row] = attr.get_renderer(self, row, 0)
    end
  end  

  # This and the following methods do a bit of book-keeping - as rows
  # and columns are deleted and inserted, the position of the columns
  # and rows with stored editors and renderers may move.
  alias :__insert_rows :insert_rows
  def insert_rows(pos = 0, num = 1, update_labels = true)
    __insert_rows(pos, num, update_labels)
    num.times { __row_editors.insert(pos, nil) }
    num.times { __row_renderers.insert(pos, nil) }
    num.times { __cell_editors.insert(pos, []) }
    num.times { __cell_renderers.insert(pos, []) }
  end
  
  alias :__insert_cols :insert_cols
  def insert_cols(pos = 0, num = 1, update_labels = true)
    __insert_cols(pos, num, update_labels)
    num.times { __col_editors.insert(pos, nil) }
    num.times { __col_renderers.insert(pos, nil) }
    num.times do
      __cell_editors.map { | col | col.insert(pos, []) if col }
    end
    num.times do
      __cell_renderers.map { | col | col.insert(pos, []) if col }
    end
  end

  alias :__delete_rows :delete_rows
  def delete_rows(pos = 0, num = 1, update_labels = true)
    __delete_rows(pos, num, update_labels)
    __row_editors.slice!(pos, num)
    __row_renderers.slice!(pos, num)
    __cell_editors.slice!(pos, num)
    __cell_renderers.slice!(pos, num)
  end
 
  alias :__delete_cols :delete_cols
  def delete_cols(pos = 0, num = 1, update_labels = true)
    __delete_cols(pos, num, update_labels)
    __col_editors.slice!(pos, num)
    __col_renderers.slice!(pos, num)
    num.times do
      __cell_editors.map { | col | col.slice!(pos, num) if col }
    end
    num.times do
      __cell_renderers.map { | col | col.slice!(pos, num) if col }
    end
  end
end
