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