nautilus-icon-container.c 234 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
   Copyright (C) 2000, 2001 Eazel, Inc.
7
   Copyright (C) 2002, 2003 Red Hat, Inc.
8
   
Ettore Perazzoli's avatar
Ettore Perazzoli committed
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.

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

#include <config.h>
29
#include <math.h>
30
#include "nautilus-icon-container.h"
31

Alexander Larsson's avatar
 
Alexander Larsson committed
32
#include "nautilus-debug-log.h"
33
#include "nautilus-global-preferences.h"
34
#include "nautilus-icon-private.h"
35
#include "nautilus-lib-self-check-functions.h"
Darin Adler's avatar
Darin Adler committed
36
#include "nautilus-marshal.h"
37 38
#include <atk/atkaction.h>
#include <eel/eel-accessibility.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
39
#include <eel/eel-background.h>
40
#include <eel/eel-vfs-extensions.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
41 42 43
#include <eel/eel-gdk-pixbuf-extensions.h>
#include <eel/eel-gnome-extensions.h>
#include <eel/eel-gtk-extensions.h>
Alexander Larsson's avatar
Alexander Larsson committed
44
#include <eel/eel-art-extensions.h>
45
#include <eel/eel-editable-label.h>
Darin Adler's avatar
Darin Adler committed
46
#include <eel/eel-marshal.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
47
#include <eel/eel-string.h>
48
#include <eel/eel-canvas-rect-ellipse.h>
49
#include <libgnomeui/gnome-icon-item.h>
50
#include <gdk/gdkkeysyms.h>
51
#include <gtk/gtkaccessible.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
52
#include <gtk/gtklayout.h>
53 54
#include <gtk/gtkmain.h>
#include <gtk/gtksignal.h>
55
#include <glib/gi18n.h>
56
#include <libgnome/gnome-macros.h>
57 58
#include <stdio.h>
#include <string.h>
59

Alexander Larsson's avatar
Alexander Larsson committed
60 61
#define TAB_NAVIGATION_DISABLED

Ettore Perazzoli's avatar
Ettore Perazzoli committed
62 63 64
/* Interval for updating the rubberband selection, in milliseconds.  */
#define RUBBERBAND_TIMEOUT_INTERVAL 10

65 66 67
/* Initial unpositioned icon value */
#define ICON_UNPOSITIONED_VALUE -1

68 69 70
/* Timeout for making the icon currently selected for keyboard operation visible.
 * If this is 0, you can get into trouble with extra scrolling after holding
 * down the arrow key for awhile when there are many items.
71
 */
72
#define KEYBOARD_ICON_REVEAL_TIMEOUT 10
Ettore Perazzoli's avatar
Ettore Perazzoli committed
73

74 75
#define CONTEXT_MENU_TIMEOUT_INTERVAL 500

76 77 78
/* Maximum amount of milliseconds the mouse button is allowed to stay down
 * and still be considered a click.
 */
79 80
#define MAX_CLICK_TIME 1500

81 82 83
/* Button assignments. */
#define DRAG_BUTTON 1
#define RUBBERBAND_BUTTON 1
84
#define MIDDLE_BUTTON 2
85
#define CONTEXTUAL_MENU_BUTTON 3
86
#define DRAG_MENU_BUTTON 2
87

88
/* Maximum size (pixels) allowed for icons at the standard zoom level. */
89
#define MINIMUM_IMAGE_SIZE 24
90
#define MAXIMUM_IMAGE_SIZE 96
91

Darin Adler's avatar
Darin Adler committed
92 93 94 95 96 97 98 99
#define ICON_PAD_LEFT 4
#define ICON_PAD_RIGHT 4
#define ICON_BASE_WIDTH 96

#define ICON_PAD_TOP 4
#define ICON_PAD_BOTTOM 4

#define CONTAINER_PAD_LEFT 4
100
#define CONTAINER_PAD_RIGHT 4
Darin Adler's avatar
Darin Adler committed
101 102 103
#define CONTAINER_PAD_TOP 4
#define CONTAINER_PAD_BOTTOM 4

104
#define STANDARD_ICON_GRID_WIDTH 155
105

Dave Camp's avatar
Dave Camp committed
106 107
#define TEXT_BESIDE_ICON_GRID_WIDTH 205

108
/* Desktop layout mode defines */
109
#define DESKTOP_PAD_HORIZONTAL 	10
110
#define DESKTOP_PAD_VERTICAL 	10
111 112
#define SNAP_SIZE_X 		78
#define SNAP_SIZE_Y 		20
113

