Logo Search packages:      
Sourcecode: agave version File versions  Download package

customtreemodel.h

/*******************************************************************************
 *  PROJECT: customtreemodel
 *
 *  AUTHOR: Jonathon Jongsma
 *
 *  Copyright (c) 2006 Jonathon Jongsma
 *
 *  License:
 *    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; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    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., 59 Temple Place, Suite 330, 
 *    Boston, MA  02111-1307  USA
 *
 *******************************************************************************/
#ifndef __CUSTOMTREEMODEL_H
#define __CUSTOMTREEMODEL_H

#include <gtkmm/treemodel.h>
#include <gtkmm/treesortable.h>
#include <gtkmm/treedragdest.h>
#include <gtkmm/treedragsource.h>
#include <gtkmm/treepath.h>
#include <string>
#include <vector>
#include <iterator>

/** This is an attempt to make a generic custom tree model.  T should be a
 * container type such as std::vector or std::list (or something that
 * presents the same interface
 *
 * You must override the following functions
 * \li get_n_columns_vfunc(): to define the number of columns in the model
 * \li get_column_type_vfunc(): define the type of the specified column
 * \li get_value_vfunc(): get the value for the specified iter and column
 */
template <class T>
00047 class ContainerTreeModel: public Gtk::TreeModel,
                          public Gtk::TreeDragSource,
                          public Gtk::TreeDragDest,
                          public Gtk::TreeSortable
{
    public:
        typedef Gtk::TreeModel::iterator iterator;
        //static Glib::RefPtr<ContainerTreeModel> create(T& container);

    protected:
        ContainerTreeModel(T& container);
        virtual Gtk::TreeModelFlags get_flags_vfunc(void) const;
        //virtual int get_n_columns_vfunc(void) const;
        //virtual GType get_column_type_vfunc(int index) const;
        //virtual void get_value_vfunc(const Gtk::TreeModel::iterator& iter, int column, Glib::ValueBase& value) const;
        virtual bool iter_next_vfunc(const iterator& iter, iterator& iter_next) const;

        virtual bool iter_children_vfunc(const iterator& parent, iterator& iter) const;
        virtual bool iter_has_child_vfunc(const iterator& iter) const;
        virtual int iter_n_children_vfunc(const iterator& iter) const;
        virtual int iter_n_root_children_vfunc(void) const;
        virtual bool iter_nth_child_vfunc(const iterator& parent, int n, iterator& iter) const;
        virtual bool iter_nth_root_child_vfunc(int n, iterator& iter) const;
        virtual bool iter_parent_vfunc(const iterator& child, iterator& iter) const;
        virtual Gtk::TreeModel::Path get_path_vfunc(const iterator& iter) const;
        virtual bool get_iter_vfunc(const Gtk::TreeModel::Path& path, iterator& iter) const;
        virtual bool iter_is_valid(const iterator& iter) const;

        // virtual functions from Gtk::TreeDragSource
        virtual bool row_draggable_vfunc(const Gtk::TreeModel::Path& path) const;
        // virtual functions from Gtk::TreeDragDest
        virtual bool row_drop_possible_vfunc(const Gtk::TreeModel::Path& dest, const Gtk::SelectionData& selection_data) const;
        // virtual functions from Gtk::TreeSortable

        Gtk::TreeModel::ColumnRecord m_columnRecord;
        T* m_pContainer;
        int m_stamp;

        struct GlueItem
        {
            GlueItem(const typename T::iterator& i) : iter(i) { }
            typename T::iterator iter;
        };

        struct GlueList
        {
            typedef std::vector<GlueItem*> gluelist_t;
            ~GlueList()
            {
                for (typename gluelist_t::iterator iter = m_items.begin();
                        iter != m_items.end(); ++iter)
                {
                    delete *iter;
                }
            }
            gluelist_t m_items;
        };

        mutable GlueList m_glueList;
};

template <class T>
ContainerTreeModel<T>::ContainerTreeModel(T& container) :
    //Glib::ObjectBase(typeid(ContainerTreeModel< T >)), // register custom GType
    //Glib::Object(),  // the GType is actually registered here
    m_pContainer(&container),
    m_stamp(1)
{
    // some GType stuff to take care of -- I don't completely understand it
    // -- borrowed from the gtkmm custom treemodel example
    //GType gtype = G_OBJECT_TYPE(gobj());    // the custom GType created in the Object constructor
    //Gtk::TreeModel::add_interface(gtype);
}


template <class T>
Gtk::TreeModelFlags ContainerTreeModel<T>::get_flags_vfunc(void) const
{
    // no flags set
    return Gtk::TreeModelFlags(0);
}


