nautilus-icon-container.c 93 KB
Newer Older
Ettore Perazzoli's avatar
Ettore Perazzoli committed
1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2

3
/* nautilus-icon-container.c - Icon container widget.
Ettore Perazzoli's avatar
Ettore Perazzoli committed
4

Elliot Lee's avatar
Elliot Lee committed
5
   Copyright (C) 1999, 2000 Free Software Foundation
6 7
   Copyright (C) 2000 Eazel, Inc.
   
Ettore Perazzoli's avatar
Ettore Perazzoli committed
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
   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.

23
   Authors: Ettore Perazzoli <ettore@gnu.org>, Darin Adler <darin@eazel.com>
Ettore Perazzoli's avatar
Ettore Perazzoli committed
24 25 26
*/

#include <config.h>
27
#include "nautilus-icon-container.h"
28

29
#include <ctype.h>
30
#include <math.h>
31
#include <stdio.h>
Ettore Perazzoli's avatar
Ettore Perazzoli committed
32

33 34 35 36
#include <gdk/gdkkeysyms.h>
#include <gtk/gtksignal.h>
#include <gtk/gtkmain.h>
#include <libgnomeui/gnome-canvas-rect-ellipse.h>
37
#include <gdk-pixbuf/gnome-canvas-pixbuf.h>
38
#include <libnautilus/nautilus-undo-manager.h>
39

40
#include "nautilus-gdk-pixbuf-extensions.h"
41 42
#include "nautilus-glib-extensions.h"
#include "nautilus-global-preferences.h"
43
#include "nautilus-gnome-extensions.h"
44
#include "nautilus-gtk-extensions.h"
45
#include "nautilus-gtk-macros.h"
46
#include "nautilus-icon-text-item.h"
47
#include "nautilus-lib-self-check-functions.h"
48

Ettore Perazzoli's avatar
Ettore Perazzoli committed
49

50 51
#include "nautilus-icon-grid.h"
#include "nautilus-icon-private.h"
52

Ettore Perazzoli's avatar
Ettore Perazzoli committed
53 54 55
/* Interval for updating the rubberband selection, in milliseconds.  */
#define RUBBERBAND_TIMEOUT_INTERVAL 10

John Sullivan's avatar
John Sullivan committed
56 57 58
/* Timeout for making the icon currently selected for keyboard operation visible. */
/* FIXME bugzilla.eazel.com 611: This *must* be higher than the double-click 
 * time in GDK, but there is no way to access its value from outside.
59
 */
60
#define KEYBOARD_ICON_REVEAL_TIMEOUT 300
Ettore Perazzoli's avatar
Ettore Perazzoli committed
61

62 63 64
/* Maximum amount of milliseconds the mouse button is allowed to stay down
 * and still be considered a click.
 */
65 66
#define MAX_CLICK_TIME 1500

67 68 69 70 71 72 73 74
/* Distance you have to move before it becomes a drag. */
#define SNAP_RESISTANCE 2 /* GMC has this set to 3, but it's too much for my (Ettore's?) taste. */

/* Button assignments. */
#define DRAG_BUTTON 1
#define RUBBERBAND_BUTTON 1
#define CONTEXTUAL_MENU_BUTTON 3

75 76 77 78
/* Maximum size (pixels) allowed for icons. */
#define MAXIMUM_IMAGE_SIZE 1000
#define MAXIMUM_EMBLEM_SIZE 100

79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
static void          activate_selected_items                  (NautilusIconContainer      *container);
static void          nautilus_icon_container_initialize_class (NautilusIconContainerClass *class);
static void          nautilus_icon_container_initialize       (NautilusIconContainer      *container);
static void          compute_stretch                          (StretchState               *start,
							       StretchState               *current);
static NautilusIcon *get_first_selected_icon                  (NautilusIconContainer      *container);
static NautilusIcon *get_nth_selected_icon                    (NautilusIconContainer      *container,
							       int                         index);
static gboolean      has_multiple_selection                   (NautilusIconContainer      *container);
static void          icon_destroy                             (NautilusIconContainer      *container,
							       NautilusIcon               *icon);
