Use macros instead of raw numbers for login type
[rofl0r-ixchat.git] / src / fe-gtk / custom-list.c
blob0c3c40b549a1a869e00aa8323dc7c99b002304ab
1 #include <string.h>
2 #include <stdlib.h>
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,
22 gint index);
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,
28 GtkTreeIter * iter);
30 static void custom_list_get_value (GtkTreeModel * tree_model,
31 GtkTreeIter * iter,
32 gint column, GValue * value);
34 static gboolean custom_list_iter_next (GtkTreeModel * tree_model,
35 GtkTreeIter * iter);
37 static gboolean custom_list_iter_children (GtkTreeModel * tree_model,
38 GtkTreeIter * iter,
39 GtkTreeIter * parent);
41 static gboolean custom_list_iter_has_child (GtkTreeModel * tree_model,
42 GtkTreeIter * iter);
44 static gint custom_list_iter_n_children (GtkTreeModel * tree_model,
45 GtkTreeIter * iter);
47 static gboolean custom_list_iter_nth_child (GtkTreeModel * tree_model,
48 GtkTreeIter * iter,
49 GtkTreeIter * parent, gint n);
51 static gboolean custom_list_iter_parent (GtkTreeModel * tree_model,
52 GtkTreeIter * iter,
53 GtkTreeIter * child);
55 /* -- GtkTreeSortable interface functions -- */
57 static gboolean custom_list_sortable_get_sort_column_id (GtkTreeSortable *
58 sortable,
59 gint * sort_col_id,
60 GtkSortType * order);
62 static void custom_list_sortable_set_sort_column_id (GtkTreeSortable *
63 sortable,
64 gint sort_col_id,
65 GtkSortType order);
67 static void custom_list_sortable_set_sort_func (GtkTreeSortable * sortable,
68 gint sort_col_id,
69 GtkTreeIterCompareFunc
70 sort_func, gpointer user_data,
71 GtkDestroyNotify
72 destroy_func);
74 static void custom_list_sortable_set_default_sort_func (GtkTreeSortable *
75 sortable,
76 GtkTreeIterCompareFunc
77 sort_func,
78 gpointer user_data,
79 GtkDestroyNotify
80 destroy_func);
82 static gboolean custom_list_sortable_has_default_sort_func (GtkTreeSortable *
83 sortable);
87 static GObjectClass *parent_class = NULL; /* GObject stuff - nothing to worry about */
90 static void
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 *****************************************************************************/
109 GType
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 */
118 if (1)
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 */
127 sizeof (CustomList),
128 0, /* n_preallocs */
129 (GInstanceInitFunc) custom_list_init
132 custom_list_type =
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 */
138 if (1)
140 static const GInterfaceInfo tree_model_info = {
141 (GInterfaceInitFunc) custom_list_tree_model_init,
142 NULL,
143 NULL
146 g_type_add_interface_static (custom_list_type, GTK_TYPE_TREE_MODEL,
147 &tree_model_info);
150 /* Add GtkTreeSortable interface */
151 if (1)
153 static const GInterfaceInfo tree_sortable_info = {
154 (GInterfaceInitFunc) custom_list_sortable_init,
155 NULL,
156 NULL
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 *****************************************************************************/
175 static void
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
191 * we implement.
193 *****************************************************************************/
195 static void
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 *****************************************************************************/
221 static void
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 *****************************************************************************/
246 static void
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 *****************************************************************************/
280 static gint
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 *****************************************************************************/
294 static GType
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 *****************************************************************************/
311 static gboolean
312 custom_list_get_iter (GtkTreeModel * tree_model,
313 GtkTreeIter * iter, GtkTreePath * path)
315 CustomList *custom_list = CUSTOM_LIST (tree_model);
316 chanlistrow *record;
317 gint n;
319 n = gtk_tree_path_get_indices (path)[0];
320 if (n >= custom_list->num_rows || n < 0)
321 return FALSE;
323 record = custom_list->rows[n];
325 /* We simply store a pointer to our custom record in the iter */
326 iter->user_data = record;
328 return TRUE;
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 *****************************************************************************/
339 static GtkTreePath *
340 custom_list_get_path (GtkTreeModel * tree_model, GtkTreeIter * iter)
342 GtkTreePath *path;
343 chanlistrow *record;
345 record = (chanlistrow *) iter->user_data;
347 path = gtk_tree_path_new ();
348 gtk_tree_path_append_index (path, record->pos);
350 return path;
354 /*****************************************************************************
356 * custom_list_get_value: Returns a row's exported data columns
357 * (_get_value is what gtk_tree_model_get uses)
359 *****************************************************************************/
361 static void
362 custom_list_get_value (GtkTreeModel * tree_model,
363 GtkTreeIter * iter, gint column, GValue * value)
365 chanlistrow *record;
366 CustomList *custom_list = CUSTOM_LIST (tree_model);
368 if (custom_list->num_rows == 0)
369 return;
371 g_value_init (value, custom_list->column_types[column]);
373 record = (chanlistrow *) iter->user_data;
375 switch (column)
377 case CUSTOM_LIST_COL_NAME:
378 g_value_set_static_string (value, GET_CHAN (record));
379 break;
381 case CUSTOM_LIST_COL_USERS:
382 g_value_set_uint (value, record->users);
383 break;
385 case CUSTOM_LIST_COL_TOPIC:
386 g_value_set_static_string (value, record->topic);
387 break;
392 /*****************************************************************************
394 * custom_list_iter_next: Takes an iter structure and sets it to point
395 * to the next row.
397 *****************************************************************************/
399 static gboolean
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)
409 return FALSE;
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;
418 return TRUE;
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 *****************************************************************************/
433 static gboolean
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 */
440 if (parent)
441 return FALSE;
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)
446 return FALSE;
448 /* Set iter to first item in list */
449 iter->user_data = custom_list->rows[0];
451 return TRUE;
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 *****************************************************************************/
463 static gboolean
464 custom_list_iter_has_child (GtkTreeModel * tree_model, GtkTreeIter * iter)
466 return FALSE;
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 *****************************************************************************/
482 static gint
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 */
488 if (!iter)
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
502 * row if it exists.
504 *****************************************************************************/
506 static gboolean
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 */
513 if (parent)
514 return FALSE;
516 /* special case: if parent == NULL, set iter to n-th top-level row */
517 if (n >= custom_list->num_rows)
518 return FALSE;
520 iter->user_data = custom_list->rows[n];
521 return TRUE;
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 *****************************************************************************/
533 static gboolean
534 custom_list_iter_parent (GtkTreeModel * tree_model,
535 GtkTreeIter * iter, GtkTreeIter * child)
537 return FALSE;
540 static gboolean
541 custom_list_sortable_get_sort_column_id (GtkTreeSortable * sortable,
542 gint * sort_col_id,
543 GtkSortType * order)
545 CustomList *custom_list = CUSTOM_LIST (sortable);
547 if (sort_col_id)
548 *sort_col_id = custom_list->sort_id;
550 if (order)
551 *order = custom_list->sort_order;
553 return TRUE;
557 static void
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)
565 return;
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);
580 static void
581 custom_list_sortable_set_sort_func (GtkTreeSortable * sortable,
582 gint sort_col_id,
583 GtkTreeIterCompareFunc sort_func,
584 gpointer user_data,
585 GtkDestroyNotify destroy_func)
589 static void
590 custom_list_sortable_set_default_sort_func (GtkTreeSortable * sortable,
591 GtkTreeIterCompareFunc sort_func,
592 gpointer user_data,
593 GtkDestroyNotify destroy_func)
597 static gboolean
598 custom_list_sortable_has_default_sort_func (GtkTreeSortable * sortable)
600 return FALSE;
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))
608 static inline int
609 fast_ascii_stricmp (const char *s1, const char *s2)
611 int c1, c2;
613 while (*s1 && *s2)
615 c1 = (int) (unsigned char) TOSML (*s1);
616 c2 = (int) (unsigned char) TOSML (*s2);
617 if (c1 != c2)
618 return (c1 - c2);
619 s1++;
620 s2++;
623 return (((int) (unsigned char) *s1) - ((int) (unsigned char) *s2));
626 static gint
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;
633 a = b;
634 b = tmp;
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 *****************************************************************************/
657 CustomList *
658 custom_list_new (void)
660 return (CustomList *) g_object_new (CUSTOM_TYPE_LIST, NULL);
663 void
664 custom_list_append (CustomList * custom_list, chanlistrow * newrecord)
666 GtkTreeIter iter;
667 GtkTreePath *path;
668 gulong newsize;
669 guint pos;
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);
697 void
698 custom_list_resort (CustomList * custom_list)
700 GtkTreePath *path;
701 gint *neworder, i;
703 if (custom_list->num_rows < 2)
704 return;
706 /* resort */
707 g_qsort_with_data (custom_list->rows,
708 custom_list->num_rows,
709 sizeof (chanlistrow *),
710 (GCompareDataFunc) custom_list_qsort_compare_func,
711 custom_list);
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,
729 neworder);
730 gtk_tree_path_free (path);
731 free (neworder);
734 void
735 custom_list_clear (CustomList * custom_list)
737 int i, max = custom_list->num_rows - 1;
738 GtkTreePath *path;
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;