/***************************************************************************
                          SIMPLY POWERFUL TOOLKIT (SPTK)
                          CListView.h  -  description
                             -------------------
    begin                : Fri Mar 31 2000
    copyright            : (C) 2000-2005 by Alexey S.Parshin
    email                : alexeyp@gmail.com
 ***************************************************************************/

/***************************************************************************
   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.

   Please report all bugs and problems to "alexeyp@gmail.com"
 ***************************************************************************/

#ifndef __LISTVIEW_H__
#define __LISTVIEW_H__

#include <FL/Fl_Group.h>

#include <sptk3/sptk.h>
#include <sptk3/CScrollBar.h>
#include <sptk3/CDataControl.h>
#include <sptk3/CControl.h>
#include <sptk3/CListViewSelection.h>
#include <sptk3/CListViewRows.h>
#include <sptk3/CPackedStrings.h>
#include <sptk3/CIntList.h>
#include <sptk3/CColumn.h>
#include <sptk3/CDataSource.h>

/// @addtogroup gui GUI Classes
/// @{

/// The refersh kind
enum CRefreshKind { 
   LV_REFRESH_FULL,     ///< Retrieve the full dataset and replace the existing records completely
   LV_REFRESH_FAST      ///< Retrieve only the changed records and replace the changed records only
};

class CInternalComboBoxPanel;
class CDBDropDownList;

/// The data mode for the data() methods.
enum CListViewDataMode {
   LV_DATA_UNDEFINED,   ///< data() sets/returns item caption. If the data connection is defined (in CDBListView) - it flips to LV_DATA_KEY mode.
   LV_DATA_KEY,         ///< data() sets/returns item key value
   LV_DATA_INDEX,       ///< data() sets/returns item index
   LV_DATA_CAPTION      ///< data() sets/returns item caption
};

/// @brief List view widget
///
/// Class CListView is the list of multicolumn rows. It allows to sort rows
/// by column, supports type-in incremental search etc.
class SP_EXPORT CListView : public CControl, public CWaiter {
   friend class CInternalComboBoxPanel;
   friend class CDBDropDownList;
private:   

   /// Constructor initializer
   void ctor_init();

   bool m_fillInProgress;                  ///< Flag to indicate the current fill() operation
   bool m_fillTerminated;                  ///< Flag to terminate the current fill() operation

protected:    
   bool           m_capitalizeColumnNames; ///< Should the column headers to be capitilized?
   bool           m_multipleSelection;     ///< Is multiple selection allowed?
   int            m_horizPosition;         ///< The horizontal position - to be
   int            m_realHorizPosition;     ///< The horizontal position - existing
   int            m_maxWidth;              ///< The maximum required width for the columns
   int            m_redraw1;               ///< The minimal update pointer
   int            m_redraw2;               ///< The minimal update pointer
   unsigned       m_top;                   ///< The scrolling position
   static int     m_scrollbarWidth;        ///< Scrollbar width for this class of object
   int            m_currentTextFont;       ///< Text font, currently used to draw the list view
   int            m_currentTextSize;       ///< Text size, currently used to draw the list view

   /// Data mode. Defines the kind of data to work through data() method
   /// @see CListViewDataMode for more information
   CListViewDataMode m_dataMode;       

protected:

   int            m_mousePressedX;         ///< The mouse pressed position
   CSelection     m_selection;             ///< The list of currently selected items
   std::string    m_searchPhrase;          ///< Type-in search buffer
   unsigned       m_activeRow;             ///< The row where the focus is or should be
   unsigned       m_headerHeight;          ///< The height of the header area

   CListViewRows  m_rows;                  ///< The list of rows
   CColumnList    m_columnList;            ///< The list of columns

   int            m_draggingColumn;        ///< Dragged column
   int            m_draggingStarted;       ///< Dragging column started

   Fl_Font        m_textFont;              ///< The text font type
   uchar          m_textSize;              ///< The text font size
   Fl_Color       m_textColor;             ///< The text font color

   bool           m_showStripes;           ///< Show/hide horizontal stripes. Stripes is an alternative to cell grid.
   bool           m_showGrid;              ///< Show/hide cell grid. Grid is an alternative to stripes.
   bool           m_showSelection;         ///< Show/hide selection
   bool           m_autoRowHeight;         ///< Auto row height on/off

   /// Computes which column contains x-coordinate
   /// @param x int, coordinate
   /// @returns column number
   int  columnAt(int x);

