nautilus-3.6.3/libnautilus-private/nautilus-column-chooser.c

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 }