No issues found
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3 /* nautilus-canvas-container.c - Canvas container widget.
4
5 Copyright (C) 1999, 2000 Free Software Foundation
6 Copyright (C) 2000, 2001 Eazel, Inc.
7 Copyright (C) 2002, 2003 Red Hat, Inc.
8
9 The Gnome Library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version.
13
14 The Gnome Library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Library General Public License for more details.
18
19 You should have received a copy of the GNU Library General Public
20 License along with the Gnome Library; see the file COPYING.LIB. If not,
21 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 Boston, MA 02111-1307, USA.
23
24 Authors: Ettore Perazzoli <ettore@gnu.org>,
25 Darin Adler <darin@bentspoon.com>
26 */
27
28 #include <config.h>
29 #include <math.h>
30 #include "nautilus-canvas-container.h"
31
32 #include "nautilus-global-preferences.h"
33 #include "nautilus-canvas-private.h"
34 #include "nautilus-lib-self-check-functions.h"
35 #include "nautilus-selection-canvas-item.h"
36 #include <atk/atkaction.h>
37 #include <eel/eel-accessibility.h>
38 #include <eel/eel-vfs-extensions.h>
39 #include <eel/eel-gtk-extensions.h>
40 #include <eel/eel-art-extensions.h>
41 #include <eel/eel-editable-label.h>
42
43 #include <gdk/gdkkeysyms.h>
44 #include <gtk/gtk.h>
45 #include <gdk/gdkx.h>
46 #include <glib/gi18n.h>
47 #include <stdio.h>
48 #include <string.h>
49
50 #define DEBUG_FLAG NAUTILUS_DEBUG_CANVAS_CONTAINER
51 #include "nautilus-debug.h"
52
53 #define TAB_NAVIGATION_DISABLED
54
55 /* Interval for updating the rubberband selection, in milliseconds. */
56 #define RUBBERBAND_TIMEOUT_INTERVAL 10
57
58 #define RUBBERBAND_SCROLL_THRESHOLD 5
59
60 /* Initial unpositioned icon value */
61 #define ICON_UNPOSITIONED_VALUE -1
62
63 /* Timeout for making the icon currently selected for keyboard operation visible.
64 * If this is 0, you can get into trouble with extra scrolling after holding
65 * down the arrow key for awhile when there are many items.
66 */
67 #define KEYBOARD_ICON_REVEAL_TIMEOUT 10
68
69 #define CONTEXT_MENU_TIMEOUT_INTERVAL 500
70
71 /* Maximum amount of milliseconds the mouse button is allowed to stay down
72 * and still be considered a click.
73 */
74 #define MAX_CLICK_TIME 1500
75
76 /* Button assignments. */
77 #define DRAG_BUTTON 1
78 #define RUBBERBAND_BUTTON 1
79 #define MIDDLE_BUTTON 2
80 #define CONTEXTUAL_MENU_BUTTON 3
81 #define DRAG_MENU_BUTTON 2
82
83 /* Maximum size (pixels) allowed for icons at the standard zoom level. */
84 #define MINIMUM_IMAGE_SIZE 24
85 #define MAXIMUM_IMAGE_SIZE 96
86
87 #define ICON_PAD_LEFT 4
88 #define ICON_PAD_RIGHT 4
89 #define ICON_BASE_WIDTH 96
90
91 #define ICON_PAD_TOP 4
92 #define ICON_PAD_BOTTOM 4
93
94 #define CONTAINER_PAD_LEFT 4
95 #define CONTAINER_PAD_RIGHT 4
96 #define CONTAINER_PAD_TOP 4
97 #define CONTAINER_PAD_BOTTOM 4
98
99 #define STANDARD_ICON_GRID_WIDTH 155
100
101 /* Desktop layout mode defines */
102 #define DESKTOP_PAD_HORIZONTAL 10
103 #define DESKTOP_PAD_VERTICAL 10
104 #define SNAP_SIZE_X 78
105 #define SNAP_SIZE_Y 20
106
107 #define MINIMUM_EMBEDDED_TEXT_RECT_WIDTH 20
108 #define MINIMUM_EMBEDDED_TEXT_RECT_HEIGHT 20
109
110 /* If icon size is bigger than this, request large embedded text.
111 * Its selected so that the non-large text should fit in "normal" icon sizes
112 */
113 #define ICON_SIZE_FOR_LARGE_EMBEDDED_TEXT 55
114
115 #define SNAP_HORIZONTAL(func,x) ((func ((double)((x) - DESKTOP_PAD_HORIZONTAL) / SNAP_SIZE_X) * SNAP_SIZE_X) + DESKTOP_PAD_HORIZONTAL)
116 #define SNAP_VERTICAL(func, y) ((func ((double)((y) - DESKTOP_PAD_VERTICAL) / SNAP_SIZE_Y) * SNAP_SIZE_Y) + DESKTOP_PAD_VERTICAL)
117
118 #define SNAP_NEAREST_HORIZONTAL(x) SNAP_HORIZONTAL (floor, x + .5)
119 #define SNAP_NEAREST_VERTICAL(y) SNAP_VERTICAL (floor, y + .5)
120
121 #define SNAP_CEIL_HORIZONTAL(x) SNAP_HORIZONTAL (ceil, x)
122 #define SNAP_CEIL_VERTICAL(y) SNAP_VERTICAL (ceil, y)
123
124 /* Copied from NautilusCanvasContainer */
125 #define NAUTILUS_CANVAS_CONTAINER_SEARCH_DIALOG_TIMEOUT 5
126
127 /* Copied from NautilusFile */
128 #define UNDEFINED_TIME ((time_t) (-1))
129
130 enum {
131 ACTION_ACTIVATE,
132 ACTION_MENU,
133 LAST_ACTION
134 };
135
136 typedef struct {
137 GList *selection;
138 char *action_descriptions[LAST_ACTION];
139 } NautilusCanvasContainerAccessiblePrivate;
140
141 static AtkObject * get_accessible (GtkWidget *widget);
142
143 static void preview_selected_items (NautilusCanvasContainer *container);
144 static void activate_selected_items (NautilusCanvasContainer *container);
145 static void activate_selected_items_alternate (NautilusCanvasContainer *container,
146 NautilusCanvasIcon *icon);
147 static void compute_stretch (StretchState *start,
148 StretchState *current);
149 static NautilusCanvasIcon *get_first_selected_icon (NautilusCanvasContainer *container);
150 static NautilusCanvasIcon *get_nth_selected_icon (NautilusCanvasContainer *container,
151 int index);
152 static gboolean has_multiple_selection (NautilusCanvasContainer *container);
153 static gboolean all_selected (NautilusCanvasContainer *container);
154 static gboolean has_selection (NautilusCanvasContainer *container);
155 static void icon_destroy (NautilusCanvasContainer *container,
156 NautilusCanvasIcon *icon);
157 static void end_renaming_mode (NautilusCanvasContainer *container,
158 gboolean commit);
159 static NautilusCanvasIcon *get_icon_being_renamed (NautilusCanvasContainer *container);
160 static void finish_adding_new_icons (NautilusCanvasContainer *container);
161 static inline void icon_get_bounding_box (NautilusCanvasIcon *icon,
162 int *x1_return,
163 int *y1_return,
164 int *x2_return,
165 int *y2_return,
166 NautilusCanvasItemBoundsUsage usage);
167 static gboolean is_renaming (NautilusCanvasContainer *container);
168 static gboolean is_renaming_pending (NautilusCanvasContainer *container);
169 static void process_pending_icon_to_rename (NautilusCanvasContainer *container);
170 static void nautilus_canvas_container_stop_monitor_top_left (NautilusCanvasContainer *container,
171 NautilusCanvasIconData *data,
172 gconstpointer client);
173 static void nautilus_canvas_container_start_monitor_top_left (NautilusCanvasContainer *container,
174 NautilusCanvasIconData *data,
175 gconstpointer client,
176 gboolean large_text);
177 static void handle_hadjustment_changed (GtkAdjustment *adjustment,
178 NautilusCanvasContainer *container);
179 static void handle_vadjustment_changed (GtkAdjustment *adjustment,
180 NautilusCanvasContainer *container);
181 static GList * nautilus_canvas_container_get_selected_icons (NautilusCanvasContainer *container);
182 static void nautilus_canvas_container_update_visible_icons (NautilusCanvasContainer *container);
183 static void reveal_icon (NautilusCanvasContainer *container,
184 NautilusCanvasIcon *icon);
185
186 static void nautilus_canvas_container_set_rtl_positions (NautilusCanvasContainer *container);
187 static double get_mirror_x_position (NautilusCanvasContainer *container,
188 NautilusCanvasIcon *icon,
189 double x);
190 static void text_ellipsis_limit_changed_container_callback (gpointer callback_data);
191
192 static int compare_icons_horizontal (NautilusCanvasContainer *container,
193 NautilusCanvasIcon *icon_a,
194 NautilusCanvasIcon *icon_b);
195
196 static int compare_icons_vertical (NautilusCanvasContainer *container,
197 NautilusCanvasIcon *icon_a,
198 NautilusCanvasIcon *icon_b);
199
200 static void store_layout_timestamps_now (NautilusCanvasContainer *container);
201
202 static const char *nautilus_canvas_container_accessible_action_names[] = {
203 "activate",
204 "menu",
205 NULL
206 };
207
208 static const char *nautilus_canvas_container_accessible_action_descriptions[] = {
209 "Activate selected items",
210 "Popup context menu",
211 NULL
212 };
213
214 G_DEFINE_TYPE (NautilusCanvasContainer, nautilus_canvas_container, EEL_TYPE_CANVAS);
215
216 /* The NautilusCanvasContainer signals. */
217 enum {
218 ACTIVATE,
219 ACTIVATE_ALTERNATE,
220 ACTIVATE_PREVIEWER,
221 BAND_SELECT_STARTED,
222 BAND_SELECT_ENDED,
223 BUTTON_PRESS,
224 CAN_ACCEPT_ITEM,
225 CONTEXT_CLICK_BACKGROUND,
226 CONTEXT_CLICK_SELECTION,
227 MIDDLE_CLICK,
228 GET_CONTAINER_URI,
229 GET_ICON_URI,
230 GET_ICON_DROP_TARGET_URI,
231 GET_STORED_ICON_POSITION,
232 ICON_POSITION_CHANGED,
233 GET_STORED_LAYOUT_TIMESTAMP,
234 STORE_LAYOUT_TIMESTAMP,
235 ICON_RENAME_STARTED,
236 ICON_RENAME_ENDED,
237 ICON_STRETCH_STARTED,
238 ICON_STRETCH_ENDED,
239 LAYOUT_CHANGED,
240 MOVE_COPY_ITEMS,
241 HANDLE_NETSCAPE_URL,
242 HANDLE_URI_LIST,
243 HANDLE_TEXT,
244 HANDLE_RAW,
245 SELECTION_CHANGED,
246 ICON_ADDED,
247 ICON_REMOVED,
248 CLEARED,
249 LAST_SIGNAL
250 };
251
252 typedef struct {
253 int **icon_grid;
254 int *grid_memory;
255 int num_rows;
256 int num_columns;
257 gboolean tight;
258 } PlacementGrid;
259
260 static guint signals[LAST_SIGNAL];
261
262 /* Functions dealing with NautilusIcons. */
263
264 static void
265 icon_free (NautilusCanvasIcon *icon)
266 {
267 /* Destroy this icon item; the parent will unref it. */
268 eel_canvas_item_destroy (EEL_CANVAS_ITEM (icon->item));
269 g_free (icon);
270 }
271
272 static gboolean
273 icon_is_positioned (const NautilusCanvasIcon *icon)
274 {
275 return icon->x != ICON_UNPOSITIONED_VALUE && icon->y != ICON_UNPOSITIONED_VALUE;
276 }
277
278
279 /* x, y are the top-left coordinates of the icon. */
280 static void
281 icon_set_position (NautilusCanvasIcon *icon,
282 double x, double y)
283 {
284 NautilusCanvasContainer *container;
285 double pixels_per_unit;
286 int container_left, container_top, container_right, container_bottom;
287 int x1, x2, y1, y2;
288 int container_x, container_y, container_width, container_height;
289 EelDRect icon_bounds;
290 int item_width, item_height;
291 int height_above, width_left;
292 int min_x, max_x, min_y, max_y;
293
294 if (icon->x == x && icon->y == y) {
295 return;
296 }
297
298 container = NAUTILUS_CANVAS_CONTAINER (EEL_CANVAS_ITEM (icon->item)->canvas);
299
300 if (icon == get_icon_being_renamed (container)) {
301 end_renaming_mode (container, TRUE);
302 }
303
304 if (nautilus_canvas_container_get_is_fixed_size (container)) {
305 /* FIXME: This should be:
306
307 container_x = GTK_WIDGET (container)->allocation.x;
308 container_y = GTK_WIDGET (container)->allocation.y;
309 container_width = GTK_WIDGET (container)->allocation.width;
310 container_height = GTK_WIDGET (container)->allocation.height;
311
312 But for some reason the widget allocation is sometimes not done
313 at startup, and the allocation is then only 45x60. which is
314 really bad.
315
316 For now, we have a cheesy workaround:
317 */
318 container_x = 0;
319 container_y = 0;
320 container_width = gdk_screen_width () - container_x
321 - container->details->left_margin
322 - container->details->right_margin;
323 container_height = gdk_screen_height () - container_y
324 - container->details->top_margin
325 - container->details->bottom_margin;
326 pixels_per_unit = EEL_CANVAS (container)->pixels_per_unit;
327 /* Clip the position of the icon within our desktop bounds */
328 container_left = container_x / pixels_per_unit;
329 container_top = container_y / pixels_per_unit;
330 container_right = container_left + container_width / pixels_per_unit;
331 container_bottom = container_top + container_height / pixels_per_unit;
332
333 icon_get_bounding_box (icon, &x1, &y1, &x2, &y2,
334 BOUNDS_USAGE_FOR_ENTIRE_ITEM);
335 item_width = x2 - x1;
336 item_height = y2 - y1;
337
338 icon_bounds = nautilus_canvas_item_get_icon_rectangle (icon->item);
339
340 /* determine icon rectangle relative to item rectangle */
341 height_above = icon_bounds.y0 - y1;
342 width_left = icon_bounds.x0 - x1;
343
344 min_x = container_left + DESKTOP_PAD_HORIZONTAL + width_left;
345 max_x = container_right - DESKTOP_PAD_HORIZONTAL - item_width + width_left;
346 x = CLAMP (x, min_x, max_x);
347
348 min_y = container_top + height_above + DESKTOP_PAD_VERTICAL;
349 max_y = container_bottom - DESKTOP_PAD_VERTICAL - item_height + height_above;
350 y = CLAMP (y, min_y, max_y);
351 }
352
353 if (icon->x == ICON_UNPOSITIONED_VALUE) {
354 icon->x = 0;
355 }
356 if (icon->y == ICON_UNPOSITIONED_VALUE) {
357 icon->y = 0;
358 }
359
360 eel_canvas_item_move (EEL_CANVAS_ITEM (icon->item),
361 x - icon->x,
362 y - icon->y);
363
364 icon->x = x;
365 icon->y = y;
366 }
367
368 static void
369 icon_get_size (NautilusCanvasContainer *container,
370 NautilusCanvasIcon *icon,
371 guint *size)
372 {
373 if (size != NULL) {
374 *size = MAX (nautilus_get_icon_size_for_zoom_level (container->details->zoom_level)
375 * icon->scale, NAUTILUS_ICON_SIZE_SMALLEST);
376 }
377 }
378
379 /* The icon_set_size function is used by the stretching user
380 * interface, which currently stretches in a way that keeps the aspect
381 * ratio. Later we might have a stretching interface that stretches Y
382 * separate from X and we will change this around.
383 */
384 static void
385 icon_set_size (NautilusCanvasContainer *container,
386 NautilusCanvasIcon *icon,
387 guint icon_size,
388 gboolean snap,
389 gboolean update_position)
390 {
391 guint old_size;
392 double scale;
393
394 icon_get_size (container, icon, &old_size);
395 if (icon_size == old_size) {
396 return;
397 }
398
399 scale = (double) icon_size /
400 nautilus_get_icon_size_for_zoom_level
401 (container->details->zoom_level);
402 nautilus_canvas_container_move_icon (container, icon,
403 icon->x, icon->y,
404 scale, FALSE,
405 snap, update_position);
406 }
407
408 static void
409 icon_raise (NautilusCanvasIcon *icon)
410 {
411 EelCanvasItem *item, *band;
412
413 item = EEL_CANVAS_ITEM (icon->item);
414 band = NAUTILUS_CANVAS_CONTAINER (item->canvas)->details->rubberband_info.selection_rectangle;
415
416 eel_canvas_item_send_behind (item, band);
417 }
418
419 static void
420 emit_stretch_started (NautilusCanvasContainer *container, NautilusCanvasIcon *icon)
421 {
422 g_signal_emit (container,
423 signals[ICON_STRETCH_STARTED], 0,
424 icon->data);
425 }
426
427 static void
428 emit_stretch_ended (NautilusCanvasContainer *container, NautilusCanvasIcon *icon)
429 {
430 g_signal_emit (container,
431 signals[ICON_STRETCH_ENDED], 0,
432 icon->data);
433 }
434
435 static void
436 icon_toggle_selected (NautilusCanvasContainer *container,
437 NautilusCanvasIcon *icon)
438 {
439 end_renaming_mode (container, TRUE);
440
441 icon->is_selected = !icon->is_selected;
442 eel_canvas_item_set (EEL_CANVAS_ITEM (icon->item),
443 "highlighted_for_selection", (gboolean) icon->is_selected,
444 NULL);
445
446 /* If the icon is deselected, then get rid of the stretch handles.
447 * No harm in doing the same if the item is newly selected.
448 */
449 if (icon == container->details->stretch_icon) {
450 container->details->stretch_icon = NULL;
451 nautilus_canvas_item_set_show_stretch_handles (icon->item, FALSE);
452 /* snap the icon if necessary */
453 if (container->details->keep_aligned) {
454 nautilus_canvas_container_move_icon (container,
455 icon,
456 icon->x, icon->y,
457 icon->scale,
458 FALSE, TRUE, TRUE);
459 }
460
461 emit_stretch_ended (container, icon);
462 }
463
464 /* Raise each newly-selected icon to the front as it is selected. */
465 if (icon->is_selected) {
466 icon_raise (icon);
467 }
468 }
469
470 /* Select an icon. Return TRUE if selection has changed. */
471 static gboolean
472 icon_set_selected (NautilusCanvasContainer *container,
473 NautilusCanvasIcon *icon,
474 gboolean select)
475 {
476 g_assert (select == FALSE || select == TRUE);
477 g_assert (icon->is_selected == FALSE || icon->is_selected == TRUE);
478
479 if (select == icon->is_selected) {
480 return FALSE;
481 }
482
483 icon_toggle_selected (container, icon);
484 g_assert (select == icon->is_selected);
485 return TRUE;
486 }
487
488 static inline void
489 icon_get_bounding_box (NautilusCanvasIcon *icon,
490 int *x1_return, int *y1_return,
491 int *x2_return, int *y2_return,
492 NautilusCanvasItemBoundsUsage usage)
493 {
494 double x1, y1, x2, y2;
495
496 if (usage == BOUNDS_USAGE_FOR_DISPLAY) {
497 eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (icon->item),
498 &x1, &y1, &x2, &y2);
499 } else if (usage == BOUNDS_USAGE_FOR_LAYOUT) {
500 nautilus_canvas_item_get_bounds_for_layout (icon->item,
501 &x1, &y1, &x2, &y2);
502 } else if (usage == BOUNDS_USAGE_FOR_ENTIRE_ITEM) {
503 nautilus_canvas_item_get_bounds_for_entire_item (icon->item,
504 &x1, &y1, &x2, &y2);
505 } else {
506 g_assert_not_reached ();
507 }
508
509 if (x1_return != NULL) {
510 *x1_return = x1;
511 }
512
513 if (y1_return != NULL) {
514 *y1_return = y1;
515 }
516
517 if (x2_return != NULL) {
518 *x2_return = x2;
519 }
520
521 if (y2_return != NULL) {
522 *y2_return = y2;
523 }
524 }
525
526 /* Utility functions for NautilusCanvasContainer. */
527
528 gboolean
529 nautilus_canvas_container_scroll (NautilusCanvasContainer *container,
530 int delta_x, int delta_y)
531 {
532 GtkAdjustment *hadj, *vadj;
533 int old_h_value, old_v_value;
534
535 hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container));
536 vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container));
537
538 /* Store the old ajustment values so we can tell if we
539 * ended up actually scrolling. We may not have in a case
540 * where the resulting value got pinned to the adjustment
541 * min or max.
542 */
543 old_h_value = gtk_adjustment_get_value (hadj);
544 old_v_value = gtk_adjustment_get_value (vadj);
545
546 gtk_adjustment_set_value (hadj, gtk_adjustment_get_value (hadj) + delta_x);
547 gtk_adjustment_set_value (vadj, gtk_adjustment_get_value (vadj) + delta_y);
548
549 /* return TRUE if we did scroll */
550 return gtk_adjustment_get_value (hadj) != old_h_value || gtk_adjustment_get_value (vadj) != old_v_value;
551 }
552
553 static void
554 pending_icon_to_reveal_destroy_callback (NautilusCanvasItem *item,
555 NautilusCanvasContainer *container)
556 {
557 g_assert (NAUTILUS_IS_CANVAS_CONTAINER (container));
558 g_assert (container->details->pending_icon_to_reveal != NULL);
559 g_assert (container->details->pending_icon_to_reveal->item == item);
560
561 container->details->pending_icon_to_reveal = NULL;
562 }
563
564 static NautilusCanvasIcon *
565 get_pending_icon_to_reveal (NautilusCanvasContainer *container)
566 {
567 return container->details->pending_icon_to_reveal;
568 }
569
570 static void
571 set_pending_icon_to_reveal (NautilusCanvasContainer *container, NautilusCanvasIcon *icon)
572 {
573 NautilusCanvasIcon *old_icon;
574
575 old_icon = container->details->pending_icon_to_reveal;
576
577 if (icon == old_icon) {
578 return;
579 }
580
581 if (old_icon != NULL) {
582 g_signal_handlers_disconnect_by_func
583 (old_icon->item,
584 G_CALLBACK (pending_icon_to_reveal_destroy_callback),
585 container);
586 }
587
588 if (icon != NULL) {
589 g_signal_connect (icon->item, "destroy",
590 G_CALLBACK (pending_icon_to_reveal_destroy_callback),
591 container);
592 }
593
594 container->details->pending_icon_to_reveal = icon;
595 }
596
597 static void
598 item_get_canvas_bounds (EelCanvasItem *item,
599 EelIRect *bounds,
600 gboolean safety_pad)
601 {
602 EelDRect world_rect;
603
604 eel_canvas_item_get_bounds (item,
605 &world_rect.x0,
606 &world_rect.y0,
607 &world_rect.x1,
608 &world_rect.y1);
609 eel_canvas_item_i2w (item->parent,
610 &world_rect.x0,
611 &world_rect.y0);
612 eel_canvas_item_i2w (item->parent,
613 &world_rect.x1,
614 &world_rect.y1);
615 if (safety_pad) {
616 world_rect.x0 -= ICON_PAD_LEFT + ICON_PAD_RIGHT;
617 world_rect.x1 += ICON_PAD_LEFT + ICON_PAD_RIGHT;
618
619 world_rect.y0 -= ICON_PAD_TOP + ICON_PAD_BOTTOM;
620 world_rect.y1 += ICON_PAD_TOP + ICON_PAD_BOTTOM;
621 }
622
623 eel_canvas_w2c (item->canvas,
624 world_rect.x0,
625 world_rect.y0,
626 &bounds->x0,
627 &bounds->y0);
628 eel_canvas_w2c (item->canvas,
629 world_rect.x1,
630 world_rect.y1,
631 &bounds->x1,
632 &bounds->y1);
633 }
634
635 static void
636 icon_get_row_and_column_bounds (NautilusCanvasContainer *container,
637 NautilusCanvasIcon *icon,
638 EelIRect *bounds,
639 gboolean safety_pad)
640 {
641 GList *p;
642 NautilusCanvasIcon *one_icon;
643 EelIRect one_bounds;
644
645 item_get_canvas_bounds (EEL_CANVAS_ITEM (icon->item), bounds, safety_pad);
646
647 for (p = container->details->icons; p != NULL; p = p->next) {
648 one_icon = p->data;
649
650 if (icon == one_icon) {
651 continue;
652 }
653
654 if (compare_icons_horizontal (container, icon, one_icon) == 0) {
655 item_get_canvas_bounds (EEL_CANVAS_ITEM (one_icon->item), &one_bounds, safety_pad);
656 bounds->x0 = MIN (bounds->x0, one_bounds.x0);
657 bounds->x1 = MAX (bounds->x1, one_bounds.x1);
658 }
659
660 if (compare_icons_vertical (container, icon, one_icon) == 0) {
661 item_get_canvas_bounds (EEL_CANVAS_ITEM (one_icon->item), &one_bounds, safety_pad);
662 bounds->y0 = MIN (bounds->y0, one_bounds.y0);
663 bounds->y1 = MAX (bounds->y1, one_bounds.y1);
664 }
665 }
666
667
668 }
669
670 static void
671 reveal_icon (NautilusCanvasContainer *container,
672 NautilusCanvasIcon *icon)
673 {
674 GtkAllocation allocation;
675 GtkAdjustment *hadj, *vadj;
676 EelIRect bounds;
677
678 if (!icon_is_positioned (icon)) {
679 set_pending_icon_to_reveal (container, icon);
680 return;
681 }
682
683 set_pending_icon_to_reveal (container, NULL);
684
685 gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
686
687 hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container));
688 vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container));
689
690 if (nautilus_canvas_container_is_auto_layout (container)) {
691 /* ensure that we reveal the entire row/column */
692 icon_get_row_and_column_bounds (container, icon, &bounds, TRUE);
693 } else {
694 item_get_canvas_bounds (EEL_CANVAS_ITEM (icon->item), &bounds, TRUE);
695 }
696 if (bounds.y0 < gtk_adjustment_get_value (vadj)) {
697 gtk_adjustment_set_value (vadj, bounds.y0);
698 } else if (bounds.y1 > gtk_adjustment_get_value (vadj) + allocation.height) {
699 gtk_adjustment_set_value
700 (vadj, bounds.y1 - allocation.height);
701 }
702
703 if (bounds.x0 < gtk_adjustment_get_value (hadj)) {
704 gtk_adjustment_set_value (hadj, bounds.x0);
705 } else if (bounds.x1 > gtk_adjustment_get_value (hadj) + allocation.width) {
706 gtk_adjustment_set_value
707 (hadj, bounds.x1 - allocation.width);
708 }
709 }
710
711 static void
712 process_pending_icon_to_reveal (NautilusCanvasContainer *container)
713 {
714 NautilusCanvasIcon *pending_icon_to_reveal;
715
716 pending_icon_to_reveal = get_pending_icon_to_reveal (container);
717
718 if (pending_icon_to_reveal != NULL) {
719 reveal_icon (container, pending_icon_to_reveal);
720 }
721 }
722
723 static gboolean
724 keyboard_icon_reveal_timeout_callback (gpointer data)
725 {
726 NautilusCanvasContainer *container;
727 NautilusCanvasIcon *icon;
728
729 container = NAUTILUS_CANVAS_CONTAINER (data);
730 icon = container->details->keyboard_icon_to_reveal;
731
732 g_assert (icon != NULL);
733
734 /* Only reveal the icon if it's still the keyboard focus or if
735 * it's still selected. Someone originally thought we should
736 * cancel this reveal if the user manages to sneak a direct
737 * scroll in before the timeout fires, but we later realized
738 * this wouldn't actually be an improvement
739 * (see bugzilla.gnome.org 40612).
740 */
741 if (icon == container->details->keyboard_focus
742 || icon->is_selected) {
743 reveal_icon (container, icon);
744 }
745 container->details->keyboard_icon_reveal_timer_id = 0;
746
747 return FALSE;
748 }
749
750 static void
751 unschedule_keyboard_icon_reveal (NautilusCanvasContainer *container)
752 {
753 NautilusCanvasContainerDetails *details;
754
755 details = container->details;
756
757 if (details->keyboard_icon_reveal_timer_id != 0) {
758 g_source_remove (details->keyboard_icon_reveal_timer_id);
759 }
760 }
761
762 static void
763 schedule_keyboard_icon_reveal (NautilusCanvasContainer *container,
764 NautilusCanvasIcon *icon)
765 {
766 NautilusCanvasContainerDetails *details;
767
768 details = container->details;
769
770 unschedule_keyboard_icon_reveal (container);
771
772 details->keyboard_icon_to_reveal = icon;
773 details->keyboard_icon_reveal_timer_id
774 = g_timeout_add (KEYBOARD_ICON_REVEAL_TIMEOUT,
775 keyboard_icon_reveal_timeout_callback,
776 container);
777 }
778
779 static void
780 clear_keyboard_focus (NautilusCanvasContainer *container)
781 {
782 if (container->details->keyboard_focus != NULL) {
783 eel_canvas_item_set (EEL_CANVAS_ITEM (container->details->keyboard_focus->item),
784 "highlighted_as_keyboard_focus", 0,
785 NULL);
786 }
787
788 container->details->keyboard_focus = NULL;
789 }
790
791 static void inline
792 emit_atk_focus_tracker_notify (NautilusCanvasIcon *icon)
793 {
794 AtkObject *atk_object = atk_gobject_accessible_for_object (G_OBJECT (icon->item));
795 atk_focus_tracker_notify (atk_object);
796 }
797
798 /* Set @icon as the icon currently selected for keyboard operations. */
799 static void
800 set_keyboard_focus (NautilusCanvasContainer *container,
801 NautilusCanvasIcon *icon)
802 {
803 g_assert (icon != NULL);
804
805 if (icon == container->details->keyboard_focus) {
806 return;
807 }
808
809 clear_keyboard_focus (container);
810
811 container->details->keyboard_focus = icon;
812
813 eel_canvas_item_set (EEL_CANVAS_ITEM (container->details->keyboard_focus->item),
814 "highlighted_as_keyboard_focus", 1,
815 NULL);
816
817 emit_atk_focus_tracker_notify (icon);
818 }
819
820 static void
821 set_keyboard_rubberband_start (NautilusCanvasContainer *container,
822 NautilusCanvasIcon *icon)
823 {
824 container->details->keyboard_rubberband_start = icon;
825 }
826
827 static void
828 clear_keyboard_rubberband_start (NautilusCanvasContainer *container)
829 {
830 container->details->keyboard_rubberband_start = NULL;
831 }
832
833 /* carbon-copy of eel_canvas_group_bounds(), but
834 * for NautilusCanvasContainerItems it returns the
835 * bounds for the âentire itemâ.
836 */
837 static void
838 get_icon_bounds_for_canvas_bounds (EelCanvasGroup *group,
839 double *x1, double *y1,
840 double *x2, double *y2,
841 NautilusCanvasItemBoundsUsage usage)
842 {
843 EelCanvasItem *child;
844 GList *list;
845 double tx1, ty1, tx2, ty2;
846 double minx, miny, maxx, maxy;
847 int set;
848
849 /* Get the bounds of the first visible item */
850
851 child = NULL; /* Unnecessary but eliminates a warning. */
852
853 set = FALSE;
854
855 for (list = group->item_list; list; list = list->next) {
856 child = list->data;
857
858 if (!NAUTILUS_IS_CANVAS_ITEM (child)) {
859 continue;
860 }
861
862 if (child->flags & EEL_CANVAS_ITEM_VISIBLE) {
863 set = TRUE;
864 if (!NAUTILUS_IS_CANVAS_ITEM (child) ||
865 usage == BOUNDS_USAGE_FOR_DISPLAY) {
866 eel_canvas_item_get_bounds (child, &minx, &miny, &maxx, &maxy);
867 } else if (usage == BOUNDS_USAGE_FOR_LAYOUT) {
868 nautilus_canvas_item_get_bounds_for_layout (NAUTILUS_CANVAS_ITEM (child),
869 &minx, &miny, &maxx, &maxy);
870 } else if (usage == BOUNDS_USAGE_FOR_ENTIRE_ITEM) {
871 nautilus_canvas_item_get_bounds_for_entire_item (NAUTILUS_CANVAS_ITEM (child),
872 &minx, &miny, &maxx, &maxy);
873 } else {
874 g_assert_not_reached ();
875 }
876 break;
877 }
878 }
879
880 /* If there were no visible items, return an empty bounding box */
881
882 if (!set) {
883 *x1 = *y1 = *x2 = *y2 = 0.0;
884 return;
885 }
886
887 /* Now we can grow the bounds using the rest of the items */
888
889 list = list->next;
890
891 for (; list; list = list->next) {
892 child = list->data;
893
894 if (!NAUTILUS_IS_CANVAS_ITEM (child)) {
895 continue;
896 }
897
898 if (!(child->flags & EEL_CANVAS_ITEM_VISIBLE))
899 continue;
900
901 if (!NAUTILUS_IS_CANVAS_ITEM (child) ||
902 usage == BOUNDS_USAGE_FOR_DISPLAY) {
903 eel_canvas_item_get_bounds (child, &tx1, &ty1, &tx2, &ty2);
904 } else if (usage == BOUNDS_USAGE_FOR_LAYOUT) {
905 nautilus_canvas_item_get_bounds_for_layout (NAUTILUS_CANVAS_ITEM (child),
906 &tx1, &ty1, &tx2, &ty2);
907 } else if (usage == BOUNDS_USAGE_FOR_ENTIRE_ITEM) {
908 nautilus_canvas_item_get_bounds_for_entire_item (NAUTILUS_CANVAS_ITEM (child),
909 &tx1, &ty1, &tx2, &ty2);
910 } else {
911 g_assert_not_reached ();
912 }
913
914 if (tx1 < minx)
915 minx = tx1;
916
917 if (ty1 < miny)
918 miny = ty1;
919
920 if (tx2 > maxx)
921 maxx = tx2;
922
923 if (ty2 > maxy)
924 maxy = ty2;
925 }
926
927 /* Make the bounds be relative to our parent's coordinate system */
928
929 if (EEL_CANVAS_ITEM (group)->parent) {
930 minx += group->xpos;
931 miny += group->ypos;
932 maxx += group->xpos;
933 maxy += group->ypos;
934 }
935
936 if (x1 != NULL) {
937 *x1 = minx;
938 }
939
940 if (y1 != NULL) {
941 *y1 = miny;
942 }
943
944 if (x2 != NULL) {
945 *x2 = maxx;
946 }
947
948 if (y2 != NULL) {
949 *y2 = maxy;
950 }
951 }
952
953 static void
954 get_all_icon_bounds (NautilusCanvasContainer *container,
955 double *x1, double *y1,
956 double *x2, double *y2,
957 NautilusCanvasItemBoundsUsage usage)
958 {
959 /* FIXME bugzilla.gnome.org 42477: Do we have to do something about the rubberband
960 * here? Any other non-icon items?
961 */
962 get_icon_bounds_for_canvas_bounds (EEL_CANVAS_GROUP (EEL_CANVAS (container)->root),
963 x1, y1, x2, y2, usage);
964 }
965
966 /* Don't preserve visible white space the next time the scroll region
967 * is recomputed when the container is not empty. */
968 void
969 nautilus_canvas_container_reset_scroll_region (NautilusCanvasContainer *container)
970 {
971 container->details->reset_scroll_region_trigger = TRUE;
972 }
973
974 /* Set a new scroll region without eliminating any of the currently-visible area. */
975 static void
976 canvas_set_scroll_region_include_visible_area (EelCanvas *canvas,
977 double x1, double y1,
978 double x2, double y2)
979 {
980 double old_x1, old_y1, old_x2, old_y2;
981 double old_scroll_x, old_scroll_y;
982 double height, width;
983 GtkAllocation allocation;
984
985 eel_canvas_get_scroll_region (canvas, &old_x1, &old_y1, &old_x2, &old_y2);
986 gtk_widget_get_allocation (GTK_WIDGET (canvas), &allocation);
987
988 width = (allocation.width) / canvas->pixels_per_unit;
989 height = (allocation.height) / canvas->pixels_per_unit;
990
991 old_scroll_x = gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas)));
992 old_scroll_y = gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas)));
993
994 x1 = MIN (x1, old_x1 + old_scroll_x);
995 y1 = MIN (y1, old_y1 + old_scroll_y);
996 x2 = MAX (x2, old_x1 + old_scroll_x + width);
997 y2 = MAX (y2, old_y1 + old_scroll_y + height);
998
999 eel_canvas_set_scroll_region
1000 (canvas, x1, y1, x2, y2);
1001 }
1002
1003 void
1004 nautilus_canvas_container_update_scroll_region (NautilusCanvasContainer *container)
1005 {
1006 double x1, y1, x2, y2;
1007 double pixels_per_unit;
1008 GtkAdjustment *hadj, *vadj;
1009 float step_increment;
1010 gboolean reset_scroll_region;
1011 GtkAllocation allocation;
1012
1013 pixels_per_unit = EEL_CANVAS (container)->pixels_per_unit;
1014
1015 if (nautilus_canvas_container_get_is_fixed_size (container)) {
1016 /* Set the scroll region to the size of the container allocation */
1017 gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
1018 eel_canvas_set_scroll_region
1019 (EEL_CANVAS (container),
1020 (double) - container->details->left_margin / pixels_per_unit,
1021 (double) - container->details->top_margin / pixels_per_unit,
1022 ((double) (allocation.width - 1)
1023 - container->details->left_margin
1024 - container->details->right_margin)
1025 / pixels_per_unit,
1026 ((double) (allocation.height - 1)
1027 - container->details->top_margin
1028 - container->details->bottom_margin)
1029 / pixels_per_unit);
1030 return;
1031 }
1032
1033 reset_scroll_region = container->details->reset_scroll_region_trigger
1034 || nautilus_canvas_container_is_empty (container)
1035 || nautilus_canvas_container_is_auto_layout (container);
1036
1037 /* The trigger is only cleared when container is non-empty, so
1038 * callers can reliably reset the scroll region when an item
1039 * is added even if extraneous relayouts are called when the
1040 * window is still empty.
1041 */
1042 if (!nautilus_canvas_container_is_empty (container)) {
1043 container->details->reset_scroll_region_trigger = FALSE;
1044 }
1045
1046 get_all_icon_bounds (container, &x1, &y1, &x2, &y2, BOUNDS_USAGE_FOR_ENTIRE_ITEM);
1047
1048 /* Add border at the "end"of the layout (i.e. after the icons), to
1049 * ensure we get some space when scrolled to the end.
1050 * For horizontal layouts, we add a bottom border.
1051 * Vertical layout is used by the compact view so the end
1052 * depends on the RTL setting.
1053 */
1054 if (nautilus_canvas_container_is_layout_vertical (container)) {
1055 if (nautilus_canvas_container_is_layout_rtl (container)) {
1056 x1 -= ICON_PAD_LEFT + CONTAINER_PAD_LEFT;
1057 } else {
1058 x2 += ICON_PAD_RIGHT + CONTAINER_PAD_RIGHT;
1059 }
1060 } else {
1061 y2 += ICON_PAD_BOTTOM + CONTAINER_PAD_BOTTOM;
1062 }
1063
1064 /* Auto-layout assumes a 0, 0 scroll origin and at least allocation->width.
1065 * Then we lay out to the right or to the left, so
1066 * x can be < 0 and > allocation */
1067 if (nautilus_canvas_container_is_auto_layout (container)) {
1068 gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
1069 x1 = MIN (x1, 0);
1070 x2 = MAX (x2, allocation.width / pixels_per_unit);
1071 y1 = 0;
1072 } else {
1073 /* Otherwise we add the padding that is at the start of the
1074 layout */
1075 if (nautilus_canvas_container_is_layout_rtl (container)) {
1076 x2 += ICON_PAD_RIGHT + CONTAINER_PAD_RIGHT;
1077 } else {
1078 x1 -= ICON_PAD_LEFT + CONTAINER_PAD_LEFT;
1079 }
1080 y1 -= ICON_PAD_TOP + CONTAINER_PAD_TOP;
1081 }
1082
1083 x2 -= 1;
1084 x2 = MAX(x1, x2);
1085
1086 y2 -= 1;
1087 y2 = MAX(y1, y2);
1088
1089 if (reset_scroll_region) {
1090 eel_canvas_set_scroll_region
1091 (EEL_CANVAS (container),
1092 x1, y1, x2, y2);
1093 } else {
1094 canvas_set_scroll_region_include_visible_area
1095 (EEL_CANVAS (container),
1096 x1, y1, x2, y2);
1097 }
1098
1099 hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container));
1100 vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container));
1101
1102 /* Scroll by 1/4 icon each time you click. */
1103 step_increment = nautilus_get_icon_size_for_zoom_level
1104 (container->details->zoom_level) / 4;
1105 if (gtk_adjustment_get_step_increment (hadj) != step_increment) {
1106 gtk_adjustment_set_step_increment (hadj, step_increment);
1107 }
1108 if (gtk_adjustment_get_step_increment (vadj) != step_increment) {
1109 gtk_adjustment_set_step_increment (vadj, step_increment);
1110 }
1111 }
1112
1113 static int
1114 compare_icons (gconstpointer a, gconstpointer b, gpointer canvas_container)
1115 {
1116 NautilusCanvasContainerClass *klass;
1117 const NautilusCanvasIcon *icon_a, *icon_b;
1118
1119 icon_a = a;
1120 icon_b = b;
1121 klass = NAUTILUS_CANVAS_CONTAINER_GET_CLASS (canvas_container);
1122
1123 return klass->compare_icons (canvas_container, icon_a->data, icon_b->data);
1124 }
1125
1126 static void
1127 sort_icons (NautilusCanvasContainer *container,
1128 GList **icons)
1129 {
1130 NautilusCanvasContainerClass *klass;
1131
1132 klass = NAUTILUS_CANVAS_CONTAINER_GET_CLASS (container);
1133 g_assert (klass->compare_icons != NULL);
1134
1135 *icons = g_list_sort_with_data (*icons, compare_icons, container);
1136 }
1137
1138 static void
1139 resort (NautilusCanvasContainer *container)
1140 {
1141 sort_icons (container, &container->details->icons);
1142 }
1143
1144 #if 0
1145 static double
1146 get_grid_width (NautilusCanvasContainer *container)
1147 {
1148 return STANDARD_ICON_GRID_WIDTH;
1149 }
1150 #endif
1151 typedef struct {
1152 double width;
1153 double height;
1154 double x_offset;
1155 double y_offset;
1156 } IconPositions;
1157
1158 static void
1159 lay_down_one_line (NautilusCanvasContainer *container,
1160 GList *line_start,
1161 GList *line_end,
1162 double y,
1163 double max_height,
1164 GArray *positions,
1165 gboolean whole_text)
1166 {
1167 GList *p;
1168 NautilusCanvasIcon *icon;
1169 double x, y_offset;
1170 IconPositions *position;
1171 int i;
1172 gboolean is_rtl;
1173
1174 is_rtl = nautilus_canvas_container_is_layout_rtl (container);
1175
1176 /* Lay out the icons along the baseline. */
1177 x = ICON_PAD_LEFT;
1178 i = 0;
1179 for (p = line_start; p != line_end; p = p->next) {
1180 icon = p->data;
1181
1182 position = &g_array_index (positions, IconPositions, i++);
1183 y_offset = position->y_offset;
1184
1185 icon_set_position
1186 (icon,
1187 is_rtl ? get_mirror_x_position (container, icon, x + position->x_offset) : x + position->x_offset,
1188 y + y_offset);
1189 nautilus_canvas_item_set_entire_text (icon->item, whole_text);
1190
1191 icon->saved_ltr_x = is_rtl ? get_mirror_x_position (container, icon, icon->x) : icon->x;
1192
1193 x += position->width;
1194 }
1195 }
1196
1197 static void
1198 lay_down_one_column (NautilusCanvasContainer *container,
1199 GList *line_start,
1200 GList *line_end,
1201 double x,
1202 double y_start,
1203 double y_iter,
1204 GArray *positions)
1205 {
1206 GList *p;
1207 NautilusCanvasIcon *icon;
1208 double y;
1209 IconPositions *position;
1210 int i;
1211 gboolean is_rtl;
1212
1213 is_rtl = nautilus_canvas_container_is_layout_rtl (container);
1214
1215 /* Lay out the icons along the baseline. */
1216 y = y_start;
1217 i = 0;
1218 for (p = line_start; p != line_end; p = p->next) {
1219 icon = p->data;
1220
1221 position = &g_array_index (positions, IconPositions, i++);
1222
1223 icon_set_position
1224 (icon,
1225 is_rtl ? get_mirror_x_position (container, icon, x + position->x_offset) : x + position->x_offset,
1226 y + position->y_offset);
1227
1228 icon->saved_ltr_x = is_rtl ? get_mirror_x_position (container, icon, icon->x) : icon->x;
1229
1230 y += y_iter;
1231 }
1232 }
1233
1234 static void
1235 lay_down_icons_horizontal (NautilusCanvasContainer *container,
1236 GList *icons,
1237 double start_y)
1238 {
1239 GList *p, *line_start;
1240 NautilusCanvasIcon *icon;
1241 double canvas_width, y;
1242 GArray *positions;
1243 IconPositions *position;
1244 EelDRect bounds;
1245 EelDRect icon_bounds;
1246 double max_height_above, max_height_below;
1247 double height_above, height_below;
1248 double line_width;
1249 double grid_width;
1250 int icon_width;
1251 int i;
1252 GtkAllocation allocation;
1253
1254 g_assert (NAUTILUS_IS_CANVAS_CONTAINER (container));
1255
1256 if (icons == NULL) {
1257 return;
1258 }
1259
1260 positions = g_array_new (FALSE, FALSE, sizeof (IconPositions));
1261 gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
1262
1263 /* Lay out icons a line at a time. */
1264 canvas_width = CANVAS_WIDTH(container, allocation);
1265
1266 grid_width = STANDARD_ICON_GRID_WIDTH;
1267
1268 line_width = 0;
1269 line_start = icons;
1270 y = start_y + CONTAINER_PAD_TOP;
1271 i = 0;
1272
1273 max_height_above = 0;
1274 max_height_below = 0;
1275 for (p = icons; p != NULL; p = p->next) {
1276 icon = p->data;
1277
1278 /* Assume it's only one level hierarchy to avoid costly affine calculations */
1279 nautilus_canvas_item_get_bounds_for_layout (icon->item,
1280 &bounds.x0, &bounds.y0,
1281 &bounds.x1, &bounds.y1);
1282
1283 icon_bounds = nautilus_canvas_item_get_icon_rectangle (icon->item);
1284 icon_width = ceil ((bounds.x1 - bounds.x0)/grid_width) * grid_width;
1285
1286 /* Calculate size above/below baseline */
1287 height_above = icon_bounds.y1 - bounds.y0;
1288 height_below = bounds.y1 - icon_bounds.y1;
1289
1290 /* If this icon doesn't fit, it's time to lay out the line that's queued up. */
1291 if (line_start != p && line_width + icon_width >= canvas_width ) {
1292 /* Advance to the baseline. */
1293 y += ICON_PAD_TOP + max_height_above;
1294
1295 lay_down_one_line (container, line_start, p, y, max_height_above, positions, FALSE);
1296
1297 /* Advance to next line. */
1298 y += max_height_below + ICON_PAD_BOTTOM;
1299
1300 line_width = 0;
1301 line_start = p;
1302 i = 0;
1303
1304 max_height_above = height_above;
1305 max_height_below = height_below;
1306 } else {
1307 if (height_above > max_height_above) {
1308 max_height_above = height_above;
1309 }
1310 if (height_below > max_height_below) {
1311 max_height_below = height_below;
1312 }
1313 }
1314
1315 g_array_set_size (positions, i + 1);
1316 position = &g_array_index (positions, IconPositions, i++);
1317 position->width = icon_width;
1318 position->height = icon_bounds.y1 - icon_bounds.y0;
1319
1320 position->x_offset = (icon_width - (icon_bounds.x1 - icon_bounds.x0)) / 2;
1321 position->y_offset = icon_bounds.y0 - icon_bounds.y1;
1322
1323 /* Add this icon. */
1324 line_width += icon_width;
1325 }
1326
1327 /* Lay down that last line of icons. */
1328 if (line_start != NULL) {
1329 /* Advance to the baseline. */
1330 y += ICON_PAD_TOP + max_height_above;
1331
1332 lay_down_one_line (container, line_start, NULL, y, max_height_above, positions, TRUE);
1333 }
1334
1335 g_array_free (positions, TRUE);
1336 }
1337
1338 static void
1339 get_max_icon_dimensions (GList *icon_start,
1340 GList *icon_end,
1341 double *max_icon_width,
1342 double *max_icon_height,
1343 double *max_text_width,
1344 double *max_text_height,
1345 double *max_bounds_height)
1346 {
1347 NautilusCanvasIcon *icon;
1348 EelDRect icon_bounds;
1349 EelDRect text_bounds;
1350 GList *p;
1351 double y1, y2;
1352
1353 *max_icon_width = *max_text_width = 0.0;
1354 *max_icon_height = *max_text_height = 0.0;
1355 *max_bounds_height = 0.0;
1356
1357 /* Would it be worth caching these bounds for the next loop? */
1358 for (p = icon_start; p != icon_end; p = p->next) {
1359 icon = p->data;
1360
1361 icon_bounds = nautilus_canvas_item_get_icon_rectangle (icon->item);
1362 *max_icon_width = MAX (*max_icon_width, ceil (icon_bounds.x1 - icon_bounds.x0));
1363 *max_icon_height = MAX (*max_icon_height, ceil (icon_bounds.y1 - icon_bounds.y0));
1364
1365 text_bounds = nautilus_canvas_item_get_text_rectangle (icon->item, TRUE);
1366 *max_text_width = MAX (*max_text_width, ceil (text_bounds.x1 - text_bounds.x0));
1367 *max_text_height = MAX (*max_text_height, ceil (text_bounds.y1 - text_bounds.y0));
1368
1369 nautilus_canvas_item_get_bounds_for_layout (icon->item,
1370 NULL, &y1,
1371 NULL, &y2);
1372 *max_bounds_height = MAX (*max_bounds_height, y2 - y1);
1373 }
1374 }
1375
1376 static void
1377 lay_down_icons_vertical (NautilusCanvasContainer *container,
1378 GList *icons,
1379 double start_y)
1380 {
1381 GList *p, *line_start;
1382 NautilusCanvasIcon *icon;
1383 double x, canvas_height;
1384 GArray *positions;
1385 IconPositions *position;
1386 EelDRect icon_bounds;
1387 EelDRect text_bounds;
1388 GtkAllocation allocation;
1389
1390 double line_height;
1391
1392 double max_height;
1393 double max_height_with_borders;
1394 double max_width;
1395 double max_width_in_column;
1396
1397 double max_bounds_height;
1398 double max_bounds_height_with_borders;
1399
1400 double max_text_width, max_icon_width;
1401 double max_text_height, max_icon_height;
1402 int height;
1403 int i;
1404
1405 g_assert (NAUTILUS_IS_CANVAS_CONTAINER (container));
1406
1407 if (icons == NULL) {
1408 return;
1409 }
1410
1411 positions = g_array_new (FALSE, FALSE, sizeof (IconPositions));
1412 gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
1413
1414 /* Lay out icons a column at a time. */
1415 canvas_height = CANVAS_HEIGHT(container, allocation);
1416
1417 max_icon_width = max_text_width = 0.0;
1418 max_icon_height = max_text_height = 0.0;
1419 max_bounds_height = 0.0;
1420
1421 get_max_icon_dimensions (icons, NULL,
1422 &max_icon_width, &max_icon_height,
1423 &max_text_width, &max_text_height,
1424 &max_bounds_height);
1425
1426 max_width = max_icon_width + max_text_width;
1427 max_height = MAX (max_icon_height, max_text_height);
1428 max_height_with_borders = ICON_PAD_TOP + max_height;
1429
1430 max_bounds_height_with_borders = ICON_PAD_TOP + max_bounds_height;
1431
1432 line_height = ICON_PAD_TOP;
1433 line_start = icons;
1434 x = 0;
1435 i = 0;
1436
1437 max_width_in_column = 0.0;
1438
1439 for (p = icons; p != NULL; p = p->next) {
1440 icon = p->data;
1441
1442 /* If this icon doesn't fit, it's time to lay out the column that's queued up. */
1443
1444 /* We use the bounds height here, since for wrapping we also want to consider
1445 * overlapping emblems at the bottom. We may wrap a little bit too early since
1446 * the icon with the max. bounds height may actually not be in the last row, but
1447 * it is better than visual glitches
1448 */
1449 if (line_start != p && line_height + (max_bounds_height_with_borders-1) >= canvas_height ) {
1450 x += ICON_PAD_LEFT;
1451
1452 /* correctly set (per-column) width */
1453 for (i = 0; i < (int) positions->len; i++) {
1454 position = &g_array_index (positions, IconPositions, i);
1455 position->width = max_width_in_column;
1456 }
1457
1458 lay_down_one_column (container, line_start, p, x, CONTAINER_PAD_TOP, max_height_with_borders, positions);
1459
1460 /* Advance to next column. */
1461 x += max_width_in_column + ICON_PAD_RIGHT;
1462
1463 line_height = ICON_PAD_TOP;
1464 line_start = p;
1465 i = 0;
1466
1467 max_width_in_column = 0;
1468 }
1469
1470 icon_bounds = nautilus_canvas_item_get_icon_rectangle (icon->item);
1471 text_bounds = nautilus_canvas_item_get_text_rectangle (icon->item, TRUE);
1472
1473 max_width_in_column = MAX (max_width_in_column,
1474 ceil (icon_bounds.x1 - icon_bounds.x0) +
1475 ceil (text_bounds.x1 - text_bounds.x0));
1476
1477 g_array_set_size (positions, i + 1);
1478 position = &g_array_index (positions, IconPositions, i++);
1479
1480 position->width = max_width;
1481 position->height = max_height;
1482 position->y_offset = ICON_PAD_TOP;
1483 position->x_offset = ICON_PAD_LEFT;
1484
1485 position->x_offset += max_icon_width - ceil (icon_bounds.x1 - icon_bounds.x0);
1486
1487 height = MAX (ceil (icon_bounds.y1 - icon_bounds.y0), ceil(text_bounds.y1 - text_bounds.y0));
1488 position->y_offset += (max_height - height) / 2;
1489
1490 /* Add this icon. */
1491 line_height += max_height_with_borders;
1492 }
1493
1494 /* Lay down that last column of icons. */
1495 if (line_start != NULL) {
1496 x += ICON_PAD_LEFT;
1497 lay_down_one_column (container, line_start, NULL, x, CONTAINER_PAD_TOP, max_height_with_borders, positions);
1498 }
1499
1500 g_array_free (positions, TRUE);
1501 }
1502
1503 static void
1504 snap_position (NautilusCanvasContainer *container,
1505 NautilusCanvasIcon *icon,
1506 int *x, int *y)
1507 {
1508 int center_x;
1509 int baseline_y;
1510 int icon_width;
1511 int icon_height;
1512 int total_width;
1513 int total_height;
1514 EelDRect canvas_position;
1515 GtkAllocation allocation;
1516
1517 canvas_position = nautilus_canvas_item_get_icon_rectangle (icon->item);
1518 icon_width = canvas_position.x1 - canvas_position.x0;
1519 icon_height = canvas_position.y1 - canvas_position.y0;
1520
1521 gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
1522 total_width = CANVAS_WIDTH (container, allocation);
1523 total_height = CANVAS_HEIGHT (container, allocation);
1524
1525 if (nautilus_canvas_container_is_layout_rtl (container))
1526 *x = get_mirror_x_position (container, icon, *x);
1527
1528 if (*x + icon_width / 2 < DESKTOP_PAD_HORIZONTAL + SNAP_SIZE_X) {
1529 *x = DESKTOP_PAD_HORIZONTAL + SNAP_SIZE_X - icon_width / 2;
1530 }
1531
1532 if (*x + icon_width / 2 > total_width - (DESKTOP_PAD_HORIZONTAL + SNAP_SIZE_X)) {
1533 *x = total_width - (DESKTOP_PAD_HORIZONTAL + SNAP_SIZE_X + (icon_width / 2));
1534 }
1535
1536 if (*y + icon_height < DESKTOP_PAD_VERTICAL + SNAP_SIZE_Y) {
1537 *y = DESKTOP_PAD_VERTICAL + SNAP_SIZE_Y - icon_height;
1538 }
1539
1540 if (*y + icon_height > total_height - (DESKTOP_PAD_VERTICAL + SNAP_SIZE_Y)) {
1541 *y = total_height - (DESKTOP_PAD_VERTICAL + SNAP_SIZE_Y + (icon_height / 2));
1542 }
1543
1544 center_x = *x + icon_width / 2;
1545 *x = SNAP_NEAREST_HORIZONTAL (center_x) - (icon_width / 2);
1546 if (nautilus_canvas_container_is_layout_rtl (container)) {
1547 *x = get_mirror_x_position (container, icon, *x);
1548 }
1549
1550
1551 /* Find the grid position vertically and place on the proper baseline */
1552 baseline_y = *y + icon_height;
1553 baseline_y = SNAP_NEAREST_VERTICAL (baseline_y);
1554 *y = baseline_y - icon_height;
1555 }
1556
1557 static int
1558 compare_icons_by_position (gconstpointer a, gconstpointer b)
1559 {
1560 NautilusCanvasIcon *icon_a, *icon_b;
1561 int x1, y1, x2, y2;
1562 int center_a;
1563 int center_b;
1564
1565 icon_a = (NautilusCanvasIcon *)a;
1566 icon_b = (NautilusCanvasIcon *)b;
1567
1568 icon_get_bounding_box (icon_a, &x1, &y1, &x2, &y2,
1569 BOUNDS_USAGE_FOR_DISPLAY);
1570 center_a = x1 + (x2 - x1) / 2;
1571 icon_get_bounding_box (icon_b, &x1, &y1, &x2, &y2,
1572 BOUNDS_USAGE_FOR_DISPLAY);
1573 center_b = x1 + (x2 - x1) / 2;
1574
1575 return center_a == center_b ?
1576 icon_a->y - icon_b->y :
1577 center_a - center_b;
1578 }
1579
1580 static PlacementGrid *
1581 placement_grid_new (NautilusCanvasContainer *container, gboolean tight)
1582 {
1583 PlacementGrid *grid;
1584 int width, height;
1585 int num_columns;
1586 int num_rows;
1587 int i;
1588 GtkAllocation allocation;
1589
1590 /* Get container dimensions */
1591 gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
1592 width = CANVAS_WIDTH(container, allocation);
1593 height = CANVAS_HEIGHT(container, allocation);
1594
1595 num_columns = width / SNAP_SIZE_X;
1596 num_rows = height / SNAP_SIZE_Y;
1597
1598 if (num_columns == 0 || num_rows == 0) {
1599 return NULL;
1600 }
1601
1602 grid = g_new0 (PlacementGrid, 1);
1603 grid->tight = tight;
1604 grid->num_columns = num_columns;
1605 grid->num_rows = num_rows;
1606
1607 grid->grid_memory = g_new0 (int, (num_rows * num_columns));
1608 grid->icon_grid = g_new0 (int *, num_columns);
1609
1610 for (i = 0; i < num_columns; i++) {
1611 grid->icon_grid[i] = grid->grid_memory + (i * num_rows);
1612 }
1613
1614 return grid;
1615 }
1616
1617 static void
1618 placement_grid_free (PlacementGrid *grid)
1619 {
1620 g_free (grid->icon_grid);
1621 g_free (grid->grid_memory);
1622 g_free (grid);
1623 }
1624
1625 static gboolean
1626 placement_grid_position_is_free (PlacementGrid *grid, EelIRect pos)
1627 {
1628 int x, y;
1629
1630 g_assert (pos.x0 >= 0 && pos.x0 < grid->num_columns);
1631 g_assert (pos.y0 >= 0 && pos.y0 < grid->num_rows);
1632 g_assert (pos.x1 >= 0 && pos.x1 < grid->num_columns);
1633 g_assert (pos.y1 >= 0 && pos.y1 < grid->num_rows);
1634
1635 for (x = pos.x0; x <= pos.x1; x++) {
1636 for (y = pos.y0; y <= pos.y1; y++) {
1637 if (grid->icon_grid[x][y] != 0) {
1638 return FALSE;
1639 }
1640 }
1641 }
1642
1643 return TRUE;
1644 }
1645
1646 static void
1647 placement_grid_mark (PlacementGrid *grid, EelIRect pos)
1648 {
1649 int x, y;
1650
1651 g_assert (pos.x0 >= 0 && pos.x0 < grid->num_columns);
1652 g_assert (pos.y0 >= 0 && pos.y0 < grid->num_rows);
1653 g_assert (pos.x1 >= 0 && pos.x1 < grid->num_columns);
1654 g_assert (pos.y1 >= 0 && pos.y1 < grid->num_rows);
1655
1656 for (x = pos.x0; x <= pos.x1; x++) {
1657 for (y = pos.y0; y <= pos.y1; y++) {
1658 grid->icon_grid[x][y] = 1;
1659 }
1660 }
1661 }
1662
1663 static void
1664 canvas_position_to_grid_position (PlacementGrid *grid,
1665 EelIRect canvas_position,
1666 EelIRect *grid_position)
1667 {
1668 /* The first causes minimal moving around during a snap, but
1669 * can end up with partially overlapping icons. The second one won't
1670 * allow any overlapping, but can cause more movement to happen
1671 * during a snap. */
1672 if (grid->tight) {
1673 grid_position->x0 = ceil ((double)(canvas_position.x0 - DESKTOP_PAD_HORIZONTAL) / SNAP_SIZE_X);
1674 grid_position->y0 = ceil ((double)(canvas_position.y0 - DESKTOP_PAD_VERTICAL) / SNAP_SIZE_Y);
1675 grid_position->x1 = floor ((double)(canvas_position.x1 - DESKTOP_PAD_HORIZONTAL) / SNAP_SIZE_X);
1676 grid_position->y1 = floor ((double)(canvas_position.y1 - DESKTOP_PAD_VERTICAL) / SNAP_SIZE_Y);
1677 } else {
1678 grid_position->x0 = floor ((double)(canvas_position.x0 - DESKTOP_PAD_HORIZONTAL) / SNAP_SIZE_X);
1679 grid_position->y0 = floor ((double)(canvas_position.y0 - DESKTOP_PAD_VERTICAL) / SNAP_SIZE_Y);
1680 grid_position->x1 = floor ((double)(canvas_position.x1 - DESKTOP_PAD_HORIZONTAL) / SNAP_SIZE_X);
1681 grid_position->y1 = floor ((double)(canvas_position.y1 - DESKTOP_PAD_VERTICAL) / SNAP_SIZE_Y);
1682 }
1683
1684 grid_position->x0 = CLAMP (grid_position->x0, 0, grid->num_columns - 1);
1685 grid_position->y0 = CLAMP (grid_position->y0, 0, grid->num_rows - 1);
1686 grid_position->x1 = CLAMP (grid_position->x1, grid_position->x0, grid->num_columns - 1);
1687 grid_position->y1 = CLAMP (grid_position->y1, grid_position->y0, grid->num_rows - 1);
1688 }
1689
1690 static void
1691 placement_grid_mark_icon (PlacementGrid *grid, NautilusCanvasIcon *icon)
1692 {
1693 EelIRect canvas_pos;
1694 EelIRect grid_pos;
1695
1696 icon_get_bounding_box (icon,
1697 &canvas_pos.x0, &canvas_pos.y0,
1698 &canvas_pos.x1, &canvas_pos.y1,
1699 BOUNDS_USAGE_FOR_LAYOUT);
1700 canvas_position_to_grid_position (grid,
1701 canvas_pos,
1702 &grid_pos);
1703 placement_grid_mark (grid, grid_pos);
1704 }
1705
1706 static void
1707 find_empty_location (NautilusCanvasContainer *container,
1708 PlacementGrid *grid,
1709 NautilusCanvasIcon *icon,
1710 int start_x,
1711 int start_y,
1712 int *x,
1713 int *y)
1714 {
1715 double icon_width, icon_height;
1716 int canvas_width;
1717 int canvas_height;
1718 int height_for_bound_check;
1719 EelIRect icon_position;
1720 EelDRect pixbuf_rect;
1721 gboolean collision;
1722 GtkAllocation allocation;
1723
1724 /* Get container dimensions */
1725 gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
1726 canvas_width = CANVAS_WIDTH(container, allocation);
1727 canvas_height = CANVAS_HEIGHT(container, allocation);
1728
1729 icon_get_bounding_box (icon,
1730 &icon_position.x0, &icon_position.y0,
1731 &icon_position.x1, &icon_position.y1,
1732 BOUNDS_USAGE_FOR_LAYOUT);
1733 icon_width = icon_position.x1 - icon_position.x0;
1734 icon_height = icon_position.y1 - icon_position.y0;
1735
1736 icon_get_bounding_box (icon,
1737 NULL, &icon_position.y0,
1738 NULL, &icon_position.y1,
1739 BOUNDS_USAGE_FOR_ENTIRE_ITEM);
1740 height_for_bound_check = icon_position.y1 - icon_position.y0;
1741
1742 pixbuf_rect = nautilus_canvas_item_get_icon_rectangle (icon->item);
1743
1744 /* Start the icon on a grid location */
1745 snap_position (container, icon, &start_x, &start_y);
1746
1747 icon_position.x0 = start_x;
1748 icon_position.y0 = start_y;
1749 icon_position.x1 = icon_position.x0 + icon_width;
1750 icon_position.y1 = icon_position.y0 + icon_height;
1751
1752 do {
1753 EelIRect grid_position;
1754 gboolean need_new_column;
1755
1756 collision = FALSE;
1757
1758 canvas_position_to_grid_position (grid,
1759 icon_position,
1760 &grid_position);
1761
1762 need_new_column = icon_position.y0 + height_for_bound_check + DESKTOP_PAD_VERTICAL > canvas_height;
1763
1764 if (need_new_column ||
1765 !placement_grid_position_is_free (grid, grid_position)) {
1766 icon_position.y0 += SNAP_SIZE_Y;
1767 icon_position.y1 = icon_position.y0 + icon_height;
1768
1769 if (need_new_column) {
1770 /* Move to the next column */
1771 icon_position.y0 = DESKTOP_PAD_VERTICAL + SNAP_SIZE_Y - (pixbuf_rect.y1 - pixbuf_rect.y0);
1772 while (icon_position.y0 < DESKTOP_PAD_VERTICAL) {
1773 icon_position.y0 += SNAP_SIZE_Y;
1774 }
1775 icon_position.y1 = icon_position.y0 + icon_height;
1776
1777 icon_position.x0 += SNAP_SIZE_X;
1778 icon_position.x1 = icon_position.x0 + icon_width;
1779 }
1780
1781 collision = TRUE;
1782 }
1783 } while (collision && (icon_position.x1 < canvas_width));
1784
1785 *x = icon_position.x0;
1786 *y = icon_position.y0;
1787 }
1788
1789 static void
1790 align_icons (NautilusCanvasContainer *container)
1791 {
1792 GList *unplaced_icons;
1793 GList *l;
1794 PlacementGrid *grid;
1795
1796 unplaced_icons = g_list_copy (container->details->icons);
1797
1798 unplaced_icons = g_list_sort (unplaced_icons,
1799 compare_icons_by_position);
1800
1801 if (nautilus_canvas_container_is_layout_rtl (container)) {
1802 unplaced_icons = g_list_reverse (unplaced_icons);
1803 }
1804
1805 grid = placement_grid_new (container, TRUE);
1806
1807 if (!grid) {
1808 return;
1809 }
1810
1811 for (l = unplaced_icons; l != NULL; l = l->next) {
1812 NautilusCanvasIcon *icon;
1813 int x, y;
1814
1815 icon = l->data;
1816 x = icon->saved_ltr_x;
1817 y = icon->y;
1818 find_empty_location (container, grid,
1819 icon, x, y, &x, &y);
1820
1821 icon_set_position (icon, x, y);
1822 icon->saved_ltr_x = icon->x;
1823 placement_grid_mark_icon (grid, icon);
1824 }
1825
1826 g_list_free (unplaced_icons);
1827
1828 placement_grid_free (grid);
1829
1830 if (nautilus_canvas_container_is_layout_rtl (container)) {
1831 nautilus_canvas_container_set_rtl_positions (container);
1832 }
1833 }
1834
1835 static double
1836 get_mirror_x_position (NautilusCanvasContainer *container, NautilusCanvasIcon *icon, double x)
1837 {
1838 EelDRect icon_bounds;
1839 GtkAllocation allocation;
1840
1841 gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
1842 icon_bounds = nautilus_canvas_item_get_icon_rectangle (icon->item);
1843
1844 return CANVAS_WIDTH(container, allocation) - x - (icon_bounds.x1 - icon_bounds.x0);
1845 }
1846
1847 static void
1848 nautilus_canvas_container_set_rtl_positions (NautilusCanvasContainer *container)
1849 {
1850 GList *l;
1851 NautilusCanvasIcon *icon;
1852 double x;
1853
1854 if (!container->details->icons) {
1855 return;
1856 }
1857
1858 for (l = container->details->icons; l != NULL; l = l->next) {
1859 icon = l->data;
1860 x = get_mirror_x_position (container, icon, icon->saved_ltr_x);
1861 icon_set_position (icon, x, icon->y);
1862 }
1863 }
1864
1865 static void
1866 lay_down_icons_vertical_desktop (NautilusCanvasContainer *container, GList *icons)
1867 {
1868 GList *p, *placed_icons, *unplaced_icons;
1869 int total, new_length, placed;
1870 NautilusCanvasIcon *icon;
1871 int height, max_width, column_width, icon_width, icon_height;
1872 int x, y, x1, x2, y1, y2;
1873 EelDRect icon_rect;
1874 GtkAllocation allocation;
1875
1876 /* Get container dimensions */
1877 gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
1878 height = CANVAS_HEIGHT(container, allocation);
1879
1880 /* Determine which icons have and have not been placed */
1881 placed_icons = NULL;
1882 unplaced_icons = NULL;
1883
1884 total = g_list_length (container->details->icons);
1885 new_length = g_list_length (icons);
1886 placed = total - new_length;
1887 if (placed > 0) {
1888 PlacementGrid *grid;
1889 /* Add only placed icons in list */
1890 for (p = container->details->icons; p != NULL; p = p->next) {
1891 icon = p->data;
1892 if (icon_is_positioned (icon)) {
1893 icon_set_position(icon, icon->saved_ltr_x, icon->y);
1894 placed_icons = g_list_prepend (placed_icons, icon);
1895 } else {
1896 icon->x = 0;
1897 icon->y = 0;
1898 unplaced_icons = g_list_prepend (unplaced_icons, icon);
1899 }
1900 }
1901 placed_icons = g_list_reverse (placed_icons);
1902 unplaced_icons = g_list_reverse (unplaced_icons);
1903
1904 grid = placement_grid_new (container, FALSE);
1905
1906 if (grid) {
1907 for (p = placed_icons; p != NULL; p = p->next) {
1908 placement_grid_mark_icon
1909 (grid, (NautilusCanvasIcon *)p->data);
1910 }
1911
1912 /* Place unplaced icons in the best locations */
1913 for (p = unplaced_icons; p != NULL; p = p->next) {
1914 icon = p->data;
1915
1916 icon_rect = nautilus_canvas_item_get_icon_rectangle (icon->item);
1917
1918 /* Start the icon in the first column */
1919 x = DESKTOP_PAD_HORIZONTAL + (SNAP_SIZE_X / 2) - ((icon_rect.x1 - icon_rect.x0) / 2);
1920 y = DESKTOP_PAD_VERTICAL + SNAP_SIZE_Y - (icon_rect.y1 - icon_rect.y0);
1921
1922 find_empty_location (container,
1923 grid,
1924 icon,
1925 x, y,
1926 &x, &y);
1927
1928 icon_set_position (icon, x, y);
1929 icon->saved_ltr_x = x;
1930 placement_grid_mark_icon (grid, icon);
1931 }
1932
1933 placement_grid_free (grid);
1934 }
1935
1936 g_list_free (placed_icons);
1937 g_list_free (unplaced_icons);
1938 } else {
1939 /* There are no placed icons. Just lay them down using our rules */
1940 x = DESKTOP_PAD_HORIZONTAL;
1941
1942 while (icons != NULL) {
1943 int center_x;
1944 int baseline;
1945 int icon_height_for_bound_check;
1946 gboolean should_snap;
1947
1948 should_snap = container->details->keep_aligned;
1949
1950 y = DESKTOP_PAD_VERTICAL;
1951
1952 max_width = 0;
1953
1954 /* Calculate max width for column */
1955 for (p = icons; p != NULL; p = p->next) {
1956 icon = p->data;
1957
1958 icon_get_bounding_box (icon, &x1, &y1, &x2, &y2,
1959 BOUNDS_USAGE_FOR_LAYOUT);
1960 icon_width = x2 - x1;
1961 icon_height = y2 - y1;
1962
1963 icon_get_bounding_box (icon, NULL, &y1, NULL, &y2,
1964 BOUNDS_USAGE_FOR_ENTIRE_ITEM);
1965 icon_height_for_bound_check = y2 - y1;
1966
1967 if (should_snap) {
1968 /* Snap the baseline to a grid position */
1969 icon_rect = nautilus_canvas_item_get_icon_rectangle (icon->item);
1970 baseline = y + (icon_rect.y1 - icon_rect.y0);
1971 baseline = SNAP_CEIL_VERTICAL (baseline);
1972 y = baseline - (icon_rect.y1 - icon_rect.y0);
1973 }
1974
1975 /* Check and see if we need to move to a new column */
1976 if (y != DESKTOP_PAD_VERTICAL && y + icon_height_for_bound_check > height) {
1977 break;
1978 }
1979
1980 if (max_width < icon_width) {
1981 max_width = icon_width;
1982 }
1983
1984 y += icon_height + DESKTOP_PAD_VERTICAL;
1985 }
1986
1987 y = DESKTOP_PAD_VERTICAL;
1988
1989 center_x = x + max_width / 2;
1990 column_width = max_width;
1991 if (should_snap) {
1992 /* Find the grid column to center on */
1993 center_x = SNAP_CEIL_HORIZONTAL (center_x);
1994 column_width = (center_x - x) + (max_width / 2);
1995 }
1996
1997 /* Lay out column */
1998 for (p = icons; p != NULL; p = p->next) {
1999 icon = p->data;
2000 icon_get_bounding_box (icon, &x1, &y1, &x2, &y2,
2001 BOUNDS_USAGE_FOR_LAYOUT);
2002 icon_height = y2 - y1;
2003
2004 icon_get_bounding_box (icon, NULL, &y1, NULL, &y2,
2005 BOUNDS_USAGE_FOR_ENTIRE_ITEM);
2006 icon_height_for_bound_check = y2 - y1;
2007
2008 icon_rect = nautilus_canvas_item_get_icon_rectangle (icon->item);
2009
2010 if (should_snap) {
2011 baseline = y + (icon_rect.y1 - icon_rect.y0);
2012 baseline = SNAP_CEIL_VERTICAL (baseline);
2013 y = baseline - (icon_rect.y1 - icon_rect.y0);
2014 }
2015
2016 /* Check and see if we need to move to a new column */
2017 if (y != DESKTOP_PAD_VERTICAL && y > height - icon_height_for_bound_check &&
2018 /* Make sure we lay out at least one icon per column, to make progress */
2019 p != icons) {
2020 x += column_width + DESKTOP_PAD_HORIZONTAL;
2021 break;
2022 }
2023
2024 icon_set_position (icon,
2025 center_x - (icon_rect.x1 - icon_rect.x0) / 2,
2026 y);
2027
2028 icon->saved_ltr_x = icon->x;
2029 y += icon_height + DESKTOP_PAD_VERTICAL;
2030 }
2031 icons = p;
2032 }
2033 }
2034
2035 /* These modes are special. We freeze all of our positions
2036 * after we do the layout.
2037 */
2038 /* FIXME bugzilla.gnome.org 42478:
2039 * This should not be tied to the direction of layout.
2040 * It should be a separate switch.
2041 */
2042 nautilus_canvas_container_freeze_icon_positions (container);
2043 }
2044
2045
2046 static void
2047 lay_down_icons (NautilusCanvasContainer *container, GList *icons, double start_y)
2048 {
2049 switch (container->details->layout_mode)
2050 {
2051 case NAUTILUS_CANVAS_LAYOUT_L_R_T_B:
2052 case NAUTILUS_CANVAS_LAYOUT_R_L_T_B:
2053 lay_down_icons_horizontal (container, icons, start_y);
2054 break;
2055
2056 case NAUTILUS_CANVAS_LAYOUT_T_B_L_R:
2057 case NAUTILUS_CANVAS_LAYOUT_T_B_R_L:
2058 if (nautilus_canvas_container_get_is_desktop (container)) {
2059 lay_down_icons_vertical_desktop (container, icons);
2060 } else {
2061 lay_down_icons_vertical (container, icons, start_y);
2062 }
2063 break;
2064
2065 default:
2066 g_assert_not_reached ();
2067 }
2068 }
2069
2070 static void
2071 redo_layout_internal (NautilusCanvasContainer *container)
2072 {
2073 finish_adding_new_icons (container);
2074
2075 /* Don't do any re-laying-out during stretching. Later we
2076 * might add smart logic that does this and leaves room for
2077 * the stretched icon, but if we do it we want it to be fast
2078 * and only re-lay-out when it's really needed.
2079 */
2080 if (container->details->auto_layout
2081 && container->details->drag_state != DRAG_STATE_STRETCH) {
2082 if (container->details->needs_resort) {
2083 resort (container);
2084 container->details->needs_resort = FALSE;
2085 }
2086 lay_down_icons (container, container->details->icons, 0);
2087 }
2088
2089 if (nautilus_canvas_container_is_layout_rtl (container)) {
2090 nautilus_canvas_container_set_rtl_positions (container);
2091 }
2092
2093 nautilus_canvas_container_update_scroll_region (container);
2094
2095 process_pending_icon_to_reveal (container);
2096 process_pending_icon_to_rename (container);
2097 nautilus_canvas_container_update_visible_icons (container);
2098 }
2099
2100 static gboolean
2101 redo_layout_callback (gpointer callback_data)
2102 {
2103 NautilusCanvasContainer *container;
2104
2105 container = NAUTILUS_CANVAS_CONTAINER (callback_data);
2106 redo_layout_internal (container);
2107 container->details->idle_id = 0;
2108
2109 return FALSE;
2110 }
2111
2112 static void
2113 unschedule_redo_layout (NautilusCanvasContainer *container)
2114 {
2115 if (container->details->idle_id != 0) {
2116 g_source_remove (container->details->idle_id);
2117 container->details->idle_id = 0;
2118 }
2119 }
2120
2121 static void
2122 schedule_redo_layout (NautilusCanvasContainer *container)
2123 {
2124 if (container->details->idle_id == 0
2125 && container->details->has_been_allocated) {
2126 container->details->idle_id = g_idle_add
2127 (redo_layout_callback, container);
2128 }
2129 }
2130
2131 static void
2132 redo_layout (NautilusCanvasContainer *container)
2133 {
2134 unschedule_redo_layout (container);
2135 redo_layout_internal (container);
2136 }
2137
2138 static void
2139 reload_icon_positions (NautilusCanvasContainer *container)
2140 {
2141 GList *p, *no_position_icons;
2142 NautilusCanvasIcon *icon;
2143 gboolean have_stored_position;
2144 NautilusCanvasPosition position;
2145 EelDRect bounds;
2146 double bottom;
2147 EelCanvasItem *item;
2148
2149 g_assert (!container->details->auto_layout);
2150
2151 resort (container);
2152
2153 no_position_icons = NULL;
2154
2155 /* Place all the icons with positions. */
2156 bottom = 0;
2157 for (p = container->details->icons; p != NULL; p = p->next) {
2158 icon = p->data;
2159
2160 have_stored_position = FALSE;
2161 g_signal_emit (container,
2162 signals[GET_STORED_ICON_POSITION], 0,
2163 icon->data,
2164 &position,
2165 &have_stored_position);
2166 if (have_stored_position) {
2167 icon_set_position (icon, position.x, position.y);
2168 item = EEL_CANVAS_ITEM (icon->item);
2169 nautilus_canvas_item_get_bounds_for_layout (icon->item,
2170 &bounds.x0,
2171 &bounds.y0,
2172 &bounds.x1,
2173 &bounds.y1);
2174 eel_canvas_item_i2w (item->parent,
2175 &bounds.x0,
2176 &bounds.y0);
2177 eel_canvas_item_i2w (item->parent,
2178 &bounds.x1,
2179 &bounds.y1);
2180 if (bounds.y1 > bottom) {
2181 bottom = bounds.y1;
2182 }
2183 } else {
2184 no_position_icons = g_list_prepend (no_position_icons, icon);
2185 }
2186 }
2187 no_position_icons = g_list_reverse (no_position_icons);
2188
2189 /* Place all the other icons. */
2190 lay_down_icons (container, no_position_icons, bottom + ICON_PAD_BOTTOM);
2191 g_list_free (no_position_icons);
2192 }
2193
2194 /* Container-level icon handling functions. */
2195
2196 static gboolean
2197 button_event_modifies_selection (GdkEventButton *event)
2198 {
2199 return (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) != 0;
2200 }
2201
2202 /* invalidate the cached label sizes for all the icons */
2203 static void
2204 invalidate_label_sizes (NautilusCanvasContainer *container)
2205 {
2206 GList *p;
2207 NautilusCanvasIcon *icon;
2208
2209 for (p = container->details->icons; p != NULL; p = p->next) {
2210 icon = p->data;
2211
2212 nautilus_canvas_item_invalidate_label_size (icon->item);
2213 }
2214 }
2215
2216 /* invalidate the entire labels (i.e. their attributes) for all the icons */
2217 static void
2218 invalidate_labels (NautilusCanvasContainer *container)
2219 {
2220 GList *p;
2221 NautilusCanvasIcon *icon;
2222
2223 for (p = container->details->icons; p != NULL; p = p->next) {
2224 icon = p->data;
2225
2226 nautilus_canvas_item_invalidate_label (icon->item);
2227 }
2228 }
2229
2230 static gboolean
2231 select_range (NautilusCanvasContainer *container,
2232 NautilusCanvasIcon *icon1,
2233 NautilusCanvasIcon *icon2,
2234 gboolean unselect_outside_range)
2235 {
2236 gboolean selection_changed;
2237 GList *p;
2238 NautilusCanvasIcon *icon;
2239 NautilusCanvasIcon *unmatched_icon;
2240 gboolean select;
2241
2242 selection_changed = FALSE;
2243
2244 unmatched_icon = NULL;
2245 select = FALSE;
2246 for (p = container->details->icons; p != NULL; p = p->next) {
2247 icon = p->data;
2248
2249 if (unmatched_icon == NULL) {
2250 if (icon == icon1) {
2251 unmatched_icon = icon2;
2252 select = TRUE;
2253 } else if (icon == icon2) {
2254 unmatched_icon = icon1;
2255 select = TRUE;
2256 }
2257 }
2258
2259 if (select || unselect_outside_range) {
2260 selection_changed |= icon_set_selected
2261 (container, icon, select);
2262 }
2263
2264 if (unmatched_icon != NULL && icon == unmatched_icon) {
2265 select = FALSE;
2266 }
2267
2268 }
2269
2270 if (selection_changed && icon2 != NULL) {
2271 emit_atk_focus_tracker_notify (icon2);
2272 }
2273 return selection_changed;
2274 }
2275
2276
2277 static gboolean
2278 select_one_unselect_others (NautilusCanvasContainer *container,
2279 NautilusCanvasIcon *icon_to_select)
2280 {
2281 gboolean selection_changed;
2282 GList *p;
2283 NautilusCanvasIcon *icon;
2284
2285 selection_changed = FALSE;
2286
2287 for (p = container->details->icons; p != NULL; p = p->next) {
2288 icon = p->data;
2289
2290 selection_changed |= icon_set_selected
2291 (container, icon, icon == icon_to_select);
2292 }
2293
2294 if (selection_changed && icon_to_select != NULL) {
2295 emit_atk_focus_tracker_notify (icon_to_select);
2296 reveal_icon (container, icon_to_select);
2297 }
2298 return selection_changed;
2299 }
2300
2301 static gboolean
2302 unselect_all (NautilusCanvasContainer *container)
2303 {
2304 return select_one_unselect_others (container, NULL);
2305 }
2306
2307 void
2308 nautilus_canvas_container_move_icon (NautilusCanvasContainer *container,
2309 NautilusCanvasIcon *icon,
2310 int x, int y,
2311 double scale,
2312 gboolean raise,
2313 gboolean snap,
2314 gboolean update_position)
2315 {
2316 NautilusCanvasContainerDetails *details;
2317 gboolean emit_signal;
2318 NautilusCanvasPosition position;
2319
2320 details = container->details;
2321
2322 emit_signal = FALSE;
2323
2324 if (icon == get_icon_being_renamed (container)) {
2325 end_renaming_mode (container, TRUE);
2326 }
2327
2328 if (scale != icon->scale) {
2329 icon->scale = scale;
2330 nautilus_canvas_container_update_icon (container, icon);
2331 if (update_position) {
2332 redo_layout (container);
2333 emit_signal = TRUE;
2334 }
2335 }
2336
2337 if (!details->auto_layout) {
2338 if (details->keep_aligned && snap) {
2339 snap_position (container, icon, &x, &y);
2340 }
2341
2342 if (x != icon->x || y != icon->y) {
2343 icon_set_position (icon, x, y);
2344 emit_signal = update_position;
2345 }
2346
2347 icon->saved_ltr_x = nautilus_canvas_container_is_layout_rtl (container) ? get_mirror_x_position (container, icon, icon->x) : icon->x;
2348 }
2349
2350 if (emit_signal) {
2351 position.x = icon->saved_ltr_x;
2352 position.y = icon->y;
2353 position.scale = scale;
2354 g_signal_emit (container,
2355 signals[ICON_POSITION_CHANGED], 0,
2356 icon->data, &position);
2357 }
2358
2359 if (raise) {
2360 icon_raise (icon);
2361 }
2362
2363 /* FIXME bugzilla.gnome.org 42474:
2364 * Handling of the scroll region is inconsistent here. In
2365 * the scale-changing case, redo_layout is called, which updates the
2366 * scroll region appropriately. In other cases, it's up to the
2367 * caller to make sure the scroll region is updated. This could
2368 * lead to hard-to-track-down bugs.
2369 */
2370 }
2371
2372 /* Implementation of rubberband selection. */
2373 static void
2374 rubberband_select (NautilusCanvasContainer *container,
2375 const EelDRect *previous_rect,
2376 const EelDRect *current_rect)
2377 {
2378 GList *p;
2379 gboolean selection_changed, is_in, canvas_rect_calculated;
2380 NautilusCanvasIcon *icon;
2381 EelIRect canvas_rect;
2382 EelCanvas *canvas;
2383
2384 selection_changed = FALSE;
2385 canvas_rect_calculated = FALSE;
2386
2387 for (p = container->details->icons; p != NULL; p = p->next) {
2388 icon = p->data;
2389
2390 if (!canvas_rect_calculated) {
2391 /* Only do this calculation once, since all the canvas items
2392 * we are interating are in the same coordinate space
2393 */
2394 canvas = EEL_CANVAS_ITEM (icon->item)->canvas;
2395 eel_canvas_w2c (canvas,
2396 current_rect->x0,
2397 current_rect->y0,
2398 &canvas_rect.x0,
2399 &canvas_rect.y0);
2400 eel_canvas_w2c (canvas,
2401 current_rect->x1,
2402 current_rect->y1,
2403 &canvas_rect.x1,
2404 &canvas_rect.y1);
2405 canvas_rect_calculated = TRUE;
2406 }
2407
2408 is_in = nautilus_canvas_item_hit_test_rectangle (icon->item, canvas_rect);
2409
2410 selection_changed |= icon_set_selected
2411 (container, icon,
2412 is_in ^ icon->was_selected_before_rubberband);
2413 }
2414
2415 if (selection_changed) {
2416 g_signal_emit (container,
2417 signals[SELECTION_CHANGED], 0);
2418 }
2419 }
2420
2421 static int
2422 rubberband_timeout_callback (gpointer data)
2423 {
2424 NautilusCanvasContainer *container;
2425 GtkWidget *widget;
2426 NautilusCanvasRubberbandInfo *band_info;
2427 int x, y;
2428 double x1, y1, x2, y2;
2429 double world_x, world_y;
2430 int x_scroll, y_scroll;
2431 int adj_x, adj_y;
2432 gboolean adj_changed;
2433 GtkAllocation allocation;
2434
2435 EelDRect selection_rect;
2436
2437 widget = GTK_WIDGET (data);
2438 container = NAUTILUS_CANVAS_CONTAINER (data);
2439 band_info = &container->details->rubberband_info;
2440
2441 g_assert (band_info->timer_id != 0);
2442
2443 adj_changed = FALSE;
2444 gtk_widget_get_allocation (widget, &allocation);
2445
2446 adj_x = gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container)));
2447 if (adj_x != band_info->last_adj_x) {
2448 band_info->last_adj_x = adj_x;
2449 adj_changed = TRUE;
2450 }
2451
2452 adj_y = gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container)));
2453 if (adj_y != band_info->last_adj_y) {
2454 band_info->last_adj_y = adj_y;
2455 adj_changed = TRUE;
2456 }
2457
2458 gdk_window_get_device_position (gtk_widget_get_window (widget),
2459 gdk_device_manager_get_client_pointer (
2460 gdk_display_get_device_manager (
2461 gtk_widget_get_display (widget))),
2462 &x, &y, NULL);
2463
2464 if (x < RUBBERBAND_SCROLL_THRESHOLD) {
2465 x_scroll = x - RUBBERBAND_SCROLL_THRESHOLD;
2466 x = 0;
2467 } else if (x >= allocation.width - RUBBERBAND_SCROLL_THRESHOLD) {
2468 x_scroll = x - allocation.width + RUBBERBAND_SCROLL_THRESHOLD + 1;
2469 x = allocation.width - 1;
2470 } else {
2471 x_scroll = 0;
2472 }
2473
2474 if (y < RUBBERBAND_SCROLL_THRESHOLD) {
2475 y_scroll = y - RUBBERBAND_SCROLL_THRESHOLD;
2476 y = 0;
2477 } else if (y >= allocation.height - RUBBERBAND_SCROLL_THRESHOLD) {
2478 y_scroll = y - allocation.height + RUBBERBAND_SCROLL_THRESHOLD + 1;
2479 y = allocation.height - 1;
2480 } else {
2481 y_scroll = 0;
2482 }
2483
2484 if (y_scroll == 0 && x_scroll == 0
2485 && (int) band_info->prev_x == x && (int) band_info->prev_y == y && !adj_changed) {
2486 return TRUE;
2487 }
2488
2489 nautilus_canvas_container_scroll (container, x_scroll, y_scroll);
2490
2491 /* Remember to convert from widget to scrolled window coords */
2492 eel_canvas_window_to_world (EEL_CANVAS (container),
2493 x + gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container))),
2494 y + gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container))),
2495 &world_x, &world_y);
2496
2497 if (world_x < band_info->start_x) {
2498 x1 = world_x;
2499 x2 = band_info->start_x;
2500 } else {
2501 x1 = band_info->start_x;
2502 x2 = world_x;
2503 }
2504
2505 if (world_y < band_info->start_y) {
2506 y1 = world_y;
2507 y2 = band_info->start_y;
2508 } else {
2509 y1 = band_info->start_y;
2510 y2 = world_y;
2511 }
2512
2513 /* Don't let the area of the selection rectangle be empty.
2514 * Aside from the fact that it would be funny when the rectangle disappears,
2515 * this also works around a crash in libart that happens sometimes when a
2516 * zero height rectangle is passed.
2517 */
2518 x2 = MAX (x1 + 1, x2);
2519 y2 = MAX (y1 + 1, y2);
2520
2521 eel_canvas_item_set
2522 (band_info->selection_rectangle,
2523 "x1", x1, "y1", y1,
2524 "x2", x2, "y2", y2,
2525 NULL);
2526
2527 selection_rect.x0 = x1;
2528 selection_rect.y0 = y1;
2529 selection_rect.x1 = x2;
2530 selection_rect.y1 = y2;
2531
2532 rubberband_select (container,
2533 &band_info->prev_rect,
2534 &selection_rect);
2535
2536 band_info->prev_x = x;
2537 band_info->prev_y = y;
2538
2539 band_info->prev_rect = selection_rect;
2540
2541 return TRUE;
2542 }
2543
2544 static void
2545 start_rubberbanding (NautilusCanvasContainer *container,
2546 GdkEventButton *event)
2547 {
2548 AtkObject *accessible;
2549 NautilusCanvasContainerDetails *details;
2550 NautilusCanvasRubberbandInfo *band_info;
2551 GdkRGBA bg_color, border_color;
2552 GList *p;
2553 NautilusCanvasIcon *icon;
2554 GtkStyleContext *context;
2555
2556 details = container->details;
2557 band_info = &details->rubberband_info;
2558
2559 g_signal_emit (container,
2560 signals[BAND_SELECT_STARTED], 0);
2561
2562 for (p = details->icons; p != NULL; p = p->next) {
2563 icon = p->data;
2564 icon->was_selected_before_rubberband = icon->is_selected;
2565 }
2566
2567 eel_canvas_window_to_world
2568 (EEL_CANVAS (container), event->x, event->y,
2569 &band_info->start_x, &band_info->start_y);
2570
2571 context = gtk_widget_get_style_context (GTK_WIDGET (container));
2572 gtk_style_context_save (context);
2573 gtk_style_context_add_class (context, GTK_STYLE_CLASS_RUBBERBAND);
2574
2575 gtk_style_context_get_background_color (context, GTK_STATE_FLAG_NORMAL, &bg_color);
2576 gtk_style_context_get_border_color (context, GTK_STATE_FLAG_NORMAL, &border_color);
2577
2578 gtk_style_context_restore (context);
2579
2580 band_info->selection_rectangle = eel_canvas_item_new
2581 (eel_canvas_root
2582 (EEL_CANVAS (container)),
2583 NAUTILUS_TYPE_SELECTION_CANVAS_ITEM,
2584 "x1", band_info->start_x,
2585 "y1", band_info->start_y,
2586 "x2", band_info->start_x,
2587 "y2", band_info->start_y,
2588 "fill_color_rgba", &bg_color,
2589 "outline_color_rgba", &border_color,
2590 "width_pixels", 1,
2591 NULL);
2592
2593 accessible = atk_gobject_accessible_for_object
2594 (G_OBJECT (band_info->selection_rectangle));
2595 atk_object_set_name (accessible, "selection");
2596 atk_object_set_description (accessible, _("The selection rectangle"));
2597
2598 band_info->prev_x = event->x - gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container)));
2599 band_info->prev_y = event->y - gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container)));
2600
2601 band_info->active = TRUE;
2602
2603 if (band_info->timer_id == 0) {
2604 band_info->timer_id = g_timeout_add
2605 (RUBBERBAND_TIMEOUT_INTERVAL,
2606 rubberband_timeout_callback,
2607 container);
2608 }
2609
2610 eel_canvas_item_grab (band_info->selection_rectangle,
2611 (GDK_POINTER_MOTION_MASK
2612 | GDK_BUTTON_RELEASE_MASK
2613 | GDK_SCROLL_MASK),
2614 NULL, event->time);
2615 }
2616
2617 static void
2618 stop_rubberbanding (NautilusCanvasContainer *container,
2619 guint32 time)
2620 {
2621 NautilusCanvasRubberbandInfo *band_info;
2622 GList *icons;
2623 gboolean enable_animation;
2624
2625 band_info = &container->details->rubberband_info;
2626
2627 g_assert (band_info->timer_id != 0);
2628 g_source_remove (band_info->timer_id);
2629 band_info->timer_id = 0;
2630
2631 band_info->active = FALSE;
2632
2633 g_object_get (gtk_settings_get_default (), "gtk-enable-animations", &enable_animation, NULL);
2634
2635 /* Destroy this canvas item; the parent will unref it. */
2636 eel_canvas_item_ungrab (band_info->selection_rectangle, time);
2637 eel_canvas_item_lower_to_bottom (band_info->selection_rectangle);
2638 if (enable_animation) {
2639 nautilus_selection_canvas_item_fade_out (NAUTILUS_SELECTION_CANVAS_ITEM (band_info->selection_rectangle), 150);
2640 } else {
2641 eel_canvas_item_destroy (band_info->selection_rectangle);
2642 }
2643 band_info->selection_rectangle = NULL;
2644
2645 /* if only one item has been selected, use it as range
2646 * selection base (cf. handle_icon_button_press) */
2647 icons = nautilus_canvas_container_get_selected_icons (container);
2648 if (g_list_length (icons) == 1) {
2649 container->details->range_selection_base_icon = icons->data;
2650 }
2651 g_list_free (icons);
2652
2653 g_signal_emit (container,
2654 signals[BAND_SELECT_ENDED], 0);
2655 }
2656
2657 /* Keyboard navigation. */
2658
2659 typedef gboolean (* IsBetterCanvasFunction) (NautilusCanvasContainer *container,
2660 NautilusCanvasIcon *start_icon,
2661 NautilusCanvasIcon *best_so_far,
2662 NautilusCanvasIcon *candidate,
2663 void *data);
2664
2665 static NautilusCanvasIcon *
2666 find_best_icon (NautilusCanvasContainer *container,
2667 NautilusCanvasIcon *start_icon,
2668 IsBetterCanvasFunction function,
2669 void *data)
2670 {
2671 GList *p;
2672 NautilusCanvasIcon *best, *candidate;
2673
2674 best = NULL;
2675 for (p = container->details->icons; p != NULL; p = p->next) {
2676 candidate = p->data;
2677
2678 if (candidate != start_icon) {
2679 if ((* function) (container, start_icon, best, candidate, data)) {
2680 best = candidate;
2681 }
2682 }
2683 }
2684 return best;
2685 }
2686
2687 static NautilusCanvasIcon *
2688 find_best_selected_icon (NautilusCanvasContainer *container,
2689 NautilusCanvasIcon *start_icon,
2690 IsBetterCanvasFunction function,
2691 void *data)
2692 {
2693 GList *p;
2694 NautilusCanvasIcon *best, *candidate;
2695
2696 best = NULL;
2697 for (p = container->details->icons; p != NULL; p = p->next) {
2698 candidate = p->data;
2699
2700 if (candidate != start_icon && candidate->is_selected) {
2701 if ((* function) (container, start_icon, best, candidate, data)) {
2702 best = candidate;
2703 }
2704 }
2705 }
2706 return best;
2707 }
2708
2709 static int
2710 compare_icons_by_uri (NautilusCanvasContainer *container,
2711 NautilusCanvasIcon *icon_a,
2712 NautilusCanvasIcon *icon_b)
2713 {
2714 char *uri_a, *uri_b;
2715 int result;
2716
2717 g_assert (NAUTILUS_IS_CANVAS_CONTAINER (container));
2718 g_assert (icon_a != NULL);
2719 g_assert (icon_b != NULL);
2720 g_assert (icon_a != icon_b);
2721
2722 uri_a = nautilus_canvas_container_get_icon_uri (container, icon_a);
2723 uri_b = nautilus_canvas_container_get_icon_uri (container, icon_b);
2724 result = strcmp (uri_a, uri_b);
2725 g_assert (result != 0);
2726 g_free (uri_a);
2727 g_free (uri_b);
2728
2729 return result;
2730 }
2731
2732 static int
2733 get_cmp_point_x (NautilusCanvasContainer *container,
2734 EelDRect icon_rect)
2735 {
2736 return (icon_rect.x0 + icon_rect.x1) / 2;
2737 }
2738
2739 static int
2740 get_cmp_point_y (NautilusCanvasContainer *container,
2741 EelDRect icon_rect)
2742 {
2743 return icon_rect.y1;
2744 }
2745
2746
2747 static int
2748 compare_icons_horizontal (NautilusCanvasContainer *container,
2749 NautilusCanvasIcon *icon_a,
2750 NautilusCanvasIcon *icon_b)
2751 {
2752 EelDRect world_rect;
2753 int ax, bx;
2754
2755 world_rect = nautilus_canvas_item_get_icon_rectangle (icon_a->item);
2756 eel_canvas_w2c
2757 (EEL_CANVAS (container),
2758 get_cmp_point_x (container, world_rect),
2759 get_cmp_point_y (container, world_rect),
2760 &ax,
2761 NULL);
2762 world_rect = nautilus_canvas_item_get_icon_rectangle (icon_b->item);
2763 eel_canvas_w2c
2764 (EEL_CANVAS (container),
2765 get_cmp_point_x (container, world_rect),
2766 get_cmp_point_y (container, world_rect),
2767 &bx,
2768 NULL);
2769
2770 if (ax < bx) {
2771 return -1;
2772 }
2773 if (ax > bx) {
2774 return +1;
2775 }
2776 return 0;
2777 }
2778
2779 static int
2780 compare_icons_vertical (NautilusCanvasContainer *container,
2781 NautilusCanvasIcon *icon_a,
2782 NautilusCanvasIcon *icon_b)
2783 {
2784 EelDRect world_rect;
2785 int ay, by;
2786
2787 world_rect = nautilus_canvas_item_get_icon_rectangle (icon_a->item);
2788 eel_canvas_w2c
2789 (EEL_CANVAS (container),
2790 get_cmp_point_x (container, world_rect),
2791 get_cmp_point_y (container, world_rect),
2792 NULL,
2793 &ay);
2794 world_rect = nautilus_canvas_item_get_icon_rectangle (icon_b->item);
2795 eel_canvas_w2c
2796 (EEL_CANVAS (container),
2797 get_cmp_point_x (container, world_rect),
2798 get_cmp_point_y (container, world_rect),
2799 NULL,
2800 &by);
2801
2802 if (ay < by) {
2803 return -1;
2804 }
2805 if (ay > by) {
2806 return +1;
2807 }
2808 return 0;
2809 }
2810
2811 static int
2812 compare_icons_horizontal_first (NautilusCanvasContainer *container,
2813 NautilusCanvasIcon *icon_a,
2814 NautilusCanvasIcon *icon_b)
2815 {
2816 EelDRect world_rect;
2817 int ax, ay, bx, by;
2818
2819 world_rect = nautilus_canvas_item_get_icon_rectangle (icon_a->item);
2820 eel_canvas_w2c
2821 (EEL_CANVAS (container),
2822 get_cmp_point_x (container, world_rect),
2823 get_cmp_point_y (container, world_rect),
2824 &ax,
2825 &ay);
2826 world_rect = nautilus_canvas_item_get_icon_rectangle (icon_b->item);
2827 eel_canvas_w2c
2828 (EEL_CANVAS (container),
2829 get_cmp_point_x (container, world_rect),
2830 get_cmp_point_y (container, world_rect),
2831 &bx,
2832 &by);
2833
2834 if (ax < bx) {
2835 return -1;
2836 }
2837 if (ax > bx) {
2838 return +1;
2839 }
2840 if (ay < by) {
2841 return -1;
2842 }
2843 if (ay > by) {
2844 return +1;
2845 }
2846 return compare_icons_by_uri (container, icon_a, icon_b);
2847 }
2848
2849 static int
2850 compare_icons_vertical_first (NautilusCanvasContainer *container,
2851 NautilusCanvasIcon *icon_a,
2852 NautilusCanvasIcon *icon_b)
2853 {
2854 EelDRect world_rect;
2855 int ax, ay, bx, by;
2856
2857 world_rect = nautilus_canvas_item_get_icon_rectangle (icon_a->item);
2858 eel_canvas_w2c
2859 (EEL_CANVAS (container),
2860 get_cmp_point_x (container, world_rect),
2861 get_cmp_point_y (container, world_rect),
2862 &ax,
2863 &ay);
2864 world_rect = nautilus_canvas_item_get_icon_rectangle (icon_b->item);
2865 eel_canvas_w2c
2866 (EEL_CANVAS (container),
2867 get_cmp_point_x (container, world_rect),
2868 get_cmp_point_y (container, world_rect),
2869 &bx,
2870 &by);
2871
2872 if (ay < by) {
2873 return -1;
2874 }
2875 if (ay > by) {
2876 return +1;
2877 }
2878 if (ax < bx) {
2879 return -1;
2880 }
2881 if (ax > bx) {
2882 return +1;
2883 }
2884 return compare_icons_by_uri (container, icon_a, icon_b);
2885 }
2886
2887 static gboolean
2888 leftmost_in_top_row (NautilusCanvasContainer *container,
2889 NautilusCanvasIcon *start_icon,
2890 NautilusCanvasIcon *best_so_far,
2891 NautilusCanvasIcon *candidate,
2892 void *data)
2893 {
2894 if (best_so_far == NULL) {
2895 return TRUE;
2896 }
2897 return compare_icons_vertical_first (container, best_so_far, candidate) > 0;
2898 }
2899
2900 static gboolean
2901 rightmost_in_top_row (NautilusCanvasContainer *container,
2902 NautilusCanvasIcon *start_icon,
2903 NautilusCanvasIcon *best_so_far,
2904 NautilusCanvasIcon *candidate,
2905 void *data)
2906 {
2907 if (best_so_far == NULL) {
2908 return TRUE;
2909 }
2910 return compare_icons_vertical (container, best_so_far, candidate) > 0;
2911 return compare_icons_horizontal (container, best_so_far, candidate) < 0;
2912 }
2913
2914 static gboolean
2915 rightmost_in_bottom_row (NautilusCanvasContainer *container,
2916 NautilusCanvasIcon *start_icon,
2917 NautilusCanvasIcon *best_so_far,
2918 NautilusCanvasIcon *candidate,
2919 void *data)
2920 {
2921 if (best_so_far == NULL) {
2922 return TRUE;
2923 }
2924 return compare_icons_vertical_first (container, best_so_far, candidate) < 0;
2925 }
2926
2927 static int
2928 compare_with_start_row (NautilusCanvasContainer *container,
2929 NautilusCanvasIcon *icon)
2930 {
2931 EelCanvasItem *item;
2932
2933 item = EEL_CANVAS_ITEM (icon->item);
2934
2935 if (container->details->arrow_key_start_y < item->y1) {
2936 return -1;
2937 }
2938 if (container->details->arrow_key_start_y > item->y2) {
2939 return +1;
2940 }
2941 return 0;
2942 }
2943
2944 static int
2945 compare_with_start_column (NautilusCanvasContainer *container,
2946 NautilusCanvasIcon *icon)
2947 {
2948 EelCanvasItem *item;
2949
2950 item = EEL_CANVAS_ITEM (icon->item);
2951
2952 if (container->details->arrow_key_start_x < item->x1) {
2953 return -1;
2954 }
2955 if (container->details->arrow_key_start_x > item->x2) {
2956 return +1;
2957 }
2958 return 0;
2959 }
2960
2961 static gboolean
2962 same_row_right_side_leftmost (NautilusCanvasContainer *container,
2963 NautilusCanvasIcon *start_icon,
2964 NautilusCanvasIcon *best_so_far,
2965 NautilusCanvasIcon *candidate,
2966 void *data)
2967 {
2968 /* Candidates not on the start row do not qualify. */
2969 if (compare_with_start_row (container, candidate) != 0) {
2970 return FALSE;
2971 }
2972
2973 /* Candidates that are farther right lose out. */
2974 if (best_so_far != NULL) {
2975 if (compare_icons_horizontal_first (container,
2976 best_so_far,
2977 candidate) < 0) {
2978 return FALSE;
2979 }
2980 }
2981
2982 /* Candidate to the left of the start do not qualify. */
2983 if (compare_icons_horizontal_first (container,
2984 candidate,
2985 start_icon) <= 0) {
2986 return FALSE;
2987 }
2988
2989 return TRUE;
2990 }
2991
2992 static gboolean
2993 same_row_left_side_rightmost (NautilusCanvasContainer *container,
2994 NautilusCanvasIcon *start_icon,
2995 NautilusCanvasIcon *best_so_far,
2996 NautilusCanvasIcon *candidate,
2997 void *data)
2998 {
2999 /* Candidates not on the start row do not qualify. */
3000 if (compare_with_start_row (container, candidate) != 0) {
3001 return FALSE;
3002 }
3003
3004 /* Candidates that are farther left lose out. */
3005 if (best_so_far != NULL) {
3006 if (compare_icons_horizontal_first (container,
3007 best_so_far,
3008 candidate) > 0) {
3009 return FALSE;
3010 }
3011 }
3012
3013 /* Candidate to the right of the start do not qualify. */
3014 if (compare_icons_horizontal_first (container,
3015 candidate,
3016 start_icon) >= 0) {
3017 return FALSE;
3018 }
3019
3020 return TRUE;
3021 }
3022
3023 static gboolean
3024 next_row_leftmost (NautilusCanvasContainer *container,
3025 NautilusCanvasIcon *start_icon,
3026 NautilusCanvasIcon *best_so_far,
3027 NautilusCanvasIcon *candidate,
3028 void *data)
3029 {
3030 /* sort out icons that are not below the current row */
3031 if (compare_with_start_row (container, candidate) >= 0) {
3032 return FALSE;
3033 }
3034
3035 if (best_so_far != NULL) {
3036 if (compare_icons_vertical_first (container,
3037 best_so_far,
3038 candidate) > 0) {
3039 /* candidate is above best choice, but below the current row */
3040 return TRUE;
3041 }
3042
3043 if (compare_icons_horizontal_first (container,
3044 best_so_far,
3045 candidate) > 0) {
3046 return TRUE;
3047 }
3048 }
3049
3050 return best_so_far == NULL;
3051 }
3052
3053 static gboolean
3054 next_row_rightmost (NautilusCanvasContainer *container,
3055 NautilusCanvasIcon *start_icon,
3056 NautilusCanvasIcon *best_so_far,
3057 NautilusCanvasIcon *candidate,
3058 void *data)
3059 {
3060 /* sort out icons that are not below the current row */
3061 if (compare_with_start_row (container, candidate) >= 0) {
3062 return FALSE;
3063 }
3064
3065 if (best_so_far != NULL) {
3066 if (compare_icons_vertical_first (container,
3067 best_so_far,
3068 candidate) > 0) {
3069 /* candidate is above best choice, but below the current row */
3070 return TRUE;
3071 }
3072
3073 if (compare_icons_horizontal_first (container,
3074 best_so_far,
3075 candidate) < 0) {
3076 return TRUE;
3077 }
3078 }
3079
3080 return best_so_far == NULL;
3081 }
3082
3083 static gboolean
3084 next_column_bottommost (NautilusCanvasContainer *container,
3085 NautilusCanvasIcon *start_icon,
3086 NautilusCanvasIcon *best_so_far,
3087 NautilusCanvasIcon *candidate,
3088 void *data)
3089 {
3090 /* sort out icons that are not on the right of the current column */
3091 if (compare_with_start_column (container, candidate) >= 0) {
3092 return FALSE;
3093 }
3094
3095 if (best_so_far != NULL) {
3096 if (compare_icons_horizontal_first (container,
3097 best_so_far,
3098 candidate) > 0) {
3099 /* candidate is above best choice, but below the current row */
3100 return TRUE;
3101 }
3102
3103 if (compare_icons_vertical_first (container,
3104 best_so_far,
3105 candidate) < 0) {
3106 return TRUE;
3107 }
3108 }
3109
3110 return best_so_far == NULL;
3111 }
3112
3113 static gboolean
3114 previous_row_rightmost (NautilusCanvasContainer *container,
3115 NautilusCanvasIcon *start_icon,
3116 NautilusCanvasIcon *best_so_far,
3117 NautilusCanvasIcon *candidate,
3118 void *data)
3119 {
3120 /* sort out icons that are not above the current row */
3121 if (compare_with_start_row (container, candidate) <= 0) {
3122 return FALSE;
3123 }
3124
3125 if (best_so_far != NULL) {
3126 if (compare_icons_vertical_first (container,
3127 best_so_far,
3128 candidate) < 0) {
3129 /* candidate is below the best choice, but above the current row */
3130 return TRUE;
3131 }
3132
3133 if (compare_icons_horizontal_first (container,
3134 best_so_far,
3135 candidate) < 0) {
3136 return TRUE;
3137 }
3138 }
3139
3140 return best_so_far == NULL;
3141 }
3142
3143 static gboolean
3144 same_column_above_lowest (NautilusCanvasContainer *container,
3145 NautilusCanvasIcon *start_icon,
3146 NautilusCanvasIcon *best_so_far,
3147 NautilusCanvasIcon *candidate,
3148 void *data)
3149 {
3150 /* Candidates not on the start column do not qualify. */
3151 if (compare_with_start_column (container, candidate) != 0) {
3152 return FALSE;
3153 }
3154
3155 /* Candidates that are higher lose out. */
3156 if (best_so_far != NULL) {
3157 if (compare_icons_vertical_first (container,
3158 best_so_far,
3159 candidate) > 0) {
3160 return FALSE;
3161 }
3162 }
3163
3164 /* Candidates below the start do not qualify. */
3165 if (compare_icons_vertical_first (container,
3166 candidate,
3167 start_icon) >= 0) {
3168 return FALSE;
3169 }
3170
3171 return TRUE;
3172 }
3173
3174 static gboolean
3175 same_column_below_highest (NautilusCanvasContainer *container,
3176 NautilusCanvasIcon *start_icon,
3177 NautilusCanvasIcon *best_so_far,
3178 NautilusCanvasIcon *candidate,
3179 void *data)
3180 {
3181 /* Candidates not on the start column do not qualify. */
3182 if (compare_with_start_column (container, candidate) != 0) {
3183 return FALSE;
3184 }
3185
3186 /* Candidates that are lower lose out. */
3187 if (best_so_far != NULL) {
3188 if (compare_icons_vertical_first (container,
3189 best_so_far,
3190 candidate) < 0) {
3191 return FALSE;
3192 }
3193 }
3194
3195 /* Candidates above the start do not qualify. */
3196 if (compare_icons_vertical_first (container,
3197 candidate,
3198 start_icon) <= 0) {
3199 return FALSE;
3200 }
3201
3202 return TRUE;
3203 }
3204
3205 static gboolean
3206 previous_column_highest (NautilusCanvasContainer *container,
3207 NautilusCanvasIcon *start_icon,
3208 NautilusCanvasIcon *best_so_far,
3209 NautilusCanvasIcon *candidate,
3210 void *data)
3211 {
3212 /* sort out icons that are not before the current column */
3213 if (compare_with_start_column (container, candidate) <= 0) {
3214 return FALSE;
3215 }
3216
3217 if (best_so_far != NULL) {
3218 if (compare_icons_horizontal (container,
3219 best_so_far,
3220 candidate) < 0) {
3221 /* candidate is right of the best choice, but left of the current column */
3222 return TRUE;
3223 }
3224
3225 if (compare_icons_vertical (container,
3226 best_so_far,
3227 candidate) > 0) {
3228 return TRUE;
3229 }
3230 }
3231
3232 return best_so_far == NULL;
3233 }
3234
3235
3236 static gboolean
3237 next_column_highest (NautilusCanvasContainer *container,
3238 NautilusCanvasIcon *start_icon,
3239 NautilusCanvasIcon *best_so_far,
3240 NautilusCanvasIcon *candidate,
3241 void *data)
3242 {
3243 /* sort out icons that are not after the current column */
3244 if (compare_with_start_column (container, candidate) >= 0) {
3245 return FALSE;
3246 }
3247
3248 if (best_so_far != NULL) {
3249 if (compare_icons_horizontal_first (container,
3250 best_so_far,
3251 candidate) > 0) {
3252 /* candidate is left of the best choice, but right of the current column */
3253 return TRUE;
3254 }
3255
3256 if (compare_icons_vertical_first (container,
3257 best_so_far,
3258 candidate) > 0) {
3259 return TRUE;
3260 }
3261 }
3262
3263 return best_so_far == NULL;
3264 }
3265
3266 static gboolean
3267 previous_column_lowest (NautilusCanvasContainer *container,
3268 NautilusCanvasIcon *start_icon,
3269 NautilusCanvasIcon *best_so_far,
3270 NautilusCanvasIcon *candidate,
3271 void *data)
3272 {
3273 /* sort out icons that are not before the current column */
3274 if (compare_with_start_column (container, candidate) <= 0) {
3275 return FALSE;
3276 }
3277
3278 if (best_so_far != NULL) {
3279 if (compare_icons_horizontal_first (container,
3280 best_so_far,
3281 candidate) < 0) {
3282 /* candidate is right of the best choice, but left of the current column */
3283 return TRUE;
3284 }
3285
3286 if (compare_icons_vertical_first (container,
3287 best_so_far,
3288 candidate) < 0) {
3289 return TRUE;
3290 }
3291 }
3292
3293 return best_so_far == NULL;
3294 }
3295
3296 static gboolean
3297 last_column_lowest (NautilusCanvasContainer *container,
3298 NautilusCanvasIcon *start_icon,
3299 NautilusCanvasIcon *best_so_far,
3300 NautilusCanvasIcon *candidate,
3301 void *data)
3302 {
3303 if (best_so_far == NULL) {
3304 return TRUE;
3305 }
3306 return compare_icons_horizontal_first (container, best_so_far, candidate) < 0;
3307 }
3308
3309 static gboolean
3310 closest_in_90_degrees (NautilusCanvasContainer *container,
3311 NautilusCanvasIcon *start_icon,
3312 NautilusCanvasIcon *best_so_far,
3313 NautilusCanvasIcon *candidate,
3314 void *data)
3315 {
3316 EelDRect world_rect;
3317 int x, y;
3318 int dx, dy;
3319 int dist;
3320 int *best_dist;
3321
3322
3323 world_rect = nautilus_canvas_item_get_icon_rectangle (candidate->item);
3324 eel_canvas_w2c
3325 (EEL_CANVAS (container),
3326 get_cmp_point_x (container, world_rect),
3327 get_cmp_point_y (container, world_rect),
3328 &x,
3329 &y);
3330
3331 dx = x - container->details->arrow_key_start_x;
3332 dy = y - container->details->arrow_key_start_y;
3333
3334 switch (container->details->arrow_key_direction) {
3335 case GTK_DIR_UP:
3336 if (dy > 0 ||
3337 ABS(dx) > ABS(dy)) {
3338 return FALSE;
3339 }
3340 break;
3341 case GTK_DIR_DOWN:
3342 if (dy < 0 ||
3343 ABS(dx) > ABS(dy)) {
3344 return FALSE;
3345 }
3346 break;
3347 case GTK_DIR_LEFT:
3348 if (dx > 0 ||
3349 ABS(dy) > ABS(dx)) {
3350 return FALSE;
3351 }
3352 break;
3353 case GTK_DIR_RIGHT:
3354 if (dx < 0 ||
3355 ABS(dy) > ABS(dx)) {
3356 return FALSE;
3357 }
3358 break;
3359 default:
3360 g_assert_not_reached();
3361 }
3362
3363 dist = dx*dx + dy*dy;
3364 best_dist = data;
3365
3366 if (best_so_far == NULL) {
3367 *best_dist = dist;
3368 return TRUE;
3369 }
3370
3371 if (dist < *best_dist) {
3372 *best_dist = dist;
3373 return TRUE;
3374 }
3375
3376 return FALSE;
3377 }
3378
3379 static EelDRect
3380 get_rubberband (NautilusCanvasIcon *icon1,
3381 NautilusCanvasIcon *icon2)
3382 {
3383 EelDRect rect1;
3384 EelDRect rect2;
3385 EelDRect ret;
3386
3387 eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (icon1->item),
3388 &rect1.x0, &rect1.y0,
3389 &rect1.x1, &rect1.y1);
3390 eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (icon2->item),
3391 &rect2.x0, &rect2.y0,
3392 &rect2.x1, &rect2.y1);
3393
3394 eel_drect_union (&ret, &rect1, &rect2);
3395
3396 return ret;
3397 }
3398
3399 static void
3400 keyboard_move_to (NautilusCanvasContainer *container,
3401 NautilusCanvasIcon *icon,
3402 NautilusCanvasIcon *from,
3403 GdkEventKey *event)
3404 {
3405 if (icon == NULL) {
3406 return;
3407 }
3408
3409 if (event != NULL &&
3410 (event->state & GDK_CONTROL_MASK) != 0 &&
3411 (event->state & GDK_SHIFT_MASK) == 0) {
3412 /* Move the keyboard focus. Use Control modifier
3413 * rather than Alt to avoid Sawfish conflict.
3414 */
3415 set_keyboard_focus (container, icon);
3416 container->details->keyboard_rubberband_start = NULL;
3417 } else if (event != NULL &&
3418 ((event->state & GDK_CONTROL_MASK) != 0 ||
3419 !container->details->auto_layout) &&
3420 (event->state & GDK_SHIFT_MASK) != 0) {
3421 /* Do rubberband selection */
3422 EelDRect rect;
3423
3424 if (from && !container->details->keyboard_rubberband_start) {
3425 set_keyboard_rubberband_start (container, from);
3426 }
3427
3428 set_keyboard_focus (container, icon);
3429
3430 if (icon && container->details->keyboard_rubberband_start) {
3431 rect = get_rubberband (container->details->keyboard_rubberband_start,
3432 icon);
3433 rubberband_select (container, NULL, &rect);
3434 }
3435 } else if (event != NULL &&
3436 (event->state & GDK_CONTROL_MASK) == 0 &&
3437 (event->state & GDK_SHIFT_MASK) != 0) {
3438 /* Select range */
3439 NautilusCanvasIcon *start_icon;
3440
3441 start_icon = container->details->range_selection_base_icon;
3442 if (start_icon == NULL || !start_icon->is_selected) {
3443 start_icon = icon;
3444 container->details->range_selection_base_icon = icon;
3445 }
3446
3447 set_keyboard_focus (container, icon);
3448
3449 if (select_range (container, start_icon, icon, TRUE)) {
3450 g_signal_emit (container,
3451 signals[SELECTION_CHANGED], 0);
3452 }
3453 } else {
3454 /* Select icons and get rid of the special keyboard focus. */
3455 clear_keyboard_focus (container);
3456 clear_keyboard_rubberband_start (container);
3457
3458 container->details->range_selection_base_icon = icon;
3459 if (select_one_unselect_others (container, icon)) {
3460 g_signal_emit (container,
3461 signals[SELECTION_CHANGED], 0);
3462 }
3463 }
3464 schedule_keyboard_icon_reveal (container, icon);
3465 }
3466
3467 static void
3468 keyboard_home (NautilusCanvasContainer *container,
3469 GdkEventKey *event)
3470 {
3471 NautilusCanvasIcon *from;
3472 NautilusCanvasIcon *to;
3473
3474 /* Home selects the first canvas.
3475 * Control-Home sets the keyboard focus to the first canvas.
3476 */
3477
3478 from = find_best_selected_icon (container, NULL,
3479 rightmost_in_bottom_row,
3480 NULL);
3481 to = find_best_icon (container, NULL, leftmost_in_top_row, NULL);
3482
3483 keyboard_move_to (container, to, from, event);
3484 }
3485
3486 static void
3487 keyboard_end (NautilusCanvasContainer *container,
3488 GdkEventKey *event)
3489 {
3490 NautilusCanvasIcon *to;
3491 NautilusCanvasIcon *from;
3492
3493 /* End selects the last canvas.
3494 * Control-End sets the keyboard focus to the last canvas.
3495 */
3496 from = find_best_selected_icon (container, NULL,
3497 leftmost_in_top_row,
3498 NULL);
3499 to = find_best_icon (container, NULL,
3500 nautilus_canvas_container_is_layout_vertical (container) ?
3501 last_column_lowest :
3502 rightmost_in_bottom_row,
3503 NULL);
3504
3505 keyboard_move_to (container, to, from, event);
3506 }
3507
3508 static void
3509 record_arrow_key_start (NautilusCanvasContainer *container,
3510 NautilusCanvasIcon *icon,
3511 GtkDirectionType direction)
3512 {
3513 EelDRect world_rect;
3514
3515 world_rect = nautilus_canvas_item_get_icon_rectangle (icon->item);
3516 eel_canvas_w2c
3517 (EEL_CANVAS (container),
3518 get_cmp_point_x (container, world_rect),
3519 get_cmp_point_y (container, world_rect),
3520 &container->details->arrow_key_start_x,
3521 &container->details->arrow_key_start_y);
3522 container->details->arrow_key_direction = direction;
3523 }
3524
3525 static void
3526 keyboard_arrow_key (NautilusCanvasContainer *container,
3527 GdkEventKey *event,
3528 GtkDirectionType direction,
3529 IsBetterCanvasFunction better_start,
3530 IsBetterCanvasFunction empty_start,
3531 IsBetterCanvasFunction better_destination,
3532 IsBetterCanvasFunction better_destination_fallback,
3533 IsBetterCanvasFunction better_destination_fallback_fallback,
3534 IsBetterCanvasFunction better_destination_manual)
3535 {
3536 NautilusCanvasIcon *from;
3537 NautilusCanvasIcon *to;
3538 int data;
3539
3540 /* Chose the icon to start with.
3541 * If we have a keyboard focus, start with it.
3542 * Otherwise, use the single selected icon.
3543 * If there's multiple selection, use the icon farthest toward the end.
3544 */
3545
3546 from = container->details->keyboard_focus;
3547
3548 if (from == NULL) {
3549 if (has_multiple_selection (container)) {
3550 if (all_selected (container)) {
3551 from = find_best_selected_icon
3552 (container, NULL,
3553 empty_start, NULL);
3554 } else {
3555 from = find_best_selected_icon
3556 (container, NULL,
3557 better_start, NULL);
3558 }
3559 } else {
3560 from = get_first_selected_icon (container);
3561 }
3562 }
3563
3564 /* If there's no icon, select the icon farthest toward the end.
3565 * If there is an icon, select the next icon based on the arrow direction.
3566 */
3567 if (from == NULL) {
3568 to = from = find_best_icon
3569 (container, NULL,
3570 empty_start, NULL);
3571 } else {
3572 record_arrow_key_start (container, from, direction);
3573
3574 to = find_best_icon
3575 (container, from,
3576 container->details->auto_layout ? better_destination : better_destination_manual,
3577 &data);
3578
3579 /* Wrap around to next/previous row/column */
3580 if (to == NULL &&
3581 better_destination_fallback != NULL) {
3582 to = find_best_icon
3583 (container, from,
3584 better_destination_fallback,
3585 &data);
3586 }
3587
3588 /* With a layout like
3589 * 1 2 3
3590 * 4
3591 * (horizontal layout)
3592 *
3593 * or
3594 *
3595 * 1 4
3596 * 2
3597 * 3
3598 * (vertical layout)
3599 *
3600 * * pressing down for any of 1,2,3 (horizontal layout)
3601 * * pressing right for any of 1,2,3 (vertical layout)
3602 *
3603 * Should select 4.
3604 */
3605 if (to == NULL &&
3606 container->details->auto_layout &&
3607 better_destination_fallback_fallback != NULL) {
3608 to = find_best_icon
3609 (container, from,
3610 better_destination_fallback_fallback,
3611 &data);
3612 }
3613
3614 if (to == NULL) {
3615 to = from;
3616 }
3617
3618 }
3619
3620 keyboard_move_to (container, to, from, event);
3621 }
3622
3623 static gboolean
3624 is_rectangle_selection_event (GdkEventKey *event)
3625 {
3626 return (event->state & GDK_CONTROL_MASK) != 0 &&
3627 (event->state & GDK_SHIFT_MASK) != 0;
3628 }
3629
3630 static void
3631 keyboard_right (NautilusCanvasContainer *container,
3632 GdkEventKey *event)
3633 {
3634 IsBetterCanvasFunction fallback;
3635 IsBetterCanvasFunction next_column_fallback;
3636
3637 fallback = NULL;
3638 if (container->details->auto_layout &&
3639 !nautilus_canvas_container_is_layout_vertical (container) &&
3640 !is_rectangle_selection_event (event)) {
3641 fallback = next_row_leftmost;
3642 }
3643
3644 next_column_fallback = NULL;
3645 if (nautilus_canvas_container_is_layout_vertical (container) &&
3646 gtk_widget_get_direction (GTK_WIDGET (container)) != GTK_TEXT_DIR_RTL) {
3647 next_column_fallback = next_column_bottommost;
3648 }
3649
3650 /* Right selects the next icon in the same row.
3651 * Control-Right sets the keyboard focus to the next icon in the same row.
3652 */
3653 keyboard_arrow_key (container,
3654 event,
3655 GTK_DIR_RIGHT,
3656 rightmost_in_bottom_row,
3657 nautilus_canvas_container_is_layout_rtl (container) ?
3658 rightmost_in_top_row : leftmost_in_top_row,
3659 same_row_right_side_leftmost,
3660 fallback,
3661 next_column_fallback,
3662 closest_in_90_degrees);
3663 }
3664
3665 static void
3666 keyboard_left (NautilusCanvasContainer *container,
3667 GdkEventKey *event)
3668 {
3669 IsBetterCanvasFunction fallback;
3670 IsBetterCanvasFunction previous_column_fallback;
3671
3672 fallback = NULL;
3673 if (container->details->auto_layout &&
3674 !nautilus_canvas_container_is_layout_vertical (container) &&
3675 !is_rectangle_selection_event (event)) {
3676 fallback = previous_row_rightmost;
3677 }
3678
3679 previous_column_fallback = NULL;
3680 if (nautilus_canvas_container_is_layout_vertical (container) &&
3681 gtk_widget_get_direction (GTK_WIDGET (container)) == GTK_TEXT_DIR_RTL) {
3682 previous_column_fallback = previous_column_lowest;
3683 }
3684
3685 /* Left selects the next icon in the same row.
3686 * Control-Left sets the keyboard focus to the next icon in the same row.
3687 */
3688 keyboard_arrow_key (container,
3689 event,
3690 GTK_DIR_LEFT,
3691 rightmost_in_bottom_row,
3692 nautilus_canvas_container_is_layout_rtl (container) ?
3693 rightmost_in_top_row : leftmost_in_top_row,
3694 same_row_left_side_rightmost,
3695 fallback,
3696 previous_column_fallback,
3697 closest_in_90_degrees);
3698 }
3699
3700 static void
3701 keyboard_down (NautilusCanvasContainer *container,
3702 GdkEventKey *event)
3703 {
3704 IsBetterCanvasFunction fallback;
3705 IsBetterCanvasFunction next_row_fallback;
3706
3707 fallback = NULL;
3708 if (container->details->auto_layout &&
3709 nautilus_canvas_container_is_layout_vertical (container) &&
3710 !is_rectangle_selection_event (event)) {
3711 if (gtk_widget_get_direction (GTK_WIDGET (container)) == GTK_TEXT_DIR_RTL) {
3712 fallback = previous_column_highest;
3713 } else {
3714 fallback = next_column_highest;
3715 }
3716 }
3717
3718 next_row_fallback = NULL;
3719 if (!nautilus_canvas_container_is_layout_vertical (container)) {
3720 if (gtk_widget_get_direction (GTK_WIDGET (container)) == GTK_TEXT_DIR_RTL) {
3721 next_row_fallback = next_row_leftmost;
3722 } else {
3723 next_row_fallback = next_row_rightmost;
3724 }
3725 }
3726
3727 /* Down selects the next icon in the same column.
3728 * Control-Down sets the keyboard focus to the next icon in the same column.
3729 */
3730 keyboard_arrow_key (container,
3731 event,
3732 GTK_DIR_DOWN,
3733 rightmost_in_bottom_row,
3734 nautilus_canvas_container_is_layout_rtl (container) ?
3735 rightmost_in_top_row : leftmost_in_top_row,
3736 same_column_below_highest,
3737 fallback,
3738 next_row_fallback,
3739 closest_in_90_degrees);
3740 }
3741
3742 static void
3743 keyboard_up (NautilusCanvasContainer *container,
3744 GdkEventKey *event)
3745 {
3746 IsBetterCanvasFunction fallback;
3747
3748 fallback = NULL;
3749 if (container->details->auto_layout &&
3750 nautilus_canvas_container_is_layout_vertical (container) &&
3751 !is_rectangle_selection_event (event)) {
3752 if (gtk_widget_get_direction (GTK_WIDGET (container)) == GTK_TEXT_DIR_RTL) {
3753 fallback = next_column_bottommost;
3754 } else {
3755 fallback = previous_column_lowest;
3756 }
3757 }
3758
3759 /* Up selects the next icon in the same column.
3760 * Control-Up sets the keyboard focus to the next icon in the same column.
3761 */
3762 keyboard_arrow_key (container,
3763 event,
3764 GTK_DIR_UP,
3765 rightmost_in_bottom_row,
3766 nautilus_canvas_container_is_layout_rtl (container) ?
3767 rightmost_in_top_row : leftmost_in_top_row,
3768 same_column_above_lowest,
3769 fallback,
3770 NULL,
3771 closest_in_90_degrees);
3772 }
3773
3774 static void
3775 keyboard_space (NautilusCanvasContainer *container,
3776 GdkEventKey *event)
3777 {
3778 NautilusCanvasIcon *icon;
3779
3780 if (!has_selection (container) &&
3781 container->details->keyboard_focus != NULL) {
3782 keyboard_move_to (container,
3783 container->details->keyboard_focus,
3784 NULL, NULL);
3785 } else if ((event->state & GDK_CONTROL_MASK) != 0 &&
3786 (event->state & GDK_SHIFT_MASK) == 0) {
3787 /* Control-space toggles the selection state of the current icon. */
3788 if (container->details->keyboard_focus != NULL) {
3789 icon_toggle_selected (container, container->details->keyboard_focus);
3790 g_signal_emit (container, signals[SELECTION_CHANGED], 0);
3791 if (container->details->keyboard_focus->is_selected) {
3792 container->details->range_selection_base_icon = container->details->keyboard_focus;
3793 }
3794 } else {
3795 icon = find_best_selected_icon (container,
3796 NULL,
3797 leftmost_in_top_row,
3798 NULL);
3799 if (icon == NULL) {
3800 icon = find_best_icon (container,
3801 NULL,
3802 leftmost_in_top_row,
3803 NULL);
3804 }
3805 if (icon != NULL) {
3806 set_keyboard_focus (container, icon);
3807 }
3808 }
3809 } else if ((event->state & GDK_SHIFT_MASK) != 0) {
3810 activate_selected_items_alternate (container, NULL);
3811 } else {
3812 preview_selected_items (container);
3813 }
3814 }
3815
3816 /* look for the first canvas that matches the longest part of a given
3817 * search pattern
3818 */
3819 typedef struct {
3820 gunichar *name;
3821 int last_match_length;
3822 } BestNameMatch;
3823
3824 #ifndef TAB_NAVIGATION_DISABLED
3825 static void
3826 select_previous_or_next_icon (NautilusCanvasContainer *container,
3827 gboolean next,
3828 GdkEventKey *event)
3829 {
3830 NautilusCanvasIcon *icon;
3831 const GList *item;
3832
3833 item = NULL;
3834 /* Chose the icon to start with.
3835 * If we have a keyboard focus, start with it.
3836 * Otherwise, use the single selected icon.
3837 */
3838 icon = container->details->keyboard_focus;
3839 if (icon == NULL) {
3840 icon = get_first_selected_icon (container);
3841 }
3842
3843 if (icon != NULL) {
3844 /* must have at least @canvas in the list */
3845 g_assert (container->details->icons != NULL);
3846 item = g_list_find (container->details->icons, icon);
3847 g_assert (item != NULL);
3848
3849 item = next ? item->next : item->prev;
3850 if (item == NULL) {
3851 item = next ? g_list_first (container->details->icons) : g_list_last (container->details->icons);
3852 }
3853
3854 } else if (container->details->icons != NULL) {
3855 /* no selection yet, pick the first or last item to select */
3856 item = next ? g_list_first (container->details->icons) : g_list_last (container->details->icons);
3857 }
3858
3859 icon = (item != NULL) ? item->data : NULL;
3860
3861 if (icon != NULL) {
3862 keyboard_move_to (container, icon, NULL, event);
3863 }
3864 }
3865 #endif
3866
3867 static void
3868 destroy (GtkWidget *object)
3869 {
3870 NautilusCanvasContainer *container;
3871
3872 container = NAUTILUS_CANVAS_CONTAINER (object);
3873
3874 nautilus_canvas_container_clear (container);
3875
3876 if (container->details->rubberband_info.timer_id != 0) {
3877 g_source_remove (container->details->rubberband_info.timer_id);
3878 container->details->rubberband_info.timer_id = 0;
3879 }
3880
3881 if (container->details->idle_id != 0) {
3882 g_source_remove (container->details->idle_id);
3883 container->details->idle_id = 0;
3884 }
3885
3886 if (container->details->stretch_idle_id != 0) {
3887 g_source_remove (container->details->stretch_idle_id);
3888 container->details->stretch_idle_id = 0;
3889 }
3890
3891 if (container->details->align_idle_id != 0) {
3892 g_source_remove (container->details->align_idle_id);
3893 container->details->align_idle_id = 0;
3894 }
3895
3896 if (container->details->selection_changed_id != 0) {
3897 g_source_remove (container->details->selection_changed_id);
3898 container->details->selection_changed_id = 0;
3899 }
3900
3901 if (container->details->size_allocation_count_id != 0) {
3902 g_source_remove (container->details->size_allocation_count_id);
3903 container->details->size_allocation_count_id = 0;
3904 }
3905
3906 GTK_WIDGET_CLASS (nautilus_canvas_container_parent_class)->destroy (object);
3907 }
3908
3909 static void
3910 finalize (GObject *object)
3911 {
3912 NautilusCanvasContainerDetails *details;
3913
3914 details = NAUTILUS_CANVAS_CONTAINER (object)->details;
3915
3916 g_signal_handlers_disconnect_by_func (nautilus_icon_view_preferences,
3917 text_ellipsis_limit_changed_container_callback,
3918 object);
3919 g_signal_handlers_disconnect_by_func (nautilus_desktop_preferences,
3920 text_ellipsis_limit_changed_container_callback,
3921 object);
3922
3923 g_hash_table_destroy (details->icon_set);
3924 details->icon_set = NULL;
3925
3926 g_free (details->font);
3927
3928 if (details->a11y_item_action_queue != NULL) {
3929 while (!g_queue_is_empty (details->a11y_item_action_queue)) {
3930 g_free (g_queue_pop_head (details->a11y_item_action_queue));
3931 }
3932 g_queue_free (details->a11y_item_action_queue);
3933 }
3934 if (details->a11y_item_action_idle_handler != 0) {
3935 g_source_remove (details->a11y_item_action_idle_handler);
3936 }
3937
3938 g_free (details);
3939
3940 G_OBJECT_CLASS (nautilus_canvas_container_parent_class)->finalize (object);
3941 }
3942
3943 /* GtkWidget methods. */
3944
3945 static gboolean
3946 clear_size_allocation_count (gpointer data)
3947 {
3948 NautilusCanvasContainer *container;
3949
3950 container = NAUTILUS_CANVAS_CONTAINER (data);
3951
3952 container->details->size_allocation_count_id = 0;
3953 container->details->size_allocation_count = 0;
3954
3955 return FALSE;
3956 }
3957
3958 static void
3959 size_allocate (GtkWidget *widget,
3960 GtkAllocation *allocation)
3961 {
3962 NautilusCanvasContainer *container;
3963 gboolean need_layout_redone;
3964 GtkAllocation wid_allocation;
3965
3966 container = NAUTILUS_CANVAS_CONTAINER (widget);
3967
3968 need_layout_redone = !container->details->has_been_allocated;
3969 gtk_widget_get_allocation (widget, &wid_allocation);
3970
3971 if (allocation->width != wid_allocation.width) {
3972 need_layout_redone = TRUE;
3973 }
3974
3975 if (allocation->height != wid_allocation.height) {
3976 need_layout_redone = TRUE;
3977 }
3978
3979 /* Under some conditions we can end up in a loop when size allocating.
3980 * This happens when the icons don't fit without a scrollbar, but fits
3981 * when a scrollbar is added (bug #129963 for details).
3982 * We keep track of this looping by increasing a counter in size_allocate
3983 * and clearing it in a high-prio idle (the only way to detect the loop is
3984 * done).
3985 * When we've done at more than two iterations (with/without scrollbar)
3986 * we terminate this looping by not redoing the layout when the width
3987 * is wider than the current one (i.e when removing the scrollbar).
3988 */
3989 if (container->details->size_allocation_count_id == 0) {
3990 container->details->size_allocation_count_id =
3991 g_idle_add_full (G_PRIORITY_HIGH,
3992 clear_size_allocation_count,
3993 container, NULL);
3994 }
3995 container->details->size_allocation_count++;
3996 if (container->details->size_allocation_count > 2 &&
3997 allocation->width >= wid_allocation.width) {
3998 need_layout_redone = FALSE;
3999 }
4000
4001 GTK_WIDGET_CLASS (nautilus_canvas_container_parent_class)->size_allocate (widget, allocation);
4002
4003 container->details->has_been_allocated = TRUE;
4004
4005 if (need_layout_redone) {
4006 redo_layout (container);
4007 }
4008 }
4009
4010 static GtkSizeRequestMode
4011 get_request_mode (GtkWidget *widget)
4012 {
4013 /* Don't trade size at all, since we get whatever we get anyway. */
4014 return GTK_SIZE_REQUEST_CONSTANT_SIZE;
4015 }
4016
4017 /* We need to implement these since the GtkScrolledWindow uses them
4018 to guess whether to show scrollbars or not, and if we don't report
4019 anything it'll tend to get it wrong causing double calls
4020 to size_allocate (at different sizes) during its size allocation. */
4021 static void
4022 get_prefered_width (GtkWidget *widget,
4023 gint *minimum_size,
4024 gint *natural_size)
4025 {
4026 EelCanvasGroup *root;
4027 double x1, x2;
4028 int cx1, cx2;
4029 int width;
4030
4031 root = eel_canvas_root (EEL_CANVAS (widget));
4032 eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (root),
4033 &x1, NULL, &x2, NULL);
4034 eel_canvas_w2c (EEL_CANVAS (widget), x1, 0, &cx1, NULL);
4035 eel_canvas_w2c (EEL_CANVAS (widget), x2, 0, &cx2, NULL);
4036
4037 width = cx2 - cx1;
4038 if (natural_size) {
4039 *natural_size = width;
4040 }
4041 if (minimum_size) {
4042 *minimum_size = width;
4043 }
4044 }
4045
4046 static void
4047 get_prefered_height (GtkWidget *widget,
4048 gint *minimum_size,
4049 gint *natural_size)
4050 {
4051 EelCanvasGroup *root;
4052 double y1, y2;
4053 int cy1, cy2;
4054 int height;
4055
4056 root = eel_canvas_root (EEL_CANVAS (widget));
4057 eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (root),
4058 NULL, &y1, NULL, &y2);
4059 eel_canvas_w2c (EEL_CANVAS (widget), 0, y1, NULL, &cy1);
4060 eel_canvas_w2c (EEL_CANVAS (widget), 0, y2, NULL, &cy2);
4061
4062 height = cy2 - cy1;
4063 if (natural_size) {
4064 *natural_size = height;
4065 }
4066 if (minimum_size) {
4067 *minimum_size = height;
4068 }
4069 }
4070
4071 static void
4072 realize (GtkWidget *widget)
4073 {
4074 GtkAdjustment *vadj, *hadj;
4075 NautilusCanvasContainer *container;
4076
4077 GTK_WIDGET_CLASS (nautilus_canvas_container_parent_class)->realize (widget);
4078
4079 container = NAUTILUS_CANVAS_CONTAINER (widget);
4080
4081 /* Ensure that the desktop window is native so the background
4082 set on it is drawn by X. */
4083 if (container->details->is_desktop) {
4084 gdk_x11_window_get_xid (gtk_layout_get_bin_window (GTK_LAYOUT (widget)));
4085 }
4086
4087 /* Set up DnD. */
4088 nautilus_canvas_dnd_init (container);
4089
4090 hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (widget));
4091 g_signal_connect (hadj, "value_changed",
4092 G_CALLBACK (handle_hadjustment_changed), widget);
4093
4094 vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (widget));
4095 g_signal_connect (vadj, "value_changed",
4096 G_CALLBACK (handle_vadjustment_changed), widget);
4097
4098 }
4099
4100 static void
4101 unrealize (GtkWidget *widget)
4102 {
4103 NautilusCanvasContainer *container;
4104
4105 container = NAUTILUS_CANVAS_CONTAINER (widget);
4106
4107 nautilus_canvas_dnd_fini (container);
4108
4109 GTK_WIDGET_CLASS (nautilus_canvas_container_parent_class)->unrealize (widget);
4110 }
4111
4112 static void
4113 style_updated (GtkWidget *widget)
4114 {
4115 NautilusCanvasContainer *container;
4116
4117 container = NAUTILUS_CANVAS_CONTAINER (widget);
4118 container->details->use_drop_shadows = container->details->drop_shadows_requested;
4119
4120 /* Don't chain up to parent, if this is a desktop container,
4121 * because that resets the background of the window.
4122 */
4123 if (!nautilus_canvas_container_get_is_desktop (container)) {
4124 GTK_WIDGET_CLASS (nautilus_canvas_container_parent_class)->style_updated (widget);
4125 }
4126
4127 if (gtk_widget_get_realized (widget)) {
4128 invalidate_labels (container);
4129 nautilus_canvas_container_request_update_all (container);
4130 }
4131 }
4132
4133 static gboolean
4134 button_press_event (GtkWidget *widget,
4135 GdkEventButton *event)
4136 {
4137 NautilusCanvasContainer *container;
4138 gboolean selection_changed;
4139 gboolean return_value;
4140 gboolean clicked_on_icon;
4141
4142 container = NAUTILUS_CANVAS_CONTAINER (widget);
4143 container->details->button_down_time = event->time;
4144
4145 /* Forget about the old keyboard selection now that we've started mousing. */
4146 clear_keyboard_focus (container);
4147 clear_keyboard_rubberband_start (container);
4148
4149 if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS) {
4150 /* We use our own double-click detection. */
4151 return TRUE;
4152 }
4153
4154 /* Invoke the canvas event handler and see if an item picks up the event. */
4155 clicked_on_icon = GTK_WIDGET_CLASS (nautilus_canvas_container_parent_class)->button_press_event (widget, event);
4156
4157 /* Move focus to canvas container, unless we're still renaming (to avoid exiting
4158 * renaming mode)
4159 */
4160 if (!gtk_widget_has_focus (widget) && !(is_renaming (container) || is_renaming_pending (container))) {
4161 gtk_widget_grab_focus (widget);
4162 }
4163
4164 if (clicked_on_icon) {
4165 return TRUE;
4166 }
4167
4168 if (event->button == DRAG_BUTTON &&
4169 event->type == GDK_BUTTON_PRESS) {
4170 /* Clear the last click icon for double click */
4171 container->details->double_click_icon[1] = container->details->double_click_icon[0];
4172 container->details->double_click_icon[0] = NULL;
4173 }
4174
4175 /* Button 1 does rubber banding. */
4176 if (event->button == RUBBERBAND_BUTTON) {
4177 if (! button_event_modifies_selection (event)) {
4178 selection_changed = unselect_all (container);
4179 if (selection_changed) {
4180 g_signal_emit (container,
4181 signals[SELECTION_CHANGED], 0);
4182 }
4183 }
4184
4185 start_rubberbanding (container, event);
4186 return TRUE;
4187 }
4188
4189 /* Prevent multi-button weirdness such as bug 6181 */
4190 if (container->details->rubberband_info.active) {
4191 return TRUE;
4192 }
4193
4194 /* Button 2 may be passed to the window manager. */
4195 if (event->button == MIDDLE_BUTTON) {
4196 selection_changed = unselect_all (container);
4197 if (selection_changed) {
4198 g_signal_emit (container, signals[SELECTION_CHANGED], 0);
4199 }
4200 g_signal_emit (widget, signals[MIDDLE_CLICK], 0, event);
4201 return TRUE;
4202 }
4203
4204 /* Button 3 does a contextual menu. */
4205 if (event->button == CONTEXTUAL_MENU_BUTTON) {
4206 end_renaming_mode (container, TRUE);
4207 selection_changed = unselect_all (container);
4208 if (selection_changed) {
4209 g_signal_emit (container, signals[SELECTION_CHANGED], 0);
4210 }
4211 g_signal_emit (widget, signals[CONTEXT_CLICK_BACKGROUND], 0, event);
4212 return TRUE;
4213 }
4214
4215 /* Otherwise, we emit a button_press message. */
4216 g_signal_emit (widget,
4217 signals[BUTTON_PRESS], 0, event,
4218 &return_value);
4219 return return_value;
4220 }
4221
4222 static void
4223 nautilus_canvas_container_did_not_drag (NautilusCanvasContainer *container,
4224 GdkEventButton *event)
4225 {
4226 NautilusCanvasContainerDetails *details;
4227 gboolean selection_changed;
4228 static gint64 last_click_time = 0;
4229 static gint click_count = 0;
4230 gint double_click_time;
4231 gint64 current_time;
4232
4233 details = container->details;
4234
4235 if (details->icon_selected_on_button_down &&
4236 ((event->state & GDK_CONTROL_MASK) != 0 ||
4237 (event->state & GDK_SHIFT_MASK) == 0)) {
4238 if (button_event_modifies_selection (event)) {
4239 details->range_selection_base_icon = NULL;
4240 icon_toggle_selected (container, details->drag_icon);
4241 g_signal_emit (container,
4242 signals[SELECTION_CHANGED], 0);
4243 } else {
4244 details->range_selection_base_icon = details->drag_icon;
4245 selection_changed = select_one_unselect_others
4246 (container, details->drag_icon);
4247
4248 if (selection_changed) {
4249 g_signal_emit (container,
4250 signals[SELECTION_CHANGED], 0);
4251 }
4252 }
4253 }
4254
4255 if (details->drag_icon != NULL &&
4256 (details->single_click_mode ||
4257 event->button == MIDDLE_BUTTON)) {
4258 /* Determine click count */
4259 g_object_get (G_OBJECT (gtk_widget_get_settings (GTK_WIDGET (container))),
4260 "gtk-double-click-time", &double_click_time,
4261 NULL);
4262 current_time = g_get_monotonic_time ();
4263 if (current_time - last_click_time < double_click_time * 1000) {
4264 click_count++;
4265 } else {
4266 click_count = 0;
4267 }
4268
4269 /* Stash time for next compare */
4270 last_click_time = current_time;
4271
4272 /* If single-click mode, activate the selected icons, unless modifying
4273 * the selection or pressing for a very long time, or double clicking.
4274 */
4275
4276
4277 if (click_count == 0 &&
4278 event->time - details->button_down_time < MAX_CLICK_TIME &&
4279 ! button_event_modifies_selection (event)) {
4280
4281 /* It's a tricky UI issue whether this should activate
4282 * just the clicked item (as if it were a link), or all
4283 * the selected items (as if you were issuing an "activate
4284 * selection" command). For now, we're trying the activate
4285 * entire selection version to see how it feels. Note that
4286 * NautilusList goes the other way because its "links" seem
4287 * much more link-like.
4288 */
4289 if (event->button == MIDDLE_BUTTON) {
4290 activate_selected_items_alternate (container, NULL);
4291 } else {
4292 activate_selected_items (container);
4293 }
4294 }
4295 }
4296 }
4297
4298 static gboolean
4299 clicked_within_double_click_interval (NautilusCanvasContainer *container)
4300 {
4301 static gint64 last_click_time = 0;
4302 static gint click_count = 0;
4303 gint double_click_time;
4304 gint64 current_time;
4305
4306 /* Determine click count */
4307 g_object_get (G_OBJECT (gtk_widget_get_settings (GTK_WIDGET (container))),
4308 "gtk-double-click-time", &double_click_time,
4309 NULL);
4310 current_time = g_get_monotonic_time ();
4311 if (current_time - last_click_time < double_click_time * 1000) {
4312 click_count++;
4313 } else {
4314 click_count = 0;
4315 }
4316
4317 /* Stash time for next compare */
4318 last_click_time = current_time;
4319
4320 /* Only allow double click */
4321 if (click_count == 1) {
4322 click_count = 0;
4323 return TRUE;
4324 } else {
4325 return FALSE;
4326 }
4327 }
4328
4329 static void
4330 clear_drag_state (NautilusCanvasContainer *container)
4331 {
4332 container->details->drag_icon = NULL;
4333 container->details->drag_state = DRAG_STATE_INITIAL;
4334 }
4335
4336 static gboolean
4337 start_stretching (NautilusCanvasContainer *container)
4338 {
4339 NautilusCanvasContainerDetails *details;
4340 NautilusCanvasIcon *icon;
4341 GtkWidget *toplevel;
4342 GtkCornerType corner;
4343 GdkCursor *cursor;
4344
4345 details = container->details;
4346 icon = details->stretch_icon;
4347
4348 /* Check if we hit the stretch handles. */
4349 if (!nautilus_canvas_item_hit_test_stretch_handles (icon->item,
4350 details->drag_x, details->drag_y,
4351 &corner)) {
4352 return FALSE;
4353 }
4354
4355 switch (corner) {
4356 case GTK_CORNER_TOP_LEFT:
4357 cursor = gdk_cursor_new (GDK_TOP_LEFT_CORNER);
4358 break;
4359 case GTK_CORNER_BOTTOM_LEFT:
4360 cursor = gdk_cursor_new (GDK_BOTTOM_LEFT_CORNER);
4361 break;
4362 case GTK_CORNER_TOP_RIGHT:
4363 cursor = gdk_cursor_new (GDK_TOP_RIGHT_CORNER);
4364 break;
4365 case GTK_CORNER_BOTTOM_RIGHT:
4366 cursor = gdk_cursor_new (GDK_BOTTOM_RIGHT_CORNER);
4367 break;
4368 default:
4369 cursor = NULL;
4370 break;
4371 }
4372 /* Set up the dragging. */
4373 details->drag_state = DRAG_STATE_STRETCH;
4374 eel_canvas_w2c (EEL_CANVAS (container),
4375 details->drag_x,
4376 details->drag_y,
4377 &details->stretch_start.pointer_x,
4378 &details->stretch_start.pointer_y);
4379 eel_canvas_w2c (EEL_CANVAS (container),
4380 icon->x, icon->y,
4381 &details->stretch_start.icon_x,
4382 &details->stretch_start.icon_y);
4383 icon_get_size (container, icon,
4384 &details->stretch_start.icon_size);
4385
4386 eel_canvas_item_grab (EEL_CANVAS_ITEM (icon->item),
4387 (GDK_POINTER_MOTION_MASK
4388 | GDK_BUTTON_RELEASE_MASK),
4389 cursor,
4390 GDK_CURRENT_TIME);
4391 if (cursor)
4392 g_object_unref (cursor);
4393
4394 /* Ensure the window itself is focused.. */
4395 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (container));
4396 if (toplevel != NULL && gtk_widget_get_realized (toplevel)) {
4397 gdk_window_focus (gtk_widget_get_window (toplevel), GDK_CURRENT_TIME);
4398 }
4399
4400 return TRUE;
4401 }
4402
4403 static gboolean
4404 update_stretch_at_idle (NautilusCanvasContainer *container)
4405 {
4406 NautilusCanvasContainerDetails *details;
4407 NautilusCanvasIcon *icon;
4408 double world_x, world_y;
4409 StretchState stretch_state;
4410
4411 details = container->details;
4412 icon = details->stretch_icon;
4413
4414 if (icon == NULL) {
4415 container->details->stretch_idle_id = 0;
4416 return FALSE;
4417 }
4418
4419 eel_canvas_w2c (EEL_CANVAS (container),
4420 details->world_x, details->world_y,
4421 &stretch_state.pointer_x, &stretch_state.pointer_y);
4422
4423 compute_stretch (&details->stretch_start,
4424 &stretch_state);
4425
4426 eel_canvas_c2w (EEL_CANVAS (container),
4427 stretch_state.icon_x, stretch_state.icon_y,
4428 &world_x, &world_y);
4429
4430 icon_set_position (icon, world_x, world_y);
4431 icon_set_size (container, icon, stretch_state.icon_size, FALSE, FALSE);
4432
4433 container->details->stretch_idle_id = 0;
4434
4435 return FALSE;
4436 }
4437
4438 static void
4439 continue_stretching (NautilusCanvasContainer *container,
4440 double world_x, double world_y)
4441 {
4442
4443 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
4444
4445 container->details->world_x = world_x;
4446 container->details->world_y = world_y;
4447
4448 if (container->details->stretch_idle_id == 0) {
4449 container->details->stretch_idle_id = g_idle_add ((GSourceFunc) update_stretch_at_idle, container);
4450 }
4451 }
4452
4453 static gboolean
4454 keyboard_stretching (NautilusCanvasContainer *container,
4455 GdkEventKey *event)
4456 {
4457 NautilusCanvasIcon *icon;
4458 guint size;
4459
4460 icon = container->details->stretch_icon;
4461
4462 if (icon == NULL || !icon->is_selected) {
4463 return FALSE;
4464 }
4465
4466 icon_get_size (container, icon, &size);
4467
4468 switch (event->keyval) {
4469 case GDK_KEY_equal:
4470 case GDK_KEY_plus:
4471 case GDK_KEY_KP_Add:
4472 icon_set_size (container, icon, size + 5, FALSE, FALSE);
4473 break;
4474 case GDK_KEY_minus:
4475 case GDK_KEY_KP_Subtract:
4476 icon_set_size (container, icon, size - 5, FALSE, FALSE);
4477 break;
4478 case GDK_KEY_0:
4479 case GDK_KEY_KP_0:
4480 nautilus_canvas_container_move_icon (container, icon,
4481 icon->x, icon->y,
4482 1.0,
4483 FALSE, TRUE, TRUE);
4484 break;
4485 }
4486
4487 return TRUE;
4488 }
4489
4490 static void
4491 ungrab_stretch_icon (NautilusCanvasContainer *container)
4492 {
4493 eel_canvas_item_ungrab (EEL_CANVAS_ITEM (container->details->stretch_icon->item),
4494 GDK_CURRENT_TIME);
4495 }
4496
4497 static void
4498 end_stretching (NautilusCanvasContainer *container,
4499 double world_x, double world_y)
4500 {
4501 NautilusCanvasPosition position;
4502 NautilusCanvasIcon *icon;
4503
4504 continue_stretching (container, world_x, world_y);
4505 ungrab_stretch_icon (container);
4506
4507 /* now that we're done stretching, update the icon's position */
4508
4509 icon = container->details->drag_icon;
4510 if (nautilus_canvas_container_is_layout_rtl (container)) {
4511 position.x = icon->saved_ltr_x = get_mirror_x_position (container, icon, icon->x);
4512 } else {
4513 position.x = icon->x;
4514 }
4515 position.y = icon->y;
4516 position.scale = icon->scale;
4517 g_signal_emit (container,
4518 signals[ICON_POSITION_CHANGED], 0,
4519 icon->data, &position);
4520
4521 clear_drag_state (container);
4522 redo_layout (container);
4523 }
4524
4525 static gboolean
4526 undo_stretching (NautilusCanvasContainer *container)
4527 {
4528 NautilusCanvasIcon *stretched_icon;
4529
4530 stretched_icon = container->details->stretch_icon;
4531
4532 if (stretched_icon == NULL) {
4533 return FALSE;
4534 }
4535
4536 if (container->details->drag_state == DRAG_STATE_STRETCH) {
4537 ungrab_stretch_icon (container);
4538 clear_drag_state (container);
4539 }
4540 nautilus_canvas_item_set_show_stretch_handles
4541 (stretched_icon->item, FALSE);
4542
4543 icon_set_position (stretched_icon,
4544 container->details->stretch_initial_x,
4545 container->details->stretch_initial_y);
4546 icon_set_size (container,
4547 stretched_icon,
4548 container->details->stretch_initial_size,
4549 TRUE,
4550 TRUE);
4551
4552 container->details->stretch_icon = NULL;
4553 emit_stretch_ended (container, stretched_icon);
4554 redo_layout (container);
4555
4556 return TRUE;
4557 }
4558
4559 static gboolean
4560 button_release_event (GtkWidget *widget,
4561 GdkEventButton *event)
4562 {
4563 NautilusCanvasContainer *container;
4564 NautilusCanvasContainerDetails *details;
4565 double world_x, world_y;
4566
4567 container = NAUTILUS_CANVAS_CONTAINER (widget);
4568 details = container->details;
4569
4570 if (event->button == RUBBERBAND_BUTTON && details->rubberband_info.active) {
4571 stop_rubberbanding (container, event->time);
4572 return TRUE;
4573 }
4574
4575 if (event->button == details->drag_button) {
4576 details->drag_button = 0;
4577
4578 switch (details->drag_state) {
4579 case DRAG_STATE_MOVE_OR_COPY:
4580 if (!details->drag_started) {
4581 nautilus_canvas_container_did_not_drag (container, event);
4582 } else {
4583 nautilus_canvas_dnd_end_drag (container);
4584 DEBUG ("Ending drag from canvas container");
4585 }
4586 break;
4587 case DRAG_STATE_STRETCH:
4588 eel_canvas_window_to_world
4589 (EEL_CANVAS (container), event->x, event->y, &world_x, &world_y);
4590 end_stretching (container, world_x, world_y);
4591 break;
4592 default:
4593 break;
4594 }
4595
4596 clear_drag_state (container);
4597 return TRUE;
4598 }
4599
4600 return GTK_WIDGET_CLASS (nautilus_canvas_container_parent_class)->button_release_event (widget, event);
4601 }
4602
4603 static int
4604 motion_notify_event (GtkWidget *widget,
4605 GdkEventMotion *event)
4606 {
4607 NautilusCanvasContainer *container;
4608 NautilusCanvasContainerDetails *details;
4609 double world_x, world_y;
4610 int canvas_x, canvas_y;
4611 GdkDragAction actions;
4612
4613 container = NAUTILUS_CANVAS_CONTAINER (widget);
4614 details = container->details;
4615
4616 if (details->drag_button != 0) {
4617 switch (details->drag_state) {
4618 case DRAG_STATE_MOVE_OR_COPY:
4619 if (details->drag_started) {
4620 break;
4621 }
4622
4623 eel_canvas_window_to_world
4624 (EEL_CANVAS (container), event->x, event->y, &world_x, &world_y);
4625
4626 if (gtk_drag_check_threshold (widget,
4627 details->drag_x,
4628 details->drag_y,
4629 world_x,
4630 world_y)) {
4631 details->drag_started = TRUE;
4632 details->drag_state = DRAG_STATE_MOVE_OR_COPY;
4633
4634 end_renaming_mode (container, TRUE);
4635
4636 eel_canvas_w2c (EEL_CANVAS (container),
4637 details->drag_x,
4638 details->drag_y,
4639 &canvas_x,
4640 &canvas_y);
4641
4642 actions = GDK_ACTION_COPY
4643 | GDK_ACTION_LINK
4644 | GDK_ACTION_ASK;
4645
4646 if (container->details->drag_allow_moves) {
4647 actions |= GDK_ACTION_MOVE;
4648 }
4649
4650 nautilus_canvas_dnd_begin_drag (container,
4651 actions,
4652 details->drag_button,
4653 event,
4654 canvas_x,
4655 canvas_y);
4656 DEBUG ("Beginning drag from canvas container");
4657 }
4658 break;
4659 case DRAG_STATE_STRETCH:
4660 eel_canvas_window_to_world
4661 (EEL_CANVAS (container), event->x, event->y, &world_x, &world_y);
4662 continue_stretching (container, world_x, world_y);
4663 break;
4664 default:
4665 break;
4666 }
4667 }
4668
4669 return GTK_WIDGET_CLASS (nautilus_canvas_container_parent_class)->motion_notify_event (widget, event);
4670 }
4671
4672 static void
4673 nautilus_canvas_container_get_icon_text (NautilusCanvasContainer *container,
4674 NautilusCanvasIconData *data,
4675 char **editable_text,
4676 char **additional_text,
4677 gboolean include_invisible)
4678 {
4679 NautilusCanvasContainerClass *klass;
4680
4681 klass = NAUTILUS_CANVAS_CONTAINER_GET_CLASS (container);
4682 g_assert (klass->get_icon_text != NULL);
4683
4684 klass->get_icon_text (container, data, editable_text, additional_text, include_invisible);
4685 }
4686
4687 static gboolean
4688 handle_popups (NautilusCanvasContainer *container,
4689 GdkEventKey *event,
4690 const char *signal)
4691 {
4692 GdkEventButton button_event = { 0 };
4693
4694 /* ensure we clear the drag state before showing the menu */
4695 clear_drag_state (container);
4696
4697 g_signal_emit_by_name (container, signal, &button_event);
4698
4699 return TRUE;
4700 }
4701
4702 static int
4703 key_press_event (GtkWidget *widget,
4704 GdkEventKey *event)
4705 {
4706 NautilusCanvasContainer *container;
4707 gboolean handled;
4708
4709 container = NAUTILUS_CANVAS_CONTAINER (widget);
4710 handled = FALSE;
4711
4712 if (is_renaming (container) || is_renaming_pending (container)) {
4713 switch (event->keyval) {
4714 case GDK_KEY_Return:
4715 case GDK_KEY_KP_Enter:
4716 end_renaming_mode (container, TRUE);
4717 handled = TRUE;
4718 break;
4719 case GDK_KEY_Escape:
4720 end_renaming_mode (container, FALSE);
4721 handled = TRUE;
4722 break;
4723 default:
4724 break;
4725 }
4726 } else {
4727 switch (event->keyval) {
4728 case GDK_KEY_Home:
4729 case GDK_KEY_KP_Home:
4730 keyboard_home (container, event);
4731 handled = TRUE;
4732 break;
4733 case GDK_KEY_End:
4734 case GDK_KEY_KP_End:
4735 keyboard_end (container, event);
4736 handled = TRUE;
4737 break;
4738 case GDK_KEY_Left:
4739 case GDK_KEY_KP_Left:
4740 /* Don't eat Alt-Left, as that is used for history browsing */
4741 if ((event->state & GDK_MOD1_MASK) == 0) {
4742 keyboard_left (container, event);
4743 handled = TRUE;
4744 }
4745 break;
4746 case GDK_KEY_Up:
4747 case GDK_KEY_KP_Up:
4748 /* Don't eat Alt-Up, as that is used for alt-shift-Up */
4749 if ((event->state & GDK_MOD1_MASK) == 0) {
4750 keyboard_up (container, event);
4751 handled = TRUE;
4752 }
4753 break;
4754 case GDK_KEY_Right:
4755 case GDK_KEY_KP_Right:
4756 /* Don't eat Alt-Right, as that is used for history browsing */
4757 if ((event->state & GDK_MOD1_MASK) == 0) {
4758 keyboard_right (container, event);
4759 handled = TRUE;
4760 }
4761 break;
4762 case GDK_KEY_Down:
4763 case GDK_KEY_KP_Down:
4764 /* Don't eat Alt-Down, as that is used for Open */
4765 if ((event->state & GDK_MOD1_MASK) == 0) {
4766 keyboard_down (container, event);
4767 handled = TRUE;
4768 }
4769 break;
4770 case GDK_KEY_space:
4771 keyboard_space (container, event);
4772 handled = TRUE;
4773 break;
4774 #ifndef TAB_NAVIGATION_DISABLED
4775 case GDK_KEY_Tab:
4776 case GDK_KEY_ISO_Left_Tab:
4777 select_previous_or_next_icon (container,
4778 (event->state & GDK_SHIFT_MASK) == 0, event);
4779 handled = TRUE;
4780 break;
4781 #endif
4782 case GDK_KEY_Return:
4783 case GDK_KEY_KP_Enter:
4784 if ((event->state & GDK_SHIFT_MASK) != 0) {
4785 activate_selected_items_alternate (container, NULL);
4786 } else {
4787 activate_selected_items (container);
4788 }
4789
4790 handled = TRUE;
4791 break;
4792 case GDK_KEY_Escape:
4793 handled = undo_stretching (container);
4794 break;
4795 case GDK_KEY_plus:
4796 case GDK_KEY_minus:
4797 case GDK_KEY_equal:
4798 case GDK_KEY_KP_Add:
4799 case GDK_KEY_KP_Subtract:
4800 case GDK_KEY_0:
4801 case GDK_KEY_KP_0:
4802 if (event->state & GDK_CONTROL_MASK) {
4803 handled = keyboard_stretching (container, event);
4804 }
4805 break;
4806 case GDK_KEY_F10:
4807 /* handle Ctrl+F10 because we want to display the
4808 * background popup even if something is selected.
4809 * The other cases are handled by popup_menu().
4810 */
4811 if (event->state & GDK_CONTROL_MASK) {
4812 handled = handle_popups (container, event,
4813 "context_click_background");
4814 }
4815 break;
4816 case GDK_KEY_v:
4817 /* Eat Control + v to not enable type ahead */
4818 if ((event->state & GDK_CONTROL_MASK) != 0) {
4819 handled = TRUE;
4820 }
4821 break;
4822 default:
4823 break;
4824 }
4825 }
4826
4827 if (!handled) {
4828 handled = GTK_WIDGET_CLASS (nautilus_canvas_container_parent_class)->key_press_event (widget, event);
4829 }
4830
4831 return handled;
4832 }
4833
4834 static gboolean
4835 popup_menu (GtkWidget *widget)
4836 {
4837 NautilusCanvasContainer *container;
4838
4839 container = NAUTILUS_CANVAS_CONTAINER (widget);
4840
4841 if (has_selection (container)) {
4842 handle_popups (container, NULL,
4843 "context_click_selection");
4844 } else {
4845 handle_popups (container, NULL,
4846 "context_click_background");
4847 }
4848
4849 return TRUE;
4850 }
4851
4852 static void
4853 draw_canvas_background (EelCanvas *icon,
4854 cairo_t *cr)
4855 {
4856 /* Don't chain up to the parent to avoid clearing and redrawing */
4857 }
4858
4859 static void
4860 grab_notify_cb (GtkWidget *widget,
4861 gboolean was_grabbed)
4862 {
4863 NautilusCanvasContainer *container;
4864
4865 container = NAUTILUS_CANVAS_CONTAINER (widget);
4866
4867 if (container->details->rubberband_info.active &&
4868 !was_grabbed) {
4869 /* we got a (un)grab-notify during rubberband.
4870 * This happens when a new modal dialog shows
4871 * up (e.g. authentication or an error). Stop
4872 * the rubberbanding so that we can handle the
4873 * dialog. */
4874 stop_rubberbanding (container,
4875 GDK_CURRENT_TIME);
4876 }
4877 }
4878
4879 static void
4880 text_ellipsis_limit_changed_container_callback (gpointer callback_data)
4881 {
4882 NautilusCanvasContainer *container;
4883
4884 container = NAUTILUS_CANVAS_CONTAINER (callback_data);
4885 invalidate_label_sizes (container);
4886 schedule_redo_layout (container);
4887 }
4888
4889 static GObject*
4890 nautilus_canvas_container_constructor (GType type,
4891 guint n_construct_params,
4892 GObjectConstructParam *construct_params)
4893 {
4894 NautilusCanvasContainer *container;
4895 GObject *object;
4896
4897 object = G_OBJECT_CLASS (nautilus_canvas_container_parent_class)->constructor
4898 (type,
4899 n_construct_params,
4900 construct_params);
4901
4902 container = NAUTILUS_CANVAS_CONTAINER (object);
4903 if (nautilus_canvas_container_get_is_desktop (container)) {
4904 g_signal_connect_swapped (nautilus_desktop_preferences,
4905 "changed::" NAUTILUS_PREFERENCES_DESKTOP_TEXT_ELLIPSIS_LIMIT,
4906 G_CALLBACK (text_ellipsis_limit_changed_container_callback),
4907 container);
4908 } else {
4909 g_signal_connect_swapped (nautilus_icon_view_preferences,
4910 "changed::" NAUTILUS_PREFERENCES_ICON_VIEW_TEXT_ELLIPSIS_LIMIT,
4911 G_CALLBACK (text_ellipsis_limit_changed_container_callback),
4912 container);
4913 }
4914
4915 return object;
4916 }
4917
4918 /* Initialization. */
4919
4920 static void
4921 nautilus_canvas_container_class_init (NautilusCanvasContainerClass *class)
4922 {
4923 GtkWidgetClass *widget_class;
4924 EelCanvasClass *canvas_class;
4925
4926 G_OBJECT_CLASS (class)->constructor = nautilus_canvas_container_constructor;
4927 G_OBJECT_CLASS (class)->finalize = finalize;
4928
4929 /* Signals. */
4930
4931 signals[SELECTION_CHANGED]
4932 = g_signal_new ("selection_changed",
4933 G_TYPE_FROM_CLASS (class),
4934 G_SIGNAL_RUN_LAST,
4935 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
4936 selection_changed),
4937 NULL, NULL,
4938 g_cclosure_marshal_VOID__VOID,
4939 G_TYPE_NONE, 0);
4940 signals[BUTTON_PRESS]
4941 = g_signal_new ("button_press",
4942 G_TYPE_FROM_CLASS (class),
4943 G_SIGNAL_RUN_LAST,
4944 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
4945 button_press),
4946 NULL, NULL,
4947 g_cclosure_marshal_generic,
4948 G_TYPE_BOOLEAN, 1,
4949 GDK_TYPE_EVENT);
4950 signals[ACTIVATE]
4951 = g_signal_new ("activate",
4952 G_TYPE_FROM_CLASS (class),
4953 G_SIGNAL_RUN_LAST,
4954 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
4955 activate),
4956 NULL, NULL,
4957 g_cclosure_marshal_VOID__POINTER,
4958 G_TYPE_NONE, 1,
4959 G_TYPE_POINTER);
4960 signals[ACTIVATE_ALTERNATE]
4961 = g_signal_new ("activate_alternate",
4962 G_TYPE_FROM_CLASS (class),
4963 G_SIGNAL_RUN_LAST,
4964 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
4965 activate_alternate),
4966 NULL, NULL,
4967 g_cclosure_marshal_VOID__POINTER,
4968 G_TYPE_NONE, 1,
4969 G_TYPE_POINTER);
4970 signals[ACTIVATE_PREVIEWER]
4971 = g_signal_new ("activate_previewer",
4972 G_TYPE_FROM_CLASS (class),
4973 G_SIGNAL_RUN_LAST,
4974 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
4975 activate_previewer),
4976 NULL, NULL,
4977 g_cclosure_marshal_generic,
4978 G_TYPE_NONE, 2,
4979 G_TYPE_POINTER, G_TYPE_POINTER);
4980 signals[CONTEXT_CLICK_SELECTION]
4981 = g_signal_new ("context_click_selection",
4982 G_TYPE_FROM_CLASS (class),
4983 G_SIGNAL_RUN_LAST,
4984 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
4985 context_click_selection),
4986 NULL, NULL,
4987 g_cclosure_marshal_VOID__POINTER,
4988 G_TYPE_NONE, 1,
4989 G_TYPE_POINTER);
4990 signals[CONTEXT_CLICK_BACKGROUND]
4991 = g_signal_new ("context_click_background",
4992 G_TYPE_FROM_CLASS (class),
4993 G_SIGNAL_RUN_LAST,
4994 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
4995 context_click_background),
4996 NULL, NULL,
4997 g_cclosure_marshal_VOID__POINTER,
4998 G_TYPE_NONE, 1,
4999 G_TYPE_POINTER);
5000 signals[MIDDLE_CLICK]
5001 = g_signal_new ("middle_click",
5002 G_TYPE_FROM_CLASS (class),
5003 G_SIGNAL_RUN_LAST,
5004 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5005 middle_click),
5006 NULL, NULL,
5007 g_cclosure_marshal_VOID__POINTER,
5008 G_TYPE_NONE, 1,
5009 G_TYPE_POINTER);
5010 signals[ICON_POSITION_CHANGED]
5011 = g_signal_new ("icon_position_changed",
5012 G_TYPE_FROM_CLASS (class),
5013 G_SIGNAL_RUN_LAST,
5014 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5015 icon_position_changed),
5016 NULL, NULL,
5017 g_cclosure_marshal_generic,
5018 G_TYPE_NONE, 2,
5019 G_TYPE_POINTER,
5020 G_TYPE_POINTER);
5021 signals[ICON_STRETCH_STARTED]
5022 = g_signal_new ("icon_stretch_started",
5023 G_TYPE_FROM_CLASS (class),
5024 G_SIGNAL_RUN_LAST,
5025 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5026 icon_stretch_started),
5027 NULL, NULL,
5028 g_cclosure_marshal_VOID__POINTER,
5029 G_TYPE_NONE, 1,
5030 G_TYPE_POINTER);
5031 signals[ICON_STRETCH_ENDED]
5032 = g_signal_new ("icon_stretch_ended",
5033 G_TYPE_FROM_CLASS (class),
5034 G_SIGNAL_RUN_LAST,
5035 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5036 icon_stretch_ended),
5037 NULL, NULL,
5038 g_cclosure_marshal_VOID__POINTER,
5039 G_TYPE_NONE, 1,
5040 G_TYPE_POINTER);
5041 signals[ICON_RENAME_STARTED]
5042 = g_signal_new ("icon_rename_started",
5043 G_TYPE_FROM_CLASS (class),
5044 G_SIGNAL_RUN_LAST,
5045 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5046 icon_rename_started),
5047 NULL, NULL,
5048 g_cclosure_marshal_VOID__POINTER,
5049 G_TYPE_NONE, 1,
5050 G_TYPE_POINTER);
5051 signals[ICON_RENAME_ENDED]
5052 = g_signal_new ("icon_rename_ended",
5053 G_TYPE_FROM_CLASS (class),
5054 G_SIGNAL_RUN_LAST,
5055 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5056 icon_rename_ended),
5057 NULL, NULL,
5058 g_cclosure_marshal_generic,
5059 G_TYPE_NONE, 2,
5060 G_TYPE_POINTER,
5061 G_TYPE_STRING);
5062 signals[GET_ICON_URI]
5063 = g_signal_new ("get_icon_uri",
5064 G_TYPE_FROM_CLASS (class),
5065 G_SIGNAL_RUN_LAST,
5066 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5067 get_icon_uri),
5068 NULL, NULL,
5069 g_cclosure_marshal_generic,
5070 G_TYPE_STRING, 1,
5071 G_TYPE_POINTER);
5072 signals[GET_ICON_DROP_TARGET_URI]
5073 = g_signal_new ("get_icon_drop_target_uri",
5074 G_TYPE_FROM_CLASS (class),
5075 G_SIGNAL_RUN_LAST,
5076 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5077 get_icon_drop_target_uri),
5078 NULL, NULL,
5079 g_cclosure_marshal_generic,
5080 G_TYPE_STRING, 1,
5081 G_TYPE_POINTER);
5082 signals[MOVE_COPY_ITEMS]
5083 = g_signal_new ("move_copy_items",
5084 G_TYPE_FROM_CLASS (class),
5085 G_SIGNAL_RUN_LAST,
5086 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5087 move_copy_items),
5088 NULL, NULL,
5089 g_cclosure_marshal_generic,
5090 G_TYPE_NONE, 6,
5091 G_TYPE_POINTER,
5092 G_TYPE_POINTER,
5093 G_TYPE_POINTER,
5094 GDK_TYPE_DRAG_ACTION,
5095 G_TYPE_INT,
5096 G_TYPE_INT);
5097 signals[HANDLE_NETSCAPE_URL]
5098 = g_signal_new ("handle_netscape_url",
5099 G_TYPE_FROM_CLASS (class),
5100 G_SIGNAL_RUN_LAST,
5101 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5102 handle_netscape_url),
5103 NULL, NULL,
5104 g_cclosure_marshal_generic,
5105 G_TYPE_NONE, 5,
5106 G_TYPE_STRING,
5107 G_TYPE_STRING,
5108 GDK_TYPE_DRAG_ACTION,
5109 G_TYPE_INT,
5110 G_TYPE_INT);
5111 signals[HANDLE_URI_LIST]
5112 = g_signal_new ("handle_uri_list",
5113 G_TYPE_FROM_CLASS (class),
5114 G_SIGNAL_RUN_LAST,
5115 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5116 handle_uri_list),
5117 NULL, NULL,
5118 g_cclosure_marshal_generic,
5119 G_TYPE_NONE, 5,
5120 G_TYPE_STRING,
5121 G_TYPE_STRING,
5122 GDK_TYPE_DRAG_ACTION,
5123 G_TYPE_INT,
5124 G_TYPE_INT);
5125 signals[HANDLE_TEXT]
5126 = g_signal_new ("handle_text",
5127 G_TYPE_FROM_CLASS (class),
5128 G_SIGNAL_RUN_LAST,
5129 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5130 handle_text),
5131 NULL, NULL,
5132 g_cclosure_marshal_generic,
5133 G_TYPE_NONE, 5,
5134 G_TYPE_STRING,
5135 G_TYPE_STRING,
5136 GDK_TYPE_DRAG_ACTION,
5137 G_TYPE_INT,
5138 G_TYPE_INT);
5139 signals[HANDLE_RAW]
5140 = g_signal_new ("handle_raw",
5141 G_TYPE_FROM_CLASS (class),
5142 G_SIGNAL_RUN_LAST,
5143 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5144 handle_raw),
5145 NULL, NULL,
5146 g_cclosure_marshal_generic,
5147 G_TYPE_NONE, 7,
5148 G_TYPE_POINTER,
5149 G_TYPE_INT,
5150 G_TYPE_STRING,
5151 G_TYPE_STRING,
5152 GDK_TYPE_DRAG_ACTION,
5153 G_TYPE_INT,
5154 G_TYPE_INT);
5155 signals[GET_CONTAINER_URI]
5156 = g_signal_new ("get_container_uri",
5157 G_TYPE_FROM_CLASS (class),
5158 G_SIGNAL_RUN_LAST,
5159 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5160 get_container_uri),
5161 NULL, NULL,
5162 g_cclosure_marshal_generic,
5163 G_TYPE_STRING, 0);
5164 signals[CAN_ACCEPT_ITEM]
5165 = g_signal_new ("can_accept_item",
5166 G_TYPE_FROM_CLASS (class),
5167 G_SIGNAL_RUN_LAST,
5168 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5169 can_accept_item),
5170 NULL, NULL,
5171 g_cclosure_marshal_generic,
5172 G_TYPE_INT, 2,
5173 G_TYPE_POINTER,
5174 G_TYPE_STRING);
5175 signals[GET_STORED_ICON_POSITION]
5176 = g_signal_new ("get_stored_icon_position",
5177 G_TYPE_FROM_CLASS (class),
5178 G_SIGNAL_RUN_LAST,
5179 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5180 get_stored_icon_position),
5181 NULL, NULL,
5182 g_cclosure_marshal_generic,
5183 G_TYPE_BOOLEAN, 2,
5184 G_TYPE_POINTER,
5185 G_TYPE_POINTER);
5186 signals[GET_STORED_LAYOUT_TIMESTAMP]
5187 = g_signal_new ("get_stored_layout_timestamp",
5188 G_TYPE_FROM_CLASS (class),
5189 G_SIGNAL_RUN_LAST,
5190 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5191 get_stored_layout_timestamp),
5192 NULL, NULL,
5193 g_cclosure_marshal_generic,
5194 G_TYPE_BOOLEAN, 2,
5195 G_TYPE_POINTER,
5196 G_TYPE_POINTER);
5197 signals[STORE_LAYOUT_TIMESTAMP]
5198 = g_signal_new ("store_layout_timestamp",
5199 G_TYPE_FROM_CLASS (class),
5200 G_SIGNAL_RUN_LAST,
5201 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5202 store_layout_timestamp),
5203 NULL, NULL,
5204 g_cclosure_marshal_generic,
5205 G_TYPE_BOOLEAN, 2,
5206 G_TYPE_POINTER,
5207 G_TYPE_POINTER);
5208 signals[LAYOUT_CHANGED]
5209 = g_signal_new ("layout_changed",
5210 G_TYPE_FROM_CLASS (class),
5211 G_SIGNAL_RUN_LAST,
5212 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5213 layout_changed),
5214 NULL, NULL,
5215 g_cclosure_marshal_VOID__VOID,
5216 G_TYPE_NONE, 0);
5217 signals[BAND_SELECT_STARTED]
5218 = g_signal_new ("band_select_started",
5219 G_TYPE_FROM_CLASS (class),
5220 G_SIGNAL_RUN_LAST,
5221 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5222 band_select_started),
5223 NULL, NULL,
5224 g_cclosure_marshal_VOID__VOID,
5225 G_TYPE_NONE, 0);
5226 signals[BAND_SELECT_ENDED]
5227 = g_signal_new ("band_select_ended",
5228 G_TYPE_FROM_CLASS (class),
5229 G_SIGNAL_RUN_LAST,
5230 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5231 band_select_ended),
5232 NULL, NULL,
5233 g_cclosure_marshal_VOID__VOID,
5234 G_TYPE_NONE, 0);
5235 signals[ICON_ADDED]
5236 = g_signal_new ("icon_added",
5237 G_TYPE_FROM_CLASS (class),
5238 G_SIGNAL_RUN_LAST,
5239 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5240 icon_added),
5241 NULL, NULL,
5242 g_cclosure_marshal_VOID__POINTER,
5243 G_TYPE_NONE, 1, G_TYPE_POINTER);
5244 signals[ICON_REMOVED]
5245 = g_signal_new ("icon_removed",
5246 G_TYPE_FROM_CLASS (class),
5247 G_SIGNAL_RUN_LAST,
5248 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5249 icon_removed),
5250 NULL, NULL,
5251 g_cclosure_marshal_VOID__POINTER,
5252 G_TYPE_NONE, 1, G_TYPE_POINTER);
5253
5254 signals[CLEARED]
5255 = g_signal_new ("cleared",
5256 G_TYPE_FROM_CLASS (class),
5257 G_SIGNAL_RUN_LAST,
5258 G_STRUCT_OFFSET (NautilusCanvasContainerClass,
5259 cleared),
5260 NULL, NULL,
5261 g_cclosure_marshal_VOID__VOID,
5262 G_TYPE_NONE, 0);
5263
5264 /* GtkWidget class. */
5265
5266 widget_class = GTK_WIDGET_CLASS (class);
5267 widget_class->destroy = destroy;
5268 widget_class->size_allocate = size_allocate;
5269 widget_class->get_request_mode = get_request_mode;
5270 widget_class->get_preferred_width = get_prefered_width;
5271 widget_class->get_preferred_height = get_prefered_height;
5272 widget_class->realize = realize;
5273 widget_class->unrealize = unrealize;
5274 widget_class->button_press_event = button_press_event;
5275 widget_class->button_release_event = button_release_event;
5276 widget_class->motion_notify_event = motion_notify_event;
5277 widget_class->key_press_event = key_press_event;
5278 widget_class->popup_menu = popup_menu;
5279 widget_class->style_updated = style_updated;
5280 widget_class->grab_notify = grab_notify_cb;
5281 widget_class->get_accessible = get_accessible;
5282
5283 canvas_class = EEL_CANVAS_CLASS (class);
5284 canvas_class->draw_background = draw_canvas_background;
5285
5286 gtk_widget_class_install_style_property (widget_class,
5287 g_param_spec_boolean ("activate_prelight_icon_label",
5288 "Activate Prelight Icon Label",
5289 "Whether icon labels should make use of its prelight color in prelight state",
5290 FALSE,
5291 G_PARAM_READABLE));
5292 }
5293
5294 static void
5295 update_selected (NautilusCanvasContainer *container)
5296 {
5297 GList *node;
5298 NautilusCanvasIcon *icon;
5299
5300 for (node = container->details->icons; node != NULL; node = node->next) {
5301 icon = node->data;
5302 if (icon->is_selected) {
5303 eel_canvas_item_request_update (EEL_CANVAS_ITEM (icon->item));
5304 }
5305 }
5306 }
5307
5308 static gboolean
5309 handle_focus_in_event (GtkWidget *widget, GdkEventFocus *event, gpointer user_data)
5310 {
5311 update_selected (NAUTILUS_CANVAS_CONTAINER (widget));
5312
5313 return FALSE;
5314 }
5315
5316 static gboolean
5317 handle_focus_out_event (GtkWidget *widget, GdkEventFocus *event, gpointer user_data)
5318 {
5319 /* End renaming and commit change. */
5320 end_renaming_mode (NAUTILUS_CANVAS_CONTAINER (widget), TRUE);
5321 update_selected (NAUTILUS_CANVAS_CONTAINER (widget));
5322
5323 return FALSE;
5324 }
5325
5326
5327 static int text_ellipsis_limits[NAUTILUS_ZOOM_LEVEL_N_ENTRIES];
5328 static int desktop_text_ellipsis_limit;
5329
5330 static gboolean
5331 get_text_ellipsis_limit_for_zoom (char **strs,
5332 const char *zoom_level,
5333 int *limit)
5334 {
5335 char **p;
5336 char *str;
5337 gboolean success;
5338
5339 success = FALSE;
5340
5341 /* default */
5342 *limit = 3;
5343
5344 if (zoom_level != NULL) {
5345 str = g_strdup_printf ("%s:%%d", zoom_level);
5346 } else {
5347 str = g_strdup ("%d");
5348 }
5349
5350 if (strs != NULL) {
5351 for (p = strs; *p != NULL; p++) {
5352 if (sscanf (*p, str, limit)) {
5353 success = TRUE;
5354 }
5355 }
5356 }
5357
5358 g_free (str);
5359
5360 return success;
5361 }
5362
5363 static const char * zoom_level_names[] = {
5364 "smallest",
5365 "smaller",
5366 "small",
5367 "standard",
5368 "large",
5369 "larger",
5370 "largest"
5371 };
5372
5373 static void
5374 text_ellipsis_limit_changed_callback (gpointer callback_data)
5375 {
5376 char **pref;
5377 unsigned int i;
5378 int one_limit;
5379
5380 pref = g_settings_get_strv (nautilus_icon_view_preferences,
5381 NAUTILUS_PREFERENCES_ICON_VIEW_TEXT_ELLIPSIS_LIMIT);
5382
5383 /* set default */
5384 get_text_ellipsis_limit_for_zoom (pref, NULL, &one_limit);
5385 for (i = 0; i < NAUTILUS_ZOOM_LEVEL_N_ENTRIES; i++) {
5386 text_ellipsis_limits[i] = one_limit;
5387 }
5388
5389 /* override for each zoom level */
5390 for (i = 0; i < G_N_ELEMENTS(zoom_level_names); i++) {
5391 if (get_text_ellipsis_limit_for_zoom (pref,
5392 zoom_level_names[i],
5393 &one_limit)) {
5394 text_ellipsis_limits[i] = one_limit;
5395 }
5396 }
5397
5398 g_strfreev (pref);
5399 }
5400
5401 static void
5402 desktop_text_ellipsis_limit_changed_callback (gpointer callback_data)
5403 {
5404 int pref;
5405
5406 pref = g_settings_get_int (nautilus_desktop_preferences, NAUTILUS_PREFERENCES_DESKTOP_TEXT_ELLIPSIS_LIMIT);
5407 desktop_text_ellipsis_limit = pref;
5408 }
5409
5410 static void
5411 nautilus_canvas_container_init (NautilusCanvasContainer *container)
5412 {
5413 NautilusCanvasContainerDetails *details;
5414 static gboolean setup_prefs = FALSE;
5415
5416 details = g_new0 (NautilusCanvasContainerDetails, 1);
5417
5418 details->icon_set = g_hash_table_new (g_direct_hash, g_direct_equal);
5419 details->layout_timestamp = UNDEFINED_TIME;
5420 details->zoom_level = NAUTILUS_ZOOM_LEVEL_STANDARD;
5421
5422 container->details = details;
5423
5424 g_signal_connect (container, "focus-in-event",
5425 G_CALLBACK (handle_focus_in_event), NULL);
5426 g_signal_connect (container, "focus-out-event",
5427 G_CALLBACK (handle_focus_out_event), NULL);
5428
5429 if (!setup_prefs) {
5430 g_signal_connect_swapped (nautilus_icon_view_preferences,
5431 "changed::" NAUTILUS_PREFERENCES_ICON_VIEW_TEXT_ELLIPSIS_LIMIT,
5432 G_CALLBACK (text_ellipsis_limit_changed_callback),
5433 NULL);
5434 text_ellipsis_limit_changed_callback (NULL);
5435
5436 g_signal_connect_swapped (nautilus_icon_view_preferences,
5437 "changed::" NAUTILUS_PREFERENCES_DESKTOP_TEXT_ELLIPSIS_LIMIT,
5438 G_CALLBACK (desktop_text_ellipsis_limit_changed_callback),
5439 NULL);
5440 desktop_text_ellipsis_limit_changed_callback (NULL);
5441
5442 setup_prefs = TRUE;
5443 }
5444 }
5445
5446 typedef struct {
5447 NautilusCanvasContainer *container;
5448 GdkEventButton *event;
5449 } ContextMenuParameters;
5450
5451 static gboolean
5452 handle_canvas_double_click (NautilusCanvasContainer *container,
5453 NautilusCanvasIcon *icon,
5454 GdkEventButton *event)
5455 {
5456 NautilusCanvasContainerDetails *details;
5457
5458 if (event->button != DRAG_BUTTON) {
5459 return FALSE;
5460 }
5461
5462 details = container->details;
5463
5464 if (!details->single_click_mode &&
5465 clicked_within_double_click_interval (container) &&
5466 details->double_click_icon[0] == details->double_click_icon[1] &&
5467 details->double_click_button[0] == details->double_click_button[1]) {
5468 if (!button_event_modifies_selection (event)) {
5469 activate_selected_items (container);
5470 return TRUE;
5471 } else if ((event->state & GDK_CONTROL_MASK) == 0 &&
5472 (event->state & GDK_SHIFT_MASK) != 0) {
5473 activate_selected_items_alternate (container, icon);
5474 return TRUE;
5475 }
5476 }
5477
5478 return FALSE;
5479 }
5480
5481 /* NautilusCanvasIcon event handling. */
5482
5483 /* Conceptually, pressing button 1 together with CTRL or SHIFT toggles
5484 * selection of a single icon without affecting the other icons;
5485 * without CTRL or SHIFT, it selects a single icon and un-selects all
5486 * the other icons. But in this latter case, the de-selection should
5487 * only happen when the button is released if the icon is already
5488 * selected, because the user might select multiple icons and drag all
5489 * of them by doing a simple click-drag.
5490 */
5491
5492 static gboolean
5493 handle_canvas_button_press (NautilusCanvasContainer *container,
5494 NautilusCanvasIcon *icon,
5495 GdkEventButton *event)
5496 {
5497 NautilusCanvasContainerDetails *details;
5498
5499 details = container->details;
5500
5501 if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS) {
5502 return TRUE;
5503 }
5504
5505 if (event->button != DRAG_BUTTON
5506 && event->button != CONTEXTUAL_MENU_BUTTON
5507 && event->button != DRAG_MENU_BUTTON) {
5508 return TRUE;
5509 }
5510
5511 if ((event->button == DRAG_BUTTON) &&
5512 event->type == GDK_BUTTON_PRESS) {
5513 /* The next double click has to be on this icon */
5514 details->double_click_icon[1] = details->double_click_icon[0];
5515 details->double_click_icon[0] = icon;
5516
5517 details->double_click_button[1] = details->double_click_button[0];
5518 details->double_click_button[0] = event->button;
5519 }
5520
5521 if (handle_canvas_double_click (container, icon, event)) {
5522 /* Double clicking does not trigger a D&D action. */
5523 details->drag_button = 0;
5524 details->drag_icon = NULL;
5525 return TRUE;
5526 }
5527
5528 if (event->button == DRAG_BUTTON
5529 || event->button == DRAG_MENU_BUTTON) {
5530 details->drag_button = event->button;
5531 details->drag_icon = icon;
5532 details->drag_x = event->x;
5533 details->drag_y = event->y;
5534 details->drag_state = DRAG_STATE_MOVE_OR_COPY;
5535 details->drag_started = FALSE;
5536
5537 /* Check to see if this is a click on the stretch handles.
5538 * If so, it won't modify the selection.
5539 */
5540 if (icon == container->details->stretch_icon) {
5541 if (start_stretching (container)) {
5542 return TRUE;
5543 }
5544 }
5545 }
5546
5547 /* Modify the selection as appropriate. Selection is modified
5548 * the same way for contextual menu as it would be without.
5549 */
5550 details->icon_selected_on_button_down = icon->is_selected;
5551
5552 if ((event->button == DRAG_BUTTON || event->button == MIDDLE_BUTTON) &&
5553 (event->state & GDK_SHIFT_MASK) != 0) {
5554 NautilusCanvasIcon *start_icon;
5555
5556 start_icon = details->range_selection_base_icon;
5557 if (start_icon == NULL || !start_icon->is_selected) {
5558 start_icon = icon;
5559 details->range_selection_base_icon = icon;
5560 }
5561 if (select_range (container, start_icon, icon,
5562 (event->state & GDK_CONTROL_MASK) == 0)) {
5563 g_signal_emit (container,
5564 signals[SELECTION_CHANGED], 0);
5565 }
5566 } else if (!details->icon_selected_on_button_down) {
5567 details->range_selection_base_icon = icon;
5568 if (button_event_modifies_selection (event)) {
5569 icon_toggle_selected (container, icon);
5570 g_signal_emit (container,
5571 signals[SELECTION_CHANGED], 0);
5572 } else {
5573 select_one_unselect_others (container, icon);
5574 g_signal_emit (container,
5575 signals[SELECTION_CHANGED], 0);
5576 }
5577 }
5578
5579 if (event->button == CONTEXTUAL_MENU_BUTTON) {
5580 clear_drag_state (container);
5581
5582 g_signal_emit (container,
5583 signals[CONTEXT_CLICK_SELECTION], 0,
5584 event);
5585 }
5586
5587
5588 return TRUE;
5589 }
5590
5591 static int
5592 item_event_callback (EelCanvasItem *item,
5593 GdkEvent *event,
5594 gpointer data)
5595 {
5596 NautilusCanvasContainer *container;
5597 NautilusCanvasIcon *icon;
5598
5599 container = NAUTILUS_CANVAS_CONTAINER (data);
5600
5601 icon = NAUTILUS_CANVAS_ITEM (item)->user_data;
5602 g_assert (icon != NULL);
5603
5604 switch (event->type) {
5605 case GDK_BUTTON_PRESS:
5606 if (handle_canvas_button_press (container, icon, &event->button)) {
5607 /* Stop the event from being passed along further. Returning
5608 * TRUE ain't enough.
5609 */
5610 return TRUE;
5611 }
5612 return FALSE;
5613 default:
5614 return FALSE;
5615 }
5616 }
5617
5618 GtkWidget *
5619 nautilus_canvas_container_new (void)
5620 {
5621 return gtk_widget_new (NAUTILUS_TYPE_CANVAS_CONTAINER, NULL);
5622 }
5623
5624 /* Clear all of the icons in the container. */
5625 void
5626 nautilus_canvas_container_clear (NautilusCanvasContainer *container)
5627 {
5628 NautilusCanvasContainerDetails *details;
5629 NautilusCanvasIcon *icon;
5630 GList *p;
5631
5632 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
5633
5634 details = container->details;
5635 details->layout_timestamp = UNDEFINED_TIME;
5636 details->store_layout_timestamps_when_finishing_new_icons = FALSE;
5637
5638 if (details->icons == NULL) {
5639 return;
5640 }
5641
5642 end_renaming_mode (container, TRUE);
5643
5644 clear_keyboard_focus (container);
5645 clear_keyboard_rubberband_start (container);
5646 unschedule_keyboard_icon_reveal (container);
5647 set_pending_icon_to_reveal (container, NULL);
5648 details->stretch_icon = NULL;
5649 details->drop_target = NULL;
5650
5651 for (p = details->icons; p != NULL; p = p->next) {
5652 icon = p->data;
5653 if (icon->is_monitored) {
5654 nautilus_canvas_container_stop_monitor_top_left (container,
5655 icon->data,
5656 icon);
5657 }
5658 icon_free (p->data);
5659 }
5660 g_list_free (details->icons);
5661 details->icons = NULL;
5662 g_list_free (details->new_icons);
5663 details->new_icons = NULL;
5664
5665 g_hash_table_destroy (details->icon_set);
5666 details->icon_set = g_hash_table_new (g_direct_hash, g_direct_equal);
5667
5668 nautilus_canvas_container_update_scroll_region (container);
5669 }
5670
5671 gboolean
5672 nautilus_canvas_container_is_empty (NautilusCanvasContainer *container)
5673 {
5674 return container->details->icons == NULL;
5675 }
5676
5677 NautilusCanvasIconData *
5678 nautilus_canvas_container_get_first_visible_icon (NautilusCanvasContainer *container)
5679 {
5680 GList *l;
5681 NautilusCanvasIcon *icon, *best_icon;
5682 double x, y;
5683 double x1, y1, x2, y2;
5684 double *pos, best_pos;
5685 double hadj_v, vadj_v, h_page_size;
5686 gboolean better_icon;
5687 gboolean compare_lt;
5688
5689 hadj_v = gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container)));
5690 vadj_v = gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container)));
5691 h_page_size = gtk_adjustment_get_page_size (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container)));
5692
5693 if (nautilus_canvas_container_is_layout_rtl (container)) {
5694 x = hadj_v + h_page_size - ICON_PAD_LEFT - 1;
5695 y = vadj_v;
5696 } else {
5697 x = hadj_v;
5698 y = vadj_v;
5699 }
5700
5701 eel_canvas_c2w (EEL_CANVAS (container),
5702 x, y,
5703 &x, &y);
5704
5705 l = container->details->icons;
5706 best_icon = NULL;
5707 best_pos = 0;
5708 while (l != NULL) {
5709 icon = l->data;
5710
5711 if (icon_is_positioned (icon)) {
5712 eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (icon->item),
5713 &x1, &y1, &x2, &y2);
5714
5715 compare_lt = FALSE;
5716 if (nautilus_canvas_container_is_layout_vertical (container)) {
5717 pos = &x1;
5718 if (nautilus_canvas_container_is_layout_rtl (container)) {
5719 compare_lt = TRUE;
5720 better_icon = x1 < x + ICON_PAD_LEFT;
5721 } else {
5722 better_icon = x2 > x + ICON_PAD_LEFT;
5723 }
5724 } else {
5725 pos = &y1;
5726 better_icon = y2 > y + ICON_PAD_TOP;
5727 }
5728 if (better_icon) {
5729 if (best_icon == NULL) {
5730 better_icon = TRUE;
5731 } else if (compare_lt) {
5732 better_icon = best_pos < *pos;
5733 } else {
5734 better_icon = best_pos > *pos;
5735 }
5736
5737 if (better_icon) {
5738 best_icon = icon;
5739 best_pos = *pos;
5740 }
5741 }
5742 }
5743
5744 l = l->next;
5745 }
5746
5747 return best_icon ? best_icon->data : NULL;
5748 }
5749
5750 /* puts the icon at the top of the screen */
5751 void
5752 nautilus_canvas_container_scroll_to_canvas (NautilusCanvasContainer *container,
5753 NautilusCanvasIconData *data)
5754 {
5755 GList *l;
5756 NautilusCanvasIcon *icon;
5757 GtkAdjustment *hadj, *vadj;
5758 EelIRect bounds;
5759 GtkAllocation allocation;
5760
5761 hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container));
5762 vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container));
5763 gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
5764
5765 /* We need to force a relayout now if there are updates queued
5766 * since we need the final positions */
5767 nautilus_canvas_container_layout_now (container);
5768
5769 l = container->details->icons;
5770 while (l != NULL) {
5771 icon = l->data;
5772
5773 if (icon->data == data &&
5774 icon_is_positioned (icon)) {
5775
5776 if (nautilus_canvas_container_is_auto_layout (container)) {
5777 /* ensure that we reveal the entire row/column */
5778 icon_get_row_and_column_bounds (container, icon, &bounds, TRUE);
5779 } else {
5780 item_get_canvas_bounds (EEL_CANVAS_ITEM (icon->item), &bounds, TRUE);
5781 }
5782
5783 if (nautilus_canvas_container_is_layout_vertical (container)) {
5784 if (nautilus_canvas_container_is_layout_rtl (container)) {
5785 gtk_adjustment_set_value (hadj, bounds.x1 - allocation.width);
5786 } else {
5787 gtk_adjustment_set_value (hadj, bounds.x0);
5788 }
5789 } else {
5790 gtk_adjustment_set_value (vadj, bounds.y0);
5791 }
5792 }
5793
5794 l = l->next;
5795 }
5796 }
5797
5798 /* Call a function for all the icons. */
5799 typedef struct {
5800 NautilusCanvasCallback callback;
5801 gpointer callback_data;
5802 } CallbackAndData;
5803
5804 static void
5805 call_canvas_callback (gpointer data, gpointer callback_data)
5806 {
5807 NautilusCanvasIcon *icon;
5808 CallbackAndData *callback_and_data;
5809
5810 icon = data;
5811 callback_and_data = callback_data;
5812 (* callback_and_data->callback) (icon->data, callback_and_data->callback_data);
5813 }
5814
5815 void
5816 nautilus_canvas_container_for_each (NautilusCanvasContainer *container,
5817 NautilusCanvasCallback callback,
5818 gpointer callback_data)
5819 {
5820 CallbackAndData callback_and_data;
5821
5822 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
5823
5824 callback_and_data.callback = callback;
5825 callback_and_data.callback_data = callback_data;
5826
5827 g_list_foreach (container->details->icons,
5828 call_canvas_callback, &callback_and_data);
5829 }
5830
5831 static int
5832 selection_changed_at_idle_callback (gpointer data)
5833 {
5834 NautilusCanvasContainer *container;
5835
5836 container = NAUTILUS_CANVAS_CONTAINER (data);
5837
5838 g_signal_emit (container,
5839 signals[SELECTION_CHANGED], 0);
5840
5841 container->details->selection_changed_id = 0;
5842 return FALSE;
5843 }
5844
5845 /* utility routine to remove a single icon from the container */
5846
5847 static void
5848 icon_destroy (NautilusCanvasContainer *container,
5849 NautilusCanvasIcon *icon)
5850 {
5851 NautilusCanvasContainerDetails *details;
5852 gboolean was_selected;
5853 NautilusCanvasIcon *icon_to_focus;
5854 GList *item;
5855
5856 details = container->details;
5857
5858 item = g_list_find (details->icons, icon);
5859 item = item->next ? item->next : item->prev;
5860 icon_to_focus = (item != NULL) ? item->data : NULL;
5861
5862 details->icons = g_list_remove (details->icons, icon);
5863 details->new_icons = g_list_remove (details->new_icons, icon);
5864 g_hash_table_remove (details->icon_set, icon->data);
5865
5866 was_selected = icon->is_selected;
5867
5868 if (details->keyboard_focus == icon ||
5869 details->keyboard_focus == NULL) {
5870 if (icon_to_focus != NULL) {
5871 set_keyboard_focus (container, icon_to_focus);
5872 } else {
5873 clear_keyboard_focus (container);
5874 }
5875 }
5876
5877 if (details->keyboard_rubberband_start == icon) {
5878 clear_keyboard_rubberband_start (container);
5879 }
5880
5881 if (details->keyboard_icon_to_reveal == icon) {
5882 unschedule_keyboard_icon_reveal (container);
5883 }
5884 if (details->drag_icon == icon) {
5885 clear_drag_state (container);
5886 }
5887 if (details->drop_target == icon) {
5888 details->drop_target = NULL;
5889 }
5890 if (details->range_selection_base_icon == icon) {
5891 details->range_selection_base_icon = NULL;
5892 }
5893 if (details->pending_icon_to_reveal == icon) {
5894 set_pending_icon_to_reveal (container, NULL);
5895 }
5896 if (details->stretch_icon == icon) {
5897 details->stretch_icon = NULL;
5898 }
5899
5900 if (icon->is_monitored) {
5901 nautilus_canvas_container_stop_monitor_top_left (container,
5902 icon->data,
5903 icon);
5904 }
5905 icon_free (icon);
5906
5907 if (was_selected) {
5908 /* Coalesce multiple removals causing multiple selection_changed events */
5909 details->selection_changed_id = g_idle_add (selection_changed_at_idle_callback, container);
5910 }
5911 }
5912
5913 /* activate any selected items in the container */
5914 static void
5915 activate_selected_items (NautilusCanvasContainer *container)
5916 {
5917 GList *selection;
5918
5919 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
5920
5921 selection = nautilus_canvas_container_get_selection (container);
5922 if (selection != NULL) {
5923 g_signal_emit (container,
5924 signals[ACTIVATE], 0,
5925 selection);
5926 }
5927 g_list_free (selection);
5928 }
5929
5930 static void
5931 preview_selected_items (NautilusCanvasContainer *container)
5932 {
5933 GList *selection;
5934 GArray *locations;
5935 gint idx;
5936
5937 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
5938
5939 selection = nautilus_canvas_container_get_selection (container);
5940 locations = nautilus_canvas_container_get_selected_icon_locations (container);
5941
5942 for (idx = 0; idx < locations->len; idx++) {
5943 GdkPoint *point = &(g_array_index (locations, GdkPoint, idx));
5944 gint scroll_x, scroll_y;
5945
5946 eel_canvas_get_scroll_offsets (EEL_CANVAS (container),
5947 &scroll_x, &scroll_y);
5948
5949 point->x -= scroll_x;
5950 point->y -= scroll_y;
5951 }
5952
5953 if (selection != NULL) {
5954 g_signal_emit (container,
5955 signals[ACTIVATE_PREVIEWER], 0,
5956 selection, locations);
5957 }
5958 g_list_free (selection);
5959 g_array_unref (locations);
5960 }
5961
5962 static void
5963 activate_selected_items_alternate (NautilusCanvasContainer *container,
5964 NautilusCanvasIcon *icon)
5965 {
5966 GList *selection;
5967
5968 g_assert (NAUTILUS_IS_CANVAS_CONTAINER (container));
5969
5970 if (icon != NULL) {
5971 selection = g_list_prepend (NULL, icon->data);
5972 } else {
5973 selection = nautilus_canvas_container_get_selection (container);
5974 }
5975 if (selection != NULL) {
5976 g_signal_emit (container,
5977 signals[ACTIVATE_ALTERNATE], 0,
5978 selection);
5979 }
5980 g_list_free (selection);
5981 }
5982
5983 static NautilusCanvasIcon *
5984 get_icon_being_renamed (NautilusCanvasContainer *container)
5985 {
5986 NautilusCanvasIcon *rename_icon;
5987
5988 if (!is_renaming (container)) {
5989 return NULL;
5990 }
5991
5992 g_assert (!has_multiple_selection (container));
5993
5994 rename_icon = get_first_selected_icon (container);
5995 g_assert (rename_icon != NULL);
5996
5997 return rename_icon;
5998 }
5999
6000 static NautilusIconInfo *
6001 nautilus_canvas_container_get_icon_images (NautilusCanvasContainer *container,
6002 NautilusCanvasIconData *data,
6003 int size,
6004 char **embedded_text,
6005 gboolean for_drag_accept,
6006 gboolean need_large_embeddded_text,
6007 gboolean *embedded_text_needs_loading,
6008 gboolean *has_open_window)
6009 {
6010 NautilusCanvasContainerClass *klass;
6011
6012 klass = NAUTILUS_CANVAS_CONTAINER_GET_CLASS (container);
6013 g_assert (klass->get_icon_images != NULL);
6014
6015 return klass->get_icon_images (container, data, size, embedded_text, for_drag_accept, need_large_embeddded_text, embedded_text_needs_loading, has_open_window);
6016 }
6017
6018 static void
6019 nautilus_canvas_container_freeze_updates (NautilusCanvasContainer *container)
6020 {
6021 NautilusCanvasContainerClass *klass;
6022
6023 klass = NAUTILUS_CANVAS_CONTAINER_GET_CLASS (container);
6024 g_assert (klass->freeze_updates != NULL);
6025
6026 klass->freeze_updates (container);
6027 }
6028
6029 static void
6030 nautilus_canvas_container_unfreeze_updates (NautilusCanvasContainer *container)
6031 {
6032 NautilusCanvasContainerClass *klass;
6033
6034 klass = NAUTILUS_CANVAS_CONTAINER_GET_CLASS (container);
6035 g_assert (klass->unfreeze_updates != NULL);
6036
6037 klass->unfreeze_updates (container);
6038 }
6039
6040 static void
6041 nautilus_canvas_container_start_monitor_top_left (NautilusCanvasContainer *container,
6042 NautilusCanvasIconData *data,
6043 gconstpointer client,
6044 gboolean large_text)
6045 {
6046 NautilusCanvasContainerClass *klass;
6047
6048 klass = NAUTILUS_CANVAS_CONTAINER_GET_CLASS (container);
6049 g_assert (klass->start_monitor_top_left != NULL);
6050
6051 klass->start_monitor_top_left (container, data, client, large_text);
6052 }
6053
6054 static void
6055 nautilus_canvas_container_stop_monitor_top_left (NautilusCanvasContainer *container,
6056 NautilusCanvasIconData *data,
6057 gconstpointer client)
6058 {
6059 NautilusCanvasContainerClass *klass;
6060
6061 klass = NAUTILUS_CANVAS_CONTAINER_GET_CLASS (container);
6062 g_return_if_fail (klass->stop_monitor_top_left != NULL);
6063
6064 klass->stop_monitor_top_left (container, data, client);
6065 }
6066
6067
6068 static void
6069 nautilus_canvas_container_prioritize_thumbnailing (NautilusCanvasContainer *container,
6070 NautilusCanvasIcon *icon)
6071 {
6072 NautilusCanvasContainerClass *klass;
6073
6074 klass = NAUTILUS_CANVAS_CONTAINER_GET_CLASS (container);
6075 g_assert (klass->prioritize_thumbnailing != NULL);
6076
6077 klass->prioritize_thumbnailing (container, icon->data);
6078 }
6079
6080 static void
6081 nautilus_canvas_container_update_visible_icons (NautilusCanvasContainer *container)
6082 {
6083 GtkAdjustment *vadj, *hadj;
6084 double min_y, max_y;
6085 double min_x, max_x;
6086 double x0, y0, x1, y1;
6087 GList *node;
6088 NautilusCanvasIcon *icon;
6089 gboolean visible;
6090 GtkAllocation allocation;
6091
6092 hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container));
6093 vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container));
6094 gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
6095
6096 min_x = gtk_adjustment_get_value (hadj);
6097 max_x = min_x + allocation.width;
6098
6099 min_y = gtk_adjustment_get_value (vadj);
6100 max_y = min_y + allocation.height;
6101
6102 eel_canvas_c2w (EEL_CANVAS (container),
6103 min_x, min_y, &min_x, &min_y);
6104 eel_canvas_c2w (EEL_CANVAS (container),
6105 max_x, max_y, &max_x, &max_y);
6106
6107 /* Do the iteration in reverse to get the render-order from top to
6108 * bottom for the prioritized thumbnails.
6109 */
6110 for (node = g_list_last (container->details->icons); node != NULL; node = node->prev) {
6111 icon = node->data;
6112
6113 if (icon_is_positioned (icon)) {
6114 eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (icon->item),
6115 &x0,
6116 &y0,
6117 &x1,
6118 &y1);
6119 eel_canvas_item_i2w (EEL_CANVAS_ITEM (icon->item)->parent,
6120 &x0,
6121 &y0);
6122 eel_canvas_item_i2w (EEL_CANVAS_ITEM (icon->item)->parent,
6123 &x1,
6124 &y1);
6125
6126 if (nautilus_canvas_container_is_layout_vertical (container)) {
6127 visible = x1 >= min_x && x0 <= max_x;
6128 } else {
6129 visible = y1 >= min_y && y0 <= max_y;
6130 }
6131
6132 if (visible) {
6133 nautilus_canvas_item_set_is_visible (icon->item, TRUE);
6134 nautilus_canvas_container_prioritize_thumbnailing (container,
6135 icon);
6136 } else {
6137 nautilus_canvas_item_set_is_visible (icon->item, FALSE);
6138 }
6139 }
6140 }
6141 }
6142
6143 static void
6144 handle_vadjustment_changed (GtkAdjustment *adjustment,
6145 NautilusCanvasContainer *container)
6146 {
6147 if (!nautilus_canvas_container_is_layout_vertical (container)) {
6148 nautilus_canvas_container_update_visible_icons (container);
6149 }
6150 }
6151
6152 static void
6153 handle_hadjustment_changed (GtkAdjustment *adjustment,
6154 NautilusCanvasContainer *container)
6155 {
6156 if (nautilus_canvas_container_is_layout_vertical (container)) {
6157 nautilus_canvas_container_update_visible_icons (container);
6158 }
6159 }
6160
6161
6162 void
6163 nautilus_canvas_container_update_icon (NautilusCanvasContainer *container,
6164 NautilusCanvasIcon *icon)
6165 {
6166 NautilusCanvasContainerDetails *details;
6167 guint icon_size;
6168 guint min_image_size, max_image_size;
6169 NautilusIconInfo *icon_info;
6170 GdkPoint *attach_points;
6171 int n_attach_points;
6172 gboolean has_embedded_text_rect;
6173 GdkPixbuf *pixbuf;
6174 char *editable_text, *additional_text;
6175 char *embedded_text;
6176 GdkRectangle embedded_text_rect;
6177 gboolean large_embedded_text;
6178 gboolean embedded_text_needs_loading;
6179 gboolean has_open_window;
6180
6181 if (icon == NULL) {
6182 return;
6183 }
6184
6185 details = container->details;
6186
6187 /* compute the maximum size based on the scale factor */
6188 min_image_size = MINIMUM_IMAGE_SIZE * EEL_CANVAS (container)->pixels_per_unit;
6189 max_image_size = MAX (MAXIMUM_IMAGE_SIZE * EEL_CANVAS (container)->pixels_per_unit, NAUTILUS_ICON_MAXIMUM_SIZE);
6190
6191 /* Get the appropriate images for the file. */
6192 icon_get_size (container, icon, &icon_size);
6193
6194 icon_size = MAX (icon_size, min_image_size);
6195 icon_size = MIN (icon_size, max_image_size);
6196
6197 DEBUG ("Icon size, getting for size %d", icon_size);
6198
6199 /* Get the icons. */
6200 embedded_text = NULL;
6201 large_embedded_text = icon_size > ICON_SIZE_FOR_LARGE_EMBEDDED_TEXT;
6202 icon_info = nautilus_canvas_container_get_icon_images (container, icon->data, icon_size,
6203 &embedded_text,
6204 icon == details->drop_target,
6205 large_embedded_text, &embedded_text_needs_loading,
6206 &has_open_window);
6207
6208 pixbuf = nautilus_icon_info_get_pixbuf (icon_info);
6209
6210 nautilus_icon_info_get_attach_points (icon_info, &attach_points, &n_attach_points);
6211 has_embedded_text_rect = nautilus_icon_info_get_embedded_rect (icon_info,
6212 &embedded_text_rect);
6213
6214 g_object_unref (icon_info);
6215
6216 if (has_embedded_text_rect && embedded_text_needs_loading) {
6217 icon->is_monitored = TRUE;
6218 nautilus_canvas_container_start_monitor_top_left (container, icon->data, icon, large_embedded_text);
6219 }
6220
6221 nautilus_canvas_container_get_icon_text (container,
6222 icon->data,
6223 &editable_text,
6224 &additional_text,
6225 FALSE);
6226
6227 /* If name of icon being renamed was changed from elsewhere, end renaming mode.
6228 * Alternatively, we could replace the characters in the editable text widget
6229 * with the new name, but that could cause timing problems if the user just
6230 * happened to be typing at that moment.
6231 */
6232 if (icon == get_icon_being_renamed (container) &&
6233 g_strcmp0 (editable_text,
6234 nautilus_canvas_item_get_editable_text (icon->item)) != 0) {
6235 end_renaming_mode (container, FALSE);
6236 }
6237
6238 eel_canvas_item_set (EEL_CANVAS_ITEM (icon->item),
6239 "editable_text", editable_text,
6240 "additional_text", additional_text,
6241 "highlighted_for_drop", icon == details->drop_target,
6242 NULL);
6243
6244 nautilus_canvas_item_set_image (icon->item, pixbuf);
6245 nautilus_canvas_item_set_attach_points (icon->item, attach_points, n_attach_points);
6246 nautilus_canvas_item_set_embedded_text_rect (icon->item, &embedded_text_rect);
6247 nautilus_canvas_item_set_embedded_text (icon->item, embedded_text);
6248
6249 /* Let the pixbufs go. */
6250 g_object_unref (pixbuf);
6251
6252 g_free (editable_text);
6253 g_free (additional_text);
6254 }
6255
6256 static gboolean
6257 assign_icon_position (NautilusCanvasContainer *container,
6258 NautilusCanvasIcon *icon)
6259 {
6260 gboolean have_stored_position;
6261 NautilusCanvasPosition position;
6262
6263 /* Get the stored position. */
6264 have_stored_position = FALSE;
6265 position.scale = 1.0;
6266 g_signal_emit (container,
6267 signals[GET_STORED_ICON_POSITION], 0,
6268 icon->data,
6269 &position,
6270 &have_stored_position);
6271 icon->scale = position.scale;
6272 if (!container->details->auto_layout) {
6273 if (have_stored_position) {
6274 icon_set_position (icon, position.x, position.y);
6275 icon->saved_ltr_x = icon->x;
6276 } else {
6277 return FALSE;
6278 }
6279 }
6280 return TRUE;
6281 }
6282
6283 static void
6284 finish_adding_icon (NautilusCanvasContainer *container,
6285 NautilusCanvasIcon *icon)
6286 {
6287 nautilus_canvas_container_update_icon (container, icon);
6288 eel_canvas_item_show (EEL_CANVAS_ITEM (icon->item));
6289
6290 g_signal_connect_object (icon->item, "event",
6291 G_CALLBACK (item_event_callback), container, 0);
6292
6293 g_signal_emit (container, signals[ICON_ADDED], 0, icon->data);
6294 }
6295
6296 static void
6297 finish_adding_new_icons (NautilusCanvasContainer *container)
6298 {
6299 GList *p, *new_icons, *no_position_icons, *semi_position_icons;
6300 NautilusCanvasIcon *icon;
6301 double bottom;
6302
6303 new_icons = container->details->new_icons;
6304 container->details->new_icons = NULL;
6305
6306 /* Position most icons (not unpositioned manual-layout icons). */
6307 new_icons = g_list_reverse (new_icons);
6308 no_position_icons = semi_position_icons = NULL;
6309 for (p = new_icons; p != NULL; p = p->next) {
6310 icon = p->data;
6311 if (icon->has_lazy_position) {
6312 assign_icon_position (container, icon);
6313 semi_position_icons = g_list_prepend (semi_position_icons, icon);
6314 } else if (!assign_icon_position (container, icon)) {
6315 no_position_icons = g_list_prepend (no_position_icons, icon);
6316 }
6317
6318 finish_adding_icon (container, icon);
6319 }
6320 g_list_free (new_icons);
6321
6322 if (semi_position_icons != NULL) {
6323 PlacementGrid *grid;
6324 time_t now;
6325 gboolean dummy;
6326
6327 g_assert (!container->details->auto_layout);
6328
6329 semi_position_icons = g_list_reverse (semi_position_icons);
6330
6331 /* This is currently only used on the desktop.
6332 * Thus, we pass FALSE for tight, like lay_down_icons_tblr */
6333 grid = placement_grid_new (container, FALSE);
6334
6335 for (p = container->details->icons; p != NULL; p = p->next) {
6336 icon = p->data;
6337
6338 if (icon_is_positioned (icon) && !icon->has_lazy_position) {
6339 placement_grid_mark_icon (grid, icon);
6340 }
6341 }
6342
6343 now = time (NULL);
6344
6345 for (p = semi_position_icons; p != NULL; p = p->next) {
6346 NautilusCanvasIcon *icon;
6347 NautilusCanvasPosition position;
6348 int x, y;
6349
6350 icon = p->data;
6351 x = icon->x;
6352 y = icon->y;
6353
6354 find_empty_location (container, grid,
6355 icon, x, y, &x, &y);
6356
6357 icon_set_position (icon, x, y);
6358
6359 position.x = icon->x;
6360 position.y = icon->y;
6361 position.scale = icon->scale;
6362 placement_grid_mark_icon (grid, icon);
6363 g_signal_emit (container, signals[ICON_POSITION_CHANGED], 0,
6364 icon->data, &position);
6365 g_signal_emit (container, signals[STORE_LAYOUT_TIMESTAMP], 0,
6366 icon->data, &now, &dummy);
6367
6368 /* ensure that next time we run this code, the formerly semi-positioned
6369 * icons are treated as being positioned. */
6370 icon->has_lazy_position = FALSE;
6371 }
6372
6373 placement_grid_free (grid);
6374
6375 g_list_free (semi_position_icons);
6376 }
6377
6378 /* Position the unpositioned manual layout icons. */
6379 if (no_position_icons != NULL) {
6380 g_assert (!container->details->auto_layout);
6381
6382 sort_icons (container, &no_position_icons);
6383 if (nautilus_canvas_container_get_is_desktop (container)) {
6384 lay_down_icons (container, no_position_icons, CONTAINER_PAD_TOP);
6385 } else {
6386 get_all_icon_bounds (container, NULL, NULL, NULL, &bottom, BOUNDS_USAGE_FOR_LAYOUT);
6387 lay_down_icons (container, no_position_icons, bottom + ICON_PAD_BOTTOM);
6388 }
6389 g_list_free (no_position_icons);
6390 }
6391
6392 if (container->details->store_layout_timestamps_when_finishing_new_icons) {
6393 store_layout_timestamps_now (container);
6394 container->details->store_layout_timestamps_when_finishing_new_icons = FALSE;
6395 }
6396 }
6397
6398 static gboolean
6399 is_old_or_unknown_icon_data (NautilusCanvasContainer *container,
6400 NautilusCanvasIconData *data)
6401 {
6402 time_t timestamp;
6403 gboolean success;
6404
6405 if (container->details->layout_timestamp == UNDEFINED_TIME) {
6406 /* don't know */
6407 return FALSE;
6408 }
6409
6410 g_signal_emit (container,
6411 signals[GET_STORED_LAYOUT_TIMESTAMP], 0,
6412 data, ×tamp, &success);
6413 return (!success || timestamp < container->details->layout_timestamp);
6414 }
6415
6416 /**
6417 * nautilus_canvas_container_add:
6418 * @container: A NautilusCanvasContainer
6419 * @data: Icon data.
6420 *
6421 * Add icon to represent @data to container.
6422 * Returns FALSE if there was already such an icon.
6423 **/
6424 gboolean
6425 nautilus_canvas_container_add (NautilusCanvasContainer *container,
6426 NautilusCanvasIconData *data)
6427 {
6428 NautilusCanvasContainerDetails *details;
6429 NautilusCanvasIcon *icon;
6430 EelCanvasItem *band, *item;
6431
6432 g_return_val_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container), FALSE);
6433 g_return_val_if_fail (data != NULL, FALSE);
6434
6435 details = container->details;
6436
6437 if (g_hash_table_lookup (details->icon_set, data) != NULL) {
6438 return FALSE;
6439 }
6440
6441 /* Create the new icon, including the canvas item. */
6442 icon = g_new0 (NautilusCanvasIcon, 1);
6443 icon->data = data;
6444 icon->x = ICON_UNPOSITIONED_VALUE;
6445 icon->y = ICON_UNPOSITIONED_VALUE;
6446
6447 /* Whether the saved icon position should only be used
6448 * if the previous icon position is free. If the position
6449 * is occupied, another position near the last one will
6450 */
6451 icon->has_lazy_position = is_old_or_unknown_icon_data (container, data);
6452 icon->scale = 1.0;
6453 icon->item = NAUTILUS_CANVAS_ITEM
6454 (eel_canvas_item_new (EEL_CANVAS_GROUP (EEL_CANVAS (container)->root),
6455 nautilus_canvas_item_get_type (),
6456 "visible", FALSE,
6457 NULL));
6458 icon->item->user_data = icon;
6459
6460 /* Make sure the icon is under the selection_rectangle */
6461 item = EEL_CANVAS_ITEM (icon->item);
6462 band = NAUTILUS_CANVAS_CONTAINER (item->canvas)->details->rubberband_info.selection_rectangle;
6463 if (band) {
6464 eel_canvas_item_send_behind (item, band);
6465 }
6466
6467 /* Put it on both lists. */
6468 details->icons = g_list_prepend (details->icons, icon);
6469 details->new_icons = g_list_prepend (details->new_icons, icon);
6470
6471 g_hash_table_insert (details->icon_set, data, icon);
6472
6473 details->needs_resort = TRUE;
6474
6475 /* Run an idle function to add the icons. */
6476 schedule_redo_layout (container);
6477
6478 return TRUE;
6479 }
6480
6481 void
6482 nautilus_canvas_container_layout_now (NautilusCanvasContainer *container)
6483 {
6484 if (container->details->idle_id != 0) {
6485 unschedule_redo_layout (container);
6486 redo_layout_internal (container);
6487 }
6488
6489 /* Also need to make sure we're properly resized, for instance
6490 * newly added files may trigger a change in the size allocation and
6491 * thus toggle scrollbars on */
6492 gtk_container_check_resize (GTK_CONTAINER (gtk_widget_get_parent (GTK_WIDGET (container))));
6493 }
6494
6495 /**
6496 * nautilus_canvas_container_remove:
6497 * @container: A NautilusCanvasContainer.
6498 * @data: Icon data.
6499 *
6500 * Remove the icon with this data.
6501 **/
6502 gboolean
6503 nautilus_canvas_container_remove (NautilusCanvasContainer *container,
6504 NautilusCanvasIconData *data)
6505 {
6506 NautilusCanvasIcon *icon;
6507
6508 g_return_val_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container), FALSE);
6509 g_return_val_if_fail (data != NULL, FALSE);
6510
6511 end_renaming_mode (container, FALSE);
6512
6513 icon = g_hash_table_lookup (container->details->icon_set, data);
6514
6515 if (icon == NULL) {
6516 return FALSE;
6517 }
6518
6519 icon_destroy (container, icon);
6520 schedule_redo_layout (container);
6521
6522 g_signal_emit (container, signals[ICON_REMOVED], 0, icon);
6523
6524 return TRUE;
6525 }
6526
6527 /**
6528 * nautilus_canvas_container_request_update:
6529 * @container: A NautilusCanvasContainer.
6530 * @data: Icon data.
6531 *
6532 * Update the icon with this data.
6533 **/
6534 void
6535 nautilus_canvas_container_request_update (NautilusCanvasContainer *container,
6536 NautilusCanvasIconData *data)
6537 {
6538 NautilusCanvasIcon *icon;
6539
6540 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
6541 g_return_if_fail (data != NULL);
6542
6543 icon = g_hash_table_lookup (container->details->icon_set, data);
6544
6545 if (icon != NULL) {
6546 nautilus_canvas_container_update_icon (container, icon);
6547 container->details->needs_resort = TRUE;
6548 schedule_redo_layout (container);
6549 }
6550 }
6551
6552 /* zooming */
6553
6554 NautilusZoomLevel
6555 nautilus_canvas_container_get_zoom_level (NautilusCanvasContainer *container)
6556 {
6557 return container->details->zoom_level;
6558 }
6559
6560 void
6561 nautilus_canvas_container_set_zoom_level (NautilusCanvasContainer *container, int new_level)
6562 {
6563 NautilusCanvasContainerDetails *details;
6564 int pinned_level;
6565 double pixels_per_unit;
6566
6567 details = container->details;
6568
6569 end_renaming_mode (container, TRUE);
6570
6571 pinned_level = new_level;
6572 if (pinned_level < NAUTILUS_ZOOM_LEVEL_SMALLEST) {
6573 pinned_level = NAUTILUS_ZOOM_LEVEL_SMALLEST;
6574 } else if (pinned_level > NAUTILUS_ZOOM_LEVEL_LARGEST) {
6575 pinned_level = NAUTILUS_ZOOM_LEVEL_LARGEST;
6576 }
6577
6578 if (pinned_level == details->zoom_level) {
6579 return;
6580 }
6581
6582 details->zoom_level = pinned_level;
6583
6584 pixels_per_unit = (double) nautilus_get_icon_size_for_zoom_level (pinned_level)
6585 / NAUTILUS_ICON_SIZE_STANDARD;
6586 eel_canvas_set_pixels_per_unit (EEL_CANVAS (container), pixels_per_unit);
6587
6588 invalidate_labels (container);
6589 nautilus_canvas_container_request_update_all (container);
6590 }
6591
6592 /**
6593 * nautilus_canvas_container_request_update_all:
6594 * For each icon, synchronizes the displayed information (image, text) with the
6595 * information from the model.
6596 *
6597 * @container: An canvas container.
6598 **/
6599 void
6600 nautilus_canvas_container_request_update_all (NautilusCanvasContainer *container)
6601 {
6602 GList *node;
6603 NautilusCanvasIcon *icon;
6604
6605 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
6606
6607 for (node = container->details->icons; node != NULL; node = node->next) {
6608 icon = node->data;
6609 nautilus_canvas_container_update_icon (container, icon);
6610 }
6611
6612 container->details->needs_resort = TRUE;
6613 redo_layout (container);
6614 }
6615
6616 /**
6617 * nautilus_canvas_container_reveal:
6618 * Change scroll position as necessary to reveal the specified item.
6619 */
6620 void
6621 nautilus_canvas_container_reveal (NautilusCanvasContainer *container, NautilusCanvasIconData *data)
6622 {
6623 NautilusCanvasIcon *icon;
6624
6625 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
6626 g_return_if_fail (data != NULL);
6627
6628 icon = g_hash_table_lookup (container->details->icon_set, data);
6629
6630 if (icon != NULL) {
6631 reveal_icon (container, icon);
6632 }
6633 }
6634
6635 /**
6636 * nautilus_canvas_container_get_selection:
6637 * @container: An canvas container.
6638 *
6639 * Get a list of the icons currently selected in @container.
6640 *
6641 * Return value: A GList of the programmer-specified data associated to each
6642 * selected icon, or NULL if no canvas is selected. The caller is expected to
6643 * free the list when it is not needed anymore.
6644 **/
6645 GList *
6646 nautilus_canvas_container_get_selection (NautilusCanvasContainer *container)
6647 {
6648 GList *list, *p;
6649
6650 g_return_val_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container), NULL);
6651
6652 list = NULL;
6653 for (p = container->details->icons; p != NULL; p = p->next) {
6654 NautilusCanvasIcon *icon;
6655
6656 icon = p->data;
6657 if (icon->is_selected) {
6658 list = g_list_prepend (list, icon->data);
6659 }
6660 }
6661
6662 return g_list_reverse (list);
6663 }
6664
6665 static GList *
6666 nautilus_canvas_container_get_selected_icons (NautilusCanvasContainer *container)
6667 {
6668 GList *list, *p;
6669
6670 g_return_val_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container), NULL);
6671
6672 list = NULL;
6673 for (p = container->details->icons; p != NULL; p = p->next) {
6674 NautilusCanvasIcon *icon;
6675
6676 icon = p->data;
6677 if (icon->is_selected) {
6678 list = g_list_prepend (list, icon);
6679 }
6680 }
6681
6682 return g_list_reverse (list);
6683 }
6684
6685 /**
6686 * nautilus_canvas_container_invert_selection:
6687 * @container: An canvas container.
6688 *
6689 * Inverts the selection in @container.
6690 *
6691 **/
6692 void
6693 nautilus_canvas_container_invert_selection (NautilusCanvasContainer *container)
6694 {
6695 GList *p;
6696
6697 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
6698
6699 for (p = container->details->icons; p != NULL; p = p->next) {
6700 NautilusCanvasIcon *icon;
6701
6702 icon = p->data;
6703 icon_toggle_selected (container, icon);
6704 }
6705
6706 g_signal_emit (container, signals[SELECTION_CHANGED], 0);
6707 }
6708
6709
6710 /* Returns an array of GdkPoints of locations of the icons. */
6711 static GArray *
6712 nautilus_canvas_container_get_icon_locations (NautilusCanvasContainer *container,
6713 GList *icons)
6714 {
6715 GArray *result;
6716 GList *node;
6717 int index;
6718
6719 result = g_array_new (FALSE, TRUE, sizeof (GdkPoint));
6720 result = g_array_set_size (result, g_list_length (icons));
6721
6722 for (index = 0, node = icons; node != NULL; index++, node = node->next) {
6723 g_array_index (result, GdkPoint, index).x =
6724 ((NautilusCanvasIcon *)node->data)->x;
6725 g_array_index (result, GdkPoint, index).y =
6726 ((NautilusCanvasIcon *)node->data)->y;
6727 }
6728
6729 return result;
6730 }
6731
6732 /**
6733 * nautilus_canvas_container_get_selected_icon_locations:
6734 * @container: An canvas container widget.
6735 *
6736 * Returns an array of GdkPoints of locations of the selected icons.
6737 **/
6738 GArray *
6739 nautilus_canvas_container_get_selected_icon_locations (NautilusCanvasContainer *container)
6740 {
6741 GArray *result;
6742 GList *icons;
6743
6744 g_return_val_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container), NULL);
6745
6746 icons = nautilus_canvas_container_get_selected_icons (container);
6747 result = nautilus_canvas_container_get_icon_locations (container, icons);
6748 g_list_free (icons);
6749
6750 return result;
6751 }
6752
6753 /**
6754 * nautilus_canvas_container_select_all:
6755 * @container: An canvas container widget.
6756 *
6757 * Select all the icons in @container at once.
6758 **/
6759 void
6760 nautilus_canvas_container_select_all (NautilusCanvasContainer *container)
6761 {
6762 gboolean selection_changed;
6763 GList *p;
6764 NautilusCanvasIcon *icon;
6765
6766 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
6767
6768 selection_changed = FALSE;
6769
6770 for (p = container->details->icons; p != NULL; p = p->next) {
6771 icon = p->data;
6772
6773 selection_changed |= icon_set_selected (container, icon, TRUE);
6774 }
6775
6776 if (selection_changed) {
6777 g_signal_emit (container,
6778 signals[SELECTION_CHANGED], 0);
6779 }
6780 }
6781
6782 /**
6783 * nautilus_canvas_container_select_first:
6784 * @container: An canvas container widget.
6785 *
6786 * Select the first icon in @container.
6787 **/
6788 void
6789 nautilus_canvas_container_select_first (NautilusCanvasContainer *container)
6790 {
6791 gboolean selection_changed;
6792 NautilusCanvasIcon *icon;
6793
6794 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
6795
6796 selection_changed = FALSE;
6797
6798 if (container->details->needs_resort) {
6799 resort (container);
6800 container->details->needs_resort = FALSE;
6801 }
6802
6803 icon = g_list_nth_data (container->details->icons, 0);
6804 if (icon) {
6805 selection_changed |= icon_set_selected (container, icon, TRUE);
6806 }
6807
6808 if (selection_changed) {
6809 g_signal_emit (container,
6810 signals[SELECTION_CHANGED], 0);
6811 }
6812 }
6813
6814 /**
6815 * nautilus_canvas_container_set_selection:
6816 * @container: An canvas container widget.
6817 * @selection: A list of NautilusCanvasIconData *.
6818 *
6819 * Set the selection to exactly the icons in @container which have
6820 * programmer data matching one of the items in @selection.
6821 **/
6822 void
6823 nautilus_canvas_container_set_selection (NautilusCanvasContainer *container,
6824 GList *selection)
6825 {
6826 gboolean selection_changed;
6827 GHashTable *hash;
6828 GList *p;
6829 gboolean res;
6830 NautilusCanvasIcon *icon, *selected_icon;
6831
6832 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
6833
6834 selection_changed = FALSE;
6835 selected_icon = NULL;
6836
6837 hash = g_hash_table_new (NULL, NULL);
6838 for (p = selection; p != NULL; p = p->next) {
6839 g_hash_table_insert (hash, p->data, p->data);
6840 }
6841 for (p = container->details->icons; p != NULL; p = p->next) {
6842 icon = p->data;
6843
6844 res = icon_set_selected
6845 (container, icon,
6846 g_hash_table_lookup (hash, icon->data) != NULL);
6847 selection_changed |= res;
6848
6849 if (res) {
6850 selected_icon = icon;
6851 }
6852 }
6853 g_hash_table_destroy (hash);
6854
6855 if (selection_changed) {
6856 /* if only one item has been selected, use it as range
6857 * selection base (cf. handle_canvas_button_press) */
6858 if (g_list_length (selection) == 1) {
6859 container->details->range_selection_base_icon = selected_icon;
6860 }
6861
6862 g_signal_emit (container,
6863 signals[SELECTION_CHANGED], 0);
6864 }
6865 }
6866
6867 /**
6868 * nautilus_canvas_container_select_list_unselect_others.
6869 * @container: An canvas container widget.
6870 * @selection: A list of NautilusCanvasIcon *.
6871 *
6872 * Set the selection to exactly the icons in @selection.
6873 **/
6874 void
6875 nautilus_canvas_container_select_list_unselect_others (NautilusCanvasContainer *container,
6876 GList *selection)
6877 {
6878 gboolean selection_changed;
6879 GHashTable *hash;
6880 GList *p;
6881 NautilusCanvasIcon *icon;
6882
6883 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
6884
6885 selection_changed = FALSE;
6886
6887 hash = g_hash_table_new (NULL, NULL);
6888 for (p = selection; p != NULL; p = p->next) {
6889 g_hash_table_insert (hash, p->data, p->data);
6890 }
6891 for (p = container->details->icons; p != NULL; p = p->next) {
6892 icon = p->data;
6893
6894 selection_changed |= icon_set_selected
6895 (container, icon,
6896 g_hash_table_lookup (hash, icon) != NULL);
6897 }
6898 g_hash_table_destroy (hash);
6899
6900 if (selection_changed) {
6901 g_signal_emit (container,
6902 signals[SELECTION_CHANGED], 0);
6903 }
6904 }
6905
6906 /**
6907 * nautilus_canvas_container_unselect_all:
6908 * @container: An canvas container widget.
6909 *
6910 * Deselect all the icons in @container.
6911 **/
6912 void
6913 nautilus_canvas_container_unselect_all (NautilusCanvasContainer *container)
6914 {
6915 if (unselect_all (container)) {
6916 g_signal_emit (container,
6917 signals[SELECTION_CHANGED], 0);
6918 }
6919 }
6920
6921 /**
6922 * nautilus_canvas_container_get_icon_by_uri:
6923 * @container: An canvas container widget.
6924 * @uri: The uri of an canvas to find.
6925 *
6926 * Locate an icon, given the URI. The URI must match exactly.
6927 * Later we may have to have some way of figuring out if the
6928 * URI specifies the same object that does not require an exact match.
6929 **/
6930 NautilusCanvasIcon *
6931 nautilus_canvas_container_get_icon_by_uri (NautilusCanvasContainer *container,
6932 const char *uri)
6933 {
6934 NautilusCanvasContainerDetails *details;
6935 GList *p;
6936
6937 /* Eventually, we must avoid searching the entire canvas list,
6938 but it's OK for now.
6939 A hash table mapping uri to canvas is one possibility.
6940 */
6941
6942 details = container->details;
6943
6944 for (p = details->icons; p != NULL; p = p->next) {
6945 NautilusCanvasIcon *icon;
6946 char *icon_uri;
6947 gboolean is_match;
6948
6949 icon = p->data;
6950
6951 icon_uri = nautilus_canvas_container_get_icon_uri
6952 (container, icon);
6953 is_match = strcmp (uri, icon_uri) == 0;
6954 g_free (icon_uri);
6955
6956 if (is_match) {
6957 return icon;
6958 }
6959 }
6960
6961 return NULL;
6962 }
6963
6964 static NautilusCanvasIcon *
6965 get_nth_selected_icon (NautilusCanvasContainer *container, int index)
6966 {
6967 GList *p;
6968 NautilusCanvasIcon *icon;
6969 int selection_count;
6970
6971 g_assert (index > 0);
6972
6973 /* Find the nth selected icon. */
6974 selection_count = 0;
6975 for (p = container->details->icons; p != NULL; p = p->next) {
6976 icon = p->data;
6977 if (icon->is_selected) {
6978 if (++selection_count == index) {
6979 return icon;
6980 }
6981 }
6982 }
6983 return NULL;
6984 }
6985
6986 static NautilusCanvasIcon *
6987 get_first_selected_icon (NautilusCanvasContainer *container)
6988 {
6989 return get_nth_selected_icon (container, 1);
6990 }
6991
6992 static gboolean
6993 has_multiple_selection (NautilusCanvasContainer *container)
6994 {
6995 return get_nth_selected_icon (container, 2) != NULL;
6996 }
6997
6998 static gboolean
6999 all_selected (NautilusCanvasContainer *container)
7000 {
7001 GList *p;
7002 NautilusCanvasIcon *icon;
7003
7004 for (p = container->details->icons; p != NULL; p = p->next) {
7005 icon = p->data;
7006 if (!icon->is_selected) {
7007 return FALSE;
7008 }
7009 }
7010 return TRUE;
7011 }
7012
7013 static gboolean
7014 has_selection (NautilusCanvasContainer *container)
7015 {
7016 return get_nth_selected_icon (container, 1) != NULL;
7017 }
7018
7019 /**
7020 * nautilus_canvas_container_show_stretch_handles:
7021 * @container: An canvas container widget.
7022 *
7023 * Makes stretch handles visible on the first selected icon.
7024 **/
7025 void
7026 nautilus_canvas_container_show_stretch_handles (NautilusCanvasContainer *container)
7027 {
7028 NautilusCanvasContainerDetails *details;
7029 NautilusCanvasIcon *icon;
7030 guint initial_size;
7031
7032 icon = get_first_selected_icon (container);
7033 if (icon == NULL) {
7034 return;
7035 }
7036
7037 /* Check if it already has stretch handles. */
7038 details = container->details;
7039 if (details->stretch_icon == icon) {
7040 return;
7041 }
7042
7043 /* Get rid of the existing stretch handles and put them on the new canvas. */
7044 if (details->stretch_icon != NULL) {
7045 nautilus_canvas_item_set_show_stretch_handles
7046 (details->stretch_icon->item, FALSE);
7047 ungrab_stretch_icon (container);
7048 emit_stretch_ended (container, details->stretch_icon);
7049 }
7050 nautilus_canvas_item_set_show_stretch_handles (icon->item, TRUE);
7051 details->stretch_icon = icon;
7052
7053 icon_get_size (container, icon, &initial_size);
7054
7055 /* only need to keep size in one dimension, since they are constrained to be the same */
7056 container->details->stretch_initial_x = icon->x;
7057 container->details->stretch_initial_y = icon->y;
7058 container->details->stretch_initial_size = initial_size;
7059
7060 emit_stretch_started (container, icon);
7061 }
7062
7063 /**
7064 * nautilus_canvas_container_has_stretch_handles
7065 * @container: An canvas container widget.
7066 *
7067 * Returns true if the first selected item has stretch handles.
7068 **/
7069 gboolean
7070 nautilus_canvas_container_has_stretch_handles (NautilusCanvasContainer *container)
7071 {
7072 NautilusCanvasIcon *icon;
7073
7074 icon = get_first_selected_icon (container);
7075 if (icon == NULL) {
7076 return FALSE;
7077 }
7078
7079 return icon == container->details->stretch_icon;
7080 }
7081
7082 /**
7083 * nautilus_canvas_container_is_stretched
7084 * @container: An canvas container widget.
7085 *
7086 * Returns true if the any selected item is stretched to a size other than 1.0.
7087 **/
7088 gboolean
7089 nautilus_canvas_container_is_stretched (NautilusCanvasContainer *container)
7090 {
7091 GList *p;
7092 NautilusCanvasIcon *icon;
7093
7094 for (p = container->details->icons; p != NULL; p = p->next) {
7095 icon = p->data;
7096 if (icon->is_selected && icon->scale != 1.0) {
7097 return TRUE;
7098 }
7099 }
7100 return FALSE;
7101 }
7102
7103 /**
7104 * nautilus_canvas_container_unstretch
7105 * @container: An canvas container widget.
7106 *
7107 * Gets rid of any canvas stretching.
7108 **/
7109 void
7110 nautilus_canvas_container_unstretch (NautilusCanvasContainer *container)
7111 {
7112 GList *p;
7113 NautilusCanvasIcon *icon;
7114
7115 for (p = container->details->icons; p != NULL; p = p->next) {
7116 icon = p->data;
7117 if (icon->is_selected) {
7118 nautilus_canvas_container_move_icon (container, icon,
7119 icon->x, icon->y,
7120 1.0,
7121 FALSE, TRUE, TRUE);
7122 }
7123 }
7124 }
7125
7126 static void
7127 compute_stretch (StretchState *start,
7128 StretchState *current)
7129 {
7130 gboolean right, bottom;
7131 int x_stretch, y_stretch;
7132
7133 /* FIXME bugzilla.gnome.org 45390: This doesn't correspond to
7134 * the way the handles are drawn.
7135 */
7136 /* Figure out which handle we are dragging. */
7137 right = start->pointer_x > start->icon_x + (int) start->icon_size / 2;
7138 bottom = start->pointer_y > start->icon_y + (int) start->icon_size / 2;
7139
7140 /* Figure out how big we should stretch. */
7141 x_stretch = start->pointer_x - current->pointer_x;
7142 y_stretch = start->pointer_y - current->pointer_y;
7143 if (right) {
7144 x_stretch = - x_stretch;
7145 }
7146 if (bottom) {
7147 y_stretch = - y_stretch;
7148 }
7149 current->icon_size = MAX ((int) start->icon_size + MIN (x_stretch, y_stretch),
7150 (int) NAUTILUS_ICON_SIZE_SMALLEST);
7151
7152 /* Figure out where the corner of the icon should be. */
7153 current->icon_x = start->icon_x;
7154 if (!right) {
7155 current->icon_x += start->icon_size - current->icon_size;
7156 }
7157 current->icon_y = start->icon_y;
7158 if (!bottom) {
7159 current->icon_y += start->icon_size - current->icon_size;
7160 }
7161 }
7162
7163 char *
7164 nautilus_canvas_container_get_icon_uri (NautilusCanvasContainer *container,
7165 NautilusCanvasIcon *icon)
7166 {
7167 char *uri;
7168
7169 uri = NULL;
7170 g_signal_emit (container,
7171 signals[GET_ICON_URI], 0,
7172 icon->data,
7173 &uri);
7174 return uri;
7175 }
7176
7177 char *
7178 nautilus_canvas_container_get_icon_drop_target_uri (NautilusCanvasContainer *container,
7179 NautilusCanvasIcon *icon)
7180 {
7181 char *uri;
7182
7183 uri = NULL;
7184 g_signal_emit (container,
7185 signals[GET_ICON_DROP_TARGET_URI], 0,
7186 icon->data,
7187 &uri);
7188 return uri;
7189 }
7190
7191 /* Call to reset the scroll region only if the container is not empty,
7192 * to avoid having the flag linger until the next file is added.
7193 */
7194 static void
7195 reset_scroll_region_if_not_empty (NautilusCanvasContainer *container)
7196 {
7197 if (!nautilus_canvas_container_is_empty (container)) {
7198 nautilus_canvas_container_reset_scroll_region (container);
7199 }
7200 }
7201
7202 /* Switch from automatic layout to manual or vice versa.
7203 * If we switch to manual layout, we restore the icon positions from the
7204 * last manual layout.
7205 */
7206 void
7207 nautilus_canvas_container_set_auto_layout (NautilusCanvasContainer *container,
7208 gboolean auto_layout)
7209 {
7210 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
7211 g_return_if_fail (auto_layout == FALSE || auto_layout == TRUE);
7212
7213 if (container->details->auto_layout == auto_layout) {
7214 return;
7215 }
7216
7217 reset_scroll_region_if_not_empty (container);
7218 container->details->auto_layout = auto_layout;
7219
7220 if (!auto_layout) {
7221 reload_icon_positions (container);
7222 nautilus_canvas_container_freeze_icon_positions (container);
7223 }
7224
7225 container->details->needs_resort = TRUE;
7226 redo_layout (container);
7227
7228 g_signal_emit (container, signals[LAYOUT_CHANGED], 0);
7229 }
7230
7231 gboolean
7232 nautilus_canvas_container_is_keep_aligned (NautilusCanvasContainer *container)
7233 {
7234 return container->details->keep_aligned;
7235 }
7236
7237 static gboolean
7238 align_icons_callback (gpointer callback_data)
7239 {
7240 NautilusCanvasContainer *container;
7241
7242 container = NAUTILUS_CANVAS_CONTAINER (callback_data);
7243 align_icons (container);
7244 container->details->align_idle_id = 0;
7245
7246 return FALSE;
7247 }
7248
7249 static void
7250 unschedule_align_icons (NautilusCanvasContainer *container)
7251 {
7252 if (container->details->align_idle_id != 0) {
7253 g_source_remove (container->details->align_idle_id);
7254 container->details->align_idle_id = 0;
7255 }
7256 }
7257
7258 static void
7259 schedule_align_icons (NautilusCanvasContainer *container)
7260 {
7261 if (container->details->align_idle_id == 0
7262 && container->details->has_been_allocated) {
7263 container->details->align_idle_id = g_idle_add
7264 (align_icons_callback, container);
7265 }
7266 }
7267
7268 void
7269 nautilus_canvas_container_set_keep_aligned (NautilusCanvasContainer *container,
7270 gboolean keep_aligned)
7271 {
7272 if (container->details->keep_aligned != keep_aligned) {
7273 container->details->keep_aligned = keep_aligned;
7274
7275 if (keep_aligned && !container->details->auto_layout) {
7276 schedule_align_icons (container);
7277 } else {
7278 unschedule_align_icons (container);
7279 }
7280 }
7281 }
7282
7283 void
7284 nautilus_canvas_container_set_layout_mode (NautilusCanvasContainer *container,
7285 NautilusCanvasLayoutMode mode)
7286 {
7287 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
7288
7289 container->details->layout_mode = mode;
7290 invalidate_labels (container);
7291
7292 container->details->needs_resort = TRUE;
7293 redo_layout (container);
7294
7295 g_signal_emit (container, signals[LAYOUT_CHANGED], 0);
7296 }
7297
7298 void
7299 nautilus_canvas_container_set_label_position (NautilusCanvasContainer *container,
7300 NautilusCanvasLabelPosition position)
7301 {
7302 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
7303
7304 if (container->details->label_position != position) {
7305 container->details->label_position = position;
7306
7307 invalidate_labels (container);
7308 nautilus_canvas_container_request_update_all (container);
7309
7310 schedule_redo_layout (container);
7311 }
7312 }
7313
7314 /* Switch from automatic to manual layout, freezing all the icons in their
7315 * current positions instead of restoring canvas positions from the last manual
7316 * layout as set_auto_layout does.
7317 */
7318 void
7319 nautilus_canvas_container_freeze_icon_positions (NautilusCanvasContainer *container)
7320 {
7321 gboolean changed;
7322 GList *p;
7323 NautilusCanvasIcon *icon;
7324 NautilusCanvasPosition position;
7325
7326 changed = container->details->auto_layout;
7327 container->details->auto_layout = FALSE;
7328
7329 for (p = container->details->icons; p != NULL; p = p->next) {
7330 icon = p->data;
7331
7332 position.x = icon->saved_ltr_x;
7333 position.y = icon->y;
7334 position.scale = icon->scale;
7335 g_signal_emit (container, signals[ICON_POSITION_CHANGED], 0,
7336 icon->data, &position);
7337 }
7338
7339 if (changed) {
7340 g_signal_emit (container, signals[LAYOUT_CHANGED], 0);
7341 }
7342 }
7343
7344 /* Re-sort, switching to automatic layout if it was in manual layout. */
7345 void
7346 nautilus_canvas_container_sort (NautilusCanvasContainer *container)
7347 {
7348 gboolean changed;
7349
7350 changed = !container->details->auto_layout;
7351 container->details->auto_layout = TRUE;
7352
7353 reset_scroll_region_if_not_empty (container);
7354 container->details->needs_resort = TRUE;
7355 redo_layout (container);
7356
7357 if (changed) {
7358 g_signal_emit (container, signals[LAYOUT_CHANGED], 0);
7359 }
7360 }
7361
7362 gboolean
7363 nautilus_canvas_container_is_auto_layout (NautilusCanvasContainer *container)
7364 {
7365 g_return_val_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container), FALSE);
7366
7367 return container->details->auto_layout;
7368 }
7369
7370 static void
7371 pending_icon_to_rename_destroy_callback (NautilusCanvasItem *item, NautilusCanvasContainer *container)
7372 {
7373 g_assert (container->details->pending_icon_to_rename != NULL);
7374 g_assert (container->details->pending_icon_to_rename->item == item);
7375 container->details->pending_icon_to_rename = NULL;
7376 }
7377
7378 static NautilusCanvasIcon *
7379 get_pending_icon_to_rename (NautilusCanvasContainer *container)
7380 {
7381 return container->details->pending_icon_to_rename;
7382 }
7383
7384 static void
7385 set_pending_icon_to_rename (NautilusCanvasContainer *container, NautilusCanvasIcon *icon)
7386 {
7387 NautilusCanvasIcon *old_icon;
7388
7389 old_icon = container->details->pending_icon_to_rename;
7390
7391 if (icon == old_icon) {
7392 return;
7393 }
7394
7395 if (old_icon != NULL) {
7396 g_signal_handlers_disconnect_by_func
7397 (old_icon->item,
7398 G_CALLBACK (pending_icon_to_rename_destroy_callback),
7399 container);
7400 }
7401
7402 if (icon != NULL) {
7403 g_signal_connect (icon->item, "destroy",
7404 G_CALLBACK (pending_icon_to_rename_destroy_callback), container);
7405 }
7406
7407 container->details->pending_icon_to_rename = icon;
7408 }
7409
7410 static void
7411 process_pending_icon_to_rename (NautilusCanvasContainer *container)
7412 {
7413 NautilusCanvasIcon *pending_icon_to_rename;
7414
7415 pending_icon_to_rename = get_pending_icon_to_rename (container);
7416
7417 if (pending_icon_to_rename != NULL) {
7418 if (pending_icon_to_rename->is_selected && !has_multiple_selection (container)) {
7419 nautilus_canvas_container_start_renaming_selected_item (container, FALSE);
7420 } else {
7421 set_pending_icon_to_rename (container, NULL);
7422 }
7423 }
7424 }
7425
7426 static gboolean
7427 is_renaming_pending (NautilusCanvasContainer *container)
7428 {
7429 return get_pending_icon_to_rename (container) != NULL;
7430 }
7431
7432 static gboolean
7433 is_renaming (NautilusCanvasContainer *container)
7434 {
7435 return container->details->renaming;
7436 }
7437
7438 /**
7439 * nautilus_canvas_container_start_renaming_selected_item
7440 * @container: An canvas container widget.
7441 * @select_all: Whether the whole file should initially be selected, or
7442 * only its basename (i.e. everything except its extension).
7443 *
7444 * Displays the edit name widget on the first selected icon
7445 **/
7446 void
7447 nautilus_canvas_container_start_renaming_selected_item (NautilusCanvasContainer *container,
7448 gboolean select_all)
7449 {
7450 NautilusCanvasContainerDetails *details;
7451 NautilusCanvasIcon *icon;
7452 EelDRect icon_rect;
7453 PangoContext *context;
7454 PangoFontDescription *desc;
7455 const char *editable_text;
7456 int x, y, width;
7457 int start_offset, end_offset;
7458
7459 /* Check if it already in renaming mode, if so - select all */
7460 details = container->details;
7461 if (details->renaming) {
7462 eel_editable_label_select_region (EEL_EDITABLE_LABEL (details->rename_widget),
7463 0,
7464 -1);
7465 return;
7466 }
7467
7468 /* Find selected icon */
7469 icon = get_first_selected_icon (container);
7470 if (icon == NULL) {
7471 return;
7472 }
7473
7474 g_assert (!has_multiple_selection (container));
7475
7476
7477 if (!icon_is_positioned (icon)) {
7478 set_pending_icon_to_rename (container, icon);
7479 return;
7480 }
7481
7482 set_pending_icon_to_rename (container, NULL);
7483
7484 /* Make a copy of the original editable text for a later compare */
7485 editable_text = nautilus_canvas_item_get_editable_text (icon->item);
7486
7487 /* This could conceivably be NULL if a rename was triggered really early. */
7488 if (editable_text == NULL) {
7489 return;
7490 }
7491
7492 details->original_text = g_strdup (editable_text);
7493
7494 /* Freeze updates so files added while renaming don't cause rename to loose focus, bug #318373 */
7495 nautilus_canvas_container_freeze_updates (container);
7496
7497 /* Create text renaming widget, if it hasn't been created already.
7498 * We deal with the broken canvas text item widget by keeping it around
7499 * so its contents can still be cut and pasted as part of the clipboard
7500 */
7501 if (details->rename_widget == NULL) {
7502 details->rename_widget = eel_editable_label_new ("Test text");
7503 eel_editable_label_set_line_wrap (EEL_EDITABLE_LABEL (details->rename_widget), TRUE);
7504 eel_editable_label_set_line_wrap_mode (EEL_EDITABLE_LABEL (details->rename_widget), PANGO_WRAP_WORD_CHAR);
7505 eel_editable_label_set_draw_outline (EEL_EDITABLE_LABEL (details->rename_widget), TRUE);
7506
7507 eel_editable_label_set_justify (EEL_EDITABLE_LABEL (details->rename_widget), GTK_JUSTIFY_CENTER);
7508
7509 gtk_misc_set_padding (GTK_MISC (details->rename_widget), 1, 1);
7510 gtk_layout_put (GTK_LAYOUT (container),
7511 details->rename_widget, 0, 0);
7512 }
7513
7514 /* Set the right font */
7515 if (details->font) {
7516 desc = pango_font_description_from_string (details->font);
7517 } else {
7518 context = gtk_widget_get_pango_context (GTK_WIDGET (container));
7519 desc = pango_font_description_copy (pango_context_get_font_description (context));
7520 }
7521 eel_editable_label_set_font_description (EEL_EDITABLE_LABEL (details->rename_widget),
7522 desc);
7523 pango_font_description_free (desc);
7524
7525 icon_rect = nautilus_canvas_item_get_icon_rectangle (icon->item);
7526
7527 width = nautilus_canvas_item_get_max_text_width (icon->item);
7528
7529 eel_canvas_w2c (EEL_CANVAS_ITEM (icon->item)->canvas,
7530 (icon_rect.x0 + icon_rect.x1) / 2,
7531 icon_rect.y1,
7532 &x, &y);
7533 x = x - width / 2 - 1;
7534
7535 gtk_layout_move (GTK_LAYOUT (container),
7536 details->rename_widget,
7537 x, y);
7538
7539 gtk_widget_set_size_request (details->rename_widget,
7540 width, -1);
7541 eel_editable_label_set_text (EEL_EDITABLE_LABEL (details->rename_widget),
7542 editable_text);
7543 if (select_all) {
7544 start_offset = 0;
7545 end_offset = -1;
7546 } else {
7547 eel_filename_get_rename_region (editable_text, &start_offset, &end_offset);
7548 }
7549
7550 gtk_widget_show (details->rename_widget);
7551 gtk_widget_grab_focus (details->rename_widget);
7552
7553 eel_editable_label_select_region (EEL_EDITABLE_LABEL (details->rename_widget),
7554 start_offset,
7555 end_offset);
7556
7557 g_signal_emit (container,
7558 signals[ICON_RENAME_STARTED], 0,
7559 GTK_EDITABLE (details->rename_widget));
7560
7561 nautilus_canvas_container_update_icon (container, icon);
7562
7563 /* We are in renaming mode */
7564 details->renaming = TRUE;
7565 nautilus_canvas_item_set_renaming (icon->item, TRUE);
7566 }
7567
7568 static void
7569 end_renaming_mode (NautilusCanvasContainer *container, gboolean commit)
7570 {
7571 NautilusCanvasIcon *icon;
7572 const char *changed_text = NULL;
7573
7574 set_pending_icon_to_rename (container, NULL);
7575
7576 icon = get_icon_being_renamed (container);
7577 if (icon == NULL) {
7578 return;
7579 }
7580
7581 /* We are not in renaming mode */
7582 container->details->renaming = FALSE;
7583 nautilus_canvas_item_set_renaming (icon->item, FALSE);
7584
7585 nautilus_canvas_container_unfreeze_updates (container);
7586
7587 if (commit) {
7588 set_pending_icon_to_reveal (container, icon);
7589 }
7590
7591 gtk_widget_grab_focus (GTK_WIDGET (container));
7592
7593 if (commit) {
7594 /* Verify that text has been modified before signalling change. */
7595 changed_text = eel_editable_label_get_text (EEL_EDITABLE_LABEL (container->details->rename_widget));
7596 if (strcmp (container->details->original_text, changed_text) == 0) {
7597 changed_text = NULL;
7598 }
7599 }
7600
7601 g_signal_emit (container,
7602 signals[ICON_RENAME_ENDED], 0,
7603 icon->data, changed_text);
7604
7605 gtk_widget_hide (container->details->rename_widget);
7606 g_free (container->details->original_text);
7607 }
7608
7609 gboolean
7610 nautilus_canvas_container_has_stored_icon_positions (NautilusCanvasContainer *container)
7611 {
7612 GList *p;
7613 NautilusCanvasIcon *icon;
7614 gboolean have_stored_position;
7615 NautilusCanvasPosition position;
7616
7617 for (p = container->details->icons; p != NULL; p = p->next) {
7618 icon = p->data;
7619
7620 have_stored_position = FALSE;
7621 g_signal_emit (container,
7622 signals[GET_STORED_ICON_POSITION], 0,
7623 icon->data,
7624 &position,
7625 &have_stored_position);
7626 if (have_stored_position) {
7627 return TRUE;
7628 }
7629 }
7630 return FALSE;
7631 }
7632
7633 void
7634 nautilus_canvas_container_set_single_click_mode (NautilusCanvasContainer *container,
7635 gboolean single_click_mode)
7636 {
7637 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
7638
7639 container->details->single_click_mode = single_click_mode;
7640 }
7641
7642 /* Return if the canvas container is a fixed size */
7643 gboolean
7644 nautilus_canvas_container_get_is_fixed_size (NautilusCanvasContainer *container)
7645 {
7646 g_return_val_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container), FALSE);
7647
7648 return container->details->is_fixed_size;
7649 }
7650
7651 /* Set the canvas container to be a fixed size */
7652 void
7653 nautilus_canvas_container_set_is_fixed_size (NautilusCanvasContainer *container,
7654 gboolean is_fixed_size)
7655 {
7656 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
7657
7658 container->details->is_fixed_size = is_fixed_size;
7659 }
7660
7661 gboolean
7662 nautilus_canvas_container_get_is_desktop (NautilusCanvasContainer *container)
7663 {
7664 g_return_val_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container), FALSE);
7665
7666 return container->details->is_desktop;
7667 }
7668
7669 void
7670 nautilus_canvas_container_set_is_desktop (NautilusCanvasContainer *container,
7671 gboolean is_desktop)
7672 {
7673 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
7674
7675 container->details->is_desktop = is_desktop;
7676
7677 if (is_desktop) {
7678 GtkStyleContext *context;
7679
7680 context = gtk_widget_get_style_context (GTK_WIDGET (container));
7681 gtk_style_context_add_class (context, "nautilus-desktop");
7682 }
7683 }
7684
7685 void
7686 nautilus_canvas_container_set_margins (NautilusCanvasContainer *container,
7687 int left_margin,
7688 int right_margin,
7689 int top_margin,
7690 int bottom_margin)
7691 {
7692 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
7693
7694 container->details->left_margin = left_margin;
7695 container->details->right_margin = right_margin;
7696 container->details->top_margin = top_margin;
7697 container->details->bottom_margin = bottom_margin;
7698
7699 /* redo layout of icons as the margins have changed */
7700 schedule_redo_layout (container);
7701 }
7702
7703 void
7704 nautilus_canvas_container_set_use_drop_shadows (NautilusCanvasContainer *container,
7705 gboolean use_drop_shadows)
7706 {
7707 if (container->details->drop_shadows_requested == use_drop_shadows) {
7708 return;
7709 }
7710
7711 container->details->drop_shadows_requested = use_drop_shadows;
7712 container->details->use_drop_shadows = use_drop_shadows;
7713 gtk_widget_queue_draw (GTK_WIDGET (container));
7714 }
7715
7716 /* handle theme changes */
7717
7718 void
7719 nautilus_canvas_container_set_font (NautilusCanvasContainer *container,
7720 const char *font)
7721 {
7722 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
7723
7724 if (g_strcmp0 (container->details->font, font) == 0) {
7725 return;
7726 }
7727
7728 g_free (container->details->font);
7729 container->details->font = g_strdup (font);
7730
7731 invalidate_labels (container);
7732 nautilus_canvas_container_request_update_all (container);
7733 gtk_widget_queue_draw (GTK_WIDGET (container));
7734 }
7735
7736 /**
7737 * nautilus_canvas_container_get_icon_description
7738 * @container: An canvas container widget.
7739 * @data: Icon data
7740 *
7741 * Gets the description for the icon. This function may return NULL.
7742 **/
7743 char*
7744 nautilus_canvas_container_get_icon_description (NautilusCanvasContainer *container,
7745 NautilusCanvasIconData *data)
7746 {
7747 NautilusCanvasContainerClass *klass;
7748
7749 klass = NAUTILUS_CANVAS_CONTAINER_GET_CLASS (container);
7750
7751 if (klass->get_icon_description) {
7752 return klass->get_icon_description (container, data);
7753 } else {
7754 return NULL;
7755 }
7756 }
7757
7758 gboolean
7759 nautilus_canvas_container_get_allow_moves (NautilusCanvasContainer *container)
7760 {
7761 g_return_val_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container), FALSE);
7762
7763 return container->details->drag_allow_moves;
7764 }
7765
7766 void
7767 nautilus_canvas_container_set_allow_moves (NautilusCanvasContainer *container,
7768 gboolean allow_moves)
7769 {
7770 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
7771
7772 container->details->drag_allow_moves = allow_moves;
7773 }
7774
7775 /**
7776 * nautilus_canvas_container_set_highlighted_for_clipboard
7777 * @container: An canvas container widget.
7778 * @data: Canvas Data associated with all icons that should be highlighted.
7779 * Others will be unhighlighted.
7780 **/
7781 void
7782 nautilus_canvas_container_set_highlighted_for_clipboard (NautilusCanvasContainer *container,
7783 GList *clipboard_canvas_data)
7784 {
7785 GList *l;
7786 NautilusCanvasIcon *icon;
7787 gboolean highlighted_for_clipboard;
7788
7789 g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container));
7790
7791 for (l = container->details->icons; l != NULL; l = l->next) {
7792 icon = l->data;
7793 highlighted_for_clipboard = (g_list_find (clipboard_canvas_data, icon->data) != NULL);
7794
7795 eel_canvas_item_set (EEL_CANVAS_ITEM (icon->item),
7796 "highlighted-for-clipboard", highlighted_for_clipboard,
7797 NULL);
7798 }
7799
7800 }
7801
7802 /* NautilusCanvasContainerAccessible */
7803 typedef struct {
7804 EelCanvasAccessible parent;
7805 NautilusCanvasContainerAccessiblePrivate *priv;
7806 } NautilusCanvasContainerAccessible;
7807
7808 typedef EelCanvasAccessibleClass NautilusCanvasContainerAccessibleClass;
7809
7810 #define GET_ACCESSIBLE_PRIV(o) ((NautilusCanvasContainerAccessible *) o)->priv
7811
7812 /* AtkAction interface */
7813 static gboolean
7814 nautilus_canvas_container_accessible_do_action (AtkAction *accessible, int i)
7815 {
7816 GtkWidget *widget;
7817 NautilusCanvasContainer *container;
7818 GList *selection;
7819
7820 g_return_val_if_fail (i < LAST_ACTION, FALSE);
7821
7822 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
7823 if (!widget) {
7824 return FALSE;
7825 }
7826
7827 container = NAUTILUS_CANVAS_CONTAINER (widget);
7828 switch (i) {
7829 case ACTION_ACTIVATE :
7830 selection = nautilus_canvas_container_get_selection (container);
7831
7832 if (selection) {
7833 g_signal_emit_by_name (container, "activate", selection);
7834 g_list_free (selection);
7835 }
7836 break;
7837 case ACTION_MENU :
7838 handle_popups (container, NULL,"context_click_background");
7839 break;
7840 default :
7841 g_warning ("Invalid action passed to NautilusCanvasContainerAccessible::do_action");
7842 return FALSE;
7843 }
7844 return TRUE;
7845 }
7846
7847 static int
7848 nautilus_canvas_container_accessible_get_n_actions (AtkAction *accessible)
7849 {
7850 return LAST_ACTION;
7851 }
7852
7853 static const char *
7854 nautilus_canvas_container_accessible_action_get_description (AtkAction *accessible,
7855 int i)
7856 {
7857 NautilusCanvasContainerAccessiblePrivate *priv;
7858
7859 g_assert (i < LAST_ACTION);
7860
7861 priv = GET_ACCESSIBLE_PRIV (accessible);
7862
7863 if (priv->action_descriptions[i]) {
7864 return priv->action_descriptions[i];
7865 } else {
7866 return nautilus_canvas_container_accessible_action_descriptions[i];
7867 }
7868 }
7869
7870 static const char *
7871 nautilus_canvas_container_accessible_action_get_name (AtkAction *accessible, int i)
7872 {
7873 g_assert (i < LAST_ACTION);
7874
7875 return nautilus_canvas_container_accessible_action_names[i];
7876 }
7877
7878 static const char *
7879 nautilus_canvas_container_accessible_action_get_keybinding (AtkAction *accessible,
7880 int i)
7881 {
7882 g_assert (i < LAST_ACTION);
7883
7884 return NULL;
7885 }
7886
7887 static gboolean
7888 nautilus_canvas_container_accessible_action_set_description (AtkAction *accessible,
7889 int i,
7890 const char *description)
7891 {
7892 NautilusCanvasContainerAccessiblePrivate *priv;
7893
7894 g_assert (i < LAST_ACTION);
7895
7896 priv = GET_ACCESSIBLE_PRIV (accessible);
7897
7898 if (priv->action_descriptions[i]) {
7899 g_free (priv->action_descriptions[i]);
7900 }
7901 priv->action_descriptions[i] = g_strdup (description);
7902
7903 return FALSE;
7904 }
7905
7906 static void
7907 nautilus_canvas_container_accessible_action_interface_init (AtkActionIface *iface)
7908 {
7909 iface->do_action = nautilus_canvas_container_accessible_do_action;
7910 iface->get_n_actions = nautilus_canvas_container_accessible_get_n_actions;
7911 iface->get_description = nautilus_canvas_container_accessible_action_get_description;
7912 iface->get_name = nautilus_canvas_container_accessible_action_get_name;
7913 iface->get_keybinding = nautilus_canvas_container_accessible_action_get_keybinding;
7914 iface->set_description = nautilus_canvas_container_accessible_action_set_description;
7915 }
7916
7917 /* AtkSelection interface */
7918
7919 static void
7920 nautilus_canvas_container_accessible_update_selection (AtkObject *accessible)
7921 {
7922 NautilusCanvasContainer *container;
7923 NautilusCanvasContainerAccessiblePrivate *priv;
7924 GList *l;
7925 NautilusCanvasIcon *icon;
7926
7927 container = NAUTILUS_CANVAS_CONTAINER (gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible)));
7928 priv = GET_ACCESSIBLE_PRIV (accessible);
7929
7930 if (priv->selection) {
7931 g_list_free (priv->selection);
7932 priv->selection = NULL;
7933 }
7934
7935 for (l = container->details->icons; l != NULL; l = l->next) {
7936 icon = l->data;
7937 if (icon->is_selected) {
7938 priv->selection = g_list_prepend (priv->selection,
7939 icon);
7940 }
7941 }
7942
7943 priv->selection = g_list_reverse (priv->selection);
7944 }
7945
7946 static void
7947 nautilus_canvas_container_accessible_selection_changed_cb (NautilusCanvasContainer *container,
7948 gpointer data)
7949 {
7950 g_signal_emit_by_name (data, "selection_changed");
7951 }
7952
7953 static void
7954 nautilus_canvas_container_accessible_icon_added_cb (NautilusCanvasContainer *container,
7955 NautilusCanvasIconData *icon_data,
7956 gpointer data)
7957 {
7958 NautilusCanvasIcon *icon;
7959 AtkObject *atk_parent;
7960 AtkObject *atk_child;
7961 int index;
7962
7963 icon = g_hash_table_lookup (container->details->icon_set, icon_data);
7964 if (icon) {
7965 atk_parent = ATK_OBJECT (data);
7966 atk_child = atk_gobject_accessible_for_object
7967 (G_OBJECT (icon->item));
7968 index = g_list_index (container->details->icons, icon);
7969
7970 g_signal_emit_by_name (atk_parent, "children_changed::add",
7971 index, atk_child, NULL);
7972 }
7973 }
7974
7975 static void
7976 nautilus_canvas_container_accessible_icon_removed_cb (NautilusCanvasContainer *container,
7977 NautilusCanvasIconData *icon_data,
7978 gpointer data)
7979 {
7980 NautilusCanvasIcon *icon;
7981 AtkObject *atk_parent;
7982 AtkObject *atk_child;
7983 int index;
7984
7985 icon = g_hash_table_lookup (container->details->icon_set, icon_data);
7986 if (icon) {
7987 atk_parent = ATK_OBJECT (data);
7988 atk_child = atk_gobject_accessible_for_object
7989 (G_OBJECT (icon->item));
7990 index = g_list_index (container->details->icons, icon);
7991
7992 g_signal_emit_by_name (atk_parent, "children_changed::remove",
7993 index, atk_child, NULL);
7994 }
7995 }
7996
7997 static void
7998 nautilus_canvas_container_accessible_cleared_cb (NautilusCanvasContainer *container,
7999 gpointer data)
8000 {
8001 g_signal_emit_by_name (data, "children_changed", 0, NULL, NULL);
8002 }
8003
8004 static gboolean
8005 nautilus_canvas_container_accessible_add_selection (AtkSelection *accessible,
8006 int i)
8007 {
8008 GtkWidget *widget;
8009 NautilusCanvasContainer *container;
8010 GList *l;
8011 GList *selection;
8012 NautilusCanvasIcon *icon;
8013
8014 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
8015 if (!widget) {
8016 return FALSE;
8017 }
8018
8019 container = NAUTILUS_CANVAS_CONTAINER (widget);
8020
8021 l = g_list_nth (container->details->icons, i);
8022 if (l) {
8023 icon = l->data;
8024
8025 selection = nautilus_canvas_container_get_selection (container);
8026 selection = g_list_prepend (selection,
8027 icon->data);
8028 nautilus_canvas_container_set_selection (container, selection);
8029
8030 g_list_free (selection);
8031 return TRUE;
8032 }
8033
8034 return FALSE;
8035 }
8036
8037 static gboolean
8038 nautilus_canvas_container_accessible_clear_selection (AtkSelection *accessible)
8039 {
8040 GtkWidget *widget;
8041 NautilusCanvasContainer *container;
8042
8043 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
8044 if (!widget) {
8045 return FALSE;
8046 }
8047
8048 container = NAUTILUS_CANVAS_CONTAINER (widget);
8049
8050 nautilus_canvas_container_unselect_all (container);
8051
8052 return TRUE;
8053 }
8054
8055 static AtkObject *
8056 nautilus_canvas_container_accessible_ref_selection (AtkSelection *accessible,
8057 int i)
8058 {
8059 NautilusCanvasContainerAccessiblePrivate *priv;
8060 AtkObject *atk_object;
8061 GList *item;
8062 NautilusCanvasIcon *icon;
8063
8064 nautilus_canvas_container_accessible_update_selection (ATK_OBJECT (accessible));
8065 priv = GET_ACCESSIBLE_PRIV (accessible);
8066
8067 item = (g_list_nth (priv->selection, i));
8068
8069 if (item) {
8070 icon = item->data;
8071 atk_object = atk_gobject_accessible_for_object (G_OBJECT (icon->item));
8072 if (atk_object) {
8073 g_object_ref (atk_object);
8074 }
8075
8076 return atk_object;
8077 } else {
8078 return NULL;
8079 }
8080 }
8081
8082 static int
8083 nautilus_canvas_container_accessible_get_selection_count (AtkSelection *accessible)
8084 {
8085 NautilusCanvasContainerAccessiblePrivate *priv;
8086 int count;
8087
8088 priv = GET_ACCESSIBLE_PRIV (accessible);
8089 nautilus_canvas_container_accessible_update_selection (ATK_OBJECT (accessible));
8090 count = g_list_length (priv->selection);
8091
8092 return count;
8093 }
8094
8095 static gboolean
8096 nautilus_canvas_container_accessible_is_child_selected (AtkSelection *accessible,
8097 int i)
8098 {
8099 NautilusCanvasContainer *container;
8100 GList *l;
8101 NautilusCanvasIcon *icon;
8102 GtkWidget *widget;
8103
8104 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
8105 if (!widget) {
8106 return FALSE;
8107 }
8108
8109 container = NAUTILUS_CANVAS_CONTAINER (widget);
8110
8111 l = g_list_nth (container->details->icons, i);
8112 if (l) {
8113 icon = l->data;
8114 return icon->is_selected;
8115 }
8116 return FALSE;
8117 }
8118
8119 static gboolean
8120 nautilus_canvas_container_accessible_remove_selection (AtkSelection *accessible,
8121 int i)
8122 {
8123 NautilusCanvasContainerAccessiblePrivate *priv;
8124 NautilusCanvasContainer *container;
8125 GList *l;
8126 GList *selection;
8127 NautilusCanvasIcon *icon;
8128 GtkWidget *widget;
8129
8130 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
8131 if (!widget) {
8132 return FALSE;
8133 }
8134
8135 container = NAUTILUS_CANVAS_CONTAINER (widget);
8136 nautilus_canvas_container_accessible_update_selection (ATK_OBJECT (accessible));
8137
8138 priv = GET_ACCESSIBLE_PRIV (accessible);
8139 l = g_list_nth (priv->selection, i);
8140 if (l) {
8141 icon = l->data;
8142
8143 selection = nautilus_canvas_container_get_selection (container);
8144 selection = g_list_remove (selection, icon->data);
8145 nautilus_canvas_container_set_selection (container, selection);
8146
8147 g_list_free (selection);
8148 return TRUE;
8149 }
8150
8151 return FALSE;
8152 }
8153
8154 static gboolean
8155 nautilus_canvas_container_accessible_select_all_selection (AtkSelection *accessible)
8156 {
8157 NautilusCanvasContainer *container;
8158 GtkWidget *widget;
8159
8160 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
8161 if (!widget) {
8162 return FALSE;
8163 }
8164
8165 container = NAUTILUS_CANVAS_CONTAINER (widget);
8166
8167 nautilus_canvas_container_select_all (container);
8168
8169 return TRUE;
8170 }
8171
8172 void
8173 nautilus_canvas_container_widget_to_file_operation_position (NautilusCanvasContainer *container,
8174 GdkPoint *position)
8175 {
8176 double x, y;
8177
8178 g_return_if_fail (position != NULL);
8179
8180 x = position->x;
8181 y = position->y;
8182
8183 eel_canvas_window_to_world (EEL_CANVAS (container), x, y, &x, &y);
8184
8185 position->x = (int) x;
8186 position->y = (int) y;
8187
8188 /* ensure that we end up in the middle of the icon */
8189 position->x -= nautilus_get_icon_size_for_zoom_level (container->details->zoom_level) / 2;
8190 position->y -= nautilus_get_icon_size_for_zoom_level (container->details->zoom_level) / 2;
8191 }
8192
8193 static void
8194 nautilus_canvas_container_accessible_selection_interface_init (AtkSelectionIface *iface)
8195 {
8196 iface->add_selection = nautilus_canvas_container_accessible_add_selection;
8197 iface->clear_selection = nautilus_canvas_container_accessible_clear_selection;
8198 iface->ref_selection = nautilus_canvas_container_accessible_ref_selection;
8199 iface->get_selection_count = nautilus_canvas_container_accessible_get_selection_count;
8200 iface->is_child_selected = nautilus_canvas_container_accessible_is_child_selected;
8201 iface->remove_selection = nautilus_canvas_container_accessible_remove_selection;
8202 iface->select_all_selection = nautilus_canvas_container_accessible_select_all_selection;
8203 }
8204
8205
8206 static gint
8207 nautilus_canvas_container_accessible_get_n_children (AtkObject *accessible)
8208 {
8209 NautilusCanvasContainer *container;
8210 GtkWidget *widget;
8211 gint i;
8212
8213 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
8214 if (!widget) {
8215 return FALSE;
8216 }
8217
8218 container = NAUTILUS_CANVAS_CONTAINER (widget);
8219
8220 i = g_hash_table_size (container->details->icon_set);
8221 if (container->details->rename_widget) {
8222 i++;
8223 }
8224
8225 return i;
8226 }
8227
8228 static AtkObject*
8229 nautilus_canvas_container_accessible_ref_child (AtkObject *accessible, int i)
8230 {
8231 AtkObject *atk_object;
8232 NautilusCanvasContainer *container;
8233 GList *item;
8234 NautilusCanvasIcon *icon;
8235 GtkWidget *widget;
8236
8237 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
8238 if (!widget) {
8239 return NULL;
8240 }
8241
8242 container = NAUTILUS_CANVAS_CONTAINER (widget);
8243
8244 item = (g_list_nth (container->details->icons, i));
8245
8246 if (item) {
8247 icon = item->data;
8248
8249 atk_object = atk_gobject_accessible_for_object (G_OBJECT (icon->item));
8250 g_object_ref (atk_object);
8251
8252 return atk_object;
8253 } else {
8254 if (i == g_list_length (container->details->icons)) {
8255 if (container->details->rename_widget) {
8256 atk_object = gtk_widget_get_accessible (container->details->rename_widget);
8257 g_object_ref (atk_object);
8258
8259 return atk_object;
8260 }
8261 }
8262 return NULL;
8263 }
8264 }
8265
8266 static GType nautilus_canvas_container_accessible_get_type (void);
8267
8268 G_DEFINE_TYPE_WITH_CODE (NautilusCanvasContainerAccessible, nautilus_canvas_container_accessible,
8269 eel_canvas_accessible_get_type (),
8270 G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, nautilus_canvas_container_accessible_action_interface_init)
8271 G_IMPLEMENT_INTERFACE (ATK_TYPE_SELECTION, nautilus_canvas_container_accessible_selection_interface_init))
8272
8273 static void
8274 nautilus_canvas_container_accessible_initialize (AtkObject *accessible,
8275 gpointer data)
8276 {
8277 NautilusCanvasContainer *container;
8278
8279 if (ATK_OBJECT_CLASS (nautilus_canvas_container_accessible_parent_class)->initialize) {
8280 ATK_OBJECT_CLASS (nautilus_canvas_container_accessible_parent_class)->initialize (accessible, data);
8281 }
8282
8283 if (GTK_IS_ACCESSIBLE (accessible)) {
8284 nautilus_canvas_container_accessible_update_selection
8285 (ATK_OBJECT (accessible));
8286
8287 container = NAUTILUS_CANVAS_CONTAINER (gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible)));
8288 g_signal_connect (container, "selection_changed",
8289 G_CALLBACK (nautilus_canvas_container_accessible_selection_changed_cb),
8290 accessible);
8291 g_signal_connect (container, "icon_added",
8292 G_CALLBACK (nautilus_canvas_container_accessible_icon_added_cb),
8293 accessible);
8294 g_signal_connect (container, "icon_removed",
8295 G_CALLBACK (nautilus_canvas_container_accessible_icon_removed_cb),
8296 accessible);
8297 g_signal_connect (container, "cleared",
8298 G_CALLBACK (nautilus_canvas_container_accessible_cleared_cb),
8299 accessible);
8300 }
8301 }
8302
8303 static void
8304 nautilus_canvas_container_accessible_finalize (GObject *object)
8305 {
8306 NautilusCanvasContainerAccessiblePrivate *priv;
8307 int i;
8308
8309 priv = GET_ACCESSIBLE_PRIV (object);
8310
8311 if (priv->selection) {
8312 g_list_free (priv->selection);
8313 }
8314
8315 for (i = 0; i < LAST_ACTION; i++) {
8316 if (priv->action_descriptions[i]) {
8317 g_free (priv->action_descriptions[i]);
8318 }
8319 }
8320
8321 G_OBJECT_CLASS (nautilus_canvas_container_accessible_parent_class)->finalize (object);
8322 }
8323
8324 static void
8325 nautilus_canvas_container_accessible_init (NautilusCanvasContainerAccessible *self)
8326 {
8327 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, nautilus_canvas_container_accessible_get_type (),
8328 NautilusCanvasContainerAccessiblePrivate);
8329 }
8330
8331 static void
8332 nautilus_canvas_container_accessible_class_init (NautilusCanvasContainerAccessibleClass *klass)
8333 {
8334 AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
8335 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
8336
8337 gobject_class->finalize = nautilus_canvas_container_accessible_finalize;
8338
8339 atk_class->get_n_children = nautilus_canvas_container_accessible_get_n_children;
8340 atk_class->ref_child = nautilus_canvas_container_accessible_ref_child;
8341 atk_class->initialize = nautilus_canvas_container_accessible_initialize;
8342
8343 g_type_class_add_private (klass, sizeof (NautilusCanvasContainerAccessiblePrivate));
8344 }
8345
8346 static AtkObject *
8347 get_accessible (GtkWidget *widget)
8348 {
8349 AtkObject *accessible;
8350
8351 if ((accessible = eel_accessibility_get_atk_object (widget))) {
8352 return accessible;
8353 }
8354
8355 accessible = g_object_new (nautilus_canvas_container_accessible_get_type (), "widget", widget, NULL);
8356
8357 return eel_accessibility_set_atk_object_return (widget, accessible);
8358 }
8359
8360 gboolean
8361 nautilus_canvas_container_is_layout_rtl (NautilusCanvasContainer *container)
8362 {
8363 g_return_val_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container), 0);
8364
8365 return container->details->layout_mode == NAUTILUS_CANVAS_LAYOUT_T_B_R_L ||
8366 container->details->layout_mode == NAUTILUS_CANVAS_LAYOUT_R_L_T_B;
8367 }
8368
8369 gboolean
8370 nautilus_canvas_container_is_layout_vertical (NautilusCanvasContainer *container)
8371 {
8372 g_return_val_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container), FALSE);
8373
8374 return (container->details->layout_mode == NAUTILUS_CANVAS_LAYOUT_T_B_L_R ||
8375 container->details->layout_mode == NAUTILUS_CANVAS_LAYOUT_T_B_R_L);
8376 }
8377
8378 int
8379 nautilus_canvas_container_get_max_layout_lines_for_pango (NautilusCanvasContainer *container)
8380 {
8381 int limit;
8382
8383 if (nautilus_canvas_container_get_is_desktop (container)) {
8384 limit = desktop_text_ellipsis_limit;
8385 } else {
8386 limit = text_ellipsis_limits[container->details->zoom_level];
8387 }
8388
8389 if (limit <= 0) {
8390 return G_MININT;
8391 }
8392
8393 return -limit;
8394 }
8395
8396 int
8397 nautilus_canvas_container_get_max_layout_lines (NautilusCanvasContainer *container)
8398 {
8399 int limit;
8400
8401 if (nautilus_canvas_container_get_is_desktop (container)) {
8402 limit = desktop_text_ellipsis_limit;
8403 } else {
8404 limit = text_ellipsis_limits[container->details->zoom_level];
8405 }
8406
8407 if (limit <= 0) {
8408 return G_MAXINT;
8409 }
8410
8411 return limit;
8412 }
8413
8414 void
8415 nautilus_canvas_container_begin_loading (NautilusCanvasContainer *container)
8416 {
8417 gboolean dummy;
8418
8419 if (nautilus_canvas_container_get_store_layout_timestamps (container)) {
8420 container->details->layout_timestamp = UNDEFINED_TIME;
8421 g_signal_emit (container,
8422 signals[GET_STORED_LAYOUT_TIMESTAMP], 0,
8423 NULL, &container->details->layout_timestamp, &dummy);
8424 }
8425 }
8426
8427 static void
8428 store_layout_timestamps_now (NautilusCanvasContainer *container)
8429 {
8430 NautilusCanvasIcon *icon;
8431 GList *p;
8432 gboolean dummy;
8433
8434 container->details->layout_timestamp = time (NULL);
8435 g_signal_emit (container,
8436 signals[STORE_LAYOUT_TIMESTAMP], 0,
8437 NULL, &container->details->layout_timestamp, &dummy);
8438
8439 for (p = container->details->icons; p != NULL; p = p->next) {
8440 icon = p->data;
8441
8442 g_signal_emit (container,
8443 signals[STORE_LAYOUT_TIMESTAMP], 0,
8444 icon->data, &container->details->layout_timestamp, &dummy);
8445 }
8446 }
8447
8448
8449 void
8450 nautilus_canvas_container_end_loading (NautilusCanvasContainer *container,
8451 gboolean all_icons_added)
8452 {
8453 if (all_icons_added &&
8454 nautilus_canvas_container_get_store_layout_timestamps (container)) {
8455 if (container->details->new_icons == NULL) {
8456 store_layout_timestamps_now (container);
8457 } else {
8458 container->details->store_layout_timestamps_when_finishing_new_icons = TRUE;
8459 }
8460 }
8461 }
8462
8463 gboolean
8464 nautilus_canvas_container_get_store_layout_timestamps (NautilusCanvasContainer *container)
8465 {
8466 return container->details->store_layout_timestamps;
8467 }
8468
8469
8470 void
8471 nautilus_canvas_container_set_store_layout_timestamps (NautilusCanvasContainer *container,
8472 gboolean store_layout_timestamps)
8473 {
8474 container->details->store_layout_timestamps = store_layout_timestamps;
8475 }
8476
8477
8478 #if ! defined (NAUTILUS_OMIT_SELF_CHECK)
8479
8480 static char *
8481 check_compute_stretch (int icon_x, int icon_y, int icon_size,
8482 int start_pointer_x, int start_pointer_y,
8483 int end_pointer_x, int end_pointer_y)
8484 {
8485 StretchState start, current;
8486
8487 start.icon_x = icon_x;
8488 start.icon_y = icon_y;
8489 start.icon_size = icon_size;
8490 start.pointer_x = start_pointer_x;
8491 start.pointer_y = start_pointer_y;
8492 current.pointer_x = end_pointer_x;
8493 current.pointer_y = end_pointer_y;
8494
8495 compute_stretch (&start, ¤t);
8496
8497 return g_strdup_printf ("%d,%d:%d",
8498 current.icon_x,
8499 current.icon_y,
8500 current.icon_size);
8501 }
8502
8503 void
8504 nautilus_self_check_canvas_container (void)
8505 {
8506 EEL_CHECK_STRING_RESULT (check_compute_stretch (0, 0, 16, 0, 0, 0, 0), "0,0:16");
8507 EEL_CHECK_STRING_RESULT (check_compute_stretch (0, 0, 16, 16, 16, 17, 17), "0,0:17");
8508 EEL_CHECK_STRING_RESULT (check_compute_stretch (0, 0, 16, 16, 16, 17, 16), "0,0:16");
8509 EEL_CHECK_STRING_RESULT (check_compute_stretch (100, 100, 64, 105, 105, 40, 40), "35,35:129");
8510 }
8511
8512 #endif /* ! NAUTILUS_OMIT_SELF_CHECK */