114 115 116
/* Value used to protect against icons being dragged outside of the desktop bounds */
#define DESKTOP_ICON_SAFETY_PAD 10

117 118
#define DEFAULT_SELECTION_BOX_ALPHA 0x40
#define DEFAULT_HIGHLIGHT_ALPHA 0xff
119
#define DEFAULT_NORMAL_ALPHA 0xff
120
#define DEFAULT_PRELIGHT_ALPHA 0xff
121 122 123
#define DEFAULT_LIGHT_INFO_COLOR 0xAAAAFD
#define DEFAULT_DARK_INFO_COLOR  0x33337F

124 125 126 127 128 129 130 131 132
#define DEFAULT_NORMAL_ICON_RENDER_MODE 0
#define DEFAULT_PRELIGHT_ICON_RENDER_MODE 1
#define DEFAULT_NORMAL_ICON_SATURATION 255
#define DEFAULT_PRELIGHT_ICON_SATURATION 255
#define DEFAULT_NORMAL_ICON_BRIGHTNESS 255
#define DEFAULT_PRELIGHT_ICON_BRIGHTNESS 255
#define DEFAULT_NORMAL_ICON_LIGHTEN 0
#define DEFAULT_PRELIGHT_ICON_LIGHTEN 0

133 134 135
#define MINIMUM_EMBEDDED_TEXT_RECT_WIDTH       20
#define MINIMUM_EMBEDDED_TEXT_RECT_HEIGHT      20

136 137 138 139 140
/* If icon size is bigger than this, request large embedded text.
 * Its selected so that the non-large text should fit in "normal" icon sizes
 */
#define ICON_SIZE_FOR_LARGE_EMBEDDED_TEXT 55

141 142 143
/* From nautilus-icon-canvas-item.c */
#define MAX_TEXT_WIDTH_BESIDE 90

144 145
#define SNAP_HORIZONTAL(func,x) ((func ((double)((x) - DESKTOP_PAD_HORIZONTAL) / SNAP_SIZE_X) * SNAP_SIZE_X) + DESKTOP_PAD_HORIZONTAL)
#define SNAP_VERTICAL(func, y) ((func ((double)((y) - DESKTOP_PAD_VERTICAL) / SNAP_SIZE_Y) * SNAP_SIZE_Y) + DESKTOP_PAD_VERTICAL)
146 147 148 149 150 151

#define SNAP_NEAREST_HORIZONTAL(x) SNAP_HORIZONTAL (eel_round, x)
#define SNAP_NEAREST_VERTICAL(y) SNAP_VERTICAL (eel_round, y)

#define SNAP_CEIL_HORIZONTAL(x) SNAP_HORIZONTAL (ceil, x)
#define SNAP_CEIL_VERTICAL(y) SNAP_VERTICAL (ceil, y)
152

153 154 155
/* Copied from NautilusIconContainer */
#define NAUTILUS_ICON_CONTAINER_SEARCH_DIALOG_TIMEOUT 5000

156 157
enum {
	ACTION_ACTIVATE,
158
	ACTION_MENU,
159 160 161 162 163 164 165 166 167 168
	LAST_ACTION
};

typedef struct {
	GList *selection;
	char *action_descriptions[LAST_ACTION];
} NautilusIconContainerAccessiblePrivate;

static GType         nautilus_icon_container_accessible_get_type (void);

169
static void          activate_selected_items                        (NautilusIconContainer *container);
170 171
static void          activate_selected_items_alternate              (NautilusIconContainer *container,
								     NautilusIcon          *icon);
172 173 174 175 176 177 178
static void          nautilus_icon_container_theme_changed          (gpointer               user_data);
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);
179
static gboolean      all_selected                                   (NautilusIconContainer *container);
Alexander Larsson's avatar
Alexander Larsson committed
180
static gboolean      has_selection                                  (NautilusIconContainer *container);
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
static void          icon_destroy                                   (NautilusIconContainer *container,
								     NautilusIcon          *icon);
static void          end_renaming_mode                              (NautilusIconContainer *container,
								     gboolean               commit);
static NautilusIcon *get_icon_being_renamed                         (NautilusIconContainer *container);
static void          finish_adding_new_icons                        (NautilusIconContainer *container);
static void          update_label_color                             (EelBackground         *background,
								     NautilusIconContainer *icon_container);
