// This may look like C code, but it's really -*- C++ -*-
/*
 * Copyright (C) 2009 Emweb bvba, Kessel-Lo, Belgium.
 *
 * See the LICENSE file for terms of use.
 */
#ifndef WT_WABSTRACTITEMVIEW_H_
#define WT_WABSTRACTITEMVIEW_H_

#include <Wt/WCompositeWidget>
#include <Wt/WModelIndex>
#include <Wt/WSignalMapper>
#include <Wt/WItemSelectionModel>
#include <Wt/WValidator>

namespace Wt {

  class WAbstractItemDelegate;
  class WAbstractItemModel;
  class WApplication;
  class WCssTemplateRule;

/*! \class WAbstractItemView Wt/WAbstractItemView Wt/WAbstractItemView
 *  \brief An abstract base class for item Views.
 *
 * See WTableView or WTreeView for a description.
 *
 *
 * <h3>i18n</h3>
 *
 * The strings used in this class can be translated by overriding
 * the default values for the following localization keys:
 * - Wt.WAbstractItemView.PageIOfN: <b>{1}</b> of <b>{2}</b>
 * - Wt.WAbstractItemView.PageBar.First: &\#xc2ab; First
 * - Wt.WAbstractItemView.PageBar.Previous: &\#xe280b9; Previous
 * - Wt.WAbstractItemView.PageBar.Next: Next &\#xe280ba;
 * - Wt.WAbstractItemView.PageBar.Last: Last &\#xc2bb;
 *
 * \ingroup modelview
 */
class WT_API WAbstractItemView : public WCompositeWidget
{
public:
  /*! \brief Enumeration that specifies the user action that triggers editing.
   *
   * \sa setEditTriggers(), edit()
   */
  enum EditTrigger {
    NoEditTrigger = 0x0,  //!< Do not allow user to initiate editing
    SingleClicked = 0x1,  //!< Edit an item when clicked
    DoubleClicked = 0x2,  //!< Edit an item when double clicked
    SelectedClicked = 0x4 //!< Edit a selected item that is clicked again
  };

  /*! \brief Enumeration that specifies editing options.
   *
   * \sa setEditOptions()
   */
  enum EditOption {
    SingleEditor = 0x1,    //!< Never show more than one active editor
    MultipleEditors = 0x2, //!< Allow multiple editors at the same time
    SaveWhenClosed = 0x4,  //!< Always save the current edit value when closing
    LeaveEditorsOpen = 0x8 //!< Editors can only be closed using closeEditor()
  };

  /*! \brief Enumeration that specifies a scrolling option.
   *
   * \sa scrollTo()
   */
  enum ScrollHint {
    EnsureVisible,         //!< Scrolls minimally to make it visible
    PositionAtTop,         //!< Positions the item at the top of the viewport
    PositionAtBottom,      //!< Positions the item at the bottom of the viewport
    PositionAtCenter       //!< Positions the item at the center of the viewport
  };

  virtual ~WAbstractItemView();

  /*! \brief Sets the model.
   *
   * The View will display data of the given \p model and changes in
   * the model are reflected by the View.
   *
   * The initial model is \c 0.
   *
   * \if cpp
   * Ownership of the model is not transferred (and thus the
   * previously set model is not deleted). A model may not be deleted as
   * long as a view exists for it.
   * \endif
   *
   * \sa setRootIndex()
   */
  virtual void setModel(WAbstractItemModel *model);

  /*! \brief Returns the model.
   *
   * \sa setModel()
   */
  WAbstractItemModel *model() const { return model_; }

  /*! \brief Sets the root index.
   *
   * The root index is the model index that is considered the root
   * node. This node itself is not rendered, but its children are.
   *
   * \if cpp
   * The default value is an invalid model index, corresponding to the model's
   * root node.
   * \endif
   * \if java
   * The default value is \c null, corresponding to the model's root node.
   * \endif
   *
   * \sa setModel()
   */
  void setRootIndex(const WModelIndex& rootIndex);

  /*! \brief Returns the root index.
   *
   * \sa setRootIndex()
   */
  const WModelIndex& rootIndex() const { return rootIndex_; }

