/*
 * ===========================
 * VDK Visual Development Kit
 * Version 2.0
 * November 2001
 * ===========================
 *
 * Copyright (C) 1998, Mario Motta
 * Developed by Mario Motta <mmotta@guest.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library 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 TREEVIEW_H
#define TREEVIEW_H
#include <vdk/vdkobj.h>
#include <vdk/dlist.h>
#include <vdk/vdkprops.h>
#include <vdk/vdkarray.h>
#include <vdk/value_sem_list.h>
class VDKTreeView;

/*!
  \class VDKTreeViewModelTuple
  This class represent a tuple, a string array with an element
  considered as a primary key.
 */
typedef VDKArray<VDKString> StringRow;
typedef bool (*VDKStringCompareFunction)(VDKString&, VDKString&);

class VDKTreeViewModelTuple: public StringRow
{
  /*
  int key_index;
  bool (*equal)(VDKString&, VDKString&);
  bool (*less)(VDKString&, VDKString&);
  */
 public:
  /*!
    Property: KeyIndex
  */
  VDKReadWriteValueProp<VDKTreeViewModelTuple,int> KeyIndex;  
  /*!
    Property: Less
  */
  VDKReadWriteValueProp<VDKTreeViewModelTuple,VDKStringCompareFunction> Less;  
  /*!
    Property: Equal
  */
  VDKReadWriteValueProp<VDKTreeViewModelTuple,VDKStringCompareFunction> Equal;  
  /*!
    Constructor.
    \param n, array dimension
    \param key, ordinal position of the key field ( 0 <= key < n)
    \param less, an user function to substitute < operator if needed
    \param equal, an user function to substitute == operator if needed

    Compare function should be declared as:
    bool compare(VDKString&, VDKString&);
   */
  VDKTreeViewModelTuple(int n = 0, int key = 0, VDKStringCompareFunction less = NULL,
			VDKStringCompareFunction equal= NULL): 
    StringRow(n),
    KeyIndex("KeyIndex",this,key),
    Less("Less",this,less),
    Equal("Equal",this,equal)
    {
      
    }

  virtual ~VDKTreeViewModelTuple() {}
  /*
  VDKTreeViewModelTuple(const VDKTreeViewModelTuple& tuple) 
    {
      *this = tuple;
    }
  
  VDKTreeViewModelTuple& operator=(const VDKTreeViewModelTuple& tuple)
  {
    resize(tuple.size());
    for(int t = 0; t < size(); t++)
      (*this)[t] = tuple[t];
    int key_index = tuple.KeyIndex;
    VDKStringCompareFunction less = tuple.Less;
    VDKStringCompareFunction equal = tuple.Equal;
    KeyIndex = key_index;
    Less = less;
    Equal = equal;
  }
  */
  int operator <(VDKTreeViewModelTuple& t) 
    { 
      int key_index = KeyIndex;
      VDKStringCompareFunction less = Less;
      if(less)
	return less((*this)[key_index],t[key_index]);
      else
	return (*this)[key_index] < t[key_index]; 
    }
  int operator==(VDKTreeViewModelTuple& t)
    { 
      int key_index = KeyIndex;
      VDKStringCompareFunction equal = Equal;
      if(equal)
	return equal((*this)[key_index],t[key_index]);
      else
	return (*this)[key_index] == t[key_index]; 
    }
};

typedef VDKValueList<VDKTreeViewModelTuple>         VDKTreeViewModelTupleList;  
typedef VDKValueListIterator<VDKTreeViewModelTuple> VDKTreeViewModelTupleListIterator;
typedef VDKArray<VDKTreeViewModelTuple>             VDKTreeViewModelTupleArray;  
/*!
  \class VDKTreeViewModel
  \brief Provides a wrapper for GtkTreeModel
  basically it stores data to be viewed with a VDKTreeView
 */