static void          icon_get_bounding_box                          (NautilusIcon          *icon,
								     int                   *x1_return,
								     int                   *y1_return,
								     int                   *x2_return,
								     int                   *y2_return);
static gboolean      is_renaming                                    (NautilusIconContainer *container);
static gboolean      is_renaming_pending                            (NautilusIconContainer *container);
static void          process_pending_icon_to_rename                 (NautilusIconContainer *container);
static void          setup_label_gcs                                (NautilusIconContainer *container);
static void          nautilus_icon_container_stop_monitor_top_left  (NautilusIconContainer *container,
								     NautilusIconData      *data,
								     gconstpointer          client);
static void          nautilus_icon_container_start_monitor_top_left (NautilusIconContainer *container,
								     NautilusIconData      *data,
203 204
								     gconstpointer          client,
								     gboolean               large_text);
205 206
static void          handle_vadjustment_changed                     (GtkAdjustment         *adjustment,
								     NautilusIconContainer *container);
207
static GList *       nautilus_icon_container_get_selected_icons (NautilusIconContainer *container);
208
static void          nautilus_icon_container_update_visible_icons   (NautilusIconContainer *container);
209 210
static void          reveal_icon                                    (NautilusIconContainer *container,
								     NautilusIcon *icon);
211

212 213 214 215
static void	     nautilus_icon_container_set_rtl_positions (NautilusIconContainer *container);
static double	     get_mirror_x_position                     (NautilusIconContainer *container,
								NautilusIcon *icon,
								double x);
216

217
static gpointer accessible_parent_class;
218 219 220 221 222

static GQuark accessible_private_data_quark = 0;

static const char *nautilus_icon_container_accessible_action_names[] = {
	"activate",
223
	"menu",
224 225 226 227 228
	NULL
};

static const char *nautilus_icon_container_accessible_action_descriptions[] = {
	"Activate selected items",
229
	"Popup context menu",
230 231 232
	NULL
};

233
GNOME_CLASS_BOILERPLATE (NautilusIconContainer, nautilus_icon_container,
234
			 EelCanvas, EEL_TYPE_CANVAS)
235

236
/* The NautilusIconContainer signals.  */
237
enum {
Ettore Perazzoli's avatar
Ettore Perazzoli committed
238
	ACTIVATE,
239
	ACTIVATE_ALTERNATE,
240 241
	BAND_SELECT_STARTED,
	BAND_SELECT_ENDED,
242
	BUTTON_PRESS,
243
	CAN_ACCEPT_ITEM,
244
	CONTEXT_CLICK_BACKGROUND,
245
	CONTEXT_CLICK_SELECTION,
246
	MIDDLE_CLICK,
247
	GET_CONTAINER_URI,
248
	GET_ICON_URI,
249
	GET_ICON_DROP_TARGET_URI,
250 251
	GET_STORED_ICON_POSITION,
	ICON_POSITION_CHANGED,
252
	ICON_TEXT_CHANGED,
253 254
	ICON_STRETCH_STARTED,
	ICON_STRETCH_ENDED,
255
	RENAMING_ICON,
256
	LAYOUT_CHANGED,
Pavel Cisler's avatar
Pavel Cisler committed
257
	MOVE_COPY_ITEMS,
258
	HANDLE_NETSCAPE_URL,
259
	HANDLE_URI_LIST,
260
	HANDLE_TEXT,
261
	PREVIEW,
262 263 264 265
	SELECTION_CHANGED,
	ICON_ADDED,
	ICON_REMOVED,
	CLEARED,
266
	START_INTERACTIVE_SEARCH,
Ettore Perazzoli's avatar
Ettore Perazzoli committed
267 268
	LAST_SIGNAL
};
269 270 271 272 273 274 275 276 277

typedef struct {
	int **icon_grid;
	int *grid_memory;
	int num_rows;
	int num_columns;
	gboolean tight;
} PlacementGrid;

278
static guint signals[LAST_SIGNAL];
Ettore Perazzoli's avatar
Ettore Perazzoli committed
279

280
/* Functions dealing with NautilusIcons.  */
Ettore Perazzoli's avatar
Ettore Perazzoli committed
281 282

static void
283
icon_free (NautilusIcon *icon)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
284
{
285
	/* Destroy this canvas item; the parent will unref it. */
286
	gtk_object_destroy (GTK_OBJECT (icon->item));
287
	g_free (icon);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
288 289
}

