Re: CList DnD/selection "format" broken?



On Thu, Oct 11, 2001 at 03:18:31PM -0500, Skip Montanaro wrote:
>     Havoc> It's actually a bit overcomplex for many uses, .... However if
>     Havoc> you are just making a simple list, clist is fine.
> 
> Which suggests to me that either clist shouldn't be deprecated or there
> should be a simpler way to use treeview/treeselection/liststore to create
> simple lists.  For all the power of the treeview stuff, most of the time I
> think people just need single-column lists.

It's not really that hard.  The biggest missing piece is a pointer array
collection that also exports a GtkTreeModel interface.  For example, see
the attached code.

-- 
Victory to the Divine Mother!!
  http://sahajayoga.org
/* GLIB - Library of useful routines for C programming
 * Copyright (C) 2001 Joshua Nathaniel Pritikin
 *
 * 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 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., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/* originally adapted from g_ptr_array */

#ifndef _G_PTRSET_H_
#define _G_PTRSET_H_

#include <glib-object.h>

typedef struct _GPtrSetClass GPtrSetClass;
typedef struct _GPtrSet  GPtrSet;

#define G_TYPE_PTR_SET		  (g_ptr_set_get_type())
#define G_PTR_SET(obj)		  (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_PTR_SET, GPtrSet))
#define G_IS_PTR_SET(obj)	  (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_PTR_SET))
#define G_PTR_SET_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_PTR_SET, GPtrSetClass))
#define G_PTR_SET_CLASS(klass)	  (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_PTR_SET, GPtrSetClass))
#define G_IS_PTR_SET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_PTR_SET))

struct _GPtrSetClass {
  GObjectClass parent_class;

  void (*delete_elem) (GPtrSet *set, gint index, gpointer data);
  void (*insert_elem) (GPtrSet *set, gint index);
};

struct _GPtrSet
{
  GObject           parent_instance;
  gpointer *        pdata;
  gint              len;
  gint              alloc;
  gconstpointer     compare_data;
  GCompareDataFunc  ptr_compare;
  GCompareDataFunc  key_compare;
  GDestroyNotify    destroy_func;
  guint             is_refcounted : 1;
};

#define    g_ptr_set_at(set,index)    ((set)->pdata)[(index)]
#define    g_ptr_set_len(set)         ((set)->len)

GType      g_ptr_set_get_type         (void);

GPtrSet *  g_ptr_set_new              (GCompareDataFunc  ptr_compare,
				       gconstpointer     compare_data);
void       g_ptr_set_set_key_compare  (GPtrSet *set,
				       GCompareDataFunc  key_compare);
void       g_ptr_set_free             (GPtrSet  *set);

gpointer   g_ptr_set_remove_index     (GPtrSet *  set,
				       gint       index);

gint       g_ptr_set_add_index        (GPtrSet *  set,
				       gint       index,
				       gpointer   data);

gboolean   g_ptr_set_lookup           (GPtrSet *       set,
				       gint *          ret,
				       gconstpointer   sample);
gboolean   g_ptr_set_lookup_ptr       (GPtrSet *       fset,
				       gint *          ret,
				       gconstpointer   sample);
gboolean   g_ptr_set_lookup_key       (GPtrSet *       set,
				       gint *          ret,
				       gconstpointer   sample_key);
gint       g_ptr_set_add              (GPtrSet *  set,
				       gpointer   data);
gboolean   g_ptr_set_try_remove       (GPtrSet *  fset,
				       gpointer   data);
void       g_ptr_set_remove           (GPtrSet *  set,
				       gpointer   data);

void       g_ptr_set_remove_all       (GPtrSet *ps);
void       g_ptr_set_sync_to_slist    (GPtrSet *ps, GSList *target);

