nautilus-3.6.3/libnautilus-private/nautilus-desktop-directory.c

No issues found

  1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
  2 
  3    nautilus-desktop-directory.c: Subclass of NautilusDirectory to implement
  4    a virtual directory consisting of the desktop directory and the desktop
  5    icons
  6  
  7    Copyright (C) 2003 Red Hat, Inc.
  8   
  9    This program is free software; you can redistribute it and/or
 10    modify it under the terms of the GNU General Public License as
 11    published by the Free Software Foundation; either version 2 of the
 12    License, or (at your option) any later version.
 13   
 14    This program is distributed in the hope that it will be useful,
 15    but WITHOUT ANY WARRANTY; without even the implied warranty of
 16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17    General Public License for more details.
 18   
 19    You should have received a copy of the GNU General Public
 20    License along with this program; if not, write to the
 21    Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 22    Boston, MA 02111-1307, USA.
 23   
 24    Author: Alexander Larsson <alexl@redhat.com>
 25 */
 26 
 27 #include <config.h>
 28 #include "nautilus-desktop-directory.h"
 29 
 30 #include "nautilus-directory-private.h"
 31 #include "nautilus-file.h"
 32 #include "nautilus-file-private.h"
 33 #include "nautilus-file-utilities.h"
 34 #include "nautilus-global-preferences.h"
 35 #include <gtk/gtk.h>
 36 
 37 struct NautilusDesktopDirectoryDetails {
 38 	NautilusDirectory *real_directory;
 39 	GHashTable *callbacks;
 40 	GHashTable *monitors;
 41 };
 42 
 43 typedef struct {
 44 	NautilusDesktopDirectory *desktop_dir;
 45 	NautilusDirectoryCallback callback;
 46 	gpointer callback_data;
 47 
 48 	NautilusFileAttributes wait_for_attributes;
 49 	gboolean wait_for_file_list;
 50 
 51 	GList *non_ready_directories;
 52 	GList *merged_file_list;
 53 } MergedCallback;
 54 
 55 
 56 typedef struct {
 57 	NautilusDesktopDirectory *desktop_dir;
 58 
 59 	gboolean monitor_hidden_files;
 60 	NautilusFileAttributes monitor_attributes;
 61 } MergedMonitor;
 62 
 63 static void desktop_directory_changed_callback (gpointer data);
 64 
 65 G_DEFINE_TYPE (NautilusDesktopDirectory, nautilus_desktop_directory,
 66 	       NAUTILUS_TYPE_DIRECTORY);
 67 
 68 static gboolean
 69 desktop_contains_file (NautilusDirectory *directory,
 70 		       NautilusFile *file)
 71 {
 72 	NautilusDesktopDirectory *desktop;
 73 
 74 	desktop = NAUTILUS_DESKTOP_DIRECTORY (directory);
 75 
 76 	if (nautilus_directory_contains_file (desktop->details->real_directory, file)) {
 77 		return TRUE;
 78 	}
 79 
 80 	return file->details->directory == directory;
 81 }
 82 
 83 static guint
 84 merged_callback_hash (gconstpointer merged_callback_as_pointer)
 85 {
 86 	const MergedCallback *merged_callback;
 87 
 88 	merged_callback = merged_callback_as_pointer;
 89 	return GPOINTER_TO_UINT (merged_callback->callback)
 90 		^ GPOINTER_TO_UINT (merged_callback->callback_data);
 91 }
 92 
 93 static gboolean
 94 merged_callback_equal (gconstpointer merged_callback_as_pointer,
 95 		       gconstpointer merged_callback_as_pointer_2)
 96 {
 97 	const MergedCallback *merged_callback, *merged_callback_2;
 98 
 99 	merged_callback = merged_callback_as_pointer;
100 	merged_callback_2 = merged_callback_as_pointer_2;
101 
102 	return merged_callback->callback == merged_callback_2->callback
103 		&& merged_callback->callback_data == merged_callback_2->callback_data;
104 }
105 
106 static void
107 merged_callback_destroy (MergedCallback *merged_callback)
108 {
109 	g_assert (merged_callback != NULL);
110 	g_assert (NAUTILUS_IS_DESKTOP_DIRECTORY (merged_callback->desktop_dir));
111 
112 	g_list_free (merged_callback->non_ready_directories);
113 	nautilus_file_list_free (merged_callback->merged_file_list);
114 	g_free (merged_callback);
115 }
116 
117 static void
118 merged_callback_check_done (MergedCallback *merged_callback)
119 {
120 	/* Check if we are ready. */
121 	if (merged_callback->non_ready_directories != NULL) {
122 		return;
123 	}
124 
125 	/* Remove from the hash table before sending it. */
126 	g_hash_table_steal (merged_callback->desktop_dir->details->callbacks, merged_callback);
127 
128 	/* We are ready, so do the real callback. */
129 	(* merged_callback->callback) (NAUTILUS_DIRECTORY (merged_callback->desktop_dir),
130 				       merged_callback->merged_file_list,
131 				       merged_callback->callback_data);
132 
133 	/* And we are done. */
134 	merged_callback_destroy (merged_callback);
135 }
136 
137 static void
138 merged_callback_remove_directory (MergedCallback *merged_callback,
139 				  NautilusDirectory *directory)
140 {
141 	merged_callback->non_ready_directories = g_list_remove
142 		(merged_callback->non_ready_directories, directory);
143 	merged_callback_check_done (merged_callback);
144 }
145 
146 static void
147 directory_ready_callback (NautilusDirectory *directory,
148 			  GList *files,
149 			  gpointer callback_data)
150 {
151 	MergedCallback *merged_callback;
152 
153 	g_assert (NAUTILUS_IS_DIRECTORY (directory));
154 	g_assert (callback_data != NULL);
155 
156 	merged_callback = callback_data;
157 	g_assert (g_list_find (merged_callback->non_ready_directories, directory) != NULL);
158 
159 	/* Update based on this call. */
160 	merged_callback->merged_file_list = g_list_concat
161 		(merged_callback->merged_file_list,
162 		 nautilus_file_list_copy (files));
163 
164 	/* Check if we are ready. */
165 	merged_callback_remove_directory (merged_callback, directory);
166 }
167 
168 static void
169 desktop_call_when_ready (NautilusDirectory *directory,
170 			 NautilusFileAttributes file_attributes,
171 			 gboolean wait_for_file_list,
172 			 NautilusDirectoryCallback callback,
173 			 gpointer callback_data)
174 {
175 	NautilusDesktopDirectory *desktop;
176 	MergedCallback search_key, *merged_callback;
177 
178 	desktop = NAUTILUS_DESKTOP_DIRECTORY (directory);
179 
180 	/* Check to be sure we aren't overwriting. */
181 	search_key.callback = callback;
182 	search_key.callback_data = callback_data;
183 	if (g_hash_table_lookup (desktop->details->callbacks, &search_key) != NULL) {
184 		g_warning ("tried to add a new callback while an old one was pending");
185 		return;
186 	}
187 
188 	/* Create a merged_callback record. */
189 	merged_callback = g_new0 (MergedCallback, 1);
190 	merged_callback->desktop_dir = desktop;
191 	merged_callback->callback = callback;
192 	merged_callback->callback_data = callback_data;
193 	merged_callback->wait_for_attributes = file_attributes;
194 	merged_callback->wait_for_file_list = wait_for_file_list;
195 	merged_callback->non_ready_directories = g_list_prepend
196 			(merged_callback->non_ready_directories, directory);
197 	merged_callback->non_ready_directories = g_list_prepend
198 			(merged_callback->non_ready_directories, desktop->details->real_directory);
199 
200 
201 	merged_callback->merged_file_list = g_list_concat (NULL,
202 							   nautilus_file_list_copy (directory->details->file_list));
203 
204 	/* Put it in the hash table. */
205 	g_hash_table_insert (desktop->details->callbacks,
206 			     merged_callback, merged_callback);
207 
208 	/* Now tell all the directories about it. */
209 	nautilus_directory_call_when_ready
210 		(desktop->details->real_directory,
211 		 merged_callback->wait_for_attributes,
212 		 merged_callback->wait_for_file_list,
213 		 directory_ready_callback, merged_callback);
214 	nautilus_directory_call_when_ready_internal
215 		(directory,
216 		 NULL,
217 		 merged_callback->wait_for_attributes,
218 		 merged_callback->wait_for_file_list,
219 		 directory_ready_callback,
220 		 NULL,
221 		 merged_callback);
222 	
223 }
224 
225 static void
226 desktop_cancel_callback (NautilusDirectory *directory,
227 			NautilusDirectoryCallback callback,
228 			gpointer callback_data)
229 {
230 	NautilusDesktopDirectory *desktop;
231 	MergedCallback search_key, *merged_callback;
232 	GList *node;
233 
234 	desktop = NAUTILUS_DESKTOP_DIRECTORY (directory);
235 
236 	/* Find the entry in the table. */
237 	search_key.callback = callback;
238 	search_key.callback_data = callback_data;
239 	merged_callback = g_hash_table_lookup (desktop->details->callbacks, &search_key);
240 	if (merged_callback == NULL) {
241 		return;
242 	}
243 
244 	/* Remove from the hash table before working with it. */
245 	g_hash_table_steal (merged_callback->desktop_dir->details->callbacks, merged_callback);
246 
247 	/* Tell all the directories to cancel the call. */
248 	for (node = merged_callback->non_ready_directories; node != NULL; node = node->next) {
249 		nautilus_directory_cancel_callback
250 			(node->data,
251 			 directory_ready_callback, merged_callback);
252 	}
253 	merged_callback_destroy (merged_callback);
254 }
255 
256 static void
257 merged_monitor_destroy (MergedMonitor *monitor)
258 {
259 	NautilusDesktopDirectory *desktop;
260 
261 	desktop = monitor->desktop_dir;
262 	
263 	/* Call through to the real directory remove calls. */
264 	nautilus_directory_file_monitor_remove (desktop->details->real_directory, monitor);
265 
266 	nautilus_directory_monitor_remove_internal (NAUTILUS_DIRECTORY (desktop), NULL, monitor);
267 	
268 	g_free (monitor);
269 }
270 
271 static void
272 build_merged_callback_list (NautilusDirectory *directory,
273 			    GList *file_list,
274 			    gpointer callback_data)
275 {
276 	GList **merged_list;
277 
278 	merged_list = callback_data;
279 	*merged_list = g_list_concat (*merged_list,
280 				      nautilus_file_list_copy (file_list));
281 }
282 
283 static void
284 desktop_monitor_add (NautilusDirectory *directory,
285 		    gconstpointer client,
286 		    gboolean monitor_hidden_files,
287 		    NautilusFileAttributes file_attributes,
288 		    NautilusDirectoryCallback callback,
289 		    gpointer callback_data)
290 {
291 	NautilusDesktopDirectory *desktop;
292 	MergedMonitor *monitor;
293 	GList *merged_callback_list;
294 
295 	desktop = NAUTILUS_DESKTOP_DIRECTORY (directory);
296 
297 	/* Map the client to a unique value so this doesn't interfere
298 	 * with direct monitoring of the directory by the same client.
299 	 */
300 	monitor = g_hash_table_lookup (desktop->details->monitors, client);
301 	if (monitor != NULL) {
302 		g_assert (monitor->desktop_dir == desktop);
303 	} else {
304 		monitor = g_new0 (MergedMonitor, 1);
305 		monitor->desktop_dir = desktop;
306 		g_hash_table_insert (desktop->details->monitors,
307 				     (gpointer) client, monitor);
308 	}
309 	monitor->monitor_hidden_files = monitor_hidden_files;
310 	monitor->monitor_attributes = file_attributes;
311 	
312 	/* Call through to the real directory add calls. */
313 	merged_callback_list = NULL;
314 
315 	/* Call up to real dir */
316 	nautilus_directory_file_monitor_add
317 		(desktop->details->real_directory, monitor,
318 		 monitor_hidden_files,
319 		 file_attributes,
320 		 build_merged_callback_list, &merged_callback_list);
321 	
322 	/* Handle the desktop part */
323 	merged_callback_list = g_list_concat (merged_callback_list,
324 					      nautilus_file_list_copy (directory->details->file_list));
325 
326 	
327 	if (callback != NULL) {
328 		(* callback) (directory, merged_callback_list, callback_data);
329 	}
330 	nautilus_file_list_free (merged_callback_list);
331 }
332 
333 static void
334 desktop_monitor_remove (NautilusDirectory *directory,
335 		       gconstpointer client)
336 {
337 	NautilusDesktopDirectory *desktop;
338 	MergedMonitor *monitor;
339 	
340 	desktop = NAUTILUS_DESKTOP_DIRECTORY (directory);
341 
342 	monitor = g_hash_table_lookup (desktop->details->monitors, client);
343 	if (monitor == NULL) {
344 		return;
345 	}
346 	
347 	g_hash_table_remove (desktop->details->monitors, client);
348 }
349 
350 static void
351 desktop_force_reload (NautilusDirectory *directory)
352 {
353 	NautilusDesktopDirectory *desktop;
354 
355 	desktop = NAUTILUS_DESKTOP_DIRECTORY (directory);
356 
357 	nautilus_directory_force_reload (desktop->details->real_directory);
358 
359 	/* We don't invalidate the files in desktop, since they are always
360 	   up to date. (And we don't ever want to mark them invalid.) */
361 }
362 
363 static gboolean
364 desktop_are_all_files_seen (NautilusDirectory *directory)
365 {
366 	NautilusDesktopDirectory *desktop;
367 
368 	desktop = NAUTILUS_DESKTOP_DIRECTORY (directory);
369 
370 	if (!nautilus_directory_are_all_files_seen (desktop->details->real_directory)) {
371 		return FALSE;
372 	}
373 
374 	return TRUE;
375 }
376 
377 static gboolean
378 desktop_is_not_empty (NautilusDirectory *directory)
379 {
380 	NautilusDesktopDirectory *desktop;
381 
382 	desktop = NAUTILUS_DESKTOP_DIRECTORY (directory);
383 
384 	if (nautilus_directory_is_not_empty (desktop->details->real_directory)) {
385 		return TRUE;
386 	}
387 
388 	return directory->details->file_list != NULL;
389 }
390 
391 static GList *
392 desktop_get_file_list (NautilusDirectory *directory)
393 {
394 	GList *real_dir_file_list, *desktop_dir_file_list = NULL;
395 	
396 	real_dir_file_list = nautilus_directory_get_file_list
397 				(NAUTILUS_DESKTOP_DIRECTORY (directory)->details->real_directory);
398 	desktop_dir_file_list = NAUTILUS_DIRECTORY_CLASS (nautilus_desktop_directory_parent_class)->get_file_list (directory);
399 
400 	return g_list_concat (real_dir_file_list, desktop_dir_file_list);
401 }
402 
403 NautilusDirectory *
404 nautilus_desktop_directory_get_real_directory (NautilusDesktopDirectory *desktop)
405 {
406 	nautilus_directory_ref (desktop->details->real_directory);
407 	return desktop->details->real_directory;
408 }
409 
410 
411 static void
412 desktop_finalize (GObject *object)
413 {
414 	NautilusDesktopDirectory *desktop;
415 
416 	desktop = NAUTILUS_DESKTOP_DIRECTORY (object);
417 
418 	nautilus_directory_unref (desktop->details->real_directory);
419 
420 	g_hash_table_destroy (desktop->details->callbacks);
421 	g_hash_table_destroy (desktop->details->monitors);
422 	g_free (desktop->details);
423 
424 	g_signal_handlers_disconnect_by_func (nautilus_preferences,
425 					      desktop_directory_changed_callback,
426 					      desktop);
427 
428 	G_OBJECT_CLASS (nautilus_desktop_directory_parent_class)->finalize (object);
429 }
430 
431 static void
432 done_loading_callback (NautilusDirectory *real_directory,
433 		       NautilusDesktopDirectory *desktop)
434 {
435      nautilus_directory_emit_done_loading (NAUTILUS_DIRECTORY (desktop));
436 }
437 
438 
439 static void
440 forward_files_added_cover (NautilusDirectory *real_directory,
441 			   GList *files,
442 			   gpointer callback_data)
443 {
444 	nautilus_directory_emit_files_added (NAUTILUS_DIRECTORY (callback_data), files);
445 }
446 
447 static void
448 forward_files_changed_cover (NautilusDirectory *real_directory,
449 			     GList *files,
450 			     gpointer callback_data)
451 {
452 	nautilus_directory_emit_files_changed (NAUTILUS_DIRECTORY (callback_data), files);
453 }
454 
455 static void
456 update_desktop_directory (NautilusDesktopDirectory *desktop)
457 {
458 	char *desktop_path;
459 	char *desktop_uri;
460 	NautilusDirectory *real_directory;
461 
462 	real_directory = desktop->details->real_directory;
463 	if (real_directory != NULL) {
464 		g_hash_table_foreach_remove (desktop->details->callbacks, (GHRFunc) gtk_true, NULL);
465 		g_hash_table_foreach_remove (desktop->details->monitors, (GHRFunc) gtk_true, NULL);
466 
467 		g_signal_handlers_disconnect_by_func (real_directory, done_loading_callback, desktop);
468 		g_signal_handlers_disconnect_by_func (real_directory, forward_files_added_cover, desktop);
469 		g_signal_handlers_disconnect_by_func (real_directory, forward_files_changed_cover, desktop);
470 
471 		nautilus_directory_unref (real_directory);
472 	}
473 
474 	desktop_path = nautilus_get_desktop_directory ();
475 	desktop_uri = g_filename_to_uri (desktop_path, NULL, NULL);
476 	real_directory = nautilus_directory_get_by_uri (desktop_uri);
477 	g_free (desktop_uri);
478 	g_free (desktop_path);
479 
480 	g_signal_connect_object (real_directory, "done_loading",
481 				 G_CALLBACK (done_loading_callback), desktop, 0);
482 	g_signal_connect_object (real_directory, "files_added",
483 				 G_CALLBACK (forward_files_added_cover), desktop, 0);
484 	g_signal_connect_object (real_directory, "files_changed",
485 				 G_CALLBACK (forward_files_changed_cover), desktop, 0);
486 
487 	desktop->details->real_directory = real_directory;
488 }
489 
490 static void
491 desktop_directory_changed_callback (gpointer data)
492 {
493 	update_desktop_directory (NAUTILUS_DESKTOP_DIRECTORY (data));
494 	nautilus_directory_force_reload (NAUTILUS_DIRECTORY (data));
495 }
496 
497 static void
498 nautilus_desktop_directory_init (NautilusDesktopDirectory *desktop)
499 {
500 	desktop->details = g_new0 (NautilusDesktopDirectoryDetails, 1);
501 
502 	desktop->details->callbacks = g_hash_table_new_full
503 		(merged_callback_hash, merged_callback_equal,
504 		 NULL, (GDestroyNotify)merged_callback_destroy);
505 	desktop->details->monitors = g_hash_table_new_full (NULL, NULL,
506 							    NULL, (GDestroyNotify)merged_monitor_destroy);
507 
508 	update_desktop_directory (NAUTILUS_DESKTOP_DIRECTORY (desktop));
509 }
510 
511 static void
512 nautilus_desktop_directory_class_init (NautilusDesktopDirectoryClass *class)
513 {
514 	NautilusDirectoryClass *directory_class;
515 
516 	directory_class = NAUTILUS_DIRECTORY_CLASS (class);
517 	
518 	G_OBJECT_CLASS (class)->finalize = desktop_finalize;
519 
520 	directory_class->contains_file = desktop_contains_file;
521 	directory_class->call_when_ready = desktop_call_when_ready;
522 	directory_class->cancel_callback = desktop_cancel_callback;
523 	directory_class->file_monitor_add = desktop_monitor_add;
524 	directory_class->file_monitor_remove = desktop_monitor_remove;
525 	directory_class->force_reload = desktop_force_reload;
526  	directory_class->are_all_files_seen = desktop_are_all_files_seen;
527 	directory_class->is_not_empty = desktop_is_not_empty;
528 	/* Override get_file_list so that we can return the list of files
529 	 * in NautilusDesktopDirectory->details->real_directory,
530 	 * in addition to the list of standard desktop icons on the desktop.
531 	 */
532 	directory_class->get_file_list = desktop_get_file_list;
533 }
534