fm-list-view.c 30.6 KB
Newer Older
1 2
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */

3
/* fm-list-view.c - implementation of list view of directory.
4 5

   Copyright (C) 2000 Eazel, Inc.
Anders Carlsson's avatar
Anders Carlsson committed
6 7
   Copyright (C) 2001, 2002 Anders Carlsson <andersca@gnu.org>
   
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
   The Gnome Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The Gnome Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with the Gnome Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.

   Authors: John Sullivan <sullivan@eazel.com>
Anders Carlsson's avatar
Anders Carlsson committed
24
            Anders Carlsson <andersca@gnu.org>
25
	    David Emory Watson <dwatson@cs.ucr.edu>
26 27
*/

28
#include <config.h>
29
#include "fm-list-view.h"
30

31
#include "fm-error-reporting.h"
Anders Carlsson's avatar
Anders Carlsson committed
32 33
#include "fm-list-model.h"
#include <eel/eel-cell-renderer-pixbuf-list.h>
34
#include <gdk/gdkkeysyms.h>
Anders Carlsson's avatar
Anders Carlsson committed
35 36 37 38
#include <gtk/gtkcellrendererpixbuf.h>
#include <gtk/gtkcellrenderertext.h>
#include <gtk/gtktreeselection.h>
#include <gtk/gtktreeview.h>
Anders Carlsson's avatar
Anders Carlsson committed
39
#include <libegg/eggtreemultidnd.h>
40
#include <libgnome/gnome-i18n.h>
41
#include <libgnome/gnome-macros.h>
42
#include <libgnomevfs/gnome-vfs-utils.h>
43
#include <libnautilus-private/nautilus-directory-background.h>
44
#include <libnautilus-private/nautilus-dnd.h>
45
#include <libnautilus-private/nautilus-file-dnd.h>
46
#include <libnautilus-private/nautilus-global-preferences.h>
47
#include <libnautilus-private/nautilus-icon-dnd.h>
48
#include <libnautilus-private/nautilus-metadata.h>
49
#include <libnautilus-private/nautilus-tree-view-drag-dest.h>
50

51
struct FMListViewDetails {
Anders Carlsson's avatar
Anders Carlsson committed
52 53
	GtkTreeView *tree_view;
	FMListModel *model;
54

55
	GtkTreeViewColumn   *file_name_column;
56 57 58 59 60 61 62 63

	GtkCellRendererPixbuf *pixbuf_cell;
	GtkCellRendererText   *file_name_cell;
	GtkCellRendererText   *size_cell;
	GtkCellRendererText   *type_cell;
	GtkCellRendererText   *date_modified_cell;

	NautilusZoomLevel zoom_level;
64

65
	NautilusTreeViewDragDest *drag_dest;
Anders Carlsson's avatar
Anders Carlsson committed
66
};
67 68 69 70 71 72

/*
 * The row height should be large enough to not clip emblems.
 * Computing this would be costly, so we just choose a number
 * that works well with the set of emblems we've designed.
 */
Anders Carlsson's avatar
Anders Carlsson committed
73
#define LIST_VIEW_MINIMUM_ROW_HEIGHT	28
74

75
static int                      click_policy_auto_value;
Anders Carlsson's avatar
Anders Carlsson committed
76 77
static NautilusFileSortType	default_sort_order_auto_value;
static gboolean			default_sort_reversed_auto_value;
78
static NautilusZoomLevel        default_zoom_level_auto_value;
79

80 81 82 83
static GList *              fm_list_view_get_selection         (FMDirectoryView *view);
static void                 fm_list_view_set_zoom_level        (FMListView *view,
								NautilusZoomLevel new_level,
								gboolean always_set_level);
84 85 86
static void		    fm_list_view_scale_font_size       (FMListView *view, 
								NautilusZoomLevel new_level,
								gboolean update_size_table);
Diego González's avatar
Diego González committed
87

88 89
GNOME_CLASS_BOILERPLATE (FMListView, fm_list_view,
			 FMDirectoryView, FM_TYPE_DIRECTORY_VIEW)
90

91
static void
Anders Carlsson's avatar
Anders Carlsson committed
92
list_selection_changed_callback (GtkTreeSelection *selection, gpointer user_data)
93
{
Anders Carlsson's avatar
Anders Carlsson committed
94
	FMDirectoryView *view;
95

Anders Carlsson's avatar
Anders Carlsson committed
96
	view = FM_DIRECTORY_VIEW (user_data);
97

Anders Carlsson's avatar
Anders Carlsson committed
98
	fm_directory_view_notify_selection_changed (view);
99 100
}

101 102
/* Move these to eel? */

103
static void
104 105 106 107 108 109 110 111 112 113
tree_selection_foreach_set_boolean (GtkTreeModel *model,
				    GtkTreePath *path,
				    GtkTreeIter *iter,
				    gpointer callback_data)
{
	* (gboolean *) callback_data = TRUE;
}

static gboolean
tree_selection_not_empty (GtkTreeSelection *selection)
114
{
115
	gboolean not_empty;
116

117 118 119 120 121 122 123 124 125 126 127 128
	not_empty = FALSE;
	gtk_tree_selection_selected_foreach (selection,
					     tree_selection_foreach_set_boolean,
					     &not_empty);
	return not_empty;
}

static gboolean
tree_view_has_selection (GtkTreeView *view)
{
	return tree_selection_not_empty (gtk_tree_view_get_selection (view));
}
129

