//
// "$Id$"
//

#ifndef FL_TREE_ITEM_H
#define FL_TREE_ITEM_H

#include <FL/Fl.H>
#include <FL/Fl_Widget.H>
#include <FL/Fl_Image.H>
#include <FL/fl_draw.H>

#include <FL/Fl_Tree_Item_Array.H>
#include <FL/Fl_Tree_Prefs.H>

//////////////////////
// FL/Fl_Tree_Item.H
//////////////////////
//
// Fl_Tree -- This file is part of the Fl_Tree widget for FLTK
// Copyright (C) 2009-2010 by Greg Ercolano.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file.  If this
// file is missing or damaged, see the license at:
//
//     http://www.fltk.org/COPYING.php
//
// Please report all bugs and problems on the following page:
//
//     http://www.fltk.org/str.php
//

///
/// \file
/// \brief This file contains the definitions for Fl_Tree_Item
///

/// \class Fl_Tree_Item
/// \brief Tree widget item.
///
/// This class is a single tree item, and manages all of the item's attributes.
/// Fl_Tree_Item is used by Fl_Tree, which is comprised of many instances of Fl_Tree_Item.
///
/// Fl_Tree_Item is hierarchical; it dynamically manages an Fl_Tree_Item_Array of children
/// that are themselves instances of Fl_Tree_Item. Each item can have zero or more children.
/// When an item has children, close() and open() can be used to hide or show them.
///
/// Items have their own attributes; font size, face, color.
/// Items maintain their own hierarchy of children.
///
/// When you make changes to items, you'll need to tell the tree to redraw()
/// for the changes to show up.
///
/// New 1.3.3 ABI feature:
/// You can define custom items by either adding a custom widget to the item
/// with Fl_Tree_Item::widget(), or override the draw_item_content() method
/// if you want to just redefine how the label is drawn.
///
/// The following shows the Fl_Tree_Item's dimensions, useful when overriding
/// the draw_item_content() method:
///
///   \image html  Fl_Tree_Item-dimensions.png "Fl_Tree_Item's internal dimensions." width=6cm
///   \image latex Fl_Tree_Item-dimensions.png "Fl_Tree_Item's internal dimensions." width=6cm
///
class Fl_Tree;
class FL_EXPORT Fl_Tree_Item {
  Fl_Tree                *_tree;		// parent tree
  const char             *_label;		// label (memory managed)
  Fl_Font                 _labelfont;		// label's font face
  Fl_Fontsize             _labelsize;		// label's font size
  Fl_Color                _labelfgcolor;	// label's fg color
  Fl_Color                _labelbgcolor;	// label's bg color (0xffffffff is 'transparent')
  /// \enum Fl_Tree_Item_Flags
  enum Fl_Tree_Item_Flags {
    OPEN                = 1<<0,		///> item is open
    VISIBLE             = 1<<1,		///> item is visible
    ACTIVE              = 1<<2,		///> item is active
    SELECTED            = 1<<3		///> item is selected
  };
  unsigned short _flags;		// misc flags
  int                     _xywh[4];		// xywh of this widget (if visible)
  int                     _collapse_xywh[4];	// xywh of collapse icon (if visible)
  int                     _label_xywh[4];	// xywh of label
  Fl_Widget              *_widget;		// item's label widget (optional)
  Fl_Image               *_usericon;		// item's user-specific icon (optional)
  Fl_Image               *_userdeicon;		// deactivated usericon
  Fl_Tree_Item_Array      _children;		// array of child items
  Fl_Tree_Item           *_parent;		// parent item (=0 if root)
  void                   *_userdata;    	// user data that can be associated with an item
  Fl_Tree_Item           *_prev_sibling;	// previous sibling (same level)
  Fl_Tree_Item           *_next_sibling;	// next sibling (same level)
  // Protected methods
protected:
  void _Init(const Fl_Tree_Prefs &prefs, Fl_Tree *tree);
  void show_widgets();
  void hide_widgets();
  void draw_vertical_connector(int x, int y1, int y2, const Fl_Tree_Prefs &prefs);
  void draw_horizontal_connector(int x1, int x2, int y, const Fl_Tree_Prefs &prefs);
  void recalc_tree();
  int calc_item_height(const Fl_Tree_Prefs &prefs) const;
  Fl_Color drawfgcolor() const;
  Fl_Color drawbgcolor() const;

public:
  Fl_Tree_Item(const Fl_Tree_Prefs &prefs);	// CTOR -- backwards compatible
  Fl_Tree_Item(Fl_Tree *tree);			// CTOR -- ABI 1.3.3+
  virtual ~Fl_Tree_Item();			// DTOR -- ABI 1.3.3+
  Fl_Tree_Item(const Fl_Tree_Item *o);		// COPY CTOR
  /// The item's x position relative to the window
  int x() const { return(_xywh[0]); }
  /// The item's y position relative to the window
  int y() const { return(_xywh[1]); }
  /// The entire item's width to right edge of Fl_Tree's inner width
  /// within scrollbars.
  int w() const { return(_xywh[2]); }
  /// The item's height
  int h() const { return(_xywh[3]); }
  /// The item's label x position relative to the window
  /// \version 1.3.3
  int label_x() const { return(_label_xywh[0]); }
  /// The item's label y position relative to the window
  /// \version 1.3.3
  int label_y() const { return(_label_xywh[1]); }
  /// The item's maximum label width to right edge of Fl_Tree's inner width
  /// within scrollbars.
  /// \version 1.3.3
  int label_w() const { return(_label_xywh[2]); }
  /// The item's label height
  /// \version 1.3.3
  int label_h() const { return(_label_xywh[3]); }
  virtual int draw_item_content(int render);
  void draw(int X, int &Y, int W, Fl_Tree_Item *itemfocus, 
	    int &tree_item_xmax, int lastchild=1, int render=1);
  void show_self(const char *indent = "") const;
  void label(const char *val);
  const char *label() const;