   /// Finds an item in vertical position y
   /// @param y int, vertical position
   /// @returns row number, or -1 if not found
   int  find_item(int y);

   /// Returns the actual size of the drawing area. Internal.
   /// @param X int, output, x-xoordinate
   /// @param Y int, output, y-xoordinate
   /// @param W int, output, width
   /// @param H int, output, height
   void bbox(int& X, int& Y, int& W, int& H) const;

protected:

   /// Internal vertical scrollback callback
   static void scrollbar_callback(Fl_Widget* s, void*);

   /// Internal horizontal scrollback callback
   static void hscrollbar_callback(Fl_Widget* s, void*);

   /// Vertical scrollback
   CScrollBar scrollbar;     

   /// Horizontal scrollbar
   CScrollBar hscrollbar;    

   /// Returns horizontal position of the scroll
   int hposition() const {return m_horizPosition;}

   /// Sets horizontal position of the scroll
   /// @param hpos int, new horizontal position
   void hposition(int hpos); // pan to here

   /// Changes the default scrollbar width for the class
   /// @param b int, new scrollbar width
   static void scrollbar_width(int b) {m_scrollbarWidth = b;}

   /// Returns the default scrollbar width for the class
   static int scrollbar_width() {return m_scrollbarWidth;}

protected:

   /// Finds item with ID
   /// @param id int, item id
   /// @returns row number, or -1 if not found
   int  find_id(int id) const;

   /// Get focus callback. Internal.
   virtual int  get_focus();

   /// Returns true if item is selected
   /// @param index unsigned, item index
   virtual bool item_selected(unsigned index) const ;

   /// Selects or deselects the item
   /// @param index unsigned, item index
   /// @param select bool, select or deselect
   virtual void item_select(unsigned index, bool select);

   /// Recomputes all items height
   virtual int  item_compute_height_all();

   /// Recomputes items height
   /// @param rowData CPackedStrings *, item pointer
   /// @returns item height
   virtual int  item_compute_height(CPackedStrings *rowData);

   /// Recomputes items height
   /// @param index unsigned, row number
   /// @returns item height
   virtual int  item_height(unsigned index) const;

   /// Returns item width
   /// @param index unsigned, row number
   /// @returns item width
   virtual int  item_width(unsigned index) const ;

   /// Draws a row in coordinates. Internal
   /// @param index unsigned, row number
   /// @param rowData const CPackedStrings *, row pointer
   /// @param x int, x-coordinate
   /// @param y int, y-coordinate
   /// @param w int, width
   /// @param h int, height
   /// @param focusMode int, focus flag
   /// @param verticalAlign int, vertical align
   /// @param paintBackground bool, true if the item should paint the background
   /// @returns item width
   virtual void item_draw(unsigned index, const CPackedStrings *rowData, int x, int y, int w, int h, int focusMode, int verticalAlign, bool paintBackground=true) const ;

   /// Internal mouse callback
   virtual void item_clicked(int mouse_btn);

   /// The function that allows to overwrite the default text color for the cell.
   /// @param column int, the column number
   /// @param rowData const CPackedStrings&, row
   /// @returns textColor() 
   virtual Fl_Color item_color(int column,const CPackedStrings& rowData) const;

   /// Draws header. Internal
   /// @param x int, x-coordinate
   /// @param y int, y-coordinate
   /// @param hh int, header height
   virtual void header_draw(int x, int y, int hh) const ;

   /// Header clicked mouse callback
   /// @param x int, x-coordinate
   virtual void header_clicked(int x);

   /// Returns true if x is the drag-sensitive area
   virtual bool header_drag_position(int x);

   /// Header dragged to position x notification
   /// @param x int, x-coordinate
   virtual void header_dragged(int x);

   /// Header released in position x notification
   /// @param x int, x-coordinate
   virtual void header_released(int x);

   /// Request to redraw line index
   /// @param index unsigned, row number
   void redraw_line(unsigned index);

   /// Redraw all the lines
   void redraw_lines() {damage(FL_DAMAGE_SCROLL);} 

   /// General draw function. Draws everything
   virtual void draw();

   /// Internal callback if selected another item with the different user _data (key).
   void key_changed(unsigned index);

public:

   /// Constructor in SPTK style 
   /// @param label const char *, label
   /// @param layoutSize int, widget align in layout
   /// @param layoutAlign CLayoutAlign, widget align in layout
   CListView(const char *label=0,int layoutSize=20,CLayoutAlign layoutAlign=SP_ALIGN_TOP);

#ifdef __COMPATIBILITY_MODE__    
   /// Constructor in FLTK style 
   /// @param x int, x-position
   /// @param y int, y-position
   /// @param w int, width
   /// @param h int, height
   /// @param label, const char * label
   CListView(int, int, int, int, const char* = 0);
#endif    