#endif
/* GLIB - Library of useful routines for C programming
 * Copyright (C) 2001 Joshua Nathaniel Pritikin
 *
 * 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 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., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include <glib.h>
#include <string.h>
#include "gptrset.h"
#include "appmarshal.h"

enum {
  PROP_0,

  PROP_COMPARE_DATA,
  PROP_KEY_COMPARE,
  PROP_PTR_COMPARE,
  PROP_DESTROY_FUNC,
  PROP_IS_REFCOUNTED
};

enum {
  DELETE_ELEM,
  INSERT_ELEM,
  LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };


#define MIN_SET_SIZE  16

static void g_ptr_set_maybe_expand (GPtrSet *set,
				    gint     len);


static void
g_ptr_set_verify_sort (GPtrSet *set)
{
  if (!set->ptr_compare)
    return;

  if (set->len <= 1)
    return;

  for (gint xx=0; xx < set->len - 1; xx++)
    {
      if (set->ptr_compare (g_ptr_set_at (set, xx),
			    g_ptr_set_at (set, xx + 1),
			    (gpointer) set->compare_data) > 0)
	g_critical ("%d, %d are unsorted", xx, xx+1);
    }
}

static gint
g_nearest_pow (gint num)
{
  gint n = 1;

  while (n < num)
    n <<= 1;

  return n;
}

GPtrSet *
g_ptr_set_new (GCompareDataFunc  ptr_compare,
	       gconstpointer     compare_data)
{
  GPtrSet *set;

  set = G_PTR_SET (g_object_new (g_ptr_set_get_type(), 0));

  set->ptr_compare = ptr_compare;
  set->compare_data = compare_data;

  return set;
}

void
g_ptr_set_set_key_compare (GPtrSet *set, GCompareDataFunc key_compare)
{
  set->key_compare = key_compare;
}

static void
g_ptr_set_init (GPtrSet *set)
{
  /* zero fill is good */
}

// g_ptr_set_maybe_shrink XXX

static void
g_ptr_set_maybe_expand (GPtrSet *set,
			gint     len)
{
  if ((set->len + len) > set->alloc)
    {
#ifdef ENABLE_GC_FRIENDLY
      gint old_alloc = set->alloc;
#endif /* ENABLE_GC_FRIENDLY */
      set->alloc = g_nearest_pow (set->len + len);
      set->alloc = MAX (set->alloc, MIN_SET_SIZE);
      set->pdata = g_realloc (set->pdata, sizeof(gpointer) * set->alloc);
#ifdef ENABLE_GC_FRIENDLY
      for ( ; old_alloc < set->alloc; old_alloc++)
	set->pdata [old_alloc] = NULL;
#endif /* ENABLE_GC_FRIENDLY */
    }
}

static gboolean
_ptr_set_lookup (GPtrSet *         fset,
		 gint *            ret,
		 GCompareDataFunc  compare,
		 gconstpointer     sample)
{
  gint first, last;
  gint mid;
  gint midsize;
  gint cmp;
  gint tx;

  g_return_val_if_fail (compare, FALSE);

  if (!fset->len)
    {
      if (ret) *ret = 0;
      return FALSE;
    }

  first = 0;
  last = fset->len - 1;

  midsize = last - first;
  
  while (midsize > 1) {
    mid = first + midsize / 2;
    
    cmp = (*compare) (sample, g_ptr_set_at (fset, mid), (gpointer) fset->compare_data);
    
    if (cmp == 0)
      {
	// if there are multiple matches then scan for the first match
	while (mid > 0 &&
	       (*compare) (sample,
			   g_ptr_set_at (fset, mid - 1),
			   (gpointer) fset->compare_data) == 0)
	  --mid;

	if (ret) *ret = mid;
	return TRUE;
      }
    
    if (cmp < 0)
      last = mid-1;
    else
      first = mid+1;
    
    midsize = last - first;
  }

  for (tx = first; tx <= last; tx++)
    {
      cmp = (*compare) (sample, g_ptr_set_at (fset, tx), (gpointer) fset->compare_data);

      if (cmp < 0)
	{
	  if (ret) *ret = tx;
	  return FALSE;
	}
      if (cmp == 0)
	{
	  if (ret) *ret = tx;
	  return TRUE;
	}
    }

  if (ret) *ret = last+1;
  return FALSE;
}

gboolean
g_ptr_set_lookup (GPtrSet *       fset,
		  gint *          ret,
		  gconstpointer   sample)
{
  g_return_val_if_fail (fset, FALSE);

  return _ptr_set_lookup (fset, ret, fset->ptr_compare, sample);
}