130 131 132
static gboolean
button_press_callback (GtkWidget *widget, GdkEventButton *event, gpointer callback_data)
{
Dave Camp's avatar
Dave Camp committed
133
	FMListView *view;
134 135
	GtkTreeView *tree_view;
	GtkTreePath *path;
136 137 138
	gboolean call_parent;
	GtkWidgetClass *tree_view_class;
	GList *file_list;
139

Dave Camp's avatar
Dave Camp committed
140
	view = FM_LIST_VIEW (callback_data);
141
	tree_view = GTK_TREE_VIEW (widget);
142
	tree_view_class = GTK_WIDGET_GET_CLASS (tree_view);
143 144 145 146 147

	if (event->window != gtk_tree_view_get_bin_window (tree_view)) {
		return FALSE;
	}

Dave Camp's avatar
Dave Camp committed
148 149 150 151 152
	fm_list_model_set_drag_view
		(FM_LIST_MODEL (gtk_tree_view_get_model (tree_view)),
		 tree_view,
		 event->x, event->y);

153
	call_parent = TRUE;
154 155
	if (gtk_tree_view_get_path_at_pos (tree_view, event->x, event->y,
					   &path, NULL, NULL, NULL)) {
156 157
		if ((event->button == 3 || 
		     (event->button == 1 && click_policy_auto_value == NAUTILUS_CLICK_POLICY_SINGLE))
158
		    && gtk_tree_selection_path_is_selected (gtk_tree_view_get_selection (tree_view), path)) {
159 160 161
                       /* Don't let the default code run because if
                          multiple rows are selected it will unselect
                          all but one row; but we- want the right
162 163
                          click menu or single click to apply to
                          everything that's currently selected. */
164
			call_parent = FALSE;
165
		}
166
		    
167 168 169 170 171
		gtk_tree_path_free (path);
	} else {
		/* Deselect if people click outside any row. It's OK to
		   let default code run; it won't reselect anything. */
		gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (tree_view));
172 173
	}

174 175 176 177 178 179 180 181
	/* Instead of doing this, this list view should probably be a
	 * derived widget.  I'm doing this quick hack because I'm hoping
	 * that gtktreeview will have the input modes thing in 2.4, 
	 * getting rid of this altogether.
	 * If we still need this in 2.4, we should rewrite this widget
	 * as a derived class. */
	if (call_parent) {
		tree_view_class->button_press_event (widget, event);
182 183
	}

184
	if (event->button == 3) {
Dave Camp's avatar
Dave Camp committed
185 186 187 188 189
		if (tree_view_has_selection (GTK_TREE_VIEW (widget))) {
			fm_directory_view_pop_up_selection_context_menu (FM_DIRECTORY_VIEW (view), (GdkEventButton *) event);
		} else {
			fm_directory_view_pop_up_background_context_menu (FM_DIRECTORY_VIEW (view), (GdkEventButton *) event);
		}
190 191 192 193 194 195 196 197 198 199 200
	} else if (event->button == 1) {
		if ((event->type == GDK_BUTTON_PRESS 
		     && click_policy_auto_value == NAUTILUS_CLICK_POLICY_SINGLE
		     && !(event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)))
		    || (event->type == GDK_2BUTTON_PRESS 
			&& click_policy_auto_value == NAUTILUS_CLICK_POLICY_DOUBLE)) {
			file_list = fm_list_view_get_selection (FM_DIRECTORY_VIEW (view));
			fm_directory_view_activate_files (FM_DIRECTORY_VIEW (view),
							  file_list);
			nautilus_file_list_free (file_list);
		}
Dave Camp's avatar
Dave Camp committed
201
	}
202 203 204 205
	
	/* We chained to the default handler in this method, so never
	 * let the default handler run */ 
	return TRUE;
206 207
}

208 209 210 211 212 213 214
static gboolean
key_press_callback (GtkWidget *widget, GdkEventKey *event, gpointer callback_data)
{
	FMDirectoryView *view;
	GList *file_list;

	view = FM_DIRECTORY_VIEW (callback_data);
215 216 217 218
	
	if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget))) {
		return FALSE;
	}
219 220

	switch (event->keyval) {
Dave Camp's avatar
Dave Camp committed
221
	case GDK_space:
222 223 224 225 226 227 228 229 230 231 232 233 234 235
	case GDK_Return:
	case GDK_KP_Enter:
		file_list = fm_list_view_get_selection (view);
		fm_directory_view_activate_files (view, file_list);
		nautilus_file_list_free (file_list);
		return TRUE;

	default:
		break;
	}

	return FALSE;
}

Diego González's avatar
Diego González committed
236
static void
237 238
sort_column_changed_callback (GtkTreeSortable *sortable, 
			      FMListView *view)
Diego González's avatar
Diego González committed
239 240 241
{
	NautilusFile *file;
	gint sort_column_id;
242 243 244
	GtkSortType reversed;
	char *sort_attr, *default_sort_attr;
	char *reversed_attr, *default_reversed_attr;
Diego González's avatar
Diego González committed
245 246 247

	file = fm_directory_view_get_directory_as_file (FM_DIRECTORY_VIEW (view));

248
	gtk_tree_sortable_get_sort_column_id (sortable, &sort_column_id, &reversed);
Diego González's avatar
Diego González committed
249

250 251 252 253 254 255 256
	sort_attr = fm_list_model_get_attribute_from_sort_column_id (sort_column_id);
	sort_column_id = fm_list_model_get_sort_column_id_from_sort_type (default_sort_order_auto_value);
	default_sort_attr = fm_list_model_get_attribute_from_sort_column_id (sort_column_id);
	nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_COLUMN,
				    default_sort_attr, sort_attr);
	g_free (default_sort_attr);
	g_free (sort_attr);
Diego González's avatar
Diego González committed
257

258 259 260 261
	default_reversed_attr = (default_sort_reversed_auto_value ? "true" : "false");
	reversed_attr = (reversed ? "true" : "false");
	nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_REVERSED,
				    default_reversed_attr, reversed_attr);
