3 #include "custom-list.h"
5 /* indent -i3 -ci3 -ut -ts3 -bli0 -c0 custom-list.c */
7 /* boring declarations of local functions */
9 static void custom_list_init (CustomList
* pkg_tree
);
11 static void custom_list_class_init (CustomListClass
* klass
);
13 static void custom_list_tree_model_init (GtkTreeModelIface
* iface
);
15 static void custom_list_finalize (GObject
* object
);
17 static GtkTreeModelFlags
custom_list_get_flags (GtkTreeModel
* tree_model
);
19 static gint
custom_list_get_n_columns (GtkTreeModel
* tree_model
);
21 static GType
custom_list_get_column_type (GtkTreeModel
* tree_model
,
24 static gboolean
custom_list_get_iter (GtkTreeModel
* tree_model
,
25 GtkTreeIter
* iter
, GtkTreePath
* path
);
27 static GtkTreePath
*custom_list_get_path (GtkTreeModel
* tree_model
,
30 static void custom_list_get_value (GtkTreeModel
* tree_model
,
32 gint column
, GValue
* value
);
34 static gboolean
custom_list_iter_next (GtkTreeModel
* tree_model
,
37 static gboolean
custom_list_iter_children (GtkTreeModel
* tree_model
,
39 GtkTreeIter
* parent
);
41 static gboolean
custom_list_iter_has_child (GtkTreeModel
* tree_model
,
44 static gint
custom_list_iter_n_children (GtkTreeModel
* tree_model
,
47 static gboolean
custom_list_iter_nth_child (GtkTreeModel
* tree_model
,
49 GtkTreeIter
* parent
, gint n
);
51 static gboolean
custom_list_iter_parent (GtkTreeModel
* tree_model
,
55 /* -- GtkTreeSortable interface functions -- */
57 static gboolean
custom_list_sortable_get_sort_column_id (GtkTreeSortable
*
62 static void custom_list_sortable_set_sort_column_id (GtkTreeSortable
*
67 static void custom_list_sortable_set_sort_func (GtkTreeSortable
* sortable
,
69 GtkTreeIterCompareFunc
70 sort_func
, gpointer user_data
,
74 static void custom_list_sortable_set_default_sort_func (GtkTreeSortable
*
76 GtkTreeIterCompareFunc
82 static gboolean
custom_list_sortable_has_default_sort_func (GtkTreeSortable
*
87 static GObjectClass
*parent_class
= NULL
; /* GObject stuff - nothing to worry about */
91 custom_list_sortable_init (GtkTreeSortableIface
* iface
)
93 iface
->get_sort_column_id
= custom_list_sortable_get_sort_column_id
;
94 iface
->set_sort_column_id
= custom_list_sortable_set_sort_column_id
;
95 iface
->set_sort_func
= custom_list_sortable_set_sort_func
; /* NOT SUPPORTED */
96 iface
->set_default_sort_func
= custom_list_sortable_set_default_sort_func
; /* NOT SUPPORTED */
97 iface
->has_default_sort_func
= custom_list_sortable_has_default_sort_func
; /* NOT SUPPORTED */
100 /*****************************************************************************
102 * custom_list_get_type: here we register our new type and its interfaces
103 * with the type system. If you want to implement
104 * additional interfaces like GtkTreeSortable, you
105 * will need to do it here.
107 *****************************************************************************/
110 custom_list_get_type (void)
112 static GType custom_list_type
= 0;
114 if (custom_list_type
)
115 return custom_list_type
;
117 /* Some boilerplate type registration stuff */
120 static const GTypeInfo custom_list_info
= {
121 sizeof (CustomListClass
),
122 NULL
, /* base_init */
123 NULL
, /* base_finalize */
124 (GClassInitFunc
) custom_list_class_init
,
125 NULL
, /* class finalize */
126 NULL
, /* class_data */
129 (GInstanceInitFunc
) custom_list_init
133 g_type_register_static (G_TYPE_OBJECT
, "CustomList",
134 &custom_list_info
, (GTypeFlags
) 0);
137 /* Here we register our GtkTreeModel interface with the type system */
140 static const GInterfaceInfo tree_model_info
= {
141 (GInterfaceInitFunc
) custom_list_tree_model_init
,
146 g_type_add_interface_static (custom_list_type
, GTK_TYPE_TREE_MODEL
,
150 /* Add GtkTreeSortable interface */
153 static const GInterfaceInfo tree_sortable_info
= {
154 (GInterfaceInitFunc
) custom_list_sortable_init
,
159 g_type_add_interface_static (custom_list_type
,
160 GTK_TYPE_TREE_SORTABLE
,
161 &tree_sortable_info
);
164 return custom_list_type
;
167 /*****************************************************************************
169 * custom_list_class_init: more boilerplate GObject/GType stuff.
170 * Init callback for the type system,
171 * called once when our new class is created.
173 *****************************************************************************/
176 custom_list_class_init (CustomListClass
* klass
)
178 GObjectClass
*object_class
;
180 parent_class
= (GObjectClass
*) g_type_class_peek_parent (klass
);
181 object_class
= (GObjectClass
*) klass
;
183 object_class
->finalize
= custom_list_finalize
;
186 /*****************************************************************************
188 * custom_list_tree_model_init: init callback for the interface registration
189 * in custom_list_get_type. Here we override
190 * the GtkTreeModel interface functions that
193 *****************************************************************************/
196 custom_list_tree_model_init (GtkTreeModelIface
* iface
)
198 iface
->get_flags
= custom_list_get_flags
;
199 iface
->get_n_columns
= custom_list_get_n_columns
;
200 iface
->get_column_type
= custom_list_get_column_type
;
201 iface
->get_iter
= custom_list_get_iter
;
202 iface
->get_path
= custom_list_get_path
;
203 iface
->get_value
= custom_list_get_value
;
204 iface
->iter_next
= custom_list_iter_next
;
205 iface
->iter_children
= custom_list_iter_children
;
206 iface
->iter_has_child
= custom_list_iter_has_child
;
207 iface
->iter_n_children
= custom_list_iter_n_children
;
208 iface
->iter_nth_child
= custom_list_iter_nth_child
;
209 iface
->iter_parent
= custom_list_iter_parent
;
213 /*****************************************************************************
215 * custom_list_init: this is called everytime a new custom list object
216 * instance is created (we do that in custom_list_new).
217 * Initialise the list structure's fields here.
219 *****************************************************************************/
222 custom_list_init (CustomList
* custom_list
)
224 custom_list
->n_columns
= CUSTOM_LIST_N_COLUMNS
;
226 custom_list
->column_types
[0] = G_TYPE_STRING
; /* CUSTOM_LIST_COL_NAME */
227 custom_list
->column_types
[1] = G_TYPE_UINT
; /* CUSTOM_LIST_COL_USERS */
228 custom_list
->column_types
[2] = G_TYPE_STRING
; /* CUSTOM_LIST_COL_TOPIC */
230 custom_list
->num_rows
= 0;
231 custom_list
->num_alloc
= 0;
232 custom_list
->rows
= NULL
;
234 custom_list
->sort_id
= SORT_ID_CHANNEL
;
235 custom_list
->sort_order
= GTK_SORT_ASCENDING
;
239 /*****************************************************************************
241 * custom_list_finalize: this is called just before a custom list is
242 * destroyed. Free dynamically allocated memory here.
244 *****************************************************************************/
247 custom_list_finalize (GObject
* object
)
249 custom_list_clear (CUSTOM_LIST (object
));
251 /* must chain up - finalize parent */
252 (*parent_class
->finalize
) (object
);
256 /*****************************************************************************
258 * custom_list_get_flags: tells the rest of the world whether our tree model
259 * has any special characteristics. In our case,
260 * we have a list model (instead of a tree), and each
261 * tree iter is valid as long as the row in question
262 * exists, as it only contains a pointer to our struct.
264 *****************************************************************************/
266 static GtkTreeModelFlags
267 custom_list_get_flags (GtkTreeModel
* tree_model
)
269 return (GTK_TREE_MODEL_LIST_ONLY
/*| GTK_TREE_MODEL_ITERS_PERSIST */ );
273 /*****************************************************************************
275 * custom_list_get_n_columns: tells the rest of the world how many data
276 * columns we export via the tree model interface
278 *****************************************************************************/
281 custom_list_get_n_columns (GtkTreeModel
* tree_model
)
283 return 3;/*CUSTOM_LIST (tree_model)->n_columns;*/
287 /*****************************************************************************
289 * custom_list_get_column_type: tells the rest of the world which type of
290 * data an exported model column contains
292 *****************************************************************************/
295 custom_list_get_column_type (GtkTreeModel
* tree_model
, gint index
)
297 return CUSTOM_LIST (tree_model
)->column_types
[index
];
301 /*****************************************************************************
303 * custom_list_get_iter: converts a tree path (physical position) into a
304 * tree iter structure (the content of the iter
305 * fields will only be used internally by our model).
306 * We simply store a pointer to our chanlistrow
307 * structure that represents that row in the tree iter.
309 *****************************************************************************/
312 custom_list_get_iter (GtkTreeModel
* tree_model
,
313 GtkTreeIter
* iter
, GtkTreePath
* path
)
315 CustomList
*custom_list
= CUSTOM_LIST (tree_model
);
319 n
= gtk_tree_path_get_indices (path
)[0];
320 if (n
>= custom_list
->num_rows
|| n
< 0)
323 record
= custom_list
->rows
[n
];
325 /* We simply store a pointer to our custom record in the iter */
326 iter
->user_data
= record
;
332 /*****************************************************************************
334 * custom_list_get_path: converts a tree iter into a tree path (ie. the
335 * physical position of that row in the list).
337 *****************************************************************************/
340 custom_list_get_path (GtkTreeModel
* tree_model
, GtkTreeIter
* iter
)
345 record
= (chanlistrow
*) iter
->user_data
;
347 path
= gtk_tree_path_new ();
348 gtk_tree_path_append_index (path
, record
->pos
);
354 /*****************************************************************************
356 * custom_list_get_value: Returns a row's exported data columns
357 * (_get_value is what gtk_tree_model_get uses)
359 *****************************************************************************/
362 custom_list_get_value (GtkTreeModel
* tree_model
,
363 GtkTreeIter
* iter
, gint column
, GValue
* value
)
366 CustomList
*custom_list
= CUSTOM_LIST (tree_model
);
368 if (custom_list
->num_rows
== 0)
371 g_value_init (value
, custom_list
->column_types
[column
]);
373 record
= (chanlistrow
*) iter
->user_data
;
377 case CUSTOM_LIST_COL_NAME
:
378 g_value_set_static_string (value
, GET_CHAN (record
));
381 case CUSTOM_LIST_COL_USERS
:
382 g_value_set_uint (value
, record
->users
);
385 case CUSTOM_LIST_COL_TOPIC
:
386 g_value_set_static_string (value
, record
->topic
);
392 /*****************************************************************************
394 * custom_list_iter_next: Takes an iter structure and sets it to point
397 *****************************************************************************/
400 custom_list_iter_next (GtkTreeModel
* tree_model
, GtkTreeIter
* iter
)
402 chanlistrow
*record
, *nextrecord
;
403 CustomList
*custom_list
= CUSTOM_LIST (tree_model
);
405 record
= (chanlistrow
*) iter
->user_data
;
407 /* Is this the last record in the list? */
408 if ((record
->pos
+ 1) >= custom_list
->num_rows
)
411 nextrecord
= custom_list
->rows
[(record
->pos
+ 1)];
413 g_assert (nextrecord
!= NULL
);
414 g_assert (nextrecord
->pos
== (record
->pos
+ 1));
416 iter
->user_data
= nextrecord
;
422 /*****************************************************************************
424 * custom_list_iter_children: Returns TRUE or FALSE depending on whether
425 * the row specified by 'parent' has any children.
426 * If it has children, then 'iter' is set to
427 * point to the first child. Special case: if
428 * 'parent' is NULL, then the first top-level
429 * row should be returned if it exists.
431 *****************************************************************************/
434 custom_list_iter_children (GtkTreeModel
* tree_model
,
435 GtkTreeIter
* iter
, GtkTreeIter
* parent
)
437 CustomList
*custom_list
= CUSTOM_LIST (tree_model
);
439 /* this is a list, nodes have no children */
443 /* parent == NULL is a special case; we need to return the first top-level row */
444 /* No rows => no first row */
445 if (custom_list
->num_rows
== 0)
448 /* Set iter to first item in list */
449 iter
->user_data
= custom_list
->rows
[0];
455 /*****************************************************************************
457 * custom_list_iter_has_child: Returns TRUE or FALSE depending on whether
458 * the row specified by 'iter' has any children.
459 * We only have a list and thus no children.
461 *****************************************************************************/
464 custom_list_iter_has_child (GtkTreeModel
* tree_model
, GtkTreeIter
* iter
)
470 /*****************************************************************************
472 * custom_list_iter_n_children: Returns the number of children the row
473 * specified by 'iter' has. This is usually 0,
474 * as we only have a list and thus do not have
475 * any children to any rows. A special case is
476 * when 'iter' is NULL, in which case we need
477 * to return the number of top-level nodes,
478 * ie. the number of rows in our list.
480 *****************************************************************************/
483 custom_list_iter_n_children (GtkTreeModel
* tree_model
, GtkTreeIter
* iter
)
485 CustomList
*custom_list
= CUSTOM_LIST (tree_model
);
487 /* special case: if iter == NULL, return number of top-level rows */
489 return custom_list
->num_rows
;
491 return 0; /* otherwise, this is easy again for a list */
495 /*****************************************************************************
497 * custom_list_iter_nth_child: If the row specified by 'parent' has any
498 * children, set 'iter' to the n-th child and
499 * return TRUE if it exists, otherwise FALSE.
500 * A special case is when 'parent' is NULL, in
501 * which case we need to set 'iter' to the n-th
504 *****************************************************************************/
507 custom_list_iter_nth_child (GtkTreeModel
* tree_model
,
508 GtkTreeIter
* iter
, GtkTreeIter
* parent
, gint n
)
510 CustomList
*custom_list
= CUSTOM_LIST (tree_model
);
512 /* a list has only top-level rows */
516 /* special case: if parent == NULL, set iter to n-th top-level row */
517 if (n
>= custom_list
->num_rows
)
520 iter
->user_data
= custom_list
->rows
[n
];
525 /*****************************************************************************
527 * custom_list_iter_parent: Point 'iter' to the parent node of 'child'. As
528 * we have a list and thus no children and no
529 * parents of children, we can just return FALSE.
531 *****************************************************************************/
534 custom_list_iter_parent (GtkTreeModel
* tree_model
,
535 GtkTreeIter
* iter
, GtkTreeIter
* child
)
541 custom_list_sortable_get_sort_column_id (GtkTreeSortable
* sortable
,
545 CustomList
*custom_list
= CUSTOM_LIST (sortable
);
548 *sort_col_id
= custom_list
->sort_id
;
551 *order
= custom_list
->sort_order
;
558 custom_list_sortable_set_sort_column_id (GtkTreeSortable
* sortable
,
559 gint sort_col_id
, GtkSortType order
)
561 CustomList
*custom_list
= CUSTOM_LIST (sortable
);
563 if (custom_list
->sort_id
== sort_col_id
564 && custom_list
->sort_order
== order
)
567 custom_list
->sort_id
= sort_col_id
;
568 custom_list
->sort_order
= order
;
570 custom_list_resort (custom_list
);
572 /* emit "sort-column-changed" signal to tell any tree views
573 * that the sort column has changed (so the little arrow
574 * in the column header of the sort column is drawn
575 * in the right column) */
577 gtk_tree_sortable_sort_column_changed (sortable
);
581 custom_list_sortable_set_sort_func (GtkTreeSortable
* sortable
,
583 GtkTreeIterCompareFunc sort_func
,
585 GtkDestroyNotify destroy_func
)
590 custom_list_sortable_set_default_sort_func (GtkTreeSortable
* sortable
,
591 GtkTreeIterCompareFunc sort_func
,
593 GtkDestroyNotify destroy_func
)
598 custom_list_sortable_has_default_sort_func (GtkTreeSortable
* sortable
)
603 /* fast as possible compare func for sorting.
604 TODO: If fast enough, use a unicode collation key and strcmp. */
606 #define TOSML(c) (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c))
609 fast_ascii_stricmp (const char *s1
, const char *s2
)
615 c1
= (int) (unsigned char) TOSML (*s1
);
616 c2
= (int) (unsigned char) TOSML (*s2
);
623 return (((int) (unsigned char) *s1
) - ((int) (unsigned char) *s2
));
627 custom_list_qsort_compare_func (chanlistrow
** a
, chanlistrow
** b
,
628 CustomList
* custom_list
)
630 if (custom_list
->sort_order
== GTK_SORT_DESCENDING
)
632 chanlistrow
**tmp
= a
;
637 if (custom_list
->sort_id
== SORT_ID_USERS
)
639 return (*a
)->users
- (*b
)->users
;
642 if (custom_list
->sort_id
== SORT_ID_TOPIC
)
644 return fast_ascii_stricmp ((*a
)->topic
, (*b
)->topic
);
647 return strcmp ((*a
)->collation_key
, (*b
)->collation_key
);
650 /*****************************************************************************
652 * custom_list_new: This is what you use in your own code to create a
653 * new custom list tree model for you to use.
655 *****************************************************************************/
658 custom_list_new (void)
660 return (CustomList
*) g_object_new (CUSTOM_TYPE_LIST
, NULL
);
664 custom_list_append (CustomList
* custom_list
, chanlistrow
* newrecord
)
671 if (custom_list
->num_rows
>= custom_list
->num_alloc
)
673 custom_list
->num_alloc
+= 64;
674 newsize
= custom_list
->num_alloc
* sizeof (chanlistrow
*);
675 custom_list
->rows
= g_realloc (custom_list
->rows
, newsize
);
678 /* TODO: Binary search insert? */
680 pos
= custom_list
->num_rows
;
681 custom_list
->rows
[pos
] = newrecord
;
682 custom_list
->num_rows
++;
683 newrecord
->pos
= pos
;
685 /* inform the tree view and other interested objects
686 * (e.g. tree row references) that we have inserted
687 * a new row, and where it was inserted */
689 path
= gtk_tree_path_new ();
690 gtk_tree_path_append_index (path
, newrecord
->pos
);
691 /* custom_list_get_iter(GTK_TREE_MODEL(custom_list), &iter, path);*/
692 iter
.user_data
= newrecord
;
693 gtk_tree_model_row_inserted (GTK_TREE_MODEL (custom_list
), path
, &iter
);
694 gtk_tree_path_free (path
);
698 custom_list_resort (CustomList
* custom_list
)
703 if (custom_list
->num_rows
< 2)
707 g_qsort_with_data (custom_list
->rows
,
708 custom_list
->num_rows
,
709 sizeof (chanlistrow
*),
710 (GCompareDataFunc
) custom_list_qsort_compare_func
,
713 /* let other objects know about the new order */
714 neworder
= malloc (sizeof (gint
) * custom_list
->num_rows
);
716 for (i
= custom_list
->num_rows
- 1; i
>= 0; i
--)
718 /* Note that the API reference might be wrong about
719 * this, see bug number 124790 on bugs.gnome.org.
720 * Both will work, but one will give you 'jumpy'
721 * selections after row reordering. */
722 /* neworder[(custom_list->rows[i])->pos] = i; */
723 neworder
[i
] = (custom_list
->rows
[i
])->pos
;
724 (custom_list
->rows
[i
])->pos
= i
;
727 path
= gtk_tree_path_new ();
728 gtk_tree_model_rows_reordered (GTK_TREE_MODEL (custom_list
), path
, NULL
,
730 gtk_tree_path_free (path
);
735 custom_list_clear (CustomList
* custom_list
)
737 int i
, max
= custom_list
->num_rows
- 1;
740 for (i
= max
; i
>= 0; i
--)
742 path
= gtk_tree_path_new ();
743 gtk_tree_path_append_index (path
, custom_list
->rows
[i
]->pos
);
744 gtk_tree_model_row_deleted (GTK_TREE_MODEL (custom_list
), path
);
745 gtk_tree_path_free (path
);
748 custom_list
->num_rows
= 0;
749 custom_list
->num_alloc
= 0;
751 g_free (custom_list
->rows
);
752 custom_list
->rows
= NULL
;