Mike Engber's avatar
 
Mike Engber committed
290 291 292 293 294 295
static gboolean
icon_is_positioned (const NautilusIcon *icon)
{
	return icon->x != ICON_UNPOSITIONED_VALUE && icon->y != ICON_UNPOSITIONED_VALUE;
}

Ettore Perazzoli's avatar
Ettore Perazzoli committed
296
static void
297
icon_set_position (NautilusIcon *icon,
298
		   double x, double y)
299 300
{	
	NautilusIconContainer *container;
301
	double pixels_per_unit;	
302
	int left, top, right, bottom;
303
	int width;
304
	int container_x, container_y, container_width, container_height;
Alexander Larsson's avatar
Alexander Larsson committed
305
	EelDRect icon_bounds;
306

307
	if (icon->x == x && icon->y == y) {
308
		return;
309
	}
Ettore Perazzoli's avatar
Ettore Perazzoli committed
310

311
	container = NAUTILUS_ICON_CONTAINER (EEL_CANVAS_ITEM (icon->item)->canvas);
312

313 314 315 316
	if (icon == get_icon_being_renamed (container)) {
		end_renaming_mode (container, TRUE);
	}

317
	if (nautilus_icon_container_get_is_fixed_size (container)) {
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
		/*  FIXME: This should be:
		    
		container_x = GTK_WIDGET (container)->allocation.x;
		container_y = GTK_WIDGET (container)->allocation.y;
		container_width = GTK_WIDGET (container)->allocation.width;
		container_height = GTK_WIDGET (container)->allocation.height;

		But for some reason the widget allocation is sometimes not done
		at startup, and the allocation is then only 45x60. which is
		really bad.

		For now, we have a cheesy workaround:
		*/
		container_x = 0;
		container_y = 0;
		container_width = gdk_screen_width ();
		container_height = gdk_screen_height ();
335
		pixels_per_unit = EEL_CANVAS (container)->pixels_per_unit;
336
		/* Clip the position of the icon within our desktop bounds */
337 338 339 340
		left = container_x / pixels_per_unit;
		top =  container_y / pixels_per_unit;
		right = left + container_width / pixels_per_unit;
		bottom = top + container_height / pixels_per_unit;
341

342 343
		icon_bounds = nautilus_icon_canvas_item_get_icon_rectangle (icon->item);
		width = icon_bounds.x1 - icon_bounds.x0;
344
				
345 346 347 348 349 350 351 352 353 354 355 356 357 358
		if (nautilus_icon_container_is_layout_rtl(container)) {
			if (x + width < left + DESKTOP_ICON_SAFETY_PAD) {
				x = left + DESKTOP_ICON_SAFETY_PAD - width;
			}
			if (x + width > right) {
				x = right - width;
			}
		} else {
			if (x > right - DESKTOP_ICON_SAFETY_PAD) {
				x = right - DESKTOP_ICON_SAFETY_PAD;
			}
			if (x < left) {
				x = left;
			}
359
		}
360
		if (y > bottom - DESKTOP_ICON_SAFETY_PAD) {
361
			y = bottom - DESKTOP_ICON_SAFETY_PAD;
362
		}
363 364
		if (y < top) {
			y = top;
365
		}		
366 367
	}

368 369 370 371 372 373
	if (icon->x == ICON_UNPOSITIONED_VALUE) {
		icon->x = 0;
	}
	if (icon->y == ICON_UNPOSITIONED_VALUE) {
		icon->y = 0;
	}
374
	
375
	eel_canvas_item_move (EEL_CANVAS_ITEM (icon->item),
376 377
				x - icon->x,
				y - icon->y);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
378 379 380 381 382

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

383
static void
384 385
icon_get_size (NautilusIconContainer *container,
	       NautilusIcon *icon,
386
	       guint *size)
387
{
388 389
	if (size != NULL) {
		*size = MAX (nautilus_get_icon_size_for_zoom_level (container->details->zoom_level)
390
			       * icon->scale, NAUTILUS_ICON_SIZE_SMALLEST);
391
	}
392 393
}

Darin Adler's avatar
Darin Adler committed
394 395 396 397 398
/* The icon_set_size function is 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.
 */
399
static void
400 401
icon_set_size (NautilusIconContainer *container,
	       NautilusIcon *icon,
402
	       guint icon_size,
403
	       gboolean snap,
404
	       gboolean update_position)
405
{
406
	guint old_size;
407 408
	double scale;

409 410
	icon_get_size (container, icon, &old_size);
	if (icon_size == old_size) {
411
		return;
412
	}
413

414
	scale = (double) icon_size /
415 416
		nautilus_get_icon_size_for_zoom_level
		(container->details->zoom_level);
417
	nautilus_icon_container_move_icon (container, icon,
Darin Adler's avatar
Darin Adler committed
418
					   icon->x, icon->y,
419
					   scale, FALSE,
420
					   snap, update_position);
421 422
}

Ettore Perazzoli's avatar
Ettore Perazzoli committed
423
static void
424
icon_raise (NautilusIcon *icon)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
425
{
426
	EelCanvasItem *item, *band;
427
	
428
	item = EEL_CANVAS_ITEM (icon->item);
429
	band = NAUTILUS_ICON_CONTAINER (item->canvas)->details->rubberband_info.selection_rectangle;
430
	
431
	eel_canvas_item_send_behind (item, band);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
432 433
}

434 435 436
static void
emit_stretch_started (NautilusIconContainer *container, NautilusIcon *icon)
{
437
	g_signal_emit (container,
438
			 signals[ICON_STRETCH_STARTED], 0,
439 440 441 442 443 444
			 icon->data);
}

static void
emit_stretch_ended (NautilusIconContainer *container, NautilusIcon *icon)
{
445
	g_signal_emit (container,
446
			 signals[ICON_STRETCH_ENDED], 0,
447 448 449
			 icon->data);
}

Ettore Perazzoli's avatar
Ettore Perazzoli committed
450
static void
451 452
icon_toggle_selected (NautilusIconContainer *container,
		      NautilusIcon *icon)
453
{		
454
	end_renaming_mode (container, TRUE);
455

456
	icon->is_selected = !icon->is_selected;
457
	eel_canvas_item_set (EEL_CANVAS_ITEM (icon->item),
458 459
			     "highlighted_for_selection", (gboolean) icon->is_selected,
			     NULL);
460 461 462 463 464 465

	/* 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;
466
		nautilus_icon_canvas_item_set_show_stretch_handles (icon->item, FALSE);
467 468 469 470 471
		/* snap the icon if necessary */
		if (container->details->keep_aligned) {
			nautilus_icon_container_move_icon (container,
							   icon,
							   icon->x, icon->y,
472
							   icon->scale,
473 474 475
							   FALSE, TRUE, TRUE);
		}
		
476
		emit_stretch_ended (container, icon);
477
	}
478 479 480 481 482

	/* Raise each newly-selected icon to the front as it is selected. */
	if (icon->is_selected) {
		icon_raise (icon);
	}
Ettore Perazzoli's avatar
Ettore Perazzoli committed
483 484
}

