/* Copyright(C) 1998-1999 The gtkmm Development Team
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <vector>

#include <gtkmm/enums.h>
#include <gtkmm/treemodel.h>
#include <gtkmm/treeiter.h>
#include <gtkmm/treepath.h>

_DEFS(gtkmm,gtk)
_PINCLUDE(glibmm/private/object_p.h)

namespace Gtk
{

class TreeView;
class TreeModel;
class TreePath;

/** Typedefed as Gtk::TreeView::Selection.
 * This is a helper object to manage the selection for a Gtk::TreeView widget.
 *
 * It is automatically created when a new Gtk::TreeView widget is created, and
 * cannot exist independently of this widget.  The primary reason this class
 * exists is for cleanliness of code and API. That is, there is no conceptual
 * reason all these methods could not be methods on the Gtk::TreeView widget
 * instead of a separate class.
 *
 * The Gtk::TreeSelection object can be obtained from a Gtk::TreeView by
 * calling Gtk::TreeView::get_selection().  It can be manipulated to check the
 * selection status of the tree, as well as to select and deselect individual
 * rows.  Selection is done completely view-side. As a result, multiple views
 * of the same model can have completely different selections.  Additionally,
 * you cannot change the selection of a row on the model that is not currently
 * displayed by the view without expanding its parents first.
 *
 * When monitoring the selection of a view, it's important to remember that the
 * "changed" signal is mostly a hint. That is, it may only emit one signal when
 * a range of rows is selected. Additionally, it may on occasion emit a
 * "changed" signal when nothing has happened.
 *
 * @ingroup TreeView
 */
class TreeSelection : public Glib::Object
{
   _CLASS_GOBJECT(TreeSelection, GtkTreeSelection, GTK_TREE_SELECTION, Glib::Object, GObject)
   _IGNORE(gtk_tree_selection_get_selected,
           gtk_tree_selection_get_selected_rows, gtk_tree_selection_count_selected_rows)
protected:


public:
  _WRAP_METHOD(void set_mode(SelectionMode type), gtk_tree_selection_set_mode)
  _WRAP_METHOD(SelectionMode get_mode() const, gtk_tree_selection_get_mode)

  /** For instance, bool on_select_function(const Glib::RefPtr<TreeModel>& model, const TreeModel::Path& path, bool path_currently_selected)
   * The select function should return true if the state of the node may be toggled,
   * and false if the state of the node should be left unchanged.
   */
  typedef sigc::slot<bool(const Glib::RefPtr<TreeModel>&, const TreeModel::Path&, bool)> SlotSelect;

  /** Sets the selection callback slot. If set, this function is called before any node is selected or unselected,
   * giving some control over which nodes are selected.
   *
   * The select function should return true if the state of the node may be toggled, and FALSE if the state of the node
   * should be left unchanged.
   *
   * @param slot The selection function.
   */
  void set_select_function(const SlotSelect& slot);
  _IGNORE(gtk_tree_selection_set_select_function, gtk_tree_selection_get_select_function)

  _IGNORE(gtk_tree_selection_get_user_data) // This would be our SignalProxy_Select* data.

  _WRAP_METHOD(TreeView* get_tree_view(), gtk_tree_selection_get_tree_view)
  _WRAP_METHOD(const TreeView* get_tree_view() const, gtk_tree_selection_get_tree_view, constversion)

  /** Shortcut for get_tree_view()->get_model().
   * @return The TreeModel associated with this TreeSelection.
   */
  Glib::RefPtr<TreeModel> get_model(); // convenience function, not in GTK+
  Glib::RefPtr<const TreeModel> get_model() const; // convenience function, not in GTK+

  //TODO: Add TreeModel::const_iterator get_selected() const, when we have a real const_iterator.

  /** Get the currently selected row.
   * @return The currently selected row.
   * @note
   * This method won't work if the selection mode is <tt>Gtk::SELECTION_MULTIPLE</tt>.
   * Use get_selected_rows() for multiple selections.
   */
  TreeModel::iterator get_selected();

  /** Get the currently selected row.
   * @return The currently selected row. Or end() if no rows were selected.
   * @retval model The current TreeModel.
   * @note
   * This method won't work if the selection mode is <tt>Gtk::SELECTION_MULTIPLE</tt>.
   * Use get_selected_rows() for multiple selections.
   */
  TreeModel::iterator get_selected(Glib::RefPtr<TreeModel>& model);