static void          end_renaming_mode                        (NautilusIconContainer      *container,
							       gboolean                    commit);
static void          hide_rename_widget                       (NautilusIconContainer      *container,
							       NautilusIcon               *icon);
94
static void          click_policy_changed_callback            (gpointer                    user_data);
95

96 97 98
static void 	     remember_selected_files		      (NautilusIconContainer 	*container);
static void 	     forget_selected_files		      (NautilusIconContainer 	*container);

99
NAUTILUS_DEFINE_CLASS_BOILERPLATE (NautilusIconContainer, nautilus_icon_container, GNOME_TYPE_CANVAS)
100

Ettore Perazzoli's avatar
Ettore Perazzoli committed
101 102


103
/* The NautilusIconContainer signals.  */
104
enum {
Ettore Perazzoli's avatar
Ettore Perazzoli committed
105
	ACTIVATE,
106
	BUTTON_PRESS,
107 108
	CAN_ACCEPT_ITEM,
	COMPARE_ICONS,
109
	CONTEXT_CLICK_BACKGROUND,
110
	CONTEXT_CLICK_SELECTION,
111
	GET_CONTAINER_URI,
112
	GET_ICON_IMAGES,
113
	GET_ICON_TEXT,
114
	GET_ICON_URI,
115 116
	GET_STORED_ICON_POSITION,
	ICON_POSITION_CHANGED,
117
	ICON_TEXT_CHANGED,
118
	LAYOUT_CHANGED,
Pavel Cisler's avatar
Pavel Cisler committed
119
	MOVE_COPY_ITEMS,
120
	SELECTION_CHANGED,	
Ettore Perazzoli's avatar
Ettore Perazzoli committed
121 122
	LAST_SIGNAL
};
123
static guint signals[LAST_SIGNAL];
Ettore Perazzoli's avatar
Ettore Perazzoli committed
124 125 126 127 128

/* Bitmap for stippled selection rectangles.  */
static GdkBitmap *stipple;
static char stipple_bits[] = { 0x02, 0x01 };

129

130
/* Functions dealing with NautilusIcons.  */
Ettore Perazzoli's avatar
Ettore Perazzoli committed
131 132

static void
133
icon_free (NautilusIcon *icon)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
134 135
{
	gtk_object_destroy (GTK_OBJECT (icon->item));
136
	g_free (icon);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
137 138 139
}

static void
140
icon_set_position (NautilusIcon *icon,
141
		   double x, double y)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
142
{
143
	if (icon->x == x && icon->y == y) {
144
		return;
145
	}
Ettore Perazzoli's avatar
Ettore Perazzoli committed
146

147 148 149
	gnome_canvas_item_move (GNOME_CANVAS_ITEM (icon->item),
				x - icon->x,
				y - icon->y);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
150 151 152 153 154

	icon->x = x;
	icon->y = y;
}

155 156 157 158 159 160 161
/* icon_get_size and icon_set_size are used by the stretching user interface,
 * which currently stretches in a way that keeps the aspect ratio. Later we
 * might have a stretching interface that stretches Y separate from X and
 * we will change this around.
 */

static void
162 163
icon_get_size (NautilusIconContainer *container,
	       NautilusIcon *icon,
164
	       guint *size_x, guint *size_y)
165
{
166 167 168 169 170 171 172 173
	if (size_x != NULL) {
		*size_x = MAX (nautilus_get_icon_size_for_zoom_level (container->details->zoom_level)
			       * icon->scale_x, NAUTILUS_ICON_SIZE_SMALLEST);
	}
	if (size_y != NULL) {
		*size_y = MAX (nautilus_get_icon_size_for_zoom_level (container->details->zoom_level)
			       * icon->scale_y, NAUTILUS_ICON_SIZE_SMALLEST);
	}
174 175 176
}

static void
177 178
icon_set_size (NautilusIconContainer *container,
	       NautilusIcon *icon,
179 180
	       guint icon_size)
{
181 182 183 184 185
	guint old_size_x, old_size_y;
	double scale;

	icon_get_size (container, icon, &old_size_x, &old_size_y);
	if (icon_size == old_size_x && icon_size == old_size_y) {
186
		return;
187
	}
188

189
	scale = (double) icon_size /
190 191
		nautilus_get_icon_size_for_zoom_level
		(container->details->zoom_level);
192
	nautilus_icon_container_move_icon (container, icon,
193 194 195
					icon->x, icon->y,
					scale, scale,
					FALSE);
196 197
}

