/* 
 * © 2008-2009 Sun Microsystems, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#ifndef _LF_TREEVIEW_H_
#define _LF_TREEVIEW_H_

#include <mforms/mforms.h>

#include "lf_view.h"

namespace mforms {
namespace gtk {


class TreeViewImpl : public ViewImpl
{
private:
  class ColumnRecord : public Gtk::TreeModelColumnRecord
  {
  public:
    std::vector<Gtk::TreeModelColumnBase*>  columns;
    Gtk::TreeModelColumn<Glib::ustring>     _tag_column;
    
    template<class C>
      const Gtk::TreeModelColumn<C> &get(int column)
      {
        Gtk::TreeModelColumnBase* c= columns[column];
        
        return *static_cast<Gtk::TreeModelColumn<C>*>(c);
      }

    virtual ~ColumnRecord()
    {
      for (std::vector<Gtk::TreeModelColumnBase*>::iterator iter= columns.begin();
           iter != columns.end(); ++iter)
        delete *iter;
    }

    void add_tag_column()
    {
      add(_tag_column);
    }

    Gtk::TreeModelColumn<Glib::ustring>& tag_column()
    {
      return _tag_column;
    }

    int add_string(Gtk::TreeView *tree, const std::string &title, bool editable)
    {
      Gtk::TreeModelColumn<Glib::ustring> *column= new Gtk::TreeModelColumn<Glib::ustring>();
      
      columns.push_back(column);
      add(*column);
            
      if (editable)
        tree->append_column_editable(title, *column);
      else
        tree->append_column(title, *column);
      
      return columns.size()-1;
    }


    int add_integer(Gtk::TreeView *tree, const std::string &title, bool editable)
    {
      Gtk::TreeModelColumn<int> *column= new Gtk::TreeModelColumn<int>();
      
      columns.push_back(column);
      add(*column);

      if (editable)
        tree->append_column_editable(title, *column);
      else
        tree->append_column(title, *column);

      return columns.size()-1;
    }


    int add_check(Gtk::TreeView *tree, const std::string &title, bool editable)
    {
      Gtk::TreeModelColumn<bool> *column= new Gtk::TreeModelColumn<bool>();
      
      columns.push_back(column);
      add(*column);

      if (editable)
        tree->append_column_editable(title, *column);
      else
        tree->append_column(title, *column);

      return columns.size()-1;
    }
  };

  ColumnRecord _columns;
  

  Gtk::ScrolledWindow *_swin;
  Gtk::TreeView *_tree;
  sigc::connection _conn;

  Glib::RefPtr<Gtk::ListStore> _list_store;

  virtual Gtk::Widget *get_outer() const { return _swin; }
  virtual Gtk::Widget *get_inner() const { return _tree; }


  TreeViewImpl(TreeView *self, mforms::TreeOptions opts)
    : ViewImpl(self)
  {
    _swin= Gtk::manage(new Gtk::ScrolledWindow());
    _swin->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
    _swin->set_shadow_type(Gtk::SHADOW_IN);
    _tree= Gtk::manage(new Gtk::TreeView());
    _tree->show();
    _conn = _tree->signal_cursor_changed().connect(sigc::mem_fun(self, &TreeView::emit_changed));

    _swin->add(*_tree);

    _tree->set_headers_visible(opts & mforms::TreeShowHeader);
    _tree->set_reorderable(opts & mforms::TreeAllowReorderRows);
  }
  
  
  void string_edited(const Glib::ustring &path, const Glib::ustring &new_text, int column)
  {
    if (_list_store)
    {
      Gtk::TreeRow row= *_list_store->get_iter(Gtk::TreePath(path));

      row[_columns.get<Glib::ustring>(column)]= new_text;
    }
  }


  int add_column(TreeColumnType type, const std::string &name, int initial_width, bool editable)
  {
    g_message("TreeViewImpl::add_column: handle width!");
    int column;
    switch (type)
    {
    case StringColumnType:
      column= _columns.add_string(_tree, name, editable);
      if (editable)
      {
        ((Gtk::CellRendererText*)_tree->get_column(column)->get_first_cell_renderer())->signal_edited().
          connect(sigc::bind(sigc::mem_fun(this, &TreeViewImpl::string_edited), column));
      }
      break;
    case IntegerColumnType: column= _columns.add_integer(_tree, name, editable); break;
    case CheckColumnType: column= _columns.add_check(_tree, name, editable); break;
    default: return -1;
    }
    
    _tree->get_column(column)->set_resizable(true);
    
    return column;
  }


  void end_columns()
  {
    _columns.add_tag_column();
    _list_store= Gtk::ListStore::create(_columns);
    _tree->set_model(_list_store);
  }



  static bool create(TreeView *self, mforms::TreeOptions opt)
  {
    return new TreeViewImpl(self, opt) != 0;
  }

  static int add_column(TreeView *self, TreeColumnType type, const std::string &name, int width, bool editable)
  {
    TreeViewImpl* tree = self->get_data<TreeViewImpl>();

    return tree->add_column(type, name, width, editable);
  }
  
  static void end_columns(TreeView *self)
  {
    TreeViewImpl* tree = self->get_data<TreeViewImpl>();

    tree->end_columns();
  }

  static void clear_rows(TreeView *self)
  {
    TreeViewImpl* tree = self->get_data<TreeViewImpl>();

    if (tree->_list_store)
      tree->_list_store->clear();
  }
  
  static void delete_row(TreeView *self, int row)
  {
    TreeViewImpl* tree = self->get_data<TreeViewImpl>();
    Gtk::TreePath path;
    
    path.append_index(row);

    if (tree->_list_store)
      tree->_list_store->erase(tree->_list_store->get_iter(path));
  }
  
  static int add_row(TreeView *self)
  {
    TreeViewImpl* tree = self->get_data<TreeViewImpl>();

    if (tree->_list_store)
      return tree->_list_store->get_path(tree->_list_store->append()).front();
    
    return -1;
  }

  static int get_selected(TreeView *self)
  {
    TreeViewImpl* tree = self->get_data<TreeViewImpl>();
    int idx = -1;
    
    if (tree->_tree)
    {
      const Gtk::TreePath path(tree->_tree->get_selection()->get_selected());
      if (path.gobj() && !path.empty())
      {
        idx = path.front();
      }
    }
    
    return idx;
  }
  
  static void set_selected(TreeView* self, const int idx)
  {
    TreeViewImpl* tree = self->get_data<TreeViewImpl>();

    Gtk::TreePath path;
    path.append_index(idx);
    
    tree->_conn.block();
    tree->_tree->set_cursor(path);
    tree->_conn.unblock();
  }
  
  static int count(TreeView *self)
  {
    TreeViewImpl* tree = self->get_data<TreeViewImpl>();

    if (tree->_list_store)
      return tree->_list_store->children().size();
    
    return 0;
  }

  static void set_string(TreeView *self, int row, int column, const std::string &value)
  {
    TreeViewImpl* tree = self->get_data<TreeViewImpl>();
    Gtk::TreeRow tree_row;
    Gtk::TreePath path;

    path.append_index(row);

    if (tree->_list_store)
      tree_row= *tree->_list_store->get_iter(path);
    else
      return;

    tree_row[tree->_columns.get<Glib::ustring>(column)]= value;
  }
  
  static void set_integer(TreeView *self, int row, int column, int value)
  {
    TreeViewImpl* tree = self->get_data<TreeViewImpl>();
    Gtk::TreeRow tree_row;
    Gtk::TreePath path;

    path.append_index(row);

    if (tree->_list_store)
      tree_row= *tree->_list_store->get_iter(path);
    else
      return;

    tree_row[tree->_columns.get<int>(column)]= value;
  }
  
  static void set_check(TreeView *self, int row, int column, bool check)
  {
    TreeViewImpl* tree = self->get_data<TreeViewImpl>();
    Gtk::TreeRow tree_row;
    Gtk::TreePath path;

    path.append_index(row);

    if (tree->_list_store)
      tree_row= *tree->_list_store->get_iter(path);
    else
      return;

    tree_row[tree->_columns.get<bool>(column)]= check;
  }
  
  
  static std::string get_string(TreeView *self, int row, int column)
  {
    TreeViewImpl* tree = self->get_data<TreeViewImpl>();
    Gtk::TreeRow tree_row;
    Gtk::TreePath path;

    path.append_index(row);

    if (tree->_list_store)
      tree_row= *tree->_list_store->get_iter(path);
    else
      return "";

    return ((Glib::ustring)tree_row[tree->_columns.get<Glib::ustring>(column)]).c_str();
  }
  
  static int get_int(TreeView *self, int row, int column)
  {
    TreeViewImpl* tree = self->get_data<TreeViewImpl>();
    Gtk::TreeRow tree_row;
    Gtk::TreePath path;

    path.append_index(row);

    if (tree->_list_store)
      tree_row= *tree->_list_store->get_iter(path);
    else
      return 0;

    return tree_row[tree->_columns.get<int>(column)];
  }
    
  static bool get_check(TreeView *self, int row, int column)
  {
    TreeViewImpl* tree = self->get_data<TreeViewImpl>();
    Gtk::TreeRow tree_row;
    Gtk::TreePath path;

    path.append_index(row);

    if (tree->_list_store)
      tree_row= *tree->_list_store->get_iter(path);
    else
      return false;

    return tree_row[tree->_columns.get<bool>(column)];
  }

  static std::string get_row_tag(TreeView *self, int row)
  {
    TreeViewImpl* tree = self->get_data<TreeViewImpl>();
    Gtk::TreeRow tree_row;
    Gtk::TreePath path;

    path.append_index(row);

    if (tree->_list_store)
      tree_row= *tree->_list_store->get_iter(path);
    else
      return "";

    return Glib::ustring(tree_row[tree->_columns.tag_column()]);
  }

  static void set_row_tag(TreeView *self, int row, const std::string &value)
  {
    TreeViewImpl* tree = self->get_data<TreeViewImpl>();
    Gtk::TreeRow tree_row;
    Gtk::TreePath path;

    path.append_index(row);

    if (tree->_list_store)
      tree_row= *tree->_list_store->get_iter(path);
    else
      return;

    tree_row[tree->_columns.tag_column()]= value;
  }

public:
  static void init()
  {
    ::mforms::ControlFactory *f = ::mforms::ControlFactory::get_instance();

    f->_treeview_impl.create= &TreeViewImpl::create;
    f->_treeview_impl.add_column= &TreeViewImpl::add_column;
    f->_treeview_impl.end_columns= &TreeViewImpl::end_columns;
    f->_treeview_impl.clear_rows= &TreeViewImpl::clear_rows;
    f->_treeview_impl.delete_row= &TreeViewImpl::delete_row;
    f->_treeview_impl.add_row= &TreeViewImpl::add_row;
    f->_treeview_impl.get_selected= &TreeViewImpl::get_selected;
    f->_treeview_impl.set_selected= &TreeViewImpl::set_selected;
    f->_treeview_impl.count= &TreeViewImpl::count;
    f->_treeview_impl.set_string= &TreeViewImpl::set_string;
    f->_treeview_impl.set_int= &TreeViewImpl::set_integer;
    f->_treeview_impl.set_check= &TreeViewImpl::set_check;
    f->_treeview_impl.get_string= &TreeViewImpl::get_string;
    f->_treeview_impl.get_int= &TreeViewImpl::get_int; 
    f->_treeview_impl.get_check= &TreeViewImpl::get_check;
    f->_treeview_impl.set_row_tag= &TreeViewImpl::set_row_tag;
    f->_treeview_impl.get_row_tag= &TreeViewImpl::get_row_tag;
  }
};

  
}
}

#endif