  /*! \brief Sets the default item delegate.
   *
   * The previous delegate is not deleted. This item delegate is for
   * all columns for which no specific item delegate is set.
   *
   * The default item delegate is a WItemDelegate.
   *
   * \sa setItemDelegateForColumn()
   */
  void setItemDelegate(WAbstractItemDelegate *delegate);

  /*! \brief Returns the default item delegate.
   *
   * \sa setItemDelegate()
   */
  WAbstractItemDelegate *itemDelegate() const { return itemDelegate_; }

  /*! \brief Sets the delegate for a column.
   *
   * A delegate previously set (if any) is not deleted.
   *
   * \sa setItemDelegate()
   */
  void setItemDelegateForColumn(int column, WAbstractItemDelegate *delegate);

  /*! \brief Returns the delegate that was set for a column.
   *
   * Returns \c 0 if no delegate was set for the column.
   *
   * \sa setItemDelegateForColumn()
   */
  WAbstractItemDelegate *itemDelegateForColumn(int column) const;

  /*! \brief Returns the delegate for rendering an item.
   *
   * \sa setItemDelegateForColumn(), setItemDelegate()
   */
  WAbstractItemDelegate *itemDelegate(const WModelIndex& index) const;

  /*! \brief Returns the delegate for a column.
   *
   * Returns either the delegate that was set for the column, or the default
   * delegate.
   */
  WAbstractItemDelegate *itemDelegate(int column) const;

  /*! \brief Returns the widget that renders an item.
   *
   * This returns the widget that renders the given item. This may return 0
   * if the item is currently not rendered.
   *
   * This widget has been created by an item delegate, and usually an item
   * delegate is involved when updating it.
   */
  virtual WWidget *itemWidget(const WModelIndex& index) const = 0;

  /*! \brief Sets the header item delegate.
   *
   * This item delegate is used for rendering items in the header.
   *
   * The previous delegate is not deleted. This item delegate is for
   * all columns for which no specific item delegate is set.
   *
   * The default item delegate is a WItemDelegate.
   */
  void setHeaderItemDelegate(WAbstractItemDelegate *delegate);

  /*! \brief Returns the header item delegate.
   *
   * \sa setHeaderItemDelegate()
   */
  WAbstractItemDelegate *headerItemDelegate() const;

  /*! \brief Sets the content alignment for a column.
   *
   * The default value is Wt::AlignLeft.
   *
   * \sa setHeaderAlignment()
   */
  virtual void setColumnAlignment(int column, AlignmentFlag alignment);

  /*! \brief Returns the content alignment for a column.
   *
   * \sa setColumnAlignment()
   */
  virtual AlignmentFlag columnAlignment(int column) const;

  /*! \brief Sets the header alignment for a column.
   *
   * The default alignemnt is horizontally left, and vertically centered.
   * (Wt::AlignLeft | Wt::AlignMiddle).
   *
   * Valid options for horizontal alignment are Wt::AlignLeft,
   * Wt::AlignCenter or Wt::AlignRight.
   *
   * Valid options for vertical alignment are Wt::AlignMiddle or
   * Wt::AlignTop. In the latter case, other contents may be added
   * below the label in createExtraHeaderWidget().
   *
   * \sa setColumnAlignment()
   */
  virtual void setHeaderAlignment(int column, WFlags<AlignmentFlag> alignment);

  /*! \brief Returns the horizontal header alignment for a column
   *         (<b>deprecated</b>).
   *
   * \sa setHeaderAlignment()
   *
   * \deprecated use horizontalHeaderAlignment() instead.
   */
  AlignmentFlag headerAlignment(int column) const;

  /*! \brief Returns the horizontal header alignment for a column.
   *
   * \sa setHeaderAlignment()
   */
  AlignmentFlag horizontalHeaderAlignment(int column) const;

  /*! \brief Returns the vertical header alignment for a column.
   *
   * \sa setHeaderAlignment()
   */
  AlignmentFlag verticalHeaderAlignment(int column) const;

  /*! \brief Configures header text wrapping.
   *
   * This setting only affects a multiline header, and the default
   * value is \c true. When set to \c false, the header itself will not
   * wrap (as with a vertically centered header), and thus extra widgets
   * will not shift down when there is a long header label.
   */
  void setHeaderWordWrap(int column, bool enabled);