Ettore Perazzoli's avatar
Ettore Perazzoli committed
198
static void
199
icon_raise (NautilusIcon *icon)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
200 201 202 203 204
{
	gnome_canvas_item_raise_to_top (GNOME_CANVAS_ITEM (icon->item));
}

static void
205
icon_show (NautilusIcon *icon)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
206 207 208 209 210
{
	gnome_canvas_item_show (GNOME_CANVAS_ITEM (icon->item));
}

static void
211 212
icon_toggle_selected (NautilusIconContainer *container,
		      NautilusIcon *icon)
213 214 215
{		
	end_renaming_mode(container, TRUE);

216
	icon->is_selected = !icon->is_selected;
217
	gnome_canvas_item_set (GNOME_CANVAS_ITEM (icon->item),
218
			       "highlighted_for_selection", (gboolean) icon->is_selected,
219
			       NULL);
220 221 222 223 224 225

	/* If the icon is deselected, then get rid of the stretch handles.
	 * No harm in doing the same if the item is newly selected.
	 */
	if (icon == container->details->stretch_icon) {
		container->details->stretch_icon = NULL;
226
		nautilus_icon_canvas_item_set_show_stretch_handles (icon->item, FALSE);
227
	}
Ettore Perazzoli's avatar
Ettore Perazzoli committed
228 229
}

230
/* Select an icon. Return TRUE if selection has changed. */
Ettore Perazzoli's avatar
Ettore Perazzoli committed
231
static gboolean
232 233
icon_set_selected (NautilusIconContainer *container,
		   NautilusIcon *icon,
234
		   gboolean select)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
235
{
236 237 238 239
	g_assert (select == FALSE || select == TRUE);
	g_assert (icon->is_selected == FALSE || icon->is_selected == TRUE);

	if (select == icon->is_selected) {
Ettore Perazzoli's avatar
Ettore Perazzoli committed
240
		return FALSE;
241
	}
242

243
	icon_toggle_selected (container, icon);
244
	g_assert (select == icon->is_selected);
245
	return TRUE;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
246 247 248
}

static void
249
icon_get_bounding_box (NautilusIcon *icon,
250 251
		       int *x1_return, int *y1_return,
		       int *x2_return, int *y2_return)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
252 253 254 255 256 257 258 259 260 261 262 263 264
{
	double x1, y1, x2, y2;

	gnome_canvas_item_get_bounds (GNOME_CANVAS_ITEM (icon->item),
				      &x1, &y1, &x2, &y2);

	*x1_return = x1;
	*y1_return = y1;
	*x2_return = x2;
	*y2_return = y2;
}


265

266
/* Utility functions for NautilusIconContainer.  */
Ettore Perazzoli's avatar
Ettore Perazzoli committed
267 268

static void
269
scroll (NautilusIconContainer *container,
270
	int delta_x, int delta_y)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
271 272 273 274 275 276
{
	GtkAdjustment *hadj, *vadj;

	hadj = GTK_LAYOUT (container)->hadjustment;
	vadj = GTK_LAYOUT (container)->vadjustment;

277 278
	gtk_adjustment_set_value (hadj, hadj->value + delta_x);
	gtk_adjustment_set_value (vadj, vadj->value + delta_y);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
279 280 281
}

static void
282 283
reveal_icon (NautilusIconContainer *container,
	     NautilusIcon *icon)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