485
/* Select an icon. Return TRUE if selection has changed. */
Ettore Perazzoli's avatar
Ettore Perazzoli committed
486
static gboolean
487 488
icon_set_selected (NautilusIconContainer *container,
		   NautilusIcon *icon,
489
		   gboolean select)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
490
{
491 492 493 494
	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
495
		return FALSE;
496
	}
497

498
	icon_toggle_selected (container, icon);
499
	g_assert (select == icon->is_selected);
500
	return TRUE;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
501 502 503
}

static void
504
icon_get_bounding_box (NautilusIcon *icon,
505 506
		       int *x1_return, int *y1_return,
		       int *x2_return, int *y2_return)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
507 508 509
{
	double x1, y1, x2, y2;

510
	eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (icon->item),
Ettore Perazzoli's avatar
Ettore Perazzoli committed
511 512 513 514 515 516 517 518
				      &x1, &y1, &x2, &y2);

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

519
/* Utility functions for NautilusIconContainer.  */
Ettore Perazzoli's avatar
Ettore Perazzoli committed
520

521
gboolean
Pavel Cisler's avatar
Pavel Cisler committed
522 523
nautilus_icon_container_scroll (NautilusIconContainer *container,
				int delta_x, int delta_y)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
524 525
{
	GtkAdjustment *hadj, *vadj;
526
	int old_h_value, old_v_value;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
527

528 529
	hadj = gtk_layout_get_hadjustment (GTK_LAYOUT (container));
	vadj = gtk_layout_get_vadjustment (GTK_LAYOUT (container));
Ettore Perazzoli's avatar
Ettore Perazzoli committed
530

531 532 533 534 535 536 537 538
	/* Store the old ajustment values so we can tell if we
	 * ended up actually scrolling. We may not have in a case
	 * where the resulting value got pinned to the adjustment
	 * min or max.
	 */
	old_h_value = hadj->value;
	old_v_value = vadj->value;
	
Ramiro Estrugo's avatar
Ramiro Estrugo committed
539 540
	eel_gtk_adjustment_set_value (hadj, hadj->value + delta_x);
	eel_gtk_adjustment_set_value (vadj, vadj->value + delta_y);
541 542 543

	/* return TRUE if we did scroll */
	return hadj->value != old_h_value || vadj->value != old_v_value;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
544 545
}