Diego González's avatar
Diego González committed
262 263
}

264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
static void
cell_renderer_edited (GtkCellRendererText *cell,
		      const char          *path_str,
		      const char          *new_text,
		      FMListView          *view)
{
	GtkTreePath *path;
	NautilusFile *file;
	GtkTreeIter iter;
	
	path = gtk_tree_path_new_from_string (path_str);

	gtk_tree_model_get_iter (GTK_TREE_MODEL (view->details->model),
				 &iter, path);

	gtk_tree_path_free (path);
	
	gtk_tree_model_get (GTK_TREE_MODEL (view->details->model),
			    &iter,
			    FM_LIST_MODEL_FILE_COLUMN, &file,
			    -1);

	fm_rename_file (file, new_text);
	
	nautilus_file_unref (file);
289 290 291 292 293 294

	/*We're done editing - make the filename-cells readonly again.*/
	g_object_set (G_OBJECT (view->details->file_name_cell),
		      "editable", FALSE,
		      NULL);

295 296
}

297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
static char *
get_root_uri_callback (NautilusTreeViewDragDest *dest,
		       gpointer user_data)
{
	FMListView *view;
	
	view = FM_LIST_VIEW (user_data);

	return fm_directory_view_get_uri (FM_DIRECTORY_VIEW (view));
}

static NautilusFile *
get_file_for_path_callback (NautilusTreeViewDragDest *dest,
			    GtkTreePath *path,
			    gpointer user_data)
{
	FMListView *view;
	GtkTreeIter iter;
	NautilusFile *file;
	
	view = FM_LIST_VIEW (user_data);

	file = NULL;

	if (gtk_tree_model_get_iter (GTK_TREE_MODEL (view->details->model),
				     &iter, path)) {
		gtk_tree_model_get (GTK_TREE_MODEL (view->details->model),
				    &iter,
				    FM_LIST_MODEL_FILE_COLUMN,
				    &file,
				    -1);
	}

	return file;
}

static void
move_copy_items_callback (NautilusTreeViewDragDest *dest,
			  const GList *item_uris,
			  const char *target_uri,
			  guint action,
			  int x, 
			  int y,
			  gpointer user_data)

{
	fm_directory_view_move_copy_items (item_uris,
					   NULL,
					   target_uri,
					   action,
					   x, y,
					   FM_DIRECTORY_VIEW (user_data));
}