gboolean
g_ptr_set_lookup_ptr (GPtrSet *       fset,
		      gint *          ret,
		      gconstpointer   sample)
{
  g_return_val_if_fail (fset, FALSE);

  gint at;
  if (!_ptr_set_lookup (fset, &at, fset->ptr_compare, sample))
    return FALSE;

  // cope with non-unique sets of unique pointers
  while (g_ptr_set_at (fset, at) != sample)
    {
      if (at < fset->len - 1 &&
	  (*fset->ptr_compare) (sample,
				g_ptr_set_at (fset, at+1),
				(gpointer) fset->compare_data) == 0)
	{ ++at; continue; }

      return FALSE;
    }

  if (ret)
    *ret = at;
  return TRUE;
}

gboolean
g_ptr_set_lookup_key (GPtrSet *      set,
		      gint *         ret,
		      gconstpointer  sample_key)
{
  g_return_val_if_fail (set, FALSE);

  return _ptr_set_lookup (set, ret, set->key_compare, sample_key);
}

gpointer
g_ptr_set_remove_index (GPtrSet* fset,
			gint    index)
{
  gpointer result;

  g_return_val_if_fail (fset, NULL);
  g_return_val_if_fail (0 <= index && index < fset->len, NULL);

  result = fset->pdata[index];
  
  if (index != fset->len - 1)
    g_memmove (fset->pdata + index, fset->pdata + index + 1, 
	       sizeof (gpointer) * (fset->len - index - 1));
  
  fset->len -= 1;

#ifdef ENABLE_GC_FRIENDLY  
  fset->pdata[fset->len] = NULL;
#endif /* ENABLE_GC_FRIENDLY */  

  g_ptr_set_verify_sort (fset);

  g_signal_emit (fset, signals[DELETE_ELEM], 0, index, result);

  if (fset->destroy_func)
    (*fset->destroy_func) (result);
  if (fset->is_refcounted)
    g_object_unref (result);

  return result;
}

gboolean
g_ptr_set_try_remove (GPtrSet* fset,
		      gpointer data)
{
  g_return_val_if_fail (fset, FALSE);
  g_return_val_if_fail (data, FALSE);

  // only remove first match
  gint at;
  if (!g_ptr_set_lookup_ptr (fset, &at, data))
    return FALSE;

  gpointer removed = g_ptr_set_remove_index (fset, at);
  g_assert (data == removed);

  return TRUE;
}

void
g_ptr_set_remove (GPtrSet* fset,
		  gpointer data)
{
  gboolean removed =
    g_ptr_set_try_remove (fset, data);
  g_assert (removed);
}

gint
g_ptr_set_add_index (GPtrSet *   set,
		     gint        index,
		     gpointer    data)
{
  g_return_val_if_fail (set, -1);
  g_return_val_if_fail (0 <= index && index <= set->len, -1);

  if (set->is_refcounted)
    g_object_ref (data);

  g_ptr_set_maybe_expand ((GPtrSet*) set, 1);

  g_memmove (&g_ptr_set_at (set, 1 + index), 
	     &g_ptr_set_at (set, index), 
	     sizeof(gpointer) * (set->len - index));

  g_ptr_set_at (set, index) = data;

  set->len += 1;

  g_ptr_set_verify_sort (set);

  g_signal_emit (set, signals[INSERT_ELEM], 0, index);

  return index;
}


gint
g_ptr_set_add (GPtrSet *   set,
	       gpointer    data)
{
  gint at;

  g_return_val_if_fail (set, -1);
  g_return_val_if_fail (data, -1);

  if (g_ptr_set_lookup (set, &at, data))
    ++at;  // reduce memmove

  return g_ptr_set_add_index (set, at, data);
}

void
g_ptr_set_remove_all (GPtrSet *ps)
{
  g_return_if_fail (ps);

  gint old_len = ps->len;
  gpointer *pdata = ps->pdata;

  ps->alloc = 0;
  ps->pdata = NULL;
  ps->len = 0;

  // For this emission, we always use the zeroth index
  // because downstream handlers don't know what we're doing.
  //
  for (gint xx=0; xx < old_len; xx++)
    g_signal_emit (ps, signals[DELETE_ELEM], 0, 0, pdata[xx]);

  if (ps->destroy_func)
    for (gint xx=0; xx < old_len; xx++)
      (*ps->destroy_func) (pdata[xx]);

  if (ps->is_refcounted)
    for (gint xx=0; xx < old_len; xx++)
      g_object_unref (pdata[xx]);

  g_free (pdata);
}