  bool headerWordWrap(int column) const;

  /*! \brief Sets if alternating row colors are to be used.
   *
   * Configure whether rows get alternating background colors, defined by the
   * current CSS theme.
   *
   * The default value is \c false.
   */
  virtual void setAlternatingRowColors(bool enable);

  /*! \brief Returns whether alternating row colors are used.
   *
   * When enabled, rows are displayed in alternating row colors, according
   * to the current theme's definition.
   *
   * \sa setAlternatingRowColors()
   */
  virtual bool alternatingRowColors() const { return alternatingRowColors_; }
  
  /*! \brief Sorts the data according to a column.
   *
   * Sorts the data according to data in column \p column and sort
   * order \p order.
   *
   * The default sorting column is -1: the model is unsorted.
   *
   * \sa WAbstractItemModel::sort()
   */
  void sortByColumn(int column, SortOrder order);

  /*! \brief Returns the current sorting columm.
   *
   * \sa sortByColumn(), sortOrder()
   */
  int sortColumn() const;

  /*! \brief Returns the current sorting order.
   *
   * \sa sortByColumn(), sortColumn()
   */
  SortOrder sortOrder() const;

  /*! \brief Enables or disables sorting for all columns.
   *
   * Enable or disable sorting by the user on all columns.
   *
   * Sorting is enabled by default.
   *
   * \sa WAbstractItemModel::sort()
   */
  void setSortingEnabled(bool enabled);

  /*! \brief Enables or disables sorting for a single column.
   *
   * Enable or disable sorting by the user for a specific column.
   *
   * Sorting is enabled by default.
   *
   * \sa WAbstractItemModel::sort()
   */
  void setSortingEnabled(int column, bool enabled);

  /*! \brief Returns whether sorting is enabled.
   *
   * \sa setSortingEnabled()
   */
  bool isSortingEnabled() const { return sorting_; }

  /*! \brief Returns whether sorting is enabled for a single column.
   *
   * \sa setSortingEnabled()
   */
  bool isSortingEnabled(int column) const;

  /*! \brief Enables interactive column resizing
   *
   * Enable or disable column resize handles for interactive resizing of
   * the columns.
   *
   * Column resizing is enabled by default when JavaScript is available.
   *
   * \sa setColumnResizeEnabled()
   */
  void setColumnResizeEnabled(bool enabled);

  /*! \brief Returns whether column resizing is enabled.
   *
   * \sa setColumnResizeEnabled()
   */
  bool isColumnResizeEnabled() const { return columnResize_; }

  /*! \brief Changes the selection behaviour.
   *
   * The selection behavior indicates whether whole rows or individual
   * items can be selected. It is a property of the selectionModel().
   *
   * By default, selection operates on rows (\link Wt::SelectRows
   * SelectRows\endlink), in which case model indexes will always be
   * in the first column (column \c 0).
   *
   * Alternatively, you can allow selection for individual items
   * (\link Wt::SelectItems SelectItems\endlink).
   *
   * \sa WItemSelectionModel::setSelectionBehavior(), setSelectionMode()
   */
  void setSelectionBehavior(SelectionBehavior behavior);

  /*! \brief Returns the selection behaviour.
   *
   * \sa setSelectionBehavior()
   */
  SelectionBehavior selectionBehavior() const;

  /*! \brief Sets the selection mode.
   *
   * By default selection is disabled (\link Wt::NoSelection
   * NoSelection \endlink).
   *
   * \sa setSelectionBehavior()
   */
  void setSelectionMode(SelectionMode mode);

  /*! \brief Returns the selection mode.
   *
   * \sa setSelectionMode()
   */
  SelectionMode selectionMode() const { return selectionMode_; }

  /*! \brief Returns the selection model.
   *
   * The selection model keeps track of the currently selected items.
   */
  WItemSelectionModel *selectionModel() const { return selectionModel_; }

  /*! \brief Sets the selected items
   *
   * Replaces the current selection with \p indexes.
   * 
   * When selection operates on rows (\link Wt::SelectRows SelectRows\endlink), 
   * it is sufficient to pass the first element in a row (column \c 0 )
   * to select the entire row.
   *
   * \sa select(), selectionModel()
   */
  void setSelectedIndexes(const WModelIndexSet& indexes);