Mike Engber's avatar
 
Mike Engber committed
546
static void
547 548
pending_icon_to_reveal_destroy_callback (NautilusIconCanvasItem *item,
					 NautilusIconContainer *container)
Mike Engber's avatar
 
Mike Engber committed
549
{
550
	g_assert (NAUTILUS_IS_ICON_CONTAINER (container));
Mike Engber's avatar
 
Mike Engber committed
551 552
	g_assert (container->details->pending_icon_to_reveal != NULL);
	g_assert (container->details->pending_icon_to_reveal->item == item);
553

Mike Engber's avatar
 
Mike Engber committed
554 555 556 557 558 559 560 561 562 563 564 565
	container->details->pending_icon_to_reveal = NULL;
}

static NautilusIcon*
get_pending_icon_to_reveal (NautilusIconContainer *container)
{
	return container->details->pending_icon_to_reveal;
}

static void
set_pending_icon_to_reveal (NautilusIconContainer *container, NautilusIcon *icon)
{
566
	NautilusIcon *old_icon;
Mike Engber's avatar
 
Mike Engber committed
567
	
568
	old_icon = container->details->pending_icon_to_reveal;
Mike Engber's avatar
 
Mike Engber committed
569
	
570
	if (icon == old_icon) {
Mike Engber's avatar
 
Mike Engber committed
571 572 573
		return;
	}
	
574 575 576 577 578
	if (old_icon != NULL) {
		g_signal_handlers_disconnect_by_func
			(old_icon->item,
			 G_CALLBACK (pending_icon_to_reveal_destroy_callback),
			 container);
Mike Engber's avatar
 
Mike Engber committed
579 580 581
	}
	
	if (icon != NULL) {
582
		g_signal_connect (icon->item, "destroy",
583 584
				  G_CALLBACK (pending_icon_to_reveal_destroy_callback),
				  container);
Mike Engber's avatar
 
Mike Engber committed
585 586 587 588 589
	}
	
	container->details->pending_icon_to_reveal = icon;
}

590
static void
Alexander Larsson's avatar
Alexander Larsson committed
591
item_get_canvas_bounds (EelCanvasItem *item, EelIRect *bounds)
592
{
Alexander Larsson's avatar
Alexander Larsson committed
593
	EelDRect world_rect;
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
	
	eel_canvas_item_get_bounds (item,
				    &world_rect.x0,
				    &world_rect.y0,
				    &world_rect.x1,
				    &world_rect.y1);
	eel_canvas_item_i2w (item->parent,
			     &world_rect.x0,
			     &world_rect.y0);
	eel_canvas_item_i2w (item->parent,
			     &world_rect.x1,
			     &world_rect.y1);
	eel_canvas_w2c (item->canvas,
			world_rect.x0,
			world_rect.y0,
			&bounds->x0,
			&bounds->y0);
	eel_canvas_w2c (item->canvas,
			world_rect.x1,
			world_rect.y1,
			&bounds->x1,
			&bounds->y1);
}

Ettore Perazzoli's avatar
Ettore Perazzoli committed
618
static void
619 620
reveal_icon (NautilusIconContainer *container,
	     NautilusIcon *icon)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
