/* 
 * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; version 2 of the
 * License.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */
#ifndef _MFORMS_TREEVIEW_H_
#define _MFORMS_TREEVIEW_H_

#include <mforms/view.h>
#include <boost/cstdint.hpp>

/**
 * Implementation of a control class for a treeview control.
 */
namespace mforms {
  class TreeView;
 
  /** Determines what type a column should have (mainly describing the column *editor*). */
  enum TreeColumnType
  {
    StringColumnType, //!< simple string, with text entry editor
    IntegerColumnType, //!< numeric field, with text entry editor
    LongIntegerColumnType, //!< 64bit numeric field, with text entry editor
    CheckColumnType, //!< boolean field, with checkbox
    IconColumnType //!< icon field, value is the icon path
  };

  /** Options used to customize what to show in the tree. */
  enum TreeOptions
  {
    TreeDefault          = 0,
    TreeNoColumns        = 1 << 3, //!< On non-Windows platforms columns are always on, so switch them on
                                   //! on Windows too by default and use this flag to switch them off, if really needed.
                                   //! At least gtk has problems with arbitrary items for a tree. Treeview in gtk is
                                   //! built on View-Source model, where source is a row/column based thing. That may
                                   //! require some hacking to support NoColums in gtk, so really think if that worth it.
    TreeAllowReorderRows = 1 << 4,
    TreeShowColumnLines  = 1 << 5, //!< show column separator lines
    TreeShowRowLines     = 1 << 6, //!< show row separator lines
    TreeNoBorder         = 1 << 7, //!< Switch off the border around the control. Default is to show the border.
    TreeSidebar          = 1 << 8, //!< sidebar style treeview
    TreeNoHeader         = 1 << 9, //!< disable header from treeview, unsupported on Windows.
    TreeShowHeader       = 0,      //!< for backwards compatibility
  };


  inline TreeOptions operator| (TreeOptions a, TreeOptions b)
  {
    return (TreeOptions) ((int) a | (int) b);
  }

#ifndef DOXYGEN_SHOULD_SKIP_THIS
#ifndef SWIG
  struct TreeViewImplPtrs
  {
    bool (__stdcall *create)(TreeView *self, TreeOptions options);
    
    int (__stdcall *add_column)(TreeView *self, TreeColumnType type, const std::string &name, int initial_width, bool editable);
    void (__stdcall *end_columns)(TreeView *self);
    
    void (__stdcall *clear_rows)(TreeView *self);

    void (__stdcall *delete_row)(TreeView *self, int row);
    int (__stdcall *add_row)(TreeView *self);
    int (__stdcall *get_selected)(TreeView *self);
    void (__stdcall *set_selected)(TreeView *self, const int idx);

    void (__stdcall *set_row_height)(TreeView *self, int height);
    
    void (__stdcall *set_allow_sorting)(TreeView *self, bool);
    
    int (__stdcall *count)(TreeView *self);

    void (__stdcall *set_row_tag)(TreeView *self, int row, const std::string &value);
    void (__stdcall *set_string)(TreeView *self, int row, int column, const std::string &value);
    void (__stdcall *set_int)(TreeView *self, int row, int column, int value);
    void (__stdcall *set_long)(TreeView *self, int row, int column, boost::int64_t value);
    void (__stdcall *set_check)(TreeView *self, int row, int column, bool value);

    void (__stdcall *freeze_refresh)(TreeView *self, bool);
    
    std::string (__stdcall *get_row_tag)(TreeView *self, int row);
    std::string (__stdcall *get_string)(TreeView *self, int row, int column);
    int (__stdcall *get_int)(TreeView *self, int row, int column);
    boost::int64_t (__stdcall *get_long)(TreeView *self, int row, int column);
    bool (__stdcall *get_check)(TreeView *self, int row, int column);
  };
#endif
#endif
  
  class Menu;

  /** Control to show items in multiple columns in the form of a tree. 
   
   However currently, only a flat list is supported.
   Before adding items, you must first define the columns and the content types with
   add_column() and end_columns()
   */
  class MFORMS_EXPORT TreeView : public View
  {
  public:
    TreeView(TreeOptions options);

    /** Adds a column to be displayed in the tree.
     
     @param type - type of value to be displayed in column
     @param name - name/caption to show in header
     @param initia_width - width in pixels of the column
     @param editable - whether editing is allowed for rows in this column
     */
    int add_column(TreeColumnType type, const std::string &name, int initial_width, bool editable);
    /** Must be called after needed add_column() calls are finished. */
    void end_columns();

