Fix typo: Similar Artist -> Similar Artists
[gmpc.git] / src / egg / eggcolumnmodel.c
blob9c6850f54f180ad68a4f80267f1069866e7a69dc
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* egg-column-model.c
3 * Copyright (C) 2001 Anders Carlsson, Jonathan Blanford
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
21 #include "eggcolumnmodel.h"
23 #include <gtk/gtk.h>
24 #include "plugin.h"
25 static void update_columns (GtkTreeView *tree_view, EggColumnModel *column_model);
28 enum
30 COLUMN_VISIBLE,
31 COLUMN_NAME,
32 COLUMN_OBJECT,
33 N_COLUMNS
38 static GObjectClass *parent_class = NULL;
41 static gint
42 egg_column_model_get_n_columns (GtkTreeModel *tree_model)
44 return N_COLUMNS;
47 static GType
48 egg_column_model_get_column_type (GtkTreeModel *tree_model,
49 gint temp_index)
51 switch (temp_index)
53 case COLUMN_VISIBLE:
54 return G_TYPE_BOOLEAN;
55 case COLUMN_NAME:
56 return G_TYPE_STRING;
57 case COLUMN_OBJECT:
58 return GTK_TYPE_TREE_VIEW_COLUMN;
59 default:
60 return G_TYPE_INVALID;
64 static gboolean
65 egg_column_model_get_iter (GtkTreeModel *tree_model,
66 GtkTreeIter *iter,
67 GtkTreePath *path)
69 EggColumnModel *column_model = (EggColumnModel *) tree_model;
70 GList *list;
71 gint i;
73 i = gtk_tree_path_get_indices (path)[0];
74 list = g_list_nth (column_model->columns, i);
76 if (list == NULL)
79 return FALSE;
81 iter->stamp = column_model->stamp;
82 iter->user_data = list;
84 return TRUE;
87 static GtkTreePath *
88 egg_column_model_get_path (GtkTreeModel *tree_model,
89 GtkTreeIter *iter)
91 EggColumnModel *column_model = (EggColumnModel *) tree_model;
92 GtkTreePath *path;
93 gint i = 0;
94 GList *list;
96 g_return_val_if_fail (EGG_IS_COLUMN_MODEL (tree_model), NULL);
97 g_return_val_if_fail (iter->stamp == EGG_COLUMN_MODEL (tree_model)->stamp, NULL);
99 for (list = column_model->columns; list; list = list->next)
101 if (list == (GList *)iter->user_data)
102 break;
103 i++;
106 if (list == NULL)
107 return NULL;
109 path = gtk_tree_path_new ();
110 gtk_tree_path_append_index (path, i);
112 return path;
116 static gboolean
117 egg_column_model_iter_has_child (GtkTreeModel *tree_model,
118 GtkTreeIter *iter)
120 g_return_val_if_fail (EGG_IS_COLUMN_MODEL (tree_model), FALSE);
121 g_return_val_if_fail (iter->stamp == EGG_COLUMN_MODEL (tree_model)->stamp, FALSE);
123 return FALSE;
126 static void
127 egg_column_model_get_value (GtkTreeModel *tree_model,
128 GtkTreeIter *iter,
129 gint column,
130 GValue *value)
132 GtkTreeViewColumn *view_column;
134 g_return_if_fail (EGG_IS_COLUMN_MODEL (tree_model));
135 g_return_if_fail (column < N_COLUMNS);
136 g_return_if_fail (column >= 0);
137 g_return_if_fail (iter->stamp == EGG_COLUMN_MODEL (tree_model)->stamp);
139 view_column = GTK_TREE_VIEW_COLUMN (((GList *)iter->user_data)->data);
140 switch (column)
142 case COLUMN_VISIBLE:
143 g_value_init (value, G_TYPE_BOOLEAN);
144 g_value_set_boolean (value, gtk_tree_view_column_get_visible (view_column));
145 break;
146 case COLUMN_NAME:
147 g_value_init (value, G_TYPE_STRING);
148 g_value_set_string (value, gtk_tree_view_column_get_title (view_column));
149 break;
150 case COLUMN_OBJECT:
151 g_value_init (value, GTK_TYPE_TREE_VIEW_COLUMN);
152 g_value_set_object (value, view_column);
153 break;
154 default:
155 g_assert_not_reached ();
156 break;
161 static gboolean
162 egg_column_model_iter_next (GtkTreeModel *tree_model,
163 GtkTreeIter *iter)
165 EggColumnModel *column_model;
167 g_return_val_if_fail (EGG_IS_COLUMN_MODEL (tree_model), FALSE);
168 g_return_val_if_fail (iter->stamp == EGG_COLUMN_MODEL (tree_model)->stamp, FALSE);
170 column_model = EGG_COLUMN_MODEL (tree_model);
172 iter->user_data = ((GList *)iter->user_data)->next;
174 return (iter->user_data != NULL);
177 static gint
178 egg_column_model_iter_n_children (GtkTreeModel *tree_model,
179 GtkTreeIter *iter)
181 g_return_val_if_fail (EGG_IS_COLUMN_MODEL (tree_model), 0);
183 if (iter)
185 g_return_val_if_fail (iter->stamp == EGG_COLUMN_MODEL (tree_model)->stamp, 0);
186 return 0;
189 return g_list_length (EGG_COLUMN_MODEL (tree_model)->columns);
192 static gboolean
193 egg_column_model_iter_nth_child (GtkTreeModel *tree_model,
194 GtkTreeIter *iter,
195 GtkTreeIter *parent,
196 gint n)
198 EggColumnModel *column_model;
200 g_return_val_if_fail (EGG_IS_COLUMN_MODEL (tree_model), FALSE);
202 column_model = EGG_COLUMN_MODEL (tree_model);
203 if (parent)
205 g_return_val_if_fail (parent->stamp == EGG_COLUMN_MODEL (tree_model)->stamp, FALSE);
206 return FALSE;
209 iter->user_data = g_list_nth ((GList *)column_model->columns, n);
211 if (iter->user_data == NULL)
212 return FALSE;
214 iter->stamp = column_model->stamp;
215 return TRUE;
220 * Gtk complains withouth this function, so add a dummy one.
222 static gboolean
223 egg_column_model_iter_children(GtkTreeModel * tree_model,
224 GtkTreeIter * iter, GtkTreeIter * parent)
226 return FALSE;
229 static void
230 egg_column_model_tree_model_init (GtkTreeModelIface *iface)
232 iface->get_n_columns = egg_column_model_get_n_columns;
233 iface->get_column_type = egg_column_model_get_column_type;
234 iface->get_iter = egg_column_model_get_iter;
235 iface->get_path = egg_column_model_get_path;
236 iface->get_value = egg_column_model_get_value;
237 iface->iter_has_child = egg_column_model_iter_has_child;
238 iface->iter_next = egg_column_model_iter_next;
239 iface->iter_nth_child = egg_column_model_iter_nth_child;
240 iface->iter_n_children = egg_column_model_iter_n_children;
241 iface->iter_children = egg_column_model_iter_children;
244 static gboolean
245 egg_column_model_drag_data_get (GtkTreeDragSource *drag_source, GtkTreePath *path, GtkSelectionData *selection_data)
247 #if 0
248 if (gtk_selection_data_set_tree_row (selection_data,
249 GTK_TREE_MODEL (drag_source),
250 path)) {
251 return TRUE;
253 else {
255 #endif
256 return FALSE;
259 static gboolean
260 egg_column_model_drag_data_delete (GtkTreeDragSource *drag_source, GtkTreePath *path)
262 return FALSE;
265 static void
266 egg_column_model_drag_source_init (GtkTreeDragSourceIface *iface)
268 iface->drag_data_get = egg_column_model_drag_data_get;
269 iface->drag_data_delete = egg_column_model_drag_data_delete;
272 static gboolean
273 egg_column_model_row_drop_possible (GtkTreeDragDest *drag_dest,
274 GtkTreePath *dest_path,
275 GtkSelectionData *selection_data)
277 gint *indices;
279 g_return_val_if_fail (EGG_IS_COLUMN_MODEL (drag_dest), FALSE);
281 if (gtk_tree_path_get_depth (dest_path) != 1)
282 return FALSE;
284 indices = gtk_tree_path_get_indices (dest_path);
286 if ((guint)indices[0] <= g_list_length (((EggColumnModel *)drag_dest)->columns))
287 return TRUE;
288 else
289 return FALSE;
292 static void
293 egg_column_model_drag_dest_init (GtkTreeDragDestIface *iface)
295 iface->row_drop_possible = egg_column_model_row_drop_possible;
298 static void
299 egg_column_model_init (EggColumnModel *model)
303 model->stamp = g_random_int ();
305 while (model->stamp == 0);
308 static void
309 egg_column_model_finalize (GObject *object)
311 EggColumnModel *model;
313 model = (EggColumnModel *)object;
315 g_list_free (model->columns);
317 g_signal_handlers_disconnect_by_func (G_OBJECT (model->tree_view), (void*)update_columns, model);
319 G_OBJECT_CLASS (parent_class)->finalize (object);
322 static void
323 egg_column_model_class_init (EggColumnModelClass *klass)
325 GObjectClass *object_class;
327 object_class = (GObjectClass *)klass;
329 parent_class = (GObjectClass *)g_type_class_peek_parent (klass);
331 object_class->finalize = egg_column_model_finalize;
334 GType
335 egg_column_model_get_type (void)
337 static GType object_type = 0;
339 if (!object_type)
341 static const GTypeInfo object_info =
343 sizeof (EggColumnModelClass),
344 NULL,
345 NULL,
346 (GClassInitFunc) egg_column_model_class_init,
347 NULL,
348 NULL,
349 sizeof (EggColumnModel),
351 (GInstanceInitFunc) egg_column_model_init,
354 static const GInterfaceInfo tree_model_info =
356 (GInterfaceInitFunc) egg_column_model_tree_model_init,
357 NULL,
358 NULL
361 static const GInterfaceInfo drag_source_info =
363 (GInterfaceInitFunc) egg_column_model_drag_source_init,
364 NULL,
365 NULL
368 static const GInterfaceInfo drag_dest_info =
370 (GInterfaceInitFunc) egg_column_model_drag_dest_init,
371 NULL,
372 NULL
375 object_type = g_type_register_static (G_TYPE_OBJECT, "EggColumnModel", &object_info, (GTypeFlags)0);
376 g_type_add_interface_static (object_type,
377 GTK_TYPE_TREE_MODEL,
378 &tree_model_info);
379 g_type_add_interface_static (object_type,
380 GTK_TYPE_TREE_DRAG_SOURCE,
381 &drag_source_info);
382 g_type_add_interface_static (object_type,
383 GTK_TYPE_TREE_DRAG_DEST,
384 &drag_dest_info);
387 return object_type;
390 static void
391 update_columns (GtkTreeView *tree_view,
392 EggColumnModel *column_model)
394 GList *old_columns = column_model->columns;
395 gint old_length, length;
396 GList *a, *b;
398 column_model->columns = gtk_tree_view_get_columns (column_model->tree_view);
400 /* As the view tells us one change at a time, we can do this hack. */
401 length = g_list_length (column_model->columns);
402 old_length = g_list_length (old_columns);
404 if (length != old_length)
406 GtkTreePath *path;
407 gint i = 0;
409 /* Find out where they differ */
410 for (a = old_columns, b = column_model->columns; a && b; a = a->next, b = b->next) {
411 if (a->data != b->data)
412 break;
413 i++;
416 path = gtk_tree_path_new ();
417 gtk_tree_path_append_index (path, i);
419 if (length < old_length)
421 column_model->stamp++;
422 gtk_tree_model_row_deleted (GTK_TREE_MODEL (column_model), path);
424 else
426 GtkTreeIter iter;
428 iter.stamp = column_model->stamp;
429 iter.user_data = b;
430 gtk_tree_model_row_inserted (GTK_TREE_MODEL (column_model), path, &iter);
433 gtk_tree_path_free (path);
435 else
437 gint i;
438 gint m = 0, n = 1;
439 gint *new_order;
440 GtkTreePath *path;
442 new_order = g_new (int, length);
443 a = old_columns; b = column_model->columns;
445 while (a->data == b->data) {
446 a = a->next;
447 b = b->next;
449 if (a == NULL)
451 g_free(new_order);
452 return;
454 m++;
457 if (a->next->data == b->data)
459 b = b->next;
460 while (b->data != a->data)
462 b = b->next;
463 n++;
465 for (i = 0; i < m; i++)
466 new_order[i] = i;
467 for (i = m; i < m+n; i++)
468 new_order[i] = i+1;
469 new_order[i] = m;
470 for (i = m + n +1; i < length; i++)
471 new_order[i] = i;
473 else
475 a = a->next;
476 while (a->data != b->data)
478 a = a->next;
479 n++;
481 for (i = 0; i < m; i++)
482 new_order[i] = i;
483 new_order[m] = m+n;
484 for (i = m+1; i < m + n+ 1; i++)
485 new_order[i] = i - 1;
486 for (i = m + n + 1; i < length; i++)
487 new_order[i] = i;
490 path = gtk_tree_path_new ();
491 gtk_tree_model_rows_reordered (GTK_TREE_MODEL (column_model),
492 path,
493 NULL,
494 new_order);
495 gtk_tree_path_free (path);
496 g_free (new_order);
499 if (old_columns)
500 g_list_free (old_columns);
503 EggColumnModel *
504 egg_column_model_new (GtkTreeView *tree_view)
506 EggColumnModel *model;
508 model = (EggColumnModel *)g_object_new (egg_column_model_get_type (), NULL);
510 model->tree_view = tree_view;
511 model->columns = gtk_tree_view_get_columns (tree_view);
513 g_signal_connect (G_OBJECT (tree_view), "columns_changed",
514 G_CALLBACK (update_columns), model);
516 return model;
519 void
520 egg_column_model_set_column_visible (EggColumnModel *model, GtkTreeIter *iter, gboolean visible)
522 GtkTreeViewColumn *column;
523 GtkTreePath *path;
524 gchar *string = NULL;
525 gpointer data;
526 int colid = 0;
527 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter);
529 column = (GtkTreeViewColumn *)((GList *)iter->user_data)->data;
530 data = g_object_get_data(G_OBJECT(column), "colid");
531 colid = GPOINTER_TO_INT(data);
533 gtk_tree_view_column_set_visible (column, visible);
534 gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, iter);
536 /* custom code */
537 string = g_strdup_printf("%i", colid);
538 cfg_set_single_value_as_int(config, "current-playlist-column-enable",string, visible);
540 gtk_tree_path_free (path);
543 gboolean
544 egg_column_model_get_column_visible (EggColumnModel *model, GtkTreeIter *iter)
546 GtkTreeViewColumn *column;
549 g_return_val_if_fail (model->stamp == iter->stamp, FALSE);
551 column = (GtkTreeViewColumn *)((GList *)iter->user_data)->data;
553 return gtk_tree_view_column_get_visible (column);
556 gboolean
557 egg_column_model_is_column_first (EggColumnModel *model, GtkTreeIter *iter)
559 g_return_val_if_fail (model->stamp == iter->stamp, FALSE);
561 return (((GList *)iter->user_data)->prev == NULL);
564 gboolean
565 egg_column_model_is_column_last (EggColumnModel *model, GtkTreeIter *iter)
567 g_return_val_if_fail (model->stamp == iter->stamp, FALSE);
569 return (((GList *)iter->user_data)->next == NULL);
572 void
573 egg_column_model_move_down_column (EggColumnModel *model, GtkTreeIter *iter)
575 GtkTreeViewColumn *column, *base_column;
576 GList *node;
578 g_return_if_fail (model->stamp == iter->stamp);
579 g_return_if_fail (((GList *)iter->user_data)->next != NULL);
581 node = (GList *)iter->user_data;
583 column = (GtkTreeViewColumn *)node->data;
584 base_column = (GtkTreeViewColumn *)node->next->data;
586 gtk_tree_view_move_column_after (model->tree_view, column, base_column);
589 void
590 egg_column_model_move_up_column (EggColumnModel *model, GtkTreeIter *iter)
592 GtkTreeViewColumn *column, *base_column;
593 GList *node;
595 g_return_if_fail (model->stamp == iter->stamp);
596 g_return_if_fail (((GList *)iter->user_data)->prev != NULL);
598 node = (GList *)iter->user_data;
600 column = (GtkTreeViewColumn *)node->prev->data;
601 base_column = (GtkTreeViewColumn *)node->data;
603 gtk_tree_view_move_column_after (model->tree_view, column, base_column);