// Do the minimum number of adds/removes.
//
void g_ptr_set_sync_to_slist (GPtrSet *ps, GSList *target)
{
  gint xx=0;
  while (xx < g_ptr_set_len (ps))
    {
      gpointer data = g_ptr_set_at (ps, xx);
      if (!g_slist_find (target, data))
	g_ptr_set_remove (ps, data);
      else
	++xx;
    }
  for (GSList *elem = target; elem; elem = g_slist_next (elem))
    {
      gpointer data = elem->data;
      if (!g_ptr_set_lookup (ps, NULL, data))
	g_ptr_set_add (ps, data);
    }
}

static void
g_ptr_set_set_property (GObject      *object,
			guint         prop_id,
			const GValue *value,
			GParamSpec   *pspec)
{
  GPtrSet *set = G_PTR_SET (object);

  g_return_if_fail (set->len == 0);

  switch (prop_id) {
  case PROP_COMPARE_DATA:
    set->compare_data = g_value_get_pointer (value);
    break;

  case PROP_KEY_COMPARE:
    set->key_compare = g_value_get_pointer (value);
    break;

  case PROP_PTR_COMPARE:
    set->ptr_compare = g_value_get_pointer (value);
    break;

  case PROP_DESTROY_FUNC:
    set->destroy_func = g_value_get_pointer (value);
    break;

  case PROP_IS_REFCOUNTED:
    set->is_refcounted = g_value_get_boolean (value);
    break;

  default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    break;
  }
}

static void
g_ptr_set_get_property (GObject      *object,
			guint         prop_id,
			GValue       *value,
			GParamSpec   *pspec)
{
  GPtrSet *set = G_PTR_SET (object);

  switch (prop_id) {
  case PROP_COMPARE_DATA:
    g_value_set_pointer (value, (gpointer) set->compare_data);
    break;

  case PROP_KEY_COMPARE:
    g_value_set_pointer (value, set->key_compare);
    break;

  case PROP_PTR_COMPARE:
    g_value_set_pointer (value, set->ptr_compare);
    break;

  case PROP_DESTROY_FUNC:
    g_value_set_pointer (value, set->destroy_func);
    break;

  case PROP_IS_REFCOUNTED:
    g_value_set_boolean (value, set->is_refcounted);
    break;

  default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    break;
  }
}

static GObjectClass *parent_class;

static void
g_ptr_set_finalize (GObject *object)
{
  GPtrSet *set;
  gint xx;

  g_return_if_fail (G_IS_PTR_SET (object));

  set = G_PTR_SET (object);

  if (set->destroy_func)
    for (xx=0; xx < set->len; xx++)
      (* set->destroy_func) (g_ptr_set_at (set, xx));

  if (set->is_refcounted)
    for (xx=0; xx < set->len; xx++)
      g_object_unref (g_ptr_set_at (set, xx));

  g_free (set->pdata);
  set->pdata = NULL;

  (* G_OBJECT_CLASS(parent_class)->finalize) (object);
}

