No issues found
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3 /* nautilus-column-chooser.h - A column chooser widget
4
5 Copyright (C) 2004 Novell, Inc.
6
7 The Gnome Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
11
12 The Gnome Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
16
17 You should have received a copy of the GNU Library General Public
18 License along with the Gnome Library; see the column COPYING.LIB. If not,
19 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.
21
22 Authors: Dave Camp <dave@ximian.com>
23 */
24
25 #include <config.h>
26 #include "nautilus-column-chooser.h"
27
28 #include <string.h>
29 #include <gtk/gtk.h>
30 #include <glib/gi18n.h>
31
32 #include "nautilus-column-utilities.h"
33
34 struct _NautilusColumnChooserDetails
35 {
36 GtkTreeView *view;
37 GtkListStore *store;
38
39 GtkWidget *move_up_button;
40 GtkWidget *move_down_button;
41 GtkWidget *use_default_button;
42
43 NautilusFile *file;
44 };
45
46 enum {
47 COLUMN_VISIBLE,
48 COLUMN_LABEL,
49 COLUMN_NAME,
50 COLUMN_SENSITIVE,
51 NUM_COLUMNS
52 };
53
54 enum {
55 PROP_FILE = 1,
56 NUM_PROPERTIES
57 };
58
59 enum {
60 CHANGED,
61 USE_DEFAULT,
62 LAST_SIGNAL
63 };
64 static guint signals[LAST_SIGNAL];
65
66
67 G_DEFINE_TYPE(NautilusColumnChooser, nautilus_column_chooser, GTK_TYPE_BOX);
68
69 static void nautilus_column_chooser_constructed (GObject *object);
70
71 static void
72 nautilus_column_chooser_set_property (GObject *object,
73 guint param_id,
74 const GValue *value,
75 GParamSpec *pspec)
76 {
77 NautilusColumnChooser *chooser;
78
79 chooser = NAUTILUS_COLUMN_CHOOSER (object);
80
81 switch (param_id) {
82 case PROP_FILE:
83 chooser->details->file = g_value_get_object (value);
84 break;
85 default:
86 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
87 break;
88 }
89 }
90
91 static void
92 nautilus_column_chooser_class_init (NautilusColumnChooserClass *chooser_class)
93 {
94 GObjectClass *oclass;
95
96 oclass = G_OBJECT_CLASS (chooser_class);
97
98 oclass->set_property = nautilus_column_chooser_set_property;
99 oclass->constructed = nautilus_column_chooser_constructed;
100
101 signals[CHANGED] = g_signal_new
102 ("changed",
103 G_TYPE_FROM_CLASS (chooser_class),
104 G_SIGNAL_RUN_LAST,
105 G_STRUCT_OFFSET (NautilusColumnChooserClass,
106 changed),
107 NULL, NULL,
108 g_cclosure_marshal_VOID__VOID,
109 G_TYPE_NONE, 0);
110
111 signals[USE_DEFAULT] = g_signal_new
112 ("use_default",
113 G_TYPE_FROM_CLASS (chooser_class),
114 G_SIGNAL_RUN_LAST,
115 G_STRUCT_OFFSET (NautilusColumnChooserClass,
116 use_default),
117 NULL, NULL,
118 g_cclosure_marshal_VOID__VOID,
119 G_TYPE_NONE, 0);
120
121 g_object_class_install_property (oclass,
122 PROP_FILE,
123 g_param_spec_object ("file",
124 "File",
125 "The file this column chooser is for",
126 NAUTILUS_TYPE_FILE,
127 G_PARAM_CONSTRUCT_ONLY |
128 G_PARAM_WRITABLE));
129
130 g_type_class_add_private (chooser_class, sizeof (NautilusColumnChooserDetails));
131 }
132
133 static void
134 update_buttons (NautilusColumnChooser *chooser)
135 {
136 GtkTreeSelection *selection;
137 GtkTreeIter iter;
138
139 selection = gtk_tree_view_get_selection (chooser->details->view);
140
141 if (gtk_tree_selection_get_selected (selection, NULL, &iter)) {
142 gboolean visible;
143 gboolean top;
144 gboolean bottom;
145 GtkTreePath *first;
146 GtkTreePath *path;
147
148 gtk_tree_model_get (GTK_TREE_MODEL (chooser->details->store),
149 &iter,
150 COLUMN_VISIBLE, &visible,
151 -1);
152
153 path = gtk_tree_model_get_path (GTK_TREE_MODEL (chooser->details->store),
154 &iter);
155 first = gtk_tree_path_new_first ();
156
157 top = (gtk_tree_path_compare (path, first) == 0);
158
159 gtk_tree_path_free (path);
160 gtk_tree_path_free (first);
161
162 bottom = !gtk_tree_model_iter_next (GTK_TREE_MODEL (chooser->details->store),
163 &iter);
164
165 gtk_widget_set_sensitive (chooser->details->move_up_button,
166 !top);
167 gtk_widget_set_sensitive (chooser->details->move_down_button,
168 !bottom);
169 } else {
170 gtk_widget_set_sensitive (chooser->details->move_up_button,
171 FALSE);
172 gtk_widget_set_sensitive (chooser->details->move_down_button,
173 FALSE);
174 }
175 }
176
177 static void
178 list_changed (NautilusColumnChooser *chooser)
179 {
180 update_buttons (chooser);
181 g_signal_emit (chooser, signals[CHANGED], 0);
182 }
183
184 static void
185 visible_toggled_callback (GtkCellRendererToggle *cell,
186 char *path_string,
187 gpointer user_data)
188 {
189 NautilusColumnChooser *chooser;
190 GtkTreePath *path;
191 GtkTreeIter iter;
192 gboolean visible;
193
194 chooser = NAUTILUS_COLUMN_CHOOSER (user_data);
195
196 path = gtk_tree_path_new_from_string (path_string);
197 gtk_tree_model_get_iter (GTK_TREE_MODEL (chooser->details->store),
198 &iter, path);
199 gtk_tree_model_get (GTK_TREE_MODEL (chooser->details->store),
200 &iter, COLUMN_VISIBLE, &visible, -1);
201 gtk_list_store_set (chooser->details->store,
202 &iter, COLUMN_VISIBLE, !visible, -1);
203 gtk_tree_path_free (path);
204 list_changed (chooser);
205 }
206
207 static void
208 selection_changed_callback (GtkTreeSelection *selection, gpointer user_data)
209 {
210 update_buttons (NAUTILUS_COLUMN_CHOOSER (user_data));
211 }
212
213 static void
214 row_deleted_callback (GtkTreeModel *model,
215 GtkTreePath *path,
216 gpointer user_data)
217 {
218 list_changed (NAUTILUS_COLUMN_CHOOSER (user_data));
219 }
220
221 static void
222 add_tree_view (NautilusColumnChooser *chooser)
223 {
224 GtkWidget *scrolled;
225 GtkWidget *view;
226 GtkListStore *store;
227 GtkCellRenderer *cell;
228 GtkTreeSelection *selection;
229
230 view = gtk_tree_view_new ();
231 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);
232
233 store = gtk_list_store_new (NUM_COLUMNS,
234 G_TYPE_BOOLEAN,
235 G_TYPE_STRING,
236 G_TYPE_STRING,
237 G_TYPE_BOOLEAN);
238
239 gtk_tree_view_set_model (GTK_TREE_VIEW (view),
240 GTK_TREE_MODEL (store));
241 g_object_unref (store);
242
243 gtk_tree_view_set_reorderable (GTK_TREE_VIEW (view), TRUE);
244
245 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
246 g_signal_connect (selection, "changed",
247 G_CALLBACK (selection_changed_callback), chooser);
248
249 cell = gtk_cell_renderer_toggle_new ();
250
251 g_signal_connect (G_OBJECT (cell), "toggled",
252 G_CALLBACK (visible_toggled_callback), chooser);
253
254 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
255 -1, NULL,
256 cell,
257 "active", COLUMN_VISIBLE,
258 "sensitive", COLUMN_SENSITIVE,
259 NULL);
260
261 cell = gtk_cell_renderer_text_new ();
262
263 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
264 -1, NULL,
265 cell,
266 "text", COLUMN_LABEL,
267 "sensitive", COLUMN_SENSITIVE,
268 NULL);
269
270 chooser->details->view = GTK_TREE_VIEW (view);
271 chooser->details->store = store;
272
273 gtk_widget_show (view);
274
275 scrolled = gtk_scrolled_window_new (NULL, NULL);
276 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
277 GTK_SHADOW_IN);
278 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
279 GTK_POLICY_AUTOMATIC,
280 GTK_POLICY_AUTOMATIC);
281 gtk_widget_show (GTK_WIDGET (scrolled));
282
283 gtk_container_add (GTK_CONTAINER (scrolled), view);
284 gtk_box_pack_start (GTK_BOX (chooser), scrolled, TRUE, TRUE, 0);
285 }
286
287 static void
288 move_up_clicked_callback (GtkWidget *button, gpointer user_data)
289 {
290 NautilusColumnChooser *chooser;
291 GtkTreeIter iter;
292 GtkTreeSelection *selection;
293
294 chooser = NAUTILUS_COLUMN_CHOOSER (user_data);
295
296 selection = gtk_tree_view_get_selection (chooser->details->view);
297
298 if (gtk_tree_selection_get_selected (selection, NULL, &iter)) {
299 GtkTreePath *path;
300 GtkTreeIter prev;
301
302 path = gtk_tree_model_get_path (GTK_TREE_MODEL (chooser->details->store), &iter);
303 gtk_tree_path_prev (path);
304 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (chooser->details->store), &prev, path)) {
305 gtk_list_store_move_before (chooser->details->store,
306 &iter,
307 &prev);
308 }
309 gtk_tree_path_free (path);
310 }
311
312 list_changed (chooser);
313 }
314
315 static void
316 move_down_clicked_callback (GtkWidget *button, gpointer user_data)
317 {
318 NautilusColumnChooser *chooser;
319 GtkTreeIter iter;
320 GtkTreeSelection *selection;
321
322 chooser = NAUTILUS_COLUMN_CHOOSER (user_data);
323
324 selection = gtk_tree_view_get_selection (chooser->details->view);
325
326 if (gtk_tree_selection_get_selected (selection, NULL, &iter)) {
327 GtkTreeIter next;
328
329 next = iter;
330
331 if (gtk_tree_model_iter_next (GTK_TREE_MODEL (chooser->details->store), &next)) {
332 gtk_list_store_move_after (chooser->details->store,
333 &iter,
334 &next);
335 }
336 }
337
338 list_changed (chooser);
339 }
340
341 static void
342 use_default_clicked_callback (GtkWidget *button, gpointer user_data)
343 {
344 g_signal_emit (NAUTILUS_COLUMN_CHOOSER (user_data),
345 signals[USE_DEFAULT], 0);
346 }
347
348 static GtkWidget *
349 button_new_with_mnemonic (const gchar *stockid, const gchar *str)
350 {
351 GtkWidget *image;
352 GtkWidget *button;
353
354 button = gtk_button_new_with_mnemonic (str);
355 image = gtk_image_new_from_stock (stockid, GTK_ICON_SIZE_BUTTON);
356
357 gtk_button_set_image (GTK_BUTTON (button), image);
358
359 return button;
360 }
361
362 static void
363 add_buttons (NautilusColumnChooser *chooser)
364 {
365 GtkWidget *box;
366 GtkWidget *separator;
367
368 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8);
369 gtk_widget_show (box);
370
371 chooser->details->move_up_button = button_new_with_mnemonic (GTK_STOCK_GO_UP,
372 _("Move _Up"));
373 g_signal_connect (chooser->details->move_up_button,
374 "clicked", G_CALLBACK (move_up_clicked_callback),
375 chooser);
376 gtk_widget_show_all (chooser->details->move_up_button);
377 gtk_widget_set_sensitive (chooser->details->move_up_button, FALSE);
378 gtk_box_pack_start (GTK_BOX (box), chooser->details->move_up_button,
379 FALSE, FALSE, 0);
380
381 chooser->details->move_down_button = button_new_with_mnemonic (GTK_STOCK_GO_DOWN,
382 _("Move Dow_n"));
383 g_signal_connect (chooser->details->move_down_button,
384 "clicked", G_CALLBACK (move_down_clicked_callback),
385 chooser);
386 gtk_widget_show_all (chooser->details->move_down_button);
387 gtk_widget_set_sensitive (chooser->details->move_down_button, FALSE);
388 gtk_box_pack_start (GTK_BOX (box), chooser->details->move_down_button,
389 FALSE, FALSE, 0);
390
391 separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
392 gtk_widget_show (separator);
393 gtk_box_pack_start (GTK_BOX (box), separator, FALSE, FALSE, 0);
394
395 chooser->details->use_default_button = gtk_button_new_with_mnemonic (_("Use De_fault"));
396 g_signal_connect (chooser->details->use_default_button,
397 "clicked", G_CALLBACK (use_default_clicked_callback),
398 chooser);
399 gtk_widget_show (chooser->details->use_default_button);
400 gtk_box_pack_start (GTK_BOX (box), chooser->details->use_default_button,
401 FALSE, FALSE, 0);
402
403 gtk_box_pack_start (GTK_BOX (chooser), box,
404 FALSE, FALSE, 0);
405 }
406
407 static void
408 populate_tree (NautilusColumnChooser *chooser)
409 {
410 GList *columns;
411 GList *l;
412
413 columns = nautilus_get_columns_for_file (chooser->details->file);
414
415 for (l = columns; l != NULL; l = l->next) {
416 GtkTreeIter iter;
417 NautilusColumn *column;
418 char *name;
419 char *label;
420 gboolean visible = FALSE;
421 gboolean sensitive = TRUE;
422
423 column = NAUTILUS_COLUMN (l->data);
424
425 g_object_get (G_OBJECT (column),
426 "name", &name, "label", &label,
427 NULL);
428
429 if (strcmp (name, "name") == 0) {
430 visible = TRUE;
431 sensitive = FALSE;
432 }
433
434 gtk_list_store_append (chooser->details->store, &iter);
435 gtk_list_store_set (chooser->details->store, &iter,
436 COLUMN_VISIBLE, visible,
437 COLUMN_LABEL, label,
438 COLUMN_NAME, name,
439 COLUMN_SENSITIVE, sensitive,
440 -1);
441
442 g_free (name);
443 g_free (label);
444 }
445
446 nautilus_column_list_free (columns);
447 }
448
449 static void
450 nautilus_column_chooser_constructed (GObject *object)
451 {
452 NautilusColumnChooser *chooser;
453
454 chooser = NAUTILUS_COLUMN_CHOOSER (object);
455
456 populate_tree (chooser);
457
458 g_signal_connect (chooser->details->store, "row_deleted",
459 G_CALLBACK (row_deleted_callback), chooser);
460 }
461
462 static void
463 nautilus_column_chooser_init (NautilusColumnChooser *chooser)
464 {
465 chooser->details = G_TYPE_INSTANCE_GET_PRIVATE ((chooser), NAUTILUS_TYPE_COLUMN_CHOOSER, NautilusColumnChooserDetails);
466
467 g_object_set (G_OBJECT (chooser),
468 "homogeneous", FALSE,
469 "spacing", 8,
470 "orientation", GTK_ORIENTATION_HORIZONTAL,
471 NULL);
472
473 add_tree_view (chooser);
474 add_buttons (chooser);
475 }
476
477 static void
478 set_visible_columns (NautilusColumnChooser *chooser,
479 char **visible_columns)
480 {
481 GHashTable *visible_columns_hash;
482 GtkTreeIter iter;
483 int i;
484
485 visible_columns_hash = g_hash_table_new (g_str_hash, g_str_equal);
486 /* always show the name column */
487 g_hash_table_insert (visible_columns_hash, "name", "name");
488 for (i = 0; visible_columns[i] != NULL; ++i) {
489 g_hash_table_insert (visible_columns_hash,
490 visible_columns[i],
491 visible_columns[i]);
492 }
493
494 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (chooser->details->store),
495 &iter)) {
496 do {
497 char *name;
498 gboolean visible;
499
500 gtk_tree_model_get (GTK_TREE_MODEL (chooser->details->store),
501 &iter,
502 COLUMN_NAME, &name,
503 -1);
504
505 visible = (g_hash_table_lookup (visible_columns_hash, name) != NULL);
506
507 gtk_list_store_set (chooser->details->store,
508 &iter,
509 COLUMN_VISIBLE, visible,
510 -1);
511 g_free (name);
512
513 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (chooser->details->store), &iter));
514 }
515
516 g_hash_table_destroy (visible_columns_hash);
517 }
518
519 static char **
520 get_column_names (NautilusColumnChooser *chooser, gboolean only_visible)
521 {
522 GPtrArray *ret;
523 GtkTreeIter iter;
524
525 ret = g_ptr_array_new ();
526 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (chooser->details->store),
527 &iter)) {
528 do {
529 char *name;
530 gboolean visible;
531 gtk_tree_model_get (GTK_TREE_MODEL (chooser->details->store),
532 &iter,
533 COLUMN_VISIBLE, &visible,
534 COLUMN_NAME, &name,
535 -1);
536 if (!only_visible || visible) {
537 /* give ownership to the array */
538 g_ptr_array_add (ret, name);
539 }
540
541 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (chooser->details->store), &iter));
542 }
543 g_ptr_array_add (ret, NULL);
544
545 return (char **) g_ptr_array_free (ret, FALSE);
546 }
547
548 static gboolean
549 get_column_iter (NautilusColumnChooser *chooser,
550 NautilusColumn *column,
551 GtkTreeIter *iter)
552 {
553 char *column_name;
554
555 g_object_get (NAUTILUS_COLUMN (column), "name", &column_name, NULL);
556
557 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (chooser->details->store),
558 iter)) {
559 do {
560 char *name;
561
562
563 gtk_tree_model_get (GTK_TREE_MODEL (chooser->details->store),
564 iter,
565 COLUMN_NAME, &name,
566 -1);
567 if (!strcmp (name, column_name)) {
568 g_free (column_name);
569 g_free (name);
570 return TRUE;
571 }
572
573 g_free (name);
574 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (chooser->details->store), iter));
575 }
576 g_free (column_name);
577 return FALSE;
578 }
579
580 static void
581 set_column_order (NautilusColumnChooser *chooser,
582 char **column_order)
583
584 {
585 GList *columns;
586 GList *l;
587 GtkTreePath *path;
588
589 columns = nautilus_get_columns_for_file (chooser->details->file);
590 columns = nautilus_sort_columns (columns, column_order);
591
592 g_signal_handlers_block_by_func (chooser->details->store,
593 G_CALLBACK (row_deleted_callback),
594 chooser);
595
596 path = gtk_tree_path_new_first ();
597 for (l = columns; l != NULL; l = l->next) {
598 GtkTreeIter iter;
599
600 if (get_column_iter (chooser, NAUTILUS_COLUMN (l->data), &iter)) {
601 GtkTreeIter before;
602 if (path) {
603 gtk_tree_model_get_iter (GTK_TREE_MODEL (chooser->details->store),
604 &before, path);
605 gtk_list_store_move_after (chooser->details->store,
606 &iter, &before);
607 gtk_tree_path_next (path);
608
609 } else {
610 gtk_list_store_move_after (chooser->details->store,
611 &iter, NULL);
612 }
613 }
614 }
615 gtk_tree_path_free (path);
616 g_signal_handlers_unblock_by_func (chooser->details->store,
617 G_CALLBACK (row_deleted_callback),
618 chooser);
619
620 nautilus_column_list_free (columns);
621 }
622
623 void
624 nautilus_column_chooser_set_settings (NautilusColumnChooser *chooser,
625 char **visible_columns,
626 char **column_order)
627 {
628 g_return_if_fail (NAUTILUS_IS_COLUMN_CHOOSER (chooser));
629 g_return_if_fail (visible_columns != NULL);
630 g_return_if_fail (column_order != NULL);
631
632 set_visible_columns (chooser, visible_columns);
633 set_column_order (chooser, column_order);
634
635 list_changed (chooser);
636 }
637
638 void
639 nautilus_column_chooser_get_settings (NautilusColumnChooser *chooser,
640 char ***visible_columns,
641 char ***column_order)
642 {
643 g_return_if_fail (NAUTILUS_IS_COLUMN_CHOOSER (chooser));
644 g_return_if_fail (visible_columns != NULL);
645 g_return_if_fail (column_order != NULL);
646
647 *visible_columns = get_column_names (chooser, TRUE);
648 *column_order = get_column_names (chooser, FALSE);
649 }
650
651 GtkWidget *
652 nautilus_column_chooser_new (NautilusFile *file)
653 {
654 return g_object_new (NAUTILUS_TYPE_COLUMN_CHOOSER, "file", file, NULL);
655 }