fm-directory-view-list.c 17 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */

/* fm-directory-view-list.c - implementation of list view of directory.

   Copyright (C) 2000 Eazel, Inc.

   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>
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "fm-directory-view-list.h"
30

31
#include "fm-icon-cache.h"
32
#include <gtk/gtkhbox.h>
33 34
#include <gtk/gtkmenu.h>
#include <gtk/gtkmenuitem.h>
35
#include <libgnome/gnome-i18n.h>
36 37
#include <libgnomeui/gnome-pixmap.h>
#include <libgnomeui/gnome-uidefs.h>
38 39
#include <libnautilus/nautilus-gtk-macros.h>
#include <libnautilus/gtkflist.h>
40
#include <libnautilus/nautilus-background.h>
41

42 43
struct _FMDirectoryViewListDetails
{
44
	gint sort_column;
45
	gboolean sort_reversed;
46
	guint icon_size;
47 48
};

49 50
#define DEFAULT_BACKGROUND_COLOR "rgb:FFFF/FFFF/FFFF"

51 52 53 54 55 56 57 58 59
#define LIST_VIEW_COLUMN_NONE		-1

#define LIST_VIEW_COLUMN_ICON		0
#define LIST_VIEW_COLUMN_NAME		1
#define LIST_VIEW_COLUMN_SIZE		2
#define LIST_VIEW_COLUMN_MIME_TYPE	3
#define LIST_VIEW_COLUMN_DATE_MODIFIED	4
#define LIST_VIEW_COLUMN_COUNT		5

60 61

/* forward declarations */
62
static void add_to_flist 			    (FMDirectoryViewList *list_view,
63
		   		 		     NautilusFile *file);
64
static void column_clicked_cb 			    (GtkCList *clist,
65 66
			       	 		     gint column,
			       	 		     gpointer user_data);
67 68 69 70 71
static void context_click_row_cb		    (GtkCList *clist,
						     gint row,
						     FMDirectoryViewList *list_view);
static void context_click_background_cb		    (GtkCList *clist,
						     FMDirectoryViewList *list_view);
72
static GtkFList *create_flist 			    (FMDirectoryViewList *list_view);
73
static void flist_activate_cb 			    (GtkFList *flist,
74 75 76 77
			       	 		     gpointer entry_data,
			       	 		     gpointer data);
static void flist_selection_changed_cb 	  	    (GtkFList *flist, gpointer data);
static void fm_directory_view_list_add_entry 	    (FMDirectoryView *view, 
78
				 		     NautilusFile *file);
79 80 81 82
static void fm_directory_view_list_background_changed_cb
                                                    (NautilusBackground *background,
						     FMDirectoryViewList *list_view);
static void fm_directory_view_list_begin_adding_entries
83 84
						    (FMDirectoryView *view);
static void fm_directory_view_list_clear 	    (FMDirectoryView *view);
85 86 87 88
static GList *fm_directory_view_list_get_selection  (FMDirectoryView *view);
static void fm_directory_view_list_initialize 	    (gpointer object, gpointer klass);
static void fm_directory_view_list_initialize_class (gpointer klass);
static void fm_directory_view_list_destroy 	    (GtkObject *object);
89
static void fm_directory_view_list_done_adding_entries 
90
						    (FMDirectoryView *view);
91 92 93
static void fm_directory_view_list_sort_items 	    (FMDirectoryViewList *list_view, 
				   		     int column, 
				   		     gboolean reversed);
94
static GtkFList *get_flist 			    (FMDirectoryViewList *list_view);
95 96 97 98
static GtkWidget *get_sort_indicator 		    (GtkFList *flist, 
						     gint column, 
						     gboolean reverse);
static void hide_sort_indicator 		    (GtkFList *flist, gint column);
99 100 101 102
static void install_icon 			    (FMDirectoryViewList *list_view, 
						     NautilusFile *file,
						     guint row,
						     guint column);
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
static void show_sort_indicator 		    (GtkFList *flist, 
						     gint column, 
						     gboolean sort_reversed);

static char * down_xpm[] = {
"6 5 2 1",
" 	c None",
".	c #000000",
"......",
"      ",
" .... ",
"      ",
"  ..  "};

static char * up_xpm[] = {
"6 5 2 1",
" 	c None",
".	c #000000",
"  ..  ",
"      ",
" .... ",
"      ",
"......"};

127

128
NAUTILUS_DEFINE_CLASS_BOILERPLATE (FMDirectoryViewList, fm_directory_view_list, FM_TYPE_DIRECTORY_VIEW);
129 130


