1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3 /* fm-icon-container.h - the container widget for file manager icons
4
5 Copyright (C) 2002 Sun Microsystems, Inc.
6
7 The Mate 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 Mate 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 Mate Library; see the file COPYING.LIB. If not,
19 write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 Boston, MA 02110-1301, USA.
21
22 Author: Michael Meeks <michael@ximian.com>
23 */
24 #include <config.h>
25 #include <string.h>
26
27 #include <glib/gi18n.h>
28 #include <gio/gio.h>
29
30 #include <eel/eel-glib-extensions.h>
31
32 #include <libcaja-private/caja-global-preferences.h>
33 #include <libcaja-private/caja-file-attributes.h>
34 #include <libcaja-private/caja-thumbnails.h>
35 #include <libcaja-private/caja-desktop-icon-file.h>
36
37 #include "fm-icon-container.h"
38
39 G_DEFINE_TYPE (FMIconContainer, fm_icon_container, CAJA_TYPE_ICON_CONTAINER);
40
41 static GQuark attribute_none_q;
42
43 static FMIconView *
get_icon_view(CajaIconContainer * container)44 get_icon_view (CajaIconContainer *container)
45 {
46 /* Type unsafe comparison for performance */
47 return ((FMIconContainer *)container)->view;
48 }
49
50 static CajaIconInfo *
fm_icon_container_get_icon_images(CajaIconContainer * container,CajaIconData * data,int size,GList ** emblem_pixbufs,char ** embedded_text,gboolean for_drag_accept,gboolean need_large_embeddded_text,gboolean * embedded_text_needs_loading,gboolean * has_window_open)51 fm_icon_container_get_icon_images (CajaIconContainer *container,
52 CajaIconData *data,
53 int size,
54 GList **emblem_pixbufs,
55 char **embedded_text,
56 gboolean for_drag_accept,
57 gboolean need_large_embeddded_text,
58 gboolean *embedded_text_needs_loading,
59 gboolean *has_window_open)
60 {
61 FMIconView *icon_view;
62 CajaFile *file;
63 gboolean use_embedding;
64 CajaFileIconFlags flags;
65 guint emblem_size;
66 gint scale;
67
68 file = (CajaFile *) data;
69
70 g_assert (CAJA_IS_FILE (file));
71 icon_view = get_icon_view (container);
72 g_return_val_if_fail (icon_view != NULL, NULL);
73
74 use_embedding = FALSE;
75 if (embedded_text)
76 {
77 *embedded_text = caja_file_peek_top_left_text (file, need_large_embeddded_text, embedded_text_needs_loading);
78 use_embedding = *embedded_text != NULL;
79 }
80
81 if (emblem_pixbufs != NULL)
82 {
83 emblem_size = caja_icon_get_emblem_size_for_icon_size (size);
84 /* don't return images larger than the actual icon size */
85 emblem_size = MIN (emblem_size, size);
86
87 if (emblem_size > 0)
88 {
89 char **emblems_to_ignore;
90
91 emblems_to_ignore = fm_directory_view_get_emblem_names_to_exclude
92 (FM_DIRECTORY_VIEW (icon_view));
93 *emblem_pixbufs = caja_file_get_emblem_pixbufs (file,
94 emblem_size,
95 FALSE,
96 emblems_to_ignore);
97 g_strfreev (emblems_to_ignore);
98 }
99 }
100
101 *has_window_open = caja_file_has_open_window (file);
102
103 flags = CAJA_FILE_ICON_FLAGS_USE_MOUNT_ICON_AS_EMBLEM;
104 if (!fm_icon_view_is_compact (icon_view) ||
105 caja_icon_container_get_zoom_level (container) > CAJA_ZOOM_LEVEL_STANDARD)
106 {
107 flags |= CAJA_FILE_ICON_FLAGS_USE_THUMBNAILS;
108 if (fm_icon_view_is_compact (icon_view))
109 {
110 flags |= CAJA_FILE_ICON_FLAGS_FORCE_THUMBNAIL_SIZE;
111 }
112 }
113
114 if (use_embedding)
115 {
116 flags |= CAJA_FILE_ICON_FLAGS_EMBEDDING_TEXT;
117 }
118 if (for_drag_accept)
119 {
120 flags |= CAJA_FILE_ICON_FLAGS_FOR_DRAG_ACCEPT;
121 }
122
123 scale = gtk_widget_get_scale_factor (GTK_WIDGET (icon_view));
124
125 return caja_file_get_icon (file, size, scale, flags);
126 }
127
128 static char *
fm_icon_container_get_icon_description(CajaIconContainer * container,CajaIconData * data)129 fm_icon_container_get_icon_description (CajaIconContainer *container,
130 CajaIconData *data)
131 {
132 CajaFile *file;
133 char *mime_type;
134 const char *description;
135
136 file = CAJA_FILE (data);
137 g_assert (CAJA_IS_FILE (file));
138
139 if (CAJA_IS_DESKTOP_ICON_FILE (file))
140 {
141 return NULL;
142 }
143
144 mime_type = caja_file_get_mime_type (file);
145 description = g_content_type_get_description (mime_type);
146 g_free (mime_type);
147 return g_strdup (description);
148 }
149
150 static void
fm_icon_container_start_monitor_top_left(CajaIconContainer * container,CajaIconData * data,gconstpointer client,gboolean large_text)151 fm_icon_container_start_monitor_top_left (CajaIconContainer *container,
152 CajaIconData *data,
153 gconstpointer client,
154 gboolean large_text)
155 {
156 CajaFile *file;
157 CajaFileAttributes attributes;
158
159 file = (CajaFile *) data;
160
161 g_assert (CAJA_IS_FILE (file));
162
163 attributes = CAJA_FILE_ATTRIBUTE_TOP_LEFT_TEXT;
164 if (large_text)
165 {
166 attributes |= CAJA_FILE_ATTRIBUTE_LARGE_TOP_LEFT_TEXT;
167 }
168 caja_file_monitor_add (file, client, attributes);
169 }
170
171 static void
fm_icon_container_stop_monitor_top_left(CajaIconContainer * container,CajaIconData * data,gconstpointer client)172 fm_icon_container_stop_monitor_top_left (CajaIconContainer *container,
173 CajaIconData *data,
174 gconstpointer client)
175 {
176 CajaFile *file;
177
178 file = (CajaFile *) data;
179
180 g_assert (CAJA_IS_FILE (file));
181
182 caja_file_monitor_remove (file, client);
183 }
184
185 static void
fm_icon_container_prioritize_thumbnailing(CajaIconContainer * container,CajaIconData * data)186 fm_icon_container_prioritize_thumbnailing (CajaIconContainer *container,
187 CajaIconData *data)
188 {
189 CajaFile *file;
190
191 file = (CajaFile *) data;
192
193 g_assert (CAJA_IS_FILE (file));
194
195 if (caja_file_is_thumbnailing (file))
196 {
197 char *uri;
198
199 uri = caja_file_get_uri (file);
200 caja_thumbnail_prioritize (uri);
201 g_free (uri);
202 }
203 }
204
205 /*
206 * Get the preference for which caption text should appear
207 * beneath icons.
208 */
209 static GQuark *
fm_icon_container_get_icon_text_attributes_from_preferences(void)210 fm_icon_container_get_icon_text_attributes_from_preferences (void)
211 {
212 static GQuark *attributes = NULL;
213
214 if (attributes == NULL)
215 {
216 eel_g_settings_add_auto_strv_as_quarks (caja_icon_view_preferences,
217 CAJA_PREFERENCES_ICON_VIEW_CAPTIONS,
218 &attributes);
219 }
220
221 /* We don't need to sanity check the attributes list even though it came
222 * from preferences.
223 *
224 * There are 2 ways that the values in the list could be bad.
225 *
226 * 1) The user picks "bad" values. "bad" values are those that result in
227 * there being duplicate attributes in the list.
228 *
229 * 2) Value stored in MateConf are tampered with. Its possible physically do
230 * this by pulling the rug underneath MateConf and manually editing its
231 * config files. Its also possible to use a third party MateConf key
232 * editor and store garbage for the keys in question.
233 *
234 * Thankfully, the Caja preferences machinery deals with both of
235 * these cases.
236 *
237 * In the first case, the preferences dialog widgetry prevents
238 * duplicate attributes by making "bad" choices insensitive.
239 *
240 * In the second case, the preferences getter (and also the auto storage) for
241 * string_array values are always valid members of the enumeration associated
242 * with the preference.
243 *
244 * So, no more error checking on attributes is needed here and we can return
245 * a the auto stored value.
246 */
247 return attributes;
248 }
249
250 static int
quarkv_length(GQuark * attributes)251 quarkv_length (GQuark *attributes)
252 {
253 int i;
254 i = 0;
255 while (attributes[i] != 0)
256 {
257 i++;
258 }
259 return i;
260 }
261
262 /**
263 * fm_icon_view_get_icon_text_attribute_names:
264 *
265 * Get a list representing which text attributes should be displayed
266 * beneath an icon. The result is dependent on zoom level and possibly
267 * user configuration. Don't free the result.
268 * @view: FMIconView to query.
269 *
270 **/
271 static GQuark *
fm_icon_container_get_icon_text_attribute_names(CajaIconContainer * container,int * len)272 fm_icon_container_get_icon_text_attribute_names (CajaIconContainer *container,
273 int *len)
274 {
275 GQuark *attributes;
276 int piece_count;
277
278 const int pieces_by_level[] =
279 {
280 0, /* CAJA_ZOOM_LEVEL_SMALLEST */
281 0, /* CAJA_ZOOM_LEVEL_SMALLER */
282 0, /* CAJA_ZOOM_LEVEL_SMALL */
283 1, /* CAJA_ZOOM_LEVEL_STANDARD */
284 2, /* CAJA_ZOOM_LEVEL_LARGE */
285 2, /* CAJA_ZOOM_LEVEL_LARGER */
286 3 /* CAJA_ZOOM_LEVEL_LARGEST */
287 };
288
289 piece_count = pieces_by_level[caja_icon_container_get_zoom_level (container)];
290
291 attributes = fm_icon_container_get_icon_text_attributes_from_preferences ();
292
293 *len = MIN (piece_count, quarkv_length (attributes));
294
295 return attributes;
296 }
297
298 /* This callback returns the text, both the editable part, and the
299 * part below that is not editable.
300 */
301 static void
fm_icon_container_get_icon_text(CajaIconContainer * container,CajaIconData * data,char ** editable_text,char ** additional_text,gboolean include_invisible)302 fm_icon_container_get_icon_text (CajaIconContainer *container,
303 CajaIconData *data,
304 char **editable_text,
305 char **additional_text,
306 gboolean include_invisible)
307 {
308 GQuark *attributes;
309 char *text_array[4];
310 int i, j, num_attributes;
311 FMIconView *icon_view;
312 CajaFile *file;
313 gboolean use_additional;
314
315 file = CAJA_FILE (data);
316
317 g_assert (CAJA_IS_FILE (file));
318 g_assert (editable_text != NULL);
319 icon_view = get_icon_view (container);
320 g_return_if_fail (icon_view != NULL);
321
322 use_additional = (additional_text != NULL);
323
324 /* In the smallest zoom mode, no text is drawn. */
325 if (caja_icon_container_get_zoom_level (container) == CAJA_ZOOM_LEVEL_SMALLEST &&
326 !include_invisible)
327 {
328 *editable_text = NULL;
329 }
330 else
331 {
332 /* Strip the suffix for caja object xml files. */
333 *editable_text = caja_file_get_display_name (file);
334 }
335
336 if (!use_additional)
337 {
338 return;
339 }
340
341 if (fm_icon_view_is_compact (icon_view))
342 {
343 *additional_text = NULL;
344 return;
345 }
346
347 if (CAJA_IS_DESKTOP_ICON_FILE (file))
348 {
349 /* Don't show the normal extra information for desktop icons, it doesn't
350 * make sense. */
351 *additional_text = NULL;
352 return;
353 }
354
355 /* Handle link files specially. */
356 if (caja_file_is_caja_link (file))
357 {
358 /* FIXME bugzilla.gnome.org 42531: Does sync. I/O and works only locally. */
359 *additional_text = NULL;
360 if (caja_file_is_local (file))
361 {
362 char *actual_uri;
363 gchar *description;
364
365 actual_uri = caja_file_get_uri (file);
366 description = caja_link_local_get_additional_text (actual_uri);
367
368 if (description)
369 *additional_text = g_strdup_printf (" \n%s\n ", description);
370
371 g_free (description);
372 g_free (actual_uri);
373 }
374 /* Don't show the normal extra information for desktop files, it doesn't
375 * make sense. */
376 return;
377 }
378
379 /* Find out what attributes go below each icon. */
380 attributes = fm_icon_container_get_icon_text_attribute_names (container,
381 &num_attributes);
382
383 /* Get the attributes. */
384 j = 0;
385 for (i = 0; i < num_attributes; ++i)
386 {
387 if (attributes[i] == attribute_none_q)
388 {
389 continue;
390 }
391
392 text_array[j++] =
393 caja_file_get_string_attribute_with_default_q (file, attributes[i]);
394 }
395 text_array[j] = NULL;
396
397 /* Return them. */
398 if (j == 0)
399 {
400 *additional_text = NULL;
401 }
402 else if (j == 1)
403 {
404 /* Only one item, avoid the strdup + free */
405 *additional_text = text_array[0];
406 }
407 else
408 {
409 *additional_text = g_strjoinv ("\n", text_array);
410
411 for (i = 0; i < j; i++)
412 {
413 g_free (text_array[i]);
414 }
415 }
416 }
417
418 /* Sort as follows:
419 * 0) computer link
420 * 1) home link
421 * 2) network link
422 * 3) mount links
423 * 4) other
424 * 5) trash link
425 */
426 typedef enum
427 {
428 SORT_COMPUTER_LINK,
429 SORT_HOME_LINK,
430 SORT_NETWORK_LINK,
431 SORT_MOUNT_LINK,
432 SORT_OTHER,
433 SORT_TRASH_LINK
434 } SortCategory;
435
436 static SortCategory
get_sort_category(CajaFile * file)437 get_sort_category (CajaFile *file)
438 {
439 SortCategory category;
440
441 category = SORT_OTHER;
442
443 if (CAJA_IS_DESKTOP_ICON_FILE (file))
444 {
445 CajaDesktopLink *link;
446
447 link = caja_desktop_icon_file_get_link (CAJA_DESKTOP_ICON_FILE (file));
448
449 if (link != NULL)
450 {
451 switch (caja_desktop_link_get_link_type (link))
452 {
453 case CAJA_DESKTOP_LINK_COMPUTER:
454 category = SORT_COMPUTER_LINK;
455 break;
456 case CAJA_DESKTOP_LINK_HOME:
457 category = SORT_HOME_LINK;
458 break;
459 case CAJA_DESKTOP_LINK_MOUNT:
460 category = SORT_MOUNT_LINK;
461 break;
462 case CAJA_DESKTOP_LINK_TRASH:
463 category = SORT_TRASH_LINK;
464 break;
465 case CAJA_DESKTOP_LINK_NETWORK:
466 category = SORT_NETWORK_LINK;
467 break;
468 default:
469 category = SORT_OTHER;
470 break;
471 }
472 g_object_unref (link);
473 }
474 }
475
476 return category;
477 }
478
479 static int
fm_desktop_icon_container_icons_compare(CajaIconContainer * container,CajaIconData * data_a,CajaIconData * data_b)480 fm_desktop_icon_container_icons_compare (CajaIconContainer *container,
481 CajaIconData *data_a,
482 CajaIconData *data_b)
483 {
484 CajaFile *file_a;
485 CajaFile *file_b;
486 FMDirectoryView *directory_view;
487 SortCategory category_a, category_b;
488
489 file_a = (CajaFile *) data_a;
490 file_b = (CajaFile *) data_b;
491
492 directory_view = FM_DIRECTORY_VIEW (FM_ICON_CONTAINER (container)->view);
493 g_return_val_if_fail (directory_view != NULL, 0);
494
495 category_a = get_sort_category (file_a);
496 category_b = get_sort_category (file_b);
497
498 if (category_a == category_b)
499 {
500 return caja_file_compare_for_sort
501 (file_a, file_b, CAJA_FILE_SORT_BY_DISPLAY_NAME,
502 fm_directory_view_should_sort_directories_first (directory_view),
503 FALSE);
504 }
505
506 if (category_a < category_b)
507 {
508 return -1;
509 }
510 else
511 {
512 return +1;
513 }
514 }
515
516 static int
fm_icon_container_compare_icons(CajaIconContainer * container,CajaIconData * icon_a,CajaIconData * icon_b)517 fm_icon_container_compare_icons (CajaIconContainer *container,
518 CajaIconData *icon_a,
519 CajaIconData *icon_b)
520 {
521 FMIconView *icon_view;
522
523 icon_view = get_icon_view (container);
524 g_return_val_if_fail (icon_view != NULL, 0);
525
526 if (FM_ICON_CONTAINER (container)->sort_for_desktop)
527 {
528 return fm_desktop_icon_container_icons_compare
529 (container, icon_a, icon_b);
530 }
531
532 /* Type unsafe comparisons for performance */
533 return fm_icon_view_compare_files (icon_view,
534 (CajaFile *)icon_a,
535 (CajaFile *)icon_b);
536 }
537
538 static int
fm_icon_container_compare_icons_by_name(CajaIconContainer * container,CajaIconData * icon_a,CajaIconData * icon_b)539 fm_icon_container_compare_icons_by_name (CajaIconContainer *container,
540 CajaIconData *icon_a,
541 CajaIconData *icon_b)
542 {
543 return caja_file_compare_for_sort
544 (CAJA_FILE (icon_a),
545 CAJA_FILE (icon_b),
546 CAJA_FILE_SORT_BY_DISPLAY_NAME,
547 FALSE, FALSE);
548 }
549
550 static void
fm_icon_container_freeze_updates(CajaIconContainer * container)551 fm_icon_container_freeze_updates (CajaIconContainer *container)
552 {
553 FMIconView *icon_view;
554 icon_view = get_icon_view (container);
555 g_return_if_fail (icon_view != NULL);
556 fm_directory_view_freeze_updates (FM_DIRECTORY_VIEW (icon_view));
557 }
558
559 static void
fm_icon_container_unfreeze_updates(CajaIconContainer * container)560 fm_icon_container_unfreeze_updates (CajaIconContainer *container)
561 {
562 FMIconView *icon_view;
563 icon_view = get_icon_view (container);
564 g_return_if_fail (icon_view != NULL);
565 fm_directory_view_unfreeze_updates (FM_DIRECTORY_VIEW (icon_view));
566 }
567
568 static void
fm_icon_container_dispose(GObject * object)569 fm_icon_container_dispose (GObject *object)
570 {
571 FMIconContainer *icon_container;
572
573 icon_container = FM_ICON_CONTAINER (object);
574
575 icon_container->view = NULL;
576
577 G_OBJECT_CLASS (fm_icon_container_parent_class)->dispose (object);
578 }
579
580 static void
fm_icon_container_class_init(FMIconContainerClass * klass)581 fm_icon_container_class_init (FMIconContainerClass *klass)
582 {
583 CajaIconContainerClass *ic_class;
584
585 ic_class = &klass->parent_class;
586
587 attribute_none_q = g_quark_from_static_string ("none");
588
589 ic_class->get_icon_text = fm_icon_container_get_icon_text;
590 ic_class->get_icon_images = fm_icon_container_get_icon_images;
591 ic_class->get_icon_description = fm_icon_container_get_icon_description;
592 ic_class->start_monitor_top_left = fm_icon_container_start_monitor_top_left;
593 ic_class->stop_monitor_top_left = fm_icon_container_stop_monitor_top_left;
594 ic_class->prioritize_thumbnailing = fm_icon_container_prioritize_thumbnailing;
595
596 ic_class->compare_icons = fm_icon_container_compare_icons;
597 ic_class->compare_icons_by_name = fm_icon_container_compare_icons_by_name;
598 ic_class->freeze_updates = fm_icon_container_freeze_updates;
599 ic_class->unfreeze_updates = fm_icon_container_unfreeze_updates;
600
601 G_OBJECT_CLASS (klass)->dispose = fm_icon_container_dispose;
602 }
603
604 static void
fm_icon_container_init(FMIconContainer * icon_container)605 fm_icon_container_init (FMIconContainer *icon_container)
606 {
607 gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (icon_container)),
608 GTK_STYLE_CLASS_VIEW);
609 }
610
611 CajaIconContainer *
fm_icon_container_construct(FMIconContainer * icon_container,FMIconView * view)612 fm_icon_container_construct (FMIconContainer *icon_container, FMIconView *view)
613 {
614 AtkObject *atk_obj;
615
616 g_return_val_if_fail (FM_IS_ICON_VIEW (view), NULL);
617
618 icon_container->view = view;
619 atk_obj = gtk_widget_get_accessible (GTK_WIDGET (icon_container));
620 atk_object_set_name (atk_obj, _("Icon View"));
621
622 return CAJA_ICON_CONTAINER (icon_container);
623 }
624
625 CajaIconContainer *
fm_icon_container_new(FMIconView * view)626 fm_icon_container_new (FMIconView *view)
627 {
628 return fm_icon_container_construct
629 (g_object_new (FM_TYPE_ICON_CONTAINER, NULL),
630 view);
631 }
632
633 void
fm_icon_container_set_sort_desktop(FMIconContainer * container,gboolean desktop)634 fm_icon_container_set_sort_desktop (FMIconContainer *container,
635 gboolean desktop)
636 {
637 container->sort_for_desktop = desktop;
638 }
639