  /// Set a user-data value for the item.
  inline void user_data( void* data ) { _userdata = data; }

  /// Retrieve the user-data value that has been assigned to the item.
  inline void* user_data() const { return _userdata; }
  
  /// Set item's label font face.
  void labelfont(Fl_Font val) {
    _labelfont = val; 
    recalc_tree();		// may change tree geometry
  }
  /// Get item's label font face.
  Fl_Font labelfont() const {
    return(_labelfont);
  }
  /// Set item's label font size.
  void labelsize(Fl_Fontsize val) {
    _labelsize = val; 
    recalc_tree();		// may change tree geometry
  }
  /// Get item's label font size.
  Fl_Fontsize labelsize() const {
    return(_labelsize);
  }
  /// Set item's label foreground text color.
  void labelfgcolor(Fl_Color val) {
    _labelfgcolor = val; 
  }
  /// Return item's label foreground text color.
  Fl_Color labelfgcolor() const {
    return(_labelfgcolor); 
  }
  /// Set item's label text color. Alias for labelfgcolor(Fl_Color)).
  void labelcolor(Fl_Color val) {
     labelfgcolor(val);
  }
  /// Return item's label text color. Alias for labelfgcolor() const).
  Fl_Color labelcolor() const {
    return labelfgcolor(); 
  }
  /// Set item's label background color.
  /// A special case is made for color 0xffffffff which uses the parent tree's bg color.
  void labelbgcolor(Fl_Color val) {
    _labelbgcolor = val; 
  }
  /// Return item's label background text color.
  /// If the color is 0xffffffff, the default behavior is the parent tree's
  /// bg color will be used. (An overloaded draw_item_content() can override
  /// this behavior.)
  Fl_Color labelbgcolor() const {
    return(_labelbgcolor); 
  }
  /// Assign an FLTK widget to this item.
  void widget(Fl_Widget *val) {
    _widget = val; 
    recalc_tree();		// may change tree geometry
  }
  /// Return FLTK widget assigned to this item.
  Fl_Widget *widget() const {
    return(_widget); 
  }
  /// Return the number of children this item has.
  int children() const {
    return(_children.total()); 
  }
  /// Return the child item for the given 'index'.
  Fl_Tree_Item *child(int index) {
    return(_children[index]); 
  }
  /// Return the const child item for the given 'index'.
  const Fl_Tree_Item *child(int t) const;
  /// See if this item has children.
  int has_children() const {
    return(children()); 
  }
  int find_child(const char *name);
  int find_child(Fl_Tree_Item *item);
  int remove_child(Fl_Tree_Item *item);
  int remove_child(const char *new_label);
  void clear_children();
  void swap_children(int ax, int bx);
  int swap_children(Fl_Tree_Item *a, Fl_Tree_Item *b);
  const Fl_Tree_Item *find_child_item(const char *name) const;
        Fl_Tree_Item *find_child_item(const char *name);
  const Fl_Tree_Item *find_child_item(char **arr) const;
        Fl_Tree_Item *find_child_item(char **arr);
  const Fl_Tree_Item *find_item(char **arr) const;
        Fl_Tree_Item *find_item(char **arr);
  //////////////////
  // Adding items
  //////////////////
  Fl_Tree_Item *add(const Fl_Tree_Prefs &prefs,
  		    const char *new_label,
  		    Fl_Tree_Item *newitem);
  Fl_Tree_Item *add(const Fl_Tree_Prefs &prefs,
  		    const char *new_label);
  Fl_Tree_Item *add(const Fl_Tree_Prefs &prefs,
  		    char **arr,
		    Fl_Tree_Item *newitem);
  Fl_Tree_Item *add(const Fl_Tree_Prefs &prefs,
  		    char **arr);
  Fl_Tree_Item *replace(Fl_Tree_Item *new_item);
  Fl_Tree_Item *replace_child(Fl_Tree_Item *olditem, Fl_Tree_Item *newitem);
  Fl_Tree_Item *insert(const Fl_Tree_Prefs &prefs, const char *new_label, int pos=0);
  Fl_Tree_Item *insert_above(const Fl_Tree_Prefs &prefs, const char *new_label);
  Fl_Tree_Item* deparent(int index);
  int reparent(Fl_Tree_Item *newchild, int index);
  int move(int to, int from);
  int move(Fl_Tree_Item *item, int op=0, int pos=0);
  int move_above(Fl_Tree_Item *item);
  int move_below(Fl_Tree_Item *item);
  int move_into(Fl_Tree_Item *item, int pos=0);
  int depth() const;
  Fl_Tree_Item *prev();
  Fl_Tree_Item *next();
  Fl_Tree_Item *next_sibling();
  Fl_Tree_Item *prev_sibling();
  void update_prev_next(int index);
  Fl_Tree_Item *next_displayed(Fl_Tree_Prefs &prefs);	// deprecated
  Fl_Tree_Item *prev_displayed(Fl_Tree_Prefs &prefs);	// deprecated
  Fl_Tree_Item *next_visible(Fl_Tree_Prefs &prefs);
  Fl_Tree_Item *prev_visible(Fl_Tree_Prefs &prefs);
  