131 132 133 134

/* GtkObject methods.  */

static void
135
fm_directory_view_list_initialize_class (gpointer klass)
136 137
{
	GtkObjectClass *object_class;
138
	FMDirectoryViewClass *fm_directory_view_class;
139

140 141
	object_class = GTK_OBJECT_CLASS (klass);
	fm_directory_view_class = FM_DIRECTORY_VIEW_CLASS (klass);
142 143

	object_class->destroy = fm_directory_view_list_destroy;
144
	
145
	fm_directory_view_class->clear = fm_directory_view_list_clear;	
146 147 148
	fm_directory_view_class->begin_adding_entries = fm_directory_view_list_begin_adding_entries;	
	fm_directory_view_class->add_entry = fm_directory_view_list_add_entry;	
	fm_directory_view_class->done_adding_entries = fm_directory_view_list_done_adding_entries;	
149
	fm_directory_view_class->get_selection = fm_directory_view_list_get_selection;	
150 151 152
}

static void
153
fm_directory_view_list_initialize (gpointer object, gpointer klass)
154
{
155 156
	FMDirectoryViewList *list_view;
	
157
	g_return_if_fail (FM_IS_DIRECTORY_VIEW_LIST (object));
158
	g_return_if_fail (GTK_BIN (object)->child == NULL);
159 160 161 162

	list_view = FM_DIRECTORY_VIEW_LIST (object);

	list_view->details = g_new0 (FMDirectoryViewListDetails, 1);
163 164

	/* FIXME: These should be read from metadata */
165
	list_view->details->sort_column = LIST_VIEW_COLUMN_NONE;
166
	list_view->details->sort_reversed = FALSE;
167
	list_view->details->icon_size = NAUTILUS_ICON_SIZE_SMALLER;
168
	
169
	create_flist (list_view);
170 171
}

172 173 174 175 176
static void
fm_directory_view_list_destroy (GtkObject *object)
{
	NAUTILUS_CALL_PARENT_CLASS (GTK_OBJECT_CLASS, destroy, (object));
}
177

178 179


180
static void 
181
column_clicked_cb (GtkCList *clist, gint column, gpointer user_data)
182 183
{
	FMDirectoryViewList *list_view;
184
	gboolean reversed;
185

186
	g_return_if_fail (GTK_IS_FLIST (clist));
187
	g_return_if_fail (FM_IS_DIRECTORY_VIEW_LIST (user_data));
188
	g_return_if_fail (get_flist (FM_DIRECTORY_VIEW_LIST (user_data)) == GTK_FLIST (clist));
189 190

	list_view = FM_DIRECTORY_VIEW_LIST (user_data);
191 192 193

	if (column == list_view->details->sort_column)
	{
194
		reversed = !list_view->details->sort_reversed;
195
	}
196
	else
197
	{
198
		reversed = FALSE;
199 200
	}

201
	fm_directory_view_list_sort_items (list_view, column, reversed);
202
}
203

204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229


static void context_click_row_cb		    (GtkCList *clist,
						     gint row,
						     FMDirectoryViewList *list_view)
{
	NautilusFile * file;

	g_assert (GTK_IS_CLIST (clist));
	g_assert (FM_IS_DIRECTORY_VIEW_LIST (list_view));

	file = NAUTILUS_FILE(gtk_clist_get_row_data (clist, clist->rows - 1));

	fm_directory_view_popup_item_context_menu (FM_DIRECTORY_VIEW (list_view), file);
}


static void context_click_background_cb		    (GtkCList *clist,
						     FMDirectoryViewList *list_view)
{
	g_assert (FM_IS_DIRECTORY_VIEW_LIST (list_view));

	fm_directory_view_popup_background_context_menu (FM_DIRECTORY_VIEW (list_view));
}


230
static GtkFList *
231
create_flist (FMDirectoryViewList *list_view)
232 233 234
{
	GtkFList *flist;
	gchar *titles[] = {
235 236 237
		NULL,
		_("Name"),
		_("Size"),
238
		_("Type"),
239 240 241
		_("Date Modified"),
	};
	uint widths[] = {
242
		 list_view->details->icon_size,	/* Icon */
243 244 245
		130,	/* Name */
		 55,	/* Size */
		 95,	/* Type */
246
		100,	/* Modified */
247
	};
248
	int i;
249

250 251
	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW_LIST (list_view), NULL);

