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 }