1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
2
3 caja-file.c: Caja file model.
4
5 Copyright (C) 1999, 2000, 2001 Eazel, Inc.
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU 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 This program 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 General Public License for more details.
16
17 You should have received a copy of the GNU General Public
18 License along with this program; if not, write to the
19 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 Boston, MA 02110-1301, USA.
21
22 Author: Darin Adler <darin@bentspoon.com>
23 */
24
25 #include <config.h>
26 #include <grp.h>
27 #include <gtk/gtk.h>
28 #include <glib/gi18n.h>
29 #include <glib/gstdio.h>
30 #include <gio/gio.h>
31 #include <glib.h>
32 #include <libxml/parser.h>
33 #include <pwd.h>
34 #include <stdlib.h>
35 #include <ctype.h>
36 #include <sys/time.h>
37 #include <time.h>
38 #include <unistd.h>
39 #include <sys/stat.h>
40
41 #include <eel/eel-debug.h>
42 #include <eel/eel-glib-extensions.h>
43 #include <eel/eel-gtk-extensions.h>
44 #include <eel/eel-vfs-extensions.h>
45 #include <eel/eel-gtk-macros.h>
46 #include <eel/eel-string.h>
47
48 #include <libcaja-extension/caja-file-info.h>
49 #include <libcaja-extension/caja-extension-private.h>
50 #include <libcaja-private/caja-extensions.h>
51
52 #include "caja-file.h"
53 #include "caja-directory-notify.h"
54 #include "caja-directory-private.h"
55 #include "caja-signaller.h"
56 #include "caja-desktop-directory.h"
57 #include "caja-desktop-directory-file.h"
58 #include "caja-desktop-icon-file.h"
59 #include "caja-file-attributes.h"
60 #include "caja-file-private.h"
61 #include "caja-file-operations.h"
62 #include "caja-file-utilities.h"
63 #include "caja-global-preferences.h"
64 #include "caja-lib-self-check-functions.h"
65 #include "caja-link.h"
66 #include "caja-metadata.h"
67 #include "caja-module.h"
68 #include "caja-search-directory.h"
69 #include "caja-search-directory-file.h"
70 #include "caja-thumbnails.h"
71 #include "caja-ui-utilities.h"
72 #include "caja-vfs-file.h"
73 #include "caja-saved-search-file.h"
74
75 #ifdef HAVE_SELINUX
76 #include <selinux/selinux.h>
77 #endif
78
79 #define ICON_NAME_THUMBNAIL_LOADING "image-loading"
80
81 #undef CAJA_FILE_DEBUG_REF
82 #undef CAJA_FILE_DEBUG_REF_VALGRIND
83
84 #ifdef CAJA_FILE_DEBUG_REF_VALGRIND
85 #include <valgrind/valgrind.h>
86 #define DEBUG_REF_PRINTF VALGRIND_PRINTF_BACKTRACE
87 #else
88 #define DEBUG_REF_PRINTF printf
89 #endif
90
91 /* Files that start with these characters sort after files that don't. */
92 #define SORT_LAST_CHAR1 '.'
93 #define SORT_LAST_CHAR2 '#'
94
95 #define METADATA_ID_IS_LIST_MASK (1U<<31)
96
97 #define SORT_BY_EXTENSION_FOLLOWING_MAX_LENGTH 3
98 #define SORT_BY_EXTENSION_MAX_SEGMENTS 3
99
100 typedef enum {
101 SHOW_HIDDEN = 1 << 0,
102 SHOW_BACKUP = 1 << 1,
103 } FilterOptions;
104
105 typedef void (* ModifyListFunction) (GList **list, CajaFile *file);
106
107 enum {
108 CHANGED,
109 UPDATED_DEEP_COUNT_IN_PROGRESS,
110 LAST_SIGNAL
111 };
112
113 static int date_format_pref;
114
115 static guint signals[LAST_SIGNAL] = { 0 };
116
117 static GHashTable *symbolic_links;
118
119 static GQuark attribute_name_q,
120 attribute_size_q,
121 attribute_size_on_disk_q,
122 attribute_type_q,
123 attribute_creation_date_q,
124 attribute_date_created_q,
125 attribute_modification_date_q,
126 attribute_date_modified_q,
127 attribute_accessed_date_q,
128 attribute_date_accessed_q,
129 attribute_emblems_q,
130 attribute_extension_q,
131 attribute_mime_type_q,
132 attribute_size_detail_q,
133 attribute_size_on_disk_detail_q,
134 attribute_deep_size_q,
135 attribute_deep_size_on_disk_q,
136 attribute_deep_file_count_q,
137 attribute_deep_directory_count_q,
138 attribute_deep_total_count_q,
139 attribute_date_changed_q,
140 attribute_trashed_on_q,
141 attribute_trash_orig_path_q,
142 attribute_date_permissions_q,
143 attribute_permissions_q,
144 attribute_selinux_context_q,
145 attribute_octal_permissions_q,
146 attribute_owner_q,
147 attribute_group_q,
148 attribute_uri_q,
149 attribute_where_q,
150 attribute_link_target_q,
151 attribute_volume_q,
152 attribute_free_space_q;
153
154 static void caja_file_info_iface_init (CajaFileInfoIface *iface);
155 static char * caja_file_get_owner_as_string (CajaFile *file,
156 gboolean include_real_name);
157 static char * caja_file_get_type_as_string (CajaFile *file);
158 static gboolean update_info_and_name (CajaFile *file,
159 GFileInfo *info);
160 static const char * caja_file_peek_display_name (CajaFile *file);
161 static const char * caja_file_peek_display_name_collation_key (CajaFile *file);
162 static void file_mount_unmounted (GMount *mount, gpointer data);
163 static void metadata_hash_free (GHashTable *hash);
164
165 G_DEFINE_TYPE_WITH_CODE (CajaFile, caja_file, G_TYPE_OBJECT,
166 G_ADD_PRIVATE (CajaFile)
167 G_IMPLEMENT_INTERFACE (CAJA_TYPE_FILE_INFO,
168 caja_file_info_iface_init));
169
170 static void
caja_file_init(CajaFile * file)171 caja_file_init (CajaFile *file)
172 {
173 file->details = caja_file_get_instance_private (file);
174
175 caja_file_clear_info (file);
176 caja_file_invalidate_extension_info_internal (file);
177 }
178
179 static GObject*
caja_file_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_params)180 caja_file_constructor (GType type,
181 guint n_construct_properties,
182 GObjectConstructParam *construct_params)
183 {
184 GObject *object;
185 CajaFile *file;
186
187 object = (* G_OBJECT_CLASS (caja_file_parent_class)->constructor) (type,
188 n_construct_properties,
189 construct_params);
190
191 file = CAJA_FILE (object);
192
193 /* Set to default type after full construction */
194 if (CAJA_FILE_GET_CLASS (file)->default_file_type != G_FILE_TYPE_UNKNOWN) {
195 file->details->type = CAJA_FILE_GET_CLASS (file)->default_file_type;
196 }
197
198 return object;
199 }
200
201 gboolean
caja_file_set_display_name(CajaFile * file,const char * display_name,const char * edit_name,gboolean custom)202 caja_file_set_display_name (CajaFile *file,
203 const char *display_name,
204 const char *edit_name,
205 gboolean custom)
206 {
207 gboolean changed;
208
209 if (custom && display_name == NULL) {
210 /* We're re-setting a custom display name, invalidate it if
211 we already set it so that the old one is re-read */
212 if (file->details->got_custom_display_name) {
213 file->details->got_custom_display_name = FALSE;
214 caja_file_invalidate_attributes (file,
215 CAJA_FILE_ATTRIBUTE_INFO);
216 }
217 return FALSE;
218 }
219
220 if (display_name == NULL || *display_name == 0) {
221 return FALSE;
222 }
223
224 if (!custom && file->details->got_custom_display_name) {
225 return FALSE;
226 }
227
228 if (edit_name == NULL) {
229 edit_name = display_name;
230 }
231
232 changed = FALSE;
233
234 if (eel_strcmp (file->details->display_name, display_name) != 0) {
235 changed = TRUE;
236
237 g_clear_pointer (&file->details->display_name, g_ref_string_release);
238
239 if (eel_strcmp (file->details->name, display_name) == 0) {
240 file->details->display_name = g_ref_string_acquire (file->details->name);
241 } else {
242 file->details->display_name = g_ref_string_new (display_name);
243 }
244
245 g_free (file->details->display_name_collation_key);
246 file->details->display_name_collation_key = g_utf8_collate_key_for_filename (display_name, -1);
247 }
248
249 if (eel_strcmp (file->details->edit_name, edit_name) != 0) {
250 changed = TRUE;
251
252 g_clear_pointer (&file->details->edit_name, g_ref_string_release);
253 if (eel_strcmp (file->details->display_name, edit_name) == 0) {
254 file->details->edit_name = g_ref_string_acquire (file->details->display_name);
255 } else {
256 file->details->edit_name = g_ref_string_new (edit_name);
257 }
258 }
259
260 file->details->got_custom_display_name = custom;
261 return changed;
262 }
263
264 static void
caja_file_clear_display_name(CajaFile * file)265 caja_file_clear_display_name (CajaFile *file)
266 {
267 g_clear_pointer (&file->details->display_name, g_ref_string_release);
268 file->details->display_name = NULL;
269 g_free (file->details->display_name_collation_key);
270 file->details->display_name_collation_key = NULL;
271 g_clear_pointer (&file->details->edit_name, g_ref_string_release);
272 file->details->edit_name = NULL;
273 }
274
275 static gboolean
foreach_metadata_free(gpointer key,gpointer value,gpointer user_data)276 foreach_metadata_free (gpointer key,
277 gpointer value,
278 gpointer user_data)
279 {
280 guint id;
281
282 id = GPOINTER_TO_UINT (key);
283
284 if (id & METADATA_ID_IS_LIST_MASK) {
285 g_strfreev ((char **)value);
286 } else {
287 g_free ((char *)value);
288 }
289 return TRUE;
290 }
291
292
293 static void
metadata_hash_free(GHashTable * hash)294 metadata_hash_free (GHashTable *hash)
295 {
296 g_hash_table_foreach_remove (hash,
297 foreach_metadata_free,
298 NULL);
299 g_hash_table_destroy (hash);
300 }
301
302 static gboolean
metadata_hash_equal(GHashTable * hash1,GHashTable * hash2)303 metadata_hash_equal (GHashTable *hash1,
304 GHashTable *hash2)
305 {
306 GHashTableIter iter;
307 gpointer key1, value1, value2;
308 guint id;
309
310 if (hash1 == NULL && hash2 == NULL) {
311 return TRUE;
312 }
313
314 if (hash1 == NULL || hash2 == NULL) {
315 return FALSE;
316 }
317
318 if (g_hash_table_size (hash1) !=
319 g_hash_table_size (hash2)) {
320 return FALSE;
321 }
322
323 g_hash_table_iter_init (&iter, hash1);
324 while (g_hash_table_iter_next (&iter, &key1, &value1)) {
325 value2 = g_hash_table_lookup (hash2, key1);
326 if (value2 == NULL) {
327 return FALSE;
328 }
329 id = GPOINTER_TO_UINT (key1);
330 if (id & METADATA_ID_IS_LIST_MASK) {
331 if (!eel_g_strv_equal ((char **)value1, (char **)value2)) {
332 return FALSE;
333 }
334 } else {
335 if (strcmp ((char *)value1, (char *)value2) != 0) {
336 return FALSE;
337 }
338 }
339 }
340
341 return TRUE;
342 }
343
344 static void
clear_metadata(CajaFile * file)345 clear_metadata (CajaFile *file)
346 {
347 if (file->details->metadata) {
348 metadata_hash_free (file->details->metadata);
349 file->details->metadata = NULL;
350 }
351 }
352
353 static GHashTable *
get_metadata_from_info(GFileInfo * info)354 get_metadata_from_info (GFileInfo *info)
355 {
356 GHashTable *metadata;
357 char **attrs;
358 guint id;
359 int i;
360 GFileAttributeType type;
361 gpointer value;
362
363 attrs = g_file_info_list_attributes (info, "metadata");
364
365 metadata = g_hash_table_new (NULL, NULL);
366
367 for (i = 0; attrs[i] != NULL; i++) {
368 id = caja_metadata_get_id (attrs[i] + strlen ("metadata::"));
369 if (id == 0) {
370 continue;
371 }
372
373 if (!g_file_info_get_attribute_data (info, attrs[i],
374 &type, &value, NULL)) {
375 continue;
376 }
377
378 if (type == G_FILE_ATTRIBUTE_TYPE_STRING) {
379 g_hash_table_insert (metadata, GUINT_TO_POINTER (id),
380 g_strdup ((char *)value));
381 } else if (type == G_FILE_ATTRIBUTE_TYPE_STRINGV) {
382 id |= METADATA_ID_IS_LIST_MASK;
383 g_hash_table_insert (metadata, GUINT_TO_POINTER (id),
384 g_strdupv ((char **)value));
385 }
386 }
387
388 g_strfreev (attrs);
389
390 return metadata;
391 }
392
393 gboolean
caja_file_update_metadata_from_info(CajaFile * file,GFileInfo * info)394 caja_file_update_metadata_from_info (CajaFile *file,
395 GFileInfo *info)
396 {
397 gboolean changed = FALSE;
398
399 if (g_file_info_has_namespace (info, "metadata")) {
400 GHashTable *metadata;
401
402 metadata = get_metadata_from_info (info);
403 if (!metadata_hash_equal (metadata,
404 file->details->metadata)) {
405 changed = TRUE;
406 clear_metadata (file);
407 file->details->metadata = metadata;
408 } else {
409 metadata_hash_free (metadata);
410 }
411 } else if (file->details->metadata) {
412 changed = TRUE;
413 clear_metadata (file);
414 }
415 return changed;
416 }
417
418 void
caja_file_clear_info(CajaFile * file)419 caja_file_clear_info (CajaFile *file)
420 {
421 file->details->got_file_info = FALSE;
422 if (file->details->get_info_error) {
423 g_error_free (file->details->get_info_error);
424 file->details->get_info_error = NULL;
425 }
426 /* Reset to default type, which might be other than unknown for
427 special kinds of files like the desktop or a search directory */
428 file->details->type = CAJA_FILE_GET_CLASS (file)->default_file_type;
429
430 if (!file->details->got_custom_display_name) {
431 caja_file_clear_display_name (file);
432 }
433
434 if (!file->details->got_custom_activation_uri &&
435 file->details->activation_uri != NULL) {
436 g_free (file->details->activation_uri);
437 file->details->activation_uri = NULL;
438 }
439
440 if (file->details->icon != NULL) {
441 g_object_unref (file->details->icon);
442 file->details->icon = NULL;
443 }
444
445 g_free (file->details->thumbnail_path);
446 file->details->thumbnail_path = NULL;
447 file->details->thumbnailing_failed = FALSE;
448
449 file->details->is_launcher = FALSE;
450 file->details->is_foreign_link = FALSE;
451 file->details->is_trusted_link = FALSE;
452 file->details->is_symlink = FALSE;
453 file->details->is_hidden = FALSE;
454 file->details->is_backup = FALSE;
455 file->details->is_mountpoint = FALSE;
456 file->details->uid = -1;
457 file->details->gid = -1;
458 file->details->can_read = TRUE;
459 file->details->can_write = TRUE;
460 file->details->can_execute = TRUE;
461 file->details->can_delete = TRUE;
462 file->details->can_trash = TRUE;
463 file->details->can_rename = TRUE;
464 file->details->can_mount = FALSE;
465 file->details->can_unmount = FALSE;
466 file->details->can_eject = FALSE;
467 file->details->can_start = FALSE;
468 file->details->can_start_degraded = FALSE;
469 file->details->can_stop = FALSE;
470 file->details->start_stop_type = G_DRIVE_START_STOP_TYPE_UNKNOWN;
471 file->details->can_poll_for_media = FALSE;
472 file->details->is_media_check_automatic = FALSE;
473 file->details->has_permissions = FALSE;
474 file->details->permissions = 0;
475 file->details->size = -1;
476 file->details->size_on_disk = -1;
477 file->details->sort_order = 0;
478 file->details->mtime = 0;
479 file->details->atime = 0;
480 file->details->ctime = 0;
481 file->details->btime = 0;
482 file->details->trash_time = 0;
483 g_free (file->details->symlink_name);
484 file->details->symlink_name = NULL;
485 g_clear_pointer (&file->details->mime_type, g_ref_string_release);
486 file->details->mime_type = NULL;
487 g_free (file->details->selinux_context);
488 file->details->selinux_context = NULL;
489 g_free (file->details->description);
490 file->details->description = NULL;
491 g_clear_pointer (&file->details->owner, g_ref_string_release);
492 file->details->owner = NULL;
493 g_clear_pointer (&file->details->owner_real, g_ref_string_release);
494 file->details->owner_real = NULL;
495 g_clear_pointer (&file->details->group, g_ref_string_release);
496 file->details->group = NULL;
497
498 g_clear_pointer (&file->details->filesystem_id, g_ref_string_release);
499 file->details->filesystem_id = NULL;
500
501 clear_metadata (file);
502 }
503
504 static CajaFile *
caja_file_new_from_filename(CajaDirectory * directory,const char * filename,gboolean self_owned)505 caja_file_new_from_filename (CajaDirectory *directory,
506 const char *filename,
507 gboolean self_owned)
508 {
509 CajaFile *file;
510
511 g_assert (CAJA_IS_DIRECTORY (directory));
512 g_assert (filename != NULL);
513 g_assert (filename[0] != '\0');
514
515 if (CAJA_IS_DESKTOP_DIRECTORY (directory)) {
516 if (self_owned) {
517 file = CAJA_FILE (g_object_new (CAJA_TYPE_DESKTOP_DIRECTORY_FILE, NULL));
518 } else {
519 /* This doesn't normally happen, unless the user somehow types in a uri
520 * that references a file like this. (See #349840) */
521 file = CAJA_FILE (g_object_new (CAJA_TYPE_VFS_FILE, NULL));
522 }
523 } else if (CAJA_IS_SEARCH_DIRECTORY (directory)) {
524 if (self_owned) {
525 file = CAJA_FILE (g_object_new (CAJA_TYPE_SEARCH_DIRECTORY_FILE, NULL));
526 } else {
527 /* This doesn't normally happen, unless the user somehow types in a uri
528 * that references a file like this. (See #349840) */
529 file = CAJA_FILE (g_object_new (CAJA_TYPE_VFS_FILE, NULL));
530 }
531 } else if (g_str_has_suffix (filename, CAJA_SAVED_SEARCH_EXTENSION)) {
532 file = CAJA_FILE (g_object_new (CAJA_TYPE_SAVED_SEARCH_FILE, NULL));
533 } else {
534 file = CAJA_FILE (g_object_new (CAJA_TYPE_VFS_FILE, NULL));
535 }
536
537 file->details->directory = caja_directory_ref (directory);
538
539 file->details->name = g_ref_string_new (filename);
540
541 #ifdef CAJA_FILE_DEBUG_REF
542 DEBUG_REF_PRINTF("%10p ref'd", file);
543 #endif
544
545 return file;
546 }
547
548 static void
modify_link_hash_table(CajaFile * file,ModifyListFunction modify_function)549 modify_link_hash_table (CajaFile *file,
550 ModifyListFunction modify_function)
551 {
552 char *target_uri;
553 gboolean found;
554 gpointer original_key;
555 GList **list_ptr;
556
557 /* Check if there is a symlink name. If none, we are OK. */
558 if (file->details->symlink_name == NULL || !caja_file_is_symbolic_link (file)) {
559 return;
560 }
561
562 /* Create the hash table first time through. */
563 if (symbolic_links == NULL) {
564 symbolic_links = g_hash_table_new (g_str_hash, g_str_equal);
565 }
566
567 target_uri = caja_file_get_symbolic_link_target_uri (file);
568
569 /* Find the old contents of the hash table. */
570 found = g_hash_table_lookup_extended
571 (symbolic_links, target_uri,
572 &original_key, (gpointer *)&list_ptr);
573 if (!found) {
574 list_ptr = g_new0 (GList *, 1);
575 original_key = g_strdup (target_uri);
576 g_hash_table_insert (symbolic_links, original_key, list_ptr);
577 }
578 (* modify_function) (list_ptr, file);
579 if (*list_ptr == NULL) {
580 g_hash_table_remove (symbolic_links, target_uri);
581 g_free (list_ptr);
582 g_free (original_key);
583 }
584 g_free (target_uri);
585 }
586
587 static void
symbolic_link_weak_notify(gpointer data,GObject * where_the_object_was)588 symbolic_link_weak_notify (gpointer data,
589 GObject *where_the_object_was)
590 {
591 GList **list = data;
592 /* This really shouldn't happen, but we're seeing some strange things in
593 bug #358172 where the symlink hashtable isn't correctly updated. */
594 *list = g_list_remove (*list, where_the_object_was);
595 }
596
597 static void
add_to_link_hash_table_list(GList ** list,CajaFile * file)598 add_to_link_hash_table_list (GList **list, CajaFile *file)
599 {
600 if (g_list_find (*list, file) != NULL) {
601 g_warning ("Adding file to symlink_table multiple times. "
602 "Please add feedback of what you were doing at https://bugzilla.gnome.org/show_bug.cgi?id=358172\n");
603 return;
604 }
605 g_object_weak_ref (G_OBJECT (file), symbolic_link_weak_notify, list);
606 *list = g_list_prepend (*list, file);
607 }
608
609 static void
add_to_link_hash_table(CajaFile * file)610 add_to_link_hash_table (CajaFile *file)
611 {
612 modify_link_hash_table (file, add_to_link_hash_table_list);
613 }
614
615 static void
remove_from_link_hash_table_list(GList ** list,CajaFile * file)616 remove_from_link_hash_table_list (GList **list, CajaFile *file)
617 {
618 if (g_list_find (*list, file) != NULL) {
619 g_object_weak_unref (G_OBJECT (file), symbolic_link_weak_notify, list);
620 *list = g_list_remove (*list, file);
621 }
622 }
623
624 static void
remove_from_link_hash_table(CajaFile * file)625 remove_from_link_hash_table (CajaFile *file)
626 {
627 modify_link_hash_table (file, remove_from_link_hash_table_list);
628 }
629
630 CajaFile *
caja_file_new_from_info(CajaDirectory * directory,GFileInfo * info)631 caja_file_new_from_info (CajaDirectory *directory,
632 GFileInfo *info)
633 {
634 CajaFile *file;
635 const char *mime_type;
636
637 g_return_val_if_fail (CAJA_IS_DIRECTORY (directory), NULL);
638 g_return_val_if_fail (info != NULL, NULL);
639
640 mime_type = g_file_info_get_content_type (info);
641 if (mime_type &&
642 strcmp (mime_type, CAJA_SAVED_SEARCH_MIMETYPE) == 0) {
643 g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
644 file = CAJA_FILE (g_object_new (CAJA_TYPE_SAVED_SEARCH_FILE, NULL));
645 } else {
646 file = CAJA_FILE (g_object_new (CAJA_TYPE_VFS_FILE, NULL));
647 }
648
649 file->details->directory = caja_directory_ref (directory);
650
651 update_info_and_name (file, info);
652
653 #ifdef CAJA_FILE_DEBUG_REF
654 DEBUG_REF_PRINTF("%10p ref'd", file);
655 #endif
656
657 return file;
658 }
659
660 static CajaFile *
caja_file_get_internal(GFile * location,gboolean create)661 caja_file_get_internal (GFile *location, gboolean create)
662 {
663 gboolean self_owned;
664 CajaDirectory *directory;
665 CajaFile *file;
666 GFile *parent;
667 char *basename;
668
669 g_assert (location != NULL);
670
671 parent = g_file_get_parent (location);
672
673 self_owned = FALSE;
674 if (parent == NULL) {
675 self_owned = TRUE;
676 parent = g_object_ref (location);
677 }
678
679 /* Get object that represents the directory. */
680 directory = caja_directory_get_internal (parent, create);
681
682 g_object_unref (parent);
683
684 /* Get the name for the file. */
685 if (self_owned && directory != NULL) {
686 basename = caja_directory_get_name_for_self_as_new_file (directory);
687 } else {
688 basename = g_file_get_basename (location);
689 }
690 /* Check to see if it's a file that's already known. */
691 if (directory == NULL) {
692 file = NULL;
693 } else if (self_owned) {
694 file = directory->details->as_file;
695 } else {
696 file = caja_directory_find_file_by_name (directory, basename);
697 }
698
699 /* Ref or create the file. */
700 if (file != NULL) {
701 caja_file_ref (file);
702 } else if (create && directory != NULL) {
703 file = caja_file_new_from_filename (directory, basename, self_owned);
704 if (self_owned) {
705 g_assert (directory->details->as_file == NULL);
706 directory->details->as_file = file;
707 } else {
708 caja_directory_add_file (directory, file);
709 }
710 }
711
712 g_free (basename);
713 caja_directory_unref (directory);
714
715 return file;
716 }
717
718 CajaFile *
caja_file_get(GFile * location)719 caja_file_get (GFile *location)
720 {
721 return caja_file_get_internal (location, TRUE);
722 }
723
724 CajaFile *
caja_file_get_existing(GFile * location)725 caja_file_get_existing (GFile *location)
726 {
727 return caja_file_get_internal (location, FALSE);
728 }
729
730 CajaFile *
caja_file_get_existing_by_uri(const char * uri)731 caja_file_get_existing_by_uri (const char *uri)
732 {
733 GFile *location;
734 CajaFile *file;
735
736 location = g_file_new_for_uri (uri);
737 file = caja_file_get_internal (location, FALSE);
738 g_object_unref (location);
739
740 return file;
741 }
742
743 CajaFile *
caja_file_get_by_uri(const char * uri)744 caja_file_get_by_uri (const char *uri)
745 {
746 GFile *location;
747 CajaFile *file;
748
749 location = g_file_new_for_uri (uri);
750 file = caja_file_get_internal (location, TRUE);
751 g_object_unref (location);
752
753 return file;
754 }
755
756 gboolean
caja_file_is_self_owned(CajaFile * file)757 caja_file_is_self_owned (CajaFile *file)
758 {
759 return file->details->directory->details->as_file == file;
760 }
761
762 static void
finalize(GObject * object)763 finalize (GObject *object)
764 {
765 CajaDirectory *directory;
766 CajaFile *file;
767
768 file = CAJA_FILE (object);
769
770 g_assert (file->details->operations_in_progress == NULL);
771
772 if (file->details->is_thumbnailing) {
773 char *uri;
774
775 uri = caja_file_get_uri (file);
776 caja_thumbnail_remove_from_queue (uri);
777 g_free (uri);
778 }
779
780 caja_async_destroying_file (file);
781
782 remove_from_link_hash_table (file);
783
784 directory = file->details->directory;
785
786 if (caja_file_is_self_owned (file)) {
787 directory->details->as_file = NULL;
788 } else {
789 if (!file->details->is_gone) {
790 caja_directory_remove_file (directory, file);
791 }
792 }
793
794 if (file->details->get_info_error) {
795 g_error_free (file->details->get_info_error);
796 }
797
798 caja_directory_unref (directory);
799 g_clear_pointer (&file->details->name, g_ref_string_release);
800 g_clear_pointer (&file->details->display_name, g_ref_string_release);
801 g_free (file->details->display_name_collation_key);
802 g_clear_pointer (&file->details->edit_name, g_ref_string_release);
803 if (file->details->icon) {
804 g_object_unref (file->details->icon);
805 }
806 g_free (file->details->thumbnail_path);
807 g_free (file->details->symlink_name);
808 g_clear_pointer (&file->details->mime_type, g_ref_string_release);
809 g_clear_pointer (&file->details->owner, g_ref_string_release);
810 g_clear_pointer (&file->details->owner_real, g_ref_string_release);
811 g_clear_pointer (&file->details->group, g_ref_string_release);
812 g_free (file->details->selinux_context);
813 g_free (file->details->description);
814 g_free (file->details->top_left_text);
815 g_free (file->details->custom_icon);
816 g_free (file->details->activation_uri);
817 g_free (file->details->compare_by_emblem_cache);
818
819 if (file->details->thumbnail) {
820 g_object_unref (file->details->thumbnail);
821 }
822 if (file->details->mount) {
823 g_signal_handlers_disconnect_by_func (file->details->mount, file_mount_unmounted, file);
824 g_object_unref (file->details->mount);
825 }
826
827 g_clear_pointer (&file->details->filesystem_id, g_ref_string_release);
828
829 g_list_free_full (file->details->mime_list, g_free);
830 g_list_free_full (file->details->pending_extension_emblems, g_free);
831 g_list_free_full (file->details->extension_emblems, g_free);
832 g_list_free_full (file->details->pending_info_providers, g_object_unref);
833
834 if (file->details->pending_extension_attributes) {
835 g_hash_table_destroy (file->details->pending_extension_attributes);
836 }
837
838 if (file->details->extension_attributes) {
839 g_hash_table_destroy (file->details->extension_attributes);
840 }
841
842 if (file->details->metadata) {
843 metadata_hash_free (file->details->metadata);
844 }
845
846 G_OBJECT_CLASS (caja_file_parent_class)->finalize (object);
847 }
848
849 CajaFile *
caja_file_ref(CajaFile * file)850 caja_file_ref (CajaFile *file)
851 {
852 if (file == NULL) {
853 return NULL;
854 }
855 g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
856
857 #ifdef CAJA_FILE_DEBUG_REF
858 DEBUG_REF_PRINTF("%10p ref'd", file);
859 #endif
860
861 return g_object_ref (file);
862 }
863
864 void
caja_file_unref(CajaFile * file)865 caja_file_unref (CajaFile *file)
866 {
867 if (file == NULL) {
868 return;
869 }
870
871 g_return_if_fail (CAJA_IS_FILE (file));
872
873 #ifdef CAJA_FILE_DEBUG_REF
874 DEBUG_REF_PRINTF("%10p unref'd", file);
875 #endif
876
877 g_object_unref (file);
878 }
879
880 /**
881 * caja_file_get_parent_uri_for_display:
882 *
883 * Get the uri for the parent directory.
884 *
885 * @file: The file in question.
886 *
887 * Return value: A string representing the parent's location,
888 * formatted for user display (including stripping "file://").
889 * If the parent is NULL, returns the empty string.
890 */
891 char *
caja_file_get_parent_uri_for_display(CajaFile * file)892 caja_file_get_parent_uri_for_display (CajaFile *file)
893 {
894 GFile *parent;
895 char *result;
896
897 g_assert (CAJA_IS_FILE (file));
898
899 parent = caja_file_get_parent_location (file);
900 if (parent) {
901 result = g_file_get_parse_name (parent);
902 g_object_unref (parent);
903 } else {
904 result = g_strdup ("");
905 }
906
907 return result;
908 }
909
910 /**
911 * caja_file_get_parent_uri:
912 *
913 * Get the uri for the parent directory.
914 *
915 * @file: The file in question.
916 *
917 * Return value: A string for the parent's location, in "raw URI" form.
918 * Use caja_file_get_parent_uri_for_display instead if the
919 * result is to be displayed on-screen.
920 * If the parent is NULL, returns the empty string.
921 */
922 char *
caja_file_get_parent_uri(CajaFile * file)923 caja_file_get_parent_uri (CajaFile *file)
924 {
925 g_assert (CAJA_IS_FILE (file));
926
927 if (caja_file_is_self_owned (file)) {
928 /* Callers expect an empty string, not a NULL. */
929 return g_strdup ("");
930 }
931
932 return caja_directory_get_uri (file->details->directory);
933 }
934
935 GFile *
caja_file_get_parent_location(CajaFile * file)936 caja_file_get_parent_location (CajaFile *file)
937 {
938 g_assert (CAJA_IS_FILE (file));
939
940 if (caja_file_is_self_owned (file)) {
941 /* Callers expect an empty string, not a NULL. */
942 return NULL;
943 }
944
945 return caja_directory_get_location (file->details->directory);
946 }
947
948 CajaFile *
caja_file_get_parent(CajaFile * file)949 caja_file_get_parent (CajaFile *file)
950 {
951 g_assert (CAJA_IS_FILE (file));
952
953 if (caja_file_is_self_owned (file)) {
954 return NULL;
955 }
956
957 return caja_directory_get_corresponding_file (file->details->directory);
958 }
959
960 /**
961 * caja_file_can_read:
962 *
963 * Check whether the user is allowed to read the contents of this file.
964 *
965 * @file: The file to check.
966 *
967 * Return value: FALSE if the user is definitely not allowed to read
968 * the contents of the file. If the user has read permission, or
969 * the code can't tell whether the user has read permission,
970 * returns TRUE (so failures must always be handled).
971 */
972 gboolean
caja_file_can_read(CajaFile * file)973 caja_file_can_read (CajaFile *file)
974 {
975 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
976
977 return file->details->can_read;
978 }
979
980 /**
981 * caja_file_can_write:
982 *
983 * Check whether the user is allowed to write to this file.
984 *
985 * @file: The file to check.
986 *
987 * Return value: FALSE if the user is definitely not allowed to write
988 * to the file. If the user has write permission, or
989 * the code can't tell whether the user has write permission,
990 * returns TRUE (so failures must always be handled).
991 */
992 gboolean
caja_file_can_write(CajaFile * file)993 caja_file_can_write (CajaFile *file)
994 {
995 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
996
997 return file->details->can_write;
998 }
999
1000 /**
1001 * caja_file_can_execute:
1002 *
1003 * Check whether the user is allowed to execute this file.
1004 *
1005 * @file: The file to check.
1006 *
1007 * Return value: FALSE if the user is definitely not allowed to execute
1008 * the file. If the user has execute permission, or
1009 * the code can't tell whether the user has execute permission,
1010 * returns TRUE (so failures must always be handled).
1011 */
1012 gboolean
caja_file_can_execute(CajaFile * file)1013 caja_file_can_execute (CajaFile *file)
1014 {
1015 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1016
1017 return file->details->can_execute;
1018 }
1019
1020 gboolean
caja_file_can_mount(CajaFile * file)1021 caja_file_can_mount (CajaFile *file)
1022 {
1023 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1024
1025 return file->details->can_mount;
1026 }
1027
1028 gboolean
caja_file_can_unmount(CajaFile * file)1029 caja_file_can_unmount (CajaFile *file)
1030 {
1031 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1032
1033 return file->details->can_unmount ||
1034 (file->details->mount != NULL &&
1035 g_mount_can_unmount (file->details->mount));
1036 }
1037
1038 gboolean
caja_file_can_eject(CajaFile * file)1039 caja_file_can_eject (CajaFile *file)
1040 {
1041 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1042
1043 return file->details->can_eject ||
1044 (file->details->mount != NULL &&
1045 g_mount_can_eject (file->details->mount));
1046 }
1047
1048 gboolean
caja_file_can_start(CajaFile * file)1049 caja_file_can_start (CajaFile *file)
1050 {
1051 gboolean ret;
1052 GDrive *drive;
1053
1054 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1055
1056 ret = FALSE;
1057
1058 if (file->details->can_start) {
1059 ret = TRUE;
1060 goto out;
1061 }
1062
1063 if (file->details->mount != NULL) {
1064 drive = g_mount_get_drive (file->details->mount);
1065 if (drive != NULL) {
1066 ret = g_drive_can_start (drive);
1067 g_object_unref (drive);
1068 }
1069 }
1070
1071 out:
1072 return ret;
1073 }
1074
1075 gboolean
caja_file_can_start_degraded(CajaFile * file)1076 caja_file_can_start_degraded (CajaFile *file)
1077 {
1078 gboolean ret;
1079 GDrive *drive;
1080
1081 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1082
1083 ret = FALSE;
1084
1085 if (file->details->can_start_degraded) {
1086 ret = TRUE;
1087 goto out;
1088 }
1089
1090 if (file->details->mount != NULL) {
1091 drive = g_mount_get_drive (file->details->mount);
1092 if (drive != NULL) {
1093 ret = g_drive_can_start_degraded (drive);
1094 g_object_unref (drive);
1095 }
1096 }
1097
1098 out:
1099 return ret;
1100 }
1101
1102 gboolean
caja_file_can_poll_for_media(CajaFile * file)1103 caja_file_can_poll_for_media (CajaFile *file)
1104 {
1105 gboolean ret;
1106 GDrive *drive;
1107
1108 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1109
1110 ret = FALSE;
1111
1112 if (file->details->can_poll_for_media) {
1113 ret = TRUE;
1114 goto out;
1115 }
1116
1117 if (file->details->mount != NULL) {
1118 drive = g_mount_get_drive (file->details->mount);
1119 if (drive != NULL) {
1120 ret = g_drive_can_poll_for_media (drive);
1121 g_object_unref (drive);
1122 }
1123 }
1124
1125 out:
1126 return ret;
1127 }
1128
1129 gboolean
caja_file_is_media_check_automatic(CajaFile * file)1130 caja_file_is_media_check_automatic (CajaFile *file)
1131 {
1132 gboolean ret;
1133 GDrive *drive;
1134
1135 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1136
1137 ret = FALSE;
1138
1139 if (file->details->is_media_check_automatic) {
1140 ret = TRUE;
1141 goto out;
1142 }
1143
1144 if (file->details->mount != NULL) {
1145 drive = g_mount_get_drive (file->details->mount);
1146 if (drive != NULL) {
1147 ret = g_drive_is_media_check_automatic (drive);
1148 g_object_unref (drive);
1149 }
1150 }
1151
1152 out:
1153 return ret;
1154 }
1155
1156
1157 gboolean
caja_file_can_stop(CajaFile * file)1158 caja_file_can_stop (CajaFile *file)
1159 {
1160 gboolean ret;
1161 GDrive *drive;
1162
1163 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1164
1165 ret = FALSE;
1166
1167 if (file->details->can_stop) {
1168 ret = TRUE;
1169 goto out;
1170 }
1171
1172 if (file->details->mount != NULL) {
1173 drive = g_mount_get_drive (file->details->mount);
1174 if (drive != NULL) {
1175 ret = g_drive_can_stop (drive);
1176 g_object_unref (drive);
1177 }
1178 }
1179
1180 out:
1181 return ret;
1182 }
1183
1184 GDriveStartStopType
caja_file_get_start_stop_type(CajaFile * file)1185 caja_file_get_start_stop_type (CajaFile *file)
1186 {
1187 GDriveStartStopType ret;
1188 GDrive *drive;
1189
1190 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1191
1192 ret = G_DRIVE_START_STOP_TYPE_UNKNOWN;
1193
1194 ret = file->details->start_stop_type;
1195 if (ret != G_DRIVE_START_STOP_TYPE_UNKNOWN)
1196 goto out;
1197
1198 if (file->details->mount != NULL) {
1199 drive = g_mount_get_drive (file->details->mount);
1200 if (drive != NULL) {
1201 ret = g_drive_get_start_stop_type (drive);
1202 g_object_unref (drive);
1203 }
1204 }
1205
1206 out:
1207 return ret;
1208 }
1209
1210 void
caja_file_mount(CajaFile * file,GMountOperation * mount_op,GCancellable * cancellable,CajaFileOperationCallback callback,gpointer callback_data)1211 caja_file_mount (CajaFile *file,
1212 GMountOperation *mount_op,
1213 GCancellable *cancellable,
1214 CajaFileOperationCallback callback,
1215 gpointer callback_data)
1216 {
1217 GError *error;
1218
1219 if (CAJA_FILE_GET_CLASS (file)->mount == NULL) {
1220 if (callback) {
1221 error = NULL;
1222 g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1223 _("This file cannot be mounted"));
1224 callback (file, NULL, error, callback_data);
1225 g_error_free (error);
1226 }
1227 } else {
1228 CAJA_FILE_GET_CLASS (file)->mount (file, mount_op, cancellable, callback, callback_data);
1229 }
1230 }
1231
1232 typedef struct {
1233 CajaFile *file;
1234 CajaFileOperationCallback callback;
1235 gpointer callback_data;
1236 } UnmountData;
1237
1238 static void
unmount_done(void * callback_data)1239 unmount_done (void *callback_data)
1240 {
1241 UnmountData *data;
1242
1243 data = (UnmountData *)callback_data;
1244 if (data->callback) {
1245 data->callback (data->file, NULL, NULL, data->callback_data);
1246 }
1247 caja_file_unref (data->file);
1248 g_free (data);
1249 }
1250
1251 void
caja_file_unmount(CajaFile * file,GMountOperation * mount_op,GCancellable * cancellable,CajaFileOperationCallback callback,gpointer callback_data)1252 caja_file_unmount (CajaFile *file,
1253 GMountOperation *mount_op,
1254 GCancellable *cancellable,
1255 CajaFileOperationCallback callback,
1256 gpointer callback_data)
1257 {
1258 GError *error;
1259 UnmountData *data;
1260
1261 if (file->details->can_unmount) {
1262 if (CAJA_FILE_GET_CLASS (file)->unmount != NULL) {
1263 CAJA_FILE_GET_CLASS (file)->unmount (file, mount_op, cancellable, callback, callback_data);
1264 } else {
1265 if (callback) {
1266 error = NULL;
1267 g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1268 _("This file cannot be unmounted"));
1269 callback (file, NULL, error, callback_data);
1270 g_error_free (error);
1271 }
1272 }
1273 } else if (file->details->mount != NULL &&
1274 g_mount_can_unmount (file->details->mount)) {
1275 data = g_new0 (UnmountData, 1);
1276 data->file = caja_file_ref (file);
1277 data->callback = callback;
1278 data->callback_data = callback_data;
1279 caja_file_operations_unmount_mount_full (NULL, file->details->mount, FALSE, TRUE, unmount_done, data);
1280 } else if (callback) {
1281 callback (file, NULL, NULL, callback_data);
1282 }
1283 }
1284
1285 void
caja_file_eject(CajaFile * file,GMountOperation * mount_op,GCancellable * cancellable,CajaFileOperationCallback callback,gpointer callback_data)1286 caja_file_eject (CajaFile *file,
1287 GMountOperation *mount_op,
1288 GCancellable *cancellable,
1289 CajaFileOperationCallback callback,
1290 gpointer callback_data)
1291 {
1292 GError *error;
1293 UnmountData *data;
1294
1295 if (file->details->can_eject) {
1296 if (CAJA_FILE_GET_CLASS (file)->eject != NULL) {
1297 CAJA_FILE_GET_CLASS (file)->eject (file, mount_op, cancellable, callback, callback_data);
1298 } else {
1299 if (callback) {
1300 error = NULL;
1301 g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1302 _("This file cannot be ejected"));
1303 callback (file, NULL, error, callback_data);
1304 g_error_free (error);
1305 }
1306 }
1307 } else if (file->details->mount != NULL &&
1308 g_mount_can_eject (file->details->mount)) {
1309 data = g_new0 (UnmountData, 1);
1310 data->file = caja_file_ref (file);
1311 data->callback = callback;
1312 data->callback_data = callback_data;
1313 caja_file_operations_unmount_mount_full (NULL, file->details->mount, TRUE, TRUE, unmount_done, data);
1314 } else if (callback) {
1315 callback (file, NULL, NULL, callback_data);
1316 }
1317 }
1318
1319 void
caja_file_start(CajaFile * file,GMountOperation * start_op,GCancellable * cancellable,CajaFileOperationCallback callback,gpointer callback_data)1320 caja_file_start (CajaFile *file,
1321 GMountOperation *start_op,
1322 GCancellable *cancellable,
1323 CajaFileOperationCallback callback,
1324 gpointer callback_data)
1325 {
1326 GError *error;
1327
1328 if ((file->details->can_start || file->details->can_start_degraded) &&
1329 CAJA_FILE_GET_CLASS (file)->start != NULL) {
1330 CAJA_FILE_GET_CLASS (file)->start (file, start_op, cancellable, callback, callback_data);
1331 } else {
1332 if (callback) {
1333 error = NULL;
1334 g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1335 _("This file cannot be started"));
1336 callback (file, NULL, error, callback_data);
1337 g_error_free (error);
1338 }
1339 }
1340 }
1341
1342 static void
file_stop_callback(GObject * source_object,GAsyncResult * res,gpointer callback_data)1343 file_stop_callback (GObject *source_object,
1344 GAsyncResult *res,
1345 gpointer callback_data)
1346 {
1347 CajaFileOperation *op;
1348 gboolean stopped;
1349 GError *error;
1350
1351 op = callback_data;
1352
1353 error = NULL;
1354 stopped = g_drive_stop_finish (G_DRIVE (source_object),
1355 res, &error);
1356
1357 if (!stopped &&
1358 error->domain == G_IO_ERROR &&
1359 (error->code == G_IO_ERROR_FAILED_HANDLED ||
1360 error->code == G_IO_ERROR_CANCELLED)) {
1361 g_error_free (error);
1362 error = NULL;
1363 }
1364
1365 caja_file_operation_complete (op, NULL, error);
1366 if (error) {
1367 g_error_free (error);
1368 }
1369 }
1370
1371 void
caja_file_stop(CajaFile * file,GMountOperation * mount_op,GCancellable * cancellable,CajaFileOperationCallback callback,gpointer callback_data)1372 caja_file_stop (CajaFile *file,
1373 GMountOperation *mount_op,
1374 GCancellable *cancellable,
1375 CajaFileOperationCallback callback,
1376 gpointer callback_data)
1377 {
1378 GError *error;
1379
1380 if (CAJA_FILE_GET_CLASS (file)->stop != NULL) {
1381 if (file->details->can_stop) {
1382 CAJA_FILE_GET_CLASS (file)->stop (file, mount_op, cancellable, callback, callback_data);
1383 } else {
1384 if (callback) {
1385 error = NULL;
1386 g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1387 _("This file cannot be stopped"));
1388 callback (file, NULL, error, callback_data);
1389 g_error_free (error);
1390 }
1391 }
1392 } else {
1393 GDrive *drive;
1394
1395 drive = NULL;
1396 if (file->details->mount != NULL)
1397 drive = g_mount_get_drive (file->details->mount);
1398
1399 if (drive != NULL && g_drive_can_stop (drive)) {
1400 CajaFileOperation *op;
1401
1402 op = caja_file_operation_new (file, callback, callback_data);
1403 if (cancellable) {
1404 g_object_unref (op->cancellable);
1405 op->cancellable = g_object_ref (cancellable);
1406 }
1407
1408 g_drive_stop (drive,
1409 G_MOUNT_UNMOUNT_NONE,
1410 mount_op,
1411 op->cancellable,
1412 file_stop_callback,
1413 op);
1414 } else {
1415 if (callback) {
1416 error = NULL;
1417 g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1418 _("This file cannot be stopped"));
1419 callback (file, NULL, error, callback_data);
1420 g_error_free (error);
1421 }
1422 }
1423
1424 if (drive != NULL) {
1425 g_object_unref (drive);
1426 }
1427 }
1428 }
1429
1430 void
caja_file_poll_for_media(CajaFile * file)1431 caja_file_poll_for_media (CajaFile *file)
1432 {
1433 if (file->details->can_poll_for_media) {
1434 if (CAJA_FILE_GET_CLASS (file)->stop != NULL) {
1435 CAJA_FILE_GET_CLASS (file)->poll_for_media (file);
1436 }
1437 } else if (file->details->mount != NULL) {
1438 GDrive *drive;
1439 drive = g_mount_get_drive (file->details->mount);
1440 if (drive != NULL) {
1441 g_drive_poll_for_media (drive,
1442 NULL, /* cancellable */
1443 NULL, /* GAsyncReadyCallback */
1444 NULL); /* user_data */
1445 g_object_unref (drive);
1446 }
1447 }
1448 }
1449
1450 /**
1451 * caja_file_is_desktop_directory:
1452 *
1453 * Check whether this file is the desktop directory.
1454 *
1455 * @file: The file to check.
1456 *
1457 * Return value: TRUE if this is the physical desktop directory.
1458 */
1459 gboolean
caja_file_is_desktop_directory(CajaFile * file)1460 caja_file_is_desktop_directory (CajaFile *file)
1461 {
1462 GFile *dir;
1463
1464 dir = file->details->directory->details->location;
1465
1466 if (dir == NULL) {
1467 return FALSE;
1468 }
1469
1470 return caja_is_desktop_directory_file (dir, file->details->name);
1471 }
1472
1473 static gboolean
is_desktop_file(CajaFile * file)1474 is_desktop_file (CajaFile *file)
1475 {
1476 return caja_file_is_mime_type (file, "application/x-desktop");
1477 }
1478
1479 static gboolean
can_rename_desktop_file(CajaFile * file)1480 can_rename_desktop_file (CajaFile *file)
1481 {
1482 GFile *location;
1483 gboolean res;
1484
1485 location = caja_file_get_location (file);
1486 res = g_file_is_native (location);
1487 g_object_unref (location);
1488 return res;
1489 }
1490
1491 /**
1492 * caja_file_can_rename:
1493 *
1494 * Check whether the user is allowed to change the name of the file.
1495 *
1496 * @file: The file to check.
1497 *
1498 * Return value: FALSE if the user is definitely not allowed to change
1499 * the name of the file. If the user is allowed to change the name, or
1500 * the code can't tell whether the user is allowed to change the name,
1501 * returns TRUE (so rename failures must always be handled).
1502 */
1503 gboolean
caja_file_can_rename(CajaFile * file)1504 caja_file_can_rename (CajaFile *file)
1505 {
1506 gboolean can_rename;
1507
1508 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1509
1510 /* Nonexistent files can't be renamed. */
1511 if (caja_file_is_gone (file)) {
1512 return FALSE;
1513 }
1514
1515 /* Self-owned files can't be renamed */
1516 if (caja_file_is_self_owned (file)) {
1517 return FALSE;
1518 }
1519
1520 if ((is_desktop_file (file) && !can_rename_desktop_file (file)) ||
1521 caja_file_is_home (file)) {
1522 return FALSE;
1523 }
1524
1525 can_rename = TRUE;
1526
1527 /* Certain types of links can't be renamed */
1528 if (CAJA_IS_DESKTOP_ICON_FILE (file)) {
1529 CajaDesktopLink *link;
1530
1531 link = caja_desktop_icon_file_get_link (CAJA_DESKTOP_ICON_FILE (file));
1532
1533 if (link != NULL) {
1534 can_rename = caja_desktop_link_can_rename (link);
1535 g_object_unref (link);
1536 }
1537 }
1538
1539 if (!can_rename) {
1540 return FALSE;
1541 }
1542
1543 return file->details->can_rename;
1544 }
1545
1546 gboolean
caja_file_can_delete(CajaFile * file)1547 caja_file_can_delete (CajaFile *file)
1548 {
1549 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1550
1551 /* Nonexistent files can't be deleted. */
1552 if (caja_file_is_gone (file)) {
1553 return FALSE;
1554 }
1555
1556 /* Self-owned files can't be deleted */
1557 if (caja_file_is_self_owned (file)) {
1558 return FALSE;
1559 }
1560
1561 return file->details->can_delete;
1562 }
1563
1564 gboolean
caja_file_can_trash(CajaFile * file)1565 caja_file_can_trash (CajaFile *file)
1566 {
1567 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1568
1569 /* Nonexistent files can't be deleted. */
1570 if (caja_file_is_gone (file)) {
1571 return FALSE;
1572 }
1573
1574 /* Self-owned files can't be deleted */
1575 if (caja_file_is_self_owned (file)) {
1576 return FALSE;
1577 }
1578
1579 return file->details->can_trash;
1580 }
1581
1582 GFile *
caja_file_get_location(CajaFile * file)1583 caja_file_get_location (CajaFile *file)
1584 {
1585 GFile *dir;
1586
1587 g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
1588
1589 dir = file->details->directory->details->location;
1590
1591 if (caja_file_is_self_owned (file)) {
1592 return g_object_ref (dir);
1593 }
1594
1595 return g_file_get_child (dir, file->details->name);
1596 }
1597
1598 /* Return the actual uri associated with the passed-in file. */
1599 char *
caja_file_get_uri(CajaFile * file)1600 caja_file_get_uri (CajaFile *file)
1601 {
1602 char *uri;
1603 GFile *loc;
1604
1605 g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
1606
1607 loc = caja_file_get_location (file);
1608 uri = g_file_get_uri (loc);
1609 g_object_unref (loc);
1610
1611 return uri;
1612 }
1613
1614 char *
caja_file_get_uri_scheme(CajaFile * file)1615 caja_file_get_uri_scheme (CajaFile *file)
1616 {
1617 GFile *loc;
1618 char *scheme;
1619
1620 g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
1621
1622 if (file->details->directory == NULL ||
1623 file->details->directory->details->location == NULL) {
1624 return NULL;
1625 }
1626
1627 loc = caja_directory_get_location (file->details->directory);
1628 scheme = g_file_get_uri_scheme (loc);
1629 g_object_unref (loc);
1630
1631 return scheme;
1632 }
1633
1634 CajaFileOperation *
caja_file_operation_new(CajaFile * file,CajaFileOperationCallback callback,gpointer callback_data)1635 caja_file_operation_new (CajaFile *file,
1636 CajaFileOperationCallback callback,
1637 gpointer callback_data)
1638 {
1639 CajaFileOperation *op;
1640
1641 op = g_new0 (CajaFileOperation, 1);
1642 op->file = caja_file_ref (file);
1643 op->callback = callback;
1644 op->callback_data = callback_data;
1645 op->cancellable = g_cancellable_new ();
1646
1647 op->file->details->operations_in_progress = g_list_prepend
1648 (op->file->details->operations_in_progress, op);
1649
1650 return op;
1651 }
1652
1653 static void
caja_file_operation_remove(CajaFileOperation * op)1654 caja_file_operation_remove (CajaFileOperation *op)
1655 {
1656 op->file->details->operations_in_progress = g_list_remove
1657 (op->file->details->operations_in_progress, op);
1658 }
1659
1660 void
caja_file_operation_free(CajaFileOperation * op)1661 caja_file_operation_free (CajaFileOperation *op)
1662 {
1663 caja_file_operation_remove (op);
1664 caja_file_unref (op->file);
1665 g_object_unref (op->cancellable);
1666 if (op->free_data) {
1667 op->free_data (op->data);
1668 }
1669 // Start UNDO-REDO
1670 caja_undostack_manager_add_action (caja_undostack_manager_instance(),
1671 op->undo_redo_data);
1672 // End UNDO-REDO
1673 g_free (op);
1674 }
1675
1676 void
caja_file_operation_complete(CajaFileOperation * op,GFile * result_file,GError * error)1677 caja_file_operation_complete (CajaFileOperation *op, GFile *result_file, GError *error)
1678 {
1679 /* Claim that something changed even if the operation failed.
1680 * This makes it easier for some clients who see the "reverting"
1681 * as "changing back".
1682 */
1683 caja_file_operation_remove (op);
1684 caja_file_changed (op->file);
1685 if (op->callback) {
1686 (* op->callback) (op->file, result_file, error, op->callback_data);
1687 }
1688 caja_file_operation_free (op);
1689 }
1690
1691 void
caja_file_operation_cancel(CajaFileOperation * op)1692 caja_file_operation_cancel (CajaFileOperation *op)
1693 {
1694 /* Cancel the operation if it's still in progress. */
1695 g_cancellable_cancel (op->cancellable);
1696 }
1697
1698 static void
rename_get_info_callback(GObject * source_object,GAsyncResult * res,gpointer callback_data)1699 rename_get_info_callback (GObject *source_object,
1700 GAsyncResult *res,
1701 gpointer callback_data)
1702 {
1703 CajaFileOperation *op;
1704 GFileInfo *new_info;
1705 GError *error;
1706
1707 op = callback_data;
1708
1709 error = NULL;
1710 new_info = g_file_query_info_finish (G_FILE (source_object), res, &error);
1711 if (new_info != NULL) {
1712 CajaDirectory *directory;
1713 CajaFile *existing_file;
1714 char *old_name;
1715 char *old_uri;
1716 char *new_uri;
1717 const char *new_name;
1718
1719 directory = op->file->details->directory;
1720
1721 new_name = g_file_info_get_name (new_info);
1722
1723 /* If there was another file by the same name in this
1724 * directory, mark it gone.
1725 */
1726 existing_file = caja_directory_find_file_by_name (directory, new_name);
1727 if (existing_file != NULL) {
1728 caja_file_mark_gone (existing_file);
1729 caja_file_changed (existing_file);
1730 }
1731
1732 old_uri = caja_file_get_uri (op->file);
1733 old_name = g_strdup (op->file->details->name);
1734
1735 update_info_and_name (op->file, new_info);
1736
1737 g_free (old_name);
1738
1739 new_uri = caja_file_get_uri (op->file);
1740 caja_directory_moved (old_uri, new_uri);
1741 g_free (new_uri);
1742 g_free (old_uri);
1743
1744 /* the rename could have affected the display name if e.g.
1745 * we're in a vfolder where the name comes from a desktop file
1746 * and a rename affects the contents of the desktop file.
1747 */
1748 if (op->file->details->got_custom_display_name) {
1749 caja_file_invalidate_attributes (op->file,
1750 CAJA_FILE_ATTRIBUTE_INFO |
1751 CAJA_FILE_ATTRIBUTE_LINK_INFO);
1752 }
1753
1754 g_object_unref (new_info);
1755 }
1756 caja_file_operation_complete (op, NULL, error);
1757 if (error) {
1758 g_error_free (error);
1759 }
1760 }
1761
1762 static void
rename_callback(GObject * source_object,GAsyncResult * res,gpointer callback_data)1763 rename_callback (GObject *source_object,
1764 GAsyncResult *res,
1765 gpointer callback_data)
1766 {
1767 CajaFileOperation *op;
1768 GFile *new_file;
1769 GError *error;
1770
1771 op = callback_data;
1772
1773 error = NULL;
1774 new_file = g_file_set_display_name_finish (G_FILE (source_object),
1775 res, &error);
1776
1777 if (new_file != NULL) {
1778 // Start UNDO-REDO
1779 caja_undostack_manager_data_set_rename_information(op->undo_redo_data, G_FILE (source_object), new_file);
1780 // End UNDO-REDO
1781 g_file_query_info_async (new_file,
1782 CAJA_FILE_DEFAULT_ATTRIBUTES,
1783 0,
1784 G_PRIORITY_DEFAULT,
1785 op->cancellable,
1786 rename_get_info_callback, op);
1787 } else {
1788 caja_file_operation_complete (op, NULL, error);
1789 g_error_free (error);
1790 }
1791 }
1792
1793 static gboolean
name_is(CajaFile * file,const char * new_name)1794 name_is (CajaFile *file, const char *new_name)
1795 {
1796 const char *old_name;
1797 old_name = file->details->name;
1798 return strcmp (new_name, old_name) == 0;
1799 }
1800
1801 void
caja_file_rename(CajaFile * file,const char * new_name,CajaFileOperationCallback callback,gpointer callback_data)1802 caja_file_rename (CajaFile *file,
1803 const char *new_name,
1804 CajaFileOperationCallback callback,
1805 gpointer callback_data)
1806 {
1807 CajaFileOperation *op;
1808 char *old_name;
1809 char *new_file_name;
1810 gboolean success, name_changed;
1811 gboolean is_renameable_desktop_file;
1812 GFile *location;
1813 GError *error;
1814
1815 g_return_if_fail (CAJA_IS_FILE (file));
1816 g_return_if_fail (new_name != NULL);
1817 g_return_if_fail (callback != NULL);
1818
1819 is_renameable_desktop_file =
1820 is_desktop_file (file) && can_rename_desktop_file (file);
1821
1822 /* Return an error for incoming names containing path separators.
1823 * But not for .desktop files as '/' are allowed for them */
1824 if (strstr (new_name, "/") != NULL && !is_renameable_desktop_file) {
1825 error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
1826 _("Slashes are not allowed in filenames"));
1827 (* callback) (file, NULL, error, callback_data);
1828 g_error_free (error);
1829 return;
1830 }
1831
1832 /* Can't rename a file that's already gone.
1833 * We need to check this here because there may be a new
1834 * file with the same name.
1835 */
1836 if (caja_file_is_gone (file)) {
1837 /* Claim that something changed even if the rename
1838 * failed. This makes it easier for some clients who
1839 * see the "reverting" to the old name as "changing
1840 * back".
1841 */
1842 caja_file_changed (file);
1843 error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
1844 _("File not found"));
1845 (* callback) (file, NULL, error, callback_data);
1846 g_error_free (error);
1847 return;
1848 }
1849
1850 /* Test the name-hasn't-changed case explicitly, for two reasons.
1851 * (1) rename returns an error if new & old are same.
1852 * (2) We don't want to send file-changed signal if nothing changed.
1853 */
1854 if (!CAJA_IS_DESKTOP_ICON_FILE (file) &&
1855 !is_renameable_desktop_file &&
1856 name_is (file, new_name)) {
1857 (* callback) (file, NULL, NULL, callback_data);
1858 return;
1859 }
1860
1861 /* Self-owned files can't be renamed. Test the name-not-actually-changing
1862 * case before this case.
1863 */
1864 if (caja_file_is_self_owned (file)) {
1865 /* Claim that something changed even if the rename
1866 * failed. This makes it easier for some clients who
1867 * see the "reverting" to the old name as "changing
1868 * back".
1869 */
1870 caja_file_changed (file);
1871 error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
1872 _("Toplevel files cannot be renamed"));
1873
1874 (* callback) (file, NULL, error, callback_data);
1875 g_error_free (error);
1876 return;
1877 }
1878
1879 if (CAJA_IS_DESKTOP_ICON_FILE (file)) {
1880 CajaDesktopLink *link;
1881
1882 link = caja_desktop_icon_file_get_link (CAJA_DESKTOP_ICON_FILE (file));
1883 old_name = caja_file_get_display_name (file);
1884
1885 if ((old_name != NULL && strcmp (new_name, old_name) == 0)) {
1886 success = TRUE;
1887 } else {
1888 success = (link != NULL && caja_desktop_link_rename (link, new_name));
1889 }
1890
1891 if (success) {
1892 (* callback) (file, NULL, NULL, callback_data);
1893 } else {
1894 error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED,
1895 _("Unable to rename desktop icon"));
1896 (* callback) (file, NULL, error, callback_data);
1897 g_error_free (error);
1898 }
1899
1900 g_free (old_name);
1901 g_object_unref (link);
1902 return;
1903 }
1904
1905 if (is_renameable_desktop_file) {
1906 char *uri;
1907
1908 /* Don't actually change the name if the new name is the same.
1909 * This helps for the vfolder method where this can happen and
1910 * we want to minimize actual changes
1911 */
1912 uri = caja_file_get_uri (file);
1913 old_name = caja_link_local_get_text (uri);
1914 if (old_name != NULL && strcmp (new_name, old_name) == 0) {
1915 success = TRUE;
1916 name_changed = FALSE;
1917 } else {
1918 success = caja_link_local_set_text (uri, new_name);
1919 name_changed = TRUE;
1920 }
1921 g_free (old_name);
1922 g_free (uri);
1923
1924 if (!success) {
1925 error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED,
1926 _("Unable to rename desktop file"));
1927 (* callback) (file, NULL, error, callback_data);
1928 g_error_free (error);
1929 return;
1930 }
1931 if (!g_str_has_suffix(new_name, ".desktop"))
1932 new_file_name = g_strdup_printf ("%s.desktop", new_name);
1933 else
1934 new_file_name = g_strdup_printf("%s", new_name);
1935 new_file_name = g_strdelimit (new_file_name, "/", '-');
1936
1937 if (name_is (file, new_file_name)) {
1938 if (name_changed) {
1939 caja_file_invalidate_attributes (file,
1940 CAJA_FILE_ATTRIBUTE_INFO |
1941 CAJA_FILE_ATTRIBUTE_LINK_INFO);
1942 }
1943
1944 (* callback) (file, NULL, NULL, callback_data);
1945 g_free (new_file_name);
1946 return;
1947 }
1948 } else {
1949 new_file_name = g_strdup (new_name);
1950 }
1951
1952 /* Set up a renaming operation. */
1953 op = caja_file_operation_new (file, callback, callback_data);
1954 op->is_rename = TRUE;
1955
1956 /* Do the renaming. */
1957
1958 location = caja_file_get_location (file);
1959
1960 // Start UNDO-REDO
1961 if (!caja_undostack_manager_is_undo_redo(caja_undostack_manager_instance())) {
1962 op->undo_redo_data = caja_undostack_manager_data_new (CAJA_UNDOSTACK_RENAME, 1);
1963 }
1964 // End UNDO-REDO
1965
1966 g_file_set_display_name_async (location,
1967 new_file_name,
1968 G_PRIORITY_DEFAULT,
1969 op->cancellable,
1970 rename_callback,
1971 op);
1972 g_free (new_file_name);
1973 g_object_unref (location);
1974 }
1975
1976 gboolean
caja_file_rename_in_progress(CajaFile * file)1977 caja_file_rename_in_progress (CajaFile *file)
1978 {
1979 GList *node;
1980 CajaFileOperation *op = NULL;
1981
1982 for (node = file->details->operations_in_progress; node != NULL; node = node->next) {
1983 op = node->data;
1984 if (op->is_rename) {
1985 return TRUE;
1986 }
1987 }
1988 return FALSE;
1989 }
1990
1991 void
caja_file_cancel(CajaFile * file,CajaFileOperationCallback callback,gpointer callback_data)1992 caja_file_cancel (CajaFile *file,
1993 CajaFileOperationCallback callback,
1994 gpointer callback_data)
1995 {
1996 GList *node, *next;
1997 CajaFileOperation *op = NULL;
1998
1999 for (node = file->details->operations_in_progress; node != NULL; node = next) {
2000 next = node->next;
2001 op = node->data;
2002
2003 g_assert (op->file == file);
2004 if (op->callback == callback && op->callback_data == callback_data) {
2005 caja_file_operation_cancel (op);
2006 }
2007 }
2008 }
2009
2010 gboolean
caja_file_matches_uri(CajaFile * file,const char * match_uri)2011 caja_file_matches_uri (CajaFile *file, const char *match_uri)
2012 {
2013 GFile *match_file, *location;
2014 gboolean result;
2015
2016 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
2017 g_return_val_if_fail (match_uri != NULL, FALSE);
2018
2019 location = caja_file_get_location (file);
2020 match_file = g_file_new_for_uri (match_uri);
2021 result = g_file_equal (location, match_file);
2022 g_object_unref (location);
2023 g_object_unref (match_file);
2024
2025 return result;
2026 }
2027
2028 int
caja_file_compare_location(CajaFile * file_1,CajaFile * file_2)2029 caja_file_compare_location (CajaFile *file_1,
2030 CajaFile *file_2)
2031 {
2032 GFile *loc_a, *loc_b;
2033 gboolean res;
2034
2035 loc_a = caja_file_get_location (file_1);
2036 loc_b = caja_file_get_location (file_2);
2037
2038 res = !g_file_equal (loc_a, loc_b);
2039
2040 g_object_unref (loc_a);
2041 g_object_unref (loc_b);
2042
2043 return (gint) res;
2044 }
2045
2046 gboolean
caja_file_is_local(CajaFile * file)2047 caja_file_is_local (CajaFile *file)
2048 {
2049 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
2050
2051 return caja_directory_is_local (file->details->directory);
2052 }
2053
2054 static void
update_link(CajaFile * link_file,CajaFile * target_file)2055 update_link (CajaFile *link_file, CajaFile *target_file)
2056 {
2057 g_assert (CAJA_IS_FILE (link_file));
2058 g_assert (CAJA_IS_FILE (target_file));
2059
2060 /* FIXME bugzilla.gnome.org 42044: If we don't put any code
2061 * here then the hash table is a waste of time.
2062 */
2063 }
2064
2065 static GList *
get_link_files(CajaFile * target_file)2066 get_link_files (CajaFile *target_file)
2067 {
2068 GList **link_files;
2069
2070 if (symbolic_links == NULL) {
2071 link_files = NULL;
2072 } else {
2073 char *uri;
2074
2075 uri = caja_file_get_uri (target_file);
2076 link_files = g_hash_table_lookup (symbolic_links, uri);
2077 g_free (uri);
2078 }
2079 if (link_files) {
2080 return caja_file_list_copy (*link_files);
2081 }
2082 return NULL;
2083 }
2084
2085 static void
update_links_if_target(CajaFile * target_file)2086 update_links_if_target (CajaFile *target_file)
2087 {
2088 GList *link_files, *p;
2089
2090 link_files = get_link_files (target_file);
2091 for (p = link_files; p != NULL; p = p->next) {
2092 update_link (CAJA_FILE (p->data), target_file);
2093 }
2094 caja_file_list_free (link_files);
2095 }
2096
2097 static gboolean
update_info_internal(CajaFile * file,GFileInfo * info,gboolean update_name)2098 update_info_internal (CajaFile *file,
2099 GFileInfo *info,
2100 gboolean update_name)
2101 {
2102 gboolean changed;
2103 gboolean is_symlink, is_hidden, is_backup, is_mountpoint;
2104 gboolean has_permissions;
2105 guint32 permissions;
2106 gboolean can_read, can_write, can_execute, can_delete, can_trash, can_rename, can_mount, can_unmount, can_eject;
2107 gboolean can_start, can_start_degraded, can_stop, can_poll_for_media, is_media_check_automatic;
2108 GDriveStartStopType start_stop_type;
2109 gboolean thumbnailing_failed;
2110 int uid, gid;
2111 goffset size;
2112 goffset size_on_disk;
2113 int sort_order;
2114 time_t atime, mtime, ctime, btime;
2115 time_t trash_time;
2116 const char * time_string;
2117 const char *symlink_name, *mime_type, *selinux_context, *thumbnail_path;
2118 GFileType file_type;
2119 GIcon *icon;
2120 const char *description;
2121 const char *filesystem_id;
2122 const char *trash_orig_path;
2123 const char *group, *owner, *owner_real;
2124 gboolean free_owner, free_group;
2125
2126 if (file->details->is_gone) {
2127 return FALSE;
2128 }
2129
2130 if (info == NULL) {
2131 caja_file_mark_gone (file);
2132 return TRUE;
2133 }
2134
2135 file->details->file_info_is_up_to_date = TRUE;
2136
2137 /* FIXME bugzilla.gnome.org 42044: Need to let links that
2138 * point to the old name know that the file has been renamed.
2139 */
2140
2141 remove_from_link_hash_table (file);
2142
2143 changed = FALSE;
2144
2145 if (!file->details->got_file_info) {
2146 changed = TRUE;
2147 }
2148 file->details->got_file_info = TRUE;
2149
2150 changed |= caja_file_set_display_name (file,
2151 g_file_info_get_display_name (info),
2152 g_file_info_get_edit_name (info),
2153 FALSE);
2154
2155 file_type = g_file_info_get_file_type (info);
2156 if (file->details->type != file_type) {
2157 changed = TRUE;
2158 }
2159 file->details->type = file_type;
2160
2161 if (!file->details->got_custom_activation_uri) {
2162 const char *activation_uri;
2163
2164 activation_uri = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
2165 if (activation_uri == NULL) {
2166 if (file->details->activation_uri) {
2167 g_free (file->details->activation_uri);
2168 file->details->activation_uri = NULL;
2169 changed = TRUE;
2170 }
2171 } else {
2172 char *old_activation_uri;
2173
2174 old_activation_uri = file->details->activation_uri;
2175 file->details->activation_uri = g_strdup (activation_uri);
2176
2177 if (old_activation_uri) {
2178 if (strcmp (old_activation_uri,
2179 file->details->activation_uri) != 0) {
2180 changed = TRUE;
2181 }
2182 g_free (old_activation_uri);
2183 } else {
2184 changed = TRUE;
2185 }
2186 }
2187 }
2188
2189 is_symlink = g_file_info_get_is_symlink (info);
2190 if (file->details->is_symlink != is_symlink) {
2191 changed = TRUE;
2192 }
2193 file->details->is_symlink = is_symlink;
2194
2195 is_hidden = g_file_info_get_is_hidden (info);
2196 is_backup = g_file_info_get_is_backup (info);
2197 if (file->details->is_hidden != is_hidden ||
2198 file->details->is_backup != is_backup) {
2199 changed = TRUE;
2200 }
2201 file->details->is_hidden = is_hidden;
2202 file->details->is_backup = is_backup;
2203
2204 is_mountpoint = g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT);
2205 if (file->details->is_mountpoint != is_mountpoint) {
2206 changed = TRUE;
2207 }
2208 file->details->is_mountpoint = is_mountpoint;
2209
2210 has_permissions = g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_UNIX_MODE);
2211 permissions = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE);;
2212 if (file->details->has_permissions != has_permissions ||
2213 file->details->permissions != permissions) {
2214 changed = TRUE;
2215 }
2216 file->details->has_permissions = has_permissions;
2217 file->details->permissions = permissions;
2218
2219 /* We default to TRUE for this if we can't know */
2220 can_read = TRUE;
2221 can_write = TRUE;
2222 can_execute = TRUE;
2223 can_delete = TRUE;
2224 can_trash = TRUE;
2225 can_rename = TRUE;
2226 can_mount = FALSE;
2227 can_unmount = FALSE;
2228 can_eject = FALSE;
2229 can_start = FALSE;
2230 can_start_degraded = FALSE;
2231 can_stop = FALSE;
2232 can_poll_for_media = FALSE;
2233 is_media_check_automatic = FALSE;
2234 start_stop_type = G_DRIVE_START_STOP_TYPE_UNKNOWN;
2235 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) {
2236 can_read = g_file_info_get_attribute_boolean (info,
2237 G_FILE_ATTRIBUTE_ACCESS_CAN_READ);
2238 }
2239 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) {
2240 can_write = g_file_info_get_attribute_boolean (info,
2241 G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
2242 }
2243 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE)) {
2244 can_execute = g_file_info_get_attribute_boolean (info,
2245 G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE);
2246 }
2247 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE)) {
2248 can_delete = g_file_info_get_attribute_boolean (info,
2249 G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE);
2250 }
2251 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH)) {
2252 can_trash = g_file_info_get_attribute_boolean (info,
2253 G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH);
2254 }
2255 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME)) {
2256 can_rename = g_file_info_get_attribute_boolean (info,
2257 G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME);
2258 }
2259 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT)) {
2260 can_mount = g_file_info_get_attribute_boolean (info,
2261 G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT);
2262 }
2263 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT)) {
2264 can_unmount = g_file_info_get_attribute_boolean (info,
2265 G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT);
2266 }
2267 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_EJECT)) {
2268 can_eject = g_file_info_get_attribute_boolean (info,
2269 G_FILE_ATTRIBUTE_MOUNTABLE_CAN_EJECT);
2270 }
2271 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_START)) {
2272 can_start = g_file_info_get_attribute_boolean (info,
2273 G_FILE_ATTRIBUTE_MOUNTABLE_CAN_START);
2274 }
2275 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_START_DEGRADED)) {
2276 can_start_degraded = g_file_info_get_attribute_boolean (info,
2277 G_FILE_ATTRIBUTE_MOUNTABLE_CAN_START_DEGRADED);
2278 }
2279 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_STOP)) {
2280 can_stop = g_file_info_get_attribute_boolean (info,
2281 G_FILE_ATTRIBUTE_MOUNTABLE_CAN_STOP);
2282 }
2283 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_MOUNTABLE_START_STOP_TYPE)) {
2284 start_stop_type = g_file_info_get_attribute_uint32 (info,
2285 G_FILE_ATTRIBUTE_MOUNTABLE_START_STOP_TYPE);
2286 }
2287 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_POLL)) {
2288 can_poll_for_media = g_file_info_get_attribute_boolean (info,
2289 G_FILE_ATTRIBUTE_MOUNTABLE_CAN_POLL);
2290 }
2291 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_MOUNTABLE_IS_MEDIA_CHECK_AUTOMATIC)) {
2292 is_media_check_automatic = g_file_info_get_attribute_boolean (info,
2293 G_FILE_ATTRIBUTE_MOUNTABLE_IS_MEDIA_CHECK_AUTOMATIC);
2294 }
2295 if (file->details->can_read != can_read ||
2296 file->details->can_write != can_write ||
2297 file->details->can_execute != can_execute ||
2298 file->details->can_delete != can_delete ||
2299 file->details->can_trash != can_trash ||
2300 file->details->can_rename != can_rename ||
2301 file->details->can_mount != can_mount ||
2302 file->details->can_unmount != can_unmount ||
2303 file->details->can_eject != can_eject ||
2304 file->details->can_start != can_start ||
2305 file->details->can_start_degraded != can_start_degraded ||
2306 file->details->can_stop != can_stop ||
2307 file->details->start_stop_type != start_stop_type ||
2308 file->details->can_poll_for_media != can_poll_for_media ||
2309 file->details->is_media_check_automatic != is_media_check_automatic) {
2310 changed = TRUE;
2311 }
2312
2313 file->details->can_read = can_read;
2314 file->details->can_write = can_write;
2315 file->details->can_execute = can_execute;
2316 file->details->can_delete = can_delete;
2317 file->details->can_trash = can_trash;
2318 file->details->can_rename = can_rename;
2319 file->details->can_mount = can_mount;
2320 file->details->can_unmount = can_unmount;
2321 file->details->can_eject = can_eject;
2322 file->details->can_start = can_start;
2323 file->details->can_start_degraded = can_start_degraded;
2324 file->details->can_stop = can_stop;
2325 file->details->start_stop_type = start_stop_type;
2326 file->details->can_poll_for_media = can_poll_for_media;
2327 file->details->is_media_check_automatic = is_media_check_automatic;
2328
2329 free_owner = FALSE;
2330 owner = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_OWNER_USER);
2331 owner_real = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_OWNER_USER_REAL);
2332 free_group = FALSE;
2333 group = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_OWNER_GROUP);
2334
2335 uid = -1;
2336 gid = -1;
2337 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_UNIX_UID)) {
2338 uid = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID);
2339 if (owner == NULL) {
2340 free_owner = TRUE;
2341 owner = g_strdup_printf ("%d", uid);
2342 }
2343 }
2344 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_UNIX_GID)) {
2345 gid = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID);
2346 if (group == NULL) {
2347 free_group = TRUE;
2348 group = g_strdup_printf ("%d", gid);
2349 }
2350 }
2351 if (file->details->uid != uid ||
2352 file->details->gid != gid) {
2353 changed = TRUE;
2354 }
2355 file->details->uid = uid;
2356 file->details->gid = gid;
2357
2358 if (eel_strcmp (file->details->owner, owner) != 0) {
2359 changed = TRUE;
2360 g_clear_pointer (&file->details->owner, g_ref_string_release);
2361 file->details->owner = g_ref_string_new_intern (owner);
2362 }
2363
2364 if (eel_strcmp (file->details->owner_real, owner_real) != 0) {
2365 changed = TRUE;
2366 g_clear_pointer (&file->details->owner_real, g_ref_string_release);
2367 file->details->owner_real = g_ref_string_new_intern (owner_real);
2368 }
2369
2370 if (eel_strcmp (file->details->group, group) != 0) {
2371 changed = TRUE;
2372 g_clear_pointer (&file->details->group, g_ref_string_release);
2373 file->details->group = g_ref_string_new_intern (group);
2374 }
2375
2376 if (free_owner) {
2377 g_free ((char *)owner);
2378 }
2379 if (free_group) {
2380 g_free ((char *)group);
2381 }
2382
2383 size = -1;
2384 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE)) {
2385 size = g_file_info_get_size (info);
2386 }
2387 if (file->details->size != size) {
2388 changed = TRUE;
2389 }
2390 file->details->size = size;
2391
2392 size_on_disk = -1;
2393 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE)) {
2394 size_on_disk = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE);
2395 }
2396 if (file->details->size_on_disk != size_on_disk) {
2397 changed = TRUE;
2398 }
2399 file->details->size_on_disk = size_on_disk;
2400
2401 sort_order = g_file_info_get_sort_order (info);
2402 if (file->details->sort_order != sort_order) {
2403 changed = TRUE;
2404 }
2405 file->details->sort_order = sort_order;
2406
2407 atime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
2408 ctime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_CHANGED);
2409 mtime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
2410 btime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_CREATED);
2411 if (file->details->atime != atime ||
2412 file->details->mtime != mtime ||
2413 file->details->ctime != ctime ||
2414 file->details->btime != btime) {
2415 if (file->details->thumbnail == NULL) {
2416 file->details->thumbnail_is_up_to_date = FALSE;
2417 }
2418
2419 changed = TRUE;
2420 }
2421 file->details->atime = atime;
2422 file->details->ctime = ctime;
2423 file->details->mtime = mtime;
2424 file->details->btime = btime;
2425
2426 if (file->details->thumbnail != NULL &&
2427 file->details->thumbnail_mtime != 0 &&
2428 file->details->thumbnail_mtime != mtime) {
2429 file->details->thumbnail_is_up_to_date = FALSE;
2430 changed = TRUE;
2431 }
2432
2433 icon = g_file_info_get_icon (info);
2434 if (!g_icon_equal (icon, file->details->icon)) {
2435 changed = TRUE;
2436
2437 if (file->details->icon) {
2438 g_object_unref (file->details->icon);
2439 }
2440 file->details->icon = g_object_ref (icon);
2441 }
2442
2443 thumbnail_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
2444 if (eel_strcmp (file->details->thumbnail_path, thumbnail_path) != 0) {
2445 changed = TRUE;
2446 g_free (file->details->thumbnail_path);
2447 file->details->thumbnail_path = g_strdup (thumbnail_path);
2448 }
2449
2450 thumbnailing_failed = g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED);
2451 if (file->details->thumbnailing_failed != thumbnailing_failed) {
2452 changed = TRUE;
2453 file->details->thumbnailing_failed = thumbnailing_failed;
2454 }
2455
2456 symlink_name = g_file_info_get_symlink_target (info);
2457 if (eel_strcmp (file->details->symlink_name, symlink_name) != 0) {
2458 changed = TRUE;
2459 g_free (file->details->symlink_name);
2460 file->details->symlink_name = g_strdup (symlink_name);
2461 }
2462
2463 mime_type = g_file_info_get_content_type (info);
2464 if (eel_strcmp (file->details->mime_type, mime_type) != 0) {
2465 changed = TRUE;
2466 g_clear_pointer (&file->details->mime_type, g_ref_string_release);
2467 file->details->mime_type = g_ref_string_new_intern (mime_type);
2468 }
2469
2470 selinux_context = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_SELINUX_CONTEXT);
2471 if (eel_strcmp (file->details->selinux_context, selinux_context) != 0) {
2472 changed = TRUE;
2473 g_free (file->details->selinux_context);
2474 file->details->selinux_context = g_strdup (selinux_context);
2475 }
2476
2477 description = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION);
2478 if (eel_strcmp (file->details->description, description) != 0) {
2479 changed = TRUE;
2480 g_free (file->details->description);
2481 file->details->description = g_strdup (description);
2482 }
2483
2484 filesystem_id = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILESYSTEM);
2485 if (eel_strcmp (file->details->filesystem_id, filesystem_id) != 0) {
2486 changed = TRUE;
2487 g_clear_pointer (&file->details->filesystem_id, g_ref_string_release);
2488 file->details->filesystem_id = g_ref_string_new_intern (filesystem_id);
2489 }
2490
2491 trash_time = 0;
2492 time_string = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_TRASH_DELETION_DATE);
2493 if (time_string != NULL) {
2494 #if GLIB_CHECK_VERSION(2,61,2)
2495 GDateTime *dt;
2496 GTimeZone *tz;
2497 tz = g_time_zone_new_local ();
2498 dt = g_date_time_new_from_iso8601 (time_string, tz);
2499 if (dt) {
2500 trash_time = (time_t) g_date_time_to_unix (dt);
2501 g_date_time_unref (dt);
2502 }
2503 g_time_zone_unref (tz);
2504 #else
2505 GTimeVal g_trash_time;
2506 g_time_val_from_iso8601 (time_string, &g_trash_time);
2507 trash_time = g_trash_time.tv_sec;
2508 #endif
2509 }
2510 if (file->details->trash_time != trash_time) {
2511 changed = TRUE;
2512 file->details->trash_time = trash_time;
2513 }
2514
2515 trash_orig_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_TRASH_ORIG_PATH);
2516 if (eel_strcmp (file->details->trash_orig_path, trash_orig_path) != 0) {
2517 changed = TRUE;
2518 g_free (file->details->trash_orig_path);
2519 file->details->trash_orig_path = g_strdup (trash_orig_path);
2520 }
2521
2522 changed |=
2523 caja_file_update_metadata_from_info (file, info);
2524
2525 if (update_name) {
2526 const char *name;
2527
2528 name = g_file_info_get_name (info);
2529 if (file->details->name == NULL ||
2530 strcmp (file->details->name, name) != 0) {
2531 GList *node;
2532
2533 changed = TRUE;
2534
2535 node = caja_directory_begin_file_name_change
2536 (file->details->directory, file);
2537
2538 g_clear_pointer (&file->details->name, g_ref_string_release);
2539 if (eel_strcmp (file->details->display_name, name) == 0) {
2540 file->details->name = g_ref_string_acquire (file->details->display_name);
2541 } else {
2542 file->details->name = g_ref_string_new (name);
2543 }
2544
2545 if (!file->details->got_custom_display_name &&
2546 g_file_info_get_display_name (info) == NULL) {
2547 /* If the file info's display name is NULL,
2548 * caja_file_set_display_name() did
2549 * not unset the display name.
2550 */
2551 caja_file_clear_display_name (file);
2552 }
2553
2554 caja_directory_end_file_name_change
2555 (file->details->directory, file, node);
2556 }
2557 }
2558
2559 if (changed) {
2560 add_to_link_hash_table (file);
2561
2562 update_links_if_target (file);
2563 }
2564
2565 return changed;
2566 }
2567
2568 static gboolean
update_info_and_name(CajaFile * file,GFileInfo * info)2569 update_info_and_name (CajaFile *file,
2570 GFileInfo *info)
2571 {
2572 return update_info_internal (file, info, TRUE);
2573 }
2574
2575 gboolean
caja_file_update_info(CajaFile * file,GFileInfo * info)2576 caja_file_update_info (CajaFile *file,
2577 GFileInfo *info)
2578 {
2579 return update_info_internal (file, info, FALSE);
2580 }
2581
2582 void
caja_file_refresh_info(CajaFile * file)2583 caja_file_refresh_info (CajaFile *file)
2584 {
2585 GFile *gfile;
2586 GFileInfo *new_info;
2587
2588 gfile = caja_file_get_location (file);
2589 new_info = g_file_query_info (gfile, CAJA_FILE_DEFAULT_ATTRIBUTES,
2590 G_FILE_QUERY_INFO_NONE, NULL, NULL);
2591 if (new_info != NULL) {
2592 if (caja_file_update_info (file, new_info)) {
2593 caja_file_changed (file);
2594 }
2595 g_object_unref (new_info);
2596 }
2597 g_object_unref (gfile);
2598 }
2599
2600 static gboolean
update_name_internal(CajaFile * file,const char * name,gboolean in_directory)2601 update_name_internal (CajaFile *file,
2602 const char *name,
2603 gboolean in_directory)
2604 {
2605 GList *node;
2606
2607 g_assert (name != NULL);
2608
2609 if (file->details->is_gone) {
2610 return FALSE;
2611 }
2612
2613 if (name_is (file, name)) {
2614 return FALSE;
2615 }
2616
2617 node = NULL;
2618 if (in_directory) {
2619 node = caja_directory_begin_file_name_change
2620 (file->details->directory, file);
2621 }
2622
2623 g_clear_pointer (&file->details->name, g_ref_string_release);
2624 file->details->name = g_ref_string_new (name);
2625
2626 if (!file->details->got_custom_display_name) {
2627 caja_file_clear_display_name (file);
2628 }
2629
2630 if (in_directory) {
2631 caja_directory_end_file_name_change
2632 (file->details->directory, file, node);
2633 }
2634
2635 return TRUE;
2636 }
2637
2638 gboolean
caja_file_update_name(CajaFile * file,const char * name)2639 caja_file_update_name (CajaFile *file, const char *name)
2640 {
2641 gboolean ret;
2642
2643 ret = update_name_internal (file, name, TRUE);
2644
2645 if (ret) {
2646 update_links_if_target (file);
2647 }
2648
2649 return ret;
2650 }
2651
2652 gboolean
caja_file_update_name_and_directory(CajaFile * file,const char * name,CajaDirectory * new_directory)2653 caja_file_update_name_and_directory (CajaFile *file,
2654 const char *name,
2655 CajaDirectory *new_directory)
2656 {
2657 CajaDirectory *old_directory;
2658 FileMonitors *monitors;
2659
2660 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
2661 g_return_val_if_fail (CAJA_IS_DIRECTORY (file->details->directory), FALSE);
2662 g_return_val_if_fail (!file->details->is_gone, FALSE);
2663 g_return_val_if_fail (!caja_file_is_self_owned (file), FALSE);
2664 g_return_val_if_fail (CAJA_IS_DIRECTORY (new_directory), FALSE);
2665
2666 old_directory = file->details->directory;
2667 if (old_directory == new_directory) {
2668 if (name) {
2669 return update_name_internal (file, name, TRUE);
2670 } else {
2671 return FALSE;
2672 }
2673 }
2674
2675 caja_file_ref (file);
2676
2677 /* FIXME bugzilla.gnome.org 42044: Need to let links that
2678 * point to the old name know that the file has been moved.
2679 */
2680
2681 remove_from_link_hash_table (file);
2682
2683 monitors = caja_directory_remove_file_monitors (old_directory, file);
2684 caja_directory_remove_file (old_directory, file);
2685
2686 file->details->directory = caja_directory_ref (new_directory);
2687 caja_directory_unref (old_directory);
2688
2689 if (name) {
2690 update_name_internal (file, name, FALSE);
2691 }
2692
2693 caja_directory_add_file (new_directory, file);
2694 caja_directory_add_file_monitors (new_directory, file, monitors);
2695
2696 add_to_link_hash_table (file);
2697
2698 update_links_if_target (file);
2699
2700 caja_file_unref (file);
2701
2702 return TRUE;
2703 }
2704
2705 void
caja_file_set_directory(CajaFile * file,CajaDirectory * new_directory)2706 caja_file_set_directory (CajaFile *file,
2707 CajaDirectory *new_directory)
2708 {
2709 caja_file_update_name_and_directory (file, NULL, new_directory);
2710 }
2711
2712 static Knowledge
get_item_count(CajaFile * file,guint * count)2713 get_item_count (CajaFile *file,
2714 guint *count)
2715 {
2716 gboolean known, unreadable;
2717
2718 known = caja_file_get_directory_item_count
2719 (file, count, &unreadable);
2720 if (!known) {
2721 return UNKNOWN;
2722 }
2723 if (unreadable) {
2724 return UNKNOWABLE;
2725 }
2726 return KNOWN;
2727 }
2728
2729 static Knowledge
get_size(CajaFile * file,goffset * size,gboolean size_on_disk)2730 get_size (CajaFile *file,
2731 goffset *size,
2732 gboolean size_on_disk)
2733 {
2734 /* If we tried and failed, then treat it like there is no size
2735 * to know.
2736 */
2737 if (file->details->get_info_failed) {
2738 return UNKNOWABLE;
2739 }
2740
2741 /* If the info is NULL that means we haven't even tried yet,
2742 * so it's just unknown, not unknowable.
2743 */
2744 if (!file->details->got_file_info) {
2745 return UNKNOWN;
2746 }
2747
2748 /* If we got info with no size in it, it means there is no
2749 * such thing as a size as far as mate-vfs is concerned,
2750 * so "unknowable".
2751 */
2752 if (size_on_disk && file->details->size_on_disk == -1) {
2753 return UNKNOWABLE;
2754 }
2755
2756 if (!size_on_disk && file->details->size == -1) {
2757 return UNKNOWABLE;
2758 }
2759
2760 /* We have a size! */
2761 if (size_on_disk) {
2762 *size = file->details->size_on_disk;
2763 } else {
2764 *size = file->details->size;
2765 }
2766
2767 return KNOWN;
2768 }
2769
2770 static Knowledge
get_time(CajaFile * file,time_t * time_out,CajaDateType type)2771 get_time (CajaFile *file,
2772 time_t *time_out,
2773 CajaDateType type)
2774 {
2775 time_t time;
2776
2777 /* If we tried and failed, then treat it like there is no size
2778 * to know.
2779 */
2780 if (file->details->get_info_failed) {
2781 return UNKNOWABLE;
2782 }
2783
2784 /* If the info is NULL that means we haven't even tried yet,
2785 * so it's just unknown, not unknowable.
2786 */
2787 if (!file->details->got_file_info) {
2788 return UNKNOWN;
2789 }
2790
2791 time = 0;
2792 switch (type) {
2793 case CAJA_DATE_TYPE_MODIFIED:
2794 time = file->details->mtime;
2795 break;
2796 case CAJA_DATE_TYPE_ACCESSED:
2797 time = file->details->atime;
2798 break;
2799 case CAJA_DATE_TYPE_CREATED:
2800 time = file->details->btime;
2801 break;
2802 case CAJA_DATE_TYPE_TRASHED:
2803 time = file->details->trash_time;
2804 break;
2805 default:
2806 g_assert_not_reached ();
2807 break;
2808 }
2809
2810 *time_out = time;
2811
2812 /* If we got info with no modification time in it, it means
2813 * there is no such thing as a modification time as far as
2814 * mate-vfs is concerned, so "unknowable".
2815 */
2816 if (time == 0) {
2817 return UNKNOWABLE;
2818 }
2819 return KNOWN;
2820 }
2821
2822 static int
compare_directories_by_count(CajaFile * file_1,CajaFile * file_2)2823 compare_directories_by_count (CajaFile *file_1, CajaFile *file_2)
2824 {
2825 /* Sort order:
2826 * Directories with unknown # of items
2827 * Directories with "unknowable" # of items
2828 * Directories with 0 items
2829 * Directories with n items
2830 */
2831
2832 Knowledge count_known_1, count_known_2;
2833 guint count_1, count_2;
2834
2835 count_known_1 = get_item_count (file_1, &count_1);
2836 count_known_2 = get_item_count (file_2, &count_2);
2837
2838 if (count_known_1 > count_known_2) {
2839 return -1;
2840 }
2841 if (count_known_1 < count_known_2) {
2842 return +1;
2843 }
2844
2845 /* count_known_1 and count_known_2 are equal now. Check if count
2846 * details are UNKNOWABLE or UNKNOWN.
2847 */
2848 if (count_known_1 == UNKNOWABLE || count_known_1 == UNKNOWN) {
2849 return 0;
2850 }
2851
2852 if (count_1 < count_2) {
2853 return -1;
2854 }
2855 if (count_1 > count_2) {
2856 return +1;
2857 }
2858
2859 return 0;
2860 }
2861
2862 static int
compare_files_by_size(CajaFile * file_1,CajaFile * file_2,gboolean size_on_disk)2863 compare_files_by_size (CajaFile *file_1, CajaFile *file_2, gboolean size_on_disk)
2864 {
2865 /* Sort order:
2866 * Files with unknown size.
2867 * Files with "unknowable" size.
2868 * Files with smaller sizes.
2869 * Files with large sizes.
2870 */
2871
2872 Knowledge size_known_1, size_known_2;
2873 goffset size_1 = 0, size_2 = 0;
2874
2875 size_known_1 = get_size (file_1, &size_1, size_on_disk);
2876 size_known_2 = get_size (file_2, &size_2, size_on_disk);
2877
2878 if (size_known_1 > size_known_2) {
2879 return -1;
2880 }
2881 if (size_known_1 < size_known_2) {
2882 return +1;
2883 }
2884
2885 /* size_known_1 and size_known_2 are equal now. Check if size
2886 * details are UNKNOWABLE or UNKNOWN
2887 */
2888 if (size_known_1 == UNKNOWABLE || size_known_1 == UNKNOWN) {
2889 return 0;
2890 }
2891
2892 if (size_1 < size_2) {
2893 return -1;
2894 }
2895 if (size_1 > size_2) {
2896 return +1;
2897 }
2898
2899 return 0;
2900 }
2901
2902 static int
compare_by_size(CajaFile * file_1,CajaFile * file_2,gboolean size_on_disk)2903 compare_by_size (CajaFile *file_1, CajaFile *file_2, gboolean size_on_disk)
2904 {
2905 /* Sort order:
2906 * Directories with n items
2907 * Directories with 0 items
2908 * Directories with "unknowable" # of items
2909 * Directories with unknown # of items
2910 * Files with large sizes.
2911 * Files with smaller sizes.
2912 * Files with "unknowable" size.
2913 * Files with unknown size.
2914 */
2915
2916 gboolean is_directory_1, is_directory_2;
2917
2918 is_directory_1 = caja_file_is_directory (file_1);
2919 is_directory_2 = caja_file_is_directory (file_2);
2920
2921 if (is_directory_1 && !is_directory_2) {
2922 return -1;
2923 }
2924 if (is_directory_2 && !is_directory_1) {
2925 return +1;
2926 }
2927
2928 if (is_directory_1) {
2929 return compare_directories_by_count (file_1, file_2);
2930 } else {
2931 return compare_files_by_size (file_1, file_2, size_on_disk);
2932 }
2933 }
2934
2935 static int
compare_by_display_name(CajaFile * file_1,CajaFile * file_2)2936 compare_by_display_name (CajaFile *file_1, CajaFile *file_2)
2937 {
2938 const char *name_1, *name_2;
2939 const char *key_1, *key_2;
2940 gboolean sort_last_1, sort_last_2;
2941 int compare;
2942
2943 name_1 = caja_file_peek_display_name (file_1);
2944 name_2 = caja_file_peek_display_name (file_2);
2945
2946 sort_last_1 = name_1[0] == SORT_LAST_CHAR1 || name_1[0] == SORT_LAST_CHAR2;
2947 sort_last_2 = name_2[0] == SORT_LAST_CHAR1 || name_2[0] == SORT_LAST_CHAR2;
2948
2949 if (sort_last_1 && !sort_last_2) {
2950 compare = +1;
2951 } else if (!sort_last_1 && sort_last_2) {
2952 compare = -1;
2953 } else {
2954 key_1 = caja_file_peek_display_name_collation_key (file_1);
2955 key_2 = caja_file_peek_display_name_collation_key (file_2);
2956 compare = strcmp (key_1, key_2);
2957 }
2958
2959 return compare;
2960 }
2961
2962 static int
compare_by_directory_name(CajaFile * file_1,CajaFile * file_2)2963 compare_by_directory_name (CajaFile *file_1, CajaFile *file_2)
2964 {
2965 char *directory_1, *directory_2;
2966 int compare;
2967
2968 if (file_1->details->directory == file_2->details->directory) {
2969 return 0;
2970 }
2971
2972 directory_1 = caja_file_get_parent_uri_for_display (file_1);
2973 directory_2 = caja_file_get_parent_uri_for_display (file_2);
2974
2975 compare = g_utf8_collate (directory_1, directory_2);
2976
2977 g_free (directory_1);
2978 g_free (directory_2);
2979
2980 return compare;
2981 }
2982
2983 static gboolean
file_has_note(CajaFile * file)2984 file_has_note (CajaFile *file)
2985 {
2986 char *note;
2987 gboolean res;
2988
2989 note = caja_file_get_metadata (file, CAJA_METADATA_KEY_ANNOTATION, NULL);
2990 res = note != NULL && note[0] != 0;
2991 g_free (note);
2992
2993 return res;
2994 }
2995
2996 static GList *
prepend_automatic_keywords(CajaFile * file,GList * names)2997 prepend_automatic_keywords (CajaFile *file,
2998 GList *names)
2999 {
3000 /* Prepend in reverse order. */
3001 CajaFile *parent;
3002
3003 parent = caja_file_get_parent (file);
3004
3005 #ifdef TRASH_IS_FAST_ENOUGH
3006 if (caja_file_is_in_trash (file)) {
3007 names = g_list_prepend
3008 (names, g_strdup (CAJA_FILE_EMBLEM_NAME_TRASH));
3009 }
3010 #endif
3011 if (file_has_note (file)) {
3012 names = g_list_prepend
3013 (names, g_strdup (CAJA_FILE_EMBLEM_NAME_NOTE));
3014 }
3015
3016 /* Trash files are assumed to be read-only,
3017 * so we want to ignore them here. */
3018 if (!caja_file_can_write (file) &&
3019 !caja_file_is_in_trash (file) &&
3020 (parent == NULL || caja_file_can_write (parent))) {
3021 names = g_list_prepend
3022 (names, g_strdup (CAJA_FILE_EMBLEM_NAME_CANT_WRITE));
3023 }
3024 if (!caja_file_can_read (file)) {
3025 names = g_list_prepend
3026 (names, g_strdup (CAJA_FILE_EMBLEM_NAME_CANT_READ));
3027 }
3028 if (caja_file_is_symbolic_link (file)) {
3029 names = g_list_prepend
3030 (names, g_strdup (CAJA_FILE_EMBLEM_NAME_SYMBOLIC_LINK));
3031 }
3032
3033 if (parent) {
3034 caja_file_unref (parent);
3035 }
3036
3037
3038 return names;
3039 }
3040
3041 static void
fill_emblem_cache_if_needed(CajaFile * file)3042 fill_emblem_cache_if_needed (CajaFile *file)
3043 {
3044 GList *node, *keywords;
3045 char *scanner;
3046 size_t length;
3047
3048 if (file->details->compare_by_emblem_cache != NULL) {
3049 /* Got a cache already. */
3050 return;
3051 }
3052
3053 keywords = caja_file_get_keywords (file);
3054
3055 /* Add up the keyword string lengths */
3056 length = 1;
3057 for (node = keywords; node != NULL; node = node->next) {
3058 length += strlen ((const char *) node->data) + 1;
3059 }
3060
3061 /* Now that we know how large the cache struct needs to be, allocate it. */
3062 file->details->compare_by_emblem_cache = g_malloc (sizeof(CajaFileSortByEmblemCache) + length);
3063
3064 /* Copy them into the cache. */
3065 scanner = file->details->compare_by_emblem_cache->emblem_keywords;
3066 for (node = keywords; node != NULL; node = node->next) {
3067 length = strlen ((const char *) node->data) + 1;
3068 memcpy (scanner, (const char *) node->data, length);
3069 scanner += length;
3070 }
3071
3072 /* Zero-terminate so we can tell where the list ends. */
3073 *scanner = 0;
3074
3075 g_list_free_full (keywords, g_free);
3076 }
3077
3078 static int
compare_by_emblems(CajaFile * file_1,CajaFile * file_2)3079 compare_by_emblems (CajaFile *file_1, CajaFile *file_2)
3080 {
3081 const char *keyword_cache_1, *keyword_cache_2;
3082 int compare_result;
3083
3084 fill_emblem_cache_if_needed (file_1);
3085 fill_emblem_cache_if_needed (file_2);
3086
3087 /* We ignore automatic emblems, and only sort by user-added keywords. */
3088 compare_result = 0;
3089 keyword_cache_1 = file_1->details->compare_by_emblem_cache->emblem_keywords;
3090 keyword_cache_2 = file_2->details->compare_by_emblem_cache->emblem_keywords;
3091 for (; *keyword_cache_1 != '\0' && *keyword_cache_2 != '\0';) {
3092 size_t length;
3093
3094 compare_result = g_utf8_collate (keyword_cache_1, keyword_cache_2);
3095 if (compare_result != 0) {
3096 return compare_result;
3097 }
3098
3099 /* Advance to the next keyword */
3100 length = strlen (keyword_cache_1);
3101 keyword_cache_1 += length + 1;
3102 keyword_cache_2 += length + 1;
3103 }
3104
3105
3106 /* One or both is now NULL. */
3107 if (*keyword_cache_1 != '\0') {
3108 g_assert (*keyword_cache_2 == '\0');
3109 return -1;
3110 } else if (*keyword_cache_2 != '\0') {
3111 return +1;
3112 }
3113
3114 return 0;
3115 }
3116
3117 static int
compare_by_type(CajaFile * file_1,CajaFile * file_2)3118 compare_by_type (CajaFile *file_1, CajaFile *file_2)
3119 {
3120 gboolean is_directory_1;
3121 gboolean is_directory_2;
3122 char *type_string_1;
3123 char *type_string_2;
3124 int result;
3125
3126 /* Directories go first. Then, if mime types are identical,
3127 * don't bother getting strings (for speed). This assumes
3128 * that the string is dependent entirely on the mime type,
3129 * which is true now but might not be later.
3130 */
3131 is_directory_1 = caja_file_is_directory (file_1);
3132 is_directory_2 = caja_file_is_directory (file_2);
3133
3134 if (is_directory_1 && is_directory_2) {
3135 return 0;
3136 }
3137
3138 if (is_directory_1) {
3139 return -1;
3140 }
3141
3142 if (is_directory_2) {
3143 return +1;
3144 }
3145
3146 if (file_1->details->mime_type != NULL &&
3147 file_2->details->mime_type != NULL &&
3148 strcmp (file_1->details->mime_type,
3149 file_2->details->mime_type) == 0) {
3150 return 0;
3151 }
3152
3153 type_string_1 = caja_file_get_type_as_string (file_1);
3154 type_string_2 = caja_file_get_type_as_string (file_2);
3155
3156 result = g_utf8_collate (type_string_1, type_string_2);
3157
3158 g_free (type_string_1);
3159 g_free (type_string_2);
3160
3161 return result;
3162 }
3163
3164 static int
compare_by_time(CajaFile * file_1,CajaFile * file_2,CajaDateType type)3165 compare_by_time (CajaFile *file_1, CajaFile *file_2, CajaDateType type)
3166 {
3167 /* Sort order:
3168 * Files with unknown times.
3169 * Files with "unknowable" times.
3170 * Files with older times.
3171 * Files with newer times.
3172 */
3173
3174 Knowledge time_known_1, time_known_2;
3175 time_t time_1, time_2;
3176
3177 time_1 = 0;
3178 time_2 = 0;
3179
3180 time_known_1 = get_time (file_1, &time_1, type);
3181 time_known_2 = get_time (file_2, &time_2, type);
3182
3183 if (time_known_1 > time_known_2) {
3184 return -1;
3185 }
3186 if (time_known_1 < time_known_2) {
3187 return +1;
3188 }
3189
3190 /* Now time_known_1 is equal to time_known_2. Check whether
3191 * we failed to get modification times for files
3192 */
3193 if(time_known_1 == UNKNOWABLE || time_known_1 == UNKNOWN) {
3194 return 0;
3195 }
3196
3197 if (time_1 < time_2) {
3198 return -1;
3199 }
3200 if (time_1 > time_2) {
3201 return +1;
3202 }
3203
3204 return 0;
3205 }
3206
3207 static int
compare_by_full_path(CajaFile * file_1,CajaFile * file_2)3208 compare_by_full_path (CajaFile *file_1, CajaFile *file_2)
3209 {
3210 int compare;
3211
3212 compare = compare_by_directory_name (file_1, file_2);
3213 if (compare != 0) {
3214 return compare;
3215 }
3216 return compare_by_display_name (file_1, file_2);
3217 }
3218
3219 /* prev_extension_segment:
3220 * @basename The basename of a file
3221 * @rem_chars A pointer to the amount of remaining characters
3222 *
3223 * Finds the next segment delimiter to the left. A starting character of '.' is
3224 * set to '\0'.
3225 *
3226 * Return value: The start of the previous segment (right of the dot) or
3227 * basename if there are none remaining.
3228 */
3229 static char *
prev_extension_segment(char * basename,int * rem_chars)3230 prev_extension_segment (char *basename, int *rem_chars)
3231 {
3232 if (*basename == '.') {
3233 *basename = 0;
3234 basename--;
3235 (*rem_chars)--;
3236 }
3237
3238 while (*rem_chars > 0 && *basename != '.') {
3239 (*rem_chars)--;
3240 basename--;
3241 }
3242
3243 return basename + 1;
3244 }
3245
3246 /* is_valid_extension_segment:
3247 * @segment Part of a modifiable zero-terminated string
3248 * @segment_index The index of the current segment
3249 *
3250 * Uses a heuristic to identify valid file extensions.
3251 *
3252 * Return value: Whether the segment is part of the file extension.
3253 */
3254 static gboolean
is_valid_extension_segment(const char * segment,int segment_index)3255 is_valid_extension_segment (const char *segment, int segment_index)
3256 {
3257 gboolean result;
3258 gboolean has_letters;
3259 int char_offset;
3260 switch (segment_index) {
3261 case 0:
3262 /* extremely long segments are probably not part of the extension */
3263 result = strlen (segment) < 20;
3264 break;
3265 default:
3266 has_letters = FALSE;
3267 char_offset = 0;
3268 while (TRUE) {
3269 char c;
3270
3271 c = *(segment + char_offset);
3272 if (c == '\0') {
3273 result = has_letters;
3274 break;
3275 }
3276 /* allow digits if there are also letters */
3277 else if (isalpha (c)) {
3278 has_letters = TRUE;
3279 }
3280 /* fail if it is neither digit nor letter */
3281 else if (!isdigit (c)) {
3282 result = FALSE;
3283 break;
3284 }
3285
3286 if (char_offset >= SORT_BY_EXTENSION_FOLLOWING_MAX_LENGTH) {
3287 result = FALSE;
3288 break;
3289 }
3290 char_offset++;
3291 }
3292 }
3293 return result;
3294 }
3295
3296 static int
compare_by_extension_segments(CajaFile * file_1,CajaFile * file_2)3297 compare_by_extension_segments (CajaFile *file_1, CajaFile *file_2)
3298 {
3299 char *name_1, *name_2;
3300 char *segment_1, *segment_2;
3301 int compare;
3302 int rem_chars_1, rem_chars_2;
3303 gboolean done_1, done_2;
3304 gboolean is_directory_1, is_directory_2;
3305 int segment_index;
3306
3307
3308 /* Directories do not have an extension */
3309 is_directory_1 = caja_file_is_directory (file_1);
3310 is_directory_2 = caja_file_is_directory (file_2);
3311
3312 if (is_directory_1 && is_directory_2) {
3313 return 0;
3314 } else if (is_directory_1) {
3315 return -1;
3316 } else if (is_directory_2) {
3317 return 1;
3318 }
3319
3320 name_1 = caja_file_get_display_name (file_1);
3321 name_2 = caja_file_get_display_name (file_2);
3322 rem_chars_1 = strlen (name_1);
3323 rem_chars_2 = strlen (name_2);
3324
3325 /* Point to one after the zero character */
3326 segment_1 = name_1 + rem_chars_1 + 1;
3327 segment_2 = name_2 + rem_chars_2 + 1;
3328
3329 segment_index = 0;
3330 do {
3331 segment_1 = prev_extension_segment (segment_1 - 1, &rem_chars_1);
3332 segment_2 = prev_extension_segment (segment_2 - 1, &rem_chars_2);
3333
3334 done_1 = rem_chars_1 <= 0 || !is_valid_extension_segment (segment_1, segment_index);
3335 done_2 = rem_chars_2 <= 0 || !is_valid_extension_segment (segment_2, segment_index);
3336 if (done_1 && !done_2) {
3337 compare = -1;
3338 break;
3339 }
3340 else if (!done_1 && done_2) {
3341 compare = 1;
3342 break;
3343 }
3344 else if (done_1 && done_2) {
3345 compare = 0;
3346 break;
3347 }
3348
3349 segment_index++;
3350 if (segment_index > SORT_BY_EXTENSION_MAX_SEGMENTS - 1) {
3351 break;
3352 }
3353 compare = strcmp (segment_1, segment_2);
3354 } while (compare == 0);
3355
3356 g_free (name_1);
3357 g_free (name_2);
3358
3359 return compare;
3360 }
3361
3362 static gchar *
caja_file_get_extension_as_string(CajaFile * file)3363 caja_file_get_extension_as_string (CajaFile *file)
3364 {
3365 int rem_chars;
3366 char *segment;
3367
3368 if (!caja_file_is_directory (file)) {
3369 char *name;
3370
3371 name = caja_file_get_display_name (file);
3372 rem_chars = strlen (name);
3373 segment = prev_extension_segment (name + rem_chars, &rem_chars);
3374
3375 if (rem_chars > 0 && is_valid_extension_segment (segment, 0)) {
3376 int segment_index;
3377 char *right_segment;
3378 char *result;
3379
3380 segment_index = 1;
3381 do {
3382 right_segment = segment;
3383 segment = prev_extension_segment (segment - 1, &rem_chars);
3384 if (rem_chars > 0 && is_valid_extension_segment (segment, segment_index)) {
3385 /* remove zero-termination of segment */
3386 *(right_segment - 1) = '.';
3387 }
3388 else {
3389 break;
3390 }
3391
3392 segment_index++;
3393 } while (segment_index < SORT_BY_EXTENSION_MAX_SEGMENTS + 1);
3394 result = g_strdup (right_segment);
3395 g_free (name);
3396 return result;
3397 }
3398 g_free (name);
3399 }
3400 return g_strdup ("");
3401 }
3402
3403 static int
caja_file_compare_for_sort_internal(CajaFile * file_1,CajaFile * file_2,gboolean directories_first,gboolean reversed)3404 caja_file_compare_for_sort_internal (CajaFile *file_1,
3405 CajaFile *file_2,
3406 gboolean directories_first,
3407 gboolean reversed)
3408 {
3409 gboolean is_directory_1, is_directory_2;
3410
3411 if (directories_first) {
3412 is_directory_1 = caja_file_is_directory (file_1);
3413 is_directory_2 = caja_file_is_directory (file_2);
3414
3415 if (is_directory_1 && !is_directory_2) {
3416 return -1;
3417 }
3418
3419 if (is_directory_2 && !is_directory_1) {
3420 return +1;
3421 }
3422 }
3423
3424 if (file_1->details->sort_order < file_2->details->sort_order) {
3425 return reversed ? 1 : -1;
3426 } else if (file_1->details->sort_order > file_2->details->sort_order) {
3427 return reversed ? -1 : 1;
3428 }
3429
3430 return 0;
3431 }
3432
3433 /**
3434 * caja_file_compare_for_sort:
3435 * @file_1: A file object
3436 * @file_2: Another file object
3437 * @sort_type: Sort criterion
3438 * @directories_first: Put all directories before any non-directories
3439 * @reversed: Reverse the order of the items, except that
3440 * the directories_first flag is still respected.
3441 *
3442 * Return value: int < 0 if @file_1 should come before file_2 in a
3443 * sorted list; int > 0 if @file_2 should come before file_1 in a
3444 * sorted list; 0 if @file_1 and @file_2 are equal for this sort criterion. Note
3445 * that each named sort type may actually break ties several ways, with the name
3446 * of the sort criterion being the primary but not only differentiator.
3447 **/
3448 int
caja_file_compare_for_sort(CajaFile * file_1,CajaFile * file_2,CajaFileSortType sort_type,gboolean directories_first,gboolean reversed)3449 caja_file_compare_for_sort (CajaFile *file_1,
3450 CajaFile *file_2,
3451 CajaFileSortType sort_type,
3452 gboolean directories_first,
3453 gboolean reversed)
3454 {
3455 int result;
3456
3457 if (file_1 == file_2) {
3458 return 0;
3459 }
3460
3461 result = caja_file_compare_for_sort_internal (file_1, file_2, directories_first, reversed);
3462
3463 if (result == 0) {
3464 switch (sort_type) {
3465 case CAJA_FILE_SORT_BY_DISPLAY_NAME:
3466 result = compare_by_display_name (file_1, file_2);
3467 if (result == 0) {
3468 result = compare_by_directory_name (file_1, file_2);
3469 }
3470 break;
3471 case CAJA_FILE_SORT_BY_DIRECTORY:
3472 result = compare_by_full_path (file_1, file_2);
3473 break;
3474 case CAJA_FILE_SORT_BY_SIZE:
3475 /* Compare directory sizes ourselves, then if necessary
3476 * use MateVFS to compare file sizes.
3477 */
3478 result = compare_by_size (file_1, file_2, FALSE);
3479 if (result == 0) {
3480 result = compare_by_full_path (file_1, file_2);
3481 }
3482 break;
3483 case CAJA_FILE_SORT_BY_SIZE_ON_DISK:
3484 /* Compare directory sizes ourselves, then if necessary
3485 * use MateVFS to compare file sizes.
3486 */
3487 result = compare_by_size (file_1, file_2, TRUE);
3488 if (result == 0) {
3489 result = compare_by_full_path (file_1, file_2);
3490 }
3491 break;
3492 case CAJA_FILE_SORT_BY_TYPE:
3493 /* MateVFS doesn't know about our special text for certain
3494 * mime types, so we handle the mime-type sorting ourselves.
3495 */
3496 result = compare_by_type (file_1, file_2);
3497 if (result == 0) {
3498 result = compare_by_full_path (file_1, file_2);
3499 }
3500 break;
3501 case CAJA_FILE_SORT_BY_MTIME:
3502 result = compare_by_time (file_1, file_2, CAJA_DATE_TYPE_MODIFIED);
3503 if (result == 0) {
3504 result = compare_by_full_path (file_1, file_2);
3505 }
3506 break;
3507 case CAJA_FILE_SORT_BY_BTIME:
3508 result = compare_by_time (file_1, file_2, CAJA_DATE_TYPE_CREATED);
3509 if (result == 0) {
3510 result = compare_by_full_path (file_1, file_2);
3511 }
3512 break;
3513 case CAJA_FILE_SORT_BY_ATIME:
3514 result = compare_by_time (file_1, file_2, CAJA_DATE_TYPE_ACCESSED);
3515 if (result == 0) {
3516 result = compare_by_full_path (file_1, file_2);
3517 }
3518 break;
3519 case CAJA_FILE_SORT_BY_TRASHED_TIME:
3520 result = compare_by_time (file_1, file_2, CAJA_DATE_TYPE_TRASHED);
3521 if (result == 0) {
3522 result = compare_by_full_path (file_1, file_2);
3523 }
3524 break;
3525 case CAJA_FILE_SORT_BY_EMBLEMS:
3526 /* MateVFS doesn't know squat about our emblems, so
3527 * we handle comparing them here, before falling back
3528 * to tie-breakers.
3529 */
3530 result = compare_by_emblems (file_1, file_2);
3531 if (result == 0) {
3532 result = compare_by_full_path (file_1, file_2);
3533 }
3534 break;
3535 case CAJA_FILE_SORT_BY_EXTENSION:
3536 result = compare_by_extension_segments (file_1, file_2);
3537 if (result == 0) {
3538 result = compare_by_full_path (file_1, file_2);
3539 }
3540 break;
3541 default:
3542 g_return_val_if_reached (0);
3543 }
3544
3545 if (reversed) {
3546 result = -result;
3547 }
3548 }
3549
3550 return result;
3551 }
3552
3553 int
caja_file_compare_for_sort_by_attribute_q(CajaFile * file_1,CajaFile * file_2,GQuark attribute,gboolean directories_first,gboolean reversed)3554 caja_file_compare_for_sort_by_attribute_q (CajaFile *file_1,
3555 CajaFile *file_2,
3556 GQuark attribute,
3557 gboolean directories_first,
3558 gboolean reversed)
3559 {
3560 int result;
3561
3562 if (file_1 == file_2) {
3563 return 0;
3564 }
3565
3566 /* Convert certain attributes into CajaFileSortTypes and use
3567 * caja_file_compare_for_sort()
3568 */
3569 if (attribute == 0 || attribute == attribute_name_q) {
3570 return caja_file_compare_for_sort (file_1, file_2,
3571 CAJA_FILE_SORT_BY_DISPLAY_NAME,
3572 directories_first,
3573 reversed);
3574 } else if (attribute == attribute_size_q) {
3575 return caja_file_compare_for_sort (file_1, file_2,
3576 CAJA_FILE_SORT_BY_SIZE,
3577 directories_first,
3578 reversed);
3579 } else if (attribute == attribute_size_on_disk_q) {
3580 return caja_file_compare_for_sort (file_1, file_2,
3581 CAJA_FILE_SORT_BY_SIZE_ON_DISK,
3582 directories_first,
3583 reversed);
3584 } else if (attribute == attribute_type_q) {
3585 return caja_file_compare_for_sort (file_1, file_2,
3586 CAJA_FILE_SORT_BY_TYPE,
3587 directories_first,
3588 reversed);
3589 } else if (attribute == attribute_modification_date_q || attribute == attribute_date_modified_q) {
3590 return caja_file_compare_for_sort (file_1, file_2,
3591 CAJA_FILE_SORT_BY_MTIME,
3592 directories_first,
3593 reversed);
3594 } else if (attribute == attribute_creation_date_q || attribute == attribute_date_created_q) {
3595 return caja_file_compare_for_sort (file_1, file_2,
3596 CAJA_FILE_SORT_BY_BTIME,
3597 directories_first,
3598 reversed);
3599 } else if (attribute == attribute_accessed_date_q || attribute == attribute_date_accessed_q) {
3600 return caja_file_compare_for_sort (file_1, file_2,
3601 CAJA_FILE_SORT_BY_ATIME,
3602 directories_first,
3603 reversed);
3604 } else if (attribute == attribute_trashed_on_q) {
3605 return caja_file_compare_for_sort (file_1, file_2,
3606 CAJA_FILE_SORT_BY_TRASHED_TIME,
3607 directories_first,
3608 reversed);
3609 } else if (attribute == attribute_emblems_q) {
3610 return caja_file_compare_for_sort (file_1, file_2,
3611 CAJA_FILE_SORT_BY_EMBLEMS,
3612 directories_first,
3613 reversed);
3614 } else if (attribute == attribute_extension_q) {
3615 return caja_file_compare_for_sort (file_1, file_2,
3616 CAJA_FILE_SORT_BY_EXTENSION,
3617 directories_first,
3618 reversed);
3619 }
3620
3621 /* it is a normal attribute, compare by strings */
3622
3623 result = caja_file_compare_for_sort_internal (file_1, file_2, directories_first, reversed);
3624
3625 if (result == 0) {
3626 char *value_1;
3627 char *value_2;
3628
3629 value_1 = caja_file_get_string_attribute_q (file_1,
3630 attribute);
3631 value_2 = caja_file_get_string_attribute_q (file_2,
3632 attribute);
3633
3634 if (value_1 != NULL && value_2 != NULL) {
3635 result = strcmp (value_1, value_2);
3636 }
3637
3638 g_free (value_1);
3639 g_free (value_2);
3640
3641 if (reversed) {
3642 result = -result;
3643 }
3644 }
3645
3646 return result;
3647 }
3648
3649 int
caja_file_compare_for_sort_by_attribute(CajaFile * file_1,CajaFile * file_2,const char * attribute,gboolean directories_first,gboolean reversed)3650 caja_file_compare_for_sort_by_attribute (CajaFile *file_1,
3651 CajaFile *file_2,
3652 const char *attribute,
3653 gboolean directories_first,
3654 gboolean reversed)
3655 {
3656 return caja_file_compare_for_sort_by_attribute_q (file_1, file_2,
3657 g_quark_from_string (attribute),
3658 directories_first,
3659 reversed);
3660 }
3661
3662
3663 /**
3664 * caja_file_compare_name:
3665 * @file: A file object
3666 * @pattern: A string we are comparing it with
3667 *
3668 * Return value: result of a comparison of the file name and the given pattern,
3669 * using the same sorting order as sort by name.
3670 **/
3671 int
caja_file_compare_display_name(CajaFile * file,const char * pattern)3672 caja_file_compare_display_name (CajaFile *file,
3673 const char *pattern)
3674 {
3675 const char *name;
3676 int result;
3677
3678 g_return_val_if_fail (pattern != NULL, -1);
3679
3680 name = caja_file_peek_display_name (file);
3681 result = g_utf8_collate (name, pattern);
3682 return result;
3683 }
3684
3685
3686 gboolean
caja_file_is_hidden_file(CajaFile * file)3687 caja_file_is_hidden_file (CajaFile *file)
3688 {
3689 return file->details->is_hidden;
3690 }
3691
3692 static gboolean
caja_file_is_backup_file(CajaFile * file)3693 caja_file_is_backup_file (CajaFile *file)
3694 {
3695 return file->details->is_backup;
3696 }
3697
3698 /**
3699 * caja_file_should_show:
3700 * @file: the file to check.
3701 * @show_hidden: whether we want to show hidden files or not.
3702 * @show_backup: whether we want to show backup files or not.
3703 *
3704 * Determines if a #CajaFile should be shown. Note that when browsing
3705 * a trash directory, this function will always return %TRUE.
3706 *
3707 * Returns: %TRUE if the file should be shown, %FALSE if it shouldn't.
3708 */
3709 gboolean
caja_file_should_show(CajaFile * file,gboolean show_hidden,gboolean show_foreign,gboolean show_backup)3710 caja_file_should_show (CajaFile *file,
3711 gboolean show_hidden,
3712 gboolean show_foreign,
3713 gboolean show_backup)
3714 {
3715 /* Never hide any files in trash. */
3716 if (caja_file_is_in_trash (file)) {
3717 return TRUE;
3718 } else {
3719 return (show_hidden || !caja_file_is_hidden_file (file)) &&
3720 (show_backup || !caja_file_is_backup_file (file)) &&
3721 (show_foreign || !(caja_file_is_in_desktop (file) && caja_file_is_foreign_link (file)));
3722 }
3723 }
3724
3725 gboolean
caja_file_is_home(CajaFile * file)3726 caja_file_is_home (CajaFile *file)
3727 {
3728 GFile *dir;
3729
3730 dir = file->details->directory->details->location;
3731 if (dir == NULL) {
3732 return FALSE;
3733 }
3734
3735 return caja_is_home_directory_file (dir, file->details->name);
3736 }
3737
3738 gboolean
caja_file_is_in_desktop(CajaFile * file)3739 caja_file_is_in_desktop (CajaFile *file)
3740 {
3741 if (file->details->directory->details->location) {
3742 return caja_is_desktop_directory (file->details->directory->details->location);
3743 }
3744 return FALSE;
3745
3746 }
3747
3748 static gboolean
filter_hidden_partition_callback(gpointer data,gpointer callback_data)3749 filter_hidden_partition_callback (gpointer data,
3750 gpointer callback_data)
3751 {
3752 CajaFile *file;
3753 FilterOptions options;
3754
3755 file = CAJA_FILE (data);
3756 options = GPOINTER_TO_INT (callback_data);
3757
3758 return caja_file_should_show (file,
3759 options & SHOW_HIDDEN,
3760 TRUE,
3761 options & SHOW_BACKUP);
3762 }
3763
3764 GList *
caja_file_list_filter_hidden(GList * files,gboolean show_hidden)3765 caja_file_list_filter_hidden (GList *files,
3766 gboolean show_hidden)
3767 {
3768 GList *filtered_files;
3769 GList *removed_files;
3770
3771 /* FIXME bugzilla.gnome.org 40653:
3772 * Eventually this should become a generic filtering thingy.
3773 */
3774
3775 filtered_files = caja_file_list_copy (files);
3776 filtered_files = eel_g_list_partition (filtered_files,
3777 filter_hidden_partition_callback,
3778 GINT_TO_POINTER ((show_hidden ? SHOW_HIDDEN : 0)),
3779 &removed_files);
3780 caja_file_list_free (removed_files);
3781
3782 return filtered_files;
3783 }
3784
3785 char *
caja_file_get_metadata(CajaFile * file,const char * key,const char * default_metadata)3786 caja_file_get_metadata (CajaFile *file,
3787 const char *key,
3788 const char *default_metadata)
3789 {
3790 guint id;
3791 char *value;
3792
3793 g_return_val_if_fail (key != NULL, g_strdup (default_metadata));
3794 g_return_val_if_fail (key[0] != '\0', g_strdup (default_metadata));
3795
3796 if (file == NULL ||
3797 file->details->metadata == NULL) {
3798 return g_strdup (default_metadata);
3799 }
3800
3801 g_return_val_if_fail (CAJA_IS_FILE (file), g_strdup (default_metadata));
3802
3803 id = caja_metadata_get_id (key);
3804 value = g_hash_table_lookup (file->details->metadata, GUINT_TO_POINTER (id));
3805
3806 if (value) {
3807 return g_strdup (value);
3808 }
3809 return g_strdup (default_metadata);
3810 }
3811
3812 GList *
caja_file_get_metadata_list(CajaFile * file,const char * key)3813 caja_file_get_metadata_list (CajaFile *file,
3814 const char *key)
3815 {
3816 guint id;
3817 char **value;
3818
3819 g_return_val_if_fail (key != NULL, NULL);
3820 g_return_val_if_fail (key[0] != '\0', NULL);
3821
3822 if (file == NULL ||
3823 file->details->metadata == NULL) {
3824 return NULL;
3825 }
3826
3827 g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
3828
3829 id = caja_metadata_get_id (key);
3830 id |= METADATA_ID_IS_LIST_MASK;
3831
3832 value = g_hash_table_lookup (file->details->metadata, GUINT_TO_POINTER (id));
3833
3834 if (value) {
3835 GList *res;
3836 int i;
3837
3838 res = NULL;
3839 for (i = 0; value[i] != NULL; i++) {
3840 res = g_list_prepend (res, g_strdup (value[i]));
3841 }
3842 return g_list_reverse (res);
3843 }
3844
3845 return NULL;
3846 }
3847
3848 void
caja_file_set_metadata(CajaFile * file,const char * key,const char * default_metadata,const char * metadata)3849 caja_file_set_metadata (CajaFile *file,
3850 const char *key,
3851 const char *default_metadata,
3852 const char *metadata)
3853 {
3854 const char *val;
3855
3856 g_return_if_fail (CAJA_IS_FILE (file));
3857 g_return_if_fail (key != NULL);
3858 g_return_if_fail (key[0] != '\0');
3859
3860 val = metadata;
3861 if (val == NULL) {
3862 val = default_metadata;
3863 }
3864
3865 EEL_CALL_METHOD
3866 (CAJA_FILE_CLASS, file,
3867 set_metadata, (file, key, val));
3868 }
3869
3870 void
caja_file_set_metadata_list(CajaFile * file,const char * key,GList * list)3871 caja_file_set_metadata_list (CajaFile *file,
3872 const char *key,
3873 GList *list)
3874 {
3875 char **val;
3876 int len, i;
3877 GList *l;
3878
3879 g_return_if_fail (CAJA_IS_FILE (file));
3880 g_return_if_fail (key != NULL);
3881 g_return_if_fail (key[0] != '\0');
3882
3883 len = g_list_length (list);
3884 val = g_new (char *, len + 1);
3885 for (l = list, i = 0; l != NULL; l = l->next, i++) {
3886 val[i] = l->data;
3887 }
3888 val[i] = NULL;
3889
3890 EEL_CALL_METHOD
3891 (CAJA_FILE_CLASS, file,
3892 set_metadata_as_list, (file, key, val));
3893
3894 g_free (val);
3895 }
3896
3897
3898 gboolean
caja_file_get_boolean_metadata(CajaFile * file,const char * key,gboolean default_metadata)3899 caja_file_get_boolean_metadata (CajaFile *file,
3900 const char *key,
3901 gboolean default_metadata)
3902 {
3903 char *result_as_string;
3904 gboolean result;
3905
3906 g_return_val_if_fail (key != NULL, default_metadata);
3907 g_return_val_if_fail (key[0] != '\0', default_metadata);
3908
3909 if (file == NULL) {
3910 return default_metadata;
3911 }
3912
3913 g_return_val_if_fail (CAJA_IS_FILE (file), default_metadata);
3914
3915 result_as_string = caja_file_get_metadata
3916 (file, key, default_metadata ? "true" : "false");
3917 g_assert (result_as_string != NULL);
3918
3919 if (g_ascii_strcasecmp (result_as_string, "true") == 0) {
3920 result = TRUE;
3921 } else if (g_ascii_strcasecmp (result_as_string, "false") == 0) {
3922 result = FALSE;
3923 } else {
3924 g_error ("boolean metadata with value other than true or false");
3925 result = default_metadata;
3926 }
3927
3928 g_free (result_as_string);
3929 return result;
3930 }
3931
3932 int
caja_file_get_integer_metadata(CajaFile * file,const char * key,int default_metadata)3933 caja_file_get_integer_metadata (CajaFile *file,
3934 const char *key,
3935 int default_metadata)
3936 {
3937 char *result_as_string;
3938 char default_as_string[32];
3939 int result;
3940 char c;
3941
3942 g_return_val_if_fail (key != NULL, default_metadata);
3943 g_return_val_if_fail (key[0] != '\0', default_metadata);
3944
3945 if (file == NULL) {
3946 return default_metadata;
3947 }
3948 g_return_val_if_fail (CAJA_IS_FILE (file), default_metadata);
3949
3950 g_snprintf (default_as_string, sizeof (default_as_string), "%d", default_metadata);
3951 result_as_string = caja_file_get_metadata
3952 (file, key, default_as_string);
3953
3954 /* Normally we can't get a a NULL, but we check for it here to
3955 * handle the oddball case of a non-existent directory.
3956 */
3957 if (result_as_string == NULL) {
3958 result = default_metadata;
3959 } else {
3960 if (sscanf (result_as_string, " %d %c", &result, &c) != 1) {
3961 result = default_metadata;
3962 }
3963 g_free (result_as_string);
3964 }
3965
3966 return result;
3967 }
3968
3969 static gboolean
get_time_from_time_string(const char * time_string,time_t * time)3970 get_time_from_time_string (const char *time_string,
3971 time_t *time)
3972 {
3973 long scanned_time;
3974 char c;
3975
3976 g_assert (time != NULL);
3977
3978 /* Only accept string if it has one integer with nothing
3979 * afterwards.
3980 */
3981 if (time_string == NULL ||
3982 sscanf (time_string, "%ld%c", &scanned_time, &c) != 1) {
3983 return FALSE;
3984 }
3985 *time = (time_t) scanned_time;
3986 return TRUE;
3987 }
3988
3989 time_t
caja_file_get_time_metadata(CajaFile * file,const char * key)3990 caja_file_get_time_metadata (CajaFile *file,
3991 const char *key)
3992 {
3993 time_t time;
3994 char *time_string;
3995
3996 time_string = caja_file_get_metadata (file, key, NULL);
3997 if (!get_time_from_time_string (time_string, &time)) {
3998 time = UNDEFINED_TIME;
3999 }
4000 g_free (time_string);
4001
4002 return time;
4003 }
4004
4005 void
caja_file_set_time_metadata(CajaFile * file,const char * key,time_t time)4006 caja_file_set_time_metadata (CajaFile *file,
4007 const char *key,
4008 time_t time)
4009 {
4010 char time_str[21];
4011 char *metadata;
4012
4013 if (time != UNDEFINED_TIME) {
4014 /* 2^64 turns out to be 20 characters */
4015 g_snprintf (time_str, 20, "%ld", (long int)time);
4016 time_str[20] = '\0';
4017 metadata = time_str;
4018 } else {
4019 metadata = NULL;
4020 }
4021
4022 caja_file_set_metadata (file, key, NULL, metadata);
4023 }
4024
4025
4026 void
caja_file_set_boolean_metadata(CajaFile * file,const char * key,gboolean default_metadata,gboolean metadata)4027 caja_file_set_boolean_metadata (CajaFile *file,
4028 const char *key,
4029 gboolean default_metadata,
4030 gboolean metadata)
4031 {
4032 g_return_if_fail (CAJA_IS_FILE (file));
4033 g_return_if_fail (key != NULL);
4034 g_return_if_fail (key[0] != '\0');
4035
4036 caja_file_set_metadata (file, key,
4037 default_metadata ? "true" : "false",
4038 metadata ? "true" : "false");
4039 }
4040
4041 void
caja_file_set_integer_metadata(CajaFile * file,const char * key,int default_metadata,int metadata)4042 caja_file_set_integer_metadata (CajaFile *file,
4043 const char *key,
4044 int default_metadata,
4045 int metadata)
4046 {
4047 char value_as_string[32];
4048 char default_as_string[32];
4049
4050 g_return_if_fail (CAJA_IS_FILE (file));
4051 g_return_if_fail (key != NULL);
4052 g_return_if_fail (key[0] != '\0');
4053
4054 g_snprintf (value_as_string, sizeof (value_as_string), "%d", metadata);
4055 g_snprintf (default_as_string, sizeof (default_as_string), "%d", default_metadata);
4056
4057 caja_file_set_metadata (file, key,
4058 default_as_string, value_as_string);
4059 }
4060
4061 static const char *
caja_file_peek_display_name_collation_key(CajaFile * file)4062 caja_file_peek_display_name_collation_key (CajaFile *file)
4063 {
4064 const char *res;
4065
4066 res = file->details->display_name_collation_key;
4067 if (res == NULL)
4068 res = "";
4069
4070 return res;
4071 }
4072
4073 static const char *
caja_file_peek_display_name(CajaFile * file)4074 caja_file_peek_display_name (CajaFile *file)
4075 {
4076 /*
4077 stefano-k: Imported 15_nautilus_file_peek_crash.patch from debian nautilus
4078 Date: Thu, 27 Jan 2011 10:22:10 +0000
4079 Subject: Prevent a crash in nautilus_file_peek_display_name() on invalid NautilusFile
4080 This is more a workaround only, expect assert failures at other
4081 places when something bad happens. There's a race condition somewhere,
4082 this patch only prevents immediate crash.
4083 Patch by Marcus Husar <marcus.husar@rose.uni-heidelberg.de>
4084 https://bugzilla.gnome.org/show_bug.cgi?id=602500
4085 */
4086 if (file == NULL || caja_file_is_gone (file))
4087 return "";
4088
4089 /* Default to display name based on filename if its not set yet */
4090
4091 if (file->details->display_name == NULL) {
4092 const char *name;
4093
4094 name = file->details->name;
4095 if (g_utf8_validate (name, -1, NULL)) {
4096 caja_file_set_display_name (file,
4097 name,
4098 NULL,
4099 FALSE);
4100 } else {
4101 char *escaped_name;
4102
4103 escaped_name = g_uri_escape_string (name, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
4104 caja_file_set_display_name (file,
4105 escaped_name,
4106 NULL,
4107 FALSE);
4108 g_free (escaped_name);
4109 }
4110 }
4111
4112 return file->details->display_name;
4113 }
4114
4115 char *
caja_file_get_display_name(CajaFile * file)4116 caja_file_get_display_name (CajaFile *file)
4117 {
4118 return g_strdup (caja_file_peek_display_name (file));
4119 }
4120
4121 char *
caja_file_get_edit_name(CajaFile * file)4122 caja_file_get_edit_name (CajaFile *file)
4123 {
4124 const char *res;
4125
4126 res = file->details->edit_name;
4127 if (res == NULL)
4128 res = "";
4129
4130 return g_strdup (res);
4131 }
4132
4133 char *
caja_file_get_name(CajaFile * file)4134 caja_file_get_name (CajaFile *file)
4135 {
4136 return g_strdup (file->details->name);
4137 }
4138
4139 /**
4140 * caja_file_get_description:
4141 * @file: a #CajaFile.
4142 *
4143 * Gets the standard::description key from @file, if
4144 * it has been cached.
4145 *
4146 * Returns: a string containing the value of the standard::description
4147 * key, or %NULL.
4148 */
4149 char *
caja_file_get_description(CajaFile * file)4150 caja_file_get_description (CajaFile *file)
4151 {
4152 return g_strdup (file->details->description);
4153 }
4154
4155 void
caja_file_monitor_add(CajaFile * file,gconstpointer client,CajaFileAttributes attributes)4156 caja_file_monitor_add (CajaFile *file,
4157 gconstpointer client,
4158 CajaFileAttributes attributes)
4159 {
4160 g_return_if_fail (CAJA_IS_FILE (file));
4161 g_return_if_fail (client != NULL);
4162
4163 EEL_CALL_METHOD
4164 (CAJA_FILE_CLASS, file,
4165 monitor_add, (file, client, attributes));
4166 }
4167
4168 void
caja_file_monitor_remove(CajaFile * file,gconstpointer client)4169 caja_file_monitor_remove (CajaFile *file,
4170 gconstpointer client)
4171 {
4172 g_return_if_fail (CAJA_IS_FILE (file));
4173 g_return_if_fail (client != NULL);
4174
4175 EEL_CALL_METHOD
4176 (CAJA_FILE_CLASS, file,
4177 monitor_remove, (file, client));
4178 }
4179
4180 gboolean
caja_file_is_launcher(CajaFile * file)4181 caja_file_is_launcher (CajaFile *file)
4182 {
4183 return file->details->is_launcher;
4184 }
4185
4186 gboolean
caja_file_is_foreign_link(CajaFile * file)4187 caja_file_is_foreign_link (CajaFile *file)
4188 {
4189 return file->details->is_foreign_link;
4190 }
4191
4192 gboolean
caja_file_is_trusted_link(CajaFile * file)4193 caja_file_is_trusted_link (CajaFile *file)
4194 {
4195 return file->details->is_trusted_link;
4196 }
4197
4198 gboolean
caja_file_has_activation_uri(CajaFile * file)4199 caja_file_has_activation_uri (CajaFile *file)
4200 {
4201 return file->details->activation_uri != NULL;
4202 }
4203
4204
4205 /* Return the uri associated with the passed-in file, which may not be
4206 * the actual uri if the file is an desktop file or a caja
4207 * xml link file.
4208 */
4209 char *
caja_file_get_activation_uri(CajaFile * file)4210 caja_file_get_activation_uri (CajaFile *file)
4211 {
4212 g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
4213
4214 if (file->details->activation_uri != NULL) {
4215 return g_strdup (file->details->activation_uri);
4216 }
4217
4218 return caja_file_get_uri (file);
4219 }
4220
4221 GFile *
caja_file_get_activation_location(CajaFile * file)4222 caja_file_get_activation_location (CajaFile *file)
4223 {
4224 g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
4225
4226 if (file->details->activation_uri != NULL) {
4227 return g_file_new_for_uri (file->details->activation_uri);
4228 }
4229
4230 return caja_file_get_location (file);
4231 }
4232
4233
4234 char *
caja_file_get_drop_target_uri(CajaFile * file)4235 caja_file_get_drop_target_uri (CajaFile *file)
4236 {
4237 char *uri, *target_uri;
4238 GFile *location;
4239
4240 g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
4241
4242 if (CAJA_IS_DESKTOP_ICON_FILE (file)) {
4243 CajaDesktopLink *link;
4244
4245 link = caja_desktop_icon_file_get_link (CAJA_DESKTOP_ICON_FILE (file));
4246
4247 if (link != NULL) {
4248 location = caja_desktop_link_get_activation_location (link);
4249 g_object_unref (link);
4250 if (location != NULL) {
4251 uri = g_file_get_uri (location);
4252 g_object_unref (location);
4253 return uri;
4254 }
4255 }
4256 }
4257
4258 uri = caja_file_get_uri (file);
4259
4260 /* Check for Caja link */
4261 if (caja_file_is_caja_link (file)) {
4262 location = caja_file_get_location (file);
4263 /* FIXME bugzilla.gnome.org 43020: This does sync. I/O and works only locally. */
4264 if (g_file_is_native (location)) {
4265 target_uri = caja_link_local_get_link_uri (uri);
4266 if (target_uri != NULL) {
4267 g_free (uri);
4268 uri = target_uri;
4269 }
4270 }
4271 g_object_unref (location);
4272 }
4273
4274 return uri;
4275 }
4276
4277 static gboolean
is_uri_relative(const char * uri)4278 is_uri_relative (const char *uri)
4279 {
4280 char *scheme;
4281 gboolean ret;
4282
4283 scheme = g_uri_parse_scheme (uri);
4284 ret = (scheme == NULL);
4285 g_free (scheme);
4286 return ret;
4287 }
4288
4289 static char *
get_custom_icon_metadata_uri(CajaFile * file)4290 get_custom_icon_metadata_uri (CajaFile *file)
4291 {
4292 char *custom_icon_uri;
4293 char *uri;
4294
4295 uri = caja_file_get_metadata (file, CAJA_METADATA_KEY_CUSTOM_ICON, NULL);
4296 if (uri != NULL &&
4297 caja_file_is_directory (file) &&
4298 is_uri_relative (uri)) {
4299 char *dir_uri;
4300
4301 dir_uri = caja_file_get_uri (file);
4302 custom_icon_uri = g_build_filename (dir_uri, uri, NULL);
4303 g_free (dir_uri);
4304 g_free (uri);
4305 } else {
4306 custom_icon_uri = uri;
4307 }
4308 return custom_icon_uri;
4309 }
4310
4311 static GIcon *
get_custom_icon(CajaFile * file)4312 get_custom_icon (CajaFile *file)
4313 {
4314 char *custom_icon_uri;
4315 GFile *icon_file;
4316 GIcon *icon;
4317
4318 if (file == NULL) {
4319 return NULL;
4320 }
4321
4322 icon = NULL;
4323
4324 /* Metadata takes precedence */
4325 custom_icon_uri = get_custom_icon_metadata_uri (file);
4326
4327 if (custom_icon_uri) {
4328 icon_file = g_file_new_for_uri (custom_icon_uri);
4329 icon = g_file_icon_new (icon_file);
4330 g_object_unref (icon_file);
4331 g_free (custom_icon_uri);
4332 }
4333
4334 if (icon == NULL && file->details->got_link_info && file->details->custom_icon != NULL) {
4335 if (g_path_is_absolute (file->details->custom_icon)) {
4336 icon_file = g_file_new_for_path (file->details->custom_icon);
4337 icon = g_file_icon_new (icon_file);
4338 g_object_unref (icon_file);
4339 } else {
4340 icon = g_themed_icon_new (file->details->custom_icon);
4341 }
4342 }
4343
4344 return icon;
4345 }
4346
4347
4348 static guint64 cached_thumbnail_limit;
4349 int cached_thumbnail_size;
4350 static int show_image_thumbs;
4351
4352 GFilesystemPreviewType
caja_file_get_filesystem_use_preview(CajaFile * file)4353 caja_file_get_filesystem_use_preview (CajaFile *file)
4354 {
4355 GFilesystemPreviewType use_preview;
4356 CajaFile *parent;
4357
4358 parent = caja_file_get_parent (file);
4359 if (parent != NULL) {
4360 use_preview = parent->details->filesystem_use_preview;
4361 g_object_unref (parent);
4362 } else {
4363 use_preview = 0;
4364 }
4365
4366 return use_preview;
4367 }
4368
4369 gboolean
caja_file_should_show_thumbnail(CajaFile * file)4370 caja_file_should_show_thumbnail (CajaFile *file)
4371 {
4372 const char *mime_type;
4373 GFilesystemPreviewType use_preview;
4374
4375 use_preview = caja_file_get_filesystem_use_preview (file);
4376
4377 mime_type = file->details->mime_type;
4378 if (mime_type == NULL) {
4379 mime_type = "application/octet-stream";
4380 }
4381
4382 /* If the thumbnail has already been created, don't care about the size
4383 * of the original file.
4384 */
4385 if (caja_thumbnail_is_mimetype_limited_by_size (mime_type) &&
4386 file->details->thumbnail_path == NULL &&
4387 caja_file_get_size (file) > cached_thumbnail_limit) {
4388 return FALSE;
4389 }
4390
4391 if (show_image_thumbs == CAJA_SPEED_TRADEOFF_ALWAYS) {
4392 if (use_preview == G_FILESYSTEM_PREVIEW_TYPE_NEVER) {
4393 return FALSE;
4394 } else {
4395 return TRUE;
4396 }
4397 } else if (show_image_thumbs == CAJA_SPEED_TRADEOFF_NEVER) {
4398 return FALSE;
4399 } else {
4400 if (use_preview == G_FILESYSTEM_PREVIEW_TYPE_NEVER) {
4401 /* file system says to never thumbnail anything */
4402 return FALSE;
4403 } else if (use_preview == G_FILESYSTEM_PREVIEW_TYPE_IF_LOCAL) {
4404 /* file system says we should treat file as if it's local */
4405 return TRUE;
4406 } else {
4407 /* only local files */
4408 return caja_file_is_local (file);
4409 }
4410 }
4411
4412 return FALSE;
4413 }
4414
4415 static void
prepend_icon_name(const char * name,GThemedIcon * icon)4416 prepend_icon_name (const char *name,
4417 GThemedIcon *icon)
4418 {
4419 g_themed_icon_prepend_name(icon, name);
4420 }
4421
4422 GIcon *
caja_file_get_gicon(CajaFile * file,CajaFileIconFlags flags)4423 caja_file_get_gicon (CajaFile *file,
4424 CajaFileIconFlags flags)
4425 {
4426 const char * const * names;
4427 GIcon *icon, *mount_icon = NULL, *emblemed_icon;
4428 gboolean is_folder = FALSE, is_preview = FALSE, is_inode_directory = FALSE;
4429
4430 if (file == NULL) {
4431 return NULL;
4432 }
4433
4434 icon = get_custom_icon (file);
4435 if (icon != NULL) {
4436 return icon;
4437 }
4438
4439 if (file->details->icon) {
4440 icon = NULL;
4441
4442 /* fetch the mount icon here, we'll use it later */
4443 if (flags & CAJA_FILE_ICON_FLAGS_USE_MOUNT_ICON ||
4444 flags & CAJA_FILE_ICON_FLAGS_USE_MOUNT_ICON_AS_EMBLEM) {
4445 GMount *mount;
4446
4447 mount = caja_file_get_mount (file);
4448
4449 if (mount != NULL) {
4450 mount_icon = g_mount_get_icon (mount);
4451 g_object_unref (mount);
4452 }
4453 }
4454
4455 if (((flags & CAJA_FILE_ICON_FLAGS_EMBEDDING_TEXT) ||
4456 (flags & CAJA_FILE_ICON_FLAGS_FOR_DRAG_ACCEPT) ||
4457 (flags & CAJA_FILE_ICON_FLAGS_FOR_OPEN_FOLDER) ||
4458 (flags & CAJA_FILE_ICON_FLAGS_USE_MOUNT_ICON) ||
4459 (flags & CAJA_FILE_ICON_FLAGS_USE_MOUNT_ICON_AS_EMBLEM) ||
4460 ((flags & CAJA_FILE_ICON_FLAGS_IGNORE_VISITING) == 0 &&
4461 caja_file_has_open_window (file))) &&
4462 G_IS_THEMED_ICON (file->details->icon)) {
4463 GPtrArray *prepend_array;
4464 int i;
4465
4466 names = g_themed_icon_get_names (G_THEMED_ICON (file->details->icon));
4467 prepend_array = g_ptr_array_new ();
4468
4469 for (i = 0; names[i] != NULL; i++) {
4470 const char *name;
4471
4472 name = names[i];
4473
4474 if (strcmp (name, "folder") == 0) {
4475 is_folder = TRUE;
4476 }
4477 if (strcmp (name, "inode-directory") == 0) {
4478 is_inode_directory = TRUE;
4479 }
4480 if (strcmp (name, "text-x-generic") == 0 &&
4481 (flags & CAJA_FILE_ICON_FLAGS_EMBEDDING_TEXT)) {
4482 is_preview = TRUE;
4483 }
4484 }
4485
4486 /* Here, we add icons in reverse order of precedence,
4487 * because they are later prepended */
4488 if (is_preview) {
4489 g_ptr_array_add (prepend_array, "text-x-preview");
4490 }
4491
4492 /* "folder" should override "inode-directory", not the other way around */
4493 if (is_inode_directory) {
4494 g_ptr_array_add (prepend_array, "folder");
4495 }
4496 if (is_folder && (flags & CAJA_FILE_ICON_FLAGS_FOR_OPEN_FOLDER)) {
4497 g_ptr_array_add (prepend_array, "folder-open");
4498 }
4499 if (is_folder &&
4500 (flags & CAJA_FILE_ICON_FLAGS_IGNORE_VISITING) == 0 &&
4501 caja_file_has_open_window (file)) {
4502 g_ptr_array_add (prepend_array, "folder-visiting");
4503 }
4504 if (is_folder &&
4505 (flags & CAJA_FILE_ICON_FLAGS_FOR_DRAG_ACCEPT)) {
4506 g_ptr_array_add (prepend_array, "folder-drag-accept");
4507 }
4508
4509 if (prepend_array->len) {
4510 /* When constructing GThemed Icon, pointers from the array
4511 * are reused, but not the array itself, so the cast is safe */
4512 icon = g_themed_icon_new_from_names ((char**) names, -1);
4513 g_ptr_array_foreach (prepend_array, (GFunc) prepend_icon_name, icon);
4514 }
4515
4516 g_ptr_array_free (prepend_array, TRUE);
4517 }
4518
4519 if (icon == NULL) {
4520 icon = g_object_ref (file->details->icon);
4521 }
4522
4523 if ((flags & CAJA_FILE_ICON_FLAGS_USE_MOUNT_ICON) &&
4524 mount_icon != NULL) {
4525 g_object_unref (icon);
4526 icon = mount_icon;
4527 } else if ((flags & CAJA_FILE_ICON_FLAGS_USE_MOUNT_ICON_AS_EMBLEM) &&
4528 mount_icon != NULL && !g_icon_equal (mount_icon, icon)) {
4529 GEmblem *emblem;
4530
4531 emblem = g_emblem_new (mount_icon);
4532 emblemed_icon = g_emblemed_icon_new (icon, emblem);
4533
4534 g_object_unref (emblem);
4535 g_object_unref (icon);
4536 g_object_unref (mount_icon);
4537
4538 icon = emblemed_icon;
4539 } else if (mount_icon != NULL) {
4540 g_object_unref (mount_icon);
4541 }
4542
4543 return icon;
4544 }
4545
4546 return g_themed_icon_new ("text-x-generic");
4547 }
4548
4549 static GIcon *
get_default_file_icon(CajaFileIconFlags flags)4550 get_default_file_icon (CajaFileIconFlags flags)
4551 {
4552 static GIcon *fallback_icon = NULL;
4553 static GIcon *fallback_icon_preview = NULL;
4554 if (fallback_icon == NULL) {
4555 fallback_icon = g_themed_icon_new ("text-x-generic");
4556 fallback_icon_preview = g_themed_icon_new ("text-x-preview");
4557 g_themed_icon_append_name (G_THEMED_ICON (fallback_icon_preview), "text-x-generic");
4558 }
4559 if (flags & CAJA_FILE_ICON_FLAGS_EMBEDDING_TEXT) {
4560 return fallback_icon_preview;
4561 } else {
4562 return fallback_icon;
4563 }
4564 }
4565
4566 CajaIconInfo *
caja_file_get_icon(CajaFile * file,int size,int scale,CajaFileIconFlags flags)4567 caja_file_get_icon (CajaFile *file,
4568 int size,
4569 int scale,
4570 CajaFileIconFlags flags)
4571 {
4572 CajaIconInfo *icon;
4573 GIcon *gicon;
4574 GdkPixbuf *scaled_pixbuf;
4575
4576 if (file == NULL) {
4577 return NULL;
4578 }
4579
4580 gicon = get_custom_icon (file);
4581 if (gicon) {
4582 GdkPixbuf *pixbuf;
4583
4584 icon = caja_icon_info_lookup (gicon, size, scale);
4585 g_object_unref (gicon);
4586
4587 pixbuf = caja_icon_info_get_pixbuf (icon);
4588 if (pixbuf != NULL) {
4589 if (!file->details->is_launcher && !gdk_pixbuf_get_has_alpha (pixbuf)) {
4590 caja_ui_frame_image (&pixbuf);
4591 }
4592 g_object_unref (icon);
4593
4594 icon = caja_icon_info_new_for_pixbuf (pixbuf, scale);
4595 g_object_unref (pixbuf);
4596 }
4597
4598 return icon;
4599 }
4600
4601 if (flags & CAJA_FILE_ICON_FLAGS_USE_THUMBNAILS &&
4602 caja_file_should_show_thumbnail (file)) {
4603 int modified_size;
4604
4605 if (flags & CAJA_FILE_ICON_FLAGS_FORCE_THUMBNAIL_SIZE) {
4606 modified_size = size * scale;
4607 } else {
4608 modified_size = size * scale * cached_thumbnail_size / CAJA_ICON_SIZE_STANDARD;
4609 }
4610
4611 if (file->details->thumbnail) {
4612 int w, h, s;
4613 double thumb_scale;
4614 GdkPixbuf *raw_pixbuf;
4615
4616 raw_pixbuf = g_object_ref (file->details->thumbnail);
4617
4618 w = gdk_pixbuf_get_width (raw_pixbuf);
4619 h = gdk_pixbuf_get_height (raw_pixbuf);
4620
4621 s = MAX (w, h);
4622 /* Don't scale up small thumbnails in the standard view */
4623 if (s <= cached_thumbnail_size) {
4624 thumb_scale = (double)size / CAJA_ICON_SIZE_STANDARD;
4625 }
4626 else {
4627 thumb_scale = (double)modified_size / s;
4628 }
4629 /* Make sure that icons don't get smaller than CAJA_ICON_SIZE_SMALLEST */
4630 if (s*thumb_scale <= CAJA_ICON_SIZE_SMALLEST) {
4631 thumb_scale = (double) CAJA_ICON_SIZE_SMALLEST / s;
4632 }
4633
4634 scaled_pixbuf = gdk_pixbuf_scale_simple (raw_pixbuf,
4635 MAX (w * thumb_scale, 1),
4636 MAX (h * thumb_scale, 1),
4637 GDK_INTERP_BILINEAR);
4638
4639 /* Render frames only for thumbnails of non-image files
4640 and for images with no alpha channel. */
4641 gboolean is_image = file->details->mime_type &&
4642 (strncmp (file->details->mime_type, "image/", 6) == 0);
4643 if (!is_image || !gdk_pixbuf_get_has_alpha (raw_pixbuf)) {
4644 caja_ui_frame_image (&scaled_pixbuf);
4645 }
4646
4647 g_object_unref (raw_pixbuf);
4648
4649 /* Don't scale up if more than 25%, then read the original
4650 image instead. We don't want to compare to exactly 100%,
4651 since the zoom level 150% gives thumbnails at 144, which is
4652 ok to scale up from 128. */
4653 if (modified_size > 128 * 1.25 * scale &&
4654 !file->details->thumbnail_wants_original &&
4655 caja_can_thumbnail_internally (file)) {
4656 /* Invalidate if we resize upward */
4657 file->details->thumbnail_wants_original = TRUE;
4658 caja_file_invalidate_attributes (file, CAJA_FILE_ATTRIBUTE_THUMBNAIL);
4659 }
4660
4661 icon = caja_icon_info_new_for_pixbuf (scaled_pixbuf, scale);
4662 g_object_unref (scaled_pixbuf);
4663 return icon;
4664 } else if (file->details->thumbnail_path == NULL &&
4665 file->details->can_read &&
4666 !file->details->is_thumbnailing &&
4667 !file->details->thumbnailing_failed) {
4668 if (caja_can_thumbnail (file)) {
4669 caja_create_thumbnail (file);
4670 }
4671 }
4672 }
4673
4674 if (file->details->is_thumbnailing &&
4675 flags & CAJA_FILE_ICON_FLAGS_USE_THUMBNAILS)
4676 gicon = g_themed_icon_new (ICON_NAME_THUMBNAIL_LOADING);
4677 else
4678 gicon = caja_file_get_gicon (file, flags);
4679
4680 if (gicon) {
4681 icon = caja_icon_info_lookup (gicon, size, scale);
4682 g_object_unref (gicon);
4683 return icon;
4684 } else {
4685 return caja_icon_info_lookup (get_default_file_icon (flags), size, scale);
4686 }
4687 }
4688
4689 cairo_surface_t *
caja_file_get_icon_surface(CajaFile * file,int size,gboolean force_size,int scale,CajaFileIconFlags flags)4690 caja_file_get_icon_surface (CajaFile *file,
4691 int size,
4692 gboolean force_size,
4693 int scale,
4694 CajaFileIconFlags flags)
4695 {
4696 CajaIconInfo *info;
4697 cairo_surface_t *surface;
4698
4699 info = caja_file_get_icon (file, size, scale, flags);
4700 if (force_size) {
4701 surface = caja_icon_info_get_surface_at_size (info, size);
4702 } else {
4703 surface = caja_icon_info_get_surface (info);
4704 }
4705 g_object_unref (info);
4706
4707 return surface;
4708 }
4709
4710 char *
caja_file_get_custom_icon(CajaFile * file)4711 caja_file_get_custom_icon (CajaFile *file)
4712 {
4713 char *custom_icon;
4714
4715 if (file == NULL) {
4716 return NULL;
4717 }
4718
4719 /* Metadata takes precedence */
4720 custom_icon = get_custom_icon_metadata_uri (file);
4721
4722 if (custom_icon == NULL && file->details->got_link_info) {
4723 custom_icon = g_strdup (file->details->custom_icon);
4724 }
4725
4726 return custom_icon;
4727 }
4728
4729
4730 gboolean
caja_file_get_date(CajaFile * file,CajaDateType date_type,time_t * date)4731 caja_file_get_date (CajaFile *file,
4732 CajaDateType date_type,
4733 time_t *date)
4734 {
4735 if (date != NULL) {
4736 *date = 0;
4737 }
4738
4739 g_return_val_if_fail (date_type == CAJA_DATE_TYPE_CHANGED
4740 || date_type == CAJA_DATE_TYPE_ACCESSED
4741 || date_type == CAJA_DATE_TYPE_MODIFIED
4742 || date_type == CAJA_DATE_TYPE_CREATED
4743 || date_type == CAJA_DATE_TYPE_TRASHED
4744 || date_type == CAJA_DATE_TYPE_PERMISSIONS_CHANGED, FALSE);
4745
4746 if (file == NULL) {
4747 return FALSE;
4748 }
4749
4750 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
4751
4752 return EEL_CALL_METHOD_WITH_RETURN_VALUE
4753 (CAJA_FILE_CLASS, file,
4754 get_date, (file, date_type, date));
4755 }
4756
4757 static char *
caja_file_get_where_string(CajaFile * file)4758 caja_file_get_where_string (CajaFile *file)
4759 {
4760 if (file == NULL) {
4761 return NULL;
4762 }
4763
4764 g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
4765
4766 return EEL_CALL_METHOD_WITH_RETURN_VALUE
4767 (CAJA_FILE_CLASS, file,
4768 get_where_string, (file));
4769 }
4770
4771 static const char *TODAY_TIME_FORMATS [] = {
4772 /* Today, use special word.
4773 * strftime patterns preceeded with the widest
4774 * possible resulting string for that pattern.
4775 *
4776 * Note to localizers: You can look at man strftime
4777 * for details on the format, but you should only use
4778 * the specifiers from the C standard, not extensions.
4779 * These include "%" followed by one of
4780 * "aAbBcdHIjmMpSUwWxXyYZ". There are two extensions
4781 * in the Caja version of strftime that can be
4782 * used (and match GNU extensions). Putting a "-"
4783 * between the "%" and any numeric directive will turn
4784 * off zero padding, and putting a "_" there will use
4785 * space padding instead of zero padding.
4786 */
4787 N_("today at 00:00:00 PM"),
4788 N_("today at %-I:%M:%S %p"),
4789
4790 N_("today at 00:00 PM"),
4791 N_("today at %-I:%M %p"),
4792
4793 N_("today, 00:00 PM"),
4794 N_("today, %-I:%M %p"),
4795
4796 N_("today"),
4797 N_("today"),
4798
4799 NULL
4800 };
4801
4802 static const char *YESTERDAY_TIME_FORMATS [] = {
4803 /* Yesterday, use special word.
4804 * Note to localizers: Same issues as "today" string.
4805 */
4806 N_("yesterday at 00:00:00 PM"),
4807 N_("yesterday at %-I:%M:%S %p"),
4808
4809 N_("yesterday at 00:00 PM"),
4810 N_("yesterday at %-I:%M %p"),
4811
4812 N_("yesterday, 00:00 PM"),
4813 N_("yesterday, %-I:%M %p"),
4814
4815 N_("yesterday"),
4816 N_("yesterday"),
4817
4818 NULL
4819 };
4820
4821 static const char *CURRENT_WEEK_TIME_FORMATS [] = {
4822 /* Current week, include day of week.
4823 * Note to localizers: Same issues as "today" string.
4824 * The width measurement templates correspond to
4825 * the day/month name with the most letters.
4826 */
4827 N_("Wednesday, September 00 0000 at 00:00:00 PM"),
4828 N_("%A, %B %-d %Y at %-I:%M:%S %p"),
4829
4830 N_("Mon, Oct 00 0000 at 00:00:00 PM"),
4831 N_("%a, %b %-d %Y at %-I:%M:%S %p"),
4832
4833 N_("Mon, Oct 00 0000 at 00:00 PM"),
4834 N_("%a, %b %-d %Y at %-I:%M %p"),
4835
4836 N_("Oct 00 0000 at 00:00 PM"),
4837 N_("%b %-d %Y at %-I:%M %p"),
4838
4839 N_("Oct 00 0000, 00:00 PM"),
4840 N_("%b %-d %Y, %-I:%M %p"),
4841
4842 N_("00/00/00, 00:00 PM"),
4843 N_("%m/%-d/%y, %-I:%M %p"),
4844
4845 N_("00/00/00"),
4846 N_("%m/%d/%y"),
4847
4848 NULL
4849 };
4850
4851 static char *
caja_file_fit_date_as_string(CajaFile * file,CajaDateType date_type,int width,CajaWidthMeasureCallback measure_callback,CajaTruncateCallback truncate_callback,void * measure_context)4852 caja_file_fit_date_as_string (CajaFile *file,
4853 CajaDateType date_type,
4854 int width,
4855 CajaWidthMeasureCallback measure_callback,
4856 CajaTruncateCallback truncate_callback,
4857 void *measure_context)
4858 {
4859 time_t file_time_raw;
4860 const char **formats;
4861 const char *format;
4862 char *date_string;
4863 gchar *result = NULL;
4864 int i;
4865 GDateTime *date_time, *today;
4866 GTimeSpan file_date_age;
4867
4868 if (!caja_file_get_date (file, date_type, &file_time_raw)) {
4869 return NULL;
4870 }
4871
4872 date_time = g_date_time_new_from_unix_local (file_time_raw);
4873
4874 if (date_format_pref == CAJA_DATE_FORMAT_LOCALE) {
4875 result = g_date_time_format (date_time, "%c");
4876 goto out;
4877 } else if (date_format_pref == CAJA_DATE_FORMAT_ISO) {
4878 result = g_date_time_format (date_time, "%Y-%m-%d %H:%M:%S");
4879 goto out;
4880 }
4881
4882 today = g_date_time_new_now_local ();
4883 file_date_age = g_date_time_difference (today, date_time);
4884 g_date_time_unref (today);
4885
4886 /* Format varies depending on how old the date is. This minimizes
4887 * the length (and thus clutter & complication) of typical dates
4888 * while providing sufficient detail for recent dates to make
4889 * them maximally understandable at a glance. Keep all format
4890 * strings separate rather than combining bits & pieces for
4891 * internationalization's sake.
4892 */
4893
4894 if (file_date_age < G_TIME_SPAN_DAY) {
4895 formats = TODAY_TIME_FORMATS;
4896 } else if (file_date_age < 2 * G_TIME_SPAN_DAY) {
4897 formats = YESTERDAY_TIME_FORMATS;
4898 } else {
4899 formats = CURRENT_WEEK_TIME_FORMATS;
4900 }
4901
4902 /* Find the date format that just fits the required width. Instead of measuring
4903 * the resulting string width directly, measure the width of a template that represents
4904 * the widest possible version of a date in a given format. This is done by using M, m
4905 * and 0 for the variable letters/digits respectively.
4906 */
4907 format = NULL;
4908
4909 for (i = 0; ; i += 2) {
4910 const char *width_template;
4911
4912 width_template = (formats [i] ? _(formats [i]) : NULL);
4913 if (width_template == NULL) {
4914 /* no more formats left */
4915 g_assert (format != NULL);
4916
4917 /* Can't fit even the shortest format -- return an ellipsized form in the
4918 * shortest format
4919 */
4920
4921 date_string = g_date_time_format (date_time, format);
4922
4923 if (truncate_callback == NULL) {
4924 result = date_string;
4925 break;
4926 }
4927
4928 result = (* truncate_callback) (date_string, width, measure_context);
4929 g_free (date_string);
4930 break;
4931 }
4932
4933 format = _(formats [i + 1]);
4934
4935 if (measure_callback == NULL) {
4936 /* don't care about fitting the width */
4937 break;
4938 }
4939
4940 if ((* measure_callback) (width_template, measure_context) <= width) {
4941 /* The template fits, this is the format we can fit. */
4942 break;
4943 }
4944 }
4945
4946 if (result == NULL) {
4947 result = g_date_time_format (date_time, format);
4948 }
4949
4950 out:
4951 g_date_time_unref (date_time);
4952 return result;
4953 }
4954
4955 /**
4956 * caja_file_fit_modified_date_as_string:
4957 *
4958 * Get a user-displayable string representing a file modification date,
4959 * truncated to @width using the measuring and truncating callbacks.
4960 * @file: CajaFile representing the file in question.
4961 * @width: The desired resulting string width.
4962 * @measure_callback: The callback used to measure the string width.
4963 * @truncate_callback: The callback used to truncate the string to a desired width.
4964 * @measure_context: Data neede when measuring and truncating.
4965 *
4966 * Returns: Newly allocated string ready to display to the user.
4967 *
4968 **/
4969 char *
caja_file_fit_modified_date_as_string(CajaFile * file,int width,CajaWidthMeasureCallback measure_callback,CajaTruncateCallback truncate_callback,void * measure_context)4970 caja_file_fit_modified_date_as_string (CajaFile *file,
4971 int width,
4972 CajaWidthMeasureCallback measure_callback,
4973 CajaTruncateCallback truncate_callback,
4974 void *measure_context)
4975 {
4976 return caja_file_fit_date_as_string (file, CAJA_DATE_TYPE_MODIFIED,
4977 width, measure_callback, truncate_callback, measure_context);
4978 }
4979
4980 static char *
caja_file_get_trash_original_file_parent_as_string(CajaFile * file)4981 caja_file_get_trash_original_file_parent_as_string (CajaFile *file)
4982 {
4983 if (file->details->trash_orig_path != NULL) {
4984 CajaFile *orig_file, *parent;
4985 GFile *location;
4986 char *filename;
4987
4988 orig_file = caja_file_get_trash_original_file (file);
4989 parent = caja_file_get_parent (orig_file);
4990 location = caja_file_get_location (parent);
4991
4992 filename = g_file_get_parse_name (location);
4993
4994 g_object_unref (location);
4995 caja_file_unref (parent);
4996 caja_file_unref (orig_file);
4997
4998 return filename;
4999 }
5000
5001 return NULL;
5002 }
5003
5004 /**
5005 * caja_file_get_date_as_string:
5006 *
5007 * Get a user-displayable string representing a file modification date.
5008 * The caller is responsible for g_free-ing this string.
5009 * @file: CajaFile representing the file in question.
5010 *
5011 * Returns: Newly allocated string ready to display to the user.
5012 *
5013 **/
5014 static char *
caja_file_get_date_as_string(CajaFile * file,CajaDateType date_type)5015 caja_file_get_date_as_string (CajaFile *file, CajaDateType date_type)
5016 {
5017 return caja_file_fit_date_as_string (file, date_type,
5018 0, NULL, NULL, NULL);
5019 }
5020
5021 static CajaSpeedTradeoffValue show_directory_item_count;
5022 static CajaSpeedTradeoffValue show_text_in_icons;
5023
5024 static void
show_text_in_icons_changed_callback(gpointer callback_data)5025 show_text_in_icons_changed_callback (gpointer callback_data)
5026 {
5027 show_text_in_icons = g_settings_get_enum (caja_preferences, CAJA_PREFERENCES_SHOW_TEXT_IN_ICONS);
5028 }
5029
5030 static void
show_directory_item_count_changed_callback(gpointer callback_data)5031 show_directory_item_count_changed_callback (gpointer callback_data)
5032 {
5033 show_directory_item_count = g_settings_get_enum (caja_preferences, CAJA_PREFERENCES_SHOW_DIRECTORY_ITEM_COUNTS);
5034 }
5035
5036 static gboolean
get_speed_tradeoff_preference_for_file(CajaFile * file,CajaSpeedTradeoffValue value)5037 get_speed_tradeoff_preference_for_file (CajaFile *file, CajaSpeedTradeoffValue value)
5038 {
5039 GFilesystemPreviewType use_preview;
5040
5041 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
5042
5043 use_preview = caja_file_get_filesystem_use_preview (file);
5044
5045 if (value == CAJA_SPEED_TRADEOFF_ALWAYS) {
5046 if (use_preview == G_FILESYSTEM_PREVIEW_TYPE_NEVER) {
5047 return FALSE;
5048 } else {
5049 return TRUE;
5050 }
5051 }
5052
5053 if (value == CAJA_SPEED_TRADEOFF_NEVER) {
5054 return FALSE;
5055 }
5056
5057 g_assert (value == CAJA_SPEED_TRADEOFF_LOCAL_ONLY);
5058
5059 if (use_preview == G_FILESYSTEM_PREVIEW_TYPE_NEVER) {
5060 /* file system says to never preview anything */
5061 return FALSE;
5062 } else if (use_preview == G_FILESYSTEM_PREVIEW_TYPE_IF_LOCAL) {
5063 /* file system says we should treat file as if it's local */
5064 return TRUE;
5065 } else {
5066 /* only local files */
5067 return caja_file_is_local (file);
5068 }
5069 }
5070
5071 gboolean
caja_file_should_show_directory_item_count(CajaFile * file)5072 caja_file_should_show_directory_item_count (CajaFile *file)
5073 {
5074 static gboolean show_directory_item_count_callback_added = FALSE;
5075
5076 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
5077
5078 if (file->details->mime_type &&
5079 strcmp (file->details->mime_type, "x-directory/smb-share") == 0) {
5080 return FALSE;
5081 }
5082
5083 /* Add the callback once for the life of our process */
5084 if (!show_directory_item_count_callback_added) {
5085 g_signal_connect_swapped (caja_preferences,
5086 "changed::" CAJA_PREFERENCES_SHOW_DIRECTORY_ITEM_COUNTS,
5087 G_CALLBACK(show_directory_item_count_changed_callback),
5088 NULL);
5089 show_directory_item_count_callback_added = TRUE;
5090
5091 /* Peek for the first time */
5092 show_directory_item_count_changed_callback (NULL);
5093 }
5094
5095 return get_speed_tradeoff_preference_for_file (file, show_directory_item_count);
5096 }
5097
5098 gboolean
caja_file_should_show_type(CajaFile * file)5099 caja_file_should_show_type (CajaFile *file)
5100 {
5101 char *uri;
5102 gboolean ret;
5103
5104 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
5105
5106 uri = caja_file_get_uri (file);
5107 ret = ((strcmp (uri, "computer:///") != 0) &&
5108 (strcmp (uri, "network:///") != 0) &&
5109 (strcmp (uri, "smb:///") != 0));
5110 g_free (uri);
5111
5112 return ret;
5113 }
5114
5115 gboolean
caja_file_should_get_top_left_text(CajaFile * file)5116 caja_file_should_get_top_left_text (CajaFile *file)
5117 {
5118 static gboolean show_text_in_icons_callback_added = FALSE;
5119
5120 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
5121
5122 /* Add the callback once for the life of our process */
5123 if (!show_text_in_icons_callback_added) {
5124 g_signal_connect_swapped (caja_preferences,
5125 "changed::" CAJA_PREFERENCES_SHOW_TEXT_IN_ICONS,
5126 G_CALLBACK (show_text_in_icons_changed_callback),
5127 NULL);
5128 show_text_in_icons_callback_added = TRUE;
5129
5130 /* Peek for the first time */
5131 show_text_in_icons_changed_callback (NULL);
5132 }
5133
5134 if (show_text_in_icons == CAJA_SPEED_TRADEOFF_ALWAYS) {
5135 return TRUE;
5136 }
5137
5138 if (show_text_in_icons == CAJA_SPEED_TRADEOFF_NEVER) {
5139 return FALSE;
5140 }
5141
5142 return get_speed_tradeoff_preference_for_file (file, show_text_in_icons);
5143 }
5144
5145 /**
5146 * caja_file_get_directory_item_count
5147 *
5148 * Get the number of items in a directory.
5149 * @file: CajaFile representing a directory.
5150 * @count: Place to put count.
5151 * @count_unreadable: Set to TRUE (if non-NULL) if permissions prevent
5152 * the item count from being read on this directory. Otherwise set to FALSE.
5153 *
5154 * Returns: TRUE if count is available.
5155 *
5156 **/
5157 gboolean
caja_file_get_directory_item_count(CajaFile * file,guint * count,gboolean * count_unreadable)5158 caja_file_get_directory_item_count (CajaFile *file,
5159 guint *count,
5160 gboolean *count_unreadable)
5161 {
5162 if (count != NULL) {
5163 *count = 0;
5164 }
5165 if (count_unreadable != NULL) {
5166 *count_unreadable = FALSE;
5167 }
5168
5169 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
5170
5171 if (!caja_file_is_directory (file)) {
5172 return FALSE;
5173 }
5174
5175 if (!caja_file_should_show_directory_item_count (file)) {
5176 return FALSE;
5177 }
5178
5179 return EEL_CALL_METHOD_WITH_RETURN_VALUE
5180 (CAJA_FILE_CLASS, file,
5181 get_item_count, (file, count, count_unreadable));
5182 }
5183
5184 /**
5185 * caja_file_get_deep_counts
5186 *
5187 * Get the statistics about items inside a directory.
5188 * @file: CajaFile representing a directory or file.
5189 * @directory_count: Place to put count of directories inside.
5190 * @files_count: Place to put count of files inside.
5191 * @unreadable_directory_count: Number of directories encountered
5192 * that were unreadable.
5193 * @total_size: Total size of all files and directories visited.
5194 * @total_size_on_disk: Total size on disk of all files and directories visited.
5195 * @force: Whether the deep counts should even be collected if
5196 * caja_file_should_show_directory_item_count returns FALSE
5197 * for this file.
5198 *
5199 * Returns: Status to indicate whether sizes are available.
5200 *
5201 **/
5202 CajaRequestStatus
caja_file_get_deep_counts(CajaFile * file,guint * directory_count,guint * file_count,guint * unreadable_directory_count,goffset * total_size,goffset * total_size_on_disk,gboolean force)5203 caja_file_get_deep_counts (CajaFile *file,
5204 guint *directory_count,
5205 guint *file_count,
5206 guint *unreadable_directory_count,
5207 goffset *total_size,
5208 goffset *total_size_on_disk,
5209 gboolean force)
5210 {
5211 if (directory_count != NULL) {
5212 *directory_count = 0;
5213 }
5214 if (file_count != NULL) {
5215 *file_count = 0;
5216 }
5217 if (unreadable_directory_count != NULL) {
5218 *unreadable_directory_count = 0;
5219 }
5220 if (total_size != NULL) {
5221 *total_size = 0;
5222 }
5223 if (total_size_on_disk != NULL) {
5224 *total_size_on_disk = 0;
5225 }
5226
5227 g_return_val_if_fail (CAJA_IS_FILE (file), CAJA_REQUEST_DONE);
5228
5229 if (!force && !caja_file_should_show_directory_item_count (file)) {
5230 /* Set field so an existing value isn't treated as up-to-date
5231 * when preference changes later.
5232 */
5233 file->details->deep_counts_status = CAJA_REQUEST_NOT_STARTED;
5234 return file->details->deep_counts_status;
5235 }
5236
5237 return EEL_CALL_METHOD_WITH_RETURN_VALUE
5238 (CAJA_FILE_CLASS, file,
5239 get_deep_counts, (file,
5240 directory_count,
5241 file_count,
5242 unreadable_directory_count,
5243 total_size,
5244 total_size_on_disk));
5245 }
5246
5247 void
caja_file_recompute_deep_counts(CajaFile * file)5248 caja_file_recompute_deep_counts (CajaFile *file)
5249 {
5250 if (file->details->deep_counts_status != CAJA_REQUEST_IN_PROGRESS) {
5251 file->details->deep_counts_status = CAJA_REQUEST_NOT_STARTED;
5252 if (file->details->directory != NULL) {
5253 caja_directory_add_file_to_work_queue (file->details->directory, file);
5254 caja_directory_async_state_changed (file->details->directory);
5255 }
5256 }
5257 }
5258
5259 gboolean
caja_file_can_get_size(CajaFile * file)5260 caja_file_can_get_size (CajaFile *file)
5261 {
5262 return file->details->size == -1;
5263 }
5264
5265
5266 /**
5267 * caja_file_get_size
5268 *
5269 * Get the file size.
5270 * @file: CajaFile representing the file in question.
5271 *
5272 * Returns: Size in bytes.
5273 *
5274 **/
5275 goffset
caja_file_get_size(CajaFile * file)5276 caja_file_get_size (CajaFile *file)
5277 {
5278 /* Before we have info on the file, we don't know the size. */
5279 if (file->details->size == -1)
5280 return 0;
5281 return file->details->size;
5282 }
5283
5284 /**
5285 * caja_file_get_size_on_disk
5286 *
5287 * Get the file size on disk (how many bytes is using on the filesystem).
5288 * e.g.: usually files with 1 byte will use a whole inode so it will return the
5289 * size of 1 inode. If the file is sparse the size on disk will be equal or less
5290 * than the size of the file.
5291 * @file: CajaFile representing the file in question.
5292 *
5293 * Returns: Size in bytes.
5294 *
5295 **/
5296 goffset
caja_file_get_size_on_disk(CajaFile * file)5297 caja_file_get_size_on_disk (CajaFile *file)
5298 {
5299 /* Before we have info on the file, we don't know the size. */
5300 if (file->details->size_on_disk == -1)
5301 return 0;
5302 return file->details->size_on_disk;
5303 }
5304
5305 time_t
caja_file_get_mtime(CajaFile * file)5306 caja_file_get_mtime (CajaFile *file)
5307 {
5308 return file->details->mtime;
5309 }
5310
5311
5312 static void
set_attributes_get_info_callback(GObject * source_object,GAsyncResult * res,gpointer callback_data)5313 set_attributes_get_info_callback (GObject *source_object,
5314 GAsyncResult *res,
5315 gpointer callback_data)
5316 {
5317 CajaFileOperation *op;
5318 GFileInfo *new_info;
5319 GError *error;
5320
5321 op = callback_data;
5322
5323 error = NULL;
5324 new_info = g_file_query_info_finish (G_FILE (source_object), res, &error);
5325 if (new_info != NULL) {
5326 if (caja_file_update_info (op->file, new_info)) {
5327 caja_file_changed (op->file);
5328 }
5329 g_object_unref (new_info);
5330 }
5331 caja_file_operation_complete (op, NULL, error);
5332 if (error) {
5333 g_error_free (error);
5334 }
5335 }
5336
5337
5338 static void
set_attributes_callback(GObject * source_object,GAsyncResult * result,gpointer callback_data)5339 set_attributes_callback (GObject *source_object,
5340 GAsyncResult *result,
5341 gpointer callback_data)
5342 {
5343 CajaFileOperation *op;
5344 GError *error;
5345 gboolean res;
5346
5347 op = callback_data;
5348
5349 error = NULL;
5350 res = g_file_set_attributes_finish (G_FILE (source_object),
5351 result,
5352 NULL,
5353 &error);
5354
5355 if (res) {
5356 g_file_query_info_async (G_FILE (source_object),
5357 CAJA_FILE_DEFAULT_ATTRIBUTES,
5358 0,
5359 G_PRIORITY_DEFAULT,
5360 op->cancellable,
5361 set_attributes_get_info_callback, op);
5362 } else {
5363 caja_file_operation_complete (op, NULL, error);
5364 g_error_free (error);
5365 }
5366 }
5367
5368 void
caja_file_set_attributes(CajaFile * file,GFileInfo * attributes,CajaFileOperationCallback callback,gpointer callback_data)5369 caja_file_set_attributes (CajaFile *file,
5370 GFileInfo *attributes,
5371 CajaFileOperationCallback callback,
5372 gpointer callback_data)
5373 {
5374 CajaFileOperation *op;
5375 GFile *location;
5376
5377 op = caja_file_operation_new (file, callback, callback_data);
5378
5379 location = caja_file_get_location (file);
5380 g_file_set_attributes_async (location,
5381 attributes,
5382 0,
5383 G_PRIORITY_DEFAULT,
5384 op->cancellable,
5385 set_attributes_callback,
5386 op);
5387 g_object_unref (location);
5388 }
5389
5390
5391 /**
5392 * caja_file_can_get_permissions:
5393 *
5394 * Check whether the permissions for a file are determinable.
5395 * This might not be the case for files on non-UNIX file systems.
5396 *
5397 * @file: The file in question.
5398 *
5399 * Return value: TRUE if the permissions are valid.
5400 */
5401 gboolean
caja_file_can_get_permissions(CajaFile * file)5402 caja_file_can_get_permissions (CajaFile *file)
5403 {
5404 return file->details->has_permissions;
5405 }
5406
5407 /**
5408 * caja_file_can_set_permissions:
5409 *
5410 * Check whether the current user is allowed to change
5411 * the permissions of a file.
5412 *
5413 * @file: The file in question.
5414 *
5415 * Return value: TRUE if the current user can change the
5416 * permissions of @file, FALSE otherwise. It's always possible
5417 * that when you actually try to do it, you will fail.
5418 */
5419 gboolean
caja_file_can_set_permissions(CajaFile * file)5420 caja_file_can_set_permissions (CajaFile *file)
5421 {
5422 uid_t user_id;
5423
5424 if (file->details->uid != -1 &&
5425 caja_file_is_local (file)) {
5426 /* Check the user. */
5427 user_id = geteuid();
5428
5429 /* Owner is allowed to set permissions. */
5430 if (user_id == (uid_t) file->details->uid) {
5431 return TRUE;
5432 }
5433
5434 /* Root is also allowed to set permissions. */
5435 if (user_id == 0) {
5436 return TRUE;
5437 }
5438
5439 /* Nobody else is allowed. */
5440 return FALSE;
5441 }
5442
5443 /* pretend to have full chmod rights when no info is available, relevant when
5444 * the FS can't provide ownership info, for instance for FTP */
5445 return TRUE;
5446 }
5447
5448 guint
caja_file_get_permissions(CajaFile * file)5449 caja_file_get_permissions (CajaFile *file)
5450 {
5451 g_return_val_if_fail (caja_file_can_get_permissions (file), 0);
5452
5453 return file->details->permissions;
5454 }
5455
5456 /**
5457 * caja_file_set_permissions:
5458 *
5459 * Change a file's permissions. This should only be called if
5460 * caja_file_can_set_permissions returned TRUE.
5461 *
5462 * @file: CajaFile representing the file in question.
5463 * @new_permissions: New permissions value. This is the whole
5464 * set of permissions, not a delta.
5465 **/
5466 void
caja_file_set_permissions(CajaFile * file,guint32 new_permissions,CajaFileOperationCallback callback,gpointer callback_data)5467 caja_file_set_permissions (CajaFile *file,
5468 guint32 new_permissions,
5469 CajaFileOperationCallback callback,
5470 gpointer callback_data)
5471 {
5472 GFileInfo *info;
5473
5474 if (!caja_file_can_set_permissions (file)) {
5475 GError *error;
5476
5477 /* Claim that something changed even if the permission change failed.
5478 * This makes it easier for some clients who see the "reverting"
5479 * to the old permissions as "changing back".
5480 */
5481 caja_file_changed (file);
5482 error = g_error_new (G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
5483 _("Not allowed to set permissions"));
5484 (* callback) (file, NULL, error, callback_data);
5485 g_error_free (error);
5486 return;
5487 }
5488
5489 /* Test the permissions-haven't-changed case explicitly
5490 * because we don't want to send the file-changed signal if
5491 * nothing changed.
5492 */
5493 if (new_permissions == file->details->permissions) {
5494 (* callback) (file, NULL, NULL, callback_data);
5495 return;
5496 }
5497
5498 // Start UNDO-REDO
5499 if (!caja_undostack_manager_is_undo_redo(caja_undostack_manager_instance())) {
5500 CajaUndoStackActionData* undo_redo_data = caja_undostack_manager_data_new (CAJA_UNDOSTACK_SETPERMISSIONS, 1);
5501 caja_undostack_manager_data_set_file_permissions(undo_redo_data, caja_file_get_uri(file), file->details->permissions, new_permissions);
5502 caja_undostack_manager_add_action (caja_undostack_manager_instance(),
5503 undo_redo_data);
5504 }
5505 // End UNDO-REDO
5506
5507 info = g_file_info_new ();
5508 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE, new_permissions);
5509 caja_file_set_attributes (file, info, callback, callback_data);
5510 g_object_unref (info);
5511 }
5512
5513 /**
5514 * caja_file_can_get_selinux_context:
5515 *
5516 * Check whether the selinux context for a file are determinable.
5517 * This might not be the case for files on non-UNIX file systems,
5518 * files without a context or systems that don't support selinux.
5519 *
5520 * @file: The file in question.
5521 *
5522 * Return value: TRUE if the permissions are valid.
5523 */
5524 gboolean
caja_file_can_get_selinux_context(CajaFile * file)5525 caja_file_can_get_selinux_context (CajaFile *file)
5526 {
5527 return file->details->selinux_context != NULL;
5528 }
5529
5530
5531 /**
5532 * caja_file_get_selinux_context:
5533 *
5534 * Get a user-displayable string representing a file's selinux
5535 * context
5536 * @file: CajaFile representing the file in question.
5537 *
5538 * Returns: Newly allocated string ready to display to the user.
5539 *
5540 **/
5541 char *
caja_file_get_selinux_context(CajaFile * file)5542 caja_file_get_selinux_context (CajaFile *file)
5543 {
5544 char *translated;
5545 char *raw;
5546
5547 g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
5548
5549 if (!caja_file_can_get_selinux_context (file)) {
5550 return NULL;
5551 }
5552
5553 raw = file->details->selinux_context;
5554
5555 #ifdef HAVE_SELINUX
5556 if (selinux_raw_to_trans_context (raw, &translated) == 0) {
5557 char *tmp;
5558 tmp = g_strdup (translated);
5559 freecon (translated);
5560 translated = tmp;
5561 }
5562 else
5563 #endif
5564 {
5565 translated = g_strdup (raw);
5566 }
5567
5568 return translated;
5569 }
5570
5571 static char *
get_real_name(const char * name,const char * gecos)5572 get_real_name (const char *name, const char *gecos)
5573 {
5574 char *locale_string, *part_before_comma, *capitalized_login_name, *real_name;
5575
5576 if (gecos == NULL) {
5577 return NULL;
5578 }
5579
5580 locale_string = eel_str_strip_substring_and_after (gecos, ",");
5581 if (!g_utf8_validate (locale_string, -1, NULL)) {
5582 part_before_comma = g_locale_to_utf8 (locale_string, -1, NULL, NULL, NULL);
5583 g_free (locale_string);
5584 } else {
5585 part_before_comma = locale_string;
5586 }
5587
5588 if (!g_utf8_validate (name, -1, NULL)) {
5589 locale_string = g_locale_to_utf8 (name, -1, NULL, NULL, NULL);
5590 } else {
5591 locale_string = g_strdup (name);
5592 }
5593
5594 capitalized_login_name = eel_str_capitalize (locale_string);
5595 g_free (locale_string);
5596
5597 if (capitalized_login_name == NULL) {
5598 real_name = part_before_comma;
5599 } else {
5600 real_name = eel_str_replace_substring
5601 (part_before_comma, "&", capitalized_login_name);
5602 g_free (part_before_comma);
5603 }
5604
5605
5606 if (eel_str_is_empty (real_name)
5607 || eel_strcmp (name, real_name) == 0
5608 || eel_strcmp (capitalized_login_name, real_name) == 0) {
5609 g_free (real_name);
5610 real_name = NULL;
5611 }
5612
5613 g_free (capitalized_login_name);
5614
5615 return real_name;
5616 }
5617
5618 static gboolean
get_group_id_from_group_name(const char * group_name,uid_t * gid)5619 get_group_id_from_group_name (const char *group_name, uid_t *gid)
5620 {
5621 struct group *group;
5622
5623 g_assert (gid != NULL);
5624
5625 group = getgrnam (group_name);
5626
5627 if (group == NULL) {
5628 return FALSE;
5629 }
5630
5631 *gid = group->gr_gid;
5632
5633 return TRUE;
5634 }
5635
5636 static gboolean
get_ids_from_user_name(const char * user_name,uid_t * uid,uid_t * gid)5637 get_ids_from_user_name (const char *user_name, uid_t *uid, uid_t *gid)
5638 {
5639 struct passwd *password_info;
5640
5641 g_assert (uid != NULL || gid != NULL);
5642
5643 password_info = getpwnam (user_name);
5644
5645 if (password_info == NULL) {
5646 return FALSE;
5647 }
5648
5649 if (uid != NULL) {
5650 *uid = password_info->pw_uid;
5651 }
5652
5653 if (gid != NULL) {
5654 *gid = password_info->pw_gid;
5655 }
5656
5657 return TRUE;
5658 }
5659
5660 static gboolean
get_user_id_from_user_name(const char * user_name,uid_t * id)5661 get_user_id_from_user_name (const char *user_name, uid_t *id)
5662 {
5663 return get_ids_from_user_name (user_name, id, NULL);
5664 }
5665
5666 static gboolean
get_id_from_digit_string(const char * digit_string,uid_t * id)5667 get_id_from_digit_string (const char *digit_string, uid_t *id)
5668 {
5669 long scanned_id;
5670 char c;
5671
5672 g_assert (id != NULL);
5673
5674 /* Only accept string if it has one integer with nothing
5675 * afterwards.
5676 */
5677 if (sscanf (digit_string, "%ld%c", &scanned_id, &c) != 1) {
5678 return FALSE;
5679 }
5680 *id = scanned_id;
5681 return TRUE;
5682 }
5683
5684 /**
5685 * caja_file_can_get_owner:
5686 *
5687 * Check whether the owner a file is determinable.
5688 * This might not be the case for files on non-UNIX file systems.
5689 *
5690 * @file: The file in question.
5691 *
5692 * Return value: TRUE if the owner is valid.
5693 */
5694 gboolean
caja_file_can_get_owner(CajaFile * file)5695 caja_file_can_get_owner (CajaFile *file)
5696 {
5697 /* Before we have info on a file, the owner is unknown. */
5698 return file->details->uid != -1;
5699 }
5700
5701 /**
5702 * caja_file_get_owner_name:
5703 *
5704 * Get the user name of the file's owner. If the owner has no
5705 * name, returns the userid as a string. The caller is responsible
5706 * for g_free-ing this string.
5707 *
5708 * @file: The file in question.
5709 *
5710 * Return value: A newly-allocated string.
5711 */
5712 char *
caja_file_get_owner_name(CajaFile * file)5713 caja_file_get_owner_name (CajaFile *file)
5714 {
5715 return caja_file_get_owner_as_string (file, FALSE);
5716 }
5717
5718 /**
5719 * caja_file_can_set_owner:
5720 *
5721 * Check whether the current user is allowed to change
5722 * the owner of a file.
5723 *
5724 * @file: The file in question.
5725 *
5726 * Return value: TRUE if the current user can change the
5727 * owner of @file, FALSE otherwise. It's always possible
5728 * that when you actually try to do it, you will fail.
5729 */
5730 gboolean
caja_file_can_set_owner(CajaFile * file)5731 caja_file_can_set_owner (CajaFile *file)
5732 {
5733 /* Not allowed to set the owner if we can't
5734 * even read it. This can happen on non-UNIX file
5735 * systems.
5736 */
5737 if (!caja_file_can_get_owner (file)) {
5738 return FALSE;
5739 }
5740
5741 /* Only root is also allowed to set the owner. */
5742 return geteuid() == 0;
5743 }
5744
5745 /**
5746 * caja_file_set_owner:
5747 *
5748 * Set the owner of a file. This will only have any effect if
5749 * caja_file_can_set_owner returns TRUE.
5750 *
5751 * @file: The file in question.
5752 * @user_name_or_id: The user name to set the owner to.
5753 * If the string does not match any user name, and the
5754 * string is an integer, the owner will be set to the
5755 * userid represented by that integer.
5756 * @callback: Function called when asynch owner change succeeds or fails.
5757 * @callback_data: Parameter passed back with callback function.
5758 */
5759 void
caja_file_set_owner(CajaFile * file,const char * user_name_or_id,CajaFileOperationCallback callback,gpointer callback_data)5760 caja_file_set_owner (CajaFile *file,
5761 const char *user_name_or_id,
5762 CajaFileOperationCallback callback,
5763 gpointer callback_data)
5764 {
5765 GError *error;
5766 GFileInfo *info;
5767 uid_t new_id;
5768
5769 if (!caja_file_can_set_owner (file)) {
5770 /* Claim that something changed even if the permission
5771 * change failed. This makes it easier for some
5772 * clients who see the "reverting" to the old owner as
5773 * "changing back".
5774 */
5775 caja_file_changed (file);
5776 error = g_error_new (G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
5777 _("Not allowed to set owner"));
5778 (* callback) (file, NULL, error, callback_data);
5779 g_error_free (error);
5780 return;
5781 }
5782
5783 /* If no match treating user_name_or_id as name, try treating
5784 * it as id.
5785 */
5786 if (!get_user_id_from_user_name (user_name_or_id, &new_id)
5787 && !get_id_from_digit_string (user_name_or_id, &new_id)) {
5788 /* Claim that something changed even if the permission
5789 * change failed. This makes it easier for some
5790 * clients who see the "reverting" to the old owner as
5791 * "changing back".
5792 */
5793 caja_file_changed (file);
5794 error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
5795 _("Specified owner '%s' doesn't exist"), user_name_or_id);
5796 (* callback) (file, NULL, error, callback_data);
5797 g_error_free (error);
5798 return;
5799 }
5800
5801 /* Test the owner-hasn't-changed case explicitly because we
5802 * don't want to send the file-changed signal if nothing
5803 * changed.
5804 */
5805 if (new_id == (uid_t) file->details->uid) {
5806 (* callback) (file, NULL, NULL, callback_data);
5807 return;
5808 }
5809
5810 // Start UNDO-REDO
5811 if (!caja_undostack_manager_is_undo_redo(caja_undostack_manager_instance())) {
5812 char* current_owner = caja_file_get_owner_as_string (file, FALSE);
5813 CajaUndoStackActionData* undo_redo_data = caja_undostack_manager_data_new (CAJA_UNDOSTACK_CHANGEOWNER, 1);
5814 caja_undostack_manager_data_set_owner_change_information(undo_redo_data, caja_file_get_uri(file), current_owner, user_name_or_id);
5815 caja_undostack_manager_add_action (caja_undostack_manager_instance(),
5816 undo_redo_data);
5817 g_free(current_owner);
5818 }
5819 // End UNDO-REDO
5820
5821 info = g_file_info_new ();
5822 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID, new_id);
5823 caja_file_set_attributes (file, info, callback, callback_data);
5824 g_object_unref (info);
5825 }
5826
5827 /**
5828 * caja_get_user_names:
5829 *
5830 * Get a list of user names. For users with a different associated
5831 * "real name", the real name follows the standard user name, separated
5832 * by a carriage return. The caller is responsible for freeing this list
5833 * and its contents.
5834 */
5835 GList *
caja_get_user_names(void)5836 caja_get_user_names (void)
5837 {
5838 GList *list;
5839 char *name;
5840 struct passwd *user;
5841
5842 list = NULL;
5843
5844 setpwent ();
5845
5846 while ((user = getpwent ()) != NULL) {
5847 char *real_name;
5848
5849 real_name = get_real_name (user->pw_name, user->pw_gecos);
5850 if (real_name != NULL) {
5851 name = g_strconcat (user->pw_name, "\n", real_name, NULL);
5852 } else {
5853 name = g_strdup (user->pw_name);
5854 }
5855 g_free (real_name);
5856 list = g_list_prepend (list, name);
5857 }
5858
5859 endpwent ();
5860
5861 return eel_g_str_list_alphabetize (list);
5862 }
5863
5864 /**
5865 * caja_file_can_get_group:
5866 *
5867 * Check whether the group a file is determinable.
5868 * This might not be the case for files on non-UNIX file systems.
5869 *
5870 * @file: The file in question.
5871 *
5872 * Return value: TRUE if the group is valid.
5873 */
5874 gboolean
caja_file_can_get_group(CajaFile * file)5875 caja_file_can_get_group (CajaFile *file)
5876 {
5877 /* Before we have info on a file, the group is unknown. */
5878 return file->details->gid != -1;
5879 }
5880
5881 /**
5882 * caja_file_get_group_name:
5883 *
5884 * Get the name of the file's group. If the group has no
5885 * name, returns the groupid as a string. The caller is responsible
5886 * for g_free-ing this string.
5887 *
5888 * @file: The file in question.
5889 *
5890 * Return value: A newly-allocated string.
5891 **/
5892 char *
caja_file_get_group_name(CajaFile * file)5893 caja_file_get_group_name (CajaFile *file)
5894 {
5895 return g_strdup (file->details->group);
5896 }
5897
5898 /**
5899 * caja_file_can_set_group:
5900 *
5901 * Check whether the current user is allowed to change
5902 * the group of a file.
5903 *
5904 * @file: The file in question.
5905 *
5906 * Return value: TRUE if the current user can change the
5907 * group of @file, FALSE otherwise. It's always possible
5908 * that when you actually try to do it, you will fail.
5909 */
5910 gboolean
caja_file_can_set_group(CajaFile * file)5911 caja_file_can_set_group (CajaFile *file)
5912 {
5913 uid_t user_id;
5914
5915 /* Not allowed to set the permissions if we can't
5916 * even read them. This can happen on non-UNIX file
5917 * systems.
5918 */
5919 if (!caja_file_can_get_group (file)) {
5920 return FALSE;
5921 }
5922
5923 /* Check the user. */
5924 user_id = geteuid();
5925
5926 /* Owner is allowed to set group (with restrictions). */
5927 if (user_id == (uid_t) file->details->uid) {
5928 return TRUE;
5929 }
5930
5931 /* Root is also allowed to set group. */
5932 if (user_id == 0) {
5933 return TRUE;
5934 }
5935
5936 /* Nobody else is allowed. */
5937 return FALSE;
5938 }
5939
5940 /* Get a list of group names, filtered to only the ones
5941 * that contain the given username. If the username is
5942 * NULL, returns a list of all group names.
5943 */
5944 static GList *
caja_get_group_names_for_user(void)5945 caja_get_group_names_for_user (void)
5946 {
5947 GList *list;
5948 int count, i;
5949 gid_t gid_list[NGROUPS_MAX + 1];
5950 struct group *group = NULL;
5951
5952
5953 list = NULL;
5954
5955 count = getgroups (NGROUPS_MAX + 1, gid_list);
5956 for (i = 0; i < count; i++) {
5957 group = getgrgid (gid_list[i]);
5958 if (group == NULL)
5959 break;
5960
5961 list = g_list_prepend (list, g_strdup (group->gr_name));
5962 }
5963
5964 return eel_g_str_list_alphabetize (list);
5965 }
5966
5967 /**
5968 * caja_get_group_names:
5969 *
5970 * Get a list of all group names.
5971 */
5972 GList *
caja_get_all_group_names(void)5973 caja_get_all_group_names (void)
5974 {
5975 GList *list;
5976 struct group *group;
5977
5978 list = NULL;
5979
5980 setgrent ();
5981
5982 while ((group = getgrent ()) != NULL)
5983 list = g_list_prepend (list, g_strdup (group->gr_name));
5984
5985 endgrent ();
5986
5987 return eel_g_str_list_alphabetize (list);
5988 }
5989
5990 /**
5991 * caja_file_get_settable_group_names:
5992 *
5993 * Get a list of all group names that the current user
5994 * can set the group of a specific file to.
5995 *
5996 * @file: The CajaFile in question.
5997 */
5998 GList *
caja_file_get_settable_group_names(CajaFile * file)5999 caja_file_get_settable_group_names (CajaFile *file)
6000 {
6001 uid_t user_id;
6002 GList *result;
6003
6004 if (!caja_file_can_set_group (file)) {
6005 return NULL;
6006 }
6007
6008 /* Check the user. */
6009 user_id = geteuid();
6010
6011 if (user_id == 0) {
6012 /* Root is allowed to set group to anything. */
6013 result = caja_get_all_group_names ();
6014 } else if (user_id == (uid_t) file->details->uid) {
6015 /* Owner is allowed to set group to any that owner is member of. */
6016 result = caja_get_group_names_for_user ();
6017 } else {
6018 g_warning ("unhandled case in caja_get_settable_group_names");
6019 result = NULL;
6020 }
6021
6022 return result;
6023 }
6024
6025 /**
6026 * caja_file_set_group:
6027 *
6028 * Set the group of a file. This will only have any effect if
6029 * caja_file_can_set_group returns TRUE.
6030 *
6031 * @file: The file in question.
6032 * @group_name_or_id: The group name to set the owner to.
6033 * If the string does not match any group name, and the
6034 * string is an integer, the group will be set to the
6035 * group id represented by that integer.
6036 * @callback: Function called when asynch group change succeeds or fails.
6037 * @callback_data: Parameter passed back with callback function.
6038 */
6039 void
caja_file_set_group(CajaFile * file,const char * group_name_or_id,CajaFileOperationCallback callback,gpointer callback_data)6040 caja_file_set_group (CajaFile *file,
6041 const char *group_name_or_id,
6042 CajaFileOperationCallback callback,
6043 gpointer callback_data)
6044 {
6045 GError *error;
6046 GFileInfo *info;
6047 uid_t new_id;
6048
6049 if (!caja_file_can_set_group (file)) {
6050 /* Claim that something changed even if the group
6051 * change failed. This makes it easier for some
6052 * clients who see the "reverting" to the old group as
6053 * "changing back".
6054 */
6055 caja_file_changed (file);
6056 error = g_error_new (G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
6057 _("Not allowed to set group"));
6058 (* callback) (file, NULL, error, callback_data);
6059 g_error_free (error);
6060 return;
6061 }
6062
6063 /* If no match treating group_name_or_id as name, try treating
6064 * it as id.
6065 */
6066 if (!get_group_id_from_group_name (group_name_or_id, &new_id)
6067 && !get_id_from_digit_string (group_name_or_id, &new_id)) {
6068 /* Claim that something changed even if the group
6069 * change failed. This makes it easier for some
6070 * clients who see the "reverting" to the old group as
6071 * "changing back".
6072 */
6073 caja_file_changed (file);
6074 error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
6075 _("Specified group '%s' doesn't exist"), group_name_or_id);
6076 (* callback) (file, NULL, error, callback_data);
6077 g_error_free (error);
6078 return;
6079 }
6080
6081 if (new_id == (gid_t) file->details->gid) {
6082 (* callback) (file, NULL, NULL, callback_data);
6083 return;
6084 }
6085
6086 // Start UNDO-REDO
6087 if (!caja_undostack_manager_is_undo_redo(caja_undostack_manager_instance())) {
6088 char* current_group = caja_file_get_group_name (file);
6089 CajaUndoStackActionData* undo_redo_data = caja_undostack_manager_data_new (CAJA_UNDOSTACK_CHANGEGROUP, 1);
6090 caja_undostack_manager_data_set_group_change_information(undo_redo_data, caja_file_get_uri(file), current_group, group_name_or_id);
6091 caja_undostack_manager_add_action (caja_undostack_manager_instance(),
6092 undo_redo_data);
6093 g_free(current_group);
6094 }
6095 // End UNDO-REDO
6096
6097 info = g_file_info_new ();
6098 g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID, new_id);
6099 caja_file_set_attributes (file, info, callback, callback_data);
6100 g_object_unref (info);
6101 }
6102
6103 /**
6104 * caja_file_get_octal_permissions_as_string:
6105 *
6106 * Get a user-displayable string representing a file's permissions
6107 * as an octal number. The caller
6108 * is responsible for g_free-ing this string.
6109 * @file: CajaFile representing the file in question.
6110 *
6111 * Returns: Newly allocated string ready to display to the user.
6112 *
6113 **/
6114 static char *
caja_file_get_octal_permissions_as_string(CajaFile * file)6115 caja_file_get_octal_permissions_as_string (CajaFile *file)
6116 {
6117 guint32 permissions;
6118
6119 g_assert (CAJA_IS_FILE (file));
6120
6121 if (!caja_file_can_get_permissions (file)) {
6122 return NULL;
6123 }
6124
6125 permissions = file->details->permissions;
6126 return g_strdup_printf ("%03o", permissions);
6127 }
6128
6129 /**
6130 * caja_file_get_permissions_as_string:
6131 *
6132 * Get a user-displayable string representing a file's permissions. The caller
6133 * is responsible for g_free-ing this string.
6134 * @file: CajaFile representing the file in question.
6135 *
6136 * Returns: Newly allocated string ready to display to the user.
6137 *
6138 **/
6139 static char *
caja_file_get_permissions_as_string(CajaFile * file)6140 caja_file_get_permissions_as_string (CajaFile *file)
6141 {
6142 guint32 permissions;
6143 gboolean is_directory;
6144 gboolean is_link;
6145 gboolean suid, sgid, sticky;
6146
6147 if (!caja_file_can_get_permissions (file)) {
6148 return NULL;
6149 }
6150
6151 g_assert (CAJA_IS_FILE (file));
6152
6153 permissions = file->details->permissions;
6154 is_directory = caja_file_is_directory (file);
6155 is_link = caja_file_is_symbolic_link (file);
6156
6157 /* We use ls conventions for displaying these three obscure flags */
6158 suid = permissions & S_ISUID;
6159 sgid = permissions & S_ISGID;
6160 sticky = permissions & S_ISVTX;
6161
6162 return g_strdup_printf ("%c%c%c%c%c%c%c%c%c%c",
6163 is_link ? 'l' : is_directory ? 'd' : '-',
6164 permissions & S_IRUSR ? 'r' : '-',
6165 permissions & S_IWUSR ? 'w' : '-',
6166 permissions & S_IXUSR
6167 ? (suid ? 's' : 'x')
6168 : (suid ? 'S' : '-'),
6169 permissions & S_IRGRP ? 'r' : '-',
6170 permissions & S_IWGRP ? 'w' : '-',
6171 permissions & S_IXGRP
6172 ? (sgid ? 's' : 'x')
6173 : (sgid ? 'S' : '-'),
6174 permissions & S_IROTH ? 'r' : '-',
6175 permissions & S_IWOTH ? 'w' : '-',
6176 permissions & S_IXOTH
6177 ? (sticky ? 't' : 'x')
6178 : (sticky ? 'T' : '-'));
6179 }
6180
6181 /**
6182 * caja_file_get_owner_as_string:
6183 *
6184 * Get a user-displayable string representing a file's owner. The caller
6185 * is responsible for g_free-ing this string.
6186 * @file: CajaFile representing the file in question.
6187 * @include_real_name: Whether or not to append the real name (if any)
6188 * for this user after the user name.
6189 *
6190 * Returns: Newly allocated string ready to display to the user.
6191 *
6192 **/
6193 static char *
caja_file_get_owner_as_string(CajaFile * file,gboolean include_real_name)6194 caja_file_get_owner_as_string (CajaFile *file, gboolean include_real_name)
6195 {
6196 char *user_name;
6197
6198 /* Before we have info on a file, the owner is unknown. */
6199 if (file->details->owner == NULL &&
6200 file->details->owner_real == NULL) {
6201 return NULL;
6202 }
6203
6204 if (file->details->owner_real == NULL) {
6205 user_name = g_strdup (file->details->owner);
6206 } else if (file->details->owner == NULL) {
6207 user_name = g_strdup (file->details->owner_real);
6208 } else if (include_real_name &&
6209 strcmp (file->details->owner, file->details->owner_real) != 0) {
6210 user_name = g_strdup_printf ("%s - %s",
6211 file->details->owner,
6212 file->details->owner_real);
6213 } else {
6214 user_name = g_strdup (file->details->owner);
6215 }
6216
6217 return user_name;
6218 }
6219
6220 static char *
format_item_count_for_display(guint item_count,gboolean includes_directories,gboolean includes_files)6221 format_item_count_for_display (guint item_count,
6222 gboolean includes_directories,
6223 gboolean includes_files)
6224 {
6225 g_assert (includes_directories || includes_files);
6226
6227 return g_strdup_printf (includes_directories
6228 ? (includes_files
6229 ? ngettext ("%'u item", "%'u items", item_count)
6230 : ngettext ("%'u folder", "%'u folders", item_count))
6231 : ngettext ("%'u file", "%'u files", item_count), item_count);
6232 }
6233
6234 /**
6235 * caja_file_get_size_as_string:
6236 *
6237 * Get a user-displayable string representing a file size. The caller
6238 * is responsible for g_free-ing this string. The string is an item
6239 * count for directories.
6240 * @file: CajaFile representing the file in question.
6241 * @size_on_disk: If TRUE will return the size on disk. If FALSE return file size.
6242 *
6243 * Returns: Newly allocated string ready to display to the user.
6244 *
6245 **/
6246 static char *
caja_file_get_size_as_string(CajaFile * file,gboolean size_on_disk)6247 caja_file_get_size_as_string (CajaFile *file,
6248 gboolean size_on_disk)
6249 {
6250 guint item_count;
6251 gboolean count_unreadable;
6252 goffset size;
6253
6254 if (file == NULL) {
6255 return NULL;
6256 }
6257
6258 g_assert (CAJA_IS_FILE (file));
6259
6260 if (caja_file_is_directory (file)) {
6261 if (!caja_file_get_directory_item_count (file, &item_count, &count_unreadable)) {
6262 return NULL;
6263 }
6264 return format_item_count_for_display (item_count, TRUE, TRUE);
6265 }
6266
6267 if (size_on_disk) {
6268 size = file->details->size_on_disk;
6269 } else {
6270 size = file->details->size;
6271 }
6272
6273 if (size == -1) {
6274 return NULL;
6275 }
6276
6277 if (g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_USE_IEC_UNITS))
6278 return g_format_size_full (size, G_FORMAT_SIZE_IEC_UNITS);
6279 else
6280 return g_format_size (size);
6281 }
6282
6283 /**
6284 * caja_file_get_size_as_string_with_real_size:
6285 *
6286 * Get a user-displayable string representing a file size. The caller
6287 * is responsible for g_free-ing this string. The string is an item
6288 * count for directories.
6289 * This function adds the real size in the string.
6290 * @file: CajaFile representing the file in question.
6291 *
6292 * Returns: Newly allocated string ready to display to the user.
6293 *
6294 **/
6295 static char *
caja_file_get_size_as_string_with_real_size(CajaFile * file,gboolean size_on_disk)6296 caja_file_get_size_as_string_with_real_size (CajaFile *file,
6297 gboolean size_on_disk)
6298 {
6299 guint item_count;
6300 gboolean count_unreadable;
6301 goffset size;
6302 char * formatted;
6303 char * formatted_plus_real;
6304 char * real_size;
6305
6306 if (file == NULL) {
6307 return NULL;
6308 }
6309
6310 g_assert (CAJA_IS_FILE (file));
6311
6312 if (caja_file_is_directory (file)) {
6313 if (!caja_file_get_directory_item_count (file, &item_count, &count_unreadable)) {
6314 return NULL;
6315 }
6316 return format_item_count_for_display (item_count, TRUE, TRUE);
6317 }
6318
6319 if (size_on_disk) {
6320 size = file->details->size_on_disk;
6321 } else {
6322 size = file->details->size;
6323 }
6324
6325 if (size == -1) {
6326 return NULL;
6327 }
6328
6329 if (g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_USE_IEC_UNITS))
6330 formatted = g_format_size_full (size, G_FORMAT_SIZE_IEC_UNITS);
6331 else
6332 formatted = g_format_size(size);
6333
6334 /* Do this in a separate stage so that we don't have to put G_GUINT64_FORMAT in the translated string */
6335 real_size = g_strdup_printf (_("%"G_GUINT64_FORMAT), (guint64) size);
6336 formatted_plus_real = g_strdup_printf (_("%s (%s bytes)"), formatted, real_size);
6337 g_free (real_size);
6338 g_free (formatted);
6339 return formatted_plus_real;
6340 }
6341
6342 static char *
caja_file_get_deep_count_as_string_internal(CajaFile * file,gboolean report_size,gboolean report_size_on_disk,gboolean report_directory_count,gboolean report_file_count)6343 caja_file_get_deep_count_as_string_internal (CajaFile *file,
6344 gboolean report_size,
6345 gboolean report_size_on_disk,
6346 gboolean report_directory_count,
6347 gboolean report_file_count)
6348 {
6349 CajaRequestStatus status;
6350 guint directory_count;
6351 guint file_count;
6352 guint unreadable_count;
6353 guint total_count;
6354 goffset total_size;
6355 goffset total_size_on_disk;
6356
6357 /* Can't ask for more than one of those: size, size on disk or (directory and/or file count) */
6358 g_assert (!(report_size && report_size_on_disk));
6359 g_assert (!(report_size && (report_directory_count || report_file_count)));
6360 g_assert (!(report_size_on_disk && (report_directory_count || report_file_count)));
6361
6362 /* Must ask for something */
6363 g_assert (report_size || report_size_on_disk || report_directory_count || report_file_count);
6364
6365 if (file == NULL) {
6366 return NULL;
6367 }
6368
6369 g_assert (CAJA_IS_FILE (file));
6370 g_assert (caja_file_is_directory (file));
6371
6372 status = caja_file_get_deep_counts (file,
6373 &directory_count,
6374 &file_count,
6375 &unreadable_count,
6376 &total_size,
6377 &total_size_on_disk,
6378 FALSE);
6379
6380 /* Check whether any info is available. */
6381 if (status == CAJA_REQUEST_NOT_STARTED) {
6382 return NULL;
6383 }
6384
6385 total_count = file_count + directory_count;
6386
6387 if (total_count == 0) {
6388 switch (status) {
6389 case CAJA_REQUEST_IN_PROGRESS:
6390 /* Don't return confident "zero" until we're finished looking,
6391 * because of next case.
6392 */
6393 return NULL;
6394 case CAJA_REQUEST_DONE:
6395 /* Don't return "zero" if we there were contents but we couldn't read them. */
6396 if (unreadable_count != 0) {
6397 return NULL;
6398 }
6399 default: break;
6400 }
6401 }
6402
6403 /* Note that we don't distinguish the "everything was readable" case
6404 * from the "some things but not everything was readable" case here.
6405 * Callers can distinguish them using caja_file_get_deep_counts
6406 * directly if desired.
6407 */
6408 if (report_size)
6409 {
6410 if (g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_USE_IEC_UNITS))
6411 return g_format_size_full (total_size, G_FORMAT_SIZE_IEC_UNITS);
6412 else
6413 return g_format_size(total_size);
6414 }
6415
6416 if (report_size_on_disk)
6417 {
6418 if (g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_USE_IEC_UNITS))
6419 return g_format_size_full (total_size_on_disk, G_FORMAT_SIZE_IEC_UNITS);
6420 else
6421 return g_format_size (total_size_on_disk);
6422 }
6423
6424 return format_item_count_for_display (report_directory_count
6425 ? (report_file_count ? total_count : directory_count)
6426 : file_count,
6427 report_directory_count, report_file_count);
6428 }
6429
6430 /**
6431 * caja_file_get_deep_size_as_string:
6432 *
6433 * Get a user-displayable string representing the size of all contained
6434 * items (only makes sense for directories). The caller
6435 * is responsible for g_free-ing this string.
6436 * @file: CajaFile representing the file in question.
6437 * @size_on_disk: if TRUE will return the size on disk, else return size of file.
6438 *
6439 * Returns: Newly allocated string ready to display to the user.
6440 *
6441 **/
6442 static char *
caja_file_get_deep_size_as_string(CajaFile * file,gboolean size_on_disk)6443 caja_file_get_deep_size_as_string (CajaFile *file, gboolean size_on_disk)
6444 {
6445 if (size_on_disk) {
6446 return caja_file_get_deep_count_as_string_internal (file, FALSE, TRUE, FALSE, FALSE);
6447 } else {
6448 return caja_file_get_deep_count_as_string_internal (file, TRUE, FALSE, FALSE, FALSE);
6449 }
6450 }
6451
6452 /**
6453 * caja_file_get_deep_total_count_as_string:
6454 *
6455 * Get a user-displayable string representing the count of all contained
6456 * items (only makes sense for directories). The caller
6457 * is responsible for g_free-ing this string.
6458 * @file: CajaFile representing the file in question.
6459 *
6460 * Returns: Newly allocated string ready to display to the user.
6461 *
6462 **/
6463 static char *
caja_file_get_deep_total_count_as_string(CajaFile * file)6464 caja_file_get_deep_total_count_as_string (CajaFile *file)
6465 {
6466 return caja_file_get_deep_count_as_string_internal (file, FALSE, FALSE, TRUE, TRUE);
6467 }
6468
6469 /**
6470 * caja_file_get_deep_file_count_as_string:
6471 *
6472 * Get a user-displayable string representing the count of all contained
6473 * items, not including directories. It only makes sense to call this
6474 * function on a directory. The caller
6475 * is responsible for g_free-ing this string.
6476 * @file: CajaFile representing the file in question.
6477 *
6478 * Returns: Newly allocated string ready to display to the user.
6479 *
6480 **/
6481 static char *
caja_file_get_deep_file_count_as_string(CajaFile * file)6482 caja_file_get_deep_file_count_as_string (CajaFile *file)
6483 {
6484 return caja_file_get_deep_count_as_string_internal (file, FALSE, FALSE, FALSE, TRUE);
6485 }
6486
6487 /**
6488 * caja_file_get_deep_directory_count_as_string:
6489 *
6490 * Get a user-displayable string representing the count of all contained
6491 * directories. It only makes sense to call this
6492 * function on a directory. The caller
6493 * is responsible for g_free-ing this string.
6494 * @file: CajaFile representing the file in question.
6495 *
6496 * Returns: Newly allocated string ready to display to the user.
6497 *
6498 **/
6499 static char *
caja_file_get_deep_directory_count_as_string(CajaFile * file)6500 caja_file_get_deep_directory_count_as_string (CajaFile *file)
6501 {
6502 return caja_file_get_deep_count_as_string_internal (file, FALSE, FALSE, TRUE, FALSE);
6503 }
6504
6505 /**
6506 * caja_file_get_string_attribute:
6507 *
6508 * Get a user-displayable string from a named attribute. Use g_free to
6509 * free this string. If the value is unknown, returns NULL. You can call
6510 * caja_file_get_string_attribute_with_default if you want a non-NULL
6511 * default.
6512 *
6513 * @file: CajaFile representing the file in question.
6514 * @attribute_name: The name of the desired attribute. The currently supported
6515 * set includes "name", "type", "mime_type", "size", "size_on_disk", "deep_size", "deep_size_on_disk",
6516 * "deep_directory_count", "deep_file_count", "deep_total_count", "date_modified", "date_changed",
6517 * "date_accessed", "date_permissions", "owner", "group", "permissions", "octal_permissions", "uri", "where",
6518 * "link_target", "volume", "free_space", "selinux_context", "trashed_on", "trashed_orig_path"
6519 *
6520 * Returns: Newly allocated string ready to display to the user, or NULL
6521 * if the value is unknown or @attribute_name is not supported.
6522 *
6523 **/
6524 char *
caja_file_get_string_attribute_q(CajaFile * file,GQuark attribute_q)6525 caja_file_get_string_attribute_q (CajaFile *file, GQuark attribute_q)
6526 {
6527 char *extension_attribute;
6528
6529 if (attribute_q == attribute_name_q) {
6530 return caja_file_get_display_name (file);
6531 }
6532 if (attribute_q == attribute_type_q) {
6533 return caja_file_get_type_as_string (file);
6534 }
6535 if (attribute_q == attribute_mime_type_q) {
6536 return caja_file_get_mime_type (file);
6537 }
6538 if (attribute_q == attribute_size_q) {
6539 return caja_file_get_size_as_string (file, FALSE);
6540 }
6541 if (attribute_q == attribute_size_on_disk_q) {
6542 return caja_file_get_size_as_string (file, TRUE);
6543 }
6544 if (attribute_q == attribute_size_detail_q) {
6545 return caja_file_get_size_as_string_with_real_size (file, FALSE);
6546 }
6547 if (attribute_q == attribute_size_on_disk_detail_q) {
6548 return caja_file_get_size_as_string_with_real_size (file, TRUE);
6549 }
6550 if (attribute_q == attribute_deep_size_q) {
6551 return caja_file_get_deep_size_as_string (file, FALSE);
6552 }
6553 if (attribute_q == attribute_deep_size_on_disk_q) {
6554 return caja_file_get_deep_size_as_string (file, TRUE);
6555 }
6556 if (attribute_q == attribute_deep_file_count_q) {
6557 return caja_file_get_deep_file_count_as_string (file);
6558 }
6559 if (attribute_q == attribute_deep_directory_count_q) {
6560 return caja_file_get_deep_directory_count_as_string (file);
6561 }
6562 if (attribute_q == attribute_deep_total_count_q) {
6563 return caja_file_get_deep_total_count_as_string (file);
6564 }
6565 if (attribute_q == attribute_trash_orig_path_q) {
6566 return caja_file_get_trash_original_file_parent_as_string (file);
6567 }
6568 if (attribute_q == attribute_date_modified_q) {
6569 return caja_file_get_date_as_string (file,
6570 CAJA_DATE_TYPE_MODIFIED);
6571 }
6572 if (attribute_q == attribute_date_changed_q) {
6573 return caja_file_get_date_as_string (file,
6574 CAJA_DATE_TYPE_CHANGED);
6575 }
6576 if (attribute_q == attribute_date_accessed_q) {
6577 return caja_file_get_date_as_string (file,
6578 CAJA_DATE_TYPE_ACCESSED);
6579 }
6580 if (attribute_q == attribute_date_created_q) {
6581 return caja_file_get_date_as_string (file,
6582 CAJA_DATE_TYPE_CREATED);
6583 }
6584 if (attribute_q == attribute_trashed_on_q) {
6585 return caja_file_get_date_as_string (file,
6586 CAJA_DATE_TYPE_TRASHED);
6587 }
6588 if (attribute_q == attribute_date_permissions_q) {
6589 return caja_file_get_date_as_string (file,
6590 CAJA_DATE_TYPE_PERMISSIONS_CHANGED);
6591 }
6592 if (attribute_q == attribute_extension_q) {
6593 return caja_file_get_extension_as_string (file);
6594 }
6595 if (attribute_q == attribute_permissions_q) {
6596 return caja_file_get_permissions_as_string (file);
6597 }
6598 if (attribute_q == attribute_selinux_context_q) {
6599 return caja_file_get_selinux_context (file);
6600 }
6601 if (attribute_q == attribute_octal_permissions_q) {
6602 return caja_file_get_octal_permissions_as_string (file);
6603 }
6604 if (attribute_q == attribute_owner_q) {
6605 return caja_file_get_owner_as_string (file, TRUE);
6606 }
6607 if (attribute_q == attribute_group_q) {
6608 return caja_file_get_group_name (file);
6609 }
6610 if (attribute_q == attribute_uri_q) {
6611 return caja_file_get_uri (file);
6612 }
6613 if (attribute_q == attribute_where_q) {
6614 return caja_file_get_where_string (file);
6615 }
6616 if (attribute_q == attribute_link_target_q) {
6617 return caja_file_get_symbolic_link_target_path (file);
6618 }
6619 if (attribute_q == attribute_volume_q) {
6620 return caja_file_get_volume_name (file);
6621 }
6622 if (attribute_q == attribute_free_space_q) {
6623 return caja_file_get_volume_free_space (file);
6624 }
6625
6626 extension_attribute = NULL;
6627
6628 if (file->details->pending_extension_attributes) {
6629 extension_attribute = g_hash_table_lookup (file->details->pending_extension_attributes,
6630 GINT_TO_POINTER (attribute_q));
6631 }
6632
6633 if (extension_attribute == NULL && file->details->extension_attributes) {
6634 extension_attribute = g_hash_table_lookup (file->details->extension_attributes,
6635 GINT_TO_POINTER (attribute_q));
6636 }
6637
6638 return g_strdup (extension_attribute);
6639 }
6640
6641 char *
caja_file_get_string_attribute(CajaFile * file,const char * attribute_name)6642 caja_file_get_string_attribute (CajaFile *file, const char *attribute_name)
6643 {
6644 return caja_file_get_string_attribute_q (file, g_quark_from_string (attribute_name));
6645 }
6646
6647
6648 /**
6649 * caja_file_get_string_attribute_with_default:
6650 *
6651 * Get a user-displayable string from a named attribute. Use g_free to
6652 * free this string. If the value is unknown, returns a string representing
6653 * the unknown value, which varies with attribute. You can call
6654 * caja_file_get_string_attribute if you want NULL instead of a default
6655 * result.
6656 *
6657 * @file: CajaFile representing the file in question.
6658 * @attribute_name: The name of the desired attribute. See the description of
6659 * caja_file_get_string for the set of available attributes.
6660 *
6661 * Returns: Newly allocated string ready to display to the user, or a string
6662 * such as "unknown" if the value is unknown or @attribute_name is not supported.
6663 *
6664 **/
6665 char *
caja_file_get_string_attribute_with_default_q(CajaFile * file,GQuark attribute_q)6666 caja_file_get_string_attribute_with_default_q (CajaFile *file, GQuark attribute_q)
6667 {
6668 char *result;
6669 guint item_count;
6670 gboolean count_unreadable;
6671 CajaRequestStatus status;
6672
6673 result = caja_file_get_string_attribute_q (file, attribute_q);
6674 if (result != NULL) {
6675 return result;
6676 }
6677
6678 /* Supply default values for the ones we know about. */
6679 /* FIXME bugzilla.gnome.org 40646:
6680 * Use hash table and switch statement or function pointers for speed?
6681 */
6682 if (attribute_q == attribute_size_q) {
6683 if (!caja_file_should_show_directory_item_count (file)) {
6684 return g_strdup ("--");
6685 }
6686 count_unreadable = FALSE;
6687 if (caja_file_is_directory (file)) {
6688 caja_file_get_directory_item_count (file, &item_count, &count_unreadable);
6689 }
6690 return g_strdup (count_unreadable ? _("? items") : "...");
6691 }
6692 if (attribute_q == attribute_deep_size_q) {
6693 status = caja_file_get_deep_counts (file, NULL, NULL, NULL, NULL, NULL, FALSE);
6694 if (status == CAJA_REQUEST_DONE) {
6695 /* This means no contents at all were readable */
6696 return g_strdup (_("? bytes"));
6697 }
6698 return g_strdup ("...");
6699 }
6700 if (attribute_q == attribute_deep_size_on_disk_q) {
6701 status = caja_file_get_deep_counts (file, NULL, NULL, NULL, NULL, NULL, FALSE);
6702 if (status == CAJA_REQUEST_DONE) {
6703 /* This means no contents at all were readable */
6704 return g_strdup (_("? bytes"));
6705 }
6706 return g_strdup ("...");
6707 }
6708 if (attribute_q == attribute_deep_file_count_q
6709 || attribute_q == attribute_deep_directory_count_q
6710 || attribute_q == attribute_deep_total_count_q) {
6711 status = caja_file_get_deep_counts (file, NULL, NULL, NULL, NULL, NULL, FALSE);
6712 if (status == CAJA_REQUEST_DONE) {
6713 /* This means no contents at all were readable */
6714 return g_strdup (_("? items"));
6715 }
6716 return g_strdup ("...");
6717 }
6718 if (attribute_q == attribute_type_q) {
6719 return g_strdup (_("unknown type"));
6720 }
6721 if (attribute_q == attribute_mime_type_q) {
6722 return g_strdup (_("unknown MIME type"));
6723 }
6724 if (attribute_q == attribute_trashed_on_q) {
6725 /* If n/a */
6726 return g_strdup ("");
6727 }
6728 if (attribute_q == attribute_trash_orig_path_q) {
6729 /* If n/a */
6730 return g_strdup ("");
6731 }
6732
6733 /* Fallback, use for both unknown attributes and attributes
6734 * for which we have no more appropriate default.
6735 */
6736 return g_strdup (_("unknown"));
6737 }
6738
6739 char *
caja_file_get_string_attribute_with_default(CajaFile * file,const char * attribute_name)6740 caja_file_get_string_attribute_with_default (CajaFile *file, const char *attribute_name)
6741 {
6742 return caja_file_get_string_attribute_with_default_q (file, g_quark_from_string (attribute_name));
6743 }
6744
6745 gboolean
caja_file_is_date_sort_attribute_q(GQuark attribute_q)6746 caja_file_is_date_sort_attribute_q (GQuark attribute_q)
6747 {
6748 if (attribute_q == attribute_modification_date_q ||
6749 attribute_q == attribute_date_modified_q ||
6750 attribute_q == attribute_creation_date_q ||
6751 attribute_q == attribute_date_created_q ||
6752 attribute_q == attribute_accessed_date_q ||
6753 attribute_q == attribute_date_accessed_q ||
6754 attribute_q == attribute_date_changed_q ||
6755 attribute_q == attribute_trashed_on_q ||
6756 attribute_q == attribute_date_permissions_q) {
6757 return TRUE;
6758 }
6759
6760 return FALSE;
6761 }
6762
6763 /**
6764 * get_description:
6765 *
6766 * Get a user-displayable string representing a file type. The caller
6767 * is responsible for g_free-ing this string.
6768 * @file: CajaFile representing the file in question.
6769 *
6770 * Returns: Newly allocated string ready to display to the user.
6771 *
6772 **/
6773 static char *
get_description(CajaFile * file)6774 get_description (CajaFile *file)
6775 {
6776 const char *mime_type;
6777 char *description;
6778
6779 g_assert (CAJA_IS_FILE (file));
6780
6781 mime_type = file->details->mime_type;
6782 if (eel_str_is_empty (mime_type)) {
6783 return NULL;
6784 }
6785
6786 if (g_content_type_is_unknown (mime_type) &&
6787 caja_file_is_executable (file)) {
6788 return g_strdup (_("program"));
6789 }
6790
6791 description = g_content_type_get_description (mime_type);
6792 if (!eel_str_is_empty (description)) {
6793 return description;
6794 }
6795
6796 return g_strdup (mime_type);
6797 }
6798
6799 /* Takes ownership of string */
6800 static char *
update_description_for_link(CajaFile * file,char * string)6801 update_description_for_link (CajaFile *file, char *string)
6802 {
6803 if (caja_file_is_symbolic_link (file)) {
6804 char *res;
6805
6806 g_assert (!caja_file_is_broken_symbolic_link (file));
6807 if (string == NULL) {
6808 return g_strdup (_("link"));
6809 }
6810 /* Note to localizers: convert file type string for file
6811 * (e.g. "folder", "plain text") to file type for symbolic link
6812 * to that kind of file (e.g. "link to folder").
6813 */
6814 res = g_strdup_printf (_("Link to %s"), string);
6815 g_free (string);
6816 return res;
6817 }
6818
6819 return string;
6820 }
6821
6822 static char *
caja_file_get_type_as_string(CajaFile * file)6823 caja_file_get_type_as_string (CajaFile *file)
6824 {
6825 if (file == NULL) {
6826 return NULL;
6827 }
6828
6829 if (caja_file_is_broken_symbolic_link (file)) {
6830 return g_strdup (_("link (broken)"));
6831 }
6832
6833 return update_description_for_link (file, get_description (file));
6834 }
6835
6836 /**
6837 * caja_file_get_file_type
6838 *
6839 * Return this file's type.
6840 * @file: CajaFile representing the file in question.
6841 *
6842 * Returns: The type.
6843 *
6844 **/
6845 GFileType
caja_file_get_file_type(CajaFile * file)6846 caja_file_get_file_type (CajaFile *file)
6847 {
6848 if (file == NULL) {
6849 return G_FILE_TYPE_UNKNOWN;
6850 }
6851
6852 return file->details->type;
6853 }
6854
6855 /**
6856 * caja_file_get_mime_type
6857 *
6858 * Return this file's default mime type.
6859 * @file: CajaFile representing the file in question.
6860 *
6861 * Returns: The mime type.
6862 *
6863 **/
6864 char *
caja_file_get_mime_type(CajaFile * file)6865 caja_file_get_mime_type (CajaFile *file)
6866 {
6867 if (file != NULL) {
6868 g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
6869 if (file->details->mime_type != NULL) {
6870 return g_strdup (file->details->mime_type);
6871 }
6872 }
6873 return g_strdup ("application/octet-stream");
6874 }
6875
6876 /**
6877 * caja_file_is_mime_type
6878 *
6879 * Check whether a file is of a particular MIME type, or inherited
6880 * from it.
6881 * @file: CajaFile representing the file in question.
6882 * @mime_type: The MIME-type string to test (e.g. "text/plain")
6883 *
6884 * Return value: TRUE if @mime_type exactly matches the
6885 * file's MIME type.
6886 *
6887 **/
6888 gboolean
caja_file_is_mime_type(CajaFile * file,const char * mime_type)6889 caja_file_is_mime_type (CajaFile *file, const char *mime_type)
6890 {
6891 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
6892 g_return_val_if_fail (mime_type != NULL, FALSE);
6893
6894 if (file->details->mime_type == NULL) {
6895 return FALSE;
6896 }
6897 return g_content_type_is_a (file->details->mime_type,
6898 mime_type);
6899 }
6900
6901 gboolean
caja_file_is_launchable(CajaFile * file)6902 caja_file_is_launchable (CajaFile *file)
6903 {
6904 gboolean type_can_be_executable;
6905
6906 type_can_be_executable = FALSE;
6907 if (file->details->mime_type != NULL) {
6908 type_can_be_executable =
6909 g_content_type_can_be_executable (file->details->mime_type);
6910 }
6911
6912 return type_can_be_executable &&
6913 caja_file_can_get_permissions (file) &&
6914 caja_file_can_execute (file) &&
6915 caja_file_is_executable (file) &&
6916 !caja_file_is_directory (file);
6917 }
6918
6919
6920 /**
6921 * caja_file_get_emblem_icons
6922 *
6923 * Return the list of names of emblems that this file should display,
6924 * in canonical order.
6925 * @file: CajaFile representing the file in question.
6926 *
6927 * Returns: A list of emblem names.
6928 *
6929 **/
6930 GList *
caja_file_get_emblem_icons(CajaFile * file,char ** exclude)6931 caja_file_get_emblem_icons (CajaFile *file,
6932 char **exclude)
6933 {
6934 GList *keywords, *l;
6935 GList *icons;
6936 char *icon_names[2];
6937 char *keyword;
6938 int i;
6939 GIcon *icon;
6940
6941 if (file == NULL) {
6942 return NULL;
6943 }
6944
6945 g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
6946
6947 keywords = caja_file_get_keywords (file);
6948 keywords = prepend_automatic_keywords (file, keywords);
6949
6950 icons = NULL;
6951 for (l = keywords; l != NULL; l = l->next) {
6952 keyword = l->data;
6953
6954 #ifdef TRASH_IS_FAST_ENOUGH
6955 if (strcmp (keyword, CAJA_FILE_EMBLEM_NAME_TRASH) == 0) {
6956 char *uri;
6957 gboolean file_is_trash;
6958 /* Leave out the trash emblem for the trash itself, since
6959 * putting a trash emblem on a trash icon is gilding the
6960 * lily.
6961 */
6962 uri = caja_file_get_uri (file);
6963 file_is_trash = strcmp (uri, EEL_TRASH_URI) == 0;
6964 g_free (uri);
6965 if (file_is_trash) {
6966 continue;
6967 }
6968 }
6969 #endif
6970 if (exclude) {
6971 for (i = 0; exclude[i] != NULL; i++) {
6972 if (strcmp (exclude[i], keyword) == 0) {
6973 continue;
6974 }
6975 }
6976 }
6977
6978
6979 icon_names[0] = g_strconcat ("emblem-", keyword, NULL);
6980 icon_names[1] = keyword;
6981 icon = g_themed_icon_new_from_names (icon_names, 2);
6982 g_free (icon_names[0]);
6983
6984 icons = g_list_prepend (icons, icon);
6985 }
6986
6987 g_list_free_full (keywords, g_free);
6988
6989 return icons;
6990 }
6991
6992 GList *
caja_file_get_emblem_pixbufs(CajaFile * file,int size,gboolean force_size,char ** exclude)6993 caja_file_get_emblem_pixbufs (CajaFile *file,
6994 int size,
6995 gboolean force_size,
6996 char **exclude)
6997 {
6998 GList *icons, *l;
6999 GList *pixbufs;
7000 GdkPixbuf *pixbuf;
7001 GIcon *icon = NULL;
7002 CajaIconInfo *icon_info = NULL;
7003
7004 icons = caja_file_get_emblem_icons (file, exclude);
7005 pixbufs = NULL;
7006
7007 for (l = icons; l != NULL; l = l->next) {
7008 icon = l->data;
7009
7010 icon_info = caja_icon_info_lookup (icon, size, 1);
7011 if (force_size) {
7012 pixbuf = caja_icon_info_get_pixbuf_nodefault_at_size (icon_info, size);
7013 } else {
7014 pixbuf = caja_icon_info_get_pixbuf_nodefault (icon_info);
7015 }
7016
7017 if (pixbuf) {
7018 pixbufs = g_list_prepend (pixbufs, pixbuf);
7019 }
7020
7021
7022 g_object_unref (icon_info);
7023 g_object_unref (icon);
7024 }
7025 g_list_free (icons);
7026
7027 return g_list_reverse (pixbufs);
7028
7029
7030 }
7031
7032 static GList *
sort_keyword_list_and_remove_duplicates(GList * keywords)7033 sort_keyword_list_and_remove_duplicates (GList *keywords)
7034 {
7035 GList *p;
7036
7037 if (keywords != NULL) {
7038 GList *duplicate_link = NULL;
7039
7040 keywords = eel_g_str_list_alphabetize (keywords);
7041
7042 p = keywords;
7043 while (p->next != NULL) {
7044 if (strcmp ((const char *) p->data, (const char *) p->next->data) == 0) {
7045 duplicate_link = p->next;
7046 keywords = g_list_remove_link (keywords, duplicate_link);
7047 g_list_free_full (duplicate_link, g_free);
7048 } else {
7049 p = p->next;
7050 }
7051 }
7052 }
7053
7054 return keywords;
7055 }
7056
7057 /**
7058 * caja_file_get_keywords
7059 *
7060 * Return this file's keywords.
7061 * @file: CajaFile representing the file in question.
7062 *
7063 * Returns: A list of keywords.
7064 *
7065 **/
7066 GList *
caja_file_get_keywords(CajaFile * file)7067 caja_file_get_keywords (CajaFile *file)
7068 {
7069 GList *keywords;
7070
7071 if (file == NULL) {
7072 return NULL;
7073 }
7074
7075 g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
7076
7077 /* Put all the keywords into a list. */
7078 keywords = caja_file_get_metadata_list
7079 (file, CAJA_METADATA_KEY_EMBLEMS);
7080
7081 keywords = g_list_concat (keywords, g_list_copy_deep (file->details->extension_emblems, (GCopyFunc) g_strdup, NULL));
7082 keywords = g_list_concat (keywords, g_list_copy_deep (file->details->pending_extension_emblems, (GCopyFunc) g_strdup, NULL));
7083
7084 return sort_keyword_list_and_remove_duplicates (keywords);
7085 }
7086
7087 /**
7088 * caja_file_set_keywords
7089 *
7090 * Change this file's keywords.
7091 * @file: CajaFile representing the file in question.
7092 * @keywords: New set of keywords (a GList of strings).
7093 *
7094 **/
7095 void
caja_file_set_keywords(CajaFile * file,GList * keywords)7096 caja_file_set_keywords (CajaFile *file, GList *keywords)
7097 {
7098 GList *canonical_keywords;
7099
7100 /* Invalidate the emblem compare cache */
7101 g_free (file->details->compare_by_emblem_cache);
7102 file->details->compare_by_emblem_cache = NULL;
7103
7104 g_return_if_fail (CAJA_IS_FILE (file));
7105
7106 canonical_keywords = sort_keyword_list_and_remove_duplicates
7107 (g_list_copy (keywords));
7108 caja_file_set_metadata_list
7109 (file, CAJA_METADATA_KEY_EMBLEMS, canonical_keywords);
7110 g_list_free (canonical_keywords);
7111 }
7112
7113 /**
7114 * caja_file_is_symbolic_link
7115 *
7116 * Check if this file is a symbolic link.
7117 * @file: CajaFile representing the file in question.
7118 *
7119 * Returns: True if the file is a symbolic link.
7120 *
7121 **/
7122 gboolean
caja_file_is_symbolic_link(CajaFile * file)7123 caja_file_is_symbolic_link (CajaFile *file)
7124 {
7125 return file->details->is_symlink;
7126 }
7127
7128 gboolean
caja_file_is_mountpoint(CajaFile * file)7129 caja_file_is_mountpoint (CajaFile *file)
7130 {
7131 return file->details->is_mountpoint;
7132 }
7133
7134 GMount *
caja_file_get_mount(CajaFile * file)7135 caja_file_get_mount (CajaFile *file)
7136 {
7137 if (file->details->mount) {
7138 return g_object_ref (file->details->mount);
7139 }
7140 return NULL;
7141 }
7142
7143 static void
file_mount_unmounted(GMount * mount,gpointer data)7144 file_mount_unmounted (GMount *mount,
7145 gpointer data)
7146 {
7147 CajaFile *file;
7148
7149 file = CAJA_FILE (data);
7150
7151 caja_file_invalidate_attributes (file, CAJA_FILE_ATTRIBUTE_MOUNT);
7152 }
7153
7154 void
caja_file_set_mount(CajaFile * file,GMount * mount)7155 caja_file_set_mount (CajaFile *file,
7156 GMount *mount)
7157 {
7158 if (file->details->mount) {
7159 g_signal_handlers_disconnect_by_func (file->details->mount, file_mount_unmounted, file);
7160 g_object_unref (file->details->mount);
7161 file->details->mount = NULL;
7162 }
7163
7164 if (mount) {
7165 file->details->mount = g_object_ref (mount);
7166 g_signal_connect (mount, "unmounted",
7167 G_CALLBACK (file_mount_unmounted), file);
7168 }
7169 }
7170
7171 /**
7172 * caja_file_is_broken_symbolic_link
7173 *
7174 * Check if this file is a symbolic link with a missing target.
7175 * @file: CajaFile representing the file in question.
7176 *
7177 * Returns: True if the file is a symbolic link with a missing target.
7178 *
7179 **/
7180 gboolean
caja_file_is_broken_symbolic_link(CajaFile * file)7181 caja_file_is_broken_symbolic_link (CajaFile *file)
7182 {
7183 if (file == NULL) {
7184 return FALSE;
7185 }
7186
7187 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
7188
7189 /* Non-broken symbolic links return the target's type for get_file_type. */
7190 return caja_file_get_file_type (file) == G_FILE_TYPE_SYMBOLIC_LINK;
7191 }
7192
7193 static void
get_fs_free_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)7194 get_fs_free_cb (GObject *source_object,
7195 GAsyncResult *res,
7196 gpointer user_data)
7197 {
7198 CajaDirectory *directory;
7199 guint64 free_space;
7200 GFileInfo *info;
7201
7202 directory = CAJA_DIRECTORY (user_data);
7203
7204 free_space = (guint64)-1;
7205 info = g_file_query_filesystem_info_finish (G_FILE (source_object),
7206 res, NULL);
7207 if (info) {
7208 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE)) {
7209 free_space = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
7210 }
7211 g_object_unref (info);
7212 }
7213
7214 if (directory->details->free_space != free_space) {
7215 CajaFile *file;
7216
7217 directory->details->free_space = free_space;
7218 file = caja_directory_get_existing_corresponding_file (directory);
7219 if (file) {
7220 caja_file_emit_changed (file);
7221 caja_file_unref (file);
7222 }
7223 }
7224 caja_directory_unref (directory);
7225 }
7226
7227 /**
7228 * caja_file_get_volume_free_space
7229 * Get a nicely formatted char with free space on the file's volume
7230 * @file: CajaFile representing the file in question.
7231 *
7232 * Returns: newly-allocated copy of file size in a formatted string
7233 */
7234 char *
caja_file_get_volume_free_space(CajaFile * file)7235 caja_file_get_volume_free_space (CajaFile *file)
7236 {
7237 CajaDirectory *directory;
7238 char *res;
7239 time_t now;
7240
7241 directory = caja_directory_get_for_file (file);
7242
7243 now = time (NULL);
7244 /* Update first time and then every 2 seconds */
7245 if (directory->details->free_space_read == 0 ||
7246 (now - directory->details->free_space_read) > 2) {
7247 GFile *location;
7248
7249 directory->details->free_space_read = now;
7250 location = caja_file_get_location (file);
7251 g_file_query_filesystem_info_async (location,
7252 G_FILE_ATTRIBUTE_FILESYSTEM_FREE,
7253 0, NULL,
7254 get_fs_free_cb,
7255 directory); /* Inherits ref */
7256 g_object_unref (location);
7257 } else {
7258 caja_directory_unref (directory);
7259 }
7260
7261
7262 res = NULL;
7263
7264 if (directory->details->free_space != (guint64) -1)
7265 {
7266 if (g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_USE_IEC_UNITS))
7267 res = g_format_size_full (directory->details->free_space, G_FORMAT_SIZE_IEC_UNITS);
7268 else
7269 res = g_format_size(directory->details->free_space);
7270 }
7271
7272 return res;
7273 }
7274
7275 /**
7276 * caja_file_get_volume_name
7277 * Get the path of the volume the file resides on
7278 * @file: CajaFile representing the file in question.
7279 *
7280 * Returns: newly-allocated copy of the volume name of the target file,
7281 * if the volume name isn't set, it returns the mount path of the volume
7282 */
7283 char *
caja_file_get_volume_name(CajaFile * file)7284 caja_file_get_volume_name (CajaFile *file)
7285 {
7286 GFile *location;
7287 char *res;
7288 GMount *mount;
7289
7290 res = NULL;
7291
7292 location = caja_file_get_location (file);
7293 mount = g_file_find_enclosing_mount (location, NULL, NULL);
7294 if (mount) {
7295 res = g_strdup (g_mount_get_name (mount));
7296 g_object_unref (mount);
7297 }
7298 g_object_unref (location);
7299
7300 return res;
7301 }
7302
7303 /**
7304 * caja_file_get_symbolic_link_target_path
7305 *
7306 * Get the file path of the target of a symbolic link. It is an error
7307 * to call this function on a file that isn't a symbolic link.
7308 * @file: CajaFile representing the symbolic link in question.
7309 *
7310 * Returns: newly-allocated copy of the file path of the target of the symbolic link.
7311 */
7312 char *
caja_file_get_symbolic_link_target_path(CajaFile * file)7313 caja_file_get_symbolic_link_target_path (CajaFile *file)
7314 {
7315 if (!caja_file_is_symbolic_link (file)) {
7316 g_warning ("File has symlink target, but is not marked as symlink");
7317 }
7318
7319 return g_strdup (file->details->symlink_name);
7320 }
7321
7322 /**
7323 * caja_file_get_symbolic_link_target_uri
7324 *
7325 * Get the uri of the target of a symbolic link. It is an error
7326 * to call this function on a file that isn't a symbolic link.
7327 * @file: CajaFile representing the symbolic link in question.
7328 *
7329 * Returns: newly-allocated copy of the uri of the target of the symbolic link.
7330 */
7331 char *
caja_file_get_symbolic_link_target_uri(CajaFile * file)7332 caja_file_get_symbolic_link_target_uri (CajaFile *file)
7333 {
7334 if (!caja_file_is_symbolic_link (file)) {
7335 g_warning ("File has symlink target, but is not marked as symlink");
7336 }
7337
7338 if (file->details->symlink_name == NULL) {
7339 return NULL;
7340 } else {
7341 GFile *location, *parent, *target;
7342 char *target_uri;
7343
7344 target = NULL;
7345
7346 location = caja_file_get_location (file);
7347 parent = g_file_get_parent (location);
7348 g_object_unref (location);
7349 if (parent) {
7350 target = g_file_resolve_relative_path (parent, file->details->symlink_name);
7351 g_object_unref (parent);
7352 }
7353
7354 target_uri = NULL;
7355 if (target) {
7356 target_uri = g_file_get_uri (target);
7357 g_object_unref (target);
7358 }
7359 return target_uri;
7360 }
7361 }
7362
7363 /**
7364 * caja_file_is_caja_link
7365 *
7366 * Check if this file is a "caja link", meaning a historical
7367 * caja xml link file or a desktop file.
7368 * @file: CajaFile representing the file in question.
7369 *
7370 * Returns: True if the file is a caja link.
7371 *
7372 **/
7373 gboolean
caja_file_is_caja_link(CajaFile * file)7374 caja_file_is_caja_link (CajaFile *file)
7375 {
7376 if (file->details->mime_type == NULL)
7377 {
7378 return FALSE;
7379 }
7380 return g_content_type_equals (file->details->mime_type,
7381 "application/x-desktop");
7382 }
7383
7384 /**
7385 * caja_file_is_directory
7386 *
7387 * Check if this file is a directory.
7388 * @file: CajaFile representing the file in question.
7389 *
7390 * Returns: TRUE if @file is a directory.
7391 *
7392 **/
7393 gboolean
caja_file_is_directory(CajaFile * file)7394 caja_file_is_directory (CajaFile *file)
7395 {
7396 return caja_file_get_file_type (file) == G_FILE_TYPE_DIRECTORY;
7397 }
7398
7399 /**
7400 * caja_file_is_user_special_directory
7401 *
7402 * Check if this file is a special platform directory.
7403 * @file: CajaFile representing the file in question.
7404 * @special_directory: GUserDirectory representing the type to test for
7405 *
7406 * Returns: TRUE if @file is a special directory of the given kind.
7407 */
7408 gboolean
caja_file_is_user_special_directory(CajaFile * file,GUserDirectory special_directory)7409 caja_file_is_user_special_directory (CajaFile *file,
7410 GUserDirectory special_directory)
7411 {
7412 gboolean is_special_dir;
7413 const gchar *special_dir;
7414
7415 special_dir = g_get_user_special_dir (special_directory);
7416 is_special_dir = FALSE;
7417
7418 if (special_dir) {
7419 GFile *loc;
7420 GFile *special_gfile;
7421
7422 loc = caja_file_get_location (file);
7423 special_gfile = g_file_new_for_path (special_dir);
7424 is_special_dir = g_file_equal (loc, special_gfile);
7425 g_object_unref (special_gfile);
7426 g_object_unref (loc);
7427 }
7428
7429 return is_special_dir;
7430 }
7431
7432 gboolean
caja_file_is_archive(CajaFile * file)7433 caja_file_is_archive (CajaFile *file)
7434 {
7435 char *mime_type;
7436 int i;
7437 static const char * archive_mime_types[] = { "application/x-gtar",
7438 "application/x-zip",
7439 "application/x-zip-compressed",
7440 "application/zip",
7441 "application/x-zip",
7442 "application/x-tar",
7443 "application/x-7z-compressed",
7444 "application/x-rar",
7445 "application/x-rar-compressed",
7446 "application/x-jar",
7447 "application/x-java-archive",
7448 "application/x-war",
7449 "application/x-ear",
7450 "application/x-arj",
7451 "application/x-gzip",
7452 "application/x-bzip-compressed-tar",
7453 "application/x-compressed-tar" };
7454
7455 g_return_val_if_fail (file != NULL, FALSE);
7456
7457 mime_type = caja_file_get_mime_type (file);
7458 for (i = 0; i < G_N_ELEMENTS (archive_mime_types); i++) {
7459 if (!strcmp (mime_type, archive_mime_types[i])) {
7460 g_free (mime_type);
7461 return TRUE;
7462 }
7463 }
7464 g_free (mime_type);
7465
7466 return FALSE;
7467 }
7468
7469
7470 /**
7471 * caja_file_is_in_trash
7472 *
7473 * Check if this file is a file in trash.
7474 * @file: CajaFile representing the file in question.
7475 *
7476 * Returns: TRUE if @file is in a trash.
7477 *
7478 **/
7479 gboolean
caja_file_is_in_trash(CajaFile * file)7480 caja_file_is_in_trash (CajaFile *file)
7481 {
7482 g_assert (CAJA_IS_FILE (file));
7483
7484 return caja_directory_is_in_trash (file->details->directory);
7485 }
7486
7487 GError *
caja_file_get_file_info_error(CajaFile * file)7488 caja_file_get_file_info_error (CajaFile *file)
7489 {
7490 if (!file->details->get_info_failed) {
7491 return NULL;
7492 }
7493
7494 return file->details->get_info_error;
7495 }
7496
7497 /**
7498 * caja_file_contains_text
7499 *
7500 * Check if this file contains text.
7501 * This is private and is used to decide whether or not to read the top left text.
7502 * @file: CajaFile representing the file in question.
7503 *
7504 * Returns: TRUE if @file has a text MIME type.
7505 *
7506 **/
7507 gboolean
caja_file_contains_text(CajaFile * file)7508 caja_file_contains_text (CajaFile *file)
7509 {
7510 if (file == NULL) {
7511 return FALSE;
7512 }
7513
7514 /* All text files inherit from text/plain */
7515 return caja_file_is_mime_type (file, "text/plain");
7516 }
7517
7518 /**
7519 * caja_file_is_binary
7520 *
7521 * Check if this file is a binary file.
7522 * This is private and is used to decide whether or not to show the diff
7523 * button in the file conflict dialog.
7524 * @file: CajaFile representing the file in question.
7525 *
7526 * Returns: TRUE if @file is a binary file.
7527 *
7528 **/
7529 gboolean
caja_file_is_binary(CajaFile * file)7530 caja_file_is_binary (CajaFile *file)
7531 {
7532 if (!caja_file_can_read(file))
7533 {
7534 return FALSE;
7535 }
7536
7537 gboolean is_binary = FALSE;
7538 int i = 0;
7539 FILE *fp;
7540
7541 /* Check the first 4096 bytes of the files. If these contains a 0,
7542 * we can assume the file is binary.
7543 * This idea is taken from python code of meld.
7544 */
7545
7546 fp = g_fopen (g_file_get_path (caja_file_get_location (file)), "r");
7547 if (fp == NULL)
7548 {
7549 return FALSE;
7550 }
7551
7552 while (!feof (fp)) {
7553 int c;
7554
7555 if (i > 4096) {
7556 break;
7557 }
7558 c = fgetc(fp);
7559 if (c == 0) {
7560 is_binary = TRUE;
7561 break;
7562 }
7563 i++;
7564 }
7565 fclose(fp);
7566
7567 return is_binary;
7568 }
7569
7570 /**
7571 * caja_file_is_executable
7572 *
7573 * Check if this file is executable at all.
7574 * @file: CajaFile representing the file in question.
7575 *
7576 * Returns: TRUE if any of the execute bits are set. FALSE if
7577 * not, or if the permissions are unknown.
7578 *
7579 **/
7580 gboolean
caja_file_is_executable(CajaFile * file)7581 caja_file_is_executable (CajaFile *file)
7582 {
7583 if (!file->details->has_permissions) {
7584 /* File's permissions field is not valid.
7585 * Can't access specific permissions, so return FALSE.
7586 */
7587 return FALSE;
7588 }
7589
7590 return file->details->can_execute;
7591 }
7592
7593 /**
7594 * caja_file_peek_top_left_text
7595 *
7596 * Peek at the text from the top left of the file.
7597 * @file: CajaFile representing the file in question.
7598 *
7599 * Returns: NULL if there is no text readable, otherwise, the text.
7600 * This string is owned by the file object and should not
7601 * be kept around or freed.
7602 *
7603 **/
7604 char *
caja_file_peek_top_left_text(CajaFile * file,gboolean need_large_text,gboolean * needs_loading)7605 caja_file_peek_top_left_text (CajaFile *file,
7606 gboolean need_large_text,
7607 gboolean *needs_loading)
7608 {
7609 g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
7610
7611 if (!caja_file_should_get_top_left_text (file)) {
7612 if (needs_loading) {
7613 *needs_loading = FALSE;
7614 }
7615 return NULL;
7616 }
7617
7618 if (needs_loading) {
7619 *needs_loading = !file->details->top_left_text_is_up_to_date;
7620 if (need_large_text) {
7621 *needs_loading |= file->details->got_top_left_text != file->details->got_large_top_left_text;
7622 }
7623 }
7624
7625 /* Show " ..." in the file until we read the contents in. */
7626 if (!file->details->got_top_left_text) {
7627
7628 if (caja_file_contains_text (file)) {
7629 return " ...";
7630 }
7631 return NULL;
7632 }
7633
7634 /* Show what we read in. */
7635 return file->details->top_left_text;
7636 }
7637
7638 /**
7639 * caja_file_get_top_left_text
7640 *
7641 * Get the text from the top left of the file.
7642 * @file: CajaFile representing the file in question.
7643 *
7644 * Returns: NULL if there is no text readable, otherwise, the text.
7645 *
7646 **/
7647 char *
caja_file_get_top_left_text(CajaFile * file)7648 caja_file_get_top_left_text (CajaFile *file)
7649 {
7650 return g_strdup (caja_file_peek_top_left_text (file, FALSE, NULL));
7651 }
7652
7653 char *
caja_file_get_filesystem_id(CajaFile * file)7654 caja_file_get_filesystem_id (CajaFile *file)
7655 {
7656 return g_strdup (file->details->filesystem_id);
7657 }
7658
7659 CajaFile *
caja_file_get_trash_original_file(CajaFile * file)7660 caja_file_get_trash_original_file (CajaFile *file)
7661 {
7662 CajaFile *original_file;
7663
7664 original_file = NULL;
7665
7666 if (file->details->trash_orig_path != NULL) {
7667 GFile *location;
7668
7669 location = g_file_new_for_path (file->details->trash_orig_path);
7670 original_file = caja_file_get (location);
7671 g_object_unref (location);
7672 }
7673
7674 return original_file;
7675
7676 }
7677
7678 void
caja_file_mark_gone(CajaFile * file)7679 caja_file_mark_gone (CajaFile *file)
7680 {
7681 CajaDirectory *directory;
7682
7683 if (file->details->is_gone)
7684 return;
7685
7686 file->details->is_gone = TRUE;
7687
7688 update_links_if_target (file);
7689
7690 /* Drop it from the symlink hash ! */
7691 remove_from_link_hash_table (file);
7692
7693 /* Let the directory know it's gone. */
7694 directory = file->details->directory;
7695 if (!caja_file_is_self_owned (file)) {
7696 caja_directory_remove_file (directory, file);
7697 }
7698
7699 caja_file_clear_info (file);
7700
7701 /* FIXME bugzilla.gnome.org 42429:
7702 * Maybe we can get rid of the name too eventually, but
7703 * for now that would probably require too many if statements
7704 * everywhere anyone deals with the name. Maybe we can give it
7705 * a hard-coded "<deleted>" name or something.
7706 */
7707 }
7708
7709 /**
7710 * caja_file_changed
7711 *
7712 * Notify the user that this file has changed.
7713 * @file: CajaFile representing the file in question.
7714 **/
7715 void
caja_file_changed(CajaFile * file)7716 caja_file_changed (CajaFile *file)
7717 {
7718 GList fake_list;
7719
7720 g_return_if_fail (CAJA_IS_FILE (file));
7721
7722 if (caja_file_is_self_owned (file)) {
7723 caja_file_emit_changed (file);
7724 } else {
7725 fake_list.data = file;
7726 fake_list.next = NULL;
7727 fake_list.prev = NULL;
7728 caja_directory_emit_change_signals
7729 (file->details->directory, &fake_list);
7730 }
7731 }
7732
7733 /**
7734 * caja_file_updated_deep_count_in_progress
7735 *
7736 * Notify clients that a newer deep count is available for
7737 * the directory in question.
7738 */
7739 void
caja_file_updated_deep_count_in_progress(CajaFile * file)7740 caja_file_updated_deep_count_in_progress (CajaFile *file) {
7741 GList *link_files, *node;
7742
7743 g_assert (CAJA_IS_FILE (file));
7744 g_assert (caja_file_is_directory (file));
7745
7746 /* Send out a signal. */
7747 g_signal_emit (file, signals[UPDATED_DEEP_COUNT_IN_PROGRESS], 0, file);
7748
7749 /* Tell link files pointing to this object about the change. */
7750 link_files = get_link_files (file);
7751 for (node = link_files; node != NULL; node = node->next) {
7752 caja_file_updated_deep_count_in_progress (CAJA_FILE (node->data));
7753 }
7754 caja_file_list_free (link_files);
7755 }
7756
7757 /**
7758 * caja_file_emit_changed
7759 *
7760 * Emit a file changed signal.
7761 * This can only be called by the directory, since the directory
7762 * also has to emit a files_changed signal.
7763 *
7764 * @file: CajaFile representing the file in question.
7765 **/
7766 void
caja_file_emit_changed(CajaFile * file)7767 caja_file_emit_changed (CajaFile *file)
7768 {
7769 GList *link_files, *p;
7770
7771 g_assert (CAJA_IS_FILE (file));
7772
7773
7774 /* Invalidate the emblem compare cache. -- This is not the cleanest
7775 * place to do it but it is the one guaranteed bottleneck through
7776 * which all change notifications pass.
7777 */
7778 g_free (file->details->compare_by_emblem_cache);
7779 file->details->compare_by_emblem_cache = NULL;
7780
7781 /* Send out a signal. */
7782 g_signal_emit (file, signals[CHANGED], 0, file);
7783
7784 /* Tell link files pointing to this object about the change. */
7785 link_files = get_link_files (file);
7786 for (p = link_files; p != NULL; p = p->next) {
7787 if (p->data != file) {
7788 caja_file_changed (CAJA_FILE (p->data));
7789 }
7790 }
7791 caja_file_list_free (link_files);
7792 }
7793
7794 /**
7795 * caja_file_is_gone
7796 *
7797 * Check if a file has already been deleted.
7798 * @file: CajaFile representing the file in question.
7799 *
7800 * Returns: TRUE if the file is already gone.
7801 **/
7802 gboolean
caja_file_is_gone(CajaFile * file)7803 caja_file_is_gone (CajaFile *file)
7804 {
7805 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
7806
7807 return file->details->is_gone;
7808 }
7809
7810 /**
7811 * caja_file_is_not_yet_confirmed
7812 *
7813 * Check if we're in a state where we don't know if a file really
7814 * exists or not, before the initial I/O is complete.
7815 * @file: CajaFile representing the file in question.
7816 *
7817 * Returns: TRUE if the file is already gone.
7818 **/
7819 gboolean
caja_file_is_not_yet_confirmed(CajaFile * file)7820 caja_file_is_not_yet_confirmed (CajaFile *file)
7821 {
7822 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
7823
7824 return !file->details->got_file_info;
7825 }
7826
7827 /**
7828 * caja_file_check_if_ready
7829 *
7830 * Check whether the values for a set of file attributes are
7831 * currently available, without doing any additional work. This
7832 * is useful for callers that want to reflect updated information
7833 * when it is ready but don't want to force the work required to
7834 * obtain the information, which might be slow network calls, e.g.
7835 *
7836 * @file: The file being queried.
7837 * @file_attributes: A bit-mask with the desired information.
7838 *
7839 * Return value: TRUE if all of the specified attributes are currently readable.
7840 */
7841 gboolean
caja_file_check_if_ready(CajaFile * file,CajaFileAttributes file_attributes)7842 caja_file_check_if_ready (CajaFile *file,
7843 CajaFileAttributes file_attributes)
7844 {
7845 /* To be parallel with call_when_ready, return
7846 * TRUE for NULL file.
7847 */
7848 if (file == NULL) {
7849 return TRUE;
7850 }
7851
7852 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
7853
7854 return EEL_CALL_METHOD_WITH_RETURN_VALUE
7855 (CAJA_FILE_CLASS, file,
7856 check_if_ready, (file, file_attributes));
7857 }
7858
7859 void
caja_file_call_when_ready(CajaFile * file,CajaFileAttributes file_attributes,CajaFileCallback callback,gpointer callback_data)7860 caja_file_call_when_ready (CajaFile *file,
7861 CajaFileAttributes file_attributes,
7862 CajaFileCallback callback,
7863 gpointer callback_data)
7864
7865 {
7866 if (file == NULL) {
7867 (* callback) (file, callback_data);
7868 return;
7869 }
7870
7871 g_return_if_fail (CAJA_IS_FILE (file));
7872
7873 EEL_CALL_METHOD
7874 (CAJA_FILE_CLASS, file,
7875 call_when_ready, (file, file_attributes,
7876 callback, callback_data));
7877 }
7878
7879 void
caja_file_cancel_call_when_ready(CajaFile * file,CajaFileCallback callback,gpointer callback_data)7880 caja_file_cancel_call_when_ready (CajaFile *file,
7881 CajaFileCallback callback,
7882 gpointer callback_data)
7883 {
7884 g_return_if_fail (callback != NULL);
7885
7886 if (file == NULL) {
7887 return;
7888 }
7889
7890 g_return_if_fail (CAJA_IS_FILE (file));
7891
7892 EEL_CALL_METHOD
7893 (CAJA_FILE_CLASS, file,
7894 cancel_call_when_ready, (file, callback, callback_data));
7895 }
7896
7897 static void
invalidate_directory_count(CajaFile * file)7898 invalidate_directory_count (CajaFile *file)
7899 {
7900 file->details->directory_count_is_up_to_date = FALSE;
7901 }
7902
7903 static void
invalidate_deep_counts(CajaFile * file)7904 invalidate_deep_counts (CajaFile *file)
7905 {
7906 file->details->deep_counts_status = CAJA_REQUEST_NOT_STARTED;
7907 }
7908
7909 static void
invalidate_mime_list(CajaFile * file)7910 invalidate_mime_list (CajaFile *file)
7911 {
7912 file->details->mime_list_is_up_to_date = FALSE;
7913 }
7914
7915 static void
invalidate_top_left_text(CajaFile * file)7916 invalidate_top_left_text (CajaFile *file)
7917 {
7918 file->details->top_left_text_is_up_to_date = FALSE;
7919 }
7920
7921 static void
invalidate_file_info(CajaFile * file)7922 invalidate_file_info (CajaFile *file)
7923 {
7924 file->details->file_info_is_up_to_date = FALSE;
7925 }
7926
7927 static void
invalidate_link_info(CajaFile * file)7928 invalidate_link_info (CajaFile *file)
7929 {
7930 file->details->link_info_is_up_to_date = FALSE;
7931 }
7932
7933 static void
invalidate_thumbnail(CajaFile * file)7934 invalidate_thumbnail (CajaFile *file)
7935 {
7936 file->details->thumbnail_is_up_to_date = FALSE;
7937 }
7938
7939 static void
invalidate_mount(CajaFile * file)7940 invalidate_mount (CajaFile *file)
7941 {
7942 file->details->mount_is_up_to_date = FALSE;
7943 }
7944
7945 void
caja_file_invalidate_extension_info_internal(CajaFile * file)7946 caja_file_invalidate_extension_info_internal (CajaFile *file)
7947 {
7948 if (file->details->pending_info_providers)
7949 g_list_free_full (file->details->pending_info_providers, g_object_unref);
7950
7951 file->details->pending_info_providers =
7952 caja_extensions_get_for_type (CAJA_TYPE_INFO_PROVIDER);
7953 }
7954
7955 void
caja_file_invalidate_attributes_internal(CajaFile * file,CajaFileAttributes file_attributes)7956 caja_file_invalidate_attributes_internal (CajaFile *file,
7957 CajaFileAttributes file_attributes)
7958 {
7959 Request request;
7960
7961 if (file == NULL) {
7962 return;
7963 }
7964
7965 if (CAJA_IS_DESKTOP_ICON_FILE (file)) {
7966 /* Desktop icon files are always up to date.
7967 * If we invalidate their attributes they
7968 * will lose data, so we just ignore them.
7969 */
7970 return;
7971 }
7972
7973 request = caja_directory_set_up_request (file_attributes);
7974
7975 if (REQUEST_WANTS_TYPE (request, REQUEST_DIRECTORY_COUNT)) {
7976 invalidate_directory_count (file);
7977 }
7978 if (REQUEST_WANTS_TYPE (request, REQUEST_DEEP_COUNT)) {
7979 invalidate_deep_counts (file);
7980 }
7981 if (REQUEST_WANTS_TYPE (request, REQUEST_MIME_LIST)) {
7982 invalidate_mime_list (file);
7983 }
7984 if (REQUEST_WANTS_TYPE (request, REQUEST_FILE_INFO)) {
7985 invalidate_file_info (file);
7986 }
7987 if (REQUEST_WANTS_TYPE (request, REQUEST_TOP_LEFT_TEXT)) {
7988 invalidate_top_left_text (file);
7989 }
7990 if (REQUEST_WANTS_TYPE (request, REQUEST_LINK_INFO)) {
7991 invalidate_link_info (file);
7992 }
7993 if (REQUEST_WANTS_TYPE (request, REQUEST_EXTENSION_INFO)) {
7994 caja_file_invalidate_extension_info_internal (file);
7995 }
7996 if (REQUEST_WANTS_TYPE (request, REQUEST_THUMBNAIL)) {
7997 invalidate_thumbnail (file);
7998 }
7999 if (REQUEST_WANTS_TYPE (request, REQUEST_MOUNT)) {
8000 invalidate_mount (file);
8001 }
8002
8003 /* FIXME bugzilla.gnome.org 45075: implement invalidating metadata */
8004 }
8005
8006 gboolean
caja_file_has_open_window(CajaFile * file)8007 caja_file_has_open_window (CajaFile *file)
8008 {
8009 return file->details->has_open_window;
8010 }
8011
8012 void
caja_file_set_has_open_window(CajaFile * file,gboolean has_open_window)8013 caja_file_set_has_open_window (CajaFile *file,
8014 gboolean has_open_window)
8015 {
8016 has_open_window = (has_open_window != FALSE);
8017
8018 if (file->details->has_open_window != has_open_window) {
8019 file->details->has_open_window = has_open_window;
8020 caja_file_changed (file);
8021 }
8022 }
8023
8024
8025 gboolean
caja_file_is_thumbnailing(CajaFile * file)8026 caja_file_is_thumbnailing (CajaFile *file)
8027 {
8028 g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
8029
8030 return file->details->is_thumbnailing;
8031 }
8032
8033 void
caja_file_set_is_thumbnailing(CajaFile * file,gboolean is_thumbnailing)8034 caja_file_set_is_thumbnailing (CajaFile *file,
8035 gboolean is_thumbnailing)
8036 {
8037 g_return_if_fail (CAJA_IS_FILE (file));
8038
8039 file->details->is_thumbnailing = is_thumbnailing;
8040 }
8041
8042
8043 /**
8044 * caja_file_invalidate_attributes
8045 *
8046 * Invalidate the specified attributes and force a reload.
8047 * @file: CajaFile representing the file in question.
8048 * @file_attributes: attributes to froget.
8049 **/
8050
8051 void
caja_file_invalidate_attributes(CajaFile * file,CajaFileAttributes file_attributes)8052 caja_file_invalidate_attributes (CajaFile *file,
8053 CajaFileAttributes file_attributes)
8054 {
8055 /* Cancel possible in-progress loads of any of these attributes */
8056 caja_directory_cancel_loading_file_attributes (file->details->directory,
8057 file,
8058 file_attributes);
8059
8060 /* Actually invalidate the values */
8061 caja_file_invalidate_attributes_internal (file, file_attributes);
8062
8063 caja_directory_add_file_to_work_queue (file->details->directory, file);
8064
8065 /* Kick off I/O if necessary */
8066 caja_directory_async_state_changed (file->details->directory);
8067 }
8068
8069 CajaFileAttributes
caja_file_get_all_attributes(void)8070 caja_file_get_all_attributes (void)
8071 {
8072 return CAJA_FILE_ATTRIBUTE_INFO |
8073 CAJA_FILE_ATTRIBUTE_LINK_INFO |
8074 CAJA_FILE_ATTRIBUTE_DEEP_COUNTS |
8075 CAJA_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT |
8076 CAJA_FILE_ATTRIBUTE_DIRECTORY_ITEM_MIME_TYPES |
8077 CAJA_FILE_ATTRIBUTE_TOP_LEFT_TEXT |
8078 CAJA_FILE_ATTRIBUTE_LARGE_TOP_LEFT_TEXT |
8079 CAJA_FILE_ATTRIBUTE_EXTENSION_INFO |
8080 CAJA_FILE_ATTRIBUTE_THUMBNAIL |
8081 CAJA_FILE_ATTRIBUTE_MOUNT;
8082 }
8083
8084 void
caja_file_invalidate_all_attributes(CajaFile * file)8085 caja_file_invalidate_all_attributes (CajaFile *file)
8086 {
8087 CajaFileAttributes all_attributes;
8088
8089 all_attributes = caja_file_get_all_attributes ();
8090 caja_file_invalidate_attributes (file, all_attributes);
8091 }
8092
8093
8094 /**
8095 * caja_file_dump
8096 *
8097 * Debugging call, prints out the contents of the file
8098 * fields.
8099 *
8100 * @file: file to dump.
8101 **/
8102 void
caja_file_dump(CajaFile * file)8103 caja_file_dump (CajaFile *file)
8104 {
8105 long size = file->details->deep_size;
8106 long size_on_disk = file->details->deep_size_on_disk;
8107 char *uri;
8108 const char *file_kind;
8109
8110 uri = caja_file_get_uri (file);
8111 g_print ("uri: %s \n", uri);
8112 if (!file->details->got_file_info) {
8113 g_print ("no file info \n");
8114 } else if (file->details->get_info_failed) {
8115 g_print ("failed to get file info \n");
8116 } else {
8117 g_print ("size: %ld \n", size);
8118 g_print ("disk size: %ld \n", size_on_disk);
8119 switch (file->details->type) {
8120 case G_FILE_TYPE_REGULAR:
8121 file_kind = "regular file";
8122 break;
8123 case G_FILE_TYPE_DIRECTORY:
8124 file_kind = "folder";
8125 break;
8126 case G_FILE_TYPE_SPECIAL:
8127 file_kind = "special";
8128 break;
8129 case G_FILE_TYPE_SYMBOLIC_LINK:
8130 file_kind = "symbolic link";
8131 break;
8132 case G_FILE_TYPE_UNKNOWN:
8133 default:
8134 file_kind = "unknown";
8135 break;
8136 }
8137 g_print ("kind: %s \n", file_kind);
8138 if (file->details->type == G_FILE_TYPE_SYMBOLIC_LINK) {
8139 g_print ("link to %s \n", file->details->symlink_name);
8140 /* FIXME bugzilla.gnome.org 42430: add following of symlinks here */
8141 }
8142 /* FIXME bugzilla.gnome.org 42431: add permissions and other useful stuff here */
8143 }
8144 g_free (uri);
8145 }
8146
8147 /**
8148 * caja_file_list_ref
8149 *
8150 * Ref all the files in a list.
8151 * @list: GList of files.
8152 **/
8153 GList *
caja_file_list_ref(GList * list)8154 caja_file_list_ref (GList *list)
8155 {
8156 g_list_foreach (list, (GFunc) caja_file_ref, NULL);
8157 return list;
8158 }
8159
8160 /**
8161 * caja_file_list_unref
8162 *
8163 * Unref all the files in a list.
8164 * @list: GList of files.
8165 **/
8166 void
caja_file_list_unref(GList * list)8167 caja_file_list_unref (GList *list)
8168 {
8169 g_list_foreach (list, (GFunc) caja_file_unref, NULL);
8170 }
8171
8172 /**
8173 * caja_file_list_free
8174 *
8175 * Free a list of files after unrefing them.
8176 * @list: GList of files.
8177 **/
8178 void
caja_file_list_free(GList * list)8179 caja_file_list_free (GList *list)
8180 {
8181 caja_file_list_unref (list);
8182 g_list_free (list);
8183 }
8184
8185 /**
8186 * caja_file_list_copy
8187 *
8188 * Copy the list of files, making a new ref of each,
8189 * @list: GList of files.
8190 **/
8191 GList *
caja_file_list_copy(GList * list)8192 caja_file_list_copy (GList *list)
8193 {
8194 return g_list_copy (caja_file_list_ref (list));
8195 }
8196
8197 static gboolean
get_attributes_for_default_sort_type(CajaFile * file,gboolean * is_download,gboolean * is_trash)8198 get_attributes_for_default_sort_type (CajaFile *file,
8199 gboolean *is_download,
8200 gboolean *is_trash)
8201 {
8202 gboolean is_download_dir, is_desktop_dir, is_trash_dir, retval;
8203
8204 *is_download = FALSE;
8205 *is_trash = FALSE;
8206 retval = FALSE;
8207
8208 /* special handling for certain directories */
8209 if (file && caja_file_is_directory (file)) {
8210 is_download_dir =
8211 caja_file_is_user_special_directory (file, G_USER_DIRECTORY_DOWNLOAD);
8212 is_desktop_dir =
8213 caja_file_is_user_special_directory (file, G_USER_DIRECTORY_DESKTOP);
8214 is_trash_dir =
8215 caja_file_is_in_trash (file);
8216
8217 if (is_download_dir && !is_desktop_dir) {
8218 *is_download = TRUE;
8219 retval = TRUE;
8220 } else if (is_trash_dir) {
8221 *is_trash = TRUE;
8222 retval = TRUE;
8223 }
8224 }
8225
8226 return retval;
8227 }
8228
8229 CajaFileSortType
caja_file_get_default_sort_type(CajaFile * file,gboolean * reversed)8230 caja_file_get_default_sort_type (CajaFile *file,
8231 gboolean *reversed)
8232 {
8233 CajaFileSortType retval;
8234 gboolean is_download, is_trash, res;
8235
8236 retval = CAJA_FILE_SORT_NONE;
8237 is_download = is_trash = FALSE;
8238 res = get_attributes_for_default_sort_type (file, &is_download, &is_trash);
8239
8240 if (res) {
8241 if (is_download) {
8242 retval = CAJA_FILE_SORT_BY_MTIME;
8243 } else if (is_trash) {
8244 retval = CAJA_FILE_SORT_BY_TRASHED_TIME;
8245 }
8246
8247 if (reversed != NULL) {
8248 *reversed = res;
8249 }
8250 }
8251
8252 return retval;
8253 }
8254
8255 const gchar *
caja_file_get_default_sort_attribute(CajaFile * file,gboolean * reversed)8256 caja_file_get_default_sort_attribute (CajaFile *file,
8257 gboolean *reversed)
8258 {
8259 const gchar *retval;
8260 gboolean is_download, is_trash, res;
8261
8262 retval = NULL;
8263 is_download = is_trash = FALSE;
8264 res = get_attributes_for_default_sort_type (file, &is_download, &is_trash);
8265
8266 if (res) {
8267 if (is_download) {
8268 retval = g_quark_to_string (attribute_date_modified_q);
8269 } else if (is_trash) {
8270 retval = g_quark_to_string (attribute_trashed_on_q);
8271 }
8272
8273 if (reversed != NULL) {
8274 *reversed = res;
8275 }
8276 }
8277
8278 return retval;
8279 }
8280
8281 static int
compare_by_display_name_cover(gconstpointer a,gconstpointer b)8282 compare_by_display_name_cover (gconstpointer a, gconstpointer b)
8283 {
8284 return compare_by_display_name (CAJA_FILE (a), CAJA_FILE (b));
8285 }
8286
8287 /**
8288 * caja_file_list_sort_by_display_name
8289 *
8290 * Sort the list of files by file name.
8291 * @list: GList of files.
8292 **/
8293 GList *
caja_file_list_sort_by_display_name(GList * list)8294 caja_file_list_sort_by_display_name (GList *list)
8295 {
8296 return g_list_sort (list, compare_by_display_name_cover);
8297 }
8298
8299 static GList *ready_data_list = NULL;
8300
8301 typedef struct
8302 {
8303 GList *file_list;
8304 GList *remaining_files;
8305 CajaFileListCallback callback;
8306 gpointer callback_data;
8307 } FileListReadyData;
8308
8309 static void
file_list_ready_data_free(FileListReadyData * data)8310 file_list_ready_data_free (FileListReadyData *data)
8311 {
8312 GList *l;
8313
8314 l = g_list_find (ready_data_list, data);
8315 if (l != NULL) {
8316 ready_data_list = g_list_delete_link (ready_data_list, l);
8317
8318 caja_file_list_free (data->file_list);
8319 g_list_free (data->remaining_files);
8320 g_free (data);
8321 }
8322 }
8323
8324 static FileListReadyData *
file_list_ready_data_new(GList * file_list,CajaFileListCallback callback,gpointer callback_data)8325 file_list_ready_data_new (GList *file_list,
8326 CajaFileListCallback callback,
8327 gpointer callback_data)
8328 {
8329 FileListReadyData *data;
8330
8331 data = g_new0 (FileListReadyData, 1);
8332 data->file_list = caja_file_list_copy (file_list);
8333 data->remaining_files = g_list_copy (file_list);
8334 data->callback = callback;
8335 data->callback_data = callback_data;
8336
8337 ready_data_list = g_list_prepend (ready_data_list, data);
8338
8339 return data;
8340 }
8341
8342 static void
file_list_file_ready_callback(CajaFile * file,gpointer user_data)8343 file_list_file_ready_callback (CajaFile *file,
8344 gpointer user_data)
8345 {
8346 FileListReadyData *data;
8347
8348 data = user_data;
8349 data->remaining_files = g_list_remove (data->remaining_files, file);
8350
8351 if (data->remaining_files == NULL) {
8352 if (data->callback) {
8353 (*data->callback) (data->file_list, data->callback_data);
8354 }
8355
8356 file_list_ready_data_free (data);
8357 }
8358 }
8359
8360 void
caja_file_list_call_when_ready(GList * file_list,CajaFileAttributes attributes,CajaFileListHandle ** handle,CajaFileListCallback callback,gpointer callback_data)8361 caja_file_list_call_when_ready (GList *file_list,
8362 CajaFileAttributes attributes,
8363 CajaFileListHandle **handle,
8364 CajaFileListCallback callback,
8365 gpointer callback_data)
8366 {
8367 GList *l;
8368 FileListReadyData *data;
8369 CajaFile *file = NULL;
8370
8371 g_return_if_fail (file_list != NULL);
8372
8373 data = file_list_ready_data_new
8374 (file_list, callback, callback_data);
8375
8376 if (handle) {
8377 *handle = (CajaFileListHandle *) data;
8378 }
8379
8380
8381 l = file_list;
8382 while (l != NULL) {
8383 file = CAJA_FILE (l->data);
8384 /* Need to do this here, as the list can be modified by this call */
8385 l = l->next;
8386
8387 if (file)
8388 caja_file_call_when_ready (file,
8389 attributes,
8390 file_list_file_ready_callback,
8391 data);
8392 }
8393 }
8394
8395 void
caja_file_list_cancel_call_when_ready(CajaFileListHandle * handle)8396 caja_file_list_cancel_call_when_ready (CajaFileListHandle *handle)
8397 {
8398 GList *l;
8399 FileListReadyData *data;
8400
8401 g_return_if_fail (handle != NULL);
8402
8403 data = (FileListReadyData *) handle;
8404
8405 l = g_list_find (ready_data_list, data);
8406 if (l != NULL) {
8407 CajaFile *file = NULL;
8408
8409 for (l = data->remaining_files; l != NULL; l = l->next) {
8410 file = CAJA_FILE (l->data);
8411
8412 EEL_CALL_METHOD
8413 (CAJA_FILE_CLASS, file,
8414 cancel_call_when_ready, (file, file_list_file_ready_callback, data));
8415 }
8416
8417 file_list_ready_data_free (data);
8418 }
8419 }
8420
8421 static char *
try_to_make_utf8(const char * text,int * length)8422 try_to_make_utf8 (const char *text, int *length)
8423 {
8424 static const char *encodings_to_try[2];
8425 static int n_encodings_to_try = 0;
8426 gsize converted_length;
8427 GError *conversion_error;
8428 char *utf8_text;
8429 int i;
8430
8431 if (n_encodings_to_try == 0) {
8432 const char *charset;
8433 gboolean charset_is_utf8;
8434
8435 charset_is_utf8 = g_get_charset (&charset);
8436 if (!charset_is_utf8) {
8437 encodings_to_try[n_encodings_to_try++] = charset;
8438 }
8439
8440 if (g_ascii_strcasecmp (charset, "ISO-8859-1") != 0) {
8441 encodings_to_try[n_encodings_to_try++] = "ISO-8859-1";
8442 }
8443 }
8444
8445 utf8_text = NULL;
8446 for (i = 0; i < n_encodings_to_try; i++) {
8447 conversion_error = NULL;
8448 utf8_text = g_convert (text, *length,
8449 "UTF-8", encodings_to_try[i],
8450 NULL, &converted_length, &conversion_error);
8451 if (utf8_text != NULL) {
8452 *length = converted_length;
8453 break;
8454 }
8455 g_error_free (conversion_error);
8456 }
8457
8458 return utf8_text;
8459 }
8460
8461
8462
8463 /* Extract the top left part of the read-in text. */
8464 char *
caja_extract_top_left_text(const char * text,gboolean large,int length)8465 caja_extract_top_left_text (const char *text,
8466 gboolean large,
8467 int length)
8468 {
8469 GString* buffer;
8470 const gchar *in;
8471 const gchar *end;
8472 int line, i;
8473 gunichar c;
8474 char *text_copy;
8475 const char *utf8_end;
8476 gboolean validated;
8477 int max_bytes, max_lines, max_cols;
8478
8479 if (large) {
8480 max_bytes = CAJA_FILE_LARGE_TOP_LEFT_TEXT_MAXIMUM_BYTES;
8481 max_lines = CAJA_FILE_LARGE_TOP_LEFT_TEXT_MAXIMUM_LINES;
8482 max_cols = CAJA_FILE_LARGE_TOP_LEFT_TEXT_MAXIMUM_CHARACTERS_PER_LINE;
8483 } else {
8484 max_bytes = CAJA_FILE_TOP_LEFT_TEXT_MAXIMUM_BYTES;
8485 max_lines = CAJA_FILE_TOP_LEFT_TEXT_MAXIMUM_LINES;
8486 max_cols = CAJA_FILE_TOP_LEFT_TEXT_MAXIMUM_CHARACTERS_PER_LINE;
8487 }
8488
8489
8490
8491 text_copy = NULL;
8492 if (text != NULL) {
8493 /* Might be a partial utf8 character at the end if we didn't read whole file */
8494 validated = g_utf8_validate (text, length, &utf8_end);
8495 if (!validated &&
8496 !(length >= max_bytes &&
8497 text + length - utf8_end < 6)) {
8498 text_copy = try_to_make_utf8 (text, &length);
8499 text = text_copy;
8500 } else if (!validated) {
8501 length = utf8_end - text;
8502 }
8503 }
8504
8505 if (text == NULL || length == 0) {
8506 return NULL;
8507 }
8508
8509 buffer = g_string_new ("");
8510 end = text + length; in = text;
8511
8512 for (line = 0; line < max_lines; line++) {
8513 /* Extract one line. */
8514 for (i = 0; i < max_cols; ) {
8515 if (*in == '\n') {
8516 break;
8517 }
8518
8519 c = g_utf8_get_char (in);
8520
8521 if (g_unichar_isprint (c)) {
8522 g_string_append_unichar (buffer, c);
8523 i++;
8524 }
8525
8526 in = g_utf8_next_char (in);
8527 if (in == end) {
8528 goto done;
8529 }
8530 }
8531
8532 /* Skip the rest of the line. */
8533 while (*in != '\n') {
8534 if (++in == end) {
8535 goto done;
8536 }
8537 }
8538 if (++in == end) {
8539 goto done;
8540 }
8541
8542 /* Put a new-line separator in. */
8543 g_string_append_c(buffer, '\n');
8544 }
8545 done:
8546 g_free (text_copy);
8547
8548 return g_string_free(buffer, FALSE);
8549 }
8550
8551 static void
thumbnail_limit_changed_callback(gpointer user_data)8552 thumbnail_limit_changed_callback (gpointer user_data)
8553 {
8554 g_settings_get (caja_preferences,
8555 CAJA_PREFERENCES_IMAGE_FILE_THUMBNAIL_LIMIT,
8556 "t", &cached_thumbnail_limit);
8557
8558 /* Tell the world that icons might have changed. We could invent a narrower-scope
8559 * signal to mean only "thumbnails might have changed" if this ends up being slow
8560 * for some reason.
8561 */
8562 emit_change_signals_for_all_files_in_all_directories ();
8563 }
8564
8565 static void
thumbnail_size_changed_callback(gpointer user_data)8566 thumbnail_size_changed_callback (gpointer user_data)
8567 {
8568 cached_thumbnail_size = g_settings_get_int (caja_icon_view_preferences, CAJA_PREFERENCES_ICON_VIEW_THUMBNAIL_SIZE);
8569
8570 /* Tell the world that icons might have changed. We could invent a narrower-scope
8571 * signal to mean only "thumbnails might have changed" if this ends up being slow
8572 * for some reason.
8573 */
8574 emit_change_signals_for_all_files_in_all_directories ();
8575 }
8576
8577 static void
show_thumbnails_changed_callback(gpointer user_data)8578 show_thumbnails_changed_callback (gpointer user_data)
8579 {
8580 show_image_thumbs = g_settings_get_enum (caja_preferences, CAJA_PREFERENCES_SHOW_IMAGE_FILE_THUMBNAILS);
8581
8582 /* Tell the world that icons might have changed. We could invent a narrower-scope
8583 * signal to mean only "thumbnails might have changed" if this ends up being slow
8584 * for some reason.
8585 */
8586 emit_change_signals_for_all_files_in_all_directories ();
8587 }
8588
8589 static void
mime_type_data_changed_callback(GObject * signaller,gpointer user_data)8590 mime_type_data_changed_callback (GObject *signaller, gpointer user_data)
8591 {
8592 /* Tell the world that icons might have changed. We could invent a narrower-scope
8593 * signal to mean only "thumbnails might have changed" if this ends up being slow
8594 * for some reason.
8595 */
8596 emit_change_signals_for_all_files_in_all_directories ();
8597 }
8598
8599 static void
icon_theme_changed_callback(GtkIconTheme * icon_theme,gpointer user_data)8600 icon_theme_changed_callback (GtkIconTheme *icon_theme,
8601 gpointer user_data)
8602 {
8603 /* Clear all pixmap caches as the icon => pixmap lookup changed */
8604 caja_icon_info_clear_caches ();
8605
8606 /* Tell the world that icons might have changed. We could invent a narrower-scope
8607 * signal to mean only "thumbnails might have changed" if this ends up being slow
8608 * for some reason.
8609 */
8610 emit_change_signals_for_all_files_in_all_directories ();
8611 }
8612
8613 static void
caja_file_class_init(CajaFileClass * class)8614 caja_file_class_init (CajaFileClass *class)
8615 {
8616 GtkIconTheme *icon_theme;
8617
8618 caja_file_info_getter = caja_file_get_internal;
8619
8620 attribute_name_q = g_quark_from_static_string ("name");
8621 attribute_size_q = g_quark_from_static_string ("size");
8622 attribute_size_on_disk_q = g_quark_from_static_string ("size_on_disk");
8623 attribute_type_q = g_quark_from_static_string ("type");
8624 attribute_modification_date_q = g_quark_from_static_string ("modification_date");
8625 attribute_date_modified_q = g_quark_from_static_string ("date_modified");
8626 attribute_creation_date_q = g_quark_from_static_string ("creation_date");
8627 attribute_date_created_q = g_quark_from_static_string ("date_created");
8628 attribute_accessed_date_q = g_quark_from_static_string ("accessed_date");
8629 attribute_date_accessed_q = g_quark_from_static_string ("date_accessed");
8630 attribute_emblems_q = g_quark_from_static_string ("emblems");
8631 attribute_extension_q = g_quark_from_static_string ("extension");
8632 attribute_mime_type_q = g_quark_from_static_string ("mime_type");
8633 attribute_size_detail_q = g_quark_from_static_string ("size_detail");
8634 attribute_size_on_disk_detail_q = g_quark_from_static_string ("size_on_disk_detail");
8635 attribute_deep_size_q = g_quark_from_static_string ("deep_size");
8636 attribute_deep_size_on_disk_q = g_quark_from_static_string ("deep_size_on_disk");
8637 attribute_deep_file_count_q = g_quark_from_static_string ("deep_file_count");
8638 attribute_deep_directory_count_q = g_quark_from_static_string ("deep_directory_count");
8639 attribute_deep_total_count_q = g_quark_from_static_string ("deep_total_count");
8640 attribute_date_changed_q = g_quark_from_static_string ("date_changed");
8641 attribute_trashed_on_q = g_quark_from_static_string ("trashed_on");
8642 attribute_trash_orig_path_q = g_quark_from_static_string ("trash_orig_path");
8643 attribute_date_permissions_q = g_quark_from_static_string ("date_permissions");
8644 attribute_permissions_q = g_quark_from_static_string ("permissions");
8645 attribute_selinux_context_q = g_quark_from_static_string ("selinux_context");
8646 attribute_octal_permissions_q = g_quark_from_static_string ("octal_permissions");
8647 attribute_owner_q = g_quark_from_static_string ("owner");
8648 attribute_group_q = g_quark_from_static_string ("group");
8649 attribute_uri_q = g_quark_from_static_string ("uri");
8650 attribute_where_q = g_quark_from_static_string ("where");
8651 attribute_link_target_q = g_quark_from_static_string ("link_target");
8652 attribute_volume_q = g_quark_from_static_string ("volume");
8653 attribute_free_space_q = g_quark_from_static_string ("free_space");
8654
8655 G_OBJECT_CLASS (class)->finalize = finalize;
8656 G_OBJECT_CLASS (class)->constructor = caja_file_constructor;
8657
8658 signals[CHANGED] =
8659 g_signal_new ("changed",
8660 G_TYPE_FROM_CLASS (class),
8661 G_SIGNAL_RUN_LAST,
8662 G_STRUCT_OFFSET (CajaFileClass, changed),
8663 NULL, NULL,
8664 g_cclosure_marshal_VOID__VOID,
8665 G_TYPE_NONE, 0);
8666
8667 signals[UPDATED_DEEP_COUNT_IN_PROGRESS] =
8668 g_signal_new ("updated_deep_count_in_progress",
8669 G_TYPE_FROM_CLASS (class),
8670 G_SIGNAL_RUN_LAST,
8671 G_STRUCT_OFFSET (CajaFileClass, updated_deep_count_in_progress),
8672 NULL, NULL,
8673 g_cclosure_marshal_VOID__VOID,
8674 G_TYPE_NONE, 0);
8675
8676 eel_g_settings_add_auto_enum (caja_preferences,
8677 CAJA_PREFERENCES_DATE_FORMAT,
8678 &date_format_pref);
8679
8680 thumbnail_limit_changed_callback (NULL);
8681 g_signal_connect_swapped (caja_preferences,
8682 "changed::" CAJA_PREFERENCES_IMAGE_FILE_THUMBNAIL_LIMIT,
8683 G_CALLBACK (thumbnail_limit_changed_callback),
8684 NULL);
8685 thumbnail_size_changed_callback (NULL);
8686 g_signal_connect_swapped (caja_icon_view_preferences,
8687 "changed::" CAJA_PREFERENCES_ICON_VIEW_THUMBNAIL_SIZE,
8688 G_CALLBACK (thumbnail_size_changed_callback),
8689 NULL);
8690 show_thumbnails_changed_callback (NULL);
8691 g_signal_connect_swapped (caja_preferences,
8692 "changed::" CAJA_PREFERENCES_SHOW_IMAGE_FILE_THUMBNAILS,
8693 G_CALLBACK (show_thumbnails_changed_callback),
8694 NULL);
8695
8696 icon_theme = gtk_icon_theme_get_default ();
8697 g_signal_connect_object (icon_theme,
8698 "changed",
8699 G_CALLBACK (icon_theme_changed_callback),
8700 NULL, 0);
8701
8702 g_signal_connect (caja_signaller_get_current (),
8703 "mime_data_changed",
8704 G_CALLBACK (mime_type_data_changed_callback),
8705 NULL);
8706 }
8707
8708 static void
caja_file_add_emblem(CajaFile * file,const char * emblem_name)8709 caja_file_add_emblem (CajaFile *file,
8710 const char *emblem_name)
8711 {
8712 if (file->details->pending_info_providers) {
8713 file->details->pending_extension_emblems = g_list_prepend (file->details->pending_extension_emblems,
8714 g_strdup (emblem_name));
8715 } else {
8716 file->details->extension_emblems = g_list_prepend (file->details->extension_emblems,
8717 g_strdup (emblem_name));
8718 }
8719
8720 caja_file_changed (file);
8721 }
8722
8723 static void
caja_file_add_string_attribute(CajaFile * file,const char * attribute_name,const char * value)8724 caja_file_add_string_attribute (CajaFile *file,
8725 const char *attribute_name,
8726 const char *value)
8727 {
8728 if (file->details->pending_info_providers) {
8729 /* Lazily create hashtable */
8730 if (!file->details->pending_extension_attributes) {
8731 file->details->pending_extension_attributes =
8732 g_hash_table_new_full (g_direct_hash, g_direct_equal,
8733 NULL,
8734 (GDestroyNotify)g_free);
8735 }
8736 g_hash_table_insert (file->details->pending_extension_attributes,
8737 GINT_TO_POINTER (g_quark_from_string (attribute_name)),
8738 g_strdup (value));
8739 } else {
8740 if (!file->details->extension_attributes) {
8741 file->details->extension_attributes =
8742 g_hash_table_new_full (g_direct_hash, g_direct_equal,
8743 NULL,
8744 (GDestroyNotify)g_free);
8745 }
8746 g_hash_table_insert (file->details->extension_attributes,
8747 GINT_TO_POINTER (g_quark_from_string (attribute_name)),
8748 g_strdup (value));
8749 }
8750
8751 caja_file_changed (file);
8752 }
8753
8754 static void
caja_file_invalidate_extension_info(CajaFile * file)8755 caja_file_invalidate_extension_info (CajaFile *file)
8756 {
8757 caja_file_invalidate_attributes (file, CAJA_FILE_ATTRIBUTE_EXTENSION_INFO);
8758 }
8759
8760 void
caja_file_info_providers_done(CajaFile * file)8761 caja_file_info_providers_done (CajaFile *file)
8762 {
8763 g_list_free_full (file->details->extension_emblems, g_free);
8764 file->details->extension_emblems = file->details->pending_extension_emblems;
8765 file->details->pending_extension_emblems = NULL;
8766
8767 if (file->details->extension_attributes) {
8768 g_hash_table_destroy (file->details->extension_attributes);
8769 }
8770
8771 file->details->extension_attributes = file->details->pending_extension_attributes;
8772 file->details->pending_extension_attributes = NULL;
8773
8774 caja_file_changed (file);
8775 }
8776
8777 static void
caja_file_info_iface_init(CajaFileInfoIface * iface)8778 caja_file_info_iface_init (CajaFileInfoIface *iface)
8779 {
8780 iface->is_gone = caja_file_is_gone;
8781 iface->get_name = caja_file_get_name;
8782 iface->get_file_type = caja_file_get_file_type;
8783 iface->get_location = caja_file_get_location;
8784 iface->get_uri = caja_file_get_uri;
8785 iface->get_parent_location = caja_file_get_parent_location;
8786 iface->get_parent_uri = caja_file_get_parent_uri;
8787 iface->get_parent_info = caja_file_get_parent;
8788 iface->get_mount = caja_file_get_mount;
8789 iface->get_uri_scheme = caja_file_get_uri_scheme;
8790 iface->get_activation_uri = caja_file_get_activation_uri;
8791 iface->get_mime_type = caja_file_get_mime_type;
8792 iface->is_mime_type = caja_file_is_mime_type;
8793 iface->is_directory = caja_file_is_directory;
8794 iface->can_write = caja_file_can_write;
8795 iface->add_emblem = caja_file_add_emblem;
8796 iface->get_string_attribute = caja_file_get_string_attribute;
8797 iface->add_string_attribute = caja_file_add_string_attribute;
8798 iface->invalidate_extension_info = caja_file_invalidate_extension_info;
8799 }
8800
8801 #if !defined (CAJA_OMIT_SELF_CHECK)
8802
8803 void
caja_self_check_file(void)8804 caja_self_check_file (void)
8805 {
8806 CajaFile *file_1;
8807 CajaFile *file_2;
8808 GList *list;
8809
8810 /* refcount checks */
8811
8812 EEL_CHECK_INTEGER_RESULT (caja_directory_number_outstanding (), 0);
8813
8814 file_1 = caja_file_get_by_uri ("file:///home/");
8815
8816 EEL_CHECK_INTEGER_RESULT (G_OBJECT (file_1)->ref_count, 1);
8817 EEL_CHECK_INTEGER_RESULT (G_OBJECT (file_1->details->directory)->ref_count, 1);
8818 EEL_CHECK_INTEGER_RESULT (caja_directory_number_outstanding (), 1);
8819
8820 caja_file_unref (file_1);
8821
8822 EEL_CHECK_INTEGER_RESULT (caja_directory_number_outstanding (), 0);
8823
8824 file_1 = caja_file_get_by_uri ("file:///etc");
8825 file_2 = caja_file_get_by_uri ("file:///usr");
8826
8827 list = NULL;
8828 list = g_list_prepend (list, file_1);
8829 list = g_list_prepend (list, file_2);
8830
8831 caja_file_list_ref (list);
8832
8833 EEL_CHECK_INTEGER_RESULT (G_OBJECT (file_1)->ref_count, 2);
8834 EEL_CHECK_INTEGER_RESULT (G_OBJECT (file_2)->ref_count, 2);
8835
8836 caja_file_list_unref (list);
8837
8838 EEL_CHECK_INTEGER_RESULT (G_OBJECT (file_1)->ref_count, 1);
8839 EEL_CHECK_INTEGER_RESULT (G_OBJECT (file_2)->ref_count, 1);
8840
8841 caja_file_list_free (list);
8842
8843 EEL_CHECK_INTEGER_RESULT (caja_directory_number_outstanding (), 0);
8844
8845
8846 /* name checks */
8847 file_1 = caja_file_get_by_uri ("file:///home/");
8848
8849 EEL_CHECK_STRING_RESULT (caja_file_get_name (file_1), "home");
8850
8851 EEL_CHECK_BOOLEAN_RESULT (caja_file_get_by_uri ("file:///home/") == file_1, TRUE);
8852 caja_file_unref (file_1);
8853
8854 EEL_CHECK_BOOLEAN_RESULT (caja_file_get_by_uri ("file:///home") == file_1, TRUE);
8855 caja_file_unref (file_1);
8856
8857 caja_file_unref (file_1);
8858
8859 file_1 = caja_file_get_by_uri ("file:///home");
8860 EEL_CHECK_STRING_RESULT (caja_file_get_name (file_1), "home");
8861 caja_file_unref (file_1);
8862
8863 #if 0
8864 /* ALEX: I removed this, because it was breaking distchecks.
8865 * It used to work, but when canonical uris changed from
8866 * foo: to foo:/// it broke. I don't expect it to matter
8867 * in real life */
8868 file_1 = caja_file_get_by_uri (":");
8869 EEL_CHECK_STRING_RESULT (caja_file_get_name (file_1), ":");
8870 caja_file_unref (file_1);
8871 #endif
8872
8873 file_1 = caja_file_get_by_uri ("eazel:");
8874 EEL_CHECK_STRING_RESULT (caja_file_get_name (file_1), "eazel");
8875 caja_file_unref (file_1);
8876
8877 /* sorting */
8878 file_1 = caja_file_get_by_uri ("file:///etc");
8879 file_2 = caja_file_get_by_uri ("file:///usr");
8880
8881 EEL_CHECK_INTEGER_RESULT (G_OBJECT (file_1)->ref_count, 1);
8882 EEL_CHECK_INTEGER_RESULT (G_OBJECT (file_2)->ref_count, 1);
8883
8884 EEL_CHECK_BOOLEAN_RESULT (caja_file_compare_for_sort (file_1, file_2, CAJA_FILE_SORT_BY_DISPLAY_NAME, FALSE, FALSE) < 0, TRUE);
8885 EEL_CHECK_BOOLEAN_RESULT (caja_file_compare_for_sort (file_1, file_2, CAJA_FILE_SORT_BY_DISPLAY_NAME, FALSE, TRUE) > 0, TRUE);
8886 EEL_CHECK_BOOLEAN_RESULT (caja_file_compare_for_sort (file_1, file_1, CAJA_FILE_SORT_BY_DISPLAY_NAME, FALSE, FALSE) == 0, TRUE);
8887 EEL_CHECK_BOOLEAN_RESULT (caja_file_compare_for_sort (file_1, file_1, CAJA_FILE_SORT_BY_DISPLAY_NAME, TRUE, FALSE) == 0, TRUE);
8888 EEL_CHECK_BOOLEAN_RESULT (caja_file_compare_for_sort (file_1, file_1, CAJA_FILE_SORT_BY_DISPLAY_NAME, FALSE, TRUE) == 0, TRUE);
8889 EEL_CHECK_BOOLEAN_RESULT (caja_file_compare_for_sort (file_1, file_1, CAJA_FILE_SORT_BY_DISPLAY_NAME, TRUE, TRUE) == 0, TRUE);
8890
8891 caja_file_unref (file_1);
8892 caja_file_unref (file_2);
8893 }
8894
8895 #endif /* !CAJA_OMIT_SELF_CHECK */
8896