284
{
285
	NautilusIconContainerDetails *details;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
286 287
	GtkAllocation *allocation;
	GtkAdjustment *hadj, *vadj;
288
	int x1, y1, x2, y2;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
289

290
	details = container->details;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
291 292 293 294 295 296 297
	allocation = &GTK_WIDGET (container)->allocation;

	hadj = GTK_LAYOUT (container)->hadjustment;
	vadj = GTK_LAYOUT (container)->vadjustment;

	icon_get_bounding_box (icon, &x1, &y1, &x2, &y2);

298
	if (y1 < vadj->value) {
Ettore Perazzoli's avatar
Ettore Perazzoli committed
299
		gtk_adjustment_set_value (vadj, y1);
300
	} else if (y2 > vadj->value + allocation->height) {
Ettore Perazzoli's avatar
Ettore Perazzoli committed
301
		gtk_adjustment_set_value (vadj, y2 - allocation->height);
302
	}
Ettore Perazzoli's avatar
Ettore Perazzoli committed
303

304
	if (x1 < hadj->value) {
Ettore Perazzoli's avatar
Ettore Perazzoli committed
305
		gtk_adjustment_set_value (hadj, x1);
306
	} else if (x2 > hadj->value + allocation->width) {
Ettore Perazzoli's avatar
Ettore Perazzoli committed
307
		gtk_adjustment_set_value (hadj, x2 - allocation->width);
308
	}
Ettore Perazzoli's avatar
Ettore Perazzoli committed
309 310
}

311
static gboolean
312
keyboard_icon_reveal_timeout_callback (gpointer data)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
313
{
314 315
	NautilusIconContainer *container;
	NautilusIcon *icon;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
316 317 318

	GDK_THREADS_ENTER ();

319
	container = NAUTILUS_ICON_CONTAINER (data);
320 321 322
	icon = container->details->keyboard_icon_to_reveal;

	g_assert (icon != NULL);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
323

324 325
	/* Only reveal the icon if it's still the keyboard focus
	 * or if it's still selected.
John Sullivan's avatar
John Sullivan committed
326 327 328
	 */
	/* FIXME bugzilla.eazel.com 612: 
	 * Need to unschedule this if the user scrolls explicitly.
329 330 331 332
	 */
	if (icon == container->details->keyboard_focus
	    || icon->is_selected) {
		reveal_icon (container, icon);
333
	}
334
	container->details->keyboard_icon_reveal_timer_id = 0;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
335 336 337 338 339 340 341

	GDK_THREADS_LEAVE ();

	return FALSE;
}

static void
342
unschedule_keyboard_icon_reveal (NautilusIconContainer *container)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
343
{
344
	NautilusIconContainerDetails *details;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
345

346
	details = container->details;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
347

348 349
	if (details->keyboard_icon_reveal_timer_id != 0) {
		gtk_timeout_remove (details->keyboard_icon_reveal_timer_id);
350
	}
Ettore Perazzoli's avatar
Ettore Perazzoli committed
351 352 353
}

static void
354 355
schedule_keyboard_icon_reveal (NautilusIconContainer *container,
			       NautilusIcon *icon)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
356
{
357
	NautilusIconContainerDetails *details;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
358

359
	details = container->details;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
360

361
	unschedule_keyboard_icon_reveal (container);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
362

363 364 365 366
	details->keyboard_icon_to_reveal = icon;
	details->keyboard_icon_reveal_timer_id
		= gtk_timeout_add (KEYBOARD_ICON_REVEAL_TIMEOUT,
				   keyboard_icon_reveal_timeout_callback,
Ettore Perazzoli's avatar
Ettore Perazzoli committed
367 368 369 370
				   container);
}

static void
371
clear_keyboard_focus (NautilusIconContainer *container)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
372
{
373 374 375
        if (container->details->keyboard_focus != NULL) {
		gnome_canvas_item_set (GNOME_CANVAS_ITEM (container->details->keyboard_focus->item),
				       "highlighted_as_keyboard_focus", 0,
376 377 378
				       NULL);
	}
	
379 380 381 382 383
	container->details->keyboard_focus = NULL;
}

/* Set `icon' as the icon currently selected for keyboard operations. */
static void
384 385
set_keyboard_focus (NautilusIconContainer *container,
		    NautilusIcon *icon)
386 387 388 389 390
{
	g_assert (icon != NULL);

	if (icon == container->details->keyboard_focus) {
		return;
391
	}
392 393 394 395 396 397 398 399

	clear_keyboard_focus (container);

	container->details->keyboard_focus = icon;

	gnome_canvas_item_set (GNOME_CANVAS_ITEM (container->details->keyboard_focus->item),
			       "highlighted_as_keyboard_focus", 1,
			       NULL);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
400 401 402
}