class VDKTreeViewModel: public VDKNotCopyAble
{
 protected:
  GtkTreeStore *model;
  // GtkTreeIter iter;
 public:
  /*!
    Return underlying GtkTreeStore object
   */
  GtkTreeStore* GtkModel() { return model; }
  /*!
    constructor
    \param types a GType array
    \param ncol the number of columns into the model
  */
  VDKTreeViewModel(GType* types, int ncol);
  /*!
    destructor
  */
  ~VDKTreeViewModel();
  /*!
    Appends a new blank tree row
    \param iter a not initialized iter address that will be filled with newly constructed tree row
    \param parent if not NULL the row will appended as a child
    otherwise it will be appended as a sibling.
    TIP: 
    - appending all rows as sibling will degenerate the tree into a list
      \code
      GtkTreeIter iter;
      model->AppendBlank();
      model->SetData(&iter,...);
      \endcode
  */
  void AppendBlank(GtkTreeIter* iter,GtkTreeIter* parent = NULL);
  /*!
    Prepends a new blank tree row
    \param iter a not initialized iter address that will be filled with newly constructed tree row
    \param parent if not NULL the row will prepended as a child
    otherwise it will be prepended as a sibling.
    TIP: 
    - prepending all rows as sibling will degenerate the tree into a list

      \code
      GtkTreeIter iter;
      model->PrependBlank();
      model->SetData(&iter,...);
      \endcode
  */
  void PrependBlank(GtkTreeIter* iter,GtkTreeIter* parent = NULL);
  /*!
    Insert a tuple into model, tuple will be inserted in order.
    \param iter a not initialized iter address that will be filled with newly constructed tree row
    \param tuple to be inserted
    \param parent if is NULL tuple will be inserted in order into top level nodes list
    otherwise will be inserted in order into parent children list
    \param recurse if true scans children/sibling list recursively to search insertion position

   */
  void InsertTuple(GtkTreeIter* iter,VDKTreeViewModelTuple &tuple,GtkTreeIter* parent = NULL, bool recurse = false);
  /*!
    Clears the tree store
  */
  void Clear();
  /*!
    Removes the row at iter
    \param i the iter to be removed
  */
  void Remove(GtkTreeIter* i);
  /*!
    Sets data into a row
    \param node the iterator to be set
    \param a list of pairs <column ordinal number,type value>
    the list must be -1 terminated.

    \code
    GtkTreeIter   iter;
    model->AppendBlank();
    model->SetData(&iter, 0, "This is row 1", 1, FALSE, 2, NULL, -1);
    \endcode
  */
  void SetData(GtkTreeIter* node,...);
  /*!
    Sets data into a cell
    \param node the iterator to be set
    \param column cell column
    \param value a string representation of GType value that will be converted
    into a GType accordlying with the model

    \code
    GtkTreeIter *iter = model->PrependBlank();
    model->SetCell(iter,0, "test");
    model->SetCell(iter,1, "true");
    \endcode

    Supported GType's:
     - G_TYPE_CHAR
     - G_TYPE_STRING
     - G_TYPE_BOOLEAN
     - G_TYPE_INT
     - G_TYPE_UINT
     - G_TYPE_LONG
     - G_TYPE_ULONG
     - G_TYPE_FLOAT
     - G_TYPE_DOUBLE
   */
  void SetCell(GtkTreeIter* node, int column, const char* value);
  /*!
    Get data from a cell, data type will be converted into their string representation
    accordlying with GType.
    \param node iterator to be retrieved
    \param column cell column

    Supported GType's:
     - G_TYPE_CHAR
     - G_TYPE_STRING
     - G_TYPE_BOOLEAN
     - G_TYPE_INT
     - G_TYPE_UINT
     - G_TYPE_LONG
     - G_TYPE_ULONG
     - G_TYPE_FLOAT
     - G_TYPE_DOUBLE
    
    Tip:
    Returned buffer address should be freed by user if not NULL.

    \code
    //signal response method
    bool 
    TvForm::OnTreeViewSelectRow(VDKObject* sender) 
    {
       // gets selections
       treeview->GetSelections();
       // disregard multiple selections
       if(treeview->Selections().size() == 1)
         {
	 VDKTreeViewModel* model = treeview->Model;
	 // gets iter position from selections list
	 GtkTreeIter iter = treeview->Selections()[0];
	 char* firstname = model->GetCell(&iter,0); // extract from iter position at column 0
	 char* lastname  = model->GetCell(&iter,1);
	 if(firstname && lastname) // GetCell() returns NULL on failure
           {
	   printf("\n[%s %s]",firstname,lastname);
	   fflush(stdout);
	   delete[] firstname;
	   delete[] lastname;
	   }
	 }
       treeview->Selections().flush();	 
      return true;
   }
   \endcode
   */
  char *GetCell(GtkTreeIter* node, int column);
  /*!
    Gets and fill a tuple with row data converted into their
    string representation
    \param node iterator to be retrieved
    \param tuple a tuple reference (tuple can be empty since
    it will be resized and filled by the method
   */
  void GetTuple(GtkTreeIter* node,VDKTreeViewModelTuple& tuple);
  /*!
    Move iterator to root node
    \param iter a not initialized iter address that will be filled with root node pointer

    \code
    GtkTreeIter iter;
    if(model->Root(&iter))
      {
      char* cell = model->GetCell(&iter,0);
      if(cell)
        {
	// ..
        delete[] cell;
	}
     }
   */
  bool Root(GtkTreeIter* iter);
  /*!
    Move iterator forward at present level
    \param iter a not initialized iter address that will be filled with next node pointer
   */
  bool Next(GtkTreeIter* iter);
  /*!
    Returns true if iter has a child
    \param iter
   */
  bool HasChild(GtkTreeIter* iter)
    { return gtk_tree_model_iter_has_child (GTK_TREE_MODEL(model), iter); }
  /*!
    Return the child of a parent node
    \param iter a not initialized iter address that will be filled with child node pointer
    \param parent a valid iter address of the parent node

    Returns true if <parent> has a child, <iter> left undefined otherwise
   */
  bool Child(GtkTreeIter* iter,GtkTreeIter* parent);
  /*!
    Executes a linear search (deep first on childs), returns true if <value> found
    \param iter a GtkTreeIter address to be filled with iter data if <value> found
    \param column to be scanned to execute search
    \param value a string representation of a GType value that will be converted
    into a GType accordlying with the model
    Supported GType's:
     - G_TYPE_CHAR
     - G_TYPE_STRING
     - G_TYPE_BOOLEAN
     - G_TYPE_INT
     - G_TYPE_UINT
     - G_TYPE_LONG
     - G_TYPE_ULONG
     - G_TYPE_FLOAT
     - G_TYPE_DOUBLE

     Returns true if found
  */
  bool  Find(GtkTreeIter* iter,int column, char* value);

};