Anders Carlsson's avatar
Anders Carlsson committed
351 352
static void
create_and_set_up_tree_view (FMListView *view)
353
{
Anders Carlsson's avatar
Anders Carlsson committed
354 355
	GtkCellRenderer *cell;
	GtkTreeViewColumn *column;
Dave Camp's avatar
Dave Camp committed
356 357 358
	GtkTargetEntry *drag_types;
	int num_drag_types;	

Anders Carlsson's avatar
Anders Carlsson committed
359
	view->details->tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
Dave Camp's avatar
Dave Camp committed
360 361 362 363 364 365 366 367 368 369 370 371

	fm_list_model_get_drag_types (&drag_types, &num_drag_types);

	egg_tree_multi_drag_add_drag_support (view->details->tree_view);

	gtk_tree_view_enable_model_drag_source
		(view->details->tree_view,
		 GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
		 drag_types,
		 num_drag_types,
		 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK);

372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
	view->details->drag_dest = 
		nautilus_tree_view_drag_dest_new (view->details->tree_view);

	g_signal_connect_object (view->details->drag_dest,
				 "get_root_uri",
				 G_CALLBACK (get_root_uri_callback),
				 view, 0);
	g_signal_connect_object (view->details->drag_dest,
				 "get_file_for_path",
				 G_CALLBACK (get_file_for_path_callback),
				 view, 0);
	g_signal_connect_object (view->details->drag_dest,
				 "move_copy_items",
				 G_CALLBACK (move_copy_items_callback),
				 view, 0);
387

388 389 390 391 392 393
	g_signal_connect_object (gtk_tree_view_get_selection (view->details->tree_view),
				 "changed",
				 G_CALLBACK (list_selection_changed_callback), view, 0);

	g_signal_connect_object (view->details->tree_view, "button_press_event",
				 G_CALLBACK (button_press_callback), view, 0);
394 395
	g_signal_connect_object (view->details->tree_view, "key_press_event",
				 G_CALLBACK (key_press_callback), view, 0);
396
	
397
	view->details->model = g_object_new (FM_TYPE_LIST_MODEL, NULL);
Anders Carlsson's avatar
Anders Carlsson committed
398
	gtk_tree_view_set_model (view->details->tree_view, GTK_TREE_MODEL (view->details->model));
Diego González's avatar
Diego González committed
399 400

	g_signal_connect_object (view->details->model, "sort_column_changed",
401
				 G_CALLBACK (sort_column_changed_callback), view, 0);
Diego González's avatar
Diego González committed
402

Anders Carlsson's avatar
Anders Carlsson committed
403 404
	gtk_tree_selection_set_mode (gtk_tree_view_get_selection (view->details->tree_view), GTK_SELECTION_MULTIPLE);
	gtk_tree_view_set_rules_hint (view->details->tree_view, TRUE);
405

Anders Carlsson's avatar
Anders Carlsson committed
406 407
	/* Create the file name column */
	cell = gtk_cell_renderer_pixbuf_new ();
408
	view->details->pixbuf_cell = (GtkCellRendererPixbuf *)cell;
Anders Carlsson's avatar
Anders Carlsson committed
409
	
410 411 412
	view->details->file_name_column = gtk_tree_view_column_new ();
	gtk_tree_view_column_set_sort_column_id (view->details->file_name_column, FM_LIST_MODEL_NAME_COLUMN);
	gtk_tree_view_column_set_title (view->details->file_name_column, _("File name"));
413
	gtk_tree_view_column_set_resizable (view->details->file_name_column, TRUE);
414 415

	gtk_tree_view_column_pack_start (view->details->file_name_column, cell, FALSE);
416 417 418 419
	gtk_tree_view_column_set_attributes (view->details->file_name_column,
					     cell,
					     "pixbuf", FM_LIST_MODEL_SMALLEST_ICON_COLUMN,
					     NULL);
420

Anders Carlsson's avatar
Anders Carlsson committed
421
	cell = gtk_cell_renderer_text_new ();
422
	view->details->file_name_cell = (GtkCellRendererText *)cell;
423
	g_signal_connect (cell, "edited", G_CALLBACK (cell_renderer_edited), view);
424

425 426
	gtk_tree_view_column_pack_start (view->details->file_name_column, cell, TRUE);
	gtk_tree_view_column_set_attributes (view->details->file_name_column, cell,
427
					     "text", FM_LIST_MODEL_NAME_COLUMN, NULL);
428
	gtk_tree_view_append_column (view->details->tree_view, view->details->file_name_column);
429

Anders Carlsson's avatar
Anders Carlsson committed
430 431
	/* Create the size column */
	cell = gtk_cell_renderer_text_new ();
432
	view->details->size_cell = (GtkCellRendererText *)cell;
Anders Carlsson's avatar
Anders Carlsson committed
433 434 435 436 437 438 439
	g_object_set (G_OBJECT (cell),
		      "xalign", 1.0,
		      NULL);
	column = gtk_tree_view_column_new_with_attributes (_("Size"),
							   cell,
							   "text", FM_LIST_MODEL_SIZE_COLUMN,
							   NULL);
440
	gtk_tree_view_column_set_resizable (column, TRUE);
Anders Carlsson's avatar
Anders Carlsson committed
441 442
	gtk_tree_view_column_set_sort_column_id (column, FM_LIST_MODEL_SIZE_COLUMN);
	gtk_tree_view_append_column (view->details->tree_view, column);
443

Anders Carlsson's avatar
Anders Carlsson committed
444 445
	/* Create the type column */
	cell = gtk_cell_renderer_text_new ();
446
	view->details->type_cell = (GtkCellRendererText *)cell;
Anders Carlsson's avatar
Anders Carlsson committed
447 448 449 450
	column = gtk_tree_view_column_new_with_attributes (_("Type"),
							   cell,
							   "text", FM_LIST_MODEL_TYPE_COLUMN,
							   NULL);
451
	gtk_tree_view_column_set_resizable (column, TRUE);
Anders Carlsson's avatar
Anders Carlsson committed
452 453
	gtk_tree_view_column_set_sort_column_id (column, FM_LIST_MODEL_TYPE_COLUMN);	
	gtk_tree_view_append_column (view->details->tree_view, column);
454

Anders Carlsson's avatar
Anders Carlsson committed
455 456
	/* Create the date modified column */
	cell = gtk_cell_renderer_text_new ();
457
	view->details->date_modified_cell = (GtkCellRendererText *)cell;
Anders Carlsson's avatar
Anders Carlsson committed
458 459 460 461
	column = gtk_tree_view_column_new_with_attributes (_("Date Modified"),
							   cell,
							   "text", FM_LIST_MODEL_DATE_MODIFIED_COLUMN,
							   NULL);
462
	gtk_tree_view_column_set_resizable (column, TRUE);
Anders Carlsson's avatar
Anders Carlsson committed
463 464
	gtk_tree_view_column_set_sort_column_id (column, FM_LIST_MODEL_DATE_MODIFIED_COLUMN);
	gtk_tree_view_append_column (view->details->tree_view, column);
465

Anders Carlsson's avatar
Anders Carlsson committed
466 467
	gtk_widget_show (GTK_WIDGET (view->details->tree_view));
	gtk_container_add (GTK_CONTAINER (view), GTK_WIDGET (view->details->tree_view));
468 469 470
}

static void
Anders Carlsson's avatar
Anders Carlsson committed
471
fm_list_view_add_file (FMDirectoryView *view, NautilusFile *file)
472
{
Anders Carlsson's avatar
Anders Carlsson committed
473
	fm_list_model_add_file (FM_LIST_VIEW (view)->details->model, file);
474 475 476
}

static void
Anders Carlsson's avatar
Anders Carlsson committed
477
set_sort_order_from_metadata_and_preferences (FMListView *list_view)
478
{
Anders Carlsson's avatar
Anders Carlsson committed
479 480 481 482 483 484
	char *sort_attribute;
	int sort_column_id;
	NautilusFile *file;
	gboolean sort_reversed;
	
	file = fm_directory_view_get_directory_as_file (FM_DIRECTORY_VIEW (list_view));
485 486 487 488 489
	sort_attribute = nautilus_file_get_metadata (file,
						     NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_COLUMN,
						     NULL);
	sort_column_id = fm_list_model_get_sort_column_id_from_attribute (sort_attribute);
	g_free (sort_attribute);
490

491
	if (sort_column_id == -1) {
492
		sort_column_id = fm_list_model_get_sort_column_id_from_sort_type (default_sort_order_auto_value);
Anders Carlsson's avatar
Anders Carlsson committed
493
	}
494

Anders Carlsson's avatar
Anders Carlsson committed
495
	sort_reversed = nautilus_file_get_boolean_metadata (file,
496 497
							    NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_REVERSED,
							    default_sort_reversed_auto_value);
498

Anders Carlsson's avatar
Anders Carlsson committed
499 500
	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list_view->details->model),
					      sort_column_id,
501
					      sort_reversed ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING);
502 503
}

