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