static void
g_ptr_set_class_init(GPtrSetClass *klass) {
  GObjectClass *object_class = G_OBJECT_CLASS(klass);

  parent_class = g_type_class_peek_parent (klass);

  object_class->set_property = g_ptr_set_set_property;
  object_class->get_property = g_ptr_set_get_property;

  object_class->finalize = g_ptr_set_finalize;

  g_object_class_install_property
    (object_class, PROP_COMPARE_DATA,
     g_param_spec_pointer ("compare-data",
			   "",
			   "",
			   G_PARAM_READABLE|G_PARAM_WRITABLE));

  g_object_class_install_property
    (object_class, PROP_KEY_COMPARE,
     g_param_spec_pointer ("key-compare",
			   "",
			   "GCompareDataFunc",
			   G_PARAM_READABLE|G_PARAM_WRITABLE));

  g_object_class_install_property
    (object_class, PROP_PTR_COMPARE,
     g_param_spec_pointer ("ptr-compare",
			   "",
			   "GCompareDataFunc",
			   G_PARAM_READABLE|G_PARAM_WRITABLE));

  g_object_class_install_property
    (object_class, PROP_DESTROY_FUNC,
     g_param_spec_pointer ("destroy-func",
			   "",
			   "GDestroyNotify",
			   G_PARAM_READABLE|G_PARAM_WRITABLE));

  g_object_class_install_property
    (object_class, PROP_IS_REFCOUNTED,
     g_param_spec_boolean ("is-refcounted",
			   "",
			   "Whether to do reference count the elements.",
			   FALSE,
			   G_PARAM_READABLE|G_PARAM_WRITABLE));

  signals[DELETE_ELEM] =
    g_signal_new ("delete_elem",
		  G_OBJECT_CLASS_TYPE (object_class),
		  G_SIGNAL_RUN_LAST,               // ACTION? XXX
		  G_STRUCT_OFFSET (GPtrSetClass, delete_elem),
		  NULL, NULL,
		  app_marshal_VOID__UINT_POINTER,
		  G_TYPE_NONE,
		  2,
		  G_TYPE_UINT,
		  G_TYPE_POINTER);
  signals[INSERT_ELEM] =
    g_signal_new ("insert_elem",
		  G_OBJECT_CLASS_TYPE (object_class),
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GPtrSetClass, insert_elem),
		  NULL, NULL,
		  app_marshal_VOID__UINT,
		  G_TYPE_NONE,
		  1,
		  G_TYPE_UINT);
}

GType g_ptr_set_get_type (void)
{
  static GType our_type = 0;

  if (our_type == 0) {
    static const GTypeInfo our_info = {
      sizeof(GPtrSetClass),
      (GBaseInitFunc) NULL,
      (GBaseFinalizeFunc) NULL,
      (GClassInitFunc) g_ptr_set_class_init,
      NULL,           /* class_finalize */
      NULL,           /* class_data */
      sizeof (GPtrSet),
      0,              /* n_preallocs */
      (GInstanceInitFunc) g_ptr_set_init,
      NULL
    };

    our_type = g_type_register_static (G_TYPE_OBJECT,
				       "GPtrSet",
				       &our_info,
				       0);
  }
  return our_type;
}
#include <gtk/gtk.h>
#include "gptrset.h"

#define APP_TYPE_LIST_STORE	       (app_list_store_get_type ())
#define APP_LIST_STORE(obj)	       (G_TYPE_CHECK_INSTANCE_CAST ((obj), APP_TYPE_LIST_STORE, AppListStore))
#define APP_LIST_STORE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), APP_TYPE_LIST_STORE, AppListStoreClass))
#define APP_IS_LIST_STORE(obj)	       (G_TYPE_CHECK_INSTANCE_TYPE ((obj), APP_TYPE_LIST_STORE))
#define APP_IS_LIST_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), APP_TYPE_LIST_STORE))
#define APP_LIST_STORE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), APP_TYPE_LIST_STORE, AppListStoreClass))

typedef struct _AppListStore       AppListStore;
typedef struct _AppListStoreClass  AppListStoreClass;

GType           app_list_store_get_type     (void);
AppListStore   *app_list_store_new          (GCompareDataFunc  ptr_compare,
					     gconstpointer     compare_data);

struct _AppListStore
{
  GPtrSet parent;

  gint stamp;
};

struct _AppListStoreClass
{
  GPtrSetClass parent_class;
};
#include "appliststore.h"

static gint
_direct_ptr_compare (gpointer p1, gpointer p2)
{
  if (p1==p2)
    return 0;
  else if (p1 < p2)
    return -1;
  else
    return 1;
}

AppListStore *
app_list_store_new (GCompareDataFunc  ptr_compare,
		    gconstpointer     compare_data)
{
  AppListStore *ret;
  GPtrSet *ps;

  ret = APP_LIST_STORE (g_object_new (APP_TYPE_LIST_STORE, NULL));

  ps = G_PTR_SET (ret);
  ps->ptr_compare = (ptr_compare?
		     ptr_compare : (GCompareDataFunc) _direct_ptr_compare);
  ps->compare_data = compare_data;

  return ret;
}