  /** Creates a list of paths of all selected rows.
   * Additionally, if you are planning on modifying the model after calling this function,
   * you may want to convert the returned list into a list of GtkTreeRowReferences.
   *
   * @returns a standard container containing a Gtk::Model::Path for each selected row.
   */
  std::vector<TreeModel::Path> get_selected_rows() const;

  /** Creates a list of paths of all selected rows.
   * Additionally, if you are planning on modifying the model after calling this function,
   * you may want to convert the returned list into a list of GtkTreeRowReferences.
   *
   * @retval model The current TreeModel.
   * @returns a standard container containing a Gtk::Model::Path for each selected row.
   */
  std::vector<TreeModel::Path> get_selected_rows(Glib::RefPtr<TreeModel>& model);

  _WRAP_METHOD(int count_selected_rows() const, gtk_tree_selection_count_selected_rows)

  //TODO: Consider deprecating these selected_foreach() methods, because get_selected_rows() should be enough.

  //TODO: SlotForEachIter should take a const_iterator, when we have a real const iterator.
  /** For example,
   * void on_foreach(const Gtk::TreeModel::iterator& iter);
   *
   * Note that you cannot modify the tree or selection from within the callback function.
   * As a result, get_selected_rows() might be more useful.
   */
  typedef sigc::slot<void(const TreeModel::iterator&)> SlotForeachIter;

  /** Calls a callback slot for each selected node.
   * Note that you cannot modify the tree or selection from within the callback function.
   * As a result, get_selected_rows() might be more useful.
   *
   * @param slot The function to call for each selected node.
   */
  void selected_foreach_iter(const SlotForeachIter& slot) const;

  /** For example,
   * void on_foreach(const Gtk::TreeModel::Path& path);
   *
   * Note that you cannot modify the tree or selection from within the callback function.
   * As a result, get_selected_rows() might be more useful.
   */
  typedef sigc::slot<void(const TreeModel::Path&)> SlotForeachPath;

  /** Calls a callback slot for each selected node.
   * Note that you cannot modify the tree or selection from within the callback function.
   * As a result, get_selected_rows() might be more useful.
   *
   * @param slot The function to call for each selected node.
   */
  void selected_foreach_path(const SlotForeachPath& slot) const;
  _IGNORE(gtk_tree_selection_selected_foreach)

  /** For example,
   * void on_foreach(const Gtk::TreeModel::Path& path, const Gtk::TreeModel::iterator& iter);
   *
   * Note that you cannot modify the tree or selection from within the callback function.
   * As a result, get_selected_rows() might be more useful.
   */
  typedef sigc::slot<void(const TreeModel::Path&, const TreeModel::iterator&)> SlotForeachPathAndIter;

  /** Calls a callback slot for each selected node.
   * Note that you cannot modify the tree or selection from within the callback function.
   * As a result, get_selected_rows() might be more useful.
   *
   * @param slot The function to call for each selected node.
   */
  void selected_foreach(const SlotForeachPathAndIter& slot) const;

  _WRAP_METHOD(void select(const TreeModel::Path& path), gtk_tree_selection_select_path)
  _WRAP_METHOD(void select(const TreeModel::iterator& iter), gtk_tree_selection_select_iter)
  _WRAP_METHOD(void select(const TreeModel::Row& row), gtk_tree_selection_select_iter)
  _WRAP_METHOD(void select(const TreeModel::Path& start_path, const TreeModel::Path& end_path), gtk_tree_selection_select_range)

  _WRAP_METHOD(void unselect(const TreeModel::Path& path), gtk_tree_selection_unselect_path)
  _WRAP_METHOD(void unselect(const TreeModel::Path& start_path, const TreeModel::Path& end_path), gtk_tree_selection_unselect_range)
  _WRAP_METHOD(void unselect(const TreeModel::iterator& iter), gtk_tree_selection_unselect_iter)

  _WRAP_METHOD(bool is_selected(const TreeModel::Path& path) const, gtk_tree_selection_path_is_selected)
  _WRAP_METHOD(bool is_selected(const TreeModel::iterator& iter) const, gtk_tree_selection_iter_is_selected)

  _WRAP_METHOD(void select_all(), gtk_tree_selection_select_all)
  _WRAP_METHOD(void unselect_all(), gtk_tree_selection_unselect_all)

  _WRAP_PROPERTY("mode", SelectionMode)

  _WRAP_SIGNAL(void changed(), "changed")
};

} // namespace Gtk