252 253 254 255
	flist = GTK_FLIST (gtk_flist_new_with_titles (LIST_VIEW_COLUMN_COUNT, titles));

	for (i = 0; i < LIST_VIEW_COLUMN_COUNT; ++i)
	{
256 257 258 259 260 261 262 263
		GtkWidget *hbox;
		GtkWidget *label;
		GtkWidget *sort_up_indicator;
		GtkWidget *sort_down_indicator;
		gboolean right_justified;

		right_justified = (i == LIST_VIEW_COLUMN_SIZE);
	
264 265
		gtk_clist_set_column_width (GTK_CLIST (flist), i, widths[i]);

266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
		/* Column header button contains three views, a title,
		 * a "sort downward" indicator, and a "sort upward" indicator. 
		 * Only one sort indicator (for all columns) is shown at once.
		 */
		hbox = gtk_hbox_new (FALSE, GNOME_PAD_SMALL);
		gtk_widget_show (GTK_WIDGET (hbox));
		label = gtk_label_new (titles[i]);
		gtk_widget_show (GTK_WIDGET (label));

		/* sort indicators are initially hidden */
		sort_up_indicator = gnome_pixmap_new_from_xpm_d (up_xpm);
		sort_down_indicator = gnome_pixmap_new_from_xpm_d (down_xpm);

		if (!right_justified)
		{
			gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
		}

		gtk_box_pack_end (GTK_BOX (hbox), sort_up_indicator, FALSE, FALSE, GNOME_PAD);
		gtk_box_pack_end (GTK_BOX (hbox), sort_down_indicator, FALSE, FALSE, GNOME_PAD);

		if (right_justified)
		{
			gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
			gtk_clist_set_column_justification (GTK_CLIST (flist), i, GTK_JUSTIFY_RIGHT);
		}
		
		gtk_clist_set_column_widget (GTK_CLIST (flist), i, hbox);
	}
295 296 297

	/* Make height tall enough for icons to look good */
	gtk_clist_set_row_height (GTK_CLIST (flist), list_view->details->icon_size);
298
	
299 300 301 302 303
	GTK_WIDGET_SET_FLAGS (flist, GTK_CAN_FOCUS);

	gtk_signal_connect (GTK_OBJECT (flist),
			    "activate",
			    GTK_SIGNAL_FUNC (flist_activate_cb),
304
			    list_view);
305 306 307
	gtk_signal_connect (GTK_OBJECT (flist),
			    "selection_changed",
			    GTK_SIGNAL_FUNC (flist_selection_changed_cb),
308
			    list_view);
309 310 311 312
	gtk_signal_connect (GTK_OBJECT (flist),
			    "click_column",
			    column_clicked_cb,
			    list_view);
313 314 315 316 317 318 319 320 321
	gtk_signal_connect (GTK_OBJECT (flist),
			    "context_click_row",
			    context_click_row_cb,
			    list_view);
	gtk_signal_connect (GTK_OBJECT (flist),
			    "context_click_background",
			    context_click_background_cb,
			    list_view);

322 323 324 325 326 327

	gtk_signal_connect (GTK_OBJECT (nautilus_get_widget_background (GTK_WIDGET (flist))),
			    "changed",
			    GTK_SIGNAL_FUNC (fm_directory_view_list_background_changed_cb),
			    list_view);

328 329


330
	gtk_container_add (GTK_CONTAINER (list_view), GTK_WIDGET (flist));
331 332 333 334 335 336
	gtk_widget_show (GTK_WIDGET (flist));

	return flist;
}

static void
337
flist_activate_cb (GtkFList *flist,
338 339 340
		   gpointer entry_data,
		   gpointer data)
{
341
	g_return_if_fail (GTK_IS_FLIST (flist));
342 343 344
	g_return_if_fail (FM_IS_DIRECTORY_VIEW_LIST (data));
	g_return_if_fail (entry_data != NULL);

345
	fm_directory_view_activate_entry (FM_DIRECTORY_VIEW (data), entry_data);
346 347 348 349 350 351
}

static void
flist_selection_changed_cb (GtkFList *flist,
			    gpointer data)
{
352 353
	g_return_if_fail (FM_IS_DIRECTORY_VIEW_LIST (data));
	g_return_if_fail (flist == get_flist (FM_DIRECTORY_VIEW_LIST (data)));
354

355
	fm_directory_view_notify_selection_changed (FM_DIRECTORY_VIEW (data));
356
}
357

358
static void
359
add_to_flist (FMDirectoryViewList *list_view,
360
	      NautilusFile *file)