504 505 506 507 508 509 510 511 512 513
static gboolean
list_view_changed_foreach (GtkTreeModel *model,
              		   GtkTreePath  *path,
			   GtkTreeIter  *iter,
			   gpointer      data)
{
	gtk_tree_model_row_changed (model, path, iter);
	return FALSE;
}

514 515 516 517 518 519 520 521 522 523 524 525 526 527
static NautilusZoomLevel
get_default_zoom_level (void) {
	NautilusZoomLevel default_zoom_level;

	default_zoom_level = default_zoom_level_auto_value;

	if (default_zoom_level <  NAUTILUS_ZOOM_LEVEL_SMALLEST
	    || NAUTILUS_ZOOM_LEVEL_LARGEST < default_zoom_level) {
		default_zoom_level = NAUTILUS_ZOOM_LEVEL_SMALL;
	}

	return default_zoom_level;
}

528 529 530 531 532 533 534 535 536 537
static void
set_zoom_level_from_metadata_and_preferences (FMListView *list_view)
{
	NautilusFile *file;
	int level;

	if (fm_directory_view_supports_zooming (FM_DIRECTORY_VIEW (list_view))) {
		file = fm_directory_view_get_directory_as_file (FM_DIRECTORY_VIEW (list_view));
		level = nautilus_file_get_integer_metadata (file,
							    NAUTILUS_METADATA_KEY_LIST_VIEW_ZOOM_LEVEL, 
538
							    get_default_zoom_level ());
539
		fm_list_view_set_zoom_level (list_view, level, TRUE);
540 541 542 543 544 545 546
		
		/* reset the font size table for the new default zoom level */
		fm_list_view_scale_font_size (list_view, level, TRUE);
		
		/* updated the rows after updating the font size */
		gtk_tree_model_foreach (GTK_TREE_MODEL (list_view->details->model),
					list_view_changed_foreach, NULL);
547 548 549
	}
}

550
static void
Anders Carlsson's avatar
Anders Carlsson committed
551
fm_list_view_begin_loading (FMDirectoryView *view)
552
{
Anders Carlsson's avatar
Anders Carlsson committed
553
	FMListView *list_view;
554

Anders Carlsson's avatar
Anders Carlsson committed
555
	list_view = FM_LIST_VIEW (view);
556

Anders Carlsson's avatar
Anders Carlsson committed
557 558
	nautilus_connect_background_to_file_metadata (GTK_WIDGET (list_view->details->tree_view),
						      fm_directory_view_get_directory_as_file (view));
559

Anders Carlsson's avatar
Anders Carlsson committed
560
	set_sort_order_from_metadata_and_preferences (list_view);
561
	set_zoom_level_from_metadata_and_preferences (list_view);
562 563 564
}

static void
Anders Carlsson's avatar
Anders Carlsson committed
565
fm_list_view_clear (FMDirectoryView *view)
566
{
Anders Carlsson's avatar
Anders Carlsson committed
567
	FMListView *list_view;
568

Anders Carlsson's avatar
Anders Carlsson committed
569
	list_view = FM_LIST_VIEW (view);
570
	
571 572 573
	if (list_view->details->model != NULL) {
		fm_list_model_clear (list_view->details->model);
	}
574
}
575

576
static void
Anders Carlsson's avatar
Anders Carlsson committed
577
fm_list_view_file_changed (FMDirectoryView *view, NautilusFile *file)
578
{
Anders Carlsson's avatar
Anders Carlsson committed
579
	fm_list_model_file_changed (FM_LIST_VIEW (view)->details->model, file);
580 581
}

Anders Carlsson's avatar
Anders Carlsson committed
582 583
static GtkWidget *
fm_list_view_get_background_widget (FMDirectoryView *view)
Mathieu Lacage's avatar
Mathieu Lacage committed
584
{
Anders Carlsson's avatar
Anders Carlsson committed
585
	return GTK_WIDGET (view);
Mathieu Lacage's avatar
Mathieu Lacage committed
586 587
}