   /// Destructor
   ~CListView() { clear(); }

   /// Overwritten handle()
   int  handle(int);

   /// Resizes the control and inside widgets. 
   /// @param x int, x-position
   /// @param y int, y-position
   /// @param w int, width
   /// @param h int, height
   void resize(int x,int y,int w,int h);

   /// Removes row
   /// @param index unsigned, row number
   virtual void removeRow(unsigned index);

   /// Adds new row
   /// @param ident int, row argument (key value)
   /// @param s1 const char *, text data for the column 1
   /// @param s2 const char *, text data for the column 2
   /// @param s3 const char *, text data for the column 3
   /// @param s4 const char *, text data for the column 4
   /// @param s5 const char *, text data for the column 5
   virtual void addRow(int ident,const char *s1,const char *s2=NULL,const char *s3=NULL,const char *s4=NULL,const char *s5=NULL);

   /// Adds new row
   /// @param count int, number of items in strings
   /// @param strings char * [], row data
   /// @param ident int, row argument (key value)
   virtual void addRow(int count,const char *strings[],int ident=0);

   /// Adds new row. Optional row ID may be defined inside row parameter.
   /// @param row CPackedStrings *, row data
   virtual void addRow(CPackedStrings *row);

   /// Adds new row. Optional row ID may be defined inside row parameter, 
   /// or through ident parameter.
   /// @param row const CStrings&, row data
   /// @param ident int, row argument (key value)
   virtual void addRow(const CStrings& row,int ident=0);

   /// Inserts new row. Optional row ID may be defined inside row parameter.
   /// @param position unsigned, insert position
   /// @param row CPackedStrings *, row data
   virtual void insertRow(unsigned position,CPackedStrings *row);

   /// Inserts new row. Optional row argument may be defined inside row parameter, 
   /// or through ident parameter.
   /// @param position unsigned, insert position
   /// @param row const CStrings&, row data
   /// @param ident int, row argument (key value)
   virtual void insertRow(unsigned position,const CStrings& row,int ident=0);

   /// Updates a row. Optional row ID may be defined inside row parameter.
   /// @param position unsigned, insert position
   /// @param row CPackedStrings *, row data
   virtual void updateRow(unsigned position,CPackedStrings *row);

   /// Updates a row. Optional row argument may be defined inside row parameter.
   /// @param position unsigned, insert position
   /// @param row const CStrings&, row data
   /// @param ident int, row argument (key value)
   virtual void updateRow(unsigned position,const CStrings& row,int ident=0);

   /// Removes all the rows
   virtual void clear();

   /// Returns the row count
   virtual unsigned size() const { return m_rows.size(); }

   /// Returns the scroll position
   virtual int top() const {return m_top;}

   /// Sets the scroll position
   /// @param index int, top row number
   virtual void top(int index);

   /// Is row number index selected?
   /// @param index int, row number
   int selected(int index) const;

   /// Switch the visibility flag for the row index
   /// @param index int, row number
   void show(int index);

   /// Switch the visibility flag for the row index
   /// @param index int, row number
   void hide(int index);

   /// Hides the list view widget
   void hide() {Fl_Widget::hide();}

   /// Is the row visible?
   /// @param index int, row number
   int visible(int index) const ;

   /// Is the widget visible?
   int visible() const { return Fl_Widget::visible(); }

   /// Returns the value the selected row and sort column cell
   std::string      textValue() const ;

   /// Selects the row where cell in the sort column is tv
   /// @param text CString, text to find and select
   void             textValue(std::string text);

   /// Sets data mode for the data() methods as LV_DATA_KEY, LV_DATA_INDEX, LV_DATA_CAPTION.
   /// Defines which information list view is working with in data() method - key value, item index, or item caption.
   /// @see CListViewDataMode for more information
   void dataMode(CListViewDataMode dm) { m_dataMode = dm; }

   /// Returns data mode as LV_DATA_KEY, LV_DATA_INDEX, LV_DATA_CAPTION.
   /// Defines which information list view is working with in data() method - key value, item index, or item caption.
   /// @see CListViewDataMode for more information
   CListViewDataMode dataMode() { return m_dataMode; }