361 362
{
	GtkCList *clist;
363
	gchar *text[LIST_VIEW_COLUMN_COUNT];
364
	gchar *name;
365 366 367
	gchar *size_string;
	gchar *modified_string;
	gchar *type_string;
368

369 370
	g_return_if_fail (FM_IS_DIRECTORY_VIEW (list_view));

371 372
	text[LIST_VIEW_COLUMN_ICON] = NULL;
	
373 374
	name = nautilus_file_get_name (file);
	text[LIST_VIEW_COLUMN_NAME] = name;
375

376
	size_string = nautilus_file_get_size_as_string (file);
377
	text[LIST_VIEW_COLUMN_SIZE] = size_string;
378

379
	modified_string = nautilus_file_get_date_as_string (file);
380
	text[LIST_VIEW_COLUMN_DATE_MODIFIED] = modified_string;
381

382
	type_string = nautilus_file_get_type_as_string (file);
383 384
	text[LIST_VIEW_COLUMN_MIME_TYPE] = type_string;
	
385
	clist = GTK_CLIST (get_flist(list_view));
386
	gtk_clist_append (clist, text);
387
	gtk_clist_set_row_data (clist, clist->rows - 1, file);
388

389 390 391 392 393
	install_icon (list_view, 
		      file, 
		      clist->rows - 1, 
		      LIST_VIEW_COLUMN_ICON);

394
	g_free (name);
395 396
	g_free (size_string);
	g_free (modified_string);
397
	g_free (type_string);
398 399 400
}

static GtkFList *
401
get_flist (FMDirectoryViewList *list_view)
402
{
403 404
	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW_LIST (list_view), NULL);
	g_return_val_if_fail (GTK_IS_FLIST (GTK_BIN (list_view)->child), NULL);
405

406
	return GTK_FLIST (GTK_BIN (list_view)->child);
407 408
}

409 410 411
static void
fm_directory_view_list_clear (FMDirectoryView *view)
{
412 413 414
	GtkFList *flist;
	char *background_color;

415 416
	g_return_if_fail (FM_IS_DIRECTORY_VIEW_LIST (view));

417 418 419 420 421 422 423 424 425 426 427 428
	flist = get_flist (FM_DIRECTORY_VIEW_LIST (view));

	/* Clear away the existing list items. */
	gtk_clist_clear (GTK_CLIST (flist));

	/* Set up the background color from the metadata. */
	background_color = nautilus_directory_get_metadata (fm_directory_view_get_model (view),
							    "LIST_VIEW_BACKGROUND_COLOR",
							    DEFAULT_BACKGROUND_COLOR);
	nautilus_background_set_color (nautilus_get_widget_background (GTK_WIDGET (flist)),
				       background_color);
	g_free (background_color);
429 430 431 432 433 434 435 436 437 438 439
}

static void
fm_directory_view_list_begin_adding_entries (FMDirectoryView *view)
{
	g_return_if_fail (FM_IS_DIRECTORY_VIEW_LIST (view));

	gtk_clist_freeze (GTK_CLIST (get_flist (FM_DIRECTORY_VIEW_LIST (view))));
}

static void
440
fm_directory_view_list_add_entry (FMDirectoryView *view, NautilusFile *file)
441 442 443
{
	g_return_if_fail (FM_IS_DIRECTORY_VIEW_LIST (view));

444
	add_to_flist (FM_DIRECTORY_VIEW_LIST (view), file);
445 446 447 448 449 450 451 452
}

static void
fm_directory_view_list_done_adding_entries (FMDirectoryView *view)
{
	g_return_if_fail (FM_IS_DIRECTORY_VIEW_LIST (view));

	gtk_clist_thaw (GTK_CLIST (get_flist (FM_DIRECTORY_VIEW_LIST (view))));
453 454
}

455 456 457 458 459 460 461 462
static GList *
fm_directory_view_list_get_selection (FMDirectoryView *view)
{
	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW_LIST (view), NULL);

	return gtk_flist_get_selection (get_flist (FM_DIRECTORY_VIEW_LIST (view)));
}