  /*! \brief Selects a single item.
   *
   * \sa setSelectedIndexes(), selectionModel()
   */
  void select(const WModelIndex& index, SelectionFlag option = Select);

  /*! \brief Returns wheter an item is selected.
   *
   * When selection operates on rows (\link Wt::SelectRows SelectRows\endlink),
   * this method returns true for each element in a selected row.
   *
   * This is a convenience method for:
   * \code
   * selectionModel()->isSelected(index)
   * \endcode
   *
   * \sa selectedIndexes(), select(), selectionModel()
   */
  bool isSelected(const WModelIndex& index) const;

  /*! \brief Returns the set of selected items.
   *
   * The model indexes are returned as a set, topologically ordered (in
   * the order they appear in the view).
   *
   * When selection operates on rows (\link Wt::SelectRows SelectRows\endlink),
   * this method only returns the model index of first column's element of the 
   * selected rows.
   *
   * This is a convenience method for:
   * \code
   * selectionModel()->selectedIndexes()
   * \endcode
   *
   * \sa setSelectedIndexes()
   */
  WModelIndexSet selectedIndexes() const;
  
  /*! \brief Enables the selection to be dragged (drag & drop).
   *
   * To enable dragging of the selection, you first need to enable
   * selection using setSelectionMode().
   *
   * Whether an individual item may be dragged is controlled by the
   * item's \link Wt::ItemIsDragEnabled ItemIsDragEnabled \endlink
   * flag. The selection can be dragged only if all items currently
   * selected can be dragged.
   *
   * \sa setDropsEnabled() 
   */
  void setDragEnabled(bool enable);

  /*! \brief Enables drop operations (drag & drop).
   *
   * When drop is enabled, the tree view will indicate that something
   * may be dropped when the mime-type of the dragged object is
   * compatible with one of the model's accepted drop mime-types (see
   * WAbstractItemModel::acceptDropMimeTypes()) or this widget's
   * accepted drop mime-types (see WWidget::acceptDrops()), and the
   * target item has drop enabled (which is controlled by the item's
   * \link Wt::ItemIsDropEnabled ItemIsDropEnabled \endlink flag).
   *
   * Drop events must be handled in dropEvent().
   *
   * \sa setDragEnabled(), dropEvent()
   */
  void setDropsEnabled(bool enable);

  /*! \brief Sets the row height.
   *
   * The view renders all rows with a same height. This method
   * configures this row height.
   *
   * The default value is 20 pixels.
   *
   * \note The height must be specified in WLength::Pixel units.
   *
   * \sa setColumnWidth()
   */
  virtual void setRowHeight(const WLength& rowHeight);

  /*! \brief Returns the row height.
   */
  const WLength& rowHeight() const { return rowHeight_; }

  /*! \brief Sets the column width.
   *
   * The default column width is 150 pixels.
   *
   * \note The actual space occupied by each column is the column width
   *       augmented by 7 pixels for internal padding and a border.
   */
  virtual void setColumnWidth(int column, const WLength& width) = 0;
 
  /*! \brief Returns the column width.
   *
   * \sa setColumnWidth()
   */
  WLength columnWidth(int column) const;

  /*! \brief Changes the visibility of a column.
   *
   * \sa isColumnHidden()
   */
  virtual void setColumnHidden(int column, bool hide);

  /*! \brief Returns if a column is hidden.
   *
   * \sa setColumnHidden()
   */
  bool isColumnHidden(int column) const;

  /*! \brief Hides a column.
   *
   * \sa showColumn(), setColumnHidden()
   */
  void hideColumn(int column);

  /*! \brief Shows a column.
   *
   * \sa hideColumn(), setColumnHidden()
   */
  void showColumn(int column);

  /*! \brief Sets the column border color
   *
   * The default border color is defined by the CSS theme. 
   */
  virtual void setColumnBorder(const WColor& color) = 0;

  /*! \brief Sets the header height.
   *
   * The default value is 20 pixels.
   */
  virtual void setHeaderHeight(const WLength& height);