403

Ettore Perazzoli's avatar
Ettore Perazzoli committed
404 405
/* Idle operation handler.  */

406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
static void
nautilus_gnome_canvas_item_request_update_deep (GnomeCanvasItem *item)
{
	GList *p;

	gnome_canvas_item_request_update (item);
	if (GNOME_IS_CANVAS_GROUP (item)) {
		for (p = GNOME_CANVAS_GROUP (item)->item_list; p != NULL; p = p->next) {
			nautilus_gnome_canvas_item_request_update_deep (p->data);
		}
	}
}

static void
nautilus_gnome_canvas_request_update_all (GnomeCanvas *canvas)
{
	nautilus_gnome_canvas_item_request_update_deep (canvas->root);
}

/* gnome_canvas_set_scroll_region doesn't do an update, even though it should.
426
 * The update is in there with an #if 0 around it, with no explanation of
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
 * why it's commented out. For now, work around this by requesting an update
 * explicitly.
 */
static void
nautilus_gnome_canvas_set_scroll_region (GnomeCanvas *canvas,
					 double x1, double y1,
					 double x2, double y2)
{
	double old_x1, old_y1, old_x2, old_y2;

	gnome_canvas_get_scroll_region (canvas, &old_x1, &old_y1, &old_x2, &old_y2);
	if (old_x1 == x1 && old_y1 == y1 && old_x2 == x2 && old_y2 == y2) {
		return;
	}

	gnome_canvas_set_scroll_region (canvas, x1, y1, x2, y2);
	nautilus_gnome_canvas_request_update_all (canvas);
	gnome_canvas_item_request_update (canvas->root);
}

Ettore Perazzoli's avatar
Ettore Perazzoli committed
447
static void
448
set_scroll_region (NautilusIconContainer *container)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
449
{
450 451 452
	double x1, y1, x2, y2;
        double content_width, content_height;
	double scroll_width, scroll_height;
453
	int step_increment;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
454 455 456 457 458 459
	GtkAllocation *allocation;
	GtkAdjustment *vadj, *hadj;

	gnome_canvas_item_get_bounds (GNOME_CANVAS (container)->root,
				      &x1, &y1, &x2, &y2);

460 461
	content_width = x2 - x1;
	content_height = y2 - y1;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
462

463
	allocation = &GTK_WIDGET (container)->allocation;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
464

465 466
	scroll_width = MAX (content_width, allocation->width);
	scroll_height = MAX (content_height, allocation->height);
Elliot Lee's avatar
Elliot Lee committed
467

John Sullivan's avatar
John Sullivan committed
468
	/* FIXME bugzilla.eazel.com 622: Why are we subtracting one from each dimension? */
469 470 471 472 473
	nautilus_gnome_canvas_set_scroll_region
		(GNOME_CANVAS (container),
		 x1, y1,
		 x1 + scroll_width - 1,
		 y1 + scroll_height - 1);
474 475 476 477

	hadj = GTK_LAYOUT (container)->hadjustment;
	vadj = GTK_LAYOUT (container)->vadjustment;

478
	if (content_width <= allocation->width) {
479
		gtk_adjustment_set_value (hadj, x1);
480 481
	}
	if (content_height <= allocation->height) {
482
		gtk_adjustment_set_value (vadj, y1);
483 484 485 486 487 488 489 490 491 492 493 494 495
	}

	step_increment = nautilus_get_icon_size_for_zoom_level
		(container->details->zoom_level) / 4;

	if (hadj->step_increment != step_increment) {
		hadj->step_increment = step_increment;
		gtk_adjustment_changed (hadj);
	}
	if (vadj->step_increment != step_increment) {
		vadj->step_increment = step_increment;
		gtk_adjustment_changed (vadj);
	}
Ettore Perazzoli's avatar
Ettore Perazzoli committed
496 497
}

498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
static NautilusIconContainer *sort_hack_container;

