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

No issues found

  1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
  2 
  3    nautilus-desktop-directory-file.c: Subclass of NautilusFile to help implement the
  4    virtual desktop.
  5  
  6    Copyright (C) 2003 Red Hat, 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., 59 Temple Place - Suite 330,
 21    Boston, MA 02111-1307, USA.
 22   
 23    Author: Alexander Larsson <alexl@redhat.com>
 24 */
 25 
 26 #include <config.h>
 27 #include "nautilus-desktop-directory-file.h"
 28 
 29 #include "nautilus-desktop-metadata.h"
 30 #include "nautilus-directory-notify.h"
 31 #include "nautilus-directory-private.h"
 32 #include "nautilus-file-attributes.h"
 33 #include "nautilus-file-private.h"
 34 #include "nautilus-file-utilities.h"
 35 #include <eel/eel-glib-extensions.h>
 36 #include "nautilus-desktop-directory.h"
 37 #include "nautilus-metadata.h"
 38 #include <gtk/gtk.h>
 39 #include <glib/gi18n.h>
 40 #include <string.h>
 41 
 42 struct NautilusDesktopDirectoryFileDetails {
 43 	NautilusDesktopDirectory *desktop_directory;
 44 
 45   	NautilusFile *real_dir_file;
 46 
 47 	GHashTable *callbacks;
 48 	GHashTable *monitors;
 49 };
 50 
 51 typedef struct {
 52 	NautilusDesktopDirectoryFile *desktop_file;
 53 	NautilusFileCallback callback;
 54 	gpointer callback_data;
 55 
 56 	NautilusFileAttributes delegated_attributes;
 57 	NautilusFileAttributes non_delegated_attributes;
 58 
 59 	GList *non_ready_files;
 60 
 61 	gboolean initializing;
 62 } DesktopCallback;
 63 
 64 typedef struct {
 65 	NautilusDesktopDirectoryFile *desktop_file;
 66 
 67 	NautilusFileAttributes delegated_attributes;
 68 	NautilusFileAttributes non_delegated_attributes;
 69 } DesktopMonitor;
 70 
 71 G_DEFINE_TYPE (NautilusDesktopDirectoryFile, nautilus_desktop_directory_file,
 72 	       NAUTILUS_TYPE_FILE);
 73 
 74 static guint
 75 desktop_callback_hash (gconstpointer desktop_callback_as_pointer)
 76 {
 77 	const DesktopCallback *desktop_callback;
 78 
 79 	desktop_callback = desktop_callback_as_pointer;
 80 	return GPOINTER_TO_UINT (desktop_callback->callback)
 81 		^ GPOINTER_TO_UINT (desktop_callback->callback_data);
 82 }
 83 
 84 static gboolean
 85 desktop_callback_equal (gconstpointer desktop_callback_as_pointer,
 86 		      gconstpointer desktop_callback_as_pointer_2)
 87 {
 88 	const DesktopCallback *desktop_callback, *desktop_callback_2;
 89 
 90 	desktop_callback = desktop_callback_as_pointer;
 91 	desktop_callback_2 = desktop_callback_as_pointer_2;
 92 
 93 	return desktop_callback->callback == desktop_callback_2->callback
 94 		&& desktop_callback->callback_data == desktop_callback_2->callback_data;
 95 }
 96 
 97      
 98 static void
 99 real_file_changed_callback (NautilusFile *real_file,
100 			    gpointer callback_data)
101 {
102 	NautilusDesktopDirectoryFile *desktop_file;
103 	
104 	desktop_file = NAUTILUS_DESKTOP_DIRECTORY_FILE (callback_data);
105 	nautilus_file_changed (NAUTILUS_FILE (desktop_file));
106 }
107 
108 static NautilusFileAttributes 
109 get_delegated_attributes_mask (void)
110 {
111 	return NAUTILUS_FILE_ATTRIBUTE_DEEP_COUNTS |
112 		NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT |
113 		NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_MIME_TYPES;
114 }
115 
116 static void
117 partition_attributes (NautilusFileAttributes attributes,
118 		      NautilusFileAttributes *delegated_attributes,
119 		      NautilusFileAttributes *non_delegated_attributes)
120 {
121 	NautilusFileAttributes mask;
122 
123 	mask = get_delegated_attributes_mask ();
124 
125 	*delegated_attributes = attributes & mask;
126 	*non_delegated_attributes = attributes & ~mask;
127 }
128 
129 static void
130 desktop_directory_file_monitor_add (NautilusFile *file,
131 				    gconstpointer client,
132 				    NautilusFileAttributes attributes)
133 {
134 	NautilusDesktopDirectoryFile *desktop_file;
135 	DesktopMonitor *monitor;
136 
137 	desktop_file = NAUTILUS_DESKTOP_DIRECTORY_FILE (file);
138 
139 	/* Map the client to a unique value so this doesn't interfere
140 	 * with direct monitoring of the file by the same client.
141 	 */
142 	monitor = g_hash_table_lookup (desktop_file->details->monitors, client);
143 	if (monitor != NULL) {
144 		g_assert (monitor->desktop_file == desktop_file);
145 	} else {
146 		monitor = g_new0 (DesktopMonitor, 1);
147 		monitor->desktop_file = desktop_file;
148 		g_hash_table_insert (desktop_file->details->monitors,
149 				     (gpointer) client, monitor);
150 	}
151 
152 	partition_attributes (attributes,
153 			      &monitor->delegated_attributes,
154 			      &monitor->non_delegated_attributes);
155 
156 	/* Pawn off partioned attributes to real dir file */
157 	nautilus_file_monitor_add (desktop_file->details->real_dir_file,
158 				   monitor, monitor->delegated_attributes);
159 
160 	/* Do the rest ourself */
161 	nautilus_directory_monitor_add_internal
162 		(file->details->directory, file,
163 		 client, TRUE,
164 		 monitor->non_delegated_attributes,
165 		 NULL, NULL);
166 }
167 
168 static void
169 desktop_directory_file_monitor_remove (NautilusFile *file,
170 				       gconstpointer client)
171 {
172 	NautilusDesktopDirectoryFile *desktop_file;
173 	DesktopMonitor *monitor;
174 	
175 	desktop_file = NAUTILUS_DESKTOP_DIRECTORY_FILE (file);
176 	
177 	/* Map the client to the value used by the earlier add call. */
178         monitor = g_hash_table_lookup (desktop_file->details->monitors, client);
179 	if (monitor == NULL) {
180 		return;
181 	}
182 
183 	/* Call through to the real file remove calls. */
184 	g_hash_table_remove (desktop_file->details->monitors, client);
185 
186 	/* Remove the locally handled parts */
187 	nautilus_directory_monitor_remove_internal
188 		(file->details->directory, file, client);
189 }
190 
191 static void
192 desktop_callback_destroy (DesktopCallback *desktop_callback)
193 {
194 	g_assert (desktop_callback != NULL);
195 	g_assert (NAUTILUS_IS_DESKTOP_DIRECTORY_FILE (desktop_callback->desktop_file));
196 
197 	nautilus_file_unref (NAUTILUS_FILE (desktop_callback->desktop_file));
198 	g_list_free (desktop_callback->non_ready_files);
199 	g_free (desktop_callback);
200 }
201 
202 static void
203 desktop_callback_check_done (DesktopCallback *desktop_callback)
204 {
205 	/* Check if we are ready. */
206 	if (desktop_callback->initializing ||
207 	    desktop_callback->non_ready_files != NULL) {
208 		return;
209 	}
210 
211 	/* Ensure our metadata is updated before calling back */
212 	nautilus_desktop_update_metadata_from_keyfile (NAUTILUS_FILE (desktop_callback->desktop_file), "directory");
213 
214 	/* Remove from the hash table before sending it. */
215 	g_hash_table_remove (desktop_callback->desktop_file->details->callbacks,
216 			     desktop_callback);
217 
218 	/* We are ready, so do the real callback. */
219 	(* desktop_callback->callback) (NAUTILUS_FILE (desktop_callback->desktop_file),
220 					desktop_callback->callback_data);
221 
222 	/* And we are done. */
223 	desktop_callback_destroy (desktop_callback);
224 }
225 
226 static void
227 desktop_callback_remove_file (DesktopCallback *desktop_callback,
228 			      NautilusFile *file)
229 {
230 	desktop_callback->non_ready_files = g_list_remove
231 		(desktop_callback->non_ready_files, file);
232 	desktop_callback_check_done (desktop_callback);
233 }
234 
235 static void
236 ready_callback (NautilusFile *file,
237 		gpointer callback_data)
238 {
239 	DesktopCallback *desktop_callback;
240 
241 	g_assert (NAUTILUS_IS_FILE (file));
242 	g_assert (callback_data != NULL);
243 
244 	desktop_callback = callback_data;
245 	g_assert (g_list_find (desktop_callback->non_ready_files, file) != NULL);
246 
247 	desktop_callback_remove_file (desktop_callback, file);
248 }
249 
250 static void
251 desktop_directory_file_call_when_ready (NautilusFile *file,
252 					NautilusFileAttributes attributes,
253 					NautilusFileCallback callback,
254 					gpointer callback_data)
255 
256 {
257 	NautilusDesktopDirectoryFile *desktop_file;
258 	DesktopCallback search_key, *desktop_callback;
259 
260 	desktop_file = NAUTILUS_DESKTOP_DIRECTORY_FILE (file);
261 
262 	/* Check to be sure we aren't overwriting. */
263 	search_key.callback = callback;
264 	search_key.callback_data = callback_data;
265 	if (g_hash_table_lookup (desktop_file->details->callbacks, &search_key) != NULL) {
266 		g_warning ("tried to add a new callback while an old one was pending");
267 		return;
268 	}
269 
270 	/* Create a desktop_callback record. */
271 	desktop_callback = g_new0 (DesktopCallback, 1);
272 	nautilus_file_ref (file);
273 	desktop_callback->desktop_file = desktop_file;
274 	desktop_callback->callback = callback;
275 	desktop_callback->callback_data = callback_data;
276 	desktop_callback->initializing = TRUE;
277 
278 	partition_attributes (attributes,
279 			      &desktop_callback->delegated_attributes,
280 			      &desktop_callback->non_delegated_attributes);
281 
282 	desktop_callback->non_ready_files = g_list_prepend
283 		(desktop_callback->non_ready_files, file);
284 	desktop_callback->non_ready_files = g_list_prepend
285 		(desktop_callback->non_ready_files, desktop_file->details->real_dir_file);
286 	
287 	/* Put it in the hash table. */
288 	g_hash_table_insert (desktop_file->details->callbacks,
289 			     desktop_callback, desktop_callback);
290 
291 	/* Now connect to each file's call_when_ready. */
292 	nautilus_directory_call_when_ready_internal
293 		(file->details->directory, file,
294 		 desktop_callback->non_delegated_attributes,
295 		 FALSE, NULL, ready_callback, desktop_callback);
296 	nautilus_file_call_when_ready
297 			(desktop_file->details->real_dir_file,
298 			 desktop_callback->delegated_attributes,
299 			 ready_callback, desktop_callback);
300 
301 	desktop_callback->initializing = FALSE;
302 
303 	/* Check if any files became read while we were connecting up
304 	 * the call_when_ready callbacks (also handles the pathological
305 	 * case where there are no files at all).
306 	 */
307 	desktop_callback_check_done (desktop_callback);
308 
309 }
310 
311 static void
312 desktop_directory_file_cancel_call_when_ready (NautilusFile *file,
313 					       NautilusFileCallback callback,
314 					       gpointer callback_data)
315 {
316 	NautilusDesktopDirectoryFile *desktop_file;
317 	DesktopCallback search_key, *desktop_callback;
318 
319 	desktop_file = NAUTILUS_DESKTOP_DIRECTORY_FILE (file);
320 
321 	/* Find the entry in the table. */
322 	search_key.callback = callback;
323 	search_key.callback_data = callback_data;
324 	desktop_callback = g_hash_table_lookup (desktop_file->details->callbacks, &search_key);
325 	if (desktop_callback == NULL) {
326 		return;
327 	}
328 
329 	/* Remove from the hash table before working with it. */
330 	g_hash_table_remove (desktop_callback->desktop_file->details->callbacks, desktop_callback);
331 
332 	/* Tell the real directory to cancel the call. */
333 	nautilus_directory_cancel_callback_internal
334 		(file->details->directory, file,
335 		 NULL, ready_callback, desktop_callback);
336 	
337 	nautilus_file_cancel_call_when_ready
338 		(desktop_file->details->real_dir_file,
339 		 ready_callback, desktop_callback);
340 	
341 	desktop_callback_destroy (desktop_callback);
342 }
343 
344 static gboolean
345 real_check_if_ready (NautilusFile *file,
346 		     NautilusFileAttributes attributes)
347 {
348 	return nautilus_directory_check_if_ready_internal
349 		(file->details->directory, file,
350 		 attributes);
351 }
352 
353 static gboolean
354 desktop_directory_file_check_if_ready (NautilusFile *file,
355 				       NautilusFileAttributes attributes)
356 {
357 	NautilusFileAttributes delegated_attributes, non_delegated_attributes;
358 	NautilusDesktopDirectoryFile *desktop_file;
359 
360 	desktop_file = NAUTILUS_DESKTOP_DIRECTORY_FILE (file);
361 
362 	partition_attributes (attributes,
363 			      &delegated_attributes,
364 			      &non_delegated_attributes);
365 
366 	return real_check_if_ready (file, non_delegated_attributes) &&
367 		nautilus_file_check_if_ready (desktop_file->details->real_dir_file,
368 					      delegated_attributes);
369 }
370 
371 static gboolean
372 desktop_directory_file_get_item_count (NautilusFile *file, 
373 				       guint *count,
374 				       gboolean *count_unreadable)
375 {
376 	NautilusDesktopDirectoryFile *desktop_file;
377 	gboolean got_count;
378 	
379 	desktop_file = NAUTILUS_DESKTOP_DIRECTORY_FILE (file);
380 	
381 	got_count = nautilus_file_get_directory_item_count (desktop_file->details->real_dir_file,
382 							    count,
383 							    count_unreadable);
384 
385 	if (count) {
386 		*count += g_list_length (file->details->directory->details->file_list);
387 	}
388 	
389 	return got_count;
390 }
391 
392 static NautilusRequestStatus
393 desktop_directory_file_get_deep_counts (NautilusFile *file,
394 					guint *directory_count,
395 					guint *file_count,
396 					guint *unreadable_directory_count,
397 					goffset *total_size)
398 {
399 	NautilusDesktopDirectoryFile *desktop_file;
400 	NautilusRequestStatus status;
401 
402 	desktop_file = NAUTILUS_DESKTOP_DIRECTORY_FILE (file);
403 	
404 	status = nautilus_file_get_deep_counts (desktop_file->details->real_dir_file,
405 						directory_count,
406 						file_count,
407 						unreadable_directory_count,
408 						total_size,
409 						TRUE);
410 
411 	if (file_count) {
412 		*file_count += g_list_length (file->details->directory->details->file_list);
413 	}
414 	
415 	return status;
416 }
417 
418 static gboolean
419 desktop_directory_file_get_date (NautilusFile *file,
420 				 NautilusDateType date_type,
421 				 time_t *date)
422 {
423 	NautilusDesktopDirectoryFile *desktop_file;
424 
425 	desktop_file = NAUTILUS_DESKTOP_DIRECTORY_FILE (file);
426 
427 	return nautilus_file_get_date (desktop_file->details->real_dir_file,
428 				       date_type,
429 				       date);
430 }
431 
432 static char *
433 desktop_directory_file_get_where_string (NautilusFile *file)
434 {
435 	return g_strdup (_("on the desktop"));
436 }
437 
438 
439 static void
440 monitor_destroy (gpointer data)
441 {
442 	DesktopMonitor *monitor = data;
443 	
444 	nautilus_file_monitor_remove
445 		(NAUTILUS_FILE (monitor->desktop_file->details->real_dir_file), monitor);
446 	g_free (monitor);
447 }
448 
449 static void
450 nautilus_desktop_directory_file_set_metadata (NautilusFile           *file,
451 					      const char             *key,
452 					      const char             *value)
453 {
454 	nautilus_desktop_set_metadata_string (file, "directory", key, value);
455 }
456 
457 static void
458 nautilus_desktop_directory_file_set_metadata_as_list (NautilusFile           *file,
459 						      const char             *key,
460 						      char                  **value)
461 {
462 	nautilus_desktop_set_metadata_stringv (file, "directory", key, (const gchar **) value);
463 }
464 
465 static void
466 nautilus_desktop_directory_file_init (NautilusDesktopDirectoryFile *desktop_file)
467 {
468 	NautilusDesktopDirectory *desktop_directory;
469 	NautilusDirectory *real_dir;
470 	NautilusFile *real_dir_file;
471 
472 	desktop_file->details = G_TYPE_INSTANCE_GET_PRIVATE (desktop_file,
473 							     NAUTILUS_TYPE_DESKTOP_DIRECTORY_FILE,
474 							     NautilusDesktopDirectoryFileDetails);
475 
476 	desktop_directory = NAUTILUS_DESKTOP_DIRECTORY (nautilus_directory_get_by_uri (EEL_DESKTOP_URI));
477 	desktop_file->details->desktop_directory = desktop_directory;
478 
479 	desktop_file->details->callbacks = g_hash_table_new
480 		(desktop_callback_hash, desktop_callback_equal);
481 	desktop_file->details->monitors = g_hash_table_new_full (NULL, NULL,
482 								 NULL, monitor_destroy);
483 
484 	real_dir = nautilus_desktop_directory_get_real_directory (desktop_directory);
485 	real_dir_file = nautilus_directory_get_corresponding_file (real_dir);
486 	nautilus_directory_unref (real_dir);
487 	
488 	desktop_file->details->real_dir_file = real_dir_file;
489 	g_signal_connect_object (real_dir_file, "changed",
490 				 G_CALLBACK (real_file_changed_callback), desktop_file, 0);
491 }
492 
493 
494 static void
495 desktop_callback_remove_file_cover (gpointer key,
496 				    gpointer value,
497 				    gpointer callback_data)
498 {
499 	desktop_callback_remove_file
500 		(value, NAUTILUS_FILE (callback_data));
501 }
502 
503 
504 static void
505 desktop_finalize (GObject *object)
506 {
507 	NautilusDesktopDirectoryFile *desktop_file;
508 	NautilusDesktopDirectory *desktop_directory;
509 
510 	desktop_file = NAUTILUS_DESKTOP_DIRECTORY_FILE (object);
511 	desktop_directory = desktop_file->details->desktop_directory;
512 
513 	/* Todo: ghash now safe? */
514 	eel_g_hash_table_safe_for_each
515 		(desktop_file->details->callbacks,
516 		 desktop_callback_remove_file_cover,
517 		 desktop_file->details->real_dir_file);
518 	
519 	if (g_hash_table_size (desktop_file->details->callbacks) != 0) {
520 		g_warning ("call_when_ready still pending when desktop virtual file is destroyed");
521 	}
522 
523 	g_hash_table_destroy (desktop_file->details->callbacks);
524 	g_hash_table_destroy (desktop_file->details->monitors);
525 
526 	nautilus_file_unref (desktop_file->details->real_dir_file);
527 	nautilus_directory_unref (NAUTILUS_DIRECTORY (desktop_directory));
528 
529 	G_OBJECT_CLASS (nautilus_desktop_directory_file_parent_class)->finalize (object);
530 }
531 
532 static void
533 nautilus_desktop_directory_file_class_init (NautilusDesktopDirectoryFileClass *klass)
534 {
535 	GObjectClass *object_class;
536 	NautilusFileClass *file_class;
537 
538 	object_class = G_OBJECT_CLASS (klass);
539 	file_class = NAUTILUS_FILE_CLASS (klass);
540 	
541 	object_class->finalize = desktop_finalize;
542 
543 	file_class->default_file_type = G_FILE_TYPE_DIRECTORY;
544 	
545 	file_class->monitor_add = desktop_directory_file_monitor_add;
546 	file_class->monitor_remove = desktop_directory_file_monitor_remove;
547 	file_class->call_when_ready = desktop_directory_file_call_when_ready;
548 	file_class->cancel_call_when_ready = desktop_directory_file_cancel_call_when_ready;
549 	file_class->check_if_ready = desktop_directory_file_check_if_ready;
550 	file_class->get_item_count = desktop_directory_file_get_item_count;
551 	file_class->get_deep_counts = desktop_directory_file_get_deep_counts;
552 	file_class->get_date = desktop_directory_file_get_date;
553 	file_class->get_where_string = desktop_directory_file_get_where_string;
554 	file_class->set_metadata = nautilus_desktop_directory_file_set_metadata;
555 	file_class->set_metadata_as_list = nautilus_desktop_directory_file_set_metadata_as_list;
556 
557 	g_type_class_add_private (klass, sizeof (NautilusDesktopDirectoryFileDetails));
558 }