1 /* -*- Mode: C; indent-tabs-mode: f; c-basic-offset: 4; tab-width: 4 -*- */
2 
3 /* fm-icon-container.h - the container widget for file manager icons
4 
5    Copyright (C) 2002 Sun Microsystems, Inc.
6 
7    The Gnome Library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Library General Public License as
9    published by the Free Software Foundation; either version 2 of the
10    License, or (at your option) any later version.
11 
12    The Gnome Library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Library General Public License for more details.
16 
17    You should have received a copy of the GNU Library General Public
18    License along with the Gnome Library; see the file COPYING.LIB.  If not,
19    write to the Free Software Foundation, Inc., 51 Franklin Street - Suite 500,
20    Boston, MA 02110-1335, USA.
21 
22    Author: Michael Meeks <michael@ximian.com>
23 */
24 #include <config.h>
25 
26 #include "nemo-icon-view-container.h"
27 
28 #include <string.h>
29 #include <errno.h>
30 #include <math.h>
31 
32 #include <glib/gi18n.h>
33 #include <gio/gio.h>
34 #include <eel/eel-glib-extensions.h>
35 #include "nemo-icon-private.h"
36 #include <libnemo-private/nemo-global-preferences.h>
37 #include <libnemo-private/nemo-file-attributes.h>
38 #include <libnemo-private/nemo-metadata.h>
39 #include <libnemo-private/nemo-thumbnails.h>
40 #include <libnemo-private/nemo-desktop-icon-file.h>
41 #include <libnemo-private/nemo-desktop-utils.h>
42 
43 #define DEBUG_FLAG NEMO_DEBUG_ICON_CONTAINER
44 #include "nemo-debug.h"
45 
46 /* Maximum size (pixels) allowed for icons at the standard zoom level. */
47 #define MINIMUM_IMAGE_SIZE 24
48 #define MAXIMUM_IMAGE_SIZE 96
49 
50 /* If icon size is bigger than this, request large embedded text.
51  * Its selected so that the non-large text should fit in "normal" icon sizes
52  */
53 #define ICON_SIZE_FOR_LARGE_EMBEDDED_TEXT 55
54 
55 static void get_max_icon_dimensions (GList *icon_start,
56                                      GList *icon_end,
57                                      double *max_icon_width,
58                                      double *max_icon_height,
59                                      double *max_text_width,
60                                      double *max_text_height,
61                                      double *max_bounds_height);
62 static void find_empty_location (NemoIconContainer *container,
63                                  NemoPlacementGrid *grid,
64                                  NemoIcon *icon,
65                                  int start_x,
66                                  int start_y,
67                                  int *x,
68                                  int *y);
69 
70 G_DEFINE_TYPE (NemoIconViewContainer, nemo_icon_view_container, NEMO_TYPE_ICON_CONTAINER);
71 
72 static GQuark attribute_none_q;
73 static GQuark *caption_attributes = NULL;
74 
75 static NemoIconView *
get_icon_view(NemoIconContainer * container)76 get_icon_view (NemoIconContainer *container)
77 {
78 	/* Type unsafe comparison for performance */
79 	return ((NemoIconViewContainer *)container)->view;
80 }
81 
82 
83 static NemoIconInfo *
nemo_icon_view_container_get_icon_images(NemoIconContainer * container,NemoIconData * data,int size,gboolean for_drag_accept,gboolean * has_window_open,gboolean visible)84 nemo_icon_view_container_get_icon_images (NemoIconContainer *container,
85                                           NemoIconData      *data,
86                                           int                size,
87                                           gboolean           for_drag_accept,
88 					                      gboolean          *has_window_open,
89                                           gboolean           visible)
90 {
91 	NemoIconView *icon_view;
92 	NemoFile *file;
93 	NemoFileIconFlags flags;
94 	NemoIconInfo *icon_info;
95 	GdkPixbuf *pixbuf;
96 	GIcon *emblemed_icon;
97 	GEmblem *emblem;
98 	GList *emblem_icons, *l;
99     gint scale;
100 
101 	file = (NemoFile *) data;
102 
103 	g_assert (NEMO_IS_FILE (file));
104 	icon_view = get_icon_view (container);
105 	g_return_val_if_fail (icon_view != NULL, NULL);
106 
107 	*has_window_open = nemo_file_has_open_window (file);
108 
109 	flags = NEMO_FILE_ICON_FLAGS_USE_MOUNT_ICON_AS_EMBLEM |
110 			NEMO_FILE_ICON_FLAGS_FORCE_THUMBNAIL_SIZE;
111 
112     if (visible) {
113         flags |= NEMO_FILE_ICON_FLAGS_USE_THUMBNAILS;
114     }
115 
116 	if (for_drag_accept) {
117 		flags |= NEMO_FILE_ICON_FLAGS_FOR_DRAG_ACCEPT;
118 	}
119 
120 	emblem_icons = nemo_file_get_emblem_icons (file,
121 		                                       nemo_view_get_directory_as_file (NEMO_VIEW (icon_view)));
122 
123     scale = gtk_widget_get_scale_factor (GTK_WIDGET (icon_view));
124 	icon_info = nemo_file_get_icon (file, size, size, scale, flags);
125 
126 	/* apply emblems */
127 	if (emblem_icons != NULL) {
128         gint w, h, s;
129         gboolean bad_ratio;
130 
131         l = emblem_icons;
132 
133         pixbuf = nemo_icon_info_get_pixbuf (icon_info);
134 
135         w = gdk_pixbuf_get_width (pixbuf);
136         h = gdk_pixbuf_get_height (pixbuf);
137 
138         s = MAX (w, h);
139         if (s < size)
140             size = s;
141 
142         bad_ratio = (int)nemo_icon_get_emblem_size_for_icon_size (size) * scale > w ||
143                     (int)nemo_icon_get_emblem_size_for_icon_size (size) * scale > h;
144 
145         if (bad_ratio)
146             goto skip_emblem; /* Would prefer to not use goto, but
147                                * I don't want to do these checks on
148                                * non-emblemed icons (the majority)
149                                * as it would be too costly */
150 
151         emblem = g_emblem_new (l->data);
152 
153 		emblemed_icon = g_emblemed_icon_new (G_ICON (pixbuf), emblem);
154 		g_object_unref (emblem);
155 
156 		for (l = l->next; l != NULL; l = l->next) {
157 			emblem = g_emblem_new (l->data);
158 			g_emblemed_icon_add_emblem (G_EMBLEMED_ICON (emblemed_icon),
159 						    emblem);
160 			g_object_unref (emblem);
161 		}
162 
163         nemo_icon_info_clear (&icon_info);
164 		icon_info = nemo_icon_info_lookup (emblemed_icon, size, scale);
165         g_object_unref (emblemed_icon);
166 
167 skip_emblem:
168 		g_object_unref (pixbuf);
169 
170 	}
171 
172 	if (emblem_icons != NULL) {
173 		g_list_free_full (emblem_icons, g_object_unref);
174 	}
175 
176 	return icon_info;
177 }
178 
179 static char *
nemo_icon_view_container_get_icon_description(NemoIconContainer * container,NemoIconData * data)180 nemo_icon_view_container_get_icon_description (NemoIconContainer *container,
181 						   NemoIconData      *data)
182 {
183 	NemoFile *file;
184 	char *mime_type;
185 	const char *description;
186 
187 	file = NEMO_FILE (data);
188 	g_assert (NEMO_IS_FILE (file));
189 
190 	if (NEMO_IS_DESKTOP_ICON_FILE (file)) {
191 		return NULL;
192 	}
193 
194 	mime_type = nemo_file_get_mime_type (file);
195 	description = g_content_type_get_description (mime_type);
196 	g_free (mime_type);
197 	return g_strdup (description);
198 }
199 
200 static void
nemo_icon_view_container_prioritize_thumbnailing(NemoIconContainer * container,NemoIconData * data)201 nemo_icon_view_container_prioritize_thumbnailing (NemoIconContainer *container,
202 						      NemoIconData      *data)
203 {
204 	NemoFile *file;
205 
206 	file = (NemoFile *) data;
207 
208 	g_assert (NEMO_IS_FILE (file));
209 
210     if (nemo_can_thumbnail (file) && !nemo_file_has_loaded_thumbnail (file)) {
211         nemo_create_thumbnail (file, 0, TRUE);
212     }
213 }
214 
215 static void
update_auto_strv_as_quarks(GSettings * settings,const gchar * key,gpointer user_data)216 update_auto_strv_as_quarks (GSettings   *settings,
217 			    const gchar *key,
218 			    gpointer     user_data)
219 {
220 	GQuark **storage = user_data;
221 	int i = 0;
222 	char **value;
223 
224 	value = g_settings_get_strv (settings, key);
225 
226 	g_free (*storage);
227 	*storage = g_new (GQuark, g_strv_length (value) + 1);
228 
229 	for (i = 0; value[i] != NULL; ++i) {
230 		(*storage)[i] = g_quark_from_string (value[i]);
231 	}
232 	(*storage)[i] = 0;
233 
234 	g_strfreev (value);
235 }
236 
237 /*
238  * Get the preference for which caption text should appear
239  * beneath icons.
240  */
241 static GQuark *
nemo_icon_view_container_get_icon_text_attributes_from_preferences(void)242 nemo_icon_view_container_get_icon_text_attributes_from_preferences (void)
243 {
244 	if (caption_attributes == NULL) {
245 		update_auto_strv_as_quarks (nemo_icon_view_preferences,
246 					    NEMO_PREFERENCES_ICON_VIEW_CAPTIONS,
247 					    &caption_attributes);
248 	}
249 
250 	/* We don't need to sanity check the attributes list even though it came
251 	 * from preferences.
252 	 *
253 	 * There are 2 ways that the values in the list could be bad.
254 	 *
255 	 * 1) The user picks "bad" values.  "bad" values are those that result in
256 	 *    there being duplicate attributes in the list.
257 	 *
258 	 * 2) Value stored in GConf are tampered with.  Its possible physically do
259 	 *    this by pulling the rug underneath GConf and manually editing its
260 	 *    config files.  Its also possible to use a third party GConf key
261 	 *    editor and store garbage for the keys in question.
262 	 *
263 	 * Thankfully, the Nemo preferences machinery deals with both of
264 	 * these cases.
265 	 *
266 	 * In the first case, the preferences dialog widgetry prevents
267 	 * duplicate attributes by making "bad" choices insensitive.
268 	 *
269 	 * In the second case, the preferences getter (and also the auto storage) for
270 	 * string_array values are always valid members of the enumeration associated
271 	 * with the preference.
272 	 *
273 	 * So, no more error checking on attributes is needed here and we can return
274 	 * a the auto stored value.
275 	 */
276 	return caption_attributes;
277 }
278 
279 static int
quarkv_length(GQuark * attributes)280 quarkv_length (GQuark *attributes)
281 {
282 	int i;
283 	i = 0;
284 	while (attributes[i] != 0) {
285 		i++;
286 	}
287 	return i;
288 }
289 
290 /**
291  * nemo_icon_view_get_icon_text_attribute_names:
292  *
293  * Get a list representing which text attributes should be displayed
294  * beneath an icon. The result is dependent on zoom level and possibly
295  * user configuration. Don't free the result.
296  * @view: NemoIconView to query.
297  *
298  **/
299 static GQuark *
nemo_icon_view_container_get_icon_text_attribute_names(NemoIconContainer * container,int * len)300 nemo_icon_view_container_get_icon_text_attribute_names (NemoIconContainer *container,
301 							    int *len)
302 {
303 	GQuark *attributes;
304 	int piece_count;
305 
306 	const int pieces_by_level[] = {
307 		0,	/* NEMO_ZOOM_LEVEL_SMALLEST */
308 		0,	/* NEMO_ZOOM_LEVEL_SMALLER */
309 		0,	/* NEMO_ZOOM_LEVEL_SMALL */
310 		1,	/* NEMO_ZOOM_LEVEL_STANDARD */
311 		2,	/* NEMO_ZOOM_LEVEL_LARGE */
312 		2,	/* NEMO_ZOOM_LEVEL_LARGER */
313 		3	/* NEMO_ZOOM_LEVEL_LARGEST */
314 	};
315 
316 	piece_count = pieces_by_level[nemo_icon_container_get_zoom_level (container)];
317 
318 	attributes = nemo_icon_view_container_get_icon_text_attributes_from_preferences ();
319 
320 	*len = MIN (piece_count, quarkv_length (attributes));
321 
322 	return attributes;
323 }
324 
325 /* This callback returns the text, both the editable part, and the
326  * part below that is not editable.
327  */
328 static void
nemo_icon_view_container_get_icon_text(NemoIconContainer * container,NemoIconData * data,char ** editable_text,char ** additional_text,gboolean * pinned,gboolean * fav_unavailable,gboolean include_invisible)329 nemo_icon_view_container_get_icon_text (NemoIconContainer *container,
330 					    NemoIconData      *data,
331 					    char                 **editable_text,
332 					    char                 **additional_text,
333                         gboolean              *pinned,
334                         gboolean              *fav_unavailable,
335 					    gboolean               include_invisible)
336 {
337 	GQuark *attributes;
338 	char *text_array[4];
339 	int i, j, num_attributes;
340 	NemoIconView *icon_view;
341 	NemoFile *file;
342 	gboolean use_additional;
343 
344 	file = NEMO_FILE (data);
345 
346 	g_assert (NEMO_IS_FILE (file));
347 	g_assert (editable_text != NULL);
348 	icon_view = get_icon_view (container);
349 	g_return_if_fail (icon_view != NULL);
350 
351 	use_additional = (additional_text != NULL);
352 
353 	/* In the smallest zoom mode, no text is drawn. */
354 	if (nemo_icon_container_get_zoom_level (container) == NEMO_ZOOM_LEVEL_SMALLEST &&
355             !include_invisible) {
356 		*editable_text = NULL;
357         if (pinned) {
358             *pinned = FALSE;
359         }
360 	} else {
361 		/* Strip the suffix for nemo object xml files. */
362 		*editable_text = nemo_file_get_display_name (file);
363         if (pinned) {
364             *pinned = nemo_file_get_pinning (file) && !nemo_file_is_in_favorites (file);
365         }
366 	}
367 
368     if (fav_unavailable) {
369         *fav_unavailable = nemo_file_is_unavailable_favorite (file);
370     }
371 
372 	if (!use_additional) {
373 		return;
374 	}
375 
376 	if (nemo_icon_view_is_compact (icon_view)) {
377 		*additional_text = NULL;
378 		return;
379 	}
380 
381 	if (NEMO_IS_DESKTOP_ICON_FILE (file) ||
382 	    nemo_file_is_nemo_link (file)) {
383 		/* Don't show the normal extra information for desktop icons,
384 		 * or desktop files, it doesn't make sense. */
385  		*additional_text = NULL;
386 		return;
387 	}
388 
389 	/* Find out what attributes go below each icon. */
390 	attributes = nemo_icon_view_container_get_icon_text_attribute_names (container,
391 									   &num_attributes);
392 
393 	/* Get the attributes. */
394 	j = 0;
395 	for (i = 0; i < num_attributes; ++i) {
396 		if (attributes[i] == attribute_none_q) {
397 			continue;
398 		}
399 
400 		text_array[j++] =
401 			nemo_file_get_string_attribute_with_default_q (file, attributes[i]);
402 	}
403 	text_array[j] = NULL;
404 
405 	/* Return them. */
406 	if (j == 0) {
407 		*additional_text = NULL;
408 	} else if (j == 1) {
409 		/* Only one item, avoid the strdup + free */
410 		*additional_text = text_array[0];
411 	} else {
412 		*additional_text = g_strjoinv ("\n", text_array);
413 
414 		for (i = 0; i < j; i++) {
415 			g_free (text_array[i]);
416 		}
417 	}
418 }
419 
420 /* Sort as follows:
421  *   0) computer link
422  *   1) home link
423  *   2) network link
424  *   3) mount links
425  *   4) trash link
426  *   5) other
427  */
428 typedef enum {
429 	SORT_COMPUTER_LINK,
430 	SORT_HOME_LINK,
431 	SORT_NETWORK_LINK,
432 	SORT_MOUNT_LINK,
433 	SORT_TRASH_LINK,
434 	SORT_OTHER
435 } SortCategory;
436 
437 static SortCategory
get_sort_category(NemoFile * file)438 get_sort_category (NemoFile *file)
439 {
440 	NemoDesktopLink *link;
441 	SortCategory category;
442 
443 	category = SORT_OTHER;
444 
445 	if (NEMO_IS_DESKTOP_ICON_FILE (file)) {
446 		link = nemo_desktop_icon_file_get_link (NEMO_DESKTOP_ICON_FILE (file));
447 		if (link != NULL) {
448 			switch (nemo_desktop_link_get_link_type (link)) {
449 			case NEMO_DESKTOP_LINK_COMPUTER:
450 				category = SORT_COMPUTER_LINK;
451 				break;
452 			case NEMO_DESKTOP_LINK_HOME:
453 				category = SORT_HOME_LINK;
454 				break;
455 			case NEMO_DESKTOP_LINK_MOUNT:
456 				category = SORT_MOUNT_LINK;
457 				break;
458 			case NEMO_DESKTOP_LINK_TRASH:
459 				category = SORT_TRASH_LINK;
460 				break;
461 			case NEMO_DESKTOP_LINK_NETWORK:
462 				category = SORT_NETWORK_LINK;
463 				break;
464 			default:
465 				category = SORT_OTHER;
466 				break;
467 			}
468 			g_object_unref (link);
469 		}
470 	}
471 
472 	return category;
473 }
474 
475 static int
fm_desktop_icon_container_icons_compare(NemoIconContainer * container,NemoIconData * data_a,NemoIconData * data_b)476 fm_desktop_icon_container_icons_compare (NemoIconContainer *container,
477 					 NemoIconData      *data_a,
478 					 NemoIconData      *data_b)
479 {
480 	NemoFile *file_a;
481 	NemoFile *file_b;
482 	NemoView *directory_view;
483 	SortCategory category_a, category_b;
484 
485 	file_a = (NemoFile *) data_a;
486 	file_b = (NemoFile *) data_b;
487 
488 	directory_view = NEMO_VIEW (NEMO_ICON_VIEW_CONTAINER (container)->view);
489 	g_return_val_if_fail (directory_view != NULL, 0);
490 
491 	category_a = get_sort_category (file_a);
492 	category_b = get_sort_category (file_b);
493 
494 	if (category_a == category_b) {
495 		return nemo_file_compare_for_sort
496 			(file_a, file_b, NEMO_FILE_SORT_BY_DISPLAY_NAME,
497 			 nemo_view_should_sort_directories_first (directory_view),
498 			 FALSE);
499 	}
500 
501 	if (category_a < category_b) {
502 		return -1;
503 	} else {
504 		return +1;
505 	}
506 }
507 
508 static int
nemo_icon_view_container_compare_icons(NemoIconContainer * container,NemoIconData * icon_a,NemoIconData * icon_b)509 nemo_icon_view_container_compare_icons (NemoIconContainer *container,
510 					    NemoIconData      *icon_a,
511 					    NemoIconData      *icon_b)
512 {
513 	NemoIconView *icon_view;
514 
515 	icon_view = get_icon_view (container);
516 	g_return_val_if_fail (icon_view != NULL, 0);
517 
518 	if (NEMO_ICON_VIEW_CONTAINER (container)->sort_for_desktop) {
519 		return fm_desktop_icon_container_icons_compare
520 			(container, icon_a, icon_b);
521 	}
522 
523 	/* Type unsafe comparisons for performance */
524 	return nemo_icon_view_compare_files (icon_view,
525 					   (NemoFile *)icon_a,
526 					   (NemoFile *)icon_b);
527 }
528 
529 static void
nemo_icon_view_container_freeze_updates(NemoIconContainer * container)530 nemo_icon_view_container_freeze_updates (NemoIconContainer *container)
531 {
532 	NemoIconView *icon_view;
533 	icon_view = get_icon_view (container);
534 	g_return_if_fail (icon_view != NULL);
535 	nemo_view_freeze_updates (NEMO_VIEW (icon_view));
536 }
537 
538 static void
nemo_icon_view_container_unfreeze_updates(NemoIconContainer * container)539 nemo_icon_view_container_unfreeze_updates (NemoIconContainer *container)
540 {
541 	NemoIconView *icon_view;
542 	icon_view = get_icon_view (container);
543 	g_return_if_fail (icon_view != NULL);
544 	nemo_view_unfreeze_updates (NEMO_VIEW (icon_view));
545 }
546 
547 inline static void
nemo_icon_view_container_icon_get_bounding_box(NemoIcon * icon,int * x1_return,int * y1_return,int * x2_return,int * y2_return,NemoIconCanvasItemBoundsUsage usage)548 nemo_icon_view_container_icon_get_bounding_box (NemoIcon *icon,
549                int *x1_return, int *y1_return,
550                int *x2_return, int *y2_return,
551                NemoIconCanvasItemBoundsUsage usage)
552 {
553     double x1, y1, x2, y2;
554 
555     if (usage == BOUNDS_USAGE_FOR_DISPLAY) {
556         eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (icon->item),
557                         &x1, &y1, &x2, &y2);
558     } else if (usage == BOUNDS_USAGE_FOR_LAYOUT) {
559         nemo_icon_canvas_item_get_bounds_for_layout (icon->item,
560                                  &x1, &y1, &x2, &y2);
561     } else if (usage == BOUNDS_USAGE_FOR_ENTIRE_ITEM) {
562         nemo_icon_canvas_item_get_bounds_for_entire_item (icon->item,
563                                       &x1, &y1, &x2, &y2);
564     } else {
565         g_assert_not_reached ();
566     }
567 
568     if (x1_return != NULL) {
569         *x1_return = x1;
570     }
571 
572     if (y1_return != NULL) {
573         *y1_return = y1;
574     }
575 
576     if (x2_return != NULL) {
577         *x2_return = x2;
578     }
579 
580     if (y2_return != NULL) {
581         *y2_return = y2;
582     }
583 }
584 
585 static gboolean
get_stored_icon_position(NemoIconContainer * container,NemoIconData * data,NemoIconPosition * position)586 get_stored_icon_position (NemoIconContainer *container,
587                           NemoIconData      *data,
588                           NemoIconPosition  *position)
589 {
590     GdkPoint point;
591     char *scale_string;
592     NemoIconView *icon_view;
593     NemoFile *file;
594 
595     g_assert (NEMO_IS_ICON_CONTAINER (container));
596     g_assert (NEMO_IS_FILE (data));
597     g_assert (position != NULL);
598 
599     file = NEMO_FILE (data);
600 
601     icon_view = get_icon_view (container);
602     g_assert (NEMO_IS_ICON_VIEW (icon_view));
603 
604     if (nemo_icon_view_is_compact (icon_view) || nemo_file_get_is_desktop_orphan (file)) {
605         return FALSE;
606     }
607 
608     nemo_file_get_position (file, &point);
609     position->x = point.x;
610     position->y = point.y;
611 
612     /* If it is the desktop directory, maybe the gnome-libs metadata has information about it */
613 
614     /* Disable scaling if not on the desktop */
615     if (nemo_icon_container_get_is_desktop (container)) {
616         /* Get the scale of the icon from the metadata. */
617         scale_string = nemo_file_get_metadata
618             (file, NEMO_METADATA_KEY_ICON_SCALE, "1");
619         position->scale = g_ascii_strtod (scale_string, NULL);
620         if (errno != 0) {
621             position->scale = 1.0;
622         }
623 
624         g_free (scale_string);
625     } else {
626         position->scale = 1.0;
627     }
628 
629     return position->x > ICON_UNPOSITIONED_VALUE;
630 }
631 
632 static void
lay_down_one_line(NemoIconContainer * container,GList * line_start,GList * line_end,double y,double max_height,GArray * positions,gboolean whole_text,gint gap)633 lay_down_one_line (NemoIconContainer *container,
634            GList *line_start,
635            GList *line_end,
636            double y,
637            double max_height,
638            GArray *positions,
639            gboolean whole_text,
640            gint     gap)
641 {
642     GList *p;
643     NemoIcon *icon;
644     double x, y_offset;
645     NemoCanvasRects *position;
646     int i;
647     gboolean is_rtl;
648 
649     is_rtl = nemo_icon_container_is_layout_rtl (container);
650 
651     /* Lay out the icons along the baseline. */
652     x = gap;
653     i = 0;
654     for (p = line_start; p != line_end; p = p->next) {
655         icon = p->data;
656 
657         position = &g_array_index (positions, NemoCanvasRects, i++);
658 
659         if (container->details->label_position == NEMO_ICON_LABEL_POSITION_BESIDE) {
660             y_offset = (max_height - position->height) / 2;
661         } else {
662             y_offset = position->y_offset;
663         }
664 
665         nemo_icon_container_icon_set_position
666             (container, icon,
667              is_rtl ? nemo_icon_container_get_mirror_x_position (container, icon, x + position->x_offset) : x + position->x_offset,
668              y + y_offset);
669         nemo_icon_canvas_item_set_entire_text (icon->item, whole_text);
670 
671         icon->saved_ltr_x = is_rtl ? nemo_icon_container_get_mirror_x_position (container, icon, icon->x) : icon->x;
672 
673         x += position->width;
674     }
675 }
676 
677 static void
lay_down_one_column(NemoIconContainer * container,GList * line_start,GList * line_end,double x,double y_start,double y_iter,GArray * positions)678 lay_down_one_column (NemoIconContainer *container,
679              GList *line_start,
680              GList *line_end,
681              double x,
682              double y_start,
683              double y_iter,
684              GArray *positions)
685 {
686     GList *p;
687     NemoIcon *icon;
688     double y;
689     NemoCanvasRects *position;
690     int i;
691     gboolean is_rtl;
692 
693         is_rtl = nemo_icon_container_is_layout_rtl (container);
694 
695     /* Lay out the icons along the baseline. */
696     y = y_start;
697     i = 0;
698     for (p = line_start; p != line_end; p = p->next) {
699         icon = p->data;
700 
701         position = &g_array_index (positions, NemoCanvasRects, i++);
702 
703         nemo_icon_container_icon_set_position
704             (container, icon,
705              is_rtl ? nemo_icon_container_get_mirror_x_position (container, icon, x + position->x_offset) : x + position->x_offset,
706              y + position->y_offset);
707 
708         icon->saved_ltr_x = is_rtl ? nemo_icon_container_get_mirror_x_position (container, icon, icon->x) : icon->x;
709 
710         y += y_iter;
711     }
712 }
713 
714 #define ICON_TEXT_GAP 4
715 #define COLUMN_GAP 4
716 #define ROW_GAP 10
717 
718 static void
lay_down_icons_horizontal(NemoIconContainer * container,GList * icons,double start_y)719 lay_down_icons_horizontal (NemoIconContainer *container,
720                GList *icons,
721                double start_y)
722 {
723     GList *p, *line_start;
724     NemoIcon *icon;
725     double canvas_width, y;
726     GArray *positions;
727     NemoCanvasRects *position;
728     EelDRect bounds;
729     EelDRect icon_bounds;
730     EelDRect text_bounds;
731     double line_width;
732     double grid_width;
733     double max_text_width, max_icon_width;
734     int icon_width;
735     int i;
736     int num_columns;
737     double ppu;
738     int icon_text_gap, column_gap, row_gap;
739     int device_canvas_width;
740     GtkAllocation allocation;
741     gint icon_size, text_size, use_size;
742 
743     g_assert (NEMO_IS_ICON_CONTAINER (container));
744 
745     if (icons == NULL) {
746         return;
747     }
748 
749     positions = g_array_new (FALSE, FALSE, sizeof (NemoCanvasRects));
750     gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
751 
752     /* Lay out icons a line at a time. */
753     canvas_width = nemo_icon_container_get_canvas_width (container, allocation);
754     max_icon_width = max_text_width = 0.0;
755 
756     ppu = EEL_CANVAS (container)->pixels_per_unit;
757     icon_text_gap = floor (ICON_TEXT_GAP / ppu);
758     column_gap = floor (COLUMN_GAP / ppu);
759     row_gap = floor (ROW_GAP / ppu);
760 
761     device_canvas_width = floor (canvas_width * ppu);
762     icon_size = nemo_get_icon_size_for_zoom_level (container->details->zoom_level);
763     text_size = nemo_get_icon_text_width_for_zoom_level (container->details->zoom_level);
764 
765     use_size = MAX (icon_size, text_size) + 15;
766     icon_size /= ppu;
767     if (container->details->label_position == NEMO_ICON_LABEL_POSITION_BESIDE) {
768         /* Would it be worth caching these bounds for the next loop? */
769         for (p = icons; p != NULL; p = p->next) {
770             icon = p->data;
771 
772             icon_bounds = nemo_icon_canvas_item_get_icon_rectangle (icon->item);
773             max_icon_width = MAX (max_icon_width, ceil (icon_bounds.x1 - icon_bounds.x0));
774 
775             text_bounds = nemo_icon_canvas_item_get_text_rectangle (icon->item, TRUE);
776             max_text_width = MAX (max_text_width, ceil (text_bounds.x1 - text_bounds.x0));
777         }
778 
779         grid_width = max_icon_width + max_text_width + column_gap;
780     } else {
781         num_columns = device_canvas_width / use_size;
782         /* Minimum of one column */
783         num_columns = MAX (num_columns, 1);
784         /* -1 prevents jitter */
785         grid_width = (((device_canvas_width / num_columns) / ppu) - 1.0);
786     }
787 
788     line_width = container->details->label_position == NEMO_ICON_LABEL_POSITION_BESIDE ? column_gap : 0;
789     line_start = icons;
790     y = start_y + row_gap;
791     i = 0;
792 
793     for (p = icons; p != NULL; p = p->next) {
794         icon = p->data;
795 
796         if (container->details->fixed_text_height == -1) {
797             container->details->fixed_text_height = nemo_icon_canvas_item_get_fixed_text_height_for_layout (icon->item) / ppu;
798         }
799 
800         /* Assume it's only one level hierarchy to avoid costly affine calculations */
801         nemo_icon_canvas_item_get_bounds_for_layout (icon->item,
802                                  &bounds.x0, &bounds.y0,
803                                  &bounds.x1, &bounds.y1);
804 
805         icon_bounds = nemo_icon_canvas_item_get_icon_rectangle (icon->item);
806         icon_width = grid_width;
807 
808         /* If this icon doesn't fit, it's time to lay out the line that's queued up. */
809         if (line_start != p && line_width + icon_width >= canvas_width ) {
810             if (container->details->label_position == NEMO_ICON_LABEL_POSITION_BESIDE) {
811                 y += row_gap;
812             } else {
813                 /* Advance to the baseline. */
814                 y += icon_text_gap + icon_size;
815             }
816 
817             lay_down_one_line (container, line_start, p, y, icon_size, positions, FALSE, column_gap);
818 
819             if (container->details->label_position == NEMO_ICON_LABEL_POSITION_BESIDE) {
820                 y += row_gap + icon_size;
821             } else {
822                 /* Advance to next line. */
823                 y += container->details->fixed_text_height + row_gap;
824             }
825 
826             line_width = container->details->label_position == NEMO_ICON_LABEL_POSITION_BESIDE ? column_gap : 0;
827             line_start = p;
828             i = 0;
829         }
830 
831         g_array_set_size (positions, i + 1);
832         position = &g_array_index (positions, NemoCanvasRects, i++);
833         position->width = icon_width;
834         position->height = icon_bounds.y1 - icon_bounds.y0;
835 
836         if (container->details->label_position == NEMO_ICON_LABEL_POSITION_BESIDE) {
837             position->x_offset = max_icon_width + (2 * row_gap) - (icon_bounds.x1 - icon_bounds.x0);
838             position->y_offset = 0;
839         } else {
840             position->x_offset = (icon_width - (icon_bounds.x1 - icon_bounds.x0)) / 2;
841             position->y_offset = icon_bounds.y0 - icon_bounds.y1;
842         }
843 
844         /* Add this icon. */
845         line_width += icon_width;
846     }
847 
848     /* Lay down that last line of icons. */
849     if (line_start != NULL) {
850             if (container->details->label_position == NEMO_ICON_LABEL_POSITION_BESIDE) {
851                 y += row_gap;
852             } else {
853                 /* Advance to the baseline. */
854                 y += icon_text_gap + icon_size;
855             }
856 
857         lay_down_one_line (container, line_start, NULL, y, icon_size, positions, TRUE, column_gap);
858     }
859 
860     g_array_free (positions, TRUE);
861 }
862 
863 /* column-wise layout. At the moment, this only works with label-beside-icon (used by "Compact View"). */
864 static void
lay_down_icons_vertical(NemoIconContainer * container,GList * icons,double start_y)865 lay_down_icons_vertical (NemoIconContainer *container,
866              GList *icons,
867              double start_y)
868 {
869     GList *p, *line_start;
870     NemoIcon *icon;
871     double x, canvas_height;
872     GArray *positions;
873     NemoCanvasRects *position;
874     EelDRect icon_bounds;
875     EelDRect text_bounds;
876     GtkAllocation allocation;
877 
878     double ppu;
879     int gap;
880     double line_height;
881 
882     double max_height;
883     double max_height_with_borders;
884     double max_width;
885     double max_width_in_column;
886 
887     double max_bounds_height;
888     double max_bounds_height_with_borders;
889 
890     double max_text_width, max_icon_width;
891     double max_text_height, max_icon_height;
892     int height;
893     int i;
894 
895     g_assert (NEMO_IS_ICON_CONTAINER (container));
896     g_assert (container->details->label_position == NEMO_ICON_LABEL_POSITION_BESIDE);
897 
898     if (icons == NULL) {
899         return;
900     }
901 
902     ppu = EEL_CANVAS (container)->pixels_per_unit;
903     gap = floor (ICON_TEXT_GAP / ppu);
904 
905     positions = g_array_new (FALSE, FALSE, sizeof (NemoCanvasRects));
906     gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
907 
908     /* Lay out icons a column at a time. */
909     canvas_height = nemo_icon_container_get_canvas_height (container, allocation);
910 
911     max_icon_width = max_text_width = 0.0;
912     max_icon_height = max_text_height = 0.0;
913     max_bounds_height = 0.0;
914 
915     get_max_icon_dimensions (icons, NULL,
916                  &max_icon_width, &max_icon_height,
917                  &max_text_width, &max_text_height,
918                  &max_bounds_height);
919 
920     max_width = max_icon_width + max_text_width;
921     max_height = MAX (max_icon_height, max_text_height);
922     max_height_with_borders = gap + max_height;
923 
924     max_bounds_height_with_borders = gap + max_bounds_height;
925 
926     line_height = gap;
927     line_start = icons;
928     x = 0;
929     i = 0;
930 
931     max_width_in_column = 0.0;
932 
933     for (p = icons; p != NULL; p = p->next) {
934         icon = p->data;
935 
936         /* If this icon doesn't fit, it's time to lay out the column that's queued up. */
937 
938         /* We use the bounds height here, since for wrapping we also want to consider
939          * overlapping emblems at the bottom. We may wrap a little bit too early since
940          * the icon with the max. bounds height may actually not be in the last row, but
941          * it is better than visual glitches
942          */
943         if (line_start != p && line_height + (max_bounds_height_with_borders-1) >= canvas_height ) {
944             x += gap;
945 
946             /* correctly set (per-column) width */
947             if (!container->details->all_columns_same_width) {
948                 for (i = 0; i < (int) positions->len; i++) {
949                     position = &g_array_index (positions, NemoCanvasRects, i);
950                     position->width = max_width_in_column;
951                 }
952             }
953 
954             lay_down_one_column (container, line_start, p, x, gap, max_height_with_borders, positions);
955 
956             /* Advance to next column. */
957             if (container->details->all_columns_same_width) {
958                 x += max_width + gap;
959             } else {
960                 x += max_width_in_column + gap;
961             }
962 
963             line_height = gap;
964             line_start = p;
965             i = 0;
966 
967             max_width_in_column = 0;
968         }
969 
970         icon_bounds = nemo_icon_canvas_item_get_icon_rectangle (icon->item);
971         text_bounds = nemo_icon_canvas_item_get_text_rectangle (icon->item, TRUE);
972 
973         max_width_in_column = MAX (max_width_in_column,
974                        ceil (icon_bounds.x1 - icon_bounds.x0) +
975                        ceil (text_bounds.x1 - text_bounds.x0));
976 
977         g_array_set_size (positions, i + 1);
978         position = &g_array_index (positions, NemoCanvasRects, i++);
979         if (container->details->all_columns_same_width) {
980             position->width = max_width;
981         }
982         position->height = max_height;
983         position->y_offset = gap;
984         position->x_offset = gap;
985 
986         position->x_offset += max_icon_width - ceil (icon_bounds.x1 - icon_bounds.x0);
987 
988         height = MAX (ceil (icon_bounds.y1 - icon_bounds.y0), ceil(text_bounds.y1 - text_bounds.y0));
989         position->y_offset += (max_height - height) / 2;
990 
991         /* Add this icon. */
992         line_height += max_height_with_borders;
993     }
994 
995     /* Lay down that last column of icons. */
996     if (line_start != NULL) {
997         x += gap;
998         lay_down_one_column (container, line_start, NULL, x, gap, max_height_with_borders, positions);
999     }
1000 
1001     g_array_free (positions, TRUE);
1002 }
1003 
1004 static void
lay_down_icons_vertical_desktop(NemoIconContainer * container,GList * icons)1005 lay_down_icons_vertical_desktop (NemoIconContainer *container, GList *icons)
1006 {
1007     GList *p, *placed_icons, *unplaced_icons;
1008     int total, new_length, placed;
1009     NemoIcon *icon;
1010     int height, max_width, column_width, icon_width, icon_height;
1011     int x, y, x1, x2, y1, y2;
1012     EelDRect icon_rect;
1013     GtkAllocation allocation;
1014 
1015     /* Get container dimensions */
1016     gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
1017     height = nemo_icon_container_get_canvas_height (container, allocation);
1018 
1019     /* Determine which icons have and have not been placed */
1020     placed_icons = NULL;
1021     unplaced_icons = NULL;
1022 
1023     total = g_list_length (container->details->icons);
1024     new_length = g_list_length (icons);
1025     placed = total - new_length;
1026     if (placed > 0) {
1027         NemoPlacementGrid *grid;
1028         /* Add only placed icons in list */
1029         for (p = container->details->icons; p != NULL; p = p->next) {
1030             icon = p->data;
1031             if (nemo_icon_container_icon_is_positioned (icon)) {
1032                 nemo_icon_container_icon_set_position(container, icon, icon->saved_ltr_x, icon->y);
1033                 placed_icons = g_list_prepend (placed_icons, icon);
1034             } else {
1035                 icon->x = 0;
1036                 icon->y = 0;
1037                 unplaced_icons = g_list_prepend (unplaced_icons, icon);
1038             }
1039         }
1040         placed_icons = g_list_reverse (placed_icons);
1041         unplaced_icons = g_list_reverse (unplaced_icons);
1042 
1043         grid = nemo_placement_grid_new (container, FALSE);
1044 
1045         if (grid) {
1046             for (p = placed_icons; p != NULL; p = p->next) {
1047                 nemo_placement_grid_mark_icon
1048                     (grid, (NemoIcon*)p->data);
1049             }
1050 
1051             /* Place unplaced icons in the best locations */
1052             for (p = unplaced_icons; p != NULL; p = p->next) {
1053                 icon = p->data;
1054 
1055                 icon_rect = nemo_icon_canvas_item_get_icon_rectangle (icon->item);
1056 
1057                 /* Start the icon in the first column */
1058                 x = GET_VIEW_CONSTANT (container, desktop_pad_horizontal) + (GET_VIEW_CONSTANT (container, snap_size_x) / 2) - ((icon_rect.x1 - icon_rect.x0) / 2);
1059                 y = GET_VIEW_CONSTANT (container, desktop_pad_vertical) + GET_VIEW_CONSTANT (container, snap_size_y) - (icon_rect.y1 - icon_rect.y0);
1060 
1061                 find_empty_location (container,
1062                              grid,
1063                              icon,
1064                              x, y,
1065                              &x, &y);
1066 
1067                 nemo_icon_container_icon_set_position (container, icon, x, y);
1068                 icon->saved_ltr_x = x;
1069                 nemo_placement_grid_mark_icon (grid, icon);
1070             }
1071 
1072             nemo_placement_grid_free (grid);
1073         }
1074 
1075         g_list_free (placed_icons);
1076         g_list_free (unplaced_icons);
1077     } else {
1078         /* There are no placed icons.  Just lay them down using our rules */
1079         x = GET_VIEW_CONSTANT (container, desktop_pad_horizontal);
1080 
1081         while (icons != NULL) {
1082             int center_x;
1083             int baseline;
1084             int icon_height_for_bound_check;
1085             gboolean should_snap;
1086 
1087             should_snap = container->details->keep_aligned;
1088 
1089 
1090             y = GET_VIEW_CONSTANT (container, desktop_pad_vertical);
1091 
1092             max_width = 0;
1093 
1094             /* Calculate max width for column */
1095             for (p = icons; p != NULL; p = p->next) {
1096                 icon = p->data;
1097 
1098                 nemo_icon_container_icon_get_bounding_box (container, icon, &x1, &y1, &x2, &y2,
1099                                BOUNDS_USAGE_FOR_LAYOUT);
1100                 icon_width = x2 - x1;
1101                 icon_height = y2 - y1;
1102 
1103                 nemo_icon_container_icon_get_bounding_box (container, icon, NULL, &y1, NULL, &y2,
1104                                BOUNDS_USAGE_FOR_ENTIRE_ITEM);
1105                 icon_height_for_bound_check = y2 - y1;
1106 
1107                 if (should_snap) {
1108                     /* Snap the baseline to a grid position */
1109                     icon_rect = nemo_icon_canvas_item_get_icon_rectangle (icon->item);
1110                     baseline = y + (icon_rect.y1 - icon_rect.y0);
1111                     baseline = SNAP_CEIL_VERTICAL (baseline);
1112                     y = baseline - (icon_rect.y1 - icon_rect.y0);
1113                 }
1114 
1115                 /* Check and see if we need to move to a new column */
1116                 if (y != GET_VIEW_CONSTANT (container, desktop_pad_vertical) && y + icon_height_for_bound_check > height) {
1117                     break;
1118                 }
1119 
1120                 if (max_width < icon_width) {
1121                     max_width = icon_width;
1122                 }
1123 
1124                 y += icon_height + GET_VIEW_CONSTANT (container, desktop_pad_vertical);
1125             }
1126 
1127             y = GET_VIEW_CONSTANT (container, desktop_pad_vertical);
1128 
1129             center_x = x + max_width / 2;
1130             column_width = max_width;
1131             if (should_snap) {
1132                 /* Find the grid column to center on */
1133                 center_x = SNAP_CEIL_HORIZONTAL (center_x);
1134                 column_width = (center_x - x) + (max_width / 2);
1135             }
1136 
1137             /* Lay out column */
1138             for (p = icons; p != NULL; p = p->next) {
1139                 icon = p->data;
1140                 nemo_icon_container_icon_get_bounding_box (container, icon, &x1, &y1, &x2, &y2,
1141                                BOUNDS_USAGE_FOR_LAYOUT);
1142                 icon_height = y2 - y1;
1143 
1144                 nemo_icon_container_icon_get_bounding_box (container, icon, NULL, &y1, NULL, &y2,
1145                                BOUNDS_USAGE_FOR_ENTIRE_ITEM);
1146                 icon_height_for_bound_check = y2 - y1;
1147 
1148                 icon_rect = nemo_icon_canvas_item_get_icon_rectangle (icon->item);
1149 
1150                 if (should_snap) {
1151                     baseline = y + (icon_rect.y1 - icon_rect.y0);
1152                     baseline = SNAP_CEIL_VERTICAL (baseline);
1153                     y = baseline - (icon_rect.y1 - icon_rect.y0);
1154                 }
1155 
1156                 /* Check and see if we need to move to a new column */
1157                 if (y != GET_VIEW_CONSTANT (container, desktop_pad_vertical) && y > height - icon_height_for_bound_check &&
1158                     /* Make sure we lay out at least one icon per column, to make progress */
1159                     p != icons) {
1160                     x += column_width + GET_VIEW_CONSTANT (container, desktop_pad_horizontal);
1161                     break;
1162                 }
1163 
1164                 nemo_icon_container_icon_set_position (container, icon,
1165                            center_x - (icon_rect.x1 - icon_rect.x0) / 2,
1166                            y);
1167 
1168                 icon->saved_ltr_x = icon->x;
1169                 y += icon_height + GET_VIEW_CONSTANT (container, desktop_pad_vertical);
1170             }
1171             icons = p;
1172         }
1173     }
1174 
1175     /* These modes are special. We freeze all of our positions
1176      * after we do the layout.
1177      */
1178     /* FIXME bugzilla.gnome.org 42478:
1179      * This should not be tied to the direction of layout.
1180      * It should be a separate switch.
1181      */
1182     nemo_icon_container_freeze_icon_positions (container);
1183 }
1184 
1185 static void
nemo_icon_view_container_lay_down_icons(NemoIconContainer * container,GList * icons,double start_y)1186 nemo_icon_view_container_lay_down_icons (NemoIconContainer *container, GList *icons, double start_y)
1187 {
1188     switch (container->details->layout_mode)
1189     {
1190     case NEMO_ICON_LAYOUT_L_R_T_B:
1191     case NEMO_ICON_LAYOUT_R_L_T_B:
1192         lay_down_icons_horizontal (container, icons, start_y);
1193         break;
1194 
1195     case NEMO_ICON_LAYOUT_T_B_L_R:
1196     case NEMO_ICON_LAYOUT_T_B_R_L:
1197         if (nemo_icon_container_get_is_desktop (container)) {
1198             lay_down_icons_vertical_desktop (container, icons);
1199         } else {
1200             lay_down_icons_vertical (container, icons, start_y);
1201         }
1202         break;
1203 
1204     default:
1205         g_assert_not_reached ();
1206     }
1207 }
1208 
1209 
1210 static void
get_max_icon_dimensions(GList * icon_start,GList * icon_end,double * max_icon_width,double * max_icon_height,double * max_text_width,double * max_text_height,double * max_bounds_height)1211 get_max_icon_dimensions (GList *icon_start,
1212              GList *icon_end,
1213              double *max_icon_width,
1214              double *max_icon_height,
1215              double *max_text_width,
1216              double *max_text_height,
1217              double *max_bounds_height)
1218 {
1219     NemoIcon *icon;
1220     EelDRect icon_bounds;
1221     EelDRect text_bounds;
1222     GList *p;
1223     double y1, y2;
1224 
1225     *max_icon_width = *max_text_width = 0.0;
1226     *max_icon_height = *max_text_height = 0.0;
1227     *max_bounds_height = 0.0;
1228 
1229     /* Would it be worth caching these bounds for the next loop? */
1230     for (p = icon_start; p != icon_end; p = p->next) {
1231         icon = p->data;
1232 
1233         icon_bounds = nemo_icon_canvas_item_get_icon_rectangle (icon->item);
1234         *max_icon_width = MAX (*max_icon_width, ceil (icon_bounds.x1 - icon_bounds.x0));
1235         *max_icon_height = MAX (*max_icon_height, ceil (icon_bounds.y1 - icon_bounds.y0));
1236 
1237         text_bounds = nemo_icon_canvas_item_get_text_rectangle (icon->item, TRUE);
1238         *max_text_width = MAX (*max_text_width, ceil (text_bounds.x1 - text_bounds.x0));
1239         *max_text_height = MAX (*max_text_height, ceil (text_bounds.y1 - text_bounds.y0));
1240 
1241         nemo_icon_canvas_item_get_bounds_for_layout (icon->item,
1242                                  NULL, &y1,
1243                                  NULL, &y2);
1244         *max_bounds_height = MAX (*max_bounds_height, y2 - y1);
1245     }
1246 }
1247 
1248 static void
snap_position(NemoIconContainer * container,NemoIcon * icon,int * x,int * y)1249 snap_position (NemoIconContainer *container,
1250            NemoIcon *icon,
1251            int *x, int *y)
1252 {
1253     int center_x;
1254     int baseline_y;
1255     int icon_width;
1256     int icon_height;
1257     int total_width;
1258     int total_height;
1259     EelDRect icon_position;
1260     GtkAllocation allocation;
1261 
1262     icon_position = nemo_icon_canvas_item_get_icon_rectangle (icon->item);
1263     icon_width = icon_position.x1 - icon_position.x0;
1264     icon_height = icon_position.y1 - icon_position.y0;
1265 
1266     gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
1267     total_width = nemo_icon_container_get_canvas_width (container, allocation);
1268     total_height = nemo_icon_container_get_canvas_height (container, allocation);
1269 
1270     if (nemo_icon_container_is_layout_rtl (container))
1271         *x = nemo_icon_container_get_mirror_x_position (container, icon, *x);
1272 
1273     if (*x + icon_width / 2 < GET_VIEW_CONSTANT (container, desktop_pad_horizontal) + GET_VIEW_CONSTANT (container, snap_size_x)) {
1274         *x = GET_VIEW_CONSTANT (container, desktop_pad_horizontal) + GET_VIEW_CONSTANT (container, snap_size_x) - icon_width / 2;
1275     }
1276 
1277     if (*x + icon_width / 2 > total_width - (GET_VIEW_CONSTANT (container, desktop_pad_horizontal) + GET_VIEW_CONSTANT (container, snap_size_x))) {
1278         *x = total_width - (GET_VIEW_CONSTANT (container, desktop_pad_horizontal) + GET_VIEW_CONSTANT (container, snap_size_x) + (icon_width / 2));
1279     }
1280 
1281     if (*y + icon_height < GET_VIEW_CONSTANT (container, desktop_pad_vertical) + GET_VIEW_CONSTANT (container, snap_size_y)) {
1282         *y = GET_VIEW_CONSTANT (container, desktop_pad_vertical) + GET_VIEW_CONSTANT (container, snap_size_y) - icon_height;
1283     }
1284 
1285     if (*y + icon_height > total_height - (GET_VIEW_CONSTANT (container, desktop_pad_vertical) + GET_VIEW_CONSTANT (container, snap_size_y))) {
1286         *y = total_height - (GET_VIEW_CONSTANT (container, desktop_pad_vertical) + GET_VIEW_CONSTANT (container, snap_size_y) + (icon_height / 2));
1287     }
1288 
1289     center_x = *x + icon_width / 2;
1290     *x = SNAP_NEAREST_HORIZONTAL (center_x) - (icon_width / 2);
1291     if (nemo_icon_container_is_layout_rtl (container)) {
1292         *x = nemo_icon_container_get_mirror_x_position (container, icon, *x);
1293     }
1294 
1295 
1296     /* Find the grid position vertically and place on the proper baseline */
1297     baseline_y = *y + icon_height;
1298     baseline_y = SNAP_NEAREST_VERTICAL (baseline_y);
1299     *y = baseline_y - icon_height;
1300 }
1301 
1302 static int
compare_icons_by_position(gconstpointer a,gconstpointer b)1303 compare_icons_by_position (gconstpointer a, gconstpointer b)
1304 {
1305     NemoIcon *icon_a, *icon_b;
1306     int x1, y1, x2, y2;
1307     int center_a;
1308     int center_b;
1309 
1310     icon_a = (NemoIcon*)a;
1311     icon_b = (NemoIcon*)b;
1312 
1313     nemo_icon_view_container_icon_get_bounding_box (icon_a, &x1, &y1, &x2, &y2,
1314                    BOUNDS_USAGE_FOR_DISPLAY);
1315     center_a = x1 + (x2 - x1) / 2;
1316     nemo_icon_view_container_icon_get_bounding_box (icon_b, &x1, &y1, &x2, &y2,
1317                    BOUNDS_USAGE_FOR_DISPLAY);
1318     center_b = x1 + (x2 - x1) / 2;
1319 
1320     return center_a == center_b ?
1321         icon_a->y - icon_b->y :
1322         center_a - center_b;
1323 }
1324 
1325 /* x, y are the top-left coordinates of the icon. */
1326 static void
nemo_icon_view_container_icon_set_position(NemoIconContainer * container,NemoIcon * icon,double x,double y)1327 nemo_icon_view_container_icon_set_position (NemoIconContainer *container,
1328                                             NemoIcon          *icon,
1329                                             double             x,
1330                                             double             y)
1331 {
1332     double pixels_per_unit;
1333     int container_left, container_top, container_right, container_bottom;
1334     int x1, x2, y1, y2;
1335     int container_x, container_y, container_width, container_height;
1336     EelDRect icon_bounds;
1337     int item_width, item_height;
1338     int height_above, width_left;
1339     int min_x, max_x, min_y, max_y;
1340 
1341     if (icon->x == x && icon->y == y) {
1342         return;
1343     }
1344 
1345     if (icon == nemo_icon_container_get_icon_being_renamed (container)) {
1346         nemo_icon_container_end_renaming_mode (container, TRUE);
1347     }
1348 
1349     if (nemo_icon_container_get_is_fixed_size (container)) {
1350         GtkAllocation alloc;
1351 
1352         gtk_widget_get_allocation (GTK_WIDGET (container), &alloc);
1353         container_x = alloc.x;
1354         container_y = alloc.y;
1355         container_width = alloc.width - container->details->left_margin - container->details->right_margin;
1356         container_height = alloc.height - container->details->top_margin - container->details->bottom_margin;
1357         pixels_per_unit = EEL_CANVAS (container)->pixels_per_unit;
1358         /* Clip the position of the icon within our desktop bounds */
1359         container_left = container_x / pixels_per_unit;
1360         container_top =  container_y / pixels_per_unit;
1361         container_right = container_left + container_width / pixels_per_unit;
1362         container_bottom = container_top + container_height / pixels_per_unit;
1363 
1364         nemo_icon_container_icon_get_bounding_box (container, icon, &x1, &y1, &x2, &y2,
1365                        BOUNDS_USAGE_FOR_ENTIRE_ITEM);
1366         item_width = x2 - x1;
1367         item_height = y2 - y1;
1368 
1369         icon_bounds = nemo_icon_canvas_item_get_icon_rectangle (icon->item);
1370 
1371         /* determine icon rectangle relative to item rectangle */
1372         height_above = icon_bounds.y0 - y1;
1373         width_left = icon_bounds.x0 - x1;
1374 
1375         min_x = container_left + GET_VIEW_CONSTANT (container, desktop_pad_horizontal) + width_left;
1376         max_x = container_right - GET_VIEW_CONSTANT (container, desktop_pad_horizontal) - item_width + width_left;
1377         x = CLAMP (x, min_x, max_x);
1378 
1379         min_y = container_top + height_above + GET_VIEW_CONSTANT (container, desktop_pad_vertical);
1380         max_y = container_bottom - GET_VIEW_CONSTANT (container, desktop_pad_vertical) - item_height + height_above;
1381         y = CLAMP (y, min_y, max_y);
1382     }
1383 
1384     if (icon->x == ICON_UNPOSITIONED_VALUE) {
1385         icon->x = 0;
1386     }
1387     if (icon->y == ICON_UNPOSITIONED_VALUE) {
1388         icon->y = 0;
1389     }
1390 
1391     eel_canvas_item_move (EEL_CANVAS_ITEM (icon->item),
1392                 x - icon->x,
1393                 y - icon->y);
1394 
1395     icon->x = x;
1396     icon->y = y;
1397 }
1398 
1399 static void
nemo_icon_view_container_move_icon(NemoIconContainer * container,NemoIcon * icon,int x,int y,double scale,gboolean raise,gboolean snap,gboolean update_position)1400 nemo_icon_view_container_move_icon (NemoIconContainer *container,
1401                    NemoIcon *icon,
1402                    int x, int y,
1403                    double scale,
1404                    gboolean raise,
1405                    gboolean snap,
1406                    gboolean update_position)
1407 {
1408     NemoIconContainerDetails *details;
1409     gboolean emit_signal;
1410     NemoIconPosition position;
1411 
1412     details = container->details;
1413 
1414     emit_signal = FALSE;
1415 
1416     if (icon == nemo_icon_container_get_icon_being_renamed (container)) {
1417         nemo_icon_container_end_renaming_mode (container, TRUE);
1418     }
1419 
1420     if (scale != icon->scale) {
1421         icon->scale = scale;
1422         nemo_icon_container_update_icon (container, icon);
1423         if (update_position) {
1424             nemo_icon_container_redo_layout (container);
1425             emit_signal = TRUE;
1426         }
1427     }
1428 
1429     if (!details->auto_layout) {
1430         if (details->keep_aligned && snap) {
1431             snap_position (container, icon, &x, &y);
1432         }
1433 
1434         if (x != icon->x || y != icon->y) {
1435             nemo_icon_container_icon_set_position (container, icon, x, y);
1436             emit_signal = update_position;
1437         }
1438 
1439         icon->saved_ltr_x = nemo_icon_container_is_layout_rtl (container) ? nemo_icon_container_get_mirror_x_position (container, icon, icon->x) : icon->x;
1440     }
1441 
1442     if (emit_signal) {
1443         position.x = icon->saved_ltr_x;
1444         position.y = icon->y;
1445         position.scale = scale;
1446         position.monitor =  nemo_desktop_utils_get_monitor_for_widget (GTK_WIDGET (container));
1447         g_signal_emit_by_name (container, "icon_position_changed", icon->data, &position);
1448     }
1449 
1450     if (raise) {
1451         nemo_icon_container_icon_raise (container, icon);
1452     }
1453 
1454     /* FIXME bugzilla.gnome.org 42474:
1455      * Handling of the scroll region is inconsistent here. In
1456      * the scale-changing case, redo_layout is called, which updates the
1457      * scroll region appropriately. In other cases, it's up to the
1458      * caller to make sure the scroll region is updated. This could
1459      * lead to hard-to-track-down bugs.
1460      */
1461 }
1462 
1463 static void
icon_get_size(NemoIconContainer * container,NemoIcon * icon,guint * size)1464 icon_get_size (NemoIconContainer *container,
1465            NemoIcon *icon,
1466            guint *size)
1467 {
1468     if (size != NULL) {
1469         *size = MAX (nemo_get_icon_size_for_zoom_level (container->details->zoom_level)
1470                    * icon->scale, NEMO_ICON_SIZE_SMALLEST);
1471     }
1472 }
1473 
1474 static void
nemo_icon_view_container_update_icon(NemoIconContainer * container,NemoIcon * icon,gboolean visible)1475 nemo_icon_view_container_update_icon (NemoIconContainer *container,
1476                                       NemoIcon          *icon,
1477                                       gboolean           visible)
1478 {
1479     NemoIconContainerDetails *details;
1480     guint icon_size;
1481     guint min_image_size, max_image_size;
1482     NemoIconInfo *icon_info;
1483     GdkPixbuf *pixbuf;
1484     char *editable_text, *additional_text;
1485     gboolean has_open_window;
1486     gboolean pinned;
1487     gboolean fav_unavailable;
1488 
1489     if (icon == NULL) {
1490         return;
1491     }
1492 
1493     details = container->details;
1494 
1495     /* compute the maximum size based on the scale factor */
1496     min_image_size = MINIMUM_IMAGE_SIZE * EEL_CANVAS (container)->pixels_per_unit;
1497     max_image_size = MAX (MAXIMUM_IMAGE_SIZE * EEL_CANVAS (container)->pixels_per_unit, NEMO_ICON_MAXIMUM_SIZE);
1498 
1499     /* Get the appropriate images for the file. */
1500     if (container->details->forced_icon_size > 0) {
1501         icon_size = container->details->forced_icon_size;
1502     } else {
1503         icon_get_size (container, icon, &icon_size);
1504     }
1505 
1506     icon_size = MAX (icon_size, min_image_size);
1507     icon_size = MIN (icon_size, max_image_size);
1508 
1509     DEBUG ("Icon size, getting for size %d", icon_size);
1510 
1511     /* Get the icons. */
1512     icon_info = nemo_icon_container_get_icon_images (container,
1513                                                      icon->data,
1514                                                      icon_size,
1515                                                      icon == details->drop_target,
1516                                                      &has_open_window,
1517                                                      visible);
1518 
1519     if (container->details->forced_icon_size > 0) {
1520         gint scale_factor;
1521 
1522         scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (container));
1523         pixbuf = nemo_icon_info_get_pixbuf_at_size (icon_info, icon_size * scale_factor);
1524     } else {
1525         pixbuf = nemo_icon_info_get_pixbuf (icon_info);
1526     }
1527 
1528     nemo_icon_info_unref (icon_info);
1529 
1530     nemo_icon_container_get_icon_text (container,
1531                            icon->data,
1532                            &editable_text,
1533                            &additional_text,
1534                            &pinned,
1535                            &fav_unavailable,
1536                            FALSE);
1537 
1538 
1539     /* If name of icon being renamed was changed from elsewhere, end renaming mode.
1540      * Alternatively, we could replace the characters in the editable text widget
1541      * with the new name, but that could cause timing problems if the user just
1542      * happened to be typing at that moment.
1543      */
1544     if (icon == nemo_icon_container_get_icon_being_renamed (container) &&
1545         g_strcmp0 (editable_text,
1546                nemo_icon_canvas_item_get_editable_text (icon->item)) != 0) {
1547         nemo_icon_container_end_renaming_mode (container, FALSE);
1548     }
1549 
1550     eel_canvas_item_set (EEL_CANVAS_ITEM (icon->item),
1551                  "editable_text", editable_text,
1552                  "additional_text", additional_text,
1553                  "highlighted_for_drop", icon == details->drop_target,
1554                  "pinned", pinned,
1555                  "fav-unavailable", fav_unavailable,
1556                  NULL);
1557 
1558     nemo_icon_canvas_item_set_image (icon->item, pixbuf);
1559 
1560     /* Let the pixbufs go. */
1561     g_object_unref (pixbuf);
1562 
1563     g_free (editable_text);
1564     g_free (additional_text);
1565 }
1566 
1567 static void
find_empty_location(NemoIconContainer * container,NemoPlacementGrid * grid,NemoIcon * icon,int start_x,int start_y,int * x,int * y)1568 find_empty_location (NemoIconContainer *container,
1569              NemoPlacementGrid *grid,
1570              NemoIcon *icon,
1571              int start_x,
1572              int start_y,
1573              int *x,
1574              int *y)
1575 {
1576     double icon_width, icon_height;
1577     int canvas_width;
1578     int canvas_height;
1579     int height_for_bound_check;
1580     EelIRect icon_position;
1581     EelDRect pixbuf_rect;
1582     gboolean collision;
1583     GtkAllocation allocation;
1584 
1585     /* Get container dimensions */
1586     gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
1587     canvas_width  = nemo_icon_container_get_canvas_width (container, allocation);
1588     canvas_height = nemo_icon_container_get_canvas_height (container, allocation);
1589 
1590     nemo_icon_container_icon_get_bounding_box (container, icon,
1591                    &icon_position.x0, &icon_position.y0,
1592                    &icon_position.x1, &icon_position.y1,
1593                    BOUNDS_USAGE_FOR_LAYOUT);
1594     icon_width = icon_position.x1 - icon_position.x0;
1595     icon_height = icon_position.y1 - icon_position.y0;
1596 
1597     nemo_icon_container_icon_get_bounding_box (container, icon,
1598                    NULL, &icon_position.y0,
1599                    NULL, &icon_position.y1,
1600                    BOUNDS_USAGE_FOR_ENTIRE_ITEM);
1601     height_for_bound_check = icon_position.y1 - icon_position.y0;
1602 
1603     pixbuf_rect = nemo_icon_canvas_item_get_icon_rectangle (icon->item);
1604 
1605     /* Start the icon on a grid location */
1606     snap_position (container, icon, &start_x, &start_y);
1607 
1608     icon_position.x0 = start_x;
1609     icon_position.y0 = start_y;
1610     icon_position.x1 = icon_position.x0 + icon_width;
1611     icon_position.y1 = icon_position.y0 + icon_height;
1612 
1613     do {
1614         EelIRect grid_position;
1615         gboolean need_new_column;
1616 
1617         collision = FALSE;
1618 
1619         nemo_placement_grid_canvas_position_to_grid_position (grid,
1620                           icon_position,
1621                           &grid_position);
1622 
1623         need_new_column = icon_position.y0 + height_for_bound_check + GET_VIEW_CONSTANT (container, desktop_pad_vertical) > canvas_height;
1624 
1625         if (need_new_column ||
1626             !nemo_placement_grid_position_is_free (grid, grid_position)) {
1627             icon_position.y0 += GET_VIEW_CONSTANT (container, snap_size_y);
1628             icon_position.y1 = icon_position.y0 + icon_height;
1629 
1630             if (need_new_column) {
1631                 /* Move to the next column */
1632                 icon_position.y0 = GET_VIEW_CONSTANT (container, desktop_pad_vertical) + GET_VIEW_CONSTANT (container, snap_size_y) - (pixbuf_rect.y1 - pixbuf_rect.y0);
1633                 while (icon_position.y0 < GET_VIEW_CONSTANT (container, desktop_pad_vertical)) {
1634                     icon_position.y0 += GET_VIEW_CONSTANT (container, snap_size_y);
1635                 }
1636                 icon_position.y1 = icon_position.y0 + icon_height;
1637 
1638                 icon_position.x0 += GET_VIEW_CONSTANT (container, snap_size_x);
1639                 icon_position.x1 = icon_position.x0 + icon_width;
1640             }
1641 
1642             collision = TRUE;
1643         }
1644     } while (collision && (icon_position.x1 < canvas_width));
1645 
1646     *x = icon_position.x0;
1647     *y = icon_position.y0;
1648 }
1649 
1650 static void
nemo_icon_view_container_align_icons(NemoIconContainer * container)1651 nemo_icon_view_container_align_icons (NemoIconContainer *container)
1652 {
1653     GList *unplaced_icons;
1654     GList *l;
1655     NemoPlacementGrid *grid;
1656 
1657     unplaced_icons = g_list_copy (container->details->icons);
1658 
1659     unplaced_icons = g_list_sort (unplaced_icons,
1660                       compare_icons_by_position);
1661 
1662     if (nemo_icon_container_is_layout_rtl (container)) {
1663         unplaced_icons = g_list_reverse (unplaced_icons);
1664     }
1665 
1666     grid = nemo_placement_grid_new (container, TRUE);
1667 
1668     if (!grid) {
1669         g_list_free (unplaced_icons);
1670         return;
1671     }
1672 
1673     for (l = unplaced_icons; l != NULL; l = l->next) {
1674         NemoIcon *icon;
1675         int x, y;
1676 
1677         icon = l->data;
1678         x = icon->saved_ltr_x;
1679         y = icon->y;
1680         find_empty_location (container, grid,
1681                      icon, x, y, &x, &y);
1682 
1683         nemo_icon_container_icon_set_position (container, icon, x, y);
1684         icon->saved_ltr_x = icon->x;
1685         nemo_placement_grid_mark_icon (grid, icon);
1686     }
1687 
1688     g_list_free (unplaced_icons);
1689 
1690     nemo_placement_grid_free (grid);
1691 
1692     if (nemo_icon_container_is_layout_rtl (container)) {
1693         nemo_icon_container_set_rtl_positions (container);
1694     }
1695 }
1696 
1697 static void
nemo_icon_view_container_reload_icon_positions(NemoIconContainer * container)1698 nemo_icon_view_container_reload_icon_positions (NemoIconContainer *container)
1699 {
1700     GList *p, *no_position_icons;
1701     NemoIcon *icon;
1702     gboolean have_stored_position;
1703     NemoIconPosition position;
1704     EelDRect bounds;
1705     double bottom;
1706     EelCanvasItem *item;
1707 
1708     g_assert (!container->details->auto_layout);
1709 
1710     nemo_icon_container_resort (container);
1711 
1712     no_position_icons = NULL;
1713 
1714     /* Place all the icons with positions. */
1715     bottom = 0;
1716     for (p = container->details->icons; p != NULL; p = p->next) {
1717         icon = p->data;
1718 
1719         have_stored_position = get_stored_icon_position (container,
1720                                                          icon->data,
1721                                                          &position);
1722 
1723         if (have_stored_position) {
1724             nemo_icon_container_icon_set_position (container, icon, position.x, position.y);
1725             item = EEL_CANVAS_ITEM (icon->item);
1726             nemo_icon_canvas_item_get_bounds_for_layout (icon->item,
1727                                      &bounds.x0,
1728                                      &bounds.y0,
1729                                      &bounds.x1,
1730                                      &bounds.y1);
1731             eel_canvas_item_i2w (item->parent,
1732                          &bounds.x0,
1733                          &bounds.y0);
1734             eel_canvas_item_i2w (item->parent,
1735                          &bounds.x1,
1736                          &bounds.y1);
1737             if (bounds.y1 > bottom) {
1738                 bottom = bounds.y1;
1739             }
1740         } else {
1741             no_position_icons = g_list_prepend (no_position_icons, icon);
1742         }
1743     }
1744     no_position_icons = g_list_reverse (no_position_icons);
1745 
1746     /* Place all the other icons. */
1747     NEMO_ICON_CONTAINER_GET_CLASS (container)->lay_down_icons (container, no_position_icons, bottom + GET_VIEW_CONSTANT (container, icon_pad_bottom));
1748     g_list_free (no_position_icons);
1749 }
1750 
1751 static gboolean
assign_icon_position(NemoIconContainer * container,NemoIcon * icon)1752 assign_icon_position (NemoIconContainer *container,
1753               NemoIcon *icon)
1754 {
1755     gboolean have_stored_position;
1756     NemoIconPosition position;
1757 
1758     /* Get the stored position. */
1759     have_stored_position = FALSE;
1760     position.scale = 1.0;
1761 
1762     have_stored_position = get_stored_icon_position (container,
1763                                                      icon->data,
1764                                                      &position);
1765 
1766     icon->scale = position.scale;
1767     if (!container->details->auto_layout) {
1768         if (have_stored_position) {
1769             nemo_icon_container_icon_set_position (container, icon, position.x, position.y);
1770             icon->saved_ltr_x = icon->x;
1771         } else {
1772             return FALSE;
1773         }
1774     }
1775     return TRUE;
1776 }
1777 
1778 static void
nemo_icon_view_container_finish_adding_new_icons(NemoIconContainer * container)1779 nemo_icon_view_container_finish_adding_new_icons (NemoIconContainer *container)
1780 {
1781     GList *p, *new_icons, *no_position_icons, *semi_position_icons;
1782     NemoIcon *icon;
1783     double bottom;
1784     gint current_monitor;
1785 
1786     new_icons = container->details->new_icons;
1787     container->details->new_icons = NULL;
1788 
1789     current_monitor = nemo_desktop_utils_get_monitor_for_widget (GTK_WIDGET(container));
1790 
1791     /* Position most icons (not unpositioned manual-layout icons). */
1792     new_icons = g_list_reverse (new_icons);
1793     no_position_icons = semi_position_icons = NULL;
1794     for (p = new_icons; p != NULL; p = p->next) {
1795         icon = p->data;
1796         nemo_icon_container_update_icon (container, icon);
1797         if (icon->has_lazy_position || nemo_icon_container_icon_is_new_for_monitor (container, icon, current_monitor)) {
1798             assign_icon_position (container, icon);
1799             semi_position_icons = g_list_prepend (semi_position_icons, icon);
1800         } else if (!assign_icon_position (container, icon)) {
1801             no_position_icons = g_list_prepend (no_position_icons, icon);
1802         }
1803 
1804         nemo_icon_container_finish_adding_icon (container, icon);
1805     }
1806     g_list_free (new_icons);
1807 
1808     if (semi_position_icons != NULL) {
1809         NemoPlacementGrid *grid;
1810         time_t now;
1811         gboolean dummy;
1812 
1813         g_assert (!container->details->auto_layout);
1814 
1815         semi_position_icons = g_list_reverse (semi_position_icons);
1816 
1817         /* This is currently only used on the desktop.
1818          * Thus, we pass FALSE for tight, like lay_down_icons_tblr */
1819         grid = nemo_placement_grid_new (container, FALSE);
1820 
1821         for (p = container->details->icons; p != NULL; p = p->next) {
1822             icon = p->data;
1823 
1824             if (nemo_icon_container_icon_is_positioned (icon) && !icon->has_lazy_position) {
1825                 nemo_placement_grid_mark_icon (grid, icon);
1826             }
1827         }
1828 
1829         now = time (NULL);
1830 
1831         for (p = semi_position_icons; p != NULL; p = p->next) {
1832             NemoIconPosition position;
1833             int x, y;
1834 
1835             icon = p->data;
1836             x = icon->x;
1837             y = icon->y;
1838 
1839             find_empty_location (container, grid,
1840                          icon, x, y, &x, &y);
1841 
1842             nemo_icon_container_icon_set_position (container, icon, x, y);
1843 
1844             position.x = icon->x;
1845             position.y = icon->y;
1846             position.scale = icon->scale;
1847             position.monitor = current_monitor;
1848             nemo_placement_grid_mark_icon (grid, icon);
1849             g_signal_emit_by_name (container, "icon_position_changed",
1850                                    icon->data, &position);
1851             g_signal_emit_by_name (container, "store_layout_timestamp",
1852                                    icon->data, &now, &dummy);
1853 
1854             /* ensure that next time we run this code, the formerly semi-positioned
1855              * icons are treated as being positioned. */
1856             icon->has_lazy_position = FALSE;
1857         }
1858 
1859         nemo_placement_grid_free (grid);
1860 
1861         g_list_free (semi_position_icons);
1862     }
1863 
1864     /* Position the unpositioned manual layout icons. */
1865     if (no_position_icons != NULL) {
1866         g_assert (!container->details->auto_layout);
1867 
1868         nemo_icon_container_sort_icons (container, &no_position_icons);
1869         if (nemo_icon_container_get_is_desktop (container)) {
1870             NEMO_ICON_CONTAINER_GET_CLASS (container)->lay_down_icons (container, no_position_icons, GET_VIEW_CONSTANT (container, container_pad_top));
1871         } else {
1872             nemo_icon_container_get_all_icon_bounds (container, NULL, NULL, NULL, &bottom, BOUNDS_USAGE_FOR_LAYOUT);
1873             NEMO_ICON_CONTAINER_GET_CLASS (container)->lay_down_icons (container, no_position_icons, bottom + GET_VIEW_CONSTANT (container, icon_pad_bottom));
1874         }
1875         g_list_free (no_position_icons);
1876     }
1877 
1878     if (container->details->store_layout_timestamps_when_finishing_new_icons) {
1879         nemo_icon_container_store_layout_timestamps_now (container);
1880         container->details->store_layout_timestamps_when_finishing_new_icons = FALSE;
1881     }
1882 }
1883 
1884 static void
nemo_icon_view_container_set_zoom_level(NemoIconContainer * container,gint new_level)1885 nemo_icon_view_container_set_zoom_level (NemoIconContainer *container, gint new_level)
1886 {
1887     NemoIconContainerDetails *details;
1888     int pinned_level;
1889     double pixels_per_unit;
1890 
1891     details = container->details;
1892 
1893     nemo_icon_container_end_renaming_mode (container, TRUE);
1894 
1895     pinned_level = new_level;
1896     if (pinned_level < NEMO_ZOOM_LEVEL_SMALLEST) {
1897         pinned_level = NEMO_ZOOM_LEVEL_SMALLEST;
1898     } else if (pinned_level > NEMO_ZOOM_LEVEL_LARGEST) {
1899         pinned_level = NEMO_ZOOM_LEVEL_LARGEST;
1900     }
1901 
1902     if (pinned_level == details->zoom_level) {
1903         return;
1904     }
1905 
1906     details->zoom_level = pinned_level;
1907 
1908     pixels_per_unit = (double) nemo_get_icon_size_for_zoom_level (pinned_level) / NEMO_ICON_SIZE_STANDARD;
1909     eel_canvas_set_pixels_per_unit (EEL_CANVAS (container), pixels_per_unit);
1910 }
1911 
1912 static int text_ellipsis_limits[NEMO_ZOOM_LEVEL_N_ENTRIES];
1913 static int desktop_text_ellipsis_limit;
1914 
1915 static gboolean
get_text_ellipsis_limit_for_zoom(char ** strs,const char * zoom_level,int * limit)1916 get_text_ellipsis_limit_for_zoom (char **strs,
1917                   const char *zoom_level,
1918                   int *limit)
1919 {
1920     char **p;
1921     char *str;
1922     gboolean success;
1923 
1924     success = FALSE;
1925 
1926     /* default */
1927     *limit = 3;
1928 
1929     if (zoom_level != NULL) {
1930         str = g_strdup_printf ("%s:%%d", zoom_level);
1931     } else {
1932         str = g_strdup ("%d");
1933     }
1934 
1935     if (strs != NULL) {
1936         for (p = strs; *p != NULL; p++) {
1937             if (sscanf (*p, str, limit)) {
1938                 success = TRUE;
1939             }
1940         }
1941     }
1942 
1943     g_free (str);
1944 
1945     return success;
1946 }
1947 
1948 static const char * zoom_level_names[] = {
1949     "smallest",
1950     "smaller",
1951     "small",
1952     "standard",
1953     "large",
1954     "larger",
1955     "largest"
1956 };
1957 
1958 static void
text_ellipsis_limit_changed_callback(gpointer callback_data)1959 text_ellipsis_limit_changed_callback (gpointer callback_data)
1960 {
1961     char **pref;
1962     unsigned int i;
1963     int one_limit;
1964 
1965     pref = g_settings_get_strv (nemo_icon_view_preferences,
1966                     NEMO_PREFERENCES_ICON_VIEW_TEXT_ELLIPSIS_LIMIT);
1967 
1968     /* set default */
1969     get_text_ellipsis_limit_for_zoom (pref, NULL, &one_limit);
1970     for (i = 0; i < NEMO_ZOOM_LEVEL_N_ENTRIES; i++) {
1971         text_ellipsis_limits[i] = one_limit;
1972     }
1973 
1974     /* override for each zoom level */
1975     for (i = 0; i < G_N_ELEMENTS(zoom_level_names); i++) {
1976         if (get_text_ellipsis_limit_for_zoom (pref,
1977                               zoom_level_names[i],
1978                               &one_limit)) {
1979             text_ellipsis_limits[i] = one_limit;
1980         }
1981     }
1982 
1983     g_strfreev (pref);
1984 }
1985 
1986 static void
desktop_text_ellipsis_limit_changed_callback(gpointer callback_data)1987 desktop_text_ellipsis_limit_changed_callback (gpointer callback_data)
1988 {
1989     int pref;
1990 
1991     pref = g_settings_get_int (nemo_desktop_preferences, NEMO_PREFERENCES_DESKTOP_TEXT_ELLIPSIS_LIMIT);
1992     desktop_text_ellipsis_limit = pref;
1993 }
1994 
1995 static gchar *
on_get_tooltip_text(NemoIconContainer * container,NemoFile * file,gpointer user_data)1996 on_get_tooltip_text (NemoIconContainer *container,
1997                      NemoFile          *file,
1998                      gpointer           user_data)
1999 {
2000     gboolean is_desktop, show_tooltip;
2001     gchar *tooltip_text = NULL;
2002 
2003     is_desktop = container->details->is_desktop;
2004 
2005     show_tooltip = (container->details->show_desktop_tooltips && is_desktop) ||
2006                    (container->details->show_icon_view_tooltips && !is_desktop);
2007 
2008     if (show_tooltip) {
2009         tooltip_text = nemo_file_construct_tooltip (file, container->details->tooltip_flags);
2010     }
2011 
2012     return tooltip_text;
2013 }
2014 
2015 static gint
nemo_icon_view_container_get_max_layout_lines_for_pango(NemoIconContainer * container)2016 nemo_icon_view_container_get_max_layout_lines_for_pango (NemoIconContainer  *container)
2017 {
2018     int limit;
2019 
2020     if (nemo_icon_container_get_is_desktop (container)) {
2021         limit = desktop_text_ellipsis_limit;
2022     } else {
2023         limit = text_ellipsis_limits[container->details->zoom_level];
2024     }
2025 
2026     if (limit <= 0) {
2027         return G_MININT;
2028     }
2029 
2030     return -limit;
2031 }
2032 
2033 static gint
nemo_icon_view_container_get_max_layout_lines(NemoIconContainer * container)2034 nemo_icon_view_container_get_max_layout_lines (NemoIconContainer  *container)
2035 {
2036     int limit;
2037 
2038     if (nemo_icon_container_get_is_desktop (container)) {
2039         limit = desktop_text_ellipsis_limit;
2040     } else {
2041         limit = text_ellipsis_limits[container->details->zoom_level];
2042     }
2043 
2044     if (limit <= 0) {
2045         return 3;
2046     }
2047 
2048     return limit;
2049 }
2050 
2051 static gint
nemo_icon_view_container_get_additional_text_line_count(NemoIconContainer * container)2052 nemo_icon_view_container_get_additional_text_line_count (NemoIconContainer *container)
2053 {
2054     GQuark *attributes;
2055     gint len, i, real_count;
2056 
2057     attributes = nemo_icon_view_container_get_icon_text_attribute_names (container, &len);
2058 
2059     i = 0;
2060     real_count = 0;
2061 
2062     for (i = 0; i < len; ++i) {
2063         if (attributes[i] == attribute_none_q) {
2064             continue;
2065         }
2066 
2067         real_count++;
2068     }
2069 
2070     return real_count;
2071 }
2072 
2073 static void
finalize(GObject * object)2074 finalize (GObject *object)
2075 {
2076     g_signal_handlers_disconnect_by_func (nemo_icon_view_preferences,
2077                                           text_ellipsis_limit_changed_callback,
2078                                           NULL);
2079 
2080     g_signal_handlers_disconnect_by_func (nemo_desktop_preferences,
2081                                           desktop_text_ellipsis_limit_changed_callback,
2082                                           NULL);
2083 
2084     g_signal_handlers_disconnect_by_func (nemo_icon_view_preferences,
2085                                           update_auto_strv_as_quarks,
2086                                           &caption_attributes);
2087 
2088     G_OBJECT_CLASS (nemo_icon_view_container_parent_class)->finalize (object);
2089 }
2090 
2091 static void
nemo_icon_view_container_class_init(NemoIconViewContainerClass * klass)2092 nemo_icon_view_container_class_init (NemoIconViewContainerClass *klass)
2093 {
2094 	NemoIconContainerClass *ic_class;
2095 
2096     G_OBJECT_CLASS (klass)->finalize = finalize;
2097 
2098 	ic_class = &klass->parent_class;
2099 
2100 	attribute_none_q = g_quark_from_static_string ("none");
2101 
2102     ic_class->is_grid_container = FALSE;
2103 	ic_class->get_icon_text = nemo_icon_view_container_get_icon_text;
2104 	ic_class->get_icon_images = nemo_icon_view_container_get_icon_images;
2105 	ic_class->get_icon_description = nemo_icon_view_container_get_icon_description;
2106 	ic_class->prioritize_thumbnailing = nemo_icon_view_container_prioritize_thumbnailing;
2107     ic_class->get_max_layout_lines_for_pango = nemo_icon_view_container_get_max_layout_lines_for_pango;
2108     ic_class->get_max_layout_lines = nemo_icon_view_container_get_max_layout_lines;
2109 
2110 	ic_class->compare_icons = nemo_icon_view_container_compare_icons;
2111 	ic_class->freeze_updates = nemo_icon_view_container_freeze_updates;
2112 	ic_class->unfreeze_updates = nemo_icon_view_container_unfreeze_updates;
2113     ic_class->lay_down_icons = nemo_icon_view_container_lay_down_icons;
2114     ic_class->icon_set_position = nemo_icon_view_container_icon_set_position;
2115     ic_class->move_icon = nemo_icon_view_container_move_icon;
2116     ic_class->update_icon = nemo_icon_view_container_update_icon;
2117     ic_class->align_icons = nemo_icon_view_container_align_icons;
2118     ic_class->reload_icon_positions = nemo_icon_view_container_reload_icon_positions;
2119     ic_class->finish_adding_new_icons = nemo_icon_view_container_finish_adding_new_icons;
2120     ic_class->icon_get_bounding_box = nemo_icon_view_container_icon_get_bounding_box;
2121     ic_class->set_zoom_level = nemo_icon_view_container_set_zoom_level;
2122     ic_class->get_additional_text_line_count = nemo_icon_view_container_get_additional_text_line_count;
2123 }
2124 
2125 static void
nemo_icon_view_container_init(NemoIconViewContainer * icon_container)2126 nemo_icon_view_container_init (NemoIconViewContainer *icon_container)
2127 {
2128 	gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (icon_container)),
2129 				     GTK_STYLE_CLASS_VIEW);
2130 
2131     static gboolean setup_prefs = FALSE;
2132 
2133     g_signal_connect (icon_container, "get-tooltip-text", G_CALLBACK (on_get_tooltip_text), NULL);
2134 
2135     if (!setup_prefs) {
2136         g_signal_connect_swapped (nemo_icon_view_preferences,
2137                       "changed::" NEMO_PREFERENCES_ICON_VIEW_TEXT_ELLIPSIS_LIMIT,
2138                       G_CALLBACK (text_ellipsis_limit_changed_callback),
2139                       NULL);
2140         text_ellipsis_limit_changed_callback (NULL);
2141 
2142         g_signal_connect_swapped (nemo_desktop_preferences,
2143                       "changed::" NEMO_PREFERENCES_DESKTOP_TEXT_ELLIPSIS_LIMIT,
2144                       G_CALLBACK (desktop_text_ellipsis_limit_changed_callback),
2145                       NULL);
2146         desktop_text_ellipsis_limit_changed_callback (NULL);
2147 
2148         g_signal_connect (nemo_icon_view_preferences,
2149                           "changed::" NEMO_PREFERENCES_ICON_VIEW_CAPTIONS,
2150                           G_CALLBACK (update_auto_strv_as_quarks),
2151                           &caption_attributes);
2152 
2153         setup_prefs = TRUE;
2154     }
2155 }
2156 
2157 NemoIconContainer *
nemo_icon_view_container_construct(NemoIconViewContainer * icon_container,NemoIconView * view,gboolean is_desktop)2158 nemo_icon_view_container_construct (NemoIconViewContainer *icon_container,
2159                                     NemoIconView          *view,
2160                                     gboolean               is_desktop)
2161 {
2162 	AtkObject *atk_obj;
2163     NemoViewLayoutConstants *constants = NEMO_ICON_CONTAINER (icon_container)->details->view_constants;
2164 
2165 	g_return_val_if_fail (NEMO_IS_ICON_VIEW (view), NULL);
2166 
2167 	icon_container->view = view;
2168     nemo_icon_container_set_is_desktop (NEMO_ICON_CONTAINER (icon_container), is_desktop);
2169 
2170 	atk_obj = gtk_widget_get_accessible (GTK_WIDGET (icon_container));
2171 	atk_object_set_name (atk_obj, _("Icon View"));
2172 
2173     constants = NEMO_ICON_CONTAINER (icon_container)->details->view_constants;
2174 
2175     constants->icon_pad_left = 4;
2176     constants->icon_pad_right = 4;
2177     constants->icon_pad_top = 4;
2178     constants->icon_pad_bottom = 4;
2179     constants->container_pad_left = 4;
2180     constants->container_pad_right = 4;
2181     constants->container_pad_top = 4;
2182     constants->container_pad_bottom = 4;
2183     constants->standard_icon_grid_width = 155;
2184     constants->text_beside_icon_grid_width = 205;
2185     constants->desktop_pad_horizontal = 10;
2186     constants->desktop_pad_vertical = 10;
2187     constants->snap_size_x = 78;
2188     constants->snap_size_y = 20;
2189     constants->max_text_width_standard = 135;
2190     constants->max_text_width_beside = 90;
2191     constants->max_text_width_beside_top_to_bottom = 150;
2192 
2193 	return NEMO_ICON_CONTAINER (icon_container);
2194 }
2195 
2196 NemoIconContainer *
nemo_icon_view_container_new(NemoIconView * view,gboolean is_desktop)2197 nemo_icon_view_container_new (NemoIconView *view,
2198                               gboolean      is_desktop)
2199 {
2200     return nemo_icon_view_container_construct (g_object_new (NEMO_TYPE_ICON_VIEW_CONTAINER, NULL),
2201                                                view,
2202                                                is_desktop);
2203 }
2204 
2205 void
nemo_icon_view_container_set_sort_desktop(NemoIconViewContainer * container,gboolean desktop)2206 nemo_icon_view_container_set_sort_desktop (NemoIconViewContainer *container,
2207 					       gboolean         desktop)
2208 {
2209 	container->sort_for_desktop = desktop;
2210 }
2211