static int
compare_icons (gconstpointer a, gconstpointer b)
{
	const NautilusIcon *icon_a, *icon_b;
	int result;

	icon_a = a;
	icon_b = b;

	result = 0;
	gtk_signal_emit (GTK_OBJECT (sort_hack_container),
			 signals[COMPARE_ICONS],
			 icon_a->data,
			 icon_b->data,
			 &result);
	return result;
}

518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
static void
resort_and_clear (NautilusIconContainer *container)
{
	sort_hack_container = container;
	container->details->icons = g_list_sort
		(container->details->icons, compare_icons);

	nautilus_icon_grid_clear (container->details->grid);
}

static void
auto_position_icon (NautilusIconContainer *container,
		    NautilusIcon *icon)
{
	ArtPoint position;

	nautilus_icon_grid_get_position (container->details->grid,
					 icon, &position);
	icon_set_position (icon, position.x, position.y);
}

539 540 541 542 543 544
static void
relayout (NautilusIconContainer *container)
{
	GList *p;
	NautilusIcon *icon;

545 546 547 548
	if (!container->details->auto_layout) {
		return;
	}

549
	resort_and_clear (container);
550

551 552 553 554 555 556 557 558 559 560 561 562
	/* An icon currently being stretched must be left in place.
	 * That's "drag_icon". This doesn't come up for cases where
	 * we are doing other kinds of drags, but if it did, the
	 * same logic would probably apply.
	 */

	/* Place the icon that must stay put first. */
	if (container->details->drag_icon != NULL) {
		nautilus_icon_grid_add (container->details->grid,
					container->details->drag_icon);
	}

563
	/* Place all the other icons. */
564 565 566
	for (p = container->details->icons; p != NULL; p = p->next) {
		icon = p->data;

567
		if (icon != container->details->drag_icon) {
568
			auto_position_icon (container, icon);
569 570
			nautilus_icon_grid_add (container->details->grid, icon);
		}
571 572 573
	}
}

574 575 576 577 578 579
static void
reload_icon_positions (NautilusIconContainer *container)
{
	GList *p, *no_position_icons;
	NautilusIcon *icon;
	gboolean have_stored_position;
580
	NautilusIconPosition position;
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595

	g_assert (!container->details->auto_layout);

	resort_and_clear (container);

	no_position_icons = NULL;

	/* Place all the icons with positions. */
	for (p = container->details->icons; p != NULL; p = p->next) {
		icon = p->data;

		have_stored_position = FALSE;
		gtk_signal_emit (GTK_OBJECT (container),
				 signals[GET_STORED_ICON_POSITION],
				 icon->data,
596 597
				 &position,
				 &have_stored_position);
598
		if (have_stored_position) {
599
			icon_set_position (icon, position.x, position.y);
600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
			nautilus_icon_grid_add (container->details->grid, icon);
		} else {
			no_position_icons = g_list_prepend (no_position_icons, icon);
		}
	}
	no_position_icons = g_list_reverse (no_position_icons);

	/* Place all the other icons. */
	for (p = no_position_icons; p != NULL; p = p->next) {
		icon = p->data;

		auto_position_icon (container, icon);
		nautilus_icon_grid_add (container->details->grid, icon);
	}

	g_list_free (no_position_icons);
}

618
static gboolean
Ettore Perazzoli's avatar
Ettore Perazzoli committed
619 620
idle_handler (gpointer data)
{
621 622
	NautilusIconContainer *container;
	NautilusIconContainerDetails *details;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
623 624 625

	GDK_THREADS_ENTER ();

626
	container = NAUTILUS_ICON_CONTAINER (data);
627
	details = container->details;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
628 629

	set_scroll_region (container);
630
	relayout (container);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
631

632
	details->idle_id = 0;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
633 634 635 636 637 638 639

	GDK_THREADS_LEAVE ();

	return FALSE;
}

static void
640
request_idle (NautilusIconContainer *container)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
641
{
642
	if (container->details->idle_id != 0) {
Ettore Perazzoli's avatar
Ettore Perazzoli committed
643
		return;
644
	}
Ettore Perazzoli's avatar
Ettore Perazzoli committed
645

646
	container->details->idle_id = gtk_idle_add (idle_handler, container);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
647 648 649 650 651
}