  /*! \brief Sets the header height, and vertical alignment (<b>deprecated</b>). 
   * By default, the header text is a single line that is centered vertically.
   *
   * Along with setting the header height, this also configures vertical
   * aligment and header label word wrapping. This has been deprecated in
   * favour of the more fine grained control using setHeaderAlignment() and
   * setHeaderWordWrap().
   *
   * When \p multiLine is \c false, the header alignment is set to
   * Wt::AlignMiddle. When \p multiLine is \c true, the header
   * alignment is set to Wt::AlignTop and header word wrap is set to
   * \c true.
   *
   * \deprecated use setHeaderHeight(), setHeaderAlignment()
   *             and setHeaderWordWrap() instead.
   */
  void setHeaderHeight(const WLength& height, bool multiLine);

  /*! \brief Returns the header height.
   *
   * \sa setHeaderHeight()
   */
  const WLength& headerHeight() const { return headerLineHeight_; } ;

  /*! \brief Returns the number of pages.
   *
   * When Ajax/JavaScript is not available, the view will use a paging
   * navigation bar to allow scrolling through the data. This returns the
   * number of pages currently shown.
   *
   * \sa createPageNavigationBar(), pageChanged()
   */
  virtual int pageCount() const = 0;

  /*! \brief Returns the page size.
   *
   * When Ajax/JavaScript is not available, the view will use a paging
   * navigation bar to allow scrolling through the data. This returns the
   * number of items per page.
   *
   * \sa createPageNavigationBar(), pageChanged()
   */
  virtual int pageSize() const = 0;

  /*! \brief Returns the current page.
   *
   * When Ajax/JavaScript is not available, the view will use a paging
   * navigation bar to allow scrolling through the data. This returns the
   * current page (between 0 and pageCount() - 1).
   *
   * \sa createPageNavigationBar(), pageChanged()
   */
  virtual int currentPage() const = 0;

  /*! \brief Sets the current page.
   *
   * When Ajax/JavaScript is not available, the view will use a paging
   * navigation bar to allow scrolling through the data. This method can
   * be used to change the current page.
   *
   * \sa createPageNavigationBar(), pageChanged()
   */
  virtual void setCurrentPage(int page) = 0;

  /*! \brief Scrolls the view to an item.
   *
   * Scrolls the view to ensure that the item which represents the
   * provided \p index is visible. A \p hint may indicate how the item
   * should appear in the viewport (if possible).
   *
   * \note Currently only implemented to scroll to the correct row, not
   *       taking into account the column.
   */
  virtual void scrollTo(const WModelIndex& index,
			ScrollHint hint = EnsureVisible) = 0;

  /*! \brief Configures what actions should trigger editing.
   *
   * The default value is DoubleClicked.
   *
   * \sa edit()
   */
  void setEditTriggers(WFlags<EditTrigger> editTriggers);

  /*! \brief Returns the editing triggers.
   *
   * \sa setEditTriggers()
   */
  WFlags<EditTrigger> editTriggers() const { return editTriggers_; }

  /*! \brief Configures editing options.
   *
   * The default value is SingleEditor;
   */
  void setEditOptions(WFlags<EditOption> options);

  /*! \brief Returns the editing options.
   *
   * \sa setEditOptions()
   */
  WFlags<EditOption> editOptions() const { return editOptions_; }

  /*! \brief Opens an editor for the given index.
   *
   * Unless multiple editors are enabled, any other open editor is closed
   * first.
   *
   * \sa setEditTriggers(), setEditOptions(), closeEditor()
   */
  void edit(const WModelIndex& index);

  /*! \brief Closes the editor for the given index.
   *
   * If \p saveData is true, then the currently edited value is saved first
   * to the model.
   *
   * \sa edit()
   */
  void closeEditor(const WModelIndex& index, bool saveData = true);

  /*! \brief Closes all open editors.
   *
   * If \p saveData is true, then the currently edited values are saved
   * to the model before closing the editor.
   *
   * \sa closeEditor()
   */
  void closeEditors(bool saveData = true);

  /*! \brief Validates the editor for the given index.
   * 
   * Validation is done by invoking WAbstractItemDelegate::validate().
   */
  WValidator::State validateEditor(const WModelIndex& index);

  /*! \brief Validates all editors.
   * 
   * \sa validateEditor().
   */
  WValidator::State validateEditors();