  /// Return the parent for this item. Returns NULL if we are the root.
  Fl_Tree_Item *parent() {
    return(_parent);
  }
  /// Return the const parent for this item. Returns NULL if we are the root.
  const Fl_Tree_Item *parent() const {
    return(_parent);
  }
  /// Set the parent for this item.
  /// Should only be used by Fl_Tree's internals.
  ///
  void parent(Fl_Tree_Item *val) {
    _parent = val;
  }
  const Fl_Tree_Prefs& prefs() const;
  /// Return the tree for this item.
  /// \version 1.3.3
  const Fl_Tree *tree() const {
    return(_tree);
  }
  /// Return the tree for this item.
  /// \version 1.3.4
  Fl_Tree *tree() {
    return(_tree);
  }
  //////////////////
  // State
  //////////////////
  void open();
  void close();
  /// See if the item is 'open'.
  int is_open() const {
    return(is_flag(OPEN));
  }
  /// See if the item is 'closed'.
  int is_close() const {
    return(is_flag(OPEN)?0:1);
  }
  /// Toggle the item's open/closed state.
  void open_toggle() {
    is_open()?close():open();	// handles calling recalc_tree()
  }
  /// Change the item's selection state to the optionally specified 'val'.
  /// If 'val' is not specified, the item will be selected.
  ///
  void select(int val=1) {
    set_flag(SELECTED, val);
  }
  /// Toggle the item's selection state.
  void select_toggle() {
    if ( is_selected() ) {
      deselect();	// deselect if selected
    } else {
      select();		// select if deselected
    }
  }
  /// Select item and all its children.
  ///     Returns count of how many items were in the 'deselected' state,
  ///     ie. how many items were "changed".
  ///
  int select_all() {
    int count = 0;
    if ( ! is_selected() ) {
      select();
      ++count;
    }
    for ( int t=0; t<children(); t++ ) {
      count += child(t)->select_all();
    }
    return(count);
  }
  /// Disable the item's selection state.
  void deselect() {
    set_flag(SELECTED, 0);
  }
  /// Deselect item and all its children.
  ///     Returns count of how many items were in the 'selected' state,
  ///     ie. how many items were "changed".
  ///
  int deselect_all() {
    int count = 0;
    if ( is_selected() ) {
      deselect();
      ++count;
    }
    for ( int t=0; t<children(); t++ ) {
      count += child(t)->deselect_all();
    }
    return(count);
  }
  /// See if the item is selected.
  char is_selected() const {
    return(is_flag(SELECTED));
  }
  /// Change the item's activation state to the optionally specified 'val'.
  ///
  /// When deactivated, the item will be 'grayed out'; the callback() 
  /// won't be invoked if the user clicks on the label. If a widget()
  /// is associated with the item, its activation state will be changed as well.
  ///
  /// If 'val' is not specified, the item will be activated.
  ///
  void activate(int val=1) {
    set_flag(ACTIVE,val);
    if ( _widget && val != (int)_widget->active() ) {
      if ( val ) {
	_widget->activate();
      } else {
	_widget->deactivate();
      }
      _widget->redraw();
    }
  }
  /// Deactivate the item; the callback() won't be invoked when clicked.
  /// Same as activate(0)
  ///
  void deactivate() {
    activate(0);
  }
  /// See if the item is activated.
  char is_activated() const {
    return(is_flag(ACTIVE));
  }
  /// See if the item is activated. Alias for is_activated().
  char is_active() const {
    return(is_activated());
  }
  /// See if the item is visible. Alias for is_visible().
  int visible() const {
    return(is_visible());
  }
  /// See if the item is visible.
  int is_visible() const {
    return(is_flag(VISIBLE));
  }
  int visible_r() const;