/* Container-level icon handling functions.  */

652 653 654
static gboolean
button_event_modifies_selection (GdkEventButton *event)
{
655
	return (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) != 0;
656 657
}

Ettore Perazzoli's avatar
Ettore Perazzoli committed
658
static gboolean
659 660
select_one_unselect_others (NautilusIconContainer *container,
			    NautilusIcon *icon_to_select)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
661 662 663 664 665
{
	GList *p;
	gboolean selection_changed;

	selection_changed = FALSE;
666
	
667
	for (p = container->details->icons; p != NULL; p = p->next) {
668
		NautilusIcon *icon;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
669 670

		icon = p->data;
671

672 673
		selection_changed |= icon_set_selected
			(container, icon, icon == icon_to_select);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
674
	}
675
	
Ettore Perazzoli's avatar
Ettore Perazzoli committed
676 677 678 679
	return selection_changed;
}

static gboolean
680
unselect_all (NautilusIconContainer *container)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
681
{
682
	return select_one_unselect_others (container, NULL);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
683 684
}

685
void
686
nautilus_icon_container_move_icon (NautilusIconContainer *container,
687 688 689 690
				   NautilusIcon *icon,
				   int x, int y,
				   double scale_x, double scale_y,
				   gboolean raise)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
691
{
692
	NautilusIconContainerDetails *details;
693
	gboolean emit_signal;
694
	NautilusIconPosition position;
695
	
696
	details = container->details;
697
	
698
	emit_signal = FALSE;
699 700 701 702 703 704
	
	if (!details->auto_layout) {
		if (x != icon->x || y != icon->y) {
			icon_set_position (icon, x, y);
			emit_signal = TRUE;
		}
705 706 707
	}
	
	if (scale_x != icon->scale_x || scale_y != icon->scale_y) {
708 709
		icon->scale_x = scale_x;
		icon->scale_y = scale_y;
710
		nautilus_icon_container_update_icon (container, icon);
711
		relayout (container);
712 713 714 715
		emit_signal = TRUE;
	}
	
	if (emit_signal) {
716 717 718 719
		position.x = x;
		position.y = y;
		position.scale_x = scale_x;
		position.scale_y = scale_y;
720 721
		gtk_signal_emit (GTK_OBJECT (container),
				 signals[ICON_POSITION_CHANGED],
722
				 icon->data, &position);
723 724 725
	}
	
	if (raise) {
726
		icon_raise (icon);
727
	}
Ettore Perazzoli's avatar
Ettore Perazzoli committed
728 729
}

730

Ettore Perazzoli's avatar
Ettore Perazzoli committed
731 732
/* Implementation of rubberband selection.  */

733
static void
734
rubberband_select (NautilusIconContainer *container,
735 736
		   const ArtDRect *previous_rect,
		   const ArtDRect *current_rect)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
737
{
738 739
	ArtDRect both_rects;
	GList *icons, *p;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
740
	gboolean selection_changed;
741
	NautilusIcon *icon;
742 743
	gboolean is_in;
		
744 745
	selection_changed = FALSE;

746 747
	/* As an optimization, ask the grid which icons intersect the rectangles. */
	art_drect_union (&both_rects, previous_rect, current_rect);
748 749
	icons = nautilus_icon_grid_get_intersecting_icons
		(container->details->grid, &both_rects);
750 751
	
	for (p = icons; p != NULL; p = p->next) {
Ettore Perazzoli's avatar
Ettore Perazzoli committed
752
		icon = p->data;
753
		
754
		is_in = nautilus_icon_canvas_item_hit_test_rectangle
755
			(icon->item, current_rect);
756

757 758
		g_assert (icon->was_selected_before_rubberband == FALSE
			  || icon->was_selected_before_rubberband == TRUE);
759 760
		selection_changed |= icon_set_selected
			(container, icon,
761
			 is_in ^ icon->was_selected_before_rubberband);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
762 763
	}

764
	g_list_free (icons);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
765

766
	if (selection_changed) {
Ettore Perazzoli's avatar
Ettore Perazzoli committed
767 768
		gtk_signal_emit (GTK_OBJECT (container),
				 signals[SELECTION_CHANGED]);
769
	}
Ettore Perazzoli's avatar
Ettore Perazzoli committed
770 771
}