463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
static void
fm_directory_view_list_sort_items (FMDirectoryViewList *list_view, 
				   int column, 
				   gboolean reversed)
{
	FMDirectoryViewSortType sort_type;
	GtkFList *flist;
	
	g_return_if_fail (FM_IS_DIRECTORY_VIEW_LIST (list_view));

	flist = get_flist (list_view);
	
	switch (column)
	{
		case LIST_VIEW_COLUMN_ICON:	
			sort_type = FM_DIRECTORY_VIEW_SORT_BYTYPE;
			break;
		case LIST_VIEW_COLUMN_NAME:
			sort_type = FM_DIRECTORY_VIEW_SORT_BYNAME;
			break;
		case LIST_VIEW_COLUMN_SIZE:
			sort_type = FM_DIRECTORY_VIEW_SORT_BYSIZE;
			break;
		case LIST_VIEW_COLUMN_DATE_MODIFIED:
			sort_type = FM_DIRECTORY_VIEW_SORT_BYMTIME;
			break;
		case LIST_VIEW_COLUMN_MIME_TYPE:
			sort_type = FM_DIRECTORY_VIEW_SORT_BYTYPE;
			break;
		default: 
			g_assert_not_reached();
			sort_type = FM_DIRECTORY_VIEW_SORT_NONE;
			break;
	}

	hide_sort_indicator (flist, list_view->details->sort_column);
	list_view->details->sort_column = column;
	list_view->details->sort_reversed = reversed;
	show_sort_indicator (flist, column, reversed);

	
	fm_directory_view_sort (FM_DIRECTORY_VIEW (list_view), 
				sort_type,
				reversed);
}

509 510 511 512 513 514 515 516 517 518
static void
fm_directory_view_list_background_changed_cb (NautilusBackground *background,
					      FMDirectoryViewList *list_view)
{
	NautilusDirectory *directory;
	char *color_spec;

	g_assert (FM_IS_DIRECTORY_VIEW_LIST (list_view));
	g_assert (background == nautilus_get_widget_background
		  (GTK_WIDGET (get_flist (list_view))));
519

520 521 522 523 524 525 526 527 528 529 530
	directory = fm_directory_view_get_model (FM_DIRECTORY_VIEW (list_view));
	if (directory == NULL)
		return;
	
	color_spec = nautilus_background_get_color (background);
	nautilus_directory_set_metadata (directory,
					 "LIST_VIEW_BACKGROUND_COLOR",
					 DEFAULT_BACKGROUND_COLOR,
					 color_spec);
	g_free (color_spec);
}
531

532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564
static GtkWidget *
get_sort_indicator (GtkFList *flist, gint column, gboolean reverse)
{
	GtkWidget *column_widget;
	GtkWidget *result;
	GList *children;

	g_return_val_if_fail (GTK_IS_FLIST (flist), NULL);
	g_return_val_if_fail (column >= 0, NULL);

	column_widget = gtk_clist_get_column_widget (GTK_CLIST (flist), column);
	g_assert (GTK_IS_HBOX (column_widget));

	children = gtk_container_children (GTK_CONTAINER (column_widget));
	result = GTK_WIDGET (g_list_nth_data (children, reverse ? 1 : 2));
	g_list_free (children);

	return result;
}

static void
hide_sort_indicator (GtkFList *flist, gint column)
{
	g_return_if_fail (GTK_IS_FLIST (flist));

	if (column == LIST_VIEW_COLUMN_NONE)
		return;

	gtk_widget_hide (get_sort_indicator (flist, column, FALSE));
	gtk_widget_hide (get_sort_indicator (flist, column, TRUE));
}


565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
/**
 * install_icon:
 *
 * Put an icon for a file into the specified cell.
 * @list_view: FMDirectoryView in which to install icon.
 * @file: NautilusFile representing file whose icon should be installed.
 * @row: row index of target cell
 * @column: column index of target cell
 * 
 **/
static void
install_icon (FMDirectoryViewList *list_view, 
	      NautilusFile *file, 
	      guint row, 
	      guint column)
{
	GdkPixbuf *pixbuf;
	GdkPixmap *pixmap;
	GdkBitmap *bitmap;

	g_return_if_fail (FM_IS_DIRECTORY_VIEW_LIST (list_view));
	g_return_if_fail (file != NULL);
	
	pixbuf = fm_icon_cache_get_icon_for_file (fm_get_current_icon_cache(), 
					 	  file,
					 	  list_view->details->icon_size);

	/* GtkCList requires a pixmap & mask rather than a pixbuf */
	gdk_pixbuf_render_pixmap_and_mask (pixbuf, &pixmap, &bitmap, 100);
	gtk_clist_set_pixmap (GTK_CLIST (get_flist (list_view)), row, column, pixmap, bitmap);

	gdk_pixbuf_unref (pixbuf);
597 598 599 600 601 602 603 604 605 606 607 608
}

static void
show_sort_indicator (GtkFList *flist, gint column, gboolean sort_reversed)
{
	g_return_if_fail (GTK_IS_FLIST (flist));

	if (column == LIST_VIEW_COLUMN_NONE)
		return;

	gtk_widget_show (get_sort_indicator (flist, column, sort_reversed));
}