621
{
622
	NautilusIconContainerDetails *details;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
623 624
	GtkAllocation *allocation;
	GtkAdjustment *hadj, *vadj;
Alexander Larsson's avatar
Alexander Larsson committed
625
	EelIRect bounds;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
626

Mike Engber's avatar
 
Mike Engber committed
627 628 629 630 631 632 633
	if (!icon_is_positioned (icon)) {
		set_pending_icon_to_reveal (container, icon);
		return;
	}
	
	set_pending_icon_to_reveal (container, NULL);

634
	details = container->details;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
635 636
	allocation = &GTK_WIDGET (container)->allocation;

637 638
	hadj = gtk_layout_get_hadjustment (GTK_LAYOUT (container));
	vadj = gtk_layout_get_vadjustment (GTK_LAYOUT (container));
Ettore Perazzoli's avatar
Ettore Perazzoli committed
639

640
	item_get_canvas_bounds (EEL_CANVAS_ITEM (icon->item), &bounds);
641
	if (bounds.y0 < vadj->value) {
Ramiro Estrugo's avatar
Ramiro Estrugo committed
642
		eel_gtk_adjustment_set_value (vadj, bounds.y0);
643
	} else if (bounds.y1 > vadj->value + allocation->height) {
Ramiro Estrugo's avatar
Ramiro Estrugo committed
644
		eel_gtk_adjustment_set_value
645
			(vadj, bounds.y1 - allocation->height);
646
	}
Ettore Perazzoli's avatar
Ettore Perazzoli committed
647

648
	if (bounds.x0 < hadj->value) {
Ramiro Estrugo's avatar
Ramiro Estrugo committed
649
		eel_gtk_adjustment_set_value (hadj, bounds.x0);
650
	} else if (bounds.x1 > hadj->value + allocation->width) {
Ramiro Estrugo's avatar
Ramiro Estrugo committed
651
		eel_gtk_adjustment_set_value
652
			(hadj, bounds.x1 - allocation->width);
653
	}
Ettore Perazzoli's avatar
Ettore Perazzoli committed
654 655
}

Mike Engber's avatar
 
Mike Engber committed
656 657 658 659 660 661 662 663 664 665 666 667
static void
process_pending_icon_to_reveal (NautilusIconContainer *container)
{
	NautilusIcon *pending_icon_to_reveal;
	
	pending_icon_to_reveal = get_pending_icon_to_reveal (container);
	
	if (pending_icon_to_reveal != NULL) {
		reveal_icon (container, pending_icon_to_reveal);
	}
}

668
static gboolean
669
keyboard_icon_reveal_timeout_callback (gpointer data)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
670
{
671 672
	NautilusIconContainer *container;
	NautilusIcon *icon;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
673

674
	container = NAUTILUS_ICON_CONTAINER (data);
675 676 677
	icon = container->details->keyboard_icon_to_reveal;

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

Darin Adler's avatar
Darin Adler committed
679
	/* Only reveal the icon if it's still the keyboard focus or if
680 681 682 683
	 * it's still selected. Someone originally thought we should
	 * cancel this reveal if the user manages to sneak a direct
	 * scroll in before the timeout fires, but we later realized
	 * this wouldn't actually be an improvement 
684
	 * (see bugzilla.gnome.org 40612).
685 686 687 688
	 */
	if (icon == container->details->keyboard_focus
	    || icon->is_selected) {
		reveal_icon (container, icon);
689
	}
690
	container->details->keyboard_icon_reveal_timer_id = 0;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
691 692 693 694 695

	return FALSE;
}

static void
696
unschedule_keyboard_icon_reveal (NautilusIconContainer *container)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
697
{
698
	NautilusIconContainerDetails *details;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
699

700
	details = container->details;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
701

702
	if (details->keyboard_icon_reveal_timer_id != 0) {
703
		g_source_remove (details->keyboard_icon_reveal_timer_id);
704
	}
Ettore Perazzoli's avatar
Ettore Perazzoli committed
705 706 707
}

static void
708 709
schedule_keyboard_icon_reveal (NautilusIconContainer *container,
			       NautilusIcon *icon)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
710
{
711
	NautilusIconContainerDetails *details;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
712

713
	details = container->details;
Ettore Perazzoli's avatar
Ettore Perazzoli committed
714

715
	unschedule_keyboard_icon_reveal (container);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
716

717 718
	details->keyboard_icon_to_reveal = icon;
	details->keyboard_icon_reveal_timer_id
719 720 721
		= g_timeout_add (KEYBOARD_ICON_REVEAL_TIMEOUT,
				 keyboard_icon_reveal_timeout_callback,
				 container);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
722 723 724
}

static void
725
clear_keyboard_focus (NautilusIconContainer *container)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
726
{
727
        if (container->details->keyboard_focus != NULL) {
728
		eel_canvas_item_set (EEL_CANVAS_ITEM (container->details->keyboard_focus->item),
729
				       "highlighted_as_keyboard_focus", 0,
730 731 732
				       NULL);
	}
	
733 734 735
	container->details->keyboard_focus = NULL;
}