/*!
  Provides an iterator on VDKTreeViewModel
 */
class VDKTreeViewModelIterator
{
  VDKTreeViewModel* model;
  GtkTreeIter iter, *internal_iter;
 public:
  VDKTreeViewModelIterator(): model(NULL),internal_iter(NULL) {}
  /*!
    Constructor
    \param model
    \param parent if NULL makes an iterator on top level nodes,
    otherwise makes an iterator on parent childs
   */
  VDKTreeViewModelIterator(VDKTreeViewModel* model,GtkTreeIter* parent = NULL);
  /*!
    return node presently visited
  */
  GtkTreeIter* current() { return internal_iter; }
  /*!
    Returns true if presently visited node is valid
   */
  operator int() { return internal_iter != NULL; }
  /*!
    Returns true if presently visited node has a child.
    TIP:
    Since iterator incremental operator skips on next sibling node, you can use this
    to visit all tree nodes on depth-first strategy using recursion.
    See example below taken from /testvdk/treeviewcompo.cc:
    \code
    void 
    TreeViewComponent::recurse(GtkTreeIter* iter)
    {
    VDKTreeViewModel* model = tree->Model;
    VDKTreeViewModelIterator ti(model,iter);
    for(;ti;ti++)
      {
      PrintRow(ti.current()); // uses presently pointed node 
      if(ti.HasChild())
        recurse (ti.current());
      }
    }
    bool
    TreeViewComponent::OnTrasverseClicked (VDKObject* sender)
    {
     recurse(NULL);
     return true;
    }
    \endcode
   */
  bool HasChild();
  /*!
    Incremental operator (postfix), visit next sibling node
  */
  void operator++();
  /*!
    Incremental operator (infix), visit next sibling node
  */
  void operator++(int);
};

/*!
  \class VDKTreeViewColumn
  \brief Provides a wrapper for GtkTreeColumn
 */