Anders Carlsson's avatar
Anders Carlsson committed
588 589
static void
fm_list_view_get_selection_foreach_func (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
590
{
Anders Carlsson's avatar
Anders Carlsson committed
591
	GList **list;
592 593
	NautilusFile *file;
	
Anders Carlsson's avatar
Anders Carlsson committed
594
	list = data;
595

Anders Carlsson's avatar
Anders Carlsson committed
596 597 598
	gtk_tree_model_get (model, iter,
			    FM_LIST_MODEL_FILE_COLUMN, &file,
			    -1);
599

Anders Carlsson's avatar
Anders Carlsson committed
600 601
	nautilus_file_ref (file);
	(* list) = g_list_prepend ((* list), file);
602 603
}

Anders Carlsson's avatar
Anders Carlsson committed
604 605
static GList *
fm_list_view_get_selection (FMDirectoryView *view)
606
{
Anders Carlsson's avatar
Anders Carlsson committed
607
	GList *list;
608

Anders Carlsson's avatar
Anders Carlsson committed
609
	list = NULL;
610

Anders Carlsson's avatar
Anders Carlsson committed
611 612
	gtk_tree_selection_selected_foreach (gtk_tree_view_get_selection (FM_LIST_VIEW (view)->details->tree_view),
					     fm_list_view_get_selection_foreach_func, &list);
613

Anders Carlsson's avatar
Anders Carlsson committed
614 615
	return list;
}
616 617

static gboolean
Anders Carlsson's avatar
Anders Carlsson committed
618
fm_list_view_is_empty (FMDirectoryView *view)
619
{
Anders Carlsson's avatar
Anders Carlsson committed
620
	return fm_list_model_is_empty (FM_LIST_VIEW (view)->details->model);
621 622 623
}

static void
Anders Carlsson's avatar
Anders Carlsson committed
624
fm_list_view_remove_file (FMDirectoryView *view, NautilusFile *file)
625
{
Anders Carlsson's avatar
Anders Carlsson committed
626
	fm_list_model_remove_file (FM_LIST_VIEW (view)->details->model, file);
627 628 629
}

static void
Anders Carlsson's avatar
Anders Carlsson committed
630
fm_list_view_set_selection (FMDirectoryView *view, GList *selection)
631
{
Anders Carlsson's avatar
Anders Carlsson committed
632
	FMListView *list_view;
Darin Adler's avatar
Darin Adler committed
633 634 635
	GtkTreeSelection *tree_selection;
	GList *node;
	GtkTreeIter iter;
Anders Carlsson's avatar
Anders Carlsson committed
636 637 638
	NautilusFile *file;
	
	list_view = FM_LIST_VIEW (view);
Darin Adler's avatar
Darin Adler committed
639
	tree_selection = gtk_tree_view_get_selection (list_view->details->tree_view);
640

Darin Adler's avatar
Darin Adler committed
641 642 643
	gtk_tree_selection_unselect_all (tree_selection);
	for (node = selection; node != NULL; node = node->next) {
		file = node->data;
644
		if (fm_list_model_get_tree_iter_from_file (list_view->details->model, file, &iter)) {
Darin Adler's avatar
Darin Adler committed
645
			gtk_tree_selection_select_iter (tree_selection, &iter);
Anders Carlsson's avatar
Anders Carlsson committed
646 647
		}
	}
648 649
}

Darin Adler's avatar
Darin Adler committed
650 651 652 653 654 655
static void
fm_list_view_select_all (FMDirectoryView *view)
{
	gtk_tree_selection_select_all (gtk_tree_view_get_selection (FM_LIST_VIEW (view)->details->tree_view));
}

656 657 658 659
/* Reset sort criteria and zoom level to match defaults */
static void
fm_list_view_reset_to_defaults (FMDirectoryView *view)
{
660 661 662
	NautilusFile *file;

	file = fm_directory_view_get_directory_as_file (view);
663

664 665
	nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_COLUMN, NULL, NULL);
	nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_REVERSED, NULL, NULL);
666
	nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_LIST_VIEW_ZOOM_LEVEL, NULL, NULL);
667 668 669 670

	gtk_tree_sortable_set_sort_column_id
		(GTK_TREE_SORTABLE (FM_LIST_VIEW (view)->details->model),
		 fm_list_model_get_sort_column_id_from_sort_type (default_sort_order_auto_value),
671
		 default_sort_reversed_auto_value ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING);
672

673
	fm_list_view_set_zoom_level (FM_LIST_VIEW (view), get_default_zoom_level (), FALSE);
674 675
}

676 677
static void
fm_list_view_scale_font_size (FMListView *view, 
678 679
			      NautilusZoomLevel new_level,
			      gboolean update_size_table)
680 681
{
	static gboolean first_time = TRUE;
682 683 684 685 686
	static double pango_scale[7];
	int default_zoom_level, i;

	g_return_if_fail (new_level >= NAUTILUS_ZOOM_LEVEL_SMALLEST &&
			  new_level <= NAUTILUS_ZOOM_LEVEL_LARGEST);
687 688 689

	if (update_size_table || first_time) {
		first_time = FALSE;
690 691 692 693 694 695 696 697 698

		default_zoom_level = get_default_zoom_level ();

		pango_scale[default_zoom_level] = PANGO_SCALE_MEDIUM;
		for (i = default_zoom_level; i > NAUTILUS_ZOOM_LEVEL_SMALLEST; i--) {
			pango_scale[i - 1] = (1 / 1.2) * pango_scale[i];
		}
		for (i = default_zoom_level; i < NAUTILUS_ZOOM_LEVEL_LARGEST; i++) {
			pango_scale[i + 1] = 1.2 * pango_scale[i];
699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715
		}
	}
					 
	g_object_set (G_OBJECT (view->details->file_name_cell),
		      "scale", pango_scale[new_level],
		      NULL);
	g_object_set (G_OBJECT (view->details->size_cell),
		      "scale", pango_scale[new_level],
		      NULL);
	g_object_set (G_OBJECT (view->details->type_cell),
		      "scale", pango_scale[new_level],
		      NULL);
	g_object_set (G_OBJECT (view->details->date_modified_cell),
		      "scale", pango_scale[new_level],
		      NULL);
}

716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740
static void
fm_list_view_set_zoom_level (FMListView *view,
			     NautilusZoomLevel new_level,
			     gboolean always_set_level)
{
	int icon_size;
	int column;

	g_return_if_fail (FM_IS_LIST_VIEW (view));
	g_return_if_fail (new_level >= NAUTILUS_ZOOM_LEVEL_SMALLEST &&
			  new_level <= NAUTILUS_ZOOM_LEVEL_LARGEST);

	if (view->details->zoom_level == new_level) {
		if (always_set_level) {
			fm_directory_view_set_zoom_level (FM_DIRECTORY_VIEW(view), new_level);
		}
		return;
	}

	view->details->zoom_level = new_level;
	fm_directory_view_set_zoom_level (FM_DIRECTORY_VIEW(view), new_level);

	nautilus_file_set_integer_metadata
		(fm_directory_view_get_directory_as_file (FM_DIRECTORY_VIEW (view)), 
		 NAUTILUS_METADATA_KEY_LIST_VIEW_ZOOM_LEVEL, 
741
		 get_default_zoom_level (),
742 743 744 745 746 747 748 749 750 751
		 new_level);

	/* Select correctly scaled icons. */
	column = fm_list_model_get_column_id_from_zoom_level (new_level);
	gtk_tree_view_column_set_attributes (view->details->file_name_column,
					     GTK_CELL_RENDERER (view->details->pixbuf_cell),
					     "pixbuf", column,
					     NULL);

	/* Scale text. */
752
	fm_list_view_scale_font_size (view, new_level, FALSE);
753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799

	/* Make all rows the same size. */
	icon_size = nautilus_get_icon_size_for_zoom_level (new_level);
	gtk_cell_renderer_set_fixed_size (GTK_CELL_RENDERER (view->details->pixbuf_cell),
					  -1, icon_size);

	fm_directory_view_update_menus (FM_DIRECTORY_VIEW (view));
}