   /// The universal data connector,
   /// @returns control data
   virtual CVariant data() const ;

   /// The universal data connector, selects row with user data
   /// @param v const CVariant, user data value to find and select
   virtual void     data(const CVariant v);

   /// Returns row pointer
   /// @param index int, row number
   /// @returns row pointer
   CPackedStrings  *row(unsigned index) const ;

   /// Returns selection list
   const CSelection& selection() const { return m_selection; }

   /// Returns currently selected row, or the last selected row in case of multiple selection.
   /// Returns 0L reference if there is no selection.
   CPackedStrings&  selectedRow() const;

   /// Returns the row number for the selected row
   int              selectedIndex() const;

   /// Selects a row
   /// @param index int, row number
   /// @param sel bool, true if select
   /// @returns true if success
   bool             selectRow(unsigned index,bool sel=true) { return select(index,sel,true); }

   /// Returns true if the row is currently visible in the scroll area
   /// @param index int, row number
   bool displayed(unsigned index) const;

   /// Makes the row visible in the scroll area
   /// @param index int, row number
   virtual void displayRow(unsigned index);

   /// Requests to redraw just one row
   /// @param index int, row number
   void redrawRow(unsigned index) { redraw_line(index); }

protected:

   /// Loads the key value from the query
   virtual void load(CQuery *); 

   /// Saves the key value from the query
   virtual void save(CQuery *); // storing the key value in data entry dialog

   /// True if the data is valid
   virtual bool valid() const { return true; }

   /// Returns the cell border width
   virtual unsigned cellBorderWidth() const;

   /// Selects the row. Internal
   /// @param index int, row number
   /// @param sel bool, true if select
   /// @param docallbacks bool, true if we want to call callbacks
   /// @returns true if success
   bool select(unsigned index,bool sel=true,int docallbacks=false);

   /// Selects the only row. Internal
   /// @param index int, row number
   /// @param docallbacks bool, true if we want to call callbacks
   /// @returns true if success
   bool select_only(unsigned index,bool docallbacks=false);

   /// Selects the row. Executes the callbacks. Internal
   /// @param index int, row number
   /// @returns true if success
   bool activate_row(unsigned index);

   /// Selects the first row. Executes the callbacks. Internal
   /// @returns true if success
   bool select_first();

   /// Selects the last row. Executes the callbacks. Internal
   /// @returns true if success
   bool select_last();

   /// Selects the next row. Executes the callbacks. Internal
   /// @returns true if success
   bool select_next();

   /// Selects the prior row. Executes the callbacks. Internal
   /// @returns true if success
   bool select_prior();

   /// Selects the next page row. Executes the callbacks. Internal
   /// @returns true if success
   bool select_next_page();

   /// Selects the prior page row. Executes the callbacks. Internal
   /// @returns true if success
   bool select_prior_page();

public:
   /// Sets a new column list
   /// @param columns const CColumnList&, new column list
   void columns(const CColumnList& columns);

   /// Retaurns column list
   CColumnList& columns()                 { return m_columnList; }

   /// Returns column informartion
   /// @param col unsigned, column number 0..columnCount() -1
   CColumn& column(unsigned col)          { return m_columnList[col]; }

   /// Returns column informartion
   /// @param colname const char *, column name
   CColumn& column(const char *colname);

   /// Adds a new column as a copy of column
   /// const column CColumn&, new column information
   void addColumn(const CColumn& column)  { m_columnList.push_back(column); }

   /// Adds a new column as a copy of column
   /// @param colname const char *, column name
   /// @param type CVariantType, column data type
   /// @param cwidth short, column width
   /// @param cvisible bool, is the column visible?
   void addColumn(const std::string colname,CVariantType type,short cwidth=100,bool cvisible=true)
   { m_columnList.push_back(CColumn(colname,type,cwidth,cvisible)); }

   /// SPTK RTTI information
   /// @returns control type as CControlKind
   /// @see CControlKind
   virtual CControlKind kind() const { return DCV_LISTBOX; }

   /// Returns auto row height mode as true/false
   bool     autoRowHeight() { return m_autoRowHeight; }

   /// Sets auto row height mode as true/false
   void     autoRowHeight(bool arh);

   /// Returns text font type
   Fl_Font  textFont() const { return m_textFont; }

   /// Sets text font type
   void     textFont(Fl_Font f) { m_textFont = f; }

   /// Returns text font size
   uchar    textSize() const { return m_textSize; }

   /// Sets text font size
   void     textSize(uchar s) { m_textSize = s; }