  /*! \brief Returns whether an editor is open for a given index.
   *
   * \sa edit()
   */
  bool isEditing(const WModelIndex& index) const;
  
  /*! \brief Returns whether an editor's state is valid.
   */
  bool isValid(const WModelIndex& index) const;

  bool isEditing() const;

  /*! \brief %Signal emitted when clicked.
   *
   * When the event happened over an item, the first argument
   * indicates the item that was clicked on.

   * \sa doubleClicked()
   */
  Signal<WModelIndex, WMouseEvent>& clicked() { return clicked_; }

  /*! \brief %Signal emitted when double clicked.
   *
   * When the event happened over an item, the first argument
   * indicates the item that was double clicked on.
   *
   * \sa clicked()
   */
  Signal<WModelIndex, WMouseEvent>& doubleClicked() { return doubleClicked_; }

  /*! \brief %Signal emitted when a mouse button is pressed down.
   *
   * When the event happened over an item, the first argument
   * indicates the item where the mouse went down.
   *
   * \sa mouseWentUp()
   */
  Signal<WModelIndex, WMouseEvent>& mouseWentDown() { return mouseWentDown_; }

  /*! \brief %Signal emitted when the mouse button is released.
   *
   * When the event happened over an item, the first argument
   * indicates the item where the mouse went up.
   *
   * \sa mouseWentDown()
   */
  Signal<WModelIndex, WMouseEvent>& mouseWentUp() { return mouseWentUp_; }

  /*! \brief %Signal emitted when the selection is changed.
   *
   * \sa select(), setSelectionMode(), setSelectionBehavior()
   */
  Signal<>& selectionChanged() { return selectionChanged_; }

  /*! \brief %Signal emitted when page information was updated.
   *
   * When Ajax/JavaScript is not available, the view will use a paging
   * navigation bar to allow scrolling through the data. This signal
   * is emitted when page-related information changed (e.g. the
   * current page was changed, or the number of rows was changed).
   *
   * \sa createPageNavigationBar()
   */
  Signal<>& pageChanged() { return pageChanged_; }

  /*! \brief Returns the signal emitted when a column is resized by the user.
   *
   * The arguments of the signal are: the column index and the new
   * width of the column.
   */
  Signal<int, WLength>& columnResized() { return columnResized_; }

  /*! \brief Configures the number of columns that are used as row
   *         headers.
   *
   * An item view does not use the vertical header data from the model
   * in any way, but instead you can configure data in the first
   * column(s) to be used as a row headers.
   *
   * These columns will not scroll horizontally together with the rest
   * of the model.
   *
   * The default value is 0.
   *
   * \note Currently, this property must be set before any other settings of
   *       the view and only a value of 0 or 1 is supported.
   */
  virtual void setRowHeaderCount(int count);

  /*! \brief Returns the number of columns that are used as row headers.
   *
   * \sa setRowHeaderCount()
   */
  int rowHeaderCount() const { return rowHeaderCount_; }

  /*! \brief Configures whether horizontal scrolling includes the first column
   *         (<b>deprecated</b>)
   *
   * To display a model with many columns, this option allows you to
   * keep the first column fixed while scrolling through the other
   * columns of the model.
   *
   * The default value is \c false.
   *
   * \deprecated Use setRowHeaderCount() instead.
   */
  void setColumn1Fixed(bool enable);

  /*! \brief Returns whether horizontal scrolling includes the first column.
   *         (<b>deprecated</b>)
   *
   * \sa setColumn1Fixed()
   *
   * \deprecated Use rowHeaderCount() instead.
   */
  bool column1Fixed() const { return rowHeaderCount_ == 1; }

protected:
  /*! \brief Creates a new item view.
   */
  WAbstractItemView(WContainerWidget *parent = 0);

  /*! \brief Handles a drop event (drag & drop).
   *
   * The \p event object contains details about the drop
   * operation, identifying the source (which provides the data) and
   * the mime-type of the data. The drop was received on the
   * \p target item.
   *
   * The drop event can be handled either by the view itself, or by
   * the model. The default implementation checks if the mime-type is
   * accepted by the model, and if so passes the drop event to the
   * model. If the source is the view's own selection model, then the
   * drop event will be handled as a \link Wt::MoveAction
   * MoveAction\endlink, otherwise the drop event will be handled as a
   * \link Wt::CopyAction CopyAction\endlink.
   *
   * \sa WAbstractItemModel::dropEvent()
   */
  virtual void dropEvent(const WDropEvent& event, const WModelIndex& target);