772
static int
773
rubberband_timeout_callback (gpointer data)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
774
{
775
	NautilusIconContainer *container;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
776
	GtkWidget *widget;
777
	NautilusIconRubberbandInfo *band_info;
778
	int x, y;
779 780
	double x1, y1, x2, y2;
	double world_x, world_y;
781
	int x_scroll, y_scroll;
782
	ArtDRect selection_rect;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
783 784 785 786

	GDK_THREADS_ENTER ();

	widget = GTK_WIDGET (data);
787
	container = NAUTILUS_ICON_CONTAINER (data);
788
	band_info = &container->details->rubberband_info;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
789

790 791 792
	g_assert (band_info->timer_id != 0);
	g_assert (GNOME_IS_CANVAS_RECT (band_info->selection_rectangle));

Ettore Perazzoli's avatar
Ettore Perazzoli committed
793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815
	gdk_window_get_pointer (widget->window, &x, &y, NULL);

	if (x < 0) {
		x_scroll = x;
		x = 0;
	} else if (x >= widget->allocation.width) {
		x_scroll = x - widget->allocation.width + 1;
		x = widget->allocation.width - 1;
	} else {
		x_scroll = 0;
	}

	if (y < 0) {
		y_scroll = y;
		y = 0;
	} else if (y >= widget->allocation.height) {
		y_scroll = y - widget->allocation.height + 1;
		y = widget->allocation.height - 1;
	} else {
		y_scroll = 0;
	}

	if (y_scroll == 0 && x_scroll == 0
816
	    && band_info->prev_x == x && band_info->prev_y == y) {
Ettore Perazzoli's avatar
Ettore Perazzoli committed
817 818 819 820 821 822 823 824 825
		GDK_THREADS_LEAVE ();
		return TRUE;
	}

	scroll (container, x_scroll, y_scroll);

	gnome_canvas_window_to_world (GNOME_CANVAS (container),
				      x, y, &world_x, &world_y);

826
	if (world_x < band_info->start_x) {
Ettore Perazzoli's avatar
Ettore Perazzoli committed
827
		x1 = world_x;
828
		x2 = band_info->start_x;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
829
	} else {
830
		x1 = band_info->start_x;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
831 832 833
		x2 = world_x;
	}

834
	if (world_y < band_info->start_y) {
Ettore Perazzoli's avatar
Ettore Perazzoli committed
835
		y1 = world_y;
836
		y2 = band_info->start_y;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
837
	} else {
838
		y1 = band_info->start_y;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
839 840 841
		y2 = world_y;
	}

842 843 844 845 846
	gnome_canvas_item_set
		(band_info->selection_rectangle,
		 "x1", x1, "y1", y1,
		 "x2", x2, "y2", y2,
		 NULL);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
847

848 849 850 851 852
	selection_rect.x0 = x1;
	selection_rect.y0 = y1;
	selection_rect.x1 = x2;
	selection_rect.y1 = y2;

Ettore Perazzoli's avatar
Ettore Perazzoli committed
853
	rubberband_select (container,
854 855
			   &band_info->prev_rect,
			   &selection_rect);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
856

857 858
	band_info->prev_x = x;
	band_info->prev_y = y;
859 860

	band_info->prev_rect = selection_rect;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
861 862 863 864 865 866 867

	GDK_THREADS_LEAVE ();

	return TRUE;
}

static void
868
start_rubberbanding (NautilusIconContainer *container,
Ettore Perazzoli's avatar
Ettore Perazzoli committed
869 870
		     GdkEventButton *event)
{
871 872
	NautilusIconContainerDetails *details;
	NautilusIconRubberbandInfo *band_info;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
873 874
	GList *p;

875
	details = container->details;
876
	band_info = &details->rubberband_info;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
877

878
	for (p = details->icons; p != NULL; p = p->next) {
879
		NautilusIcon *icon;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
880 881 882 883 884

		icon = p->data;
		icon->was_selected_before_rubberband = icon->is_selected;
	}

885 886 887 888 889 890 891 892 893 894 895 896 897