   /// Returns text color
   Fl_Color textColor() const { return m_textColor;}

   /// Sets text color
   void     textColor(Fl_Color n) {m_textColor = n;}

   /// Returns show grid mode
   bool     showGrid() { return m_showGrid; }

   /// Sets show grid mode
   void     showGrid(bool flag);

   /// Returns show stripes
   bool     showStripes() { return m_showStripes; }

   /// Sets show stripes mode
   void     showStripes(bool flag);

   /// Returns show selection mode
   bool     showSelection() { return m_showSelection; }

   /// Sets show selection mode
   void     showSelection(bool flag) { m_showSelection = flag; }

   /// Returns multiple selection enabled or disabled
   bool     multiSelect() const { return m_multipleSelection; }

   /// Sets multiple selection enabled or disabled
   void     multiSelect(bool ms) { m_multipleSelection = ms; }

   /// Returns a selected rows ID list
   void     getSelections(CIntList& selection) const;

   /// Sets a selected rows ID list
   void     setSelections(const CIntList& selection);

   /// Finds an item with the caption (a string in the first column).
   /// @param caption std::string, the caption to find and select.
   /// @returns an item, or NULL if item caption is not found
   CPackedStrings *findCaption(std::string caption);

   /// Finds an item with the key (an integer associated with the item - argument()).
   /// @param keyValue int, the caption to find and select.
   /// @returns an item, or NULL if item caption is not found
   CPackedStrings *findKey(int keyValue);

   /// Returns maximum width of all items including the header row
   unsigned fullWidth() const ;   // current width of all items

   /// Returns summary height of all items including the header row
   unsigned fullHeight();  // current height of all items

   /// Finds a string in the sort column of list view defined with sortColumn()
   /// @param str std::string, string to find
   /// @param select bool, true if we want to select the found row
   /// @param startRow unsigned, the row number to start
   /// @param endRow unsigned, the row number to finish
   /// @returns the row number, or -1 if not found
   virtual int findString(std::string str,bool select=true,unsigned startRow=0,unsigned endRow=0);

   /// Returns sort column name
   std::string sortColumnName() const;

   /// Sets the sort column 
   /// @param column int, sort column number
   /// @param sortNow bool, true if you want to sort immediatedly
   void     sortColumn(int column,bool sortNow);

   /// Returns the sort column number
   int      sortColumn() const;

   /// Returns the sort direction - ascending/descending
   bool     sortAscending() const { return m_rows.sortAscending(); }

   /// Sets the sort direction - ascending/descending
   /// @param ascending bool, sort direction - ascending/descending
   /// @param sortNow bool, true if you want to sort immediatedly
   void     sortAscending(bool ascending,bool sortNow); 


   /// Sorts ListView rows using existing information about sort column and direction
   void     sort() { m_rows.sort(); }

   /// Fills the list view with the data from datasource.
   /// Uses all available datasource information about column and data types.
   /// @param ds CDataSource&, the datasource
   /// @param keyFieldName std::string, field used to fill in key values
   /// @param recordsLimit unsigned, maximum records to retrieve
   /// @param recordsEstimated unsigned, records estimated (to use the fill progression event)
   /// @param refreshKind CRefreshKind, type of refresh - full or just changed records
   /// @see CRefreshKind
   /// @see CDataSource
   void     fill(CDataSource& ds,std::string keyFieldName="",unsigned recordsLimit=0,unsigned recordsEstimated=0,CRefreshKind refreshKind=LV_REFRESH_FULL);

   /// Computes the optimal widgets size
   /// @param w int&, input - width offered by the program, output - width required by widget
   /// @param h int&, input - height offered by the program, output - height required by widget
   virtual void preferredSize(int& w,int& h);

   /// Returns header height
   unsigned headerHeight() const { return m_headerHeight; }
   /// Sets the header height, 0 to hide headers
   void     headerHeight(unsigned hh) { m_headerHeight = hh; }

   /// Returns the currently active row
   unsigned activeRow() const { return m_activeRow; }

   /// Returns true if column names are auto-capitilized
   bool capitalizeColumnNames() const { return m_capitalizeColumnNames; }

   /// Defines if column names should be auto-capitilized
   /// @param ccn bool, true if column names should be auto-capitilized
   void capitalizeColumnNames(bool ccn) { m_capitalizeColumnNames = ccn; }

   /// Terminate the current fill() operation (if in progress)
   void terminateFill() { m_fillTerminated = true; }
};

/// @}

#endif