class VDKTreeViewColumn: public VDKNotCopyAble
{
 protected:
  static void edited_callback (GtkCellRendererText *cell,
	       gchar             *path_string,
	       gchar             *new_text,
	       gpointer          data);
  static void toggled_callback (GtkCellRendererToggle *cell,
				gchar                 *path_string,
				gpointer               data);
 private:
  GtkCellRenderer *cell;
  GtkTreeViewColumn *column;
  VDKTreeView* owner;
  gulong handler_seq_no;
  int id;
 public:
  /*!
    NormalBackground
  */
  VDKReadWriteValueProp<VDKTreeViewColumn,VDKRgb> NormalBackground;
  /*!
    Foreground
  */
  VDKReadWriteValueProp<VDKTreeViewColumn,VDKRgb> Foreground;
  /*!
    Font
  */
  VDKReadWriteValueProp<VDKTreeViewColumn,VDKFont*> Font;
  /*!
    Title
  */
  VDKReadWriteValueProp<VDKTreeViewColumn,const char*> Title;
  /*!
    Width
    Setting this property to a value greater than 0 sets column
    to a fixed size, setting to 0 makes it auto resizeable
  */
  VDKReadWriteValueProp<VDKTreeViewColumn,int> Width;
  /*!
    Sortable
   */
  VDKReadWriteValueProp<VDKTreeViewColumn,bool> Sortable;

  /*!
    Constructor
    \param owner treeview owner
    \param column model column number from which data should be gathered 
    \param title column title
    \param editable if true column cells can be edited provided that related
    editcol cell has a true value (has meaning only if column type is G_TYPE_STRING)
    \param editcol model column number that must contain boolean value that flags
    editable (has meaning only if editable is true)

    \section
    these types of columns are supported:
    - G_TYPE_STRING
    - G_TYPE_BOOLEAN
    - GDK_TYPE_PIXBUF
    \section
    - A column cell of G_TYPE_BOOLEAN can answer to "toggled" signal using
    LS extended signal system (see treeviewcompo.cc on ./testvdk).
    \code
    //..
    tree->OnCellToggled.connect(slot(*this,&TreeViewComponent::OnCellToggled));
    //..
    void TreeViewComponent::OnCellToggled(VDKObject* sender,
				GtkTreeIter* iter, 
				int col, bool toggled)
    {
    VDKTreeView* tree = dynamic_cast<VDKTreeView*>(sender);
    if(!sender)
      return;
    else
      {
      VDKTreeViewModel* model = tree->Model;
      toggled = !toggled;
      model->SetCell(iter, col, toggled ? "true" : "false");
      printf("\ntoggled column:%d\n\tvalue:%s", col, toggled ? "true" : "false");
      fflush(stdout);
     }
   }
    \endcode
    - If a column cell of G_TYPE_STRING is editable it can answer to "edited" signal
    using LS extended signal system (see treeviewcompo.cc on ./testvdk):
    \code
    // ...
    tree->OnCellEdited.connect(slot(*this,&TreeViewComponent::OnCellEdited));
    //..
    void
    TreeViewComponent::OnCellEdited(VDKObject* sender,
				GtkTreeIter* iter, 
				int col, char* new_text)
    {
    VDKTreeView* tree = dynamic_cast<VDKTreeView*>(sender);
    if(!sender)
      return;
    else
      {
      VDKTreeViewModel* model = tree->Model;
      char* old_text = model->GetCell(iter,col);
      printf("\nedited column:%d\n\told text:%s\n\tnew text:%s",
	     col, old_text,new_text);
      fflush(stdout);
      model->SetCell(iter, col, new_text);
     }
    }
    \endcode
  */
  VDKTreeViewColumn(VDKTreeView *owner,
		    int column, 
		    char* title = NULL, 
		    bool editable = false, 
		    int editcol = -1);
  /*!
    Destructor
  */
  ~VDKTreeViewColumn();
  /*!
    Return underlying GtkColumn
  */
  GtkTreeViewColumn *GtkColumn() { return column; }
  /*!
    Returns underlying GtkRenderer
  */
  GtkCellRenderer* Renderer() { return cell; }
  /*!
    Returns column owner
  */
  VDKTreeView* Owner() { return owner; }
  /*!
    Enable/Disable title, if enabled title can be selected with a click    
  */
  void ActiveTitle(bool flag = true);

 protected:
  void SetNormalBackground(VDKRgb rgb);
  void SetForeground(VDKRgb rgb);
  void SetFont(VDKFont* font);
  void SetTitle(const char* title);
  const char* GetTitle();
  void SetWidth(int w);
  int GetWidth();
  void SetSortable(bool flag);
  bool GetSortable();
};

typedef VDKList<VDKTreeViewColumn> VDKTreeViewColumnList;
typedef VDKListIterator<VDKTreeViewColumn> VDKTreeViewColumnListIterator;
/*!
  \class VDKTreeViewIter
  \brief Provides a wrapper for GtkTreeIter
 */
