1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
2 
3    caja-directory.c: Caja directory 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 <gtk/gtk.h>
27 #include <string.h>
28 
29 #include <eel/eel-glib-extensions.h>
30 #include <eel/eel-gtk-macros.h>
31 
32 #include "caja-directory-private.h"
33 #include "caja-directory-notify.h"
34 #include "caja-file-attributes.h"
35 #include "caja-file-private.h"
36 #include "caja-file-utilities.h"
37 #include "caja-search-directory.h"
38 #include "caja-global-preferences.h"
39 #include "caja-lib-self-check-functions.h"
40 #include "caja-metadata.h"
41 #include "caja-desktop-directory.h"
42 #include "caja-vfs-directory.h"
43 
44 enum
45 {
46     FILES_ADDED,
47     FILES_CHANGED,
48     DONE_LOADING,
49     LOAD_ERROR,
50     LAST_SIGNAL
51 };
52 
53 static guint signals[LAST_SIGNAL] = { 0 };
54 
55 static GHashTable *directories;
56 
57 static void               caja_directory_finalize         (GObject                *object);
58 static CajaDirectory *caja_directory_new              (GFile                  *location);
59 static char *             real_get_name_for_self_as_new_file  (CajaDirectory      *directory);
60 static GList *            real_get_file_list                  (CajaDirectory      *directory);
61 static gboolean		  real_is_editable                    (CajaDirectory      *directory);
62 static void               set_directory_location              (CajaDirectory      *directory,
63         GFile                  *location);
64 
G_DEFINE_TYPE_WITH_PRIVATE(CajaDirectory,caja_directory,G_TYPE_OBJECT)65 G_DEFINE_TYPE_WITH_PRIVATE (CajaDirectory, caja_directory, G_TYPE_OBJECT)
66 
67 static void
68 caja_directory_class_init (CajaDirectoryClass *klass)
69 {
70     GObjectClass *object_class;
71 
72     object_class = G_OBJECT_CLASS (klass);
73 
74     object_class->finalize = caja_directory_finalize;
75 
76     signals[FILES_ADDED] =
77         g_signal_new ("files_added",
78                       G_TYPE_FROM_CLASS (object_class),
79                       G_SIGNAL_RUN_LAST,
80                       G_STRUCT_OFFSET (CajaDirectoryClass, files_added),
81                       NULL, NULL,
82                       g_cclosure_marshal_VOID__POINTER,
83                       G_TYPE_NONE, 1, G_TYPE_POINTER);
84     signals[FILES_CHANGED] =
85         g_signal_new ("files_changed",
86                       G_TYPE_FROM_CLASS (object_class),
87                       G_SIGNAL_RUN_LAST,
88                       G_STRUCT_OFFSET (CajaDirectoryClass, files_changed),
89                       NULL, NULL,
90                       g_cclosure_marshal_VOID__POINTER,
91                       G_TYPE_NONE, 1, G_TYPE_POINTER);
92     signals[DONE_LOADING] =
93         g_signal_new ("done_loading",
94                       G_TYPE_FROM_CLASS (object_class),
95                       G_SIGNAL_RUN_LAST,
96                       G_STRUCT_OFFSET (CajaDirectoryClass, done_loading),
97                       NULL, NULL,
98                       g_cclosure_marshal_VOID__VOID,
99                       G_TYPE_NONE, 0);
100     signals[LOAD_ERROR] =
101         g_signal_new ("load_error",
102                       G_TYPE_FROM_CLASS (object_class),
103                       G_SIGNAL_RUN_LAST,
104                       G_STRUCT_OFFSET (CajaDirectoryClass, load_error),
105                       NULL, NULL,
106                       g_cclosure_marshal_VOID__POINTER,
107                       G_TYPE_NONE, 1, G_TYPE_POINTER);
108 
109     klass->get_name_for_self_as_new_file = real_get_name_for_self_as_new_file;
110     klass->get_file_list = real_get_file_list;
111     klass->is_editable = real_is_editable;
112 }
113 
114 static void
caja_directory_init(CajaDirectory * directory)115 caja_directory_init (CajaDirectory *directory)
116 {
117     directory->details = caja_directory_get_instance_private (directory);
118     directory->details->file_hash = g_hash_table_new (g_str_hash, g_str_equal);
119     directory->details->high_priority_queue = caja_file_queue_new ();
120     directory->details->low_priority_queue = caja_file_queue_new ();
121     directory->details->extension_queue = caja_file_queue_new ();
122     directory->details->free_space = (guint64)-1;
123 }
124 
125 CajaDirectory *
caja_directory_ref(CajaDirectory * directory)126 caja_directory_ref (CajaDirectory *directory)
127 {
128     if (directory == NULL)
129     {
130         return directory;
131     }
132 
133     g_return_val_if_fail (CAJA_IS_DIRECTORY (directory), NULL);
134 
135     g_object_ref (directory);
136     return directory;
137 }
138 
139 void
caja_directory_unref(CajaDirectory * directory)140 caja_directory_unref (CajaDirectory *directory)
141 {
142     if (directory == NULL)
143     {
144         return;
145     }
146 
147     g_return_if_fail (CAJA_IS_DIRECTORY (directory));
148 
149     g_object_unref (directory);
150 }
151 
152 static void
caja_directory_finalize(GObject * object)153 caja_directory_finalize (GObject *object)
154 {
155     CajaDirectory *directory;
156 
157     directory = CAJA_DIRECTORY (object);
158 
159     g_hash_table_remove (directories, directory->details->location);
160 
161     caja_directory_cancel (directory);
162     g_assert (directory->details->count_in_progress == NULL);
163     g_assert (directory->details->top_left_read_state == NULL);
164 
165     if (directory->details->monitor_list != NULL)
166     {
167         g_warning ("destroying a CajaDirectory while it's being monitored");
168         g_list_free_full (directory->details->monitor_list, g_free);
169     }
170 
171     if (directory->details->monitor != NULL)
172     {
173         caja_monitor_cancel (directory->details->monitor);
174     }
175 
176     if (directory->details->dequeue_pending_idle_id != 0)
177     {
178         g_source_remove (directory->details->dequeue_pending_idle_id);
179     }
180 
181     if (directory->details->call_ready_idle_id != 0)
182     {
183         g_source_remove (directory->details->call_ready_idle_id);
184     }
185 
186     if (directory->details->location)
187     {
188         g_object_unref (directory->details->location);
189     }
190 
191     g_assert (directory->details->file_list == NULL);
192     g_hash_table_destroy (directory->details->file_hash);
193 
194     caja_file_queue_destroy (directory->details->high_priority_queue);
195     caja_file_queue_destroy (directory->details->low_priority_queue);
196     caja_file_queue_destroy (directory->details->extension_queue);
197     g_assert (directory->details->directory_load_in_progress == NULL);
198     g_assert (directory->details->count_in_progress == NULL);
199     g_assert (directory->details->dequeue_pending_idle_id == 0);
200     g_list_free_full (directory->details->pending_file_info, g_object_unref);
201 
202     G_OBJECT_CLASS (caja_directory_parent_class)->finalize (object);
203 }
204 
205 void
emit_change_signals_for_all_files(CajaDirectory * directory)206 emit_change_signals_for_all_files (CajaDirectory *directory)
207 {
208     GList *files;
209 
210     files = g_list_copy (directory->details->file_list);
211     if (directory->details->as_file != NULL)
212     {
213         files = g_list_prepend (files, directory->details->as_file);
214     }
215 
216     caja_file_list_ref (files);
217     caja_directory_emit_change_signals (directory, files);
218 
219     caja_file_list_free (files);
220 }
221 
222 static void
collect_all_directories(gpointer key,gpointer value,gpointer callback_data)223 collect_all_directories (gpointer key, gpointer value, gpointer callback_data)
224 {
225     CajaDirectory *directory;
226     GList **dirs;
227 
228     directory = CAJA_DIRECTORY (value);
229     dirs = callback_data;
230 
231     *dirs = g_list_prepend (*dirs, caja_directory_ref (directory));
232 }
233 
234 void
emit_change_signals_for_all_files_in_all_directories(void)235 emit_change_signals_for_all_files_in_all_directories (void)
236 {
237     GList *dirs, *l;
238     CajaDirectory *directory = NULL;
239 
240     dirs = NULL;
241     g_hash_table_foreach (directories,
242                           collect_all_directories,
243                           &dirs);
244 
245     for (l = dirs; l != NULL; l = l->next)
246     {
247         directory = CAJA_DIRECTORY (l->data);
248         emit_change_signals_for_all_files (directory);
249         caja_directory_unref (directory);
250     }
251 
252     g_list_free (dirs);
253 }
254 
255 /**
256  * caja_directory_get_by_uri:
257  * @uri: URI of directory to get.
258  *
259  * Get a directory given a uri.
260  * Creates the appropriate subclass given the uri mappings.
261  * Returns a referenced object, not a floating one. Unref when finished.
262  * If two windows are viewing the same uri, the directory object is shared.
263  */
264 CajaDirectory *
caja_directory_get_internal(GFile * location,gboolean create)265 caja_directory_get_internal (GFile *location, gboolean create)
266 {
267     CajaDirectory *directory;
268 
269     /* Create the hash table first time through. */
270     if (directories == NULL) {
271         directories = g_hash_table_new (g_file_hash, (GCompareFunc) g_file_equal);
272         caja_global_preferences_init ();
273     }
274 
275     /* If the object is already in the hash table, look it up. */
276 
277     directory = g_hash_table_lookup (directories,
278                                      location);
279     if (directory != NULL)
280     {
281         caja_directory_ref (directory);
282     }
283     else if (create)
284     {
285         /* Create a new directory object instead. */
286         directory = caja_directory_new (location);
287         if (directory == NULL)
288         {
289             return NULL;
290         }
291 
292         /* Put it in the hash table. */
293         g_hash_table_insert (directories,
294                              directory->details->location,
295                              directory);
296     }
297 
298     return directory;
299 }
300 
301 CajaDirectory *
caja_directory_get(GFile * location)302 caja_directory_get (GFile *location)
303 {
304     if (location == NULL)
305     {
306         return NULL;
307     }
308 
309     return caja_directory_get_internal (location, TRUE);
310 }
311 
312 CajaDirectory *
caja_directory_get_existing(GFile * location)313 caja_directory_get_existing (GFile *location)
314 {
315     if (location == NULL)
316     {
317         return NULL;
318     }
319 
320     return caja_directory_get_internal (location, FALSE);
321 }
322 
323 
324 CajaDirectory *
caja_directory_get_by_uri(const char * uri)325 caja_directory_get_by_uri (const char *uri)
326 {
327     CajaDirectory *directory;
328     GFile *location;
329 
330     if (uri == NULL)
331     {
332         return NULL;
333     }
334 
335     location = g_file_new_for_uri (uri);
336 
337     directory = caja_directory_get_internal (location, TRUE);
338     g_object_unref (location);
339     return directory;
340 }
341 
342 CajaDirectory *
caja_directory_get_for_file(CajaFile * file)343 caja_directory_get_for_file (CajaFile *file)
344 {
345     char *uri;
346     CajaDirectory *directory;
347 
348     g_return_val_if_fail (CAJA_IS_FILE (file), NULL);
349 
350     uri = caja_file_get_uri (file);
351     directory = caja_directory_get_by_uri (uri);
352     g_free (uri);
353     return directory;
354 }
355 
356 /* Returns a reffed CajaFile object for this directory.
357  */
358 CajaFile *
caja_directory_get_corresponding_file(CajaDirectory * directory)359 caja_directory_get_corresponding_file (CajaDirectory *directory)
360 {
361     CajaFile *file;
362 
363     file = caja_directory_get_existing_corresponding_file (directory);
364     if (file == NULL)
365     {
366         char *uri;
367 
368         uri = caja_directory_get_uri (directory);
369         file = caja_file_get_by_uri (uri);
370         g_free (uri);
371     }
372 
373     return file;
374 }
375 
376 /* Returns a reffed CajaFile object for this directory, but only if the
377  * CajaFile object has already been created.
378  */
379 CajaFile *
caja_directory_get_existing_corresponding_file(CajaDirectory * directory)380 caja_directory_get_existing_corresponding_file (CajaDirectory *directory)
381 {
382     CajaFile *file;
383     char *uri;
384 
385     file = directory->details->as_file;
386     if (file != NULL)
387     {
388         caja_file_ref (file);
389         return file;
390     }
391 
392     uri = caja_directory_get_uri (directory);
393     file = caja_file_get_existing_by_uri (uri);
394     g_free (uri);
395     return file;
396 }
397 
398 /* caja_directory_get_name_for_self_as_new_file:
399  *
400  * Get a name to display for the file representing this
401  * directory. This is called only when there's no VFS
402  * directory for this CajaDirectory.
403  */
404 char *
caja_directory_get_name_for_self_as_new_file(CajaDirectory * directory)405 caja_directory_get_name_for_self_as_new_file (CajaDirectory *directory)
406 {
407     g_return_val_if_fail (CAJA_IS_DIRECTORY (directory), NULL);
408 
409     if (CAJA_DIRECTORY_GET_CLASS(directory)->get_name_for_self_as_new_file == NULL)
410             return NULL;
411     else
412             return CAJA_DIRECTORY_GET_CLASS(directory)->get_name_for_self_as_new_file (directory);
413 }
414 
415 static char *
real_get_name_for_self_as_new_file(CajaDirectory * directory)416 real_get_name_for_self_as_new_file (CajaDirectory *directory)
417 {
418     char *directory_uri;
419     char *name, *colon;
420 
421     directory_uri = caja_directory_get_uri (directory);
422 
423     colon = strchr (directory_uri, ':');
424     if (colon == NULL || colon == directory_uri)
425     {
426         name = g_strdup (directory_uri);
427     }
428     else
429     {
430         name = g_strndup (directory_uri, colon - directory_uri);
431     }
432     g_free (directory_uri);
433 
434     return name;
435 }
436 
437 char *
caja_directory_get_uri(CajaDirectory * directory)438 caja_directory_get_uri (CajaDirectory *directory)
439 {
440     g_return_val_if_fail (CAJA_IS_DIRECTORY (directory), NULL);
441 
442     return g_file_get_uri (directory->details->location);
443 }
444 
445 GFile *
caja_directory_get_location(CajaDirectory * directory)446 caja_directory_get_location (CajaDirectory  *directory)
447 {
448     g_return_val_if_fail (CAJA_IS_DIRECTORY (directory), NULL);
449 
450     return g_object_ref (directory->details->location);
451 }
452 
453 static CajaDirectory *
caja_directory_new(GFile * location)454 caja_directory_new (GFile *location)
455 {
456     CajaDirectory *directory;
457     char *uri;
458 
459     uri = g_file_get_uri (location);
460 
461     if (eel_uri_is_desktop (uri))
462     {
463         directory = CAJA_DIRECTORY (g_object_new (CAJA_TYPE_DESKTOP_DIRECTORY, NULL));
464     }
465     else if (eel_uri_is_search (uri))
466     {
467         directory = CAJA_DIRECTORY (g_object_new (CAJA_TYPE_SEARCH_DIRECTORY, NULL));
468     }
469     else if (g_str_has_suffix (uri, CAJA_SAVED_SEARCH_EXTENSION))
470     {
471         directory = CAJA_DIRECTORY (caja_search_directory_new_from_saved_search (uri));
472     }
473     else
474     {
475         directory = CAJA_DIRECTORY (g_object_new (CAJA_TYPE_VFS_DIRECTORY, NULL));
476     }
477 
478     set_directory_location (directory, location);
479 
480     g_free (uri);
481 
482     return directory;
483 }
484 
485 gboolean
caja_directory_is_local(CajaDirectory * directory)486 caja_directory_is_local (CajaDirectory *directory)
487 {
488     g_return_val_if_fail (CAJA_IS_DIRECTORY (directory), FALSE);
489 
490     if (directory->details->location == NULL)
491     {
492         return TRUE;
493     }
494 
495     return caja_directory_is_in_trash (directory) ||
496            g_file_is_native (directory->details->location);
497 }
498 
499 gboolean
caja_directory_is_in_trash(CajaDirectory * directory)500 caja_directory_is_in_trash (CajaDirectory *directory)
501 {
502     g_assert (CAJA_IS_DIRECTORY (directory));
503 
504     if (directory->details->location == NULL)
505     {
506         return FALSE;
507     }
508 
509     return g_file_has_uri_scheme (directory->details->location, "trash");
510 }
511 
512 gboolean
caja_directory_are_all_files_seen(CajaDirectory * directory)513 caja_directory_are_all_files_seen (CajaDirectory *directory)
514 {
515     g_return_val_if_fail (CAJA_IS_DIRECTORY (directory), FALSE);
516 
517     if (CAJA_DIRECTORY_GET_CLASS(directory)->are_all_files_seen == NULL)
518     {
519         return FALSE;
520     } else {
521         return CAJA_DIRECTORY_GET_CLASS(directory)->are_all_files_seen (directory);
522     }
523 }
524 
525 static void
add_to_hash_table(CajaDirectory * directory,CajaFile * file,GList * node)526 add_to_hash_table (CajaDirectory *directory, CajaFile *file, GList *node)
527 {
528     const char *name;
529 
530     name = file->details->name;
531 
532     g_assert (node != NULL);
533     g_assert (g_hash_table_lookup (directory->details->file_hash,
534                                    name) == NULL);
535     g_hash_table_insert (directory->details->file_hash, (char *) name, node);
536 }
537 
538 static GList *
extract_from_hash_table(CajaDirectory * directory,CajaFile * file)539 extract_from_hash_table (CajaDirectory *directory, CajaFile *file)
540 {
541     const char *name;
542     GList *node;
543 
544     name = file->details->name;
545     if (name == NULL)
546     {
547         return NULL;
548     }
549 
550     /* Find the list node in the hash table. */
551     node = g_hash_table_lookup (directory->details->file_hash, name);
552     g_hash_table_remove (directory->details->file_hash, name);
553 
554     return node;
555 }
556 
557 void
caja_directory_add_file(CajaDirectory * directory,CajaFile * file)558 caja_directory_add_file (CajaDirectory *directory, CajaFile *file)
559 {
560     GList *node;
561     gboolean add_to_work_queue;
562 
563     g_assert (CAJA_IS_DIRECTORY (directory));
564     g_assert (CAJA_IS_FILE (file));
565     g_assert (file->details->name != NULL);
566 
567     /* Add to list. */
568     node = g_list_prepend (directory->details->file_list, file);
569     directory->details->file_list = node;
570 
571     /* Add to hash table. */
572     add_to_hash_table (directory, file, node);
573 
574     directory->details->confirmed_file_count++;
575 
576     add_to_work_queue = FALSE;
577     if (caja_directory_is_file_list_monitored (directory))
578     {
579         /* Ref if we are monitoring, since monitoring owns the file list. */
580         caja_file_ref (file);
581         add_to_work_queue = TRUE;
582     }
583     else if (caja_directory_has_active_request_for_file (directory, file))
584     {
585         /* We're waiting for the file in a call_when_ready. Make sure
586            we add the file to the work queue so that said waiter won't
587            wait forever for e.g. all files in the directory to be done */
588         add_to_work_queue = TRUE;
589     }
590 
591     if (add_to_work_queue)
592     {
593         caja_directory_add_file_to_work_queue (directory, file);
594     }
595 }
596 
597 void
caja_directory_remove_file(CajaDirectory * directory,CajaFile * file)598 caja_directory_remove_file (CajaDirectory *directory, CajaFile *file)
599 {
600     GList *node;
601 
602     g_assert (CAJA_IS_DIRECTORY (directory));
603     g_assert (CAJA_IS_FILE (file));
604     g_assert (file->details->name != NULL);
605 
606     /* Find the list node in the hash table. */
607     node = extract_from_hash_table (directory, file);
608     g_assert (node != NULL);
609     g_assert (node->data == file);
610 
611     /* Remove the item from the list. */
612     directory->details->file_list = g_list_remove_link
613                                     (directory->details->file_list, node);
614     g_list_free_1 (node);
615 
616     caja_directory_remove_file_from_work_queue (directory, file);
617 
618     if (!file->details->unconfirmed)
619     {
620         directory->details->confirmed_file_count--;
621     }
622 
623     /* Unref if we are monitoring. */
624     if (caja_directory_is_file_list_monitored (directory))
625     {
626         caja_file_unref (file);
627     }
628 }
629 
630 GList *
caja_directory_begin_file_name_change(CajaDirectory * directory,CajaFile * file)631 caja_directory_begin_file_name_change (CajaDirectory *directory,
632                                        CajaFile *file)
633 {
634     /* Find the list node in the hash table. */
635     return extract_from_hash_table (directory, file);
636 }
637 
638 void
caja_directory_end_file_name_change(CajaDirectory * directory,CajaFile * file,GList * node)639 caja_directory_end_file_name_change (CajaDirectory *directory,
640                                      CajaFile *file,
641                                      GList *node)
642 {
643     /* Add the list node to the hash table. */
644     if (node != NULL)
645     {
646         add_to_hash_table (directory, file, node);
647     }
648 }
649 
650 CajaFile *
caja_directory_find_file_by_name(CajaDirectory * directory,const char * name)651 caja_directory_find_file_by_name (CajaDirectory *directory,
652                                   const char *name)
653 {
654     GList *node;
655 
656     g_return_val_if_fail (CAJA_IS_DIRECTORY (directory), NULL);
657     g_return_val_if_fail (name != NULL, NULL);
658 
659     node = g_hash_table_lookup (directory->details->file_hash,
660                                 name);
661     return node == NULL ? NULL : CAJA_FILE (node->data);
662 }
663 
664 void
caja_directory_emit_files_added(CajaDirectory * directory,GList * added_files)665 caja_directory_emit_files_added (CajaDirectory *directory,
666                                  GList *added_files)
667 {
668     if (added_files != NULL)
669     {
670         g_signal_emit (directory,
671                        signals[FILES_ADDED], 0,
672                        added_files);
673     }
674 }
675 
676 void
caja_directory_emit_files_changed(CajaDirectory * directory,GList * changed_files)677 caja_directory_emit_files_changed (CajaDirectory *directory,
678                                    GList *changed_files)
679 {
680     if (changed_files != NULL)
681     {
682         g_signal_emit (directory,
683                        signals[FILES_CHANGED], 0,
684                        changed_files);
685     }
686 }
687 
688 void
caja_directory_emit_change_signals(CajaDirectory * directory,GList * changed_files)689 caja_directory_emit_change_signals (CajaDirectory *directory,
690                                     GList *changed_files)
691 {
692     GList *p;
693 
694     for (p = changed_files; p != NULL; p = p->next)
695     {
696         caja_file_emit_changed (p->data);
697     }
698     caja_directory_emit_files_changed (directory, changed_files);
699 }
700 
701 void
caja_directory_emit_done_loading(CajaDirectory * directory)702 caja_directory_emit_done_loading (CajaDirectory *directory)
703 {
704     g_signal_emit (directory,
705                    signals[DONE_LOADING], 0);
706 }
707 
708 void
caja_directory_emit_load_error(CajaDirectory * directory,GError * error)709 caja_directory_emit_load_error (CajaDirectory *directory,
710                                 GError *error)
711 {
712     g_signal_emit (directory,
713                    signals[LOAD_ERROR], 0,
714                    error);
715 }
716 
717 /* Return a directory object for this one's parent. */
718 static CajaDirectory *
get_parent_directory(GFile * location)719 get_parent_directory (GFile *location)
720 {
721     GFile *parent;
722 
723     parent = g_file_get_parent (location);
724     if (parent)
725     {
726         CajaDirectory *directory;
727 
728         directory = caja_directory_get_internal (parent, TRUE);
729         g_object_unref (parent);
730         return directory;
731     }
732     return NULL;
733 }
734 
735 /* If a directory object exists for this one's parent, then
736  * return it, otherwise return NULL.
737  */
738 static CajaDirectory *
get_parent_directory_if_exists(GFile * location)739 get_parent_directory_if_exists (GFile *location)
740 {
741     GFile *parent;
742 
743     parent = g_file_get_parent (location);
744     if (parent)
745     {
746         CajaDirectory *directory;
747 
748         directory = caja_directory_get_internal (parent, FALSE);
749         g_object_unref (parent);
750         return directory;
751     }
752     return NULL;
753 }
754 
755 static void
hash_table_list_prepend(GHashTable * table,gconstpointer key,gpointer data)756 hash_table_list_prepend (GHashTable *table, gconstpointer key, gpointer data)
757 {
758     GList *list;
759 
760     list = g_hash_table_lookup (table, key);
761     list = g_list_prepend (list, data);
762     g_hash_table_insert (table, (gpointer) key, list);
763 }
764 
765 static void
call_files_added_free_list(gpointer key,gpointer value,gpointer user_data)766 call_files_added_free_list (gpointer key, gpointer value, gpointer user_data)
767 {
768     g_assert (CAJA_IS_DIRECTORY (key));
769     g_assert (value != NULL);
770     g_assert (user_data == NULL);
771 
772     g_signal_emit (key,
773                    signals[FILES_ADDED], 0,
774                    value);
775     g_list_free (value);
776 }
777 
778 static void
call_files_changed_common(CajaDirectory * directory,GList * file_list)779 call_files_changed_common (CajaDirectory *directory, GList *file_list)
780 {
781     GList *node;
782     CajaFile *file = NULL;
783 
784     for (node = file_list; node != NULL; node = node->next)
785     {
786         file = node->data;
787         if (file->details->directory == directory)
788         {
789             caja_directory_add_file_to_work_queue (directory,
790                                                    file);
791         }
792     }
793     caja_directory_async_state_changed (directory);
794     caja_directory_emit_change_signals (directory, file_list);
795 }
796 
797 static void
call_files_changed_free_list(gpointer key,gpointer value,gpointer user_data)798 call_files_changed_free_list (gpointer key, gpointer value, gpointer user_data)
799 {
800     g_assert (value != NULL);
801     g_assert (user_data == NULL);
802 
803     call_files_changed_common (CAJA_DIRECTORY (key), value);
804     g_list_free (value);
805 }
806 
807 static void
call_files_changed_unref_free_list(gpointer key,gpointer value,gpointer user_data)808 call_files_changed_unref_free_list (gpointer key, gpointer value, gpointer user_data)
809 {
810     g_assert (value != NULL);
811     g_assert (user_data == NULL);
812 
813     call_files_changed_common (CAJA_DIRECTORY (key), value);
814     caja_file_list_free (value);
815 }
816 
817 static void
call_get_file_info_free_list(gpointer key,gpointer value,gpointer user_data)818 call_get_file_info_free_list (gpointer key, gpointer value, gpointer user_data)
819 {
820     CajaDirectory *directory;
821     GList *files;
822 
823     g_assert (CAJA_IS_DIRECTORY (key));
824     g_assert (value != NULL);
825     g_assert (user_data == NULL);
826 
827     directory = key;
828     files = value;
829 
830     caja_directory_get_info_for_new_files (directory, files);
831     g_list_free_full (files, g_object_unref);
832 }
833 
834 static void
invalidate_count_and_unref(gpointer key,gpointer value,gpointer user_data)835 invalidate_count_and_unref (gpointer key, gpointer value, gpointer user_data)
836 {
837     g_assert (CAJA_IS_DIRECTORY (key));
838     g_assert (value == key);
839     g_assert (user_data == NULL);
840 
841     caja_directory_invalidate_count_and_mime_list (key);
842     caja_directory_unref (key);
843 }
844 
845 static void
collect_parent_directories(GHashTable * hash_table,CajaDirectory * directory)846 collect_parent_directories (GHashTable *hash_table, CajaDirectory *directory)
847 {
848     g_assert (hash_table != NULL);
849     g_assert (CAJA_IS_DIRECTORY (directory));
850 
851     if (g_hash_table_lookup (hash_table, directory) == NULL)
852     {
853         caja_directory_ref (directory);
854         g_hash_table_insert  (hash_table, directory, directory);
855     }
856 }
857 
858 void
caja_directory_notify_files_added(GList * files)859 caja_directory_notify_files_added (GList *files)
860 {
861     GHashTable *added_lists;
862     GList *p;
863     GHashTable *parent_directories;
864     CajaFile *file;
865     GFile *parent;
866     CajaDirectory *directory = NULL;
867     GFile *location = NULL;
868 
869     /* Make a list of added files in each directory. */
870     added_lists = g_hash_table_new (NULL, NULL);
871 
872     /* Make a list of parent directories that will need their counts updated. */
873     parent_directories = g_hash_table_new (NULL, NULL);
874 
875     for (p = files; p != NULL; p = p->next)
876     {
877         location = p->data;
878 
879         /* See if the directory is already known. */
880         directory = get_parent_directory_if_exists (location);
881         if (directory == NULL)
882         {
883             /* In case the directory is not being
884              * monitored, but the corresponding file is,
885              * we must invalidate it's item count.
886              */
887 
888 
889             file = NULL;
890             parent = g_file_get_parent (location);
891             if (parent)
892             {
893                 file = caja_file_get_existing (parent);
894                 g_object_unref (parent);
895             }
896 
897             if (file != NULL)
898             {
899                 caja_file_invalidate_count_and_mime_list (file);
900                 caja_file_unref (file);
901             }
902 
903             continue;
904         }
905 
906         collect_parent_directories (parent_directories, directory);
907 
908         /* If no one is monitoring files in the directory, nothing to do. */
909         if (!caja_directory_is_file_list_monitored (directory))
910         {
911             caja_directory_unref (directory);
912             continue;
913         }
914 
915         file = caja_file_get_existing (location);
916         /* We check is_added here, because the file could have been added
917          * to the directory by a caja_file_get() but not gotten
918          * files_added emitted
919          */
920         if (file && file->details->is_added)
921         {
922             /* A file already exists, it was probably renamed.
923              * If it was renamed this could be ignored, but
924              * queue a change just in case */
925             caja_file_changed (file);
926         }
927         else
928         {
929             hash_table_list_prepend (added_lists,
930                                      directory,
931                                      g_object_ref (location));
932         }
933         caja_file_unref (file);
934         caja_directory_unref (directory);
935     }
936 
937     /* Now get file info for the new files. This creates CajaFile
938      * objects for the new files, and sends out a files_added signal.
939      */
940     g_hash_table_foreach (added_lists, call_get_file_info_free_list, NULL);
941     g_hash_table_destroy (added_lists);
942 
943     /* Invalidate count for each parent directory. */
944     g_hash_table_foreach (parent_directories, invalidate_count_and_unref, NULL);
945     g_hash_table_destroy (parent_directories);
946 }
947 
948 void
caja_directory_notify_files_changed(GList * files)949 caja_directory_notify_files_changed (GList *files)
950 {
951     GHashTable *changed_lists;
952     GList *node;
953     GFile *location = NULL;
954     CajaFile *file = NULL;
955 
956     /* Make a list of changed files in each directory. */
957     changed_lists = g_hash_table_new (NULL, NULL);
958 
959     /* Go through all the notifications. */
960     for (node = files; node != NULL; node = node->next)
961     {
962         location = node->data;
963 
964         /* Find the file. */
965         file = caja_file_get_existing (location);
966         if (file != NULL)
967         {
968             /* Tell it to re-get info now, and later emit
969              * a changed signal.
970              */
971             file->details->file_info_is_up_to_date = FALSE;
972             file->details->top_left_text_is_up_to_date = FALSE;
973             file->details->link_info_is_up_to_date = FALSE;
974             caja_file_invalidate_extension_info_internal (file);
975 
976             hash_table_list_prepend (changed_lists,
977                                      file->details->directory,
978                                      file);
979         }
980     }
981     /* Now send out the changed signals. */
982     g_hash_table_foreach (changed_lists, call_files_changed_unref_free_list, NULL);
983     g_hash_table_destroy (changed_lists);
984 }
985 
986 void
caja_directory_notify_files_removed(GList * files)987 caja_directory_notify_files_removed (GList *files)
988 {
989     GHashTable *changed_lists;
990     GList *p;
991     GHashTable *parent_directories;
992     CajaDirectory *directory = NULL;
993     CajaFile *file = NULL;
994     GFile *location = NULL;
995 
996     /* Make a list of changed files in each directory. */
997     changed_lists = g_hash_table_new (NULL, NULL);
998 
999     /* Make a list of parent directories that will need their counts updated. */
1000     parent_directories = g_hash_table_new (NULL, NULL);
1001 
1002     /* Go through all the notifications. */
1003     for (p = files; p != NULL; p = p->next)
1004     {
1005         location = p->data;
1006 
1007         /* Update file count for parent directory if anyone might care. */
1008         directory = get_parent_directory_if_exists (location);
1009         if (directory != NULL)
1010         {
1011             collect_parent_directories (parent_directories, directory);
1012             caja_directory_unref (directory);
1013         }
1014 
1015         /* Find the file. */
1016         file = caja_file_get_existing (location);
1017         if (file != NULL && !caja_file_rename_in_progress (file))
1018         {
1019             /* Mark it gone and prepare to send the changed signal. */
1020             caja_file_mark_gone (file);
1021             hash_table_list_prepend (changed_lists,
1022                                      file->details->directory,
1023                                      caja_file_ref (file));
1024         }
1025         caja_file_unref (file);
1026     }
1027     /* Now send out the changed signals. */
1028     g_hash_table_foreach (changed_lists, call_files_changed_unref_free_list, NULL);
1029     g_hash_table_destroy (changed_lists);
1030 
1031     /* Invalidate count for each parent directory. */
1032     g_hash_table_foreach (parent_directories, invalidate_count_and_unref, NULL);
1033     g_hash_table_destroy (parent_directories);
1034 }
1035 
1036 static void
set_directory_location(CajaDirectory * directory,GFile * location)1037 set_directory_location (CajaDirectory *directory,
1038                         GFile *location)
1039 {
1040     if (directory->details->location)
1041     {
1042         g_object_unref (directory->details->location);
1043     }
1044     directory->details->location = g_object_ref (location);
1045 
1046 }
1047 
1048 static void
change_directory_location(CajaDirectory * directory,GFile * new_location)1049 change_directory_location (CajaDirectory *directory,
1050                            GFile *new_location)
1051 {
1052     /* I believe it's impossible for a self-owned file/directory
1053      * to be moved. But if that did somehow happen, this function
1054      * wouldn't do enough to handle it.
1055      */
1056     g_assert (directory->details->as_file == NULL);
1057 
1058     g_hash_table_remove (directories,
1059                          directory->details->location);
1060 
1061     set_directory_location (directory, new_location);
1062 
1063     g_hash_table_insert (directories,
1064                          directory->details->location,
1065                          directory);
1066 }
1067 
1068 typedef struct
1069 {
1070     GFile *container;
1071     GList *directories;
1072 } CollectData;
1073 
1074 static void
collect_directories_by_container(gpointer key,gpointer value,gpointer callback_data)1075 collect_directories_by_container (gpointer key, gpointer value, gpointer callback_data)
1076 {
1077     CajaDirectory *directory;
1078     CollectData *collect_data;
1079     GFile *location;
1080 
1081     location = (GFile *) key;
1082     directory = CAJA_DIRECTORY (value);
1083     collect_data = (CollectData *) callback_data;
1084 
1085     if (g_file_has_prefix (location, collect_data->container) ||
1086             g_file_equal (collect_data->container, location))
1087     {
1088         caja_directory_ref (directory);
1089         collect_data->directories =
1090             g_list_prepend (collect_data->directories,
1091                             directory);
1092     }
1093 }
1094 
1095 static GList *
caja_directory_moved_internal(GFile * old_location,GFile * new_location)1096 caja_directory_moved_internal (GFile *old_location,
1097                                GFile *new_location)
1098 {
1099     CollectData collection;
1100     GList *node, *affected_files;
1101     char *relative_path;
1102     CajaDirectory *directory = NULL;
1103     GFile *new_directory_location = NULL;
1104 
1105     collection.container = old_location;
1106     collection.directories = NULL;
1107 
1108     g_hash_table_foreach (directories,
1109                           collect_directories_by_container,
1110                           &collection);
1111 
1112     affected_files = NULL;
1113 
1114     for (node = collection.directories; node != NULL; node = node->next)
1115     {
1116         directory = CAJA_DIRECTORY (node->data);
1117         new_directory_location = NULL;
1118 
1119         if (g_file_equal (directory->details->location, old_location))
1120         {
1121             new_directory_location = g_object_ref (new_location);
1122         }
1123         else
1124         {
1125             relative_path = g_file_get_relative_path (old_location,
1126                             directory->details->location);
1127             if (relative_path != NULL)
1128             {
1129                 new_directory_location = g_file_resolve_relative_path (new_location, relative_path);
1130                 g_free (relative_path);
1131 
1132             }
1133         }
1134 
1135         if (new_directory_location)
1136         {
1137             change_directory_location (directory, new_directory_location);
1138             g_object_unref (new_directory_location);
1139 
1140             /* Collect affected files. */
1141             if (directory->details->as_file != NULL)
1142             {
1143                 affected_files = g_list_prepend
1144                                  (affected_files,
1145                                   caja_file_ref (directory->details->as_file));
1146             }
1147             affected_files = g_list_concat
1148                              (affected_files,
1149                               caja_file_list_copy (directory->details->file_list));
1150         }
1151 
1152         caja_directory_unref (directory);
1153     }
1154 
1155     g_list_free (collection.directories);
1156 
1157     return affected_files;
1158 }
1159 
1160 void
caja_directory_moved(const char * old_uri,const char * new_uri)1161 caja_directory_moved (const char *old_uri,
1162                       const char *new_uri)
1163 {
1164     GList *list, *node;
1165     GHashTable *hash;
1166     GFile *old_location;
1167     GFile *new_location;
1168     CajaFile *file = NULL;
1169 
1170     hash = g_hash_table_new (NULL, NULL);
1171 
1172     old_location = g_file_new_for_uri (old_uri);
1173     new_location = g_file_new_for_uri (new_uri);
1174 
1175     list = caja_directory_moved_internal (old_location, new_location);
1176     for (node = list; node != NULL; node = node->next)
1177     {
1178         file = CAJA_FILE (node->data);
1179         hash_table_list_prepend (hash,
1180                                  file->details->directory,
1181                                  caja_file_ref (file));
1182     }
1183     caja_file_list_free (list);
1184 
1185     g_object_unref (old_location);
1186     g_object_unref (new_location);
1187 
1188     g_hash_table_foreach (hash, call_files_changed_unref_free_list, NULL);
1189     g_hash_table_destroy (hash);
1190 }
1191 
1192 void
caja_directory_notify_files_moved(GList * file_pairs)1193 caja_directory_notify_files_moved (GList *file_pairs)
1194 {
1195     GList *p, *affected_files, *node;
1196     GFilePair *pair;
1197     CajaFile *file;
1198     CajaDirectory *old_directory, *new_directory;
1199     GHashTable *parent_directories;
1200     GList *new_files_list, *unref_list;
1201     GHashTable *added_lists, *changed_lists;
1202     char *name;
1203     CajaFileAttributes cancel_attributes;
1204     GFile *to_location = NULL;
1205     GFile *from_location = NULL;
1206 
1207     /* Make a list of added and changed files in each directory. */
1208     new_files_list = NULL;
1209     added_lists = g_hash_table_new (NULL, NULL);
1210     changed_lists = g_hash_table_new (NULL, NULL);
1211     unref_list = NULL;
1212 
1213     /* Make a list of parent directories that will need their counts updated. */
1214     parent_directories = g_hash_table_new (NULL, NULL);
1215 
1216     cancel_attributes = caja_file_get_all_attributes ();
1217 
1218     for (p = file_pairs; p != NULL; p = p->next)
1219     {
1220         pair = p->data;
1221         from_location = pair->from;
1222         to_location = pair->to;
1223 
1224         /* Handle overwriting a file. */
1225         file = caja_file_get_existing (to_location);
1226         if (file != NULL)
1227         {
1228             /* Mark it gone and prepare to send the changed signal. */
1229             caja_file_mark_gone (file);
1230             new_directory = file->details->directory;
1231             hash_table_list_prepend (changed_lists,
1232                                      new_directory,
1233                                      file);
1234             collect_parent_directories (parent_directories,
1235                                         new_directory);
1236         }
1237 
1238         /* Update any directory objects that are affected. */
1239         affected_files = caja_directory_moved_internal (from_location,
1240                          to_location);
1241         for (node = affected_files; node != NULL; node = node->next)
1242         {
1243             file = CAJA_FILE (node->data);
1244             hash_table_list_prepend (changed_lists,
1245                                      file->details->directory,
1246                                      file);
1247         }
1248         unref_list = g_list_concat (unref_list, affected_files);
1249 
1250         /* Move an existing file. */
1251         file = caja_file_get_existing (from_location);
1252         if (file == NULL)
1253         {
1254             /* Handle this as if it was a new file. */
1255             new_files_list = g_list_prepend (new_files_list,
1256                                              to_location);
1257         }
1258         else
1259         {
1260             /* Handle notification in the old directory. */
1261             old_directory = file->details->directory;
1262             collect_parent_directories (parent_directories, old_directory);
1263 
1264             /* Cancel loading of attributes in the old directory */
1265             caja_directory_cancel_loading_file_attributes
1266             (old_directory, file, cancel_attributes);
1267 
1268             /* Locate the new directory. */
1269             new_directory = get_parent_directory (to_location);
1270             collect_parent_directories (parent_directories, new_directory);
1271             /* We can unref now -- new_directory is in the
1272              * parent directories list so it will be
1273              * around until the end of this function
1274              * anyway.
1275              */
1276             caja_directory_unref (new_directory);
1277 
1278             /* Update the file's name and directory. */
1279             name = g_file_get_basename (to_location);
1280             caja_file_update_name_and_directory
1281             (file, name, new_directory);
1282             g_free (name);
1283 
1284             /* Update file attributes */
1285             caja_file_invalidate_attributes (file, CAJA_FILE_ATTRIBUTE_INFO);
1286 
1287             hash_table_list_prepend (changed_lists,
1288                                      old_directory,
1289                                      file);
1290             if (old_directory != new_directory)
1291             {
1292                 hash_table_list_prepend	(added_lists,
1293                                          new_directory,
1294                                          file);
1295             }
1296 
1297             /* Unref each file once to balance out caja_file_get_by_uri. */
1298             unref_list = g_list_prepend (unref_list, file);
1299         }
1300     }
1301 
1302     /* Now send out the changed and added signals for existing file objects. */
1303     g_hash_table_foreach (changed_lists, call_files_changed_free_list, NULL);
1304     g_hash_table_destroy (changed_lists);
1305     g_hash_table_foreach (added_lists, call_files_added_free_list, NULL);
1306     g_hash_table_destroy (added_lists);
1307 
1308     /* Let the file objects go. */
1309     caja_file_list_free (unref_list);
1310 
1311     /* Invalidate count for each parent directory. */
1312     g_hash_table_foreach (parent_directories, invalidate_count_and_unref, NULL);
1313     g_hash_table_destroy (parent_directories);
1314 
1315     /* Separate handling for brand new file objects. */
1316     caja_directory_notify_files_added (new_files_list);
1317     g_list_free (new_files_list);
1318 }
1319 
1320 void
caja_directory_schedule_position_set(GList * position_setting_list)1321 caja_directory_schedule_position_set (GList *position_setting_list)
1322 {
1323     GList *p;
1324     char str[64];
1325     time_t now;
1326     const CajaFileChangesQueuePosition *item = NULL;
1327     CajaFile *file = NULL;
1328 
1329     time (&now);
1330 
1331     for (p = position_setting_list; p != NULL; p = p->next)
1332     {
1333         item = (CajaFileChangesQueuePosition *) p->data;
1334 
1335         file = caja_file_get (item->location);
1336 
1337         if (item->set)
1338         {
1339             g_snprintf (str, sizeof (str), "%d,%d", item->point.x, item->point.y);
1340         }
1341         else
1342         {
1343             str[0] = 0;
1344         }
1345         caja_file_set_metadata
1346         (file,
1347          CAJA_METADATA_KEY_ICON_POSITION,
1348          NULL,
1349          str);
1350 
1351         if (item->set)
1352         {
1353             caja_file_set_time_metadata
1354             (file,
1355              CAJA_METADATA_KEY_ICON_POSITION_TIMESTAMP,
1356              now);
1357         }
1358         else
1359         {
1360             caja_file_set_time_metadata
1361             (file,
1362              CAJA_METADATA_KEY_ICON_POSITION_TIMESTAMP,
1363              UNDEFINED_TIME);
1364         }
1365 
1366         if (item->set)
1367         {
1368             g_snprintf (str, sizeof (str), "%d", item->screen);
1369         }
1370         else
1371         {
1372             str[0] = 0;
1373         }
1374         caja_file_set_metadata
1375         (file,
1376          CAJA_METADATA_KEY_SCREEN,
1377          NULL,
1378          str);
1379 
1380         caja_file_unref (file);
1381     }
1382 }
1383 
1384 gboolean
caja_directory_contains_file(CajaDirectory * directory,CajaFile * file)1385 caja_directory_contains_file (CajaDirectory *directory,
1386                               CajaFile *file)
1387 {
1388     g_return_val_if_fail (CAJA_IS_DIRECTORY (directory), FALSE);
1389     g_return_val_if_fail (CAJA_IS_FILE (file), FALSE);
1390 
1391     if (caja_file_is_gone (file))
1392     {
1393         return FALSE;
1394     }
1395 
1396     if (CAJA_DIRECTORY_GET_CLASS(directory)->contains_file == NULL)
1397         return FALSE;
1398     else
1399         return CAJA_DIRECTORY_GET_CLASS(directory)->contains_file (directory, file);
1400 }
1401 
1402 void
caja_directory_call_when_ready(CajaDirectory * directory,CajaFileAttributes file_attributes,gboolean wait_for_all_files,CajaDirectoryCallback callback,gpointer callback_data)1403 caja_directory_call_when_ready (CajaDirectory *directory,
1404                                 CajaFileAttributes file_attributes,
1405                                 gboolean wait_for_all_files,
1406                                 CajaDirectoryCallback callback,
1407                                 gpointer callback_data)
1408 {
1409     g_return_if_fail (CAJA_IS_DIRECTORY (directory));
1410     g_return_if_fail (callback != NULL);
1411 
1412     if (CAJA_DIRECTORY_GET_CLASS(directory)->call_when_ready != NULL)
1413     {
1414         CAJA_DIRECTORY_GET_CLASS(directory)->call_when_ready (directory,
1415                                                               file_attributes,
1416                                                               wait_for_all_files,
1417                                                               callback,
1418                                                               callback_data);
1419     }
1420 }
1421 
1422 void
caja_directory_cancel_callback(CajaDirectory * directory,CajaDirectoryCallback callback,gpointer callback_data)1423 caja_directory_cancel_callback (CajaDirectory *directory,
1424                                 CajaDirectoryCallback callback,
1425                                 gpointer callback_data)
1426 {
1427     g_return_if_fail (CAJA_IS_DIRECTORY (directory));
1428     g_return_if_fail (callback != NULL);
1429 
1430     if (CAJA_DIRECTORY_GET_CLASS(directory)->cancel_callback != NULL)
1431     {
1432         CAJA_DIRECTORY_GET_CLASS(directory)->cancel_callback (directory, callback, callback_data);
1433     }
1434 }
1435 
1436 void
caja_directory_file_monitor_add(CajaDirectory * directory,gconstpointer client,gboolean monitor_hidden_files,CajaFileAttributes file_attributes,CajaDirectoryCallback callback,gpointer callback_data)1437 caja_directory_file_monitor_add (CajaDirectory *directory,
1438                                  gconstpointer client,
1439                                  gboolean monitor_hidden_files,
1440                                  CajaFileAttributes file_attributes,
1441                                  CajaDirectoryCallback callback,
1442                                  gpointer callback_data)
1443 {
1444     g_return_if_fail (CAJA_IS_DIRECTORY (directory));
1445     g_return_if_fail (client != NULL);
1446 
1447     if (CAJA_DIRECTORY_GET_CLASS(directory)->file_monitor_add != NULL)
1448     {
1449         CAJA_DIRECTORY_GET_CLASS(directory)->file_monitor_add (directory,
1450                                                                client,
1451                                                                monitor_hidden_files,
1452                                                                file_attributes,
1453                                                                callback, callback_data);
1454     }
1455 }
1456 
1457 void
caja_directory_file_monitor_remove(CajaDirectory * directory,gconstpointer client)1458 caja_directory_file_monitor_remove (CajaDirectory *directory,
1459                                     gconstpointer client)
1460 {
1461     g_return_if_fail (CAJA_IS_DIRECTORY (directory));
1462     g_return_if_fail (client != NULL);
1463 
1464     if (CAJA_DIRECTORY_GET_CLASS(directory)->file_monitor_remove != NULL)
1465     {
1466         CAJA_DIRECTORY_GET_CLASS(directory)->file_monitor_remove (directory, client);
1467     }
1468 }
1469 
1470 void
caja_directory_force_reload(CajaDirectory * directory)1471 caja_directory_force_reload (CajaDirectory *directory)
1472 {
1473     g_return_if_fail (CAJA_IS_DIRECTORY (directory));
1474 
1475     if (CAJA_DIRECTORY_GET_CLASS(directory)->force_reload != NULL)
1476     {
1477         CAJA_DIRECTORY_GET_CLASS(directory)->force_reload (directory);
1478     }
1479 }
1480 
1481 gboolean
caja_directory_is_not_empty(CajaDirectory * directory)1482 caja_directory_is_not_empty (CajaDirectory *directory)
1483 {
1484     g_return_val_if_fail (CAJA_IS_DIRECTORY (directory), FALSE);
1485 
1486     if (CAJA_DIRECTORY_GET_CLASS(directory)->is_not_empty == NULL)
1487     {
1488         return FALSE;
1489     } else {
1490         return CAJA_DIRECTORY_GET_CLASS(directory)->is_not_empty (directory);
1491     }
1492 }
1493 
1494 static gboolean
is_tentative(gpointer data,gpointer callback_data)1495 is_tentative (gpointer data, gpointer callback_data)
1496 {
1497     CajaFile *file;
1498 
1499     g_assert (callback_data == NULL);
1500 
1501     file = CAJA_FILE (data);
1502     /* Avoid returning files with !is_added, because these
1503      * will later be sent with the files_added signal, and a
1504      * user doing get_file_list + files_added monitoring will
1505      * then see the file twice */
1506     return !file->details->got_file_info || !file->details->is_added;
1507 }
1508 
1509 GList *
caja_directory_get_file_list(CajaDirectory * directory)1510 caja_directory_get_file_list (CajaDirectory *directory)
1511 {
1512     if (CAJA_DIRECTORY_GET_CLASS(directory)->get_file_list == NULL)
1513     {
1514         return NULL;
1515     } else {
1516         return CAJA_DIRECTORY_GET_CLASS(directory)->get_file_list (directory);
1517     }
1518 }
1519 
1520 static GList *
real_get_file_list(CajaDirectory * directory)1521 real_get_file_list (CajaDirectory *directory)
1522 {
1523     GList *tentative_files, *non_tentative_files;
1524 
1525     tentative_files = eel_g_list_partition
1526                       (g_list_copy (directory->details->file_list),
1527                        is_tentative, NULL, &non_tentative_files);
1528     g_list_free (tentative_files);
1529 
1530     caja_file_list_ref (non_tentative_files);
1531     return non_tentative_files;
1532 }
1533 
1534 static gboolean
real_is_editable(CajaDirectory * directory)1535 real_is_editable (CajaDirectory *directory)
1536 {
1537     return TRUE;
1538 }
1539 
1540 gboolean
caja_directory_is_editable(CajaDirectory * directory)1541 caja_directory_is_editable (CajaDirectory *directory)
1542 {
1543     if (CAJA_DIRECTORY_GET_CLASS(directory)->is_editable == NULL)
1544     {
1545         return FALSE;
1546     } else {
1547         return CAJA_DIRECTORY_GET_CLASS(directory)->is_editable (directory);
1548     }
1549 }
1550 
1551 GList *
caja_directory_match_pattern(CajaDirectory * directory,const char * pattern)1552 caja_directory_match_pattern (CajaDirectory *directory, const char *pattern)
1553 {
1554     GList *files, *l, *ret;
1555     GPatternSpec *spec;
1556 
1557 
1558     ret = NULL;
1559     spec = g_pattern_spec_new (pattern);
1560 
1561     files = caja_directory_get_file_list (directory);
1562     for (l = files; l; l = l->next)
1563     {
1564         CajaFile *file;
1565         char *name;
1566 
1567         file = CAJA_FILE (l->data);
1568         name = caja_file_get_display_name (file);
1569 
1570         if (g_pattern_match_string (spec, name))
1571         {
1572             ret = g_list_prepend(ret, caja_file_ref (file));
1573         }
1574 
1575         g_free (name);
1576     }
1577 
1578     g_pattern_spec_free (spec);
1579     caja_file_list_free (files);
1580 
1581     return ret;
1582 }
1583 
1584 /**
1585  * caja_directory_list_ref
1586  *
1587  * Ref all the directories in a list.
1588  * @list: GList of directories.
1589  **/
1590 GList *
caja_directory_list_ref(GList * list)1591 caja_directory_list_ref (GList *list)
1592 {
1593     g_list_foreach (list, (GFunc) caja_directory_ref, NULL);
1594     return list;
1595 }
1596 
1597 /**
1598  * caja_directory_list_unref
1599  *
1600  * Unref all the directories in a list.
1601  * @list: GList of directories.
1602  **/
1603 void
caja_directory_list_unref(GList * list)1604 caja_directory_list_unref (GList *list)
1605 {
1606     g_list_foreach (list, (GFunc) caja_directory_unref, NULL);
1607 }
1608 
1609 /**
1610  * caja_directory_list_free
1611  *
1612  * Free a list of directories after unrefing them.
1613  * @list: GList of directories.
1614  **/
1615 void
caja_directory_list_free(GList * list)1616 caja_directory_list_free (GList *list)
1617 {
1618     caja_directory_list_unref (list);
1619     g_list_free (list);
1620 }
1621 
1622 /**
1623  * caja_directory_list_copy
1624  *
1625  * Copy the list of directories, making a new ref of each,
1626  * @list: GList of directories.
1627  **/
1628 GList *
caja_directory_list_copy(GList * list)1629 caja_directory_list_copy (GList *list)
1630 {
1631     return g_list_copy (caja_directory_list_ref (list));
1632 }
1633 
1634 static int
compare_by_uri(CajaDirectory * a,CajaDirectory * b)1635 compare_by_uri (CajaDirectory *a, CajaDirectory *b)
1636 {
1637     char *uri_a, *uri_b;
1638     int res;
1639 
1640     uri_a = g_file_get_uri (a->details->location);
1641     uri_b = g_file_get_uri (b->details->location);
1642 
1643     res = strcmp (uri_a, uri_b);
1644 
1645     g_free (uri_a);
1646     g_free (uri_b);
1647 
1648     return res;
1649 }
1650 
1651 static int
compare_by_uri_cover(gconstpointer a,gconstpointer b)1652 compare_by_uri_cover (gconstpointer a, gconstpointer b)
1653 {
1654     return compare_by_uri (CAJA_DIRECTORY (a), CAJA_DIRECTORY (b));
1655 }
1656 
1657 /**
1658  * caja_directory_list_sort_by_uri
1659  *
1660  * Sort the list of directories by directory uri.
1661  * @list: GList of directories.
1662  **/
1663 GList *
caja_directory_list_sort_by_uri(GList * list)1664 caja_directory_list_sort_by_uri (GList *list)
1665 {
1666     return g_list_sort (list, compare_by_uri_cover);
1667 }
1668 
1669 gboolean
caja_directory_is_desktop_directory(CajaDirectory * directory)1670 caja_directory_is_desktop_directory (CajaDirectory   *directory)
1671 {
1672     if (directory->details->location == NULL)
1673     {
1674         return FALSE;
1675     }
1676 
1677     return caja_is_desktop_directory (directory->details->location);
1678 }
1679 
1680 #if !defined (CAJA_OMIT_SELF_CHECK)
1681 
1682 #include <eel/eel-debug.h>
1683 #include "caja-file-attributes.h"
1684 
1685 static int data_dummy;
1686 static gboolean got_files_flag;
1687 
1688 static void
got_files_callback(CajaDirectory * directory,GList * files,gpointer callback_data)1689 got_files_callback (CajaDirectory *directory, GList *files, gpointer callback_data)
1690 {
1691     g_assert (CAJA_IS_DIRECTORY (directory));
1692     g_assert (g_list_length (files) > 10);
1693     g_assert (callback_data == &data_dummy);
1694 
1695     got_files_flag = TRUE;
1696 }
1697 
1698 /* Return the number of extant CajaDirectories */
1699 int
caja_directory_number_outstanding(void)1700 caja_directory_number_outstanding (void)
1701 {
1702     return directories ? g_hash_table_size (directories) : 0;
1703 }
1704 
1705 void
caja_self_check_directory(void)1706 caja_self_check_directory (void)
1707 {
1708     CajaDirectory *directory;
1709     CajaFile *file;
1710 
1711     directory = caja_directory_get_by_uri ("file:///etc");
1712     file = caja_file_get_by_uri ("file:///etc/passwd");
1713 
1714     EEL_CHECK_INTEGER_RESULT (g_hash_table_size (directories), 1);
1715 
1716     caja_directory_file_monitor_add
1717     (directory, &data_dummy,
1718      TRUE, 0, NULL, NULL);
1719 
1720     /* FIXME: these need to be updated to the new metadata infrastructure
1721      *  as make check doesn't pass.
1722     caja_file_set_metadata (file, "test", "default", "value");
1723     EEL_CHECK_STRING_RESULT (caja_file_get_metadata (file, "test", "default"), "value");
1724 
1725     caja_file_set_boolean_metadata (file, "test_boolean", TRUE, TRUE);
1726     EEL_CHECK_BOOLEAN_RESULT (caja_file_get_boolean_metadata (file, "test_boolean", TRUE), TRUE);
1727     caja_file_set_boolean_metadata (file, "test_boolean", TRUE, FALSE);
1728     EEL_CHECK_BOOLEAN_RESULT (caja_file_get_boolean_metadata (file, "test_boolean", TRUE), FALSE);
1729     EEL_CHECK_BOOLEAN_RESULT (caja_file_get_boolean_metadata (NULL, "test_boolean", TRUE), TRUE);
1730 
1731     caja_file_set_integer_metadata (file, "test_integer", 0, 17);
1732     EEL_CHECK_INTEGER_RESULT (caja_file_get_integer_metadata (file, "test_integer", 0), 17);
1733     caja_file_set_integer_metadata (file, "test_integer", 0, -1);
1734     EEL_CHECK_INTEGER_RESULT (caja_file_get_integer_metadata (file, "test_integer", 0), -1);
1735     caja_file_set_integer_metadata (file, "test_integer", 42, 42);
1736     EEL_CHECK_INTEGER_RESULT (caja_file_get_integer_metadata (file, "test_integer", 42), 42);
1737     EEL_CHECK_INTEGER_RESULT (caja_file_get_integer_metadata (NULL, "test_integer", 42), 42);
1738     EEL_CHECK_INTEGER_RESULT (caja_file_get_integer_metadata (file, "nonexistent_key", 42), 42);
1739     */
1740 
1741     EEL_CHECK_BOOLEAN_RESULT (caja_directory_get_by_uri ("file:///etc") == directory, TRUE);
1742     caja_directory_unref (directory);
1743 
1744     EEL_CHECK_BOOLEAN_RESULT (caja_directory_get_by_uri ("file:///etc/") == directory, TRUE);
1745     caja_directory_unref (directory);
1746 
1747     EEL_CHECK_BOOLEAN_RESULT (caja_directory_get_by_uri ("file:///etc////") == directory, TRUE);
1748     caja_directory_unref (directory);
1749 
1750     caja_file_unref (file);
1751 
1752     caja_directory_file_monitor_remove (directory, &data_dummy);
1753 
1754     caja_directory_unref (directory);
1755 
1756     while (g_hash_table_size (directories) != 0)
1757     {
1758         gtk_main_iteration ();
1759     }
1760 
1761     EEL_CHECK_INTEGER_RESULT (g_hash_table_size (directories), 0);
1762 
1763     directory = caja_directory_get_by_uri ("file:///etc");
1764 
1765     got_files_flag = FALSE;
1766 
1767     caja_directory_call_when_ready (directory,
1768                                     CAJA_FILE_ATTRIBUTE_INFO |
1769                                     CAJA_FILE_ATTRIBUTE_DEEP_COUNTS,
1770                                     TRUE,
1771                                     got_files_callback, &data_dummy);
1772 
1773     while (!got_files_flag)
1774     {
1775         gtk_main_iteration ();
1776     }
1777 
1778     EEL_CHECK_BOOLEAN_RESULT (directory->details->file_list == NULL, TRUE);
1779 
1780     EEL_CHECK_INTEGER_RESULT (g_hash_table_size (directories), 1);
1781 
1782     file = caja_file_get_by_uri ("file:///etc/passwd");
1783 
1784     /* EEL_CHECK_STRING_RESULT (caja_file_get_metadata (file, "test", "default"), "value"); */
1785 
1786     caja_file_unref (file);
1787 
1788     caja_directory_unref (directory);
1789 
1790     EEL_CHECK_INTEGER_RESULT (g_hash_table_size (directories), 0);
1791 }
1792 
1793 #endif /* !CAJA_OMIT_SELF_CHECK */
1794