    /** Removes all rows */
    void clear_rows();
    /** Removes specified row */
    void delete_row(int row);
    /** Adds a new row and return its index */
    int add_row();
    /** Retuns index of the selected item, if no item is selected -1 is returned */
    int get_selected();
    /** Sets the selected index */
    void set_selected(const int idx);
    
    /** Sets the height of a row in pixels */
    void set_row_height(int height);
    
    /** Toggles sorting of rows when the user clicks on a column header
     
     Be careful with the get_selected() and set_selected() methods when sorting is enabled,
     as the index of a row will change depending on the sorting. Use get_row_tag() for
     an immutable identifier for rows.
     
     \warning When sorting is enabled, you must use freeze_refresh()/thaw_refresh() when making changes to tree,
     otherwise the contents will become corrupted.
     */
    void set_allow_sorting(bool flag);

    /** Return number of items in tree */
    int count();
    
    /** Gets a tag set to a row with set_row_tag() */
    std::string get_row_tag(int row);
    /** Sets a tag (an arbitrary string) to the given row */
    void set_row_tag(int row, const std::string &tag);

#ifdef SWIG
%rename(set_string) set(int row, int column, const std::string &value);
%rename(set_int) set(int row, int column, int value);
%rename(set_long) set(int row, int column, boost::int64_t value);
%rename(set_bool) set(int row, int column, bool check);
%ignore set(int row, int column, const char *value);
#endif
    /** Sets the value of a cell in a StringColumnType column.
     
     In Python use set_string() */
    void set(int row, int column, const std::string &value);
    /** Sets the value of a cell in a StringColumnType column.
     
     In Python use set_string() */    
    void set(int row, int column, const char *value) { set(row, column, std::string(value)); }
    /** Sets the value of a cell in a IntegerColumnType column.
     
     In Python use set_int() */    
    void set(int row, int column, int value);
    /** Sets the value of a cell in a LongIntegerColumnType column.
     
     In Python use set_long() */    
    void set(int row, int column, boost::int64_t value);    
    /** Sets the value of a cell in a CheckColumnType column.
     
     In Python use set_bool() */    
    void set(int row, int column, bool check);

    /** Gets the value from a StringColumnType column cell */
    std::string get_string(int row, int column);
    /** Gets the value from a IntegerColumnType column cell */
    int get_int(int row, int column);
    /** Gets the value from a LongIntegerColumnType column cell */
    boost::int64_t get_long(int row, int column);
    /** Gets the value from a CheckColumnType column cell */
    bool get_check(int row, int column);

    /** Freezes refresh of tree display. Use when updating a large number of rows or when sorting is enabled. */
    void freeze_refresh();
    
    /** Unfreezes a previously done freeze_refresh() */
    void thaw_refresh();
    
    /** Signal emitted when the selected row is changed 
     
     In Python use add_changed_callback()
     */
    boost::signals2::signal<void ()>* signal_changed() {return &_signal_changed;}
    /** Signal emitted when the user double-clicks a cell.
     
     Arguments passed are row index and column index.
     
     In Python use add_row_activated_callback()
     */
    boost::signals2::signal<void (int, int)>* signal_row_activated() {return &_signal_activated;}

    /** Sets a callback to handle changed made by user to tree cells.
     
     if this handler is set, it must call set() itself whenever a cell is edited
     otherwise changes will not be commited.
     */
    void set_cell_edit_handler(const boost::function<void (int, int, std::string)> &handler);
 
    /** Sets a context menu to be attached to the treeview, to be shown on right click
     
     Note: Ownership of the context menu remains with the caller and it will not be freed
     when this object is deleted. */
    void set_context_menu(Menu *menu);
    
    /** Returns the context menu object attached to the treeview */
    Menu *get_context_menu() { return _context_menu; }
    
#ifndef DOXYGEN_SHOULD_SKIP_THIS   
#ifndef SWIG
  public: 
    // Following methods are for internal use.
    void changed();
    void row_activated(int row, int column);

    // To be called when user edits a cell. If this returns true, the cell value should be
    // updated by the caller, otherwise it should be left unchanged.
    bool cell_edited(int row, int column, const std::string &value);
#endif
#endif
  protected:
    TreeViewImplPtrs *_treeview_impl;
    boost::signals2::signal<void ()>  _signal_changed;
    boost::signals2::signal<void (int, int)>  _signal_activated;
    boost::function<void (int, int, std::string)> _cell_edited;
    Menu *_context_menu;
    bool _updating;
  };
}

#endif /* _TREEVIEW_H_ */