static void
fm_list_view_bump_zoom_level (FMDirectoryView *view, int zoom_increment)
{
	FMListView *list_view;
	NautilusZoomLevel new_level;

	g_return_if_fail (FM_IS_LIST_VIEW (view));

	list_view = FM_LIST_VIEW (view);
	new_level = list_view->details->zoom_level + zoom_increment;

	if (new_level >= NAUTILUS_ZOOM_LEVEL_SMALLEST &&
	    new_level <= NAUTILUS_ZOOM_LEVEL_LARGEST) {
		fm_list_view_set_zoom_level (list_view, new_level, FALSE);
	}
}

static void
fm_list_view_zoom_to_level (FMDirectoryView *view, int zoom_level)
{
	FMListView *list_view;

	g_return_if_fail (FM_IS_LIST_VIEW (view));

	list_view = FM_LIST_VIEW (view);

	fm_list_view_set_zoom_level (list_view, zoom_level, FALSE);
}

static void
fm_list_view_restore_default_zoom_level (FMDirectoryView *view)
{
	FMListView *list_view;

	g_return_if_fail (FM_IS_LIST_VIEW (view));

	list_view = FM_LIST_VIEW (view);

800
	fm_list_view_set_zoom_level (list_view, get_default_zoom_level (), FALSE);
801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818
}

static gboolean 
fm_list_view_can_zoom_in (FMDirectoryView *view) 
{
	g_return_val_if_fail (FM_IS_LIST_VIEW (view), FALSE);

	return FM_LIST_VIEW (view)->details->zoom_level	< NAUTILUS_ZOOM_LEVEL_LARGEST;
}

static gboolean 
fm_list_view_can_zoom_out (FMDirectoryView *view) 
{
	g_return_val_if_fail (FM_IS_LIST_VIEW (view), FALSE);

	return FM_LIST_VIEW (view)->details->zoom_level > NAUTILUS_ZOOM_LEVEL_SMALLEST;
}

819 820 821 822 823 824 825 826 827 828 829 830 831 832
static void
fm_list_view_start_renaming_file (FMDirectoryView *view, NautilusFile *file)
{
	FMListView *list_view;
	GtkTreeIter iter;
	GtkTreePath *path;
	
	list_view = FM_LIST_VIEW (view);
	
	if (!fm_list_model_get_tree_iter_from_file (list_view->details->model, file, &iter)) {
		return;
	}

	path = gtk_tree_model_get_path (GTK_TREE_MODEL (list_view->details->model), &iter);
833 834 835 836 837

	/*Make filename-cells editable.*/
	g_object_set (G_OBJECT (list_view->details->file_name_cell),
		      "editable", TRUE,
		      NULL);
838 839 840 841 842
	
	gtk_tree_view_set_cursor (list_view->details->tree_view,
				  path,
				  list_view->details->file_name_column,
				  TRUE);
Dave Camp's avatar
Dave Camp committed
843 844

	gtk_tree_path_free (path);
845 846
}

847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862
static void
click_policy_changed_callback (gpointer callback_data)
{
	FMListView *view;

	view = FM_LIST_VIEW (callback_data);

	if (click_policy_auto_value == NAUTILUS_CLICK_POLICY_SINGLE) {
		g_object_set (G_OBJECT (view->details->file_name_cell),
			      "underline", PANGO_UNDERLINE_SINGLE,
			      NULL);
	} else {
		g_object_set (G_OBJECT (view->details->file_name_cell),
			      "underline", PANGO_UNDERLINE_NONE,
			      NULL);
	}
863 864 865

	gtk_tree_model_foreach (GTK_TREE_MODEL (view->details->model),
				list_view_changed_foreach, NULL);
866 867 868 869 870 871 872 873 874 875 876 877
}

static void
default_sort_order_changed_callback (gpointer callback_data)
{
	FMListView *list_view;

	list_view = FM_LIST_VIEW (callback_data);

	set_sort_order_from_metadata_and_preferences (list_view);
}

878 879 880 881 882 883 884 885 886 887
static void
default_zoom_level_changed_callback (gpointer callback_data)
{
	FMListView *list_view;

	list_view = FM_LIST_VIEW (callback_data);

	set_zoom_level_from_metadata_and_preferences (list_view);
}

888
static void
Anders Carlsson's avatar
Anders Carlsson committed
889
fm_list_view_sort_directories_first_changed (FMDirectoryView *view)
890
{
Anders Carlsson's avatar
Anders Carlsson committed
891
	FMListView *list_view;
892

Anders Carlsson's avatar
Anders Carlsson committed
893
	list_view = FM_LIST_VIEW (view);
Ramiro Estrugo's avatar
Ramiro Estrugo committed
894

Anders Carlsson's avatar
Anders Carlsson committed
895 896
	fm_list_model_set_should_sort_directories_first (list_view->details->model,
							 fm_directory_view_should_sort_directories_first (view));
Ramiro Estrugo's avatar
Ramiro Estrugo committed
897 898
}