static guint
app_list_store_get_flags (GtkTreeModel *tree_model)
{
  g_return_val_if_fail (APP_IS_LIST_STORE (tree_model), 0);

  return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY;
}

static gint
app_list_store_get_n_columns (GtkTreeModel *tree_model)
{
  g_return_val_if_fail (APP_IS_LIST_STORE (tree_model), 0);

  return 1;
}

static GType
app_list_store_get_column_type (GtkTreeModel *tree_model,
				gint          index)
{
  g_return_val_if_fail (APP_IS_LIST_STORE (tree_model), G_TYPE_INVALID);
  g_return_val_if_fail (index == 0, G_TYPE_INVALID);

  return G_PTR_SET (tree_model)->is_refcounted? G_TYPE_OBJECT : G_TYPE_POINTER;
}

static gboolean
app_list_store_get_iter (GtkTreeModel *tree_model,
			 GtkTreeIter  *iter,
			 GtkTreePath  *path)
{
  gint i;

  g_return_val_if_fail (APP_IS_LIST_STORE (tree_model), FALSE);
  g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);

  i = gtk_tree_path_get_indices (path)[0];

  if (i >= G_PTR_SET (tree_model)->len)
    return FALSE;

  iter->stamp = APP_LIST_STORE (tree_model)->stamp;
  iter->user_data = GINT_TO_POINTER (i);

  return TRUE;
}

static GtkTreePath *
app_list_store_get_path (GtkTreeModel *tree_model,
			 GtkTreeIter  *iter)
{
  GtkTreePath *retval;

  g_return_val_if_fail (APP_IS_LIST_STORE (tree_model), NULL);
  g_return_val_if_fail (iter->stamp == APP_LIST_STORE (tree_model)->stamp, NULL);

  retval = gtk_tree_path_new ();
  gtk_tree_path_append_index (retval, GPOINTER_TO_INT (iter->user_data));
  return retval;
}

static void
app_list_store_get_value (GtkTreeModel *tree_model,
			  GtkTreeIter  *iter,
			  gint          column,
			  GValue       *value)
{
  GPtrSet *ps;

  g_return_if_fail (APP_IS_LIST_STORE (tree_model));
  g_return_if_fail (column < 1);
  g_return_if_fail (APP_LIST_STORE (tree_model)->stamp == iter->stamp);

  ps = G_PTR_SET (tree_model);

  if (ps->is_refcounted)
    {
      g_value_init (value, G_TYPE_OBJECT);
      g_value_set_object (value, g_ptr_set_at (ps, GPOINTER_TO_INT(iter->user_data)));
    }
  else
    {
      g_value_init (value, G_TYPE_POINTER);
      g_value_set_pointer (value, g_ptr_set_at (ps, GPOINTER_TO_INT(iter->user_data)));
    }
}

static gboolean
app_list_store_iter_next (GtkTreeModel  *tree_model,
			  GtkTreeIter   *iter)
{
  gint at;

  g_return_val_if_fail (APP_IS_LIST_STORE (tree_model), FALSE);
  g_return_val_if_fail (APP_LIST_STORE (tree_model)->stamp == iter->stamp, FALSE);

  at = GPOINTER_TO_INT (iter->user_data) + 1;

  iter->user_data = GINT_TO_POINTER (at);

  return at < G_PTR_SET(tree_model)->len;
}

static gboolean
app_list_store_iter_children (GtkTreeModel *tree_model,
			      GtkTreeIter  *iter,
			      GtkTreeIter  *parent)
{
  if (parent)
    return FALSE;

  if (G_PTR_SET (tree_model)->len)
    {
      iter->stamp = APP_LIST_STORE (tree_model)->stamp;
      iter->user_data = 0;
      return TRUE;
    }
  else
    return FALSE;
}

static gboolean
app_list_store_iter_has_child (GtkTreeModel *tree_model,
			       GtkTreeIter  *iter)
{
  return FALSE;
}