template <class T>
bool ContainerTreeModel<T>::iter_next_vfunc(const iterator& iter, iterator& iter_next) const
{
    if (iter_is_valid(iter))
    {
        iter_next = iterator();
        iter_next.set_stamp(m_stamp);
        // abuse the user_data field of the underlying GtkTreeIter to store
        // a pointer to an iterator of the underlying container
        GlueItem* pItem = (GlueItem*) (iter.gobj()->user_data);
        typename T::iterator stl_iter = pItem->iter;
        // increment the underlying iterator
        ++stl_iter;
        // check whether the next iterator is between the beginning and end of
        // the container
        if (m_pContainer->begin() <= stl_iter && stl_iter < m_pContainer->end())
        {
            // store the next row number in the user_data field of the next
            // iterator
            GlueItem* pItem = new GlueItem(stl_iter);
            iter_next.gobj()->user_data = (void*) pItem;
            m_glueList.m_items.push_back(pItem);
            return true;    // next row ok
        }
        else
        {
            // row is beyond the size of the container, so we'll return
            // false when we drop out the end.
        }
    }
    else
    {
        iter_next = iterator(); // default iterator is invalid
    }

    return false;   // no next row
}


template <class T>
bool ContainerTreeModel<T>::iter_children_vfunc(const iterator& parent, iterator& iter) const
{
    // invalid by default.  This treemodel has no children so always return
    // an invalid iterator
    iter = iterator();
    return false;
}


template <class T>
bool ContainerTreeModel<T>::iter_has_child_vfunc(const iterator& iter) const
{
    // this treemodel has no children
    return false;
}


template <class T>
int ContainerTreeModel<T>::iter_n_children_vfunc(const iterator& iter) const
{
    // no children in this model
    return 0;
}


template <class T>
int ContainerTreeModel<T>::iter_n_root_children_vfunc(void) const
{
    // all rows are root children
    return m_pContainer->size();
}


template <class T>
bool ContainerTreeModel<T>::iter_nth_child_vfunc(const iterator& parent, int n, iterator& iter) const
{
    iter = iterator();  // invalid by default -- we have no children
    return false;
}


template <class T>
bool ContainerTreeModel<T>::iter_nth_root_child_vfunc(int n, iterator& iter) const
{
    iter = iterator();   // invalid by default.
    typename T::iterator stl_iter = m_pContainer->begin();
    std::advance(stl_iter, n);
    if (stl_iter < m_pContainer->end())
    {
        iter.set_stamp(m_stamp);
        // again, abuse the user_data field to store the row number
        GlueItem* pItem = new GlueItem(stl_iter);
        iter.gobj()->user_data = (void*) pItem;
        m_glueList.m_items.push_back(pItem);
        return true;
    }
    return false;
}


template <class T>
bool ContainerTreeModel<T>::iter_parent_vfunc(const iterator& child, iterator& iter) const
{
    // since there are no children in this model, there are also no parents
    iter = iterator();  // invalid by default
    return false;
}


template <class T>
Gtk::TreeModel::Path ContainerTreeModel<T>::get_path_vfunc(const iterator& iter) const
{
    Gtk::TreeModel::Path path;
    GlueItem* pItem = (GlueItem*) iter->gobj()->user_data;
    path.push_back(std::distance(m_pContainer->begin(),
                pItem->iter));
    return path;
}


template <class T>
bool ContainerTreeModel<T>::get_iter_vfunc(const Gtk::TreeModel::Path& path, iterator& iter) const
{
    iter = iterator();
    // path size must be exactly one.  if it's zero, there's no path, and if
    // it's greater than one, it's asking for a child node, which doesn't
    // exist in this model
    if (path.size() != 1)
    {
        return false;
    }
    else if (path[0] < static_cast<int>(m_pContainer->size()))
    {
        iter.set_stamp(m_stamp);
        // again, abuse the user_data field...
        typename T::iterator stl_iter = m_pContainer->begin();
        std::advance(stl_iter, path[0]);
        GlueItem* pItem = new GlueItem(stl_iter);
        iter.gobj()->user_data = (void*) pItem;
        return true;
    }
    return false;
}


template <class T>
bool ContainerTreeModel<T>::iter_is_valid(const iterator& iter) const
{
    if (iter.get_stamp() != m_stamp)
    {
        return false;
    }
    else
    {
        // TODO: not sure why we do this but it's done in the gtkmm
        // example...
        return Gtk::TreeModel::iter_is_valid(iter);
    }
}


template <class T>
bool ContainerTreeModel<T>::row_draggable_vfunc(const Gtk::TreeModel::Path& path) const
{
    Gtk::TreeIter iter;
    get_iter_vfunc(path, iter);
    if (iter_is_valid(iter))
    {
        return true;
    }
    return false;
}


template <class T>
bool ContainerTreeModel<T>::row_drop_possible_vfunc(const Gtk::TreeModel::Path&
        dest, const Gtk::SelectionData& selection_data) const
{
    Gtk::TreeIter iter;
    get_iter_vfunc(dest, iter);
    if (iter_is_valid(iter))
    {
        return true;
    }
    return false;
}

#endif // __CUSTOMTREEMODEL_H

Generated by  Doxygen 1.6.0   Back to index