736 737 738 739 740 741 742
static void inline
emit_atk_focus_tracker_notify (NautilusIcon *icon)
{
	AtkObject *atk_object = eel_accessibility_for_object (icon->item);
	atk_focus_tracker_notify (atk_object);
}

Darin Adler's avatar
Darin Adler committed
743
/* Set @icon as the icon currently selected for keyboard operations. */
744
static void
745 746
set_keyboard_focus (NautilusIconContainer *container,
		    NautilusIcon *icon)
747 748 749 750 751
{
	g_assert (icon != NULL);

	if (icon == container->details->keyboard_focus) {
		return;
752
	}
753 754 755 756 757

	clear_keyboard_focus (container);

	container->details->keyboard_focus = icon;

758
	eel_canvas_item_set (EEL_CANVAS_ITEM (container->details->keyboard_focus->item),
759 760
			       "highlighted_as_keyboard_focus", 1,
			       NULL);
761 762

	emit_atk_focus_tracker_notify (icon);
Ettore Perazzoli's avatar
Ettore Perazzoli committed
763 764
}

765 766 767 768 769 770 771 772 773 774 775 776 777
static void
set_keyboard_rubberband_start (NautilusIconContainer *container,
			       NautilusIcon *icon)
{
	container->details->keyboard_rubberband_start = icon;
}

static void
clear_keyboard_rubberband_start (NautilusIconContainer *container)
{
	container->details->keyboard_rubberband_start = NULL;
}

778 779 780 781 782
static void
get_all_icon_bounds (NautilusIconContainer *container,
		     double *x1, double *y1,
		     double *x2, double *y2)
{
783
	/* FIXME bugzilla.gnome.org 42477: Do we have to do something about the rubberband
784 785
	 * here? Any other non-icon items?
	 */
786 787
	eel_canvas_item_get_bounds
		(EEL_CANVAS (container)->root,
788 789
		 x1, y1, x2, y2);
}
Ettore Perazzoli's avatar
Ettore Perazzoli committed
790

791 792 793 794 795 796 797 798
/* Don't preserve visible white space the next time the scroll region
 * is recomputed when the container is not empty. */
void
nautilus_icon_container_reset_scroll_region (NautilusIconContainer *container)
{
	container->details->reset_scroll_region_trigger = TRUE;
}

799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825
/* Set a new scroll region without eliminating any of the currently-visible area. */
static void
canvas_set_scroll_region_include_visible_area (EelCanvas *canvas,
					       double x1, double y1,
					       double x2, double y2)
{
	double old_x1, old_y1, old_x2, old_y2;
	double old_scroll_x, old_scroll_y;
	double height, width;
	
	eel_canvas_get_scroll_region (canvas, &old_x1, &old_y1, &old_x2, &old_y2);

	width = (GTK_WIDGET (canvas)->allocation.width) / canvas->pixels_per_unit;
	height = (GTK_WIDGET (canvas)->allocation.height) / canvas->pixels_per_unit;

	old_scroll_x = gtk_layout_get_hadjustment (GTK_LAYOUT (canvas))->value;
	old_scroll_y = gtk_layout_get_vadjustment (GTK_LAYOUT (canvas))->value;

	x1 = MIN (x1, old_x1 + old_scroll_x);
	y1 = MIN (y1, old_y1 + old_scroll_y);
	x2 = MAX (x2, old_x1 + old_scroll_x + width);
	y2 = MAX (y2, old_y1 + old_scroll_y + height);

	eel_canvas_set_scroll_region
		(canvas, x1, y1, x2, y2);
}

826 827
void
nautilus_icon_container_update_scroll_region (NautilusIconContainer *container)
Ettore Perazzoli's avatar
Ettore Perazzoli committed
828
{
829
	double x1, y1, x2, y2;
830
	double pixels_per_unit;
831 832
	GtkAdjustment *hadj, *vadj;
	float step_increment;
833
	GtkAllocation *allocation;
834
	gboolean reset_scroll_region;
835 836

	if (nautilus_icon_container_get_is_fixed_size (container)) {
837
		pixels_per_unit = EEL_CANVAS (container)->pixels_per_unit;
838
		
839
		/* Set the scroll region to the size of the container allocation */
840 841 842
		allocation = &GTK_WIDGET (container)->allocation;
		eel_canvas_set_scroll_region
			(EEL_CANVAS (container),
843 844 845
			 (double) - container