899 900 901 902 903 904 905 906 907 908 909 910
static void
fm_list_view_dispose (GObject *object)
{
	FMListView *list_view;

	list_view = FM_LIST_VIEW (object);

	if (list_view->details->model) {
		g_object_unref (list_view->details->model);
		list_view->details->model = NULL;
	}

911 912 913 914 915
	if (list_view->details->drag_dest) {
		g_object_unref (list_view->details->drag_dest);
		list_view->details->drag_dest = NULL;
	}

916 917 918
	G_OBJECT_CLASS (parent_class)->dispose (object);
}

Ramiro Estrugo's avatar
Ramiro Estrugo committed
919
static void
Anders Carlsson's avatar
Anders Carlsson committed
920
fm_list_view_finalize (GObject *object)
Ramiro Estrugo's avatar
Ramiro Estrugo committed
921
{
922 923 924 925 926 927 928
	FMListView *list_view;

	list_view = FM_LIST_VIEW (object);

	g_free (list_view->details);

	G_OBJECT_CLASS (parent_class)->finalize (object);
929 930
}

Alexander Larsson's avatar
Alexander Larsson committed
931 932 933 934 935 936 937 938 939 940 941 942
static void
fm_list_view_emblems_changed (FMDirectoryView *directory_view)
{
	g_assert (FM_IS_LIST_VIEW (directory_view));

#if GNOME2_CONVERSION_COMPLETE
	/* FIXME: This needs to update the emblems of the icons, since
	 * relative emblems may have changed.
	 */
#endif
}

Anders Carlsson's avatar
Anders Carlsson committed
943
static void
944
fm_list_view_class_init (FMListViewClass *class)
945
{
Anders Carlsson's avatar
Anders Carlsson committed
946
	FMDirectoryViewClass *fm_directory_view_class;
947

948 949
	fm_directory_view_class = FM_DIRECTORY_VIEW_CLASS (class);

950
	G_OBJECT_CLASS (class)->dispose = fm_list_view_dispose;
951
	G_OBJECT_CLASS (class)->finalize = fm_list_view_finalize;
952

Anders Carlsson's avatar
Anders Carlsson committed
953 954
	fm_directory_view_class->add_file = fm_list_view_add_file;
	fm_directory_view_class->begin_loading = fm_list_view_begin_loading;
955 956 957 958
	fm_directory_view_class->bump_zoom_level = fm_list_view_bump_zoom_level;
	fm_directory_view_class->zoom_to_level = fm_list_view_zoom_to_level;
	fm_directory_view_class->can_zoom_in = fm_list_view_can_zoom_in;
	fm_directory_view_class->can_zoom_out = fm_list_view_can_zoom_out;
Anders Carlsson's avatar
Anders Carlsson committed
959 960 961 962 963
	fm_directory_view_class->clear = fm_list_view_clear;
	fm_directory_view_class->file_changed = fm_list_view_file_changed;
	fm_directory_view_class->get_background_widget = fm_list_view_get_background_widget;
	fm_directory_view_class->get_selection = fm_list_view_get_selection;
	fm_directory_view_class->is_empty = fm_list_view_is_empty;
964
	fm_directory_view_class->reset_to_defaults = fm_list_view_reset_to_defaults;
965
	fm_directory_view_class->restore_default_zoom_level = fm_list_view_restore_default_zoom_level;
Anders Carlsson's avatar
Anders Carlsson committed
966
	fm_directory_view_class->remove_file = fm_list_view_remove_file;
Darin Adler's avatar
Darin Adler committed
967
	fm_directory_view_class->select_all = fm_list_view_select_all;
Anders Carlsson's avatar
Anders Carlsson committed
968
	fm_directory_view_class->set_selection = fm_list_view_set_selection;
Alexander Larsson's avatar
Alexander Larsson committed
969
        fm_directory_view_class->emblems_changed = fm_list_view_emblems_changed;
Anders Carlsson's avatar
Anders Carlsson committed
970
	fm_directory_view_class->sort_directories_first_changed = fm_list_view_sort_directories_first_changed;
971
	fm_directory_view_class->start_renaming_file = fm_list_view_start_renaming_file;
972

973 974
	eel_preferences_add_auto_enum (NAUTILUS_PREFERENCES_CLICK_POLICY,
				       &click_policy_auto_value);
975 976
	eel_preferences_add_auto_enum (NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_SORT_ORDER,
				       (int *) &default_sort_order_auto_value);
Anders Carlsson's avatar
Anders Carlsson committed
977 978
	eel_preferences_add_auto_boolean (NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_SORT_IN_REVERSE_ORDER,
					  &default_sort_reversed_auto_value);
979 980
	eel_preferences_add_auto_enum (NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_ZOOM_LEVEL,
				       (int *) &default_zoom_level_auto_value);
981
}
982 983 984 985 986 987 988 989

static void
fm_list_view_instance_init (FMListView *list_view)
{
	list_view->details = g_new0 (FMListViewDetails, 1);

	create_and_set_up_tree_view (list_view);

990 991 992
	eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_CLICK_POLICY,
						  click_policy_changed_callback,
						  list_view, G_OBJECT (list_view));
993 994 995 996 997 998 999 1000 1001
	eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_SORT_ORDER,
						  default_sort_order_changed_callback,
						  list_view, G_OBJECT (list_view));
	eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_SORT_IN_REVERSE_ORDER,
						  default_sort_order_changed_callback,
						  list_view, G_OBJECT (list_view));
	eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_ZOOM_LEVEL,
						  default_zoom_level_changed_callback,
						  list_view, G_OBJECT (list_view));
1002 1003

	click_policy_changed_callback (list_view);
1004
	
1005 1006
	fm_list_view_sort_directories_first_changed (FM_DIRECTORY_VIEW (list_view));
}