class VDKTreeViewIter: public GtkTreeIter
{
  GtkTreeModel *model;
 public:
  /*!
    Constructor
    \param model data model (defaults to  NULL)
  */
  VDKTreeViewIter(GtkTreeModel *model= NULL): model(model) {}
  /*!
    \internal
   */
  GtkTreeModel* Model(GtkTreeModel* m = NULL) { if(m) model = m; return model; }
  // meaningless
  /*!
    \internal 
   */
  bool operator==(VDKTreeViewIter& i) { return false; }
  /*!
    \internal 
   */
  bool operator<(VDKTreeViewIter& i) { return false; }
  /*!
    \param child_iter
    Returns true if iterator has a child, child_iter initialized to
    child, undefined otherwise.
   */
  bool Child(VDKTreeViewIter* child_iter);
  /*!
    \param parent_iter
    Returns true if iterator has a parent, parent_iter initialized to
    parent, undefined otherwise.
   */
  bool Parent(VDKTreeViewIter* parent_iter);
};

typedef VDKArray<VDKTreeViewIter> VDKTreeViewIterArray;
typedef VDKValueList<VDKTreeViewIter> VDKTreeViewIterList;
typedef VDKValueListIterator<VDKTreeViewIter> VDKTreeViewIterListIterator;

/*!
  \class VDKTreeView
  \brief Provides a wrapper for GtkTreeView widget
 */

class VDKTreeView: public VDKObject
{
 private:
  GtkTreeSelection *selection;
  VDKTreeViewColumnList *columns;
  VDKTreeViewIterList selections;

 protected:

 public:
  /*!
    Gets/Sets tree data model
   */
  VDKReadWriteValueProp<VDKTreeView,VDKTreeViewModel*> Model;
  /*!
    Gets selected column
  */
  VDKReadOnlyValueProp<VDKTreeView,int> SelectedColumn;
  /*!
    Constructor
    \param owner
    \param model data model (can be NULL)
    \param selection_mode

    Modes can be:
    GTK_SELECTION_SINGLE
    GTK_SELECTION_MULTIPLE
    GTK_SELECTION_BROWSE (tree does not react to selections)
  */
  VDKTreeView(VDKForm* owner ,
	      VDKTreeViewModel* model = NULL, 
	      GtkSelectionMode mode = GTK_SELECTION_SINGLE);
  /*!
    Destructor
  */
  ~VDKTreeView();

  /*!
    \internal
   */
  void SetModel(VDKTreeViewModel* model);

  /*!
    Return a list of columns
  */
  VDKTreeViewColumnList * Columns() { return columns; }  
  /*!
    Fills a list with selected iterators
  */
  void GetSelections();
  /*!
    Return selections list, that is a list og GtkTreeIter
    - Is safer call GetSelections() before use it.
    - We suggest also to Selections().flush() after use
   */
  VDKTreeViewIterList &Selections() { return selections; }
  /*!
    Selects a node
    \param iter to be selected
  */
  void SelectNode(GtkTreeIter* iter);
  /*!
    Unselects a node
    \param iter to be unselected
  */
  void UnselectNode(GtkTreeIter* iter);
  /*!
    Expands the node
    \param iter to be expanded, if NULL all node will be expanded
    \param expand_all if true expand all childs recursively (meaningfull only if iter != NULL )
  */
  void Expand(GtkTreeIter* iter = NULL, bool expand_all = false);
  /*!
    Remove all selected nodes and flushes selections list.
  */
  void RemoveSelected(void);
#ifdef USE_SIGCPLUSPLUS
  /*!
      Extended LS signal system:
      Received when a string type cell is being edited
      Response methods  have these signatures:
      void SomeClass::OnCellEdited(VDKObject* sender,
				GtkTreeIter* iter, 
				int column, 
				char* new_text)

      void SomeClass::OnCellEdited(GtkTreeIter* iter, 
				int column, 
				char* new_text)
  */
  VDKSignal3< void, GtkTreeIter*, int , char* > OnCellEdited;
  /*!
      Extended LS signal system:
      Received when a boolean type cell is being toggled
      Response methods have these signatures:
      void SomeClass::OnCellToggled(VDKObject* sender,
				GtkTreeIter* iter, 
				int column, 
				bool toggled)

      void SomeClass::OnCellToggled(GtkTreeIter* iter, 
				int column, 
				bool toggled)
  */
  VDKSignal3< void, GtkTreeIter*, int, bool>    OnCellToggled;
#endif  
};

#endif