  /*! \brief Create an extra widget in the header.
   *
   * You may reimplement this method to provide an extra widget to be placed
   * below the header label. The extra widget will be visible only if
   * a multi-line header is configured using setHeaderHeight().
   *
   * The widget is created only once, but this method may be called
   * repeatedly for a column for which prior calls returned \c 0
   * (i.e. each time the header is rerendered).
   *
   * The default implementation returns \c 0.
   *
   * \sa setHeaderHeight(), extraHeaderWidget()
   */
  virtual WWidget *createExtraHeaderWidget(int column);

  /*! \brief Returns the extra header widget.
   *
   * Returns the widget previously created using createExtraHeaderWidget()
   *
   * \sa createExtraHeaderWidget()
   */
  WWidget *extraHeaderWidget(int column);

  /*! \brief Returns a page navigation widget.
   *
   * When Ajax/JavaScript is not available, the view will use a paging
   * navigation bar to allow scrolling through the data, created by this
   * method. The default implementation displays a simple navigation bar
   * with (First, Prevous, Next, Last) buttons and a page counter.
   *
   * You may want to reimplement this method to provide a custom page
   * navigation bar. You can use the currentPage(), pageCount(), and
   * setCurrentPage() methods to set or get the page information, and
   * listen to the pageChanged() signal to react to changes.
   */
  virtual WWidget *createPageNavigationBar();

protected:
  struct ColumnInfo {
    WCssTemplateRule *styleRule;
    int id;
    SortOrder sortOrder;
    AlignmentFlag alignment;
    AlignmentFlag headerHAlignment, headerVAlignment;
    bool headerWordWrap;
    WLength width;
    WWidget *extraHeaderWidget;
    bool sorting, hidden;
    WAbstractItemDelegate *itemDelegate_;

    std::string styleClass() const;

    ColumnInfo(const WAbstractItemView *view, int id);
  };

  enum RenderState {
    RenderOk = 0,
    NeedAdjustViewPort = 1,
    NeedRerenderData = 2,
    NeedRerenderHeader = 3,
    NeedRerender = 4
  };

  WContainerWidget *impl_;
  RenderState renderState_;
  std::vector<boost::signals::connection> modelConnections_;

  WSignalMapper<WModelIndex, WMouseEvent> *clickedMapper_;

  mutable std::vector<ColumnInfo> columns_;
  int currentSortColumn_;

  bool dragEnabled_, dropsEnabled_;
  WWidget *dragWidget_;

  virtual void scheduleRerender(RenderState what);

  virtual void modelDataChanged(const WModelIndex& topLeft,
				const WModelIndex& bottomRight) = 0;
  virtual void modelLayoutAboutToBeChanged();
  virtual void modelLayoutChanged();
  void modelHeaderDataChanged(Orientation orientation, int start, int end);
  void modelReset();

  ColumnInfo& columnInfo(int column) const;
  int columnById(int columnid) const;

  int columnCount() const;
  int visibleColumnCount() const;
  int visibleColumnIndex(int modelColumn) const;
  int modelColumnIndex(int visibleColumn) const;

  virtual ColumnInfo createColumnInfo(int column) const;

  void saveExtraHeaderWidgets();
  WWidget *createHeaderWidget(WApplication *app, int column);
  WText *headerTextWidget(int column);

  /*! \brief Handles a click event.
   *
   * This processes the event for internal purposes (such as
   * selection or editing) and emits the clicked() signal.
   *
   * You may want to override this signal to override the built-in
   * selection or editing behaviour.
   */
  virtual void handleClick(const WModelIndex& index, const WMouseEvent& event);

  /*! \brief Handles a double click event.
   *
   * This processes the event for internal purposes (such as
   * editing) and emits the doubleClicked() signal.
   *
   * You may want to override this signal to override the built-in
   * editing behaviour.
   */
  virtual void handleDoubleClick(const WModelIndex& index,
				 const WMouseEvent& event);