  /// Set the item's user icon to an Fl_Image. Use '0' to disable.
  /// No internal copy is made, caller must manage icon's memory.
  ///
  /// Note, if you expect your items to be deactivated(),
  /// use userdeicon(Fl_Image*) to set up a 'grayed out' version of your icon
  /// to be used for display.
  ///
  /// \see userdeicon(Fl_Image*)
  ///
  void usericon(Fl_Image *val) {
    _usericon = val;
    recalc_tree();		// may change tree geometry
  }
  /// Get the item's user icon as an Fl_Image. Returns '0' if disabled.
  Fl_Image *usericon() const {
    return(_usericon);
  }
  /// Set the usericon to draw when the item is deactivated. Use '0' to disable.
  /// No internal copy is made; caller must manage icon's memory.
  ///
  /// To create a typical 'grayed out' version of your usericon image,
  /// you can do the following:
  ///
  /// \code
  ///      // Create tree + usericon for items
  ///      Fl_Tree *tree = new Fl_Tree(..);
  ///      Fl_Image *usr_icon = new Fl_Pixmap(..); // your usericon
  ///      Fl_Image *de_icon  = usr_icon->copy();  // make a copy, and..
  ///      de_icon->inactive();                    // make it 'grayed out'
  ///      ...
  ///      for ( .. ) {                 // item loop..
  ///        item = tree->add("...");   // create new item
  ///        item->usericon(usr_icon);  // assign usericon to items
  ///        item->userdeicon(de_icon); // assign userdeicon to items
  ///        ..
  ///      }
  /// \endcode
  ///
  /// In the above example, the app should 'delete' the two icons
  /// when they're no longer needed (e.g. after the tree is destroyed)
  ///
  /// \version 1.3.4
  ///
  void userdeicon(Fl_Image* val) {
    _userdeicon = val;
  }
  /// Return the deactivated version of the user icon, if any.
  /// Returns 0 if none.
  Fl_Image* userdeicon() const {
    return _userdeicon;
  }
  //////////////////
  // Events
  //////////////////
  const Fl_Tree_Item* find_clicked(const Fl_Tree_Prefs &prefs, int yonly=0) const;
  Fl_Tree_Item* find_clicked(const Fl_Tree_Prefs &prefs, int yonly=0);
  int event_on_collapse_icon(const Fl_Tree_Prefs &prefs) const;
  int event_on_label(const Fl_Tree_Prefs &prefs) const;
  /// Is this item the root of the tree?
  int is_root() const {
    return(_parent==0?1:0);
  }

  // Protected methods
  // TODO: move these to top 'protected:' section
protected:
  /// Set a flag to an on or off value. val is 0 or 1.
  inline void set_flag(unsigned short flag,int val) {
    if ( flag==OPEN || flag==VISIBLE ) {
      recalc_tree();		// may change tree geometry
    }
    if ( val ) _flags |= flag; else _flags &= ~flag;
  }
  /// See if flag set. Returns 0 or 1.
  inline int is_flag(unsigned short val) const {
    return(_flags & val ? 1 : 0);
  }

};

#endif /*FL_TREE_ITEM_H*/

//
// End of "$Id$".
//