static gint
app_list_store_iter_n_children (GtkTreeModel *tree_model,
				GtkTreeIter  *iter)
{
  g_return_val_if_fail (APP_IS_LIST_STORE (tree_model), -1);

  if (iter == NULL)
    return G_PTR_SET (tree_model)->len;

  g_return_val_if_fail (APP_LIST_STORE (tree_model)->stamp == iter->stamp, -1);

  return 0;
}

static gboolean
app_list_store_iter_nth_child (GtkTreeModel *tree_model,
			       GtkTreeIter  *iter,
			       GtkTreeIter  *parent,
			       gint          n)
{
  g_return_val_if_fail (APP_IS_LIST_STORE (tree_model), FALSE);

  if (parent)
    return FALSE;

  if (n <= G_PTR_SET(tree_model)->len)
    {
      iter->stamp = APP_LIST_STORE (tree_model)->stamp;
      iter->user_data = GINT_TO_POINTER (n);
      return TRUE;
    }
  else
    return FALSE;
}

static gboolean
app_list_store_iter_parent (GtkTreeModel *tree_model,
			    GtkTreeIter  *iter,
			    GtkTreeIter  *child)
{
  return FALSE;
}

static void
app_list_store_delete_elem (GPtrSet *ps, gint index, gpointer data)
{
  AppListStore *list_store;
  GtkTreePath *path;

  list_store = APP_LIST_STORE (ps);
  ++list_store->stamp;

  path = gtk_tree_path_new ();
  gtk_tree_path_append_index (path, index);
  gtk_tree_model_row_deleted (GTK_TREE_MODEL (ps), path);
  gtk_tree_path_free (path);
}

static void
app_list_store_insert_elem (GPtrSet *ps, gint index)
{
  AppListStore *list_store;
  GtkTreePath *path;
  GtkTreeIter iter;

  list_store = APP_LIST_STORE (ps);
  ++list_store->stamp;

  iter.stamp = list_store->stamp;
  iter.user_data = GINT_TO_POINTER (index);

  path = gtk_tree_path_new ();
  gtk_tree_path_append_index (path, index);
  gtk_tree_model_row_inserted (GTK_TREE_MODEL (ps), path, &iter);
  gtk_tree_path_free (path);
}

static void
app_list_store_init (AppListStore *list_store)
{
  list_store->stamp = g_random_int ();
}

static void
app_list_store_class_init (AppListStoreClass *klass)
{
  GPtrSetClass *ps_class;

  ps_class = (GPtrSetClass*) klass;

  ps_class->delete_elem = app_list_store_delete_elem;
  ps_class->insert_elem = app_list_store_insert_elem;
}

static void
app_list_store_tree_model_init (GtkTreeModelIface *iface)
{
  iface->get_flags = app_list_store_get_flags;
  iface->get_n_columns = app_list_store_get_n_columns;
  iface->get_column_type = app_list_store_get_column_type;
  iface->get_iter = app_list_store_get_iter;
  iface->get_path = app_list_store_get_path;
  iface->get_value = app_list_store_get_value;
  iface->iter_next = app_list_store_iter_next;
  iface->iter_children = app_list_store_iter_children;
  iface->iter_has_child = app_list_store_iter_has_child;
  iface->iter_n_children = app_list_store_iter_n_children;
  iface->iter_nth_child = app_list_store_iter_nth_child;
  iface->iter_parent = app_list_store_iter_parent;
}

GType app_list_store_get_type (void)
{
  static GType list_store_type = 0;

  if (!list_store_type)
    {
      static const GTypeInfo list_store_info =
      {
	sizeof (AppListStoreClass),
	NULL,		/* base_init */
	NULL,		/* base_finalize */
        (GClassInitFunc) app_list_store_class_init,
	NULL,		/* class_finalize */
	NULL,		/* class_data */
        sizeof (AppListStore),
	0,
        (GInstanceInitFunc) app_list_store_init,
	NULL
      };

      static const GInterfaceInfo tree_model_info =
      {
	(GInterfaceInitFunc) app_list_store_tree_model_init,
	NULL,
	NULL
      };

      list_store_type = g_type_register_static (G_TYPE_PTR_SET, "AppListStore", &list_store_info, 0);
      g_type_add_interface_static (list_store_type,
				   GTK_TYPE_TREE_MODEL,
				   &tree_model_info);
    }

  return list_store_type;
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]