1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
2 
3    nemo-merged-directory.c: Subclass of NemoDirectory to implement the
4    virtual merged directory.
5 
6    Copyright (C) 1999, 2000 Eazel, Inc.
7 
8    This program is free software; you can redistribute it and/or
9    modify it under the terms of the GNU General Public License as
10    published by the Free Software Foundation; either version 2 of the
11    License, or (at your option) any later version.
12 
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    General Public License for more details.
17 
18    You should have received a copy of the GNU General Public
19    License along with this program; if not, write to the
20    Free Software Foundation, Inc., 51 Franklin Street - Suite 500,
21    Boston, MA 02110-1335, USA.
22 
23    Author: Darin Adler <darin@bentspoon.com>
24 */
25 
26 #include <config.h>
27 #include "nemo-merged-directory.h"
28 
29 #include "nemo-directory-private.h"
30 #include "nemo-directory-notify.h"
31 #include "nemo-file.h"
32 #include <eel/eel-glib-extensions.h>
33 #include <gtk/gtk.h>
34 
35 struct NemoMergedDirectoryDetails {
36 	GList *directories;
37 	GList *directories_not_done_loading;
38 	GHashTable *callbacks;
39 	GHashTable *monitors;
40 };
41 
42 typedef struct {
43 	NemoMergedDirectory *merged;
44 	NemoDirectoryCallback callback;
45 	gpointer callback_data;
46 
47 	NemoFileAttributes wait_for_attributes;
48 	gboolean wait_for_file_list;
49 
50 	GList *non_ready_directories;
51 	GList *merged_file_list;
52 } MergedCallback;
53 
54 typedef struct {
55 	NemoMergedDirectory *merged;
56 
57 	gboolean monitor_hidden_files;
58 	NemoFileAttributes monitor_attributes;
59 } MergedMonitor;
60 
61 enum {
62 	ADD_REAL_DIRECTORY,
63 	REMOVE_REAL_DIRECTORY,
64 	LAST_SIGNAL
65 };
66 
67 static guint signals[LAST_SIGNAL] = { 0 };
68 
69 G_DEFINE_TYPE (NemoMergedDirectory, nemo_merged_directory,
70 	       NEMO_TYPE_DIRECTORY);
71 
72 static guint
merged_callback_hash(gconstpointer merged_callback_as_pointer)73 merged_callback_hash (gconstpointer merged_callback_as_pointer)
74 {
75 	const MergedCallback *merged_callback;
76 
77 	merged_callback = merged_callback_as_pointer;
78 	return GPOINTER_TO_UINT (merged_callback->callback)
79 		^ GPOINTER_TO_UINT (merged_callback->callback_data);
80 }
81 
82 static gboolean
merged_callback_equal(gconstpointer merged_callback_as_pointer,gconstpointer merged_callback_as_pointer_2)83 merged_callback_equal (gconstpointer merged_callback_as_pointer,
84 		       gconstpointer merged_callback_as_pointer_2)
85 {
86 	const MergedCallback *merged_callback, *merged_callback_2;
87 
88 	merged_callback = merged_callback_as_pointer;
89 	merged_callback_2 = merged_callback_as_pointer_2;
90 
91 	return merged_callback->callback == merged_callback_2->callback
92 		&& merged_callback->callback_data == merged_callback_2->callback_data;
93 }
94 
95 static void
merged_callback_destroy(MergedCallback * merged_callback)96 merged_callback_destroy (MergedCallback *merged_callback)
97 {
98 	g_assert (merged_callback != NULL);
99 	g_assert (NEMO_IS_MERGED_DIRECTORY (merged_callback->merged));
100 
101 	g_list_free (merged_callback->non_ready_directories);
102 	nemo_file_list_free (merged_callback->merged_file_list);
103 	g_free (merged_callback);
104 }
105 
106 static void
merged_callback_check_done(MergedCallback * merged_callback)107 merged_callback_check_done (MergedCallback *merged_callback)
108 {
109 	/* Check if we are ready. */
110 	if (merged_callback->non_ready_directories != NULL) {
111 		return;
112 	}
113 
114 	/* Remove from the hash table before sending it. */
115 	g_hash_table_remove (merged_callback->merged->details->callbacks, merged_callback);
116 
117 	/* We are ready, so do the real callback. */
118 	(* merged_callback->callback) (NEMO_DIRECTORY (merged_callback->merged),
119 				       merged_callback->merged_file_list,
120 				       merged_callback->callback_data);
121 
122 	/* And we are done. */
123 	merged_callback_destroy (merged_callback);
124 }
125 
126 static void
merged_callback_remove_directory(MergedCallback * merged_callback,NemoDirectory * directory)127 merged_callback_remove_directory (MergedCallback *merged_callback,
128 				  NemoDirectory *directory)
129 {
130 	merged_callback->non_ready_directories = g_list_remove
131 		(merged_callback->non_ready_directories, directory);
132 	merged_callback_check_done (merged_callback);
133 }
134 
135 static void
directory_ready_callback(NemoDirectory * directory,GList * files,gpointer callback_data)136 directory_ready_callback (NemoDirectory *directory,
137 			  GList *files,
138 			  gpointer callback_data)
139 {
140 	MergedCallback *merged_callback;
141 
142 	g_assert (NEMO_IS_DIRECTORY (directory));
143 	g_assert (callback_data != NULL);
144 
145 	merged_callback = callback_data;
146 	g_assert (g_list_find (merged_callback->non_ready_directories, directory) != NULL);
147 
148 	/* Update based on this call. */
149 	merged_callback->merged_file_list = g_list_concat
150 		(merged_callback->merged_file_list,
151 		 nemo_file_list_copy (files));
152 
153 	/* Check if we are ready. */
154 	merged_callback_remove_directory (merged_callback, directory);
155 }
156 
157 static void
merged_call_when_ready(NemoDirectory * directory,NemoFileAttributes file_attributes,gboolean wait_for_file_list,NemoDirectoryCallback callback,gpointer callback_data)158 merged_call_when_ready (NemoDirectory *directory,
159 			NemoFileAttributes file_attributes,
160 			gboolean wait_for_file_list,
161 			NemoDirectoryCallback callback,
162 			gpointer callback_data)
163 {
164 	NemoMergedDirectory *merged;
165 	MergedCallback search_key, *merged_callback;
166 	GList *node;
167 
168 	merged = NEMO_MERGED_DIRECTORY (directory);
169 
170 	/* Check to be sure we aren't overwriting. */
171 	search_key.callback = callback;
172 	search_key.callback_data = callback_data;
173 	if (g_hash_table_lookup (merged->details->callbacks, &search_key) != NULL) {
174 		g_warning ("tried to add a new callback while an old one was pending");
175 		return;
176 	}
177 
178 	/* Create a merged_callback record. */
179 	merged_callback = g_new0 (MergedCallback, 1);
180 	merged_callback->merged = merged;
181 	merged_callback->callback = callback;
182 	merged_callback->callback_data = callback_data;
183 	merged_callback->wait_for_attributes = file_attributes;
184 	merged_callback->wait_for_file_list = wait_for_file_list;
185 	for (node = merged->details->directories; node != NULL; node = node->next) {
186 		merged_callback->non_ready_directories = g_list_prepend
187 			(merged_callback->non_ready_directories, node->data);
188 	}
189 
190 	/* Put it in the hash table. */
191 	g_hash_table_insert (merged->details->callbacks,
192 			     merged_callback, merged_callback);
193 
194 	/* Handle the pathological case where there are no directories. */
195 	if (merged->details->directories == NULL) {
196 		merged_callback_check_done (merged_callback);
197 	}
198 
199 	/* Now tell all the directories about it. */
200 	for (node = merged->details->directories; node != NULL; node = node->next) {
201 		nemo_directory_call_when_ready
202 			(node->data,
203 			 merged_callback->wait_for_attributes,
204 			 merged_callback->wait_for_file_list,
205 			 directory_ready_callback, merged_callback);
206 	}
207 }
208 
209 static void
merged_cancel_callback(NemoDirectory * directory,NemoDirectoryCallback callback,gpointer callback_data)210 merged_cancel_callback (NemoDirectory *directory,
211 			NemoDirectoryCallback callback,
212 			gpointer callback_data)
213 {
214 	NemoMergedDirectory *merged;
215 	MergedCallback search_key, *merged_callback;
216 	GList *node;
217 
218 	merged = NEMO_MERGED_DIRECTORY (directory);
219 
220 	/* Find the entry in the table. */
221 	search_key.callback = callback;
222 	search_key.callback_data = callback_data;
223 	merged_callback = g_hash_table_lookup (merged->details->callbacks, &search_key);
224 	if (merged_callback == NULL) {
225 		return;
226 	}
227 
228 	/* Remove from the hash table before working with it. */
229 	g_hash_table_remove (merged_callback->merged->details->callbacks, merged_callback);
230 
231 	/* Tell all the directories to cancel the call. */
232 	for (node = merged_callback->non_ready_directories; node != NULL; node = node->next) {
233 		nemo_directory_cancel_callback
234 			(node->data,
235 			 directory_ready_callback, merged_callback);
236 	}
237 	merged_callback_destroy (merged_callback);
238 }
239 
240 static void
build_merged_callback_list(NemoDirectory * directory,GList * file_list,gpointer callback_data)241 build_merged_callback_list (NemoDirectory *directory,
242 			    GList *file_list,
243 			    gpointer callback_data)
244 {
245 	GList **merged_list;
246 
247 	merged_list = callback_data;
248 	*merged_list = g_list_concat (*merged_list,
249 				      nemo_file_list_copy (file_list));
250 }
251 
252 /* Create a monitor on each of the directories in the list. */
253 static void
merged_monitor_add(NemoDirectory * directory,gconstpointer client,gboolean monitor_hidden_files,NemoFileAttributes file_attributes,NemoDirectoryCallback callback,gpointer callback_data)254 merged_monitor_add (NemoDirectory *directory,
255 		    gconstpointer client,
256 		    gboolean monitor_hidden_files,
257 		    NemoFileAttributes file_attributes,
258 		    NemoDirectoryCallback callback,
259 		    gpointer callback_data)
260 {
261 	NemoMergedDirectory *merged;
262 	MergedMonitor *monitor;
263 	GList *node;
264 	GList *merged_callback_list;
265 
266 	merged = NEMO_MERGED_DIRECTORY (directory);
267 
268 	/* Map the client to a unique value so this doesn't interfere
269 	 * with direct monitoring of the directory by the same client.
270 	 */
271 	monitor = g_hash_table_lookup (merged->details->monitors, client);
272 	if (monitor != NULL) {
273 		g_assert (monitor->merged == merged);
274 	} else {
275 		monitor = g_new0 (MergedMonitor, 1);
276 		monitor->merged = merged;
277 		g_hash_table_insert (merged->details->monitors,
278 				     (gpointer) client, monitor);
279 	}
280 	monitor->monitor_hidden_files = monitor_hidden_files;
281 	monitor->monitor_attributes = file_attributes;
282 
283 	/* Call through to the real directory add calls. */
284 	merged_callback_list = NULL;
285 	for (node = merged->details->directories; node != NULL; node = node->next) {
286 		nemo_directory_file_monitor_add
287 			(node->data, monitor,
288 			 monitor_hidden_files,
289 			 file_attributes,
290 			 build_merged_callback_list, &merged_callback_list);
291 	}
292 	if (callback != NULL) {
293 		(* callback) (directory, merged_callback_list, callback_data);
294 	}
295 	nemo_file_list_free (merged_callback_list);
296 }
297 
298 static void
merged_monitor_destroy(NemoMergedDirectory * merged,MergedMonitor * monitor)299 merged_monitor_destroy (NemoMergedDirectory *merged, MergedMonitor *monitor)
300 {
301 	GList *node;
302 
303 	/* Call through to the real directory remove calls. */
304 	for (node = merged->details->directories; node != NULL; node = node->next) {
305 		nemo_directory_file_monitor_remove (node->data, monitor);
306 	}
307 
308 	g_free (monitor);
309 }
310 
311 /* Remove the monitor from each of the directories in the list. */
312 static void
merged_monitor_remove(NemoDirectory * directory,gconstpointer client)313 merged_monitor_remove (NemoDirectory *directory,
314 		       gconstpointer client)
315 {
316 	NemoMergedDirectory *merged;
317 	MergedMonitor *monitor;
318 
319 	merged = NEMO_MERGED_DIRECTORY (directory);
320 
321 	/* Map the client to the value used by the earlier add call. */
322         monitor = g_hash_table_lookup (merged->details->monitors, client);
323 	if (monitor == NULL) {
324 		return;
325 	}
326 	g_hash_table_remove (merged->details->monitors, client);
327 
328 	merged_monitor_destroy (merged, monitor);
329 }
330 
331 static void
merged_force_reload(NemoDirectory * directory)332 merged_force_reload (NemoDirectory *directory)
333 {
334 	NemoMergedDirectory *merged;
335 	GList *node;
336 
337 	merged = NEMO_MERGED_DIRECTORY (directory);
338 
339 	/* Call through to the real force_reload calls. */
340 	for (node = merged->details->directories; node != NULL; node = node->next) {
341 		nemo_directory_force_reload (node->data);
342 	}
343 }
344 
345 /* Return true if any directory in the list does. */
346 static gboolean
merged_contains_file(NemoDirectory * directory,NemoFile * file)347 merged_contains_file (NemoDirectory *directory,
348 		      NemoFile *file)
349 {
350 	NemoMergedDirectory *merged;
351 	GList *node;
352 
353 	merged = NEMO_MERGED_DIRECTORY (directory);
354 
355 	for (node = merged->details->directories; node != NULL; node = node->next) {
356 		if (nemo_directory_contains_file (node->data, file)) {
357 			return TRUE;
358 		}
359 	}
360 	return FALSE;
361 }
362 
363 /* Return true only if all directories in the list do. */
364 static gboolean
merged_are_all_files_seen(NemoDirectory * directory)365 merged_are_all_files_seen (NemoDirectory *directory)
366 {
367 	NemoMergedDirectory *merged;
368 	GList *node;
369 
370 	merged = NEMO_MERGED_DIRECTORY (directory);
371 
372 	for (node = merged->details->directories; node != NULL; node = node->next) {
373 		if (!nemo_directory_are_all_files_seen (node->data)) {
374 			return FALSE;
375 		}
376 	}
377 	return TRUE;
378 }
379 
380 /* Return true if any directory in the list does. */
381 static gboolean
merged_is_not_empty(NemoDirectory * directory)382 merged_is_not_empty (NemoDirectory *directory)
383 {
384 	NemoMergedDirectory *merged;
385 	GList *node;
386 
387 	merged = NEMO_MERGED_DIRECTORY (directory);
388 
389 	for (node = merged->details->directories; node != NULL; node = node->next) {
390 		if (nemo_directory_is_not_empty (node->data)) {
391 			return TRUE;
392 		}
393 	}
394 	return FALSE;
395 }
396 
397 static GList *
merged_get_file_list(NemoDirectory * directory)398 merged_get_file_list (NemoDirectory *directory)
399 {
400 	GList *dirs_file_list, *merged_dir_file_list = NULL;
401 	GList *dir_list;
402 	GList *cur_node;
403 
404 	dirs_file_list = NULL;
405 	dir_list = NEMO_MERGED_DIRECTORY (directory)->details->directories;
406 
407 	for (cur_node = dir_list; cur_node != NULL; cur_node = cur_node->next) {
408 		NemoDirectory *cur_dir;
409 
410 		cur_dir = NEMO_DIRECTORY (cur_node->data);
411 		dirs_file_list = g_list_concat (dirs_file_list,
412 						 nemo_directory_get_file_list (cur_dir));
413 	}
414 
415 	merged_dir_file_list = NEMO_DIRECTORY_CLASS
416 		(nemo_merged_directory_parent_class)->get_file_list (directory);
417 
418 	return g_list_concat (dirs_file_list, merged_dir_file_list);
419 }
420 
421 static void
forward_files_added_cover(NemoDirectory * real_directory,GList * files,gpointer callback_data)422 forward_files_added_cover (NemoDirectory *real_directory,
423 			   GList *files,
424 			   gpointer callback_data)
425 {
426 	nemo_directory_emit_files_added (NEMO_DIRECTORY (callback_data), files);
427 }
428 
429 static void
forward_files_changed_cover(NemoDirectory * real_directory,GList * files,gpointer callback_data)430 forward_files_changed_cover (NemoDirectory *real_directory,
431 			     GList *files,
432 			     gpointer callback_data)
433 {
434 	nemo_directory_emit_files_changed (NEMO_DIRECTORY (callback_data), files);
435 }
436 
437 static void
done_loading_callback(NemoDirectory * real_directory,NemoMergedDirectory * merged)438 done_loading_callback (NemoDirectory *real_directory,
439 		       NemoMergedDirectory *merged)
440 {
441 	merged->details->directories_not_done_loading = g_list_remove
442 		(merged->details->directories_not_done_loading, real_directory);
443 	if (merged->details->directories_not_done_loading == NULL) {
444 		nemo_directory_emit_done_loading (NEMO_DIRECTORY (merged));
445 	}
446 }
447 
448 static void
monitor_add_directory(gpointer key,gpointer value,gpointer callback_data)449 monitor_add_directory (gpointer key,
450 		       gpointer value,
451 		       gpointer callback_data)
452 {
453 	MergedMonitor *monitor;
454 
455 	monitor = value;
456 	nemo_directory_file_monitor_add
457 		(NEMO_DIRECTORY (callback_data), monitor,
458 		 monitor->monitor_hidden_files,
459 		 monitor->monitor_attributes,
460 		 forward_files_added_cover, monitor->merged);
461 }
462 
463 static void
merged_add_real_directory(NemoMergedDirectory * merged,NemoDirectory * real_directory)464 merged_add_real_directory (NemoMergedDirectory *merged,
465 			   NemoDirectory *real_directory)
466 {
467 	g_return_if_fail (NEMO_IS_MERGED_DIRECTORY (merged));
468 	g_return_if_fail (NEMO_IS_DIRECTORY (real_directory));
469 	g_return_if_fail (!NEMO_IS_MERGED_DIRECTORY (real_directory));
470 	g_return_if_fail (g_list_find (merged->details->directories, real_directory) == NULL);
471 
472 	/* Add to our list of directories. */
473 	nemo_directory_ref (real_directory);
474 	merged->details->directories = g_list_prepend
475 		(merged->details->directories, real_directory);
476 	merged->details->directories_not_done_loading = g_list_prepend
477 		(merged->details->directories_not_done_loading, real_directory);
478 
479 	g_signal_connect_object (real_directory, "done_loading",
480 				 G_CALLBACK (done_loading_callback), merged, 0);
481 
482 	/* FIXME bugzilla.gnome.org 45084: The done_loading part won't work for the case where
483          * we have no directories in our list.
484 	 */
485 
486 	/* Add the directory to any extant monitors. */
487 	g_hash_table_foreach (merged->details->monitors,
488 			      monitor_add_directory,
489 			      real_directory);
490 	/* FIXME bugzilla.gnome.org 42541: Do we need to add the directory to callbacks too? */
491 
492 	g_signal_connect_object (real_directory, "files_added",
493 				 G_CALLBACK (forward_files_added_cover), merged, 0);
494 	g_signal_connect_object (real_directory, "files_changed",
495 				 G_CALLBACK (forward_files_changed_cover), merged, 0);
496 }
497 
498 void
nemo_merged_directory_add_real_directory(NemoMergedDirectory * merged,NemoDirectory * real_directory)499 nemo_merged_directory_add_real_directory (NemoMergedDirectory *merged,
500 					      NemoDirectory *real_directory)
501 {
502 	g_return_if_fail (NEMO_IS_MERGED_DIRECTORY (merged));
503 	g_return_if_fail (NEMO_IS_DIRECTORY (real_directory));
504 	g_return_if_fail (!NEMO_IS_MERGED_DIRECTORY (real_directory));
505 
506 	/* Quietly do nothing if asked to add something that's already there. */
507 	if (g_list_find (merged->details->directories, real_directory) != NULL) {
508 		return;
509 	}
510 
511 	g_signal_emit (merged, signals[ADD_REAL_DIRECTORY], 0, real_directory);
512 }
513 
514 GList *
nemo_merged_directory_get_real_directories(NemoMergedDirectory * merged)515 nemo_merged_directory_get_real_directories (NemoMergedDirectory *merged)
516 {
517 	return g_list_copy (merged->details->directories);
518 }
519 
520 static void
merged_callback_remove_directory_cover(gpointer key,gpointer value,gpointer callback_data)521 merged_callback_remove_directory_cover (gpointer key,
522 					gpointer value,
523 					gpointer callback_data)
524 {
525 	merged_callback_remove_directory
526 		(value, NEMO_DIRECTORY (callback_data));
527 }
528 
529 static void
monitor_remove_directory(gpointer key,gpointer value,gpointer callback_data)530 monitor_remove_directory (gpointer key,
531 			  gpointer value,
532 			  gpointer callback_data)
533 {
534 	nemo_directory_file_monitor_remove
535 		(NEMO_DIRECTORY (callback_data), value);
536 }
537 
538 static void
real_directory_notify_files_removed(NemoDirectory * real_directory)539 real_directory_notify_files_removed (NemoDirectory *real_directory)
540 {
541 	GList *files, *l;
542 
543 	files = nemo_directory_get_file_list (real_directory);
544 
545 	for (l = files; l; l = l->next) {
546 		NemoFile *file;
547 		char *uri;
548 
549 		file = NEMO_FILE (l->data);
550 		uri = nemo_file_get_uri (file);
551 		nemo_file_unref (file);
552 
553 		l->data = uri;
554 	}
555 
556 	if (files) {
557 		nemo_directory_notify_files_removed_by_uri (files);
558 	}
559 
560 	g_list_free_full (files, g_free);
561 }
562 
563 static void
merged_remove_real_directory(NemoMergedDirectory * merged,NemoDirectory * real_directory)564 merged_remove_real_directory (NemoMergedDirectory *merged,
565 			      NemoDirectory *real_directory)
566 {
567 	g_return_if_fail (NEMO_IS_MERGED_DIRECTORY (merged));
568 	g_return_if_fail (NEMO_IS_DIRECTORY (real_directory));
569 	g_return_if_fail (g_list_find (merged->details->directories, real_directory) != NULL);
570 
571 	/* Since the real directory will be going away, act as if files were removed */
572 	real_directory_notify_files_removed (real_directory);
573 
574 	/* Remove this directory from callbacks and monitors. */
575 	eel_g_hash_table_safe_for_each (merged->details->callbacks,
576 					merged_callback_remove_directory_cover,
577 					real_directory);
578 	g_hash_table_foreach (merged->details->monitors,
579 			      monitor_remove_directory,
580 			      real_directory);
581 
582 	/* Disconnect all the signals. */
583 	g_signal_handlers_disconnect_matched
584 		(real_directory, G_SIGNAL_MATCH_DATA,
585 		 0, 0, NULL, NULL, merged);
586 
587 	/* Remove from our list of directories. */
588 	merged->details->directories = g_list_remove
589 		(merged->details->directories, real_directory);
590 	merged->details->directories_not_done_loading = g_list_remove
591 		(merged->details->directories_not_done_loading, real_directory);
592 	nemo_directory_unref (real_directory);
593 }
594 
595 void
nemo_merged_directory_remove_real_directory(NemoMergedDirectory * merged,NemoDirectory * real_directory)596 nemo_merged_directory_remove_real_directory (NemoMergedDirectory *merged,
597 						 NemoDirectory *real_directory)
598 {
599 	g_return_if_fail (NEMO_IS_MERGED_DIRECTORY (merged));
600 
601 	/* Quietly do nothing if asked to remove something that's not there. */
602 	if (g_list_find (merged->details->directories, real_directory) == NULL) {
603 		return;
604 	}
605 
606 	g_signal_emit (merged, signals[REMOVE_REAL_DIRECTORY], 0, real_directory);
607 }
608 
609 static void
merged_monitor_destroy_cover(gpointer key,gpointer value,gpointer callback_data)610 merged_monitor_destroy_cover (gpointer key,
611 			      gpointer value,
612 			      gpointer callback_data)
613 {
614 	merged_monitor_destroy (callback_data, value);
615 }
616 
617 static void
merged_callback_destroy_cover(gpointer key,gpointer value,gpointer callback_data)618 merged_callback_destroy_cover (gpointer key,
619 			       gpointer value,
620 			       gpointer callback_data)
621 {
622 	merged_callback_destroy (value);
623 }
624 
625 static void
merged_finalize(GObject * object)626 merged_finalize (GObject *object)
627 {
628 	NemoMergedDirectory *merged;
629 
630 	merged = NEMO_MERGED_DIRECTORY (object);
631 
632 	g_hash_table_foreach (merged->details->monitors,
633 			      merged_monitor_destroy_cover, merged);
634 	g_hash_table_foreach (merged->details->callbacks,
635 			      merged_callback_destroy_cover, NULL);
636 
637 	g_hash_table_destroy (merged->details->callbacks);
638 	g_hash_table_destroy (merged->details->monitors);
639 	nemo_directory_list_free (merged->details->directories);
640 	g_list_free (merged->details->directories_not_done_loading);
641 
642 	G_OBJECT_CLASS (nemo_merged_directory_parent_class)->finalize (object);
643 }
644 
645 static void
nemo_merged_directory_init(NemoMergedDirectory * merged)646 nemo_merged_directory_init (NemoMergedDirectory *merged)
647 {
648 	merged->details = G_TYPE_INSTANCE_GET_PRIVATE (merged, NEMO_TYPE_MERGED_DIRECTORY,
649 						       NemoMergedDirectoryDetails);
650 	merged->details->callbacks = g_hash_table_new
651 		(merged_callback_hash, merged_callback_equal);
652 	merged->details->monitors = g_hash_table_new (NULL, NULL);
653 }
654 
655 static void
nemo_merged_directory_class_init(NemoMergedDirectoryClass * class)656 nemo_merged_directory_class_init (NemoMergedDirectoryClass *class)
657 {
658 	NemoDirectoryClass *directory_class;
659 
660 	directory_class = NEMO_DIRECTORY_CLASS (class);
661 
662 	G_OBJECT_CLASS (class)->finalize = merged_finalize;
663 
664 	directory_class->contains_file = merged_contains_file;
665 	directory_class->call_when_ready = merged_call_when_ready;
666 	directory_class->cancel_callback = merged_cancel_callback;
667 	directory_class->file_monitor_add = merged_monitor_add;
668 	directory_class->file_monitor_remove = merged_monitor_remove;
669 	directory_class->force_reload = merged_force_reload;
670  	directory_class->are_all_files_seen = merged_are_all_files_seen;
671 	directory_class->is_not_empty = merged_is_not_empty;
672 	/* Override get_file_list so that we can return a list that includes
673 	 * the files from each of the directories in NemoMergedDirectory->details->directories.
674          */
675 	directory_class->get_file_list = merged_get_file_list;
676 
677 	class->add_real_directory = merged_add_real_directory;
678 	class->remove_real_directory = merged_remove_real_directory;
679 
680 	g_type_class_add_private (class, sizeof (NemoMergedDirectoryDetails));
681 
682 	signals[ADD_REAL_DIRECTORY]
683 		= g_signal_new ("add_real_directory",
684 		                G_TYPE_FROM_CLASS (class),
685 		                G_SIGNAL_RUN_LAST,
686 		                G_STRUCT_OFFSET (NemoMergedDirectoryClass,
687 						 add_real_directory),
688 		                NULL, NULL,
689 		                g_cclosure_marshal_VOID__POINTER,
690 		                G_TYPE_NONE, 1, G_TYPE_POINTER);
691 	signals[REMOVE_REAL_DIRECTORY]
692 		= g_signal_new ("remove_real_directory",
693 		                G_TYPE_FROM_CLASS (class),
694 		                G_SIGNAL_RUN_LAST,
695 		                G_STRUCT_OFFSET (NemoMergedDirectoryClass,
696 						 remove_real_directory),
697 		                NULL, NULL,
698 		                g_cclosure_marshal_VOID__POINTER,
699 		                G_TYPE_NONE, 1, G_TYPE_POINTER);
700 }
701