  /*! \brief Handles a mouse down event.
   *
   * This emits the mouseWentDown() signal.
   */
  virtual void handleMouseDown(const WModelIndex& index,
			       const WMouseEvent& event);

  /*! \brief Handles a mouse up event.
   *
   * This emits the mouseWentUp() signal.
   */
  virtual void handleMouseUp(const WModelIndex& index,
			     const WMouseEvent& event);

  virtual bool internalSelect(const WModelIndex& index, SelectionFlag option);

  void setEditState(const WModelIndex& index, const boost::any& editState);
  boost::any editState(const WModelIndex& index) const;
  bool hasEditFocus(const WModelIndex& index) const;

  void setEditorWidget(const WModelIndex& index, WWidget *editor);

  void bindObjJS(JSlot& slot, const std::string& jsMethod);
  void connectObjJS(EventSignalBase& s, const std::string& jsMethod);
  bool shiftEditorRows(const WModelIndex& parent, int start, int count,
		       bool persistWhenShifted);
  bool shiftEditorColumns(const WModelIndex& parent, int start, int count,
			  bool persistWhenShifted);
  void persistEditor(const WModelIndex& index);

private:
  struct Editor {
    Editor() :
      widget(0),
      stateSaved(false),
      valid(false) {
      editState = boost::any();
    }
    WWidget *widget;
    boost::any editState;
    bool stateSaved;
    bool valid;
  };

  WAbstractItemModel *model_, *headerModel_;
  WModelIndex rootIndex_;
  WAbstractItemDelegate *itemDelegate_;
  WAbstractItemDelegate *headerItemDelegate_;
  WItemSelectionModel *selectionModel_;
  WLength rowHeight_, headerLineHeight_;
  SelectionMode selectionMode_;
  bool sorting_, columnResize_;
  AlignmentFlag defaultHeaderVAlignment_;
  bool defaultHeaderWordWrap_;
  int rowHeaderCount_;
  WString computedDragMimeType_;

  JSignal<int, int>    columnWidthChanged_;
  Signal<int, WLength> columnResized_;

  WCssTemplateRule *headerHeightRule_;

  mutable int nextColumnId_;
  
  WSignalMapper<int>
    *clickedForSortMapper_, *clickedForExpandMapper_,
    *clickedForCollapseMapper_;

  bool alternatingRowColors_;

  JSlot resizeHandleMDownJS_;

  typedef std::map<WModelIndex, Editor> EditorMap;
  EditorMap editedItems_;

  Signal<WModelIndex, WMouseEvent> clicked_;
  Signal<WModelIndex, WMouseEvent> doubleClicked_;
  Signal<WModelIndex, WMouseEvent> mouseWentDown_;
  Signal<WModelIndex, WMouseEvent> mouseWentUp_;
  Signal<> selectionChanged_;
  Signal<> pageChanged_;

  WFlags<EditTrigger> editTriggers_;
  WFlags<EditOption> editOptions_;

  void closeEditorWidget(WWidget *editor, bool saveData);
  void saveEditedValue(const WModelIndex& index, Editor& editor);
  void persistEditor(const WModelIndex& index, Editor& editor);

  virtual WWidget *headerWidget(int column, bool contentsOnly = true) = 0;
  virtual WText *headerSortIconWidget(int column);

  void selectionHandleClick(const WModelIndex& index,
			    WFlags<KeyboardModifier> modifiers);
  void clearSelection();
  void extendSelection(const WModelIndex& index);
  virtual void selectRange(const WModelIndex& first, const WModelIndex& last)
    = 0;

  void checkDragSelection();
  void configureModelDragDrop();

  void toggleSortColumn(int columnid);
  void updateColumnWidth(int columnId, int width);

  virtual WContainerWidget* headerContainer() = 0;

  int headerLevel(int column) const;
  int headerLevelCount() const;

  void expandColumn(int columnid);
  void collapseColumn(int columnid);

  void initDragDrop();
};

W_DECLARE_OPERATORS_FOR_FLAGS(WAbstractItemView::EditOption);
W_DECLARE_OPERATORS_FOR_FLAGS(WAbstractItemView::EditTrigger);

}

#endif // WT_WABSTRACTITEMVIEW_H_
