]>
Commit | Line | Data |
---|---|---|
8f66dda0 PZ |
1 | diff -urN ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-item.c ./libgnome-desktop/egg-recent-item.c |
2 | --- ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-item.c 1969-12-31 19:00:00.000000000 -0500 | |
3 | +++ ./libgnome-desktop/egg-recent-item.c 2006-05-08 16:18:44.000000000 -0400 | |
4 | @@ -0,0 +1,426 @@ | |
5 | +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ | |
6 | +/* | |
7 | + * This program is free software; you can redistribute it and/or modify | |
8 | + * 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 | |
15 | + * GNU General Public License for more details. | |
16 | + * | |
17 | + * You should have received a copy of the GNU General Public License | |
18 | + * along with this program; if not, write to the Free Software | |
19 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. | |
20 | + * | |
21 | + * Authors: | |
22 | + * James Willcox <jwillcox@cs.indiana.edu> | |
23 | + */ | |
24 | + | |
25 | + | |
26 | +#include <stdio.h> | |
27 | +#include <string.h> | |
28 | +#include <glib.h> | |
29 | +#include <libgnomevfs/gnome-vfs.h> | |
30 | +#include <libgnomevfs/gnome-vfs-mime-utils.h> | |
31 | +#include "egg-recent-item.h" | |
32 | + | |
33 | + | |
34 | + | |
35 | +EggRecentItem * | |
36 | +egg_recent_item_new (void) | |
37 | +{ | |
38 | + EggRecentItem *item; | |
39 | + | |
40 | + item = g_new (EggRecentItem, 1); | |
41 | + | |
42 | + item->groups = NULL; | |
43 | + item->private_data = FALSE; | |
44 | + item->uri = NULL; | |
45 | + item->mime_type = NULL; | |
46 | + item->mime_type_is_explicit = FALSE; | |
47 | + | |
48 | + item->refcount = 1; | |
49 | + | |
50 | + return item; | |
51 | +} | |
52 | + | |
53 | +static void | |
54 | +egg_recent_item_free (EggRecentItem *item) | |
55 | +{ | |
56 | + if (item->uri) | |
57 | + g_free (item->uri); | |
58 | + | |
59 | + if (item->mime_type) | |
60 | + g_free (item->mime_type); | |
61 | + | |
62 | + if (item->groups) { | |
63 | + g_list_foreach (item->groups, (GFunc)g_free, NULL); | |
64 | + g_list_free (item->groups); | |
65 | + item->groups = NULL; | |
66 | + } | |
67 | + | |
68 | + g_free (item); | |
69 | +} | |
70 | + | |
71 | +EggRecentItem * | |
72 | +egg_recent_item_ref (EggRecentItem *item) | |
73 | +{ | |
74 | + item->refcount++; | |
75 | + return item; | |
76 | +} | |
77 | + | |
78 | +EggRecentItem * | |
79 | +egg_recent_item_unref (EggRecentItem *item) | |
80 | +{ | |
81 | + item->refcount--; | |
82 | + | |
83 | + if (item->refcount == 0) { | |
84 | + egg_recent_item_free (item); | |
85 | + } | |
86 | + | |
87 | + return item; | |
88 | +} | |
89 | + | |
90 | + | |
91 | +EggRecentItem * | |
92 | +egg_recent_item_new_from_uri (const gchar *uri) | |
93 | +{ | |
94 | + EggRecentItem *item; | |
95 | + | |
96 | + g_return_val_if_fail (uri != NULL, NULL); | |
97 | + | |
98 | + item = egg_recent_item_new (); | |
99 | + | |
100 | + if (!egg_recent_item_set_uri (item ,uri)) { | |
101 | + egg_recent_item_free (item); | |
102 | + return NULL; | |
103 | + } | |
104 | + | |
105 | + return item; | |
106 | +} | |
107 | + | |
108 | +static void | |
109 | +egg_recent_item_update_mime_type (EggRecentItem *item) | |
110 | +{ | |
111 | + if (!item->mime_type_is_explicit) { | |
112 | + g_free (item->mime_type); | |
113 | + item->mime_type = NULL; | |
114 | + | |
115 | + if (item->uri) | |
116 | + item->mime_type = gnome_vfs_get_mime_type (item->uri); | |
117 | + | |
118 | + if (!item->mime_type) | |
119 | + item->mime_type = g_strdup (GNOME_VFS_MIME_TYPE_UNKNOWN); | |
120 | + } | |
121 | +} | |
122 | + | |
123 | +gboolean | |
124 | +egg_recent_item_set_uri (EggRecentItem *item, const gchar *uri) | |
125 | +{ | |
126 | + gchar *utf8_uri; | |
127 | + | |
128 | + /* if G_BROKEN_FILENAMES is not set, this should succede */ | |
129 | + if (g_utf8_validate (uri, -1, NULL)) { | |
130 | + item->uri = gnome_vfs_make_uri_from_input (uri); | |
131 | + } else { | |
132 | + utf8_uri = g_filename_to_utf8 (uri, -1, NULL, NULL, NULL); | |
133 | + | |
134 | + if (utf8_uri == NULL) { | |
135 | + g_warning ("Couldn't convert URI to UTF-8"); | |
136 | + return FALSE; | |
137 | + } | |
138 | + | |
139 | + if (g_utf8_validate (utf8_uri, -1, NULL)) { | |
140 | + item->uri = gnome_vfs_make_uri_from_input (utf8_uri); | |
141 | + } else { | |
142 | + g_free (utf8_uri); | |
143 | + return FALSE; | |
144 | + } | |
145 | + | |
146 | + g_free (utf8_uri); | |
147 | + } | |
148 | + | |
149 | + return TRUE; | |
150 | +} | |
151 | + | |
152 | +gchar * | |
153 | +egg_recent_item_get_uri (const EggRecentItem *item) | |
154 | +{ | |
155 | + return g_strdup (item->uri); | |
156 | +} | |
157 | + | |
158 | +G_CONST_RETURN gchar * | |
159 | +egg_recent_item_peek_uri (const EggRecentItem *item) | |
160 | +{ | |
161 | + return item->uri; | |
162 | +} | |
163 | + | |
164 | +gchar * | |
165 | +egg_recent_item_get_uri_utf8 (const EggRecentItem *item) | |
166 | +{ | |
167 | + /* this could fail, but it's not likely, since we've already done it | |
168 | + * once in set_uri() | |
169 | + */ | |
170 | + return g_filename_to_utf8 (item->uri, -1, NULL, NULL, NULL); | |
171 | +} | |
172 | + | |
173 | +gchar * | |
174 | +egg_recent_item_get_uri_for_display (const EggRecentItem *item) | |
175 | +{ | |
176 | + return gnome_vfs_format_uri_for_display (item->uri); | |
177 | +} | |
178 | + | |
179 | +/* Stolen from gnome_vfs_make_valid_utf8() */ | |
180 | +static char * | |
181 | +make_valid_utf8 (const char *name) | |
182 | +{ | |
183 | + GString *string; | |
184 | + const char *remainder, *invalid; | |
185 | + int remaining_bytes, valid_bytes; | |
186 | + | |
187 | + string = NULL; | |
188 | + remainder = name; | |
189 | + remaining_bytes = name ? strlen (name) : 0; | |
190 | + | |
191 | + while (remaining_bytes != 0) { | |
192 | + if (g_utf8_validate (remainder, remaining_bytes, &invalid)) | |
193 | + break; | |
194 | + | |
195 | + valid_bytes = invalid - remainder; | |
196 | + | |
197 | + if (string == NULL) | |
198 | + string = g_string_sized_new (remaining_bytes); | |
199 | + | |
200 | + g_string_append_len (string, remainder, valid_bytes); | |
201 | + g_string_append_c (string, '?'); | |
202 | + | |
203 | + remaining_bytes -= valid_bytes + 1; | |
204 | + remainder = invalid + 1; | |
205 | + } | |
206 | + | |
207 | + if (string == NULL) | |
208 | + return g_strdup (name); | |
209 | + | |
210 | + g_string_append (string, remainder); | |
211 | +/* g_string_append (string, _(" (invalid file name)")); */ | |
212 | + g_assert (g_utf8_validate (string->str, -1, NULL)); | |
213 | + | |
214 | + return g_string_free (string, FALSE); | |
215 | +} | |
216 | + | |
217 | +static gchar * | |
218 | +get_uri_shortname_for_display (GnomeVFSURI *uri) | |
219 | +{ | |
220 | + gchar *name; | |
221 | + gboolean validated; | |
222 | + | |
223 | + validated = FALSE; | |
224 | + name = gnome_vfs_uri_extract_short_name (uri); | |
225 | + | |
226 | + if (name == NULL) | |
227 | + { | |
228 | + name = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_PASSWORD); | |
229 | + } | |
230 | + else if (g_ascii_strcasecmp (uri->method_string, "file") == 0) | |
231 | + { | |
232 | + gchar *text_uri; | |
233 | + gchar *local_file; | |
234 | + text_uri = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_PASSWORD); | |
235 | + local_file = gnome_vfs_get_local_path_from_uri (text_uri); | |
236 | + | |
237 | + if (local_file != NULL) | |
238 | + { | |
239 | + g_free (name); | |
240 | + name = g_filename_display_basename (local_file); | |
241 | + validated = TRUE; | |
242 | + } | |
243 | + | |
244 | + g_free (local_file); | |
245 | + g_free (text_uri); | |
246 | + } | |
247 | + else if (!gnome_vfs_uri_has_parent (uri)) | |
248 | + { | |
249 | + const gchar *method; | |
250 | + | |
251 | + method = uri->method_string; | |
252 | + | |
253 | + if (name == NULL || | |
254 | + strcmp (name, GNOME_VFS_URI_PATH_STR) == 0) | |
255 | + { | |
256 | + g_free (name); | |
257 | + name = g_strdup (method); | |
258 | + } | |
259 | + else | |
260 | + { | |
261 | + gchar *tmp; | |
262 | + | |
263 | + tmp = name; | |
264 | + name = g_strdup_printf ("%s: %s", method, name); | |
265 | + g_free (tmp); | |
266 | + } | |
267 | + } | |
268 | + | |
269 | + if (!validated && !g_utf8_validate (name, -1, NULL)) | |
270 | + { | |
271 | + gchar *utf8_name; | |
272 | + | |
273 | + utf8_name = make_valid_utf8 (name); | |
274 | + g_free (name); | |
275 | + name = utf8_name; | |
276 | + } | |
277 | + | |
278 | + return name; | |
279 | +} | |
280 | + | |
281 | +/** | |
282 | + * egg_recent_item_get_short_name: | |
283 | + * @item: an #EggRecentItem | |
284 | + * | |
285 | + * Computes a valid UTF-8 string that can be used as the name of the item in a | |
286 | + * menu or list. For example, calling this function on an item that refers to | |
287 | + * "file:///foo/bar.txt" will yield "bar.txt". | |
288 | + * | |
289 | + * Return value: A newly-allocated string in UTF-8 encoding; free it with | |
290 | + * g_free(). | |
291 | + **/ | |
292 | +gchar * | |
293 | +egg_recent_item_get_short_name (const EggRecentItem *item) | |
294 | +{ | |
295 | + GnomeVFSURI *uri; | |
296 | + gchar *short_name; | |
297 | + | |
298 | + g_return_val_if_fail (item != NULL, NULL); | |
299 | + | |
300 | + if (item->uri == NULL) | |
301 | + return NULL; | |
302 | + | |
303 | + uri = gnome_vfs_uri_new (item->uri); | |
304 | + if (uri == NULL) | |
305 | + return NULL; | |
306 | + | |
307 | + short_name = get_uri_shortname_for_display (uri); | |
308 | + | |
309 | + gnome_vfs_uri_unref (uri); | |
310 | + | |
311 | + return short_name; | |
312 | +} | |
313 | + | |
314 | +void | |
315 | +egg_recent_item_set_mime_type (EggRecentItem *item, const gchar *mime) | |
316 | +{ | |
317 | + g_free (item->mime_type); | |
318 | + item->mime_type = NULL; | |
319 | + | |
320 | + if (mime && mime[0]) { | |
321 | + item->mime_type_is_explicit = TRUE; | |
322 | + item->mime_type = g_strdup (mime); | |
323 | + } else { | |
324 | + item->mime_type_is_explicit = FALSE; | |
325 | + } | |
326 | +} | |
327 | + | |
328 | +gchar * | |
329 | +egg_recent_item_get_mime_type (EggRecentItem *item) | |
330 | +{ | |
331 | + egg_recent_item_update_mime_type (item); | |
332 | + | |
333 | + return g_strdup (item->mime_type); | |
334 | +} | |
335 | + | |
336 | +void | |
337 | +egg_recent_item_set_timestamp (EggRecentItem *item, time_t timestamp) | |
338 | +{ | |
339 | + if (timestamp == (time_t) -1) | |
340 | + time (×tamp); | |
341 | + | |
342 | + item->timestamp = timestamp; | |
343 | +} | |
344 | + | |
345 | +time_t | |
346 | +egg_recent_item_get_timestamp (const EggRecentItem *item) | |
347 | +{ | |
348 | + return item->timestamp; | |
349 | +} | |
350 | + | |
351 | +G_CONST_RETURN GList * | |
352 | +egg_recent_item_get_groups (const EggRecentItem *item) | |
353 | +{ | |
354 | + return item->groups; | |
355 | +} | |
356 | + | |
357 | +gboolean | |
358 | +egg_recent_item_in_group (const EggRecentItem *item, const gchar *group_name) | |
359 | +{ | |
360 | + GList *tmp; | |
361 | + | |
362 | + tmp = item->groups; | |
363 | + while (tmp != NULL) { | |
364 | + gchar *val = (gchar *)tmp->data; | |
365 | + | |
366 | + if (strcmp (group_name, val) == 0) | |
367 | + return TRUE; | |
368 | + | |
369 | + tmp = tmp->next; | |
370 | + } | |
371 | + | |
372 | + return FALSE; | |
373 | +} | |
374 | + | |
375 | +void | |
376 | +egg_recent_item_add_group (EggRecentItem *item, const gchar *group_name) | |
377 | +{ | |
378 | + g_return_if_fail (group_name != NULL); | |
379 | + | |
380 | + if (!egg_recent_item_in_group (item, group_name)) | |
381 | + item->groups = g_list_append (item->groups, g_strdup (group_name)); | |
382 | +} | |
383 | + | |
384 | +void | |
385 | +egg_recent_item_remove_group (EggRecentItem *item, const gchar *group_name) | |
386 | +{ | |
387 | + GList *tmp; | |
388 | + | |
389 | + g_return_if_fail (group_name != NULL); | |
390 | + | |
391 | + tmp = item->groups; | |
392 | + while (tmp != NULL) { | |
393 | + gchar *val = (gchar *)tmp->data; | |
394 | + | |
395 | + if (strcmp (group_name, val) == 0) { | |
396 | + item->groups = g_list_remove (item->groups, | |
397 | + val); | |
398 | + g_free (val); | |
399 | + break; | |
400 | + } | |
401 | + | |
402 | + tmp = tmp->next; | |
403 | + } | |
404 | +} | |
405 | + | |
406 | +void | |
407 | +egg_recent_item_set_private (EggRecentItem *item, gboolean priv) | |
408 | +{ | |
409 | + item->private_data = priv; | |
410 | +} | |
411 | + | |
412 | +gboolean | |
413 | +egg_recent_item_get_private (const EggRecentItem *item) | |
414 | +{ | |
415 | + return item->private_data; | |
416 | +} | |
417 | + | |
418 | +GType | |
419 | +egg_recent_item_get_type (void) | |
420 | +{ | |
421 | + static GType boxed_type = 0; | |
422 | + | |
423 | + if (!boxed_type) { | |
424 | + boxed_type = g_boxed_type_register_static ("EggRecentItem", | |
425 | + (GBoxedCopyFunc)egg_recent_item_ref, | |
426 | + (GBoxedFreeFunc)egg_recent_item_unref); | |
427 | + } | |
428 | + | |
429 | + return boxed_type; | |
430 | +} | |
431 | diff -urN ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-item.h ./libgnome-desktop/egg-recent-item.h | |
432 | --- ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-item.h 1969-12-31 19:00:00.000000000 -0500 | |
433 | +++ ./libgnome-desktop/egg-recent-item.h 2006-05-08 16:18:44.000000000 -0400 | |
434 | @@ -0,0 +1,80 @@ | |
435 | + | |
436 | +#ifndef __EGG_RECENT_ITEM_H__ | |
437 | +#define __EGG_RECENT_ITEM_H__ | |
438 | + | |
439 | +#include <time.h> | |
440 | +#include <glib.h> | |
441 | +#include <glib-object.h> | |
442 | + | |
443 | +G_BEGIN_DECLS | |
444 | + | |
445 | +#define EGG_TYPE_RECENT_ITEM (egg_recent_item_get_type ()) | |
446 | + | |
447 | +#define EGG_RECENT_ITEM_LIST_UNREF(list) \ | |
448 | + g_list_foreach (list, (GFunc)egg_recent_item_unref, NULL); \ | |
449 | + g_list_free (list); | |
450 | + | |
451 | +typedef struct _EggRecentItem EggRecentItem; | |
452 | + | |
453 | +struct _EggRecentItem { | |
454 | + /* do not access any of these directly */ | |
455 | + gchar *uri; | |
456 | + gchar *mime_type; | |
457 | + time_t timestamp; | |
458 | + | |
459 | + gboolean private_data; | |
460 | + | |
461 | + GList *groups; | |
462 | + | |
463 | + int refcount; | |
464 | + | |
465 | + guint mime_type_is_explicit : 1; | |
466 | +}; | |
467 | + | |
468 | +GType egg_recent_item_get_type (void) G_GNUC_CONST; | |
469 | + | |
470 | +/* constructors */ | |
471 | +EggRecentItem * egg_recent_item_new (void); | |
472 | + | |
473 | +EggRecentItem * egg_recent_item_ref (EggRecentItem *item); | |
474 | +EggRecentItem * egg_recent_item_unref (EggRecentItem *item); | |
475 | + | |
476 | +/* automatically fetches the mime type, etc */ | |
477 | +EggRecentItem * egg_recent_item_new_from_uri (const gchar *uri); | |
478 | + | |
479 | +gboolean egg_recent_item_set_uri (EggRecentItem *item, const gchar *uri); | |
480 | +gchar * egg_recent_item_get_uri (const EggRecentItem *item); | |
481 | +gchar * egg_recent_item_get_uri_utf8 (const EggRecentItem *item); | |
482 | +gchar * egg_recent_item_get_uri_for_display (const EggRecentItem *item); | |
483 | +gchar * egg_recent_item_get_short_name (const EggRecentItem *item); | |
484 | + | |
485 | +void egg_recent_item_set_mime_type (EggRecentItem *item, const gchar *mime); | |
486 | +gchar * egg_recent_item_get_mime_type (EggRecentItem *item); | |
487 | + | |
488 | +void egg_recent_item_set_timestamp (EggRecentItem *item, time_t timestamp); | |
489 | +time_t egg_recent_item_get_timestamp (const EggRecentItem *item); | |
490 | + | |
491 | +G_CONST_RETURN gchar *egg_recent_item_peek_uri (const EggRecentItem *item); | |
492 | + | |
493 | + | |
494 | +/* groups */ | |
495 | +G_CONST_RETURN GList * egg_recent_item_get_groups (const EggRecentItem *item); | |
496 | + | |
497 | +gboolean egg_recent_item_in_group (const EggRecentItem *item, | |
498 | + const gchar *group_name); | |
499 | + | |
500 | +void egg_recent_item_add_group (EggRecentItem *item, | |
501 | + const gchar *group_name); | |
502 | + | |
503 | +void egg_recent_item_remove_group (EggRecentItem *item, | |
504 | + const gchar *group_name); | |
505 | + | |
506 | +void egg_recent_item_set_private (EggRecentItem *item, | |
507 | + gboolean priv); | |
508 | + | |
509 | +gboolean egg_recent_item_get_private (const EggRecentItem *item); | |
510 | + | |
511 | + | |
512 | +G_END_DECLS | |
513 | + | |
514 | +#endif /* __EGG_RECENT_ITEM_H__ */ | |
515 | diff -urN ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-model-multi.c ./libgnome-desktop/egg-recent-model-multi.c | |
516 | --- ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-model-multi.c 1969-12-31 19:00:00.000000000 -0500 | |
517 | +++ ./libgnome-desktop/egg-recent-model-multi.c 2006-05-08 17:03:13.000000000 -0400 | |
518 | @@ -0,0 +1,1990 @@ | |
519 | +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ | |
520 | +/* | |
521 | + * This program is free software; you can redistribute it and/or modify | |
522 | + * it under the terms of the GNU General Public License as | |
523 | + * published by the Free Software Foundation; either version 2 of the | |
524 | + * License, or (at your option) any later version. | |
525 | + * | |
526 | + * This program is distributed in the hope that it will be useful, | |
527 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
528 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
529 | + * GNU General Public License for more details. | |
530 | + * | |
531 | + * You should have received a copy of the GNU General Public License | |
532 | + * along with this program; if not, write to the Free Software | |
533 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. | |
534 | + * | |
535 | + * Authors: | |
536 | + * James Willcox <jwillcox@cs.indiana.edu> | |
537 | + */ | |
538 | + | |
539 | +#ifdef HAVE_CONFIG_H | |
540 | +#include <config.h> | |
541 | +#endif | |
542 | + | |
543 | +#include <stdio.h> | |
544 | +#include <string.h> | |
545 | +#include <errno.h> | |
546 | +#include <stdlib.h> | |
547 | +#include <unistd.h> | |
548 | +#include <fcntl.h> | |
549 | +#include <sys/time.h> | |
550 | +#include <sys/stat.h> | |
551 | +#include <time.h> | |
552 | +#include <gtk/gtk.h> | |
553 | +#include <libgnomevfs/gnome-vfs.h> | |
554 | +#include <libgnomevfs/gnome-vfs-mime-utils.h> | |
555 | +#include <gconf/gconf-client.h> | |
556 | +#include "egg-recent-model-multi.h" | |
557 | +#include "egg-recent-item.h" | |
558 | + | |
559 | +#define EGG_RECENT_MODEL_MULTI_DEFAULT_FILE_PATH ".recently-used" | |
560 | +#define EGG_RECENT_MODEL_MULTI_BUFFER_SIZE 8192 | |
561 | + | |
562 | +#define EGG_RECENT_MODEL_MULTI_MAX_ITEMS 500 | |
563 | +#define EGG_RECENT_MODEL_MULTI_DEFAULT_LIMIT 10 | |
564 | +#define EGG_RECENT_MODEL_MULTI_TIMEOUT_LENGTH 200 | |
565 | +#define EGG_RECENT_MODEL_MULTI_POLL_TIME 3 | |
566 | + | |
567 | +/* needed for Darwin */ | |
568 | +#if !HAVE_DECL_LOCKF | |
569 | +int lockf (int filedes, int function, off_t size); | |
570 | +#endif | |
571 | + | |
572 | +#define EGG_RECENT_MODEL_MULTI_KEY_DIR "/desktop/gnome/recent_files" | |
573 | +#define EGG_RECENT_MODEL_MULTI_DEFAULT_LIMIT_KEY EGG_RECENT_MODEL_MULTI_KEY_DIR "/default_limit" | |
574 | +#define EGG_RECENT_MODEL_MULTI_EXPIRE_KEY EGG_RECENT_MODEL_MULTI_KEY_DIR "/expire" | |
575 | + | |
576 | +struct _EggRecentModelMultiPrivate { | |
577 | + GSList *mime_filter_values; /* list of mime types we allow */ | |
578 | + GSList *group_filter_values; /* list of groups we allow */ | |
579 | + GSList *scheme_filter_values; /* list of URI schemes we allow */ | |
580 | + | |
581 | + EggRecentModelMultiSort sort_type; /* type of sorting to be done */ | |
582 | + | |
583 | + int limit; /* soft limit for length of the list */ | |
584 | + int expire_days; /* number of days to hold an item */ | |
585 | + | |
586 | + char *path; /* path to the file we store stuff in */ | |
587 | + | |
588 | + GHashTable *monitors; | |
589 | + | |
590 | + GnomeVFSMonitorHandle *monitor; | |
591 | + | |
592 | + GConfClient *client; | |
593 | + gboolean use_default_limit; | |
594 | + | |
595 | + guint limit_change_notify_id; | |
596 | + guint expiration_change_notify_id; | |
597 | + | |
598 | + guint changed_timeout; | |
599 | + guint poll_timeout; | |
600 | + time_t last_mtime; | |
601 | +}; | |
602 | + | |
603 | +/* signals */ | |
604 | +enum { | |
605 | + CHANGED, | |
606 | + LAST_SIGNAL | |
607 | +}; | |
608 | + | |
609 | +static GType model_signals[LAST_SIGNAL] = { 0 }; | |
610 | + | |
611 | +/* properties */ | |
612 | +enum { | |
613 | + PROP_BOGUS, | |
614 | + PROP_MIME_FILTERS, | |
615 | + PROP_GROUP_FILTERS, | |
616 | + PROP_SCHEME_FILTERS, | |
617 | + PROP_SORT_TYPE, | |
618 | + PROP_LIMIT, | |
619 | + PROP_FILE_PATH | |
620 | +}; | |
621 | + | |
622 | +typedef struct { | |
623 | + GSList *states; | |
624 | + GList *items; | |
625 | + EggRecentItem *current_item; | |
626 | +} ParseInfo; | |
627 | + | |
628 | +typedef enum { | |
629 | + STATE_START, | |
630 | + STATE_RECENT_FILES, | |
631 | + STATE_RECENT_ITEM, | |
632 | + STATE_URI, | |
633 | + STATE_MIME_TYPE, | |
634 | + STATE_TIMESTAMP, | |
635 | + STATE_PRIVATE, | |
636 | + STATE_GROUPS, | |
637 | + STATE_GROUP | |
638 | +} ParseState; | |
639 | + | |
640 | +typedef struct { | |
641 | + EggRecentModelMulti *model; | |
642 | + GList *list; | |
643 | +} ChangedData; | |
644 | + | |
645 | +#define TAG_RECENT_FILES "RecentFiles" | |
646 | +#define TAG_RECENT_ITEM "RecentItem" | |
647 | +#define TAG_URI "URI" | |
648 | +#define TAG_MIME_TYPE "Mime-Type" | |
649 | +#define TAG_TIMESTAMP "Timestamp" | |
650 | +#define TAG_PRIVATE "Private" | |
651 | +#define TAG_GROUPS "Groups" | |
652 | +#define TAG_GROUP "Group" | |
653 | + | |
654 | +static void start_element_handler (GMarkupParseContext *context, | |
655 | + const gchar *element_name, | |
656 | + const gchar **attribute_names, | |
657 | + const gchar **attribute_values, | |
658 | + gpointer user_data, | |
659 | + GError **error); | |
660 | + | |
661 | +static void end_element_handler (GMarkupParseContext *context, | |
662 | + const gchar *element_name, | |
663 | + gpointer user_data, | |
664 | + GError **error); | |
665 | + | |
666 | +static void text_handler (GMarkupParseContext *context, | |
667 | + const gchar *text, | |
668 | + gsize text_len, | |
669 | + gpointer user_data, | |
670 | + GError **error); | |
671 | + | |
672 | +static void error_handler (GMarkupParseContext *context, | |
673 | + GError *error, | |
674 | + gpointer user_data); | |
675 | + | |
676 | +static GMarkupParser parser = {start_element_handler, end_element_handler, | |
677 | + text_handler, | |
678 | + NULL, | |
679 | + error_handler}; | |
680 | + | |
681 | +static GObjectClass *parent_class; | |
682 | + | |
683 | +static void egg_recent_model_multi_clear_mime_filter (EggRecentModelMulti *model); | |
684 | +static void egg_recent_model_multi_clear_group_filter (EggRecentModelMulti *model); | |
685 | +static void egg_recent_model_multi_clear_scheme_filter (EggRecentModelMulti *model); | |
686 | + | |
687 | +static GObjectClass *parent_class; | |
688 | + | |
689 | +static gboolean | |
690 | +egg_recent_model_multi_string_match (const GSList *list, const gchar *str) | |
691 | +{ | |
692 | + const GSList *tmp; | |
693 | + | |
694 | + if (list == NULL || str == NULL) | |
695 | + return TRUE; | |
696 | + | |
697 | + tmp = list; | |
698 | + | |
699 | + while (tmp) { | |
700 | + if (g_pattern_match_string (tmp->data, str)) | |
701 | + return TRUE; | |
702 | + | |
703 | + tmp = tmp->next; | |
704 | + } | |
705 | + | |
706 | + return FALSE; | |
707 | +} | |
708 | + | |
709 | +static gboolean | |
710 | +egg_recent_model_multi_write_raw (EggRecentModelMulti *model, FILE *file, | |
711 | + const gchar *content) | |
712 | +{ | |
713 | + int len; | |
714 | + int fd; | |
715 | + struct stat sbuf; | |
716 | + | |
717 | + rewind (file); | |
718 | + | |
719 | + len = strlen (content); | |
720 | + fd = fileno (file); | |
721 | + | |
722 | + if (fstat (fd, &sbuf) < 0) | |
723 | + g_warning ("Couldn't stat XML document."); | |
724 | + | |
725 | + if ((off_t)len < sbuf.st_size) { | |
726 | + ftruncate (fd, len); | |
727 | + } | |
728 | + | |
729 | + if (fputs (content, file) == EOF) | |
730 | + return FALSE; | |
731 | + | |
732 | +#ifndef G_OS_WIN32 | |
733 | + fsync (fd); | |
734 | +#endif | |
735 | + rewind (file); | |
736 | + | |
737 | + return TRUE; | |
738 | +} | |
739 | + | |
740 | +static GList * | |
741 | +egg_recent_model_multi_delete_from_list (GList *list, | |
742 | + const gchar *uri) | |
743 | +{ | |
744 | + GList *tmp; | |
745 | + | |
746 | + if (!uri) | |
747 | + return list; | |
748 | + | |
749 | + tmp = list; | |
750 | + | |
751 | + while (tmp) { | |
752 | + EggRecentItem *item = tmp->data; | |
753 | + GList *next; | |
754 | + | |
755 | + next = tmp->next; | |
756 | + | |
757 | + if (!strcmp (egg_recent_item_peek_uri (item), uri)) { | |
758 | + egg_recent_item_unref (item); | |
759 | + | |
760 | + list = g_list_remove_link (list, tmp); | |
761 | + g_list_free_1 (tmp); | |
762 | + } | |
763 | + | |
764 | + tmp = next; | |
765 | + } | |
766 | + | |
767 | + return list; | |
768 | +} | |
769 | + | |
770 | +static void | |
771 | +egg_recent_model_multi_add_new_groups (EggRecentItem *item, | |
772 | + EggRecentItem *upd_item) | |
773 | +{ | |
774 | + const GList *tmp; | |
775 | + | |
776 | + tmp = egg_recent_item_get_groups (upd_item); | |
777 | + | |
778 | + while (tmp) { | |
779 | + char *group = tmp->data; | |
780 | + | |
781 | + if (!egg_recent_item_in_group (item, group)) | |
782 | + egg_recent_item_add_group (item, group); | |
783 | + | |
784 | + tmp = tmp->next; | |
785 | + } | |
786 | +} | |
787 | + | |
788 | +static gboolean | |
789 | +egg_recent_model_multi_update_item (GList *items, EggRecentItem *upd_item) | |
790 | +{ | |
791 | + GList *tmp; | |
792 | + const char *uri; | |
793 | + | |
794 | + uri = egg_recent_item_peek_uri (upd_item); | |
795 | + | |
796 | + tmp = items; | |
797 | + | |
798 | + while (tmp) { | |
799 | + EggRecentItem *item = tmp->data; | |
800 | + | |
801 | + if (gnome_vfs_uris_match (egg_recent_item_peek_uri (item), uri)) { | |
802 | + egg_recent_item_set_timestamp (item, (time_t) -1); | |
803 | + | |
804 | + egg_recent_model_multi_add_new_groups (item, upd_item); | |
805 | + | |
806 | + return TRUE; | |
807 | + } | |
808 | + | |
809 | + tmp = tmp->next; | |
810 | + } | |
811 | + | |
812 | + return FALSE; | |
813 | +} | |
814 | + | |
815 | +static gchar * | |
816 | +egg_recent_model_multi_read_raw (EggRecentModelMulti *model, FILE *file) | |
817 | +{ | |
818 | + GString *string; | |
819 | + char buf[EGG_RECENT_MODEL_MULTI_BUFFER_SIZE]; | |
820 | + | |
821 | + rewind (file); | |
822 | + | |
823 | + string = g_string_new (NULL); | |
824 | + while (fgets (buf, EGG_RECENT_MODEL_MULTI_BUFFER_SIZE, file)) { | |
825 | + string = g_string_append (string, buf); | |
826 | + } | |
827 | + | |
828 | + rewind (file); | |
829 | + | |
830 | + return g_string_free (string, FALSE); | |
831 | +} | |
832 | + | |
833 | + | |
834 | + | |
835 | +static ParseInfo * | |
836 | +parse_info_init (void) | |
837 | +{ | |
838 | + ParseInfo *retval; | |
839 | + | |
840 | + retval = g_new0 (ParseInfo, 1); | |
841 | + retval->states = g_slist_prepend (NULL, STATE_START); | |
842 | + retval->items = NULL; | |
843 | + | |
844 | + return retval; | |
845 | +} | |
846 | + | |
847 | +static void | |
848 | +parse_info_free (ParseInfo *info) | |
849 | +{ | |
850 | + g_slist_free (info->states); | |
851 | + g_free (info); | |
852 | +} | |
853 | + | |
854 | +static void | |
855 | +push_state (ParseInfo *info, | |
856 | + ParseState state) | |
857 | +{ | |
858 | + info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state)); | |
859 | +} | |
860 | + | |
861 | +static void | |
862 | +pop_state (ParseInfo *info) | |
863 | +{ | |
864 | + g_return_if_fail (info->states != NULL); | |
865 | + | |
866 | + info->states = g_slist_remove (info->states, info->states->data); | |
867 | +} | |
868 | + | |
869 | +static ParseState | |
870 | +peek_state (ParseInfo *info) | |
871 | +{ | |
872 | + g_return_val_if_fail (info->states != NULL, STATE_START); | |
873 | + | |
874 | + return GPOINTER_TO_INT (info->states->data); | |
875 | +} | |
876 | + | |
877 | +#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0) | |
878 | + | |
879 | +static gboolean | |
880 | +valid_element (ParseInfo *info, | |
881 | + int valid_parent_state, | |
882 | + const gchar *element_name, | |
883 | + const gchar *valid_element, | |
884 | + GError **error) | |
885 | +{ | |
886 | + if (peek_state (info) != valid_parent_state) { | |
887 | + g_set_error (error, | |
888 | + G_MARKUP_ERROR, | |
889 | + G_MARKUP_ERROR_INVALID_CONTENT, | |
890 | + "Unexpected tag '%s', tag '%s' expected", | |
891 | + element_name, valid_element); | |
892 | + return FALSE; | |
893 | + } | |
894 | + | |
895 | + return TRUE; | |
896 | +} | |
897 | + | |
898 | +static void | |
899 | +start_element_handler (GMarkupParseContext *context, | |
900 | + const gchar *element_name, | |
901 | + const gchar **attribute_names, | |
902 | + const gchar **attribute_values, | |
903 | + gpointer user_data, | |
904 | + GError **error) | |
905 | +{ | |
906 | + ParseInfo *info = (ParseInfo *)user_data; | |
907 | + | |
908 | + if (ELEMENT_IS (TAG_RECENT_FILES)) | |
909 | + push_state (info, STATE_RECENT_FILES); | |
910 | + else if (ELEMENT_IS (TAG_RECENT_ITEM)) { | |
911 | + if (valid_element (info, STATE_RECENT_FILES, | |
912 | + TAG_RECENT_ITEM, TAG_RECENT_FILES, error)) { | |
913 | + info->current_item = egg_recent_item_new (); | |
914 | + push_state (info, STATE_RECENT_ITEM); | |
915 | + } | |
916 | + } else if (ELEMENT_IS (TAG_URI)) { | |
917 | + if (valid_element (info, STATE_RECENT_ITEM, | |
918 | + TAG_URI, TAG_RECENT_ITEM, error)) { | |
919 | + push_state (info, STATE_URI); | |
920 | + } | |
921 | + } else if (ELEMENT_IS (TAG_MIME_TYPE)) { | |
922 | + if (valid_element (info, STATE_RECENT_ITEM, | |
923 | + TAG_MIME_TYPE, TAG_RECENT_ITEM, error)) { | |
924 | + push_state (info, STATE_MIME_TYPE); | |
925 | + } | |
926 | + } else if (ELEMENT_IS (TAG_TIMESTAMP)) { | |
927 | + if (valid_element (info, STATE_RECENT_ITEM, | |
928 | + TAG_TIMESTAMP, TAG_RECENT_ITEM, error)) { | |
929 | + push_state (info, STATE_TIMESTAMP); | |
930 | + } | |
931 | + } else if (ELEMENT_IS (TAG_PRIVATE)) { | |
932 | + if (valid_element (info, STATE_RECENT_ITEM, | |
933 | + TAG_PRIVATE, TAG_RECENT_ITEM, error)) { | |
934 | + push_state (info, STATE_PRIVATE); | |
935 | + egg_recent_item_set_private (info->current_item, TRUE); | |
936 | + } | |
937 | + } else if (ELEMENT_IS (TAG_GROUPS)) { | |
938 | + if (valid_element (info, STATE_RECENT_ITEM, | |
939 | + TAG_GROUPS, TAG_RECENT_ITEM, error)) { | |
940 | + push_state (info, STATE_GROUPS); | |
941 | + } | |
942 | + } else if (ELEMENT_IS (TAG_GROUP)) { | |
943 | + if (valid_element (info, STATE_GROUPS, | |
944 | + TAG_GROUP, TAG_GROUPS, error)) { | |
945 | + push_state (info, STATE_GROUP); | |
946 | + } | |
947 | + } | |
948 | +} | |
949 | + | |
950 | +static gint | |
951 | +list_compare_func_mru (gpointer a, gpointer b) | |
952 | +{ | |
953 | + EggRecentItem *item_a = (EggRecentItem *)a; | |
954 | + EggRecentItem *item_b = (EggRecentItem *)b; | |
955 | + | |
956 | + return item_a->timestamp < item_b->timestamp; | |
957 | +} | |
958 | + | |
959 | +static gint | |
960 | +list_compare_func_lru (gpointer a, gpointer b) | |
961 | +{ | |
962 | + EggRecentItem *item_a = (EggRecentItem *)a; | |
963 | + EggRecentItem *item_b = (EggRecentItem *)b; | |
964 | + | |
965 | + return item_a->timestamp > item_b->timestamp; | |
966 | +} | |
967 | + | |
968 | + | |
969 | + | |
970 | +static void | |
971 | +end_element_handler (GMarkupParseContext *context, | |
972 | + const gchar *element_name, | |
973 | + gpointer user_data, | |
974 | + GError **error) | |
975 | +{ | |
976 | + ParseInfo *info = (ParseInfo *)user_data; | |
977 | + | |
978 | + switch (peek_state (info)) { | |
979 | + case STATE_RECENT_ITEM: | |
980 | + if (!info->current_item) { | |
981 | + g_warning ("No recent item found\n"); | |
982 | + break; | |
983 | + } | |
984 | + | |
985 | + if (!info->current_item->uri) { | |
986 | + g_warning ("Invalid item found\n"); | |
987 | + break; | |
988 | + } | |
989 | + | |
990 | + info->items = g_list_prepend (info->items, | |
991 | + info->current_item); | |
992 | + info->current_item = NULL; | |
993 | + break; | |
994 | + default: | |
995 | + break; | |
996 | + } | |
997 | + | |
998 | + pop_state (info); | |
999 | +} | |
1000 | + | |
1001 | +static void | |
1002 | +text_handler (GMarkupParseContext *context, | |
1003 | + const gchar *text, | |
1004 | + gsize text_len, | |
1005 | + gpointer user_data, | |
1006 | + GError **error) | |
1007 | +{ | |
1008 | + ParseInfo *info = (ParseInfo *)user_data; | |
1009 | + gchar *value; | |
1010 | + | |
1011 | + value = g_strndup (text, text_len); | |
1012 | + | |
1013 | + switch (peek_state (info)) { | |
1014 | + case STATE_START: | |
1015 | + case STATE_RECENT_FILES: | |
1016 | + case STATE_RECENT_ITEM: | |
1017 | + case STATE_PRIVATE: | |
1018 | + case STATE_GROUPS: | |
1019 | + break; | |
1020 | + case STATE_URI: | |
1021 | + egg_recent_item_set_uri (info->current_item, value); | |
1022 | + break; | |
1023 | + case STATE_MIME_TYPE: | |
1024 | + egg_recent_item_set_mime_type (info->current_item, value); | |
1025 | + break; | |
1026 | + case STATE_TIMESTAMP: | |
1027 | + egg_recent_item_set_timestamp (info->current_item, | |
1028 | + (time_t)atoi (value)); | |
1029 | + break; | |
1030 | + case STATE_GROUP: | |
1031 | + egg_recent_item_add_group (info->current_item, | |
1032 | + text); | |
1033 | + break; | |
1034 | + } | |
1035 | + | |
1036 | + g_free (value); | |
1037 | +} | |
1038 | + | |
1039 | +static void | |
1040 | +error_handler (GMarkupParseContext *context, | |
1041 | + GError *error, | |
1042 | + gpointer user_data) | |
1043 | +{ | |
1044 | + g_warning ("Error in parse: %s", error->message); | |
1045 | +} | |
1046 | + | |
1047 | +static void | |
1048 | +egg_recent_model_multi_enforce_limit (GList *list, int limit) | |
1049 | +{ | |
1050 | + int len; | |
1051 | + GList *end; | |
1052 | + | |
1053 | + /* limit < 0 means unlimited */ | |
1054 | + if (limit <= 0) | |
1055 | + return; | |
1056 | + | |
1057 | + len = g_list_length (list); | |
1058 | + | |
1059 | + if (len > limit) { | |
1060 | + GList *next; | |
1061 | + | |
1062 | + end = g_list_nth (list, limit-1); | |
1063 | + next = end->next; | |
1064 | + | |
1065 | + end->next = NULL; | |
1066 | + | |
1067 | + EGG_RECENT_ITEM_LIST_UNREF (next); | |
1068 | + } | |
1069 | +} | |
1070 | + | |
1071 | +static GList * | |
1072 | +egg_recent_model_multi_sort (EggRecentModelMulti *model, GList *list) | |
1073 | +{ | |
1074 | + switch (model->priv->sort_type) { | |
1075 | + case EGG_RECENT_MODEL_MULTI_SORT_MRU: | |
1076 | + list = g_list_sort (list, | |
1077 | + (GCompareFunc)list_compare_func_mru); | |
1078 | + break; | |
1079 | + case EGG_RECENT_MODEL_MULTI_SORT_LRU: | |
1080 | + list = g_list_sort (list, | |
1081 | + (GCompareFunc)list_compare_func_lru); | |
1082 | + break; | |
1083 | + case EGG_RECENT_MODEL_MULTI_SORT_NONE: | |
1084 | + break; | |
1085 | + } | |
1086 | + | |
1087 | + return list; | |
1088 | +} | |
1089 | + | |
1090 | +static gboolean | |
1091 | +egg_recent_model_multi_group_match (EggRecentItem *item, GSList *groups) | |
1092 | +{ | |
1093 | + GSList *tmp; | |
1094 | + | |
1095 | + tmp = groups; | |
1096 | + | |
1097 | + while (tmp != NULL) { | |
1098 | + const gchar * group = (const gchar *)tmp->data; | |
1099 | + | |
1100 | + if (egg_recent_item_in_group (item, group)) | |
1101 | + return TRUE; | |
1102 | + | |
1103 | + tmp = tmp->next; | |
1104 | + } | |
1105 | + | |
1106 | + return FALSE; | |
1107 | +} | |
1108 | + | |
1109 | +static GList * | |
1110 | +egg_recent_model_multi_filter (EggRecentModelMulti *model, GList *list) | |
1111 | +{ | |
1112 | + GList *newlist = NULL; | |
1113 | + GList *l; | |
1114 | + gchar *mime_type; | |
1115 | + gchar *uri; | |
1116 | + | |
1117 | + g_return_val_if_fail (list != NULL, NULL); | |
1118 | + | |
1119 | + for (l = list; l != NULL ; l = l->next) { | |
1120 | + EggRecentItem *item = (EggRecentItem *) l->data; | |
1121 | + gboolean pass_mime_test = FALSE; | |
1122 | + gboolean pass_group_test = FALSE; | |
1123 | + gboolean pass_scheme_test = FALSE; | |
1124 | + | |
1125 | + g_assert (item != NULL); | |
1126 | + | |
1127 | + uri = egg_recent_item_get_uri (item); | |
1128 | + | |
1129 | + /* filter by mime type */ | |
1130 | + if (model->priv->mime_filter_values != NULL) { | |
1131 | + mime_type = egg_recent_item_get_mime_type (item); | |
1132 | + | |
1133 | + if (egg_recent_model_multi_string_match | |
1134 | + (model->priv->mime_filter_values, | |
1135 | + mime_type)) | |
1136 | + pass_mime_test = TRUE; | |
1137 | + | |
1138 | + g_free (mime_type); | |
1139 | + } else | |
1140 | + pass_mime_test = TRUE; | |
1141 | + | |
1142 | + /* filter by group */ | |
1143 | + if (pass_mime_test && model->priv->group_filter_values != NULL) { | |
1144 | + if (egg_recent_model_multi_group_match | |
1145 | + (item, model->priv->group_filter_values)) | |
1146 | + pass_group_test = TRUE; | |
1147 | + } else if (egg_recent_item_get_private (item)) { | |
1148 | + pass_group_test = FALSE; | |
1149 | + } else | |
1150 | + pass_group_test = TRUE; | |
1151 | + | |
1152 | + /* filter by URI scheme */ | |
1153 | + if (pass_mime_test && pass_group_test && | |
1154 | + model->priv->scheme_filter_values != NULL) { | |
1155 | + gchar *scheme; | |
1156 | + | |
1157 | + scheme = gnome_vfs_get_uri_scheme (uri); | |
1158 | + | |
1159 | + if (egg_recent_model_multi_string_match | |
1160 | + (model->priv->scheme_filter_values, scheme)) | |
1161 | + pass_scheme_test = TRUE; | |
1162 | + | |
1163 | + g_free (scheme); | |
1164 | + } else | |
1165 | + pass_scheme_test = TRUE; | |
1166 | + | |
1167 | + if (pass_mime_test && pass_group_test && pass_scheme_test) | |
1168 | + newlist = g_list_prepend (newlist, item); | |
1169 | + else | |
1170 | + egg_recent_item_unref (item); | |
1171 | + | |
1172 | + g_free (uri); | |
1173 | + } | |
1174 | + | |
1175 | + g_list_free (list); | |
1176 | + | |
1177 | + return g_list_reverse (newlist); | |
1178 | +} | |
1179 | + | |
1180 | + | |
1181 | + | |
1182 | +#if 0 | |
1183 | +static void | |
1184 | +egg_recent_model_multi_monitor_list_cb (GnomeVFSMonitorHandle *handle, | |
1185 | + const gchar *monitor_uri, | |
1186 | + const gchar *info_uri, | |
1187 | + GnomeVFSMonitorEventType event_type, | |
1188 | + gpointer user_data) | |
1189 | +{ | |
1190 | + EggRecentModelMulti *model; | |
1191 | + | |
1192 | + model = EGG_RECENT_MODEL_MULTI (user_data); | |
1193 | + | |
1194 | + if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED) { | |
1195 | + egg_recent_model_multi_delete (model, monitor_uri); | |
1196 | + g_hash_table_remove (model->priv->monitors, monitor_uri); | |
1197 | + } | |
1198 | +} | |
1199 | + | |
1200 | + | |
1201 | + | |
1202 | +static void | |
1203 | +egg_recent_model_multi_monitor_list (EggRecentModelMulti *model, GList *list) | |
1204 | +{ | |
1205 | + GList *tmp; | |
1206 | + | |
1207 | + tmp = list; | |
1208 | + while (tmp) { | |
1209 | + EggRecentItem *item = (EggRecentItem *)tmp->data; | |
1210 | + GnomeVFSMonitorHandle *handle; | |
1211 | + GnomeVFSResult res; | |
1212 | + gchar *uri; | |
1213 | + | |
1214 | + tmp = tmp->next; | |
1215 | + | |
1216 | + uri = egg_recent_item_get_uri (item); | |
1217 | + if (g_hash_table_lookup (model->priv->monitors, uri)) { | |
1218 | + /* already monitoring this one */ | |
1219 | + g_free (uri); | |
1220 | + continue; | |
1221 | + } | |
1222 | + | |
1223 | + res = gnome_vfs_monitor_add (&handle, uri, | |
1224 | + GNOME_VFS_MONITOR_FILE, | |
1225 | + egg_recent_model_multi_monitor_list_cb, | |
1226 | + model); | |
1227 | + | |
1228 | + if (res == GNOME_VFS_OK) | |
1229 | + g_hash_table_insert (model->priv->monitors, uri, handle); | |
1230 | + else | |
1231 | + g_free (uri); | |
1232 | + } | |
1233 | +} | |
1234 | +#endif | |
1235 | + | |
1236 | + | |
1237 | +static gboolean | |
1238 | +egg_recent_model_multi_changed_timeout (EggRecentModelMulti *model) | |
1239 | +{ | |
1240 | + model->priv->changed_timeout = 0; | |
1241 | + | |
1242 | + egg_recent_model_multi_changed (model); | |
1243 | + | |
1244 | + return FALSE; | |
1245 | +} | |
1246 | + | |
1247 | +static void | |
1248 | +egg_recent_model_multi_monitor_cb (GnomeVFSMonitorHandle *handle, | |
1249 | + const gchar *monitor_uri, | |
1250 | + const gchar *info_uri, | |
1251 | + GnomeVFSMonitorEventType event_type, | |
1252 | + gpointer user_data) | |
1253 | +{ | |
1254 | + EggRecentModelMulti *model; | |
1255 | + | |
1256 | + g_return_if_fail (user_data != NULL); | |
1257 | + g_return_if_fail (EGG_IS_RECENT_MODEL_MULTI (user_data)); | |
1258 | + model = EGG_RECENT_MODEL_MULTI (user_data); | |
1259 | + | |
1260 | + if (event_type == GNOME_VFS_MONITOR_EVENT_CHANGED || | |
1261 | + event_type == GNOME_VFS_MONITOR_EVENT_CREATED || | |
1262 | + event_type == GNOME_VFS_MONITOR_EVENT_DELETED) { | |
1263 | + if (model->priv->changed_timeout > 0) { | |
1264 | + g_source_remove (model->priv->changed_timeout); | |
1265 | + } | |
1266 | + | |
1267 | + model->priv->changed_timeout = g_timeout_add ( | |
1268 | + EGG_RECENT_MODEL_MULTI_TIMEOUT_LENGTH, | |
1269 | + (GSourceFunc)egg_recent_model_multi_changed_timeout, | |
1270 | + model); | |
1271 | + } | |
1272 | +} | |
1273 | + | |
1274 | +static gboolean | |
1275 | +egg_recent_model_multi_poll_timeout (gpointer user_data) | |
1276 | +{ | |
1277 | + EggRecentModelMulti *model; | |
1278 | + struct stat stat_buf; | |
1279 | + int stat_res; | |
1280 | + | |
1281 | + model = EGG_RECENT_MODEL_MULTI (user_data); | |
1282 | + stat_res = stat (model->priv->path, &stat_buf); | |
1283 | + | |
1284 | + if (!stat_res && stat_buf.st_mtime && | |
1285 | + stat_buf.st_mtime != model->priv->last_mtime) { | |
1286 | + model->priv->last_mtime = stat_buf.st_mtime; | |
1287 | + | |
1288 | + if (model->priv->changed_timeout > 0) | |
1289 | + g_source_remove (model->priv->changed_timeout); | |
1290 | + | |
1291 | + model->priv->changed_timeout = g_timeout_add ( | |
1292 | + EGG_RECENT_MODEL_MULTI_TIMEOUT_LENGTH, | |
1293 | + (GSourceFunc)egg_recent_model_multi_changed_timeout, | |
1294 | + model); | |
1295 | + } | |
1296 | + return TRUE; | |
1297 | +} | |
1298 | + | |
1299 | +static void | |
1300 | +egg_recent_model_multi_monitor (EggRecentModelMulti *model, gboolean should_monitor) | |
1301 | +{ | |
1302 | + if (should_monitor && model->priv->monitor == NULL) { | |
1303 | + char *uri; | |
1304 | + GnomeVFSResult result; | |
1305 | + | |
1306 | + uri = gnome_vfs_get_uri_from_local_path (model->priv->path); | |
1307 | + | |
1308 | + result = gnome_vfs_monitor_add (&model->priv->monitor, | |
1309 | + uri, | |
1310 | + GNOME_VFS_MONITOR_FILE, | |
1311 | + egg_recent_model_multi_monitor_cb, | |
1312 | + model); | |
1313 | + | |
1314 | + g_free (uri); | |
1315 | + | |
1316 | + /* if the above fails, don't worry about it. | |
1317 | + * local notifications will still happen | |
1318 | + */ | |
1319 | + if (result == GNOME_VFS_ERROR_NOT_SUPPORTED) { | |
1320 | + if (model->priv->poll_timeout > 0) | |
1321 | + g_source_remove (model->priv->poll_timeout); | |
1322 | + | |
1323 | + model->priv->poll_timeout = g_timeout_add ( | |
1324 | + EGG_RECENT_MODEL_MULTI_POLL_TIME * 1000, | |
1325 | + egg_recent_model_multi_poll_timeout, | |
1326 | + model); | |
1327 | + } | |
1328 | + | |
1329 | + } else if (!should_monitor && model->priv->monitor != NULL) { | |
1330 | + gnome_vfs_monitor_cancel (model->priv->monitor); | |
1331 | + model->priv->monitor = NULL; | |
1332 | + } | |
1333 | +} | |
1334 | + | |
1335 | +static void | |
1336 | +egg_recent_model_multi_set_limit_internal (EggRecentModelMulti *model, int limit) | |
1337 | +{ | |
1338 | + model->priv->limit = limit; | |
1339 | + | |
1340 | + if (limit <= 0) | |
1341 | + egg_recent_model_multi_monitor (model, FALSE); | |
1342 | + else { | |
1343 | + egg_recent_model_multi_monitor (model, TRUE); | |
1344 | + egg_recent_model_multi_changed (model); | |
1345 | + } | |
1346 | +} | |
1347 | + | |
1348 | +static GList * | |
1349 | +egg_recent_model_multi_read (EggRecentModelMulti *model, FILE *file) | |
1350 | +{ | |
1351 | + GList *list=NULL; | |
1352 | + gchar *content; | |
1353 | + GMarkupParseContext *ctx; | |
1354 | + ParseInfo *info; | |
1355 | + GError *error; | |
1356 | + | |
1357 | + content = egg_recent_model_multi_read_raw (model, file); | |
1358 | + | |
1359 | + if (strlen (content) <= 0) { | |
1360 | + g_free (content); | |
1361 | + return NULL; | |
1362 | + } | |
1363 | + | |
1364 | + info = parse_info_init (); | |
1365 | + | |
1366 | + ctx = g_markup_parse_context_new (&parser, 0, info, NULL); | |
1367 | + | |
1368 | + error = NULL; | |
1369 | + if (!g_markup_parse_context_parse (ctx, content, strlen (content), &error)) { | |
1370 | + g_warning ("Error while parsing the .recently-used file: %s\n", | |
1371 | + error->message); | |
1372 | + | |
1373 | + g_error_free (error); | |
1374 | + parse_info_free (info); | |
1375 | + | |
1376 | + return NULL; | |
1377 | + } | |
1378 | + | |
1379 | + error = NULL; | |
1380 | + if (!g_markup_parse_context_end_parse (ctx, &error)) { | |
1381 | + g_warning ("Unable to complete parsing of the .recently-used file: %s\n", | |
1382 | + error->message); | |
1383 | + | |
1384 | + g_error_free (error); | |
1385 | + g_markup_parse_context_free (ctx); | |
1386 | + parse_info_free (info); | |
1387 | + | |
1388 | + return NULL; | |
1389 | + } | |
1390 | + | |
1391 | + list = g_list_reverse (info->items); | |
1392 | + | |
1393 | + g_markup_parse_context_free (ctx); | |
1394 | + parse_info_free (info); | |
1395 | + g_free (content); | |
1396 | + | |
1397 | + return list; | |
1398 | +} | |
1399 | + | |
1400 | + | |
1401 | +static gboolean | |
1402 | +egg_recent_model_multi_write (EggRecentModelMulti *model, FILE *file, GList *list) | |
1403 | +{ | |
1404 | + GString *string; | |
1405 | + gchar *data; | |
1406 | + EggRecentItem *item; | |
1407 | + const GList *groups; | |
1408 | + int i; | |
1409 | + int ret; | |
1410 | + | |
1411 | + string = g_string_new ("<?xml version=\"1.0\"?>\n"); | |
1412 | + string = g_string_append (string, "<" TAG_RECENT_FILES ">\n"); | |
1413 | + | |
1414 | + i=0; | |
1415 | + while (list) { | |
1416 | + gchar *uri; | |
1417 | + gchar *mime_type; | |
1418 | + gchar *escaped_uri; | |
1419 | + time_t timestamp; | |
1420 | + item = (EggRecentItem *)list->data; | |
1421 | + | |
1422 | + | |
1423 | + uri = egg_recent_item_get_uri_utf8 (item); | |
1424 | + escaped_uri = g_markup_escape_text (uri, | |
1425 | + strlen (uri)); | |
1426 | + g_free (uri); | |
1427 | + | |
1428 | + mime_type = egg_recent_item_get_mime_type (item); | |
1429 | + timestamp = egg_recent_item_get_timestamp (item); | |
1430 | + | |
1431 | + string = g_string_append (string, " <" TAG_RECENT_ITEM ">\n"); | |
1432 | + | |
1433 | + g_string_append_printf (string, | |
1434 | + " <" TAG_URI ">%s</" TAG_URI ">\n", escaped_uri); | |
1435 | + | |
1436 | + if (mime_type) | |
1437 | + g_string_append_printf (string, | |
1438 | + " <" TAG_MIME_TYPE ">%s</" TAG_MIME_TYPE ">\n", mime_type); | |
1439 | + else | |
1440 | + g_string_append_printf (string, | |
1441 | + " <" TAG_MIME_TYPE "></" TAG_MIME_TYPE ">\n"); | |
1442 | + | |
1443 | + | |
1444 | + g_string_append_printf (string, | |
1445 | + " <" TAG_TIMESTAMP ">%d</" TAG_TIMESTAMP ">\n", (int)timestamp); | |
1446 | + | |
1447 | + if (egg_recent_item_get_private (item)) | |
1448 | + string = g_string_append (string, | |
1449 | + " <" TAG_PRIVATE "/>\n"); | |
1450 | + | |
1451 | + /* write the groups */ | |
1452 | + string = g_string_append (string, | |
1453 | + " <" TAG_GROUPS ">\n"); | |
1454 | + groups = egg_recent_item_get_groups (item); | |
1455 | + | |
1456 | + if (groups == NULL && egg_recent_item_get_private (item)) | |
1457 | + g_warning ("Item with URI \"%s\" marked as private, but" | |
1458 | + " does not belong to any groups.\n", uri); | |
1459 | + | |
1460 | + while (groups) { | |
1461 | + const gchar *group = (const gchar *)groups->data; | |
1462 | + gchar *escaped_group; | |
1463 | + | |
1464 | + escaped_group = g_markup_escape_text (group, strlen(group)); | |
1465 | + | |
1466 | + g_string_append_printf (string, | |
1467 | + " <" TAG_GROUP ">%s</" TAG_GROUP ">\n", | |
1468 | + escaped_group); | |
1469 | + | |
1470 | + g_free (escaped_group); | |
1471 | + | |
1472 | + groups = groups->next; | |
1473 | + } | |
1474 | + | |
1475 | + string = g_string_append (string, " </" TAG_GROUPS ">\n"); | |
1476 | + | |
1477 | + string = g_string_append (string, | |
1478 | + " </" TAG_RECENT_ITEM ">\n"); | |
1479 | + | |
1480 | + g_free (mime_type); | |
1481 | + g_free (escaped_uri); | |
1482 | + | |
1483 | + list = list->next; | |
1484 | + i++; | |
1485 | + } | |
1486 | + | |
1487 | + string = g_string_append (string, "</" TAG_RECENT_FILES ">"); | |
1488 | + | |
1489 | + data = g_string_free (string, FALSE); | |
1490 | + | |
1491 | + ret = egg_recent_model_multi_write_raw (model, file, data); | |
1492 | + | |
1493 | + g_free (data); | |
1494 | + | |
1495 | + return ret; | |
1496 | +} | |
1497 | + | |
1498 | +static FILE * | |
1499 | +egg_recent_model_multi_open_file (EggRecentModelMulti *model, | |
1500 | + gboolean for_writing) | |
1501 | +{ | |
1502 | + FILE *file; | |
1503 | + mode_t prev_umask; | |
1504 | + | |
1505 | + file = fopen (model->priv->path, "r+"); | |
1506 | + if (file == NULL && for_writing) { | |
1507 | + /* be paranoid */ | |
1508 | + prev_umask = umask (077); | |
1509 | + | |
1510 | + file = fopen (model->priv->path, "w+"); | |
1511 | + | |
1512 | + umask (prev_umask); | |
1513 | + | |
1514 | + g_return_val_if_fail (file != NULL, NULL); | |
1515 | + } | |
1516 | + | |
1517 | + return file; | |
1518 | +} | |
1519 | + | |
1520 | +static gboolean | |
1521 | +egg_recent_model_multi_lock_file (FILE *file) | |
1522 | +{ | |
1523 | +#ifdef HAVE_LOCKF | |
1524 | + int fd; | |
1525 | + gint try = 5; | |
1526 | + | |
1527 | + rewind (file); | |
1528 | + fd = fileno (file); | |
1529 | + | |
1530 | + /* Attempt to lock the file 5 times, | |
1531 | + * waiting a random interval (< 1 second) | |
1532 | + * in between attempts. | |
1533 | + * We should really be doing asynchronous | |
1534 | + * locking, but requires substantially larger | |
1535 | + * changes. | |
1536 | + */ | |
1537 | + | |
1538 | + while (try > 0) | |
1539 | + { | |
1540 | + int rand_interval; | |
1541 | + | |
1542 | + if (lockf (fd, F_TLOCK, 0) == 0) | |
1543 | + return TRUE; | |
1544 | + | |
1545 | + rand_interval = 1 + (int) (10.0 * rand()/(RAND_MAX + 1.0)); | |
1546 | + | |
1547 | + g_usleep (100000 * rand_interval); | |
1548 | + | |
1549 | + --try; | |
1550 | + } | |
1551 | + | |
1552 | + return FALSE; | |
1553 | +#else | |
1554 | + return TRUE; | |
1555 | +#endif /* HAVE_LOCKF */ | |
1556 | +} | |
1557 | + | |
1558 | +static gboolean | |
1559 | +egg_recent_model_multi_unlock_file (FILE *file) | |
1560 | +{ | |
1561 | +#ifdef HAVE_LOCKF | |
1562 | + int fd; | |
1563 | + | |
1564 | + rewind (file); | |
1565 | + fd = fileno (file); | |
1566 | + | |
1567 | + return (lockf (fd, F_ULOCK, 0) == 0) ? TRUE : FALSE; | |
1568 | +#else | |
1569 | + return TRUE; | |
1570 | +#endif /* HAVE_LOCKF */ | |
1571 | +} | |
1572 | + | |
1573 | +static void | |
1574 | +egg_recent_model_multi_finalize (GObject *object) | |
1575 | +{ | |
1576 | + EggRecentModelMulti *model = EGG_RECENT_MODEL_MULTI (object); | |
1577 | + | |
1578 | + if (model->priv->changed_timeout > 0) { | |
1579 | + g_source_remove (model->priv->changed_timeout); | |
1580 | + } | |
1581 | + | |
1582 | + egg_recent_model_multi_monitor (model, FALSE); | |
1583 | + | |
1584 | + | |
1585 | + g_slist_foreach (model->priv->mime_filter_values, | |
1586 | + (GFunc) g_pattern_spec_free, NULL); | |
1587 | + g_slist_free (model->priv->mime_filter_values); | |
1588 | + model->priv->mime_filter_values = NULL; | |
1589 | + | |
1590 | + g_slist_foreach (model->priv->scheme_filter_values, | |
1591 | + (GFunc) g_pattern_spec_free, NULL); | |
1592 | + g_slist_free (model->priv->scheme_filter_values); | |
1593 | + model->priv->scheme_filter_values = NULL; | |
1594 | + | |
1595 | + g_slist_foreach (model->priv->group_filter_values, | |
1596 | + (GFunc) g_free, NULL); | |
1597 | + g_slist_free (model->priv->group_filter_values); | |
1598 | + model->priv->group_filter_values = NULL; | |
1599 | + | |
1600 | + | |
1601 | + if (model->priv->limit_change_notify_id) | |
1602 | + gconf_client_notify_remove (model->priv->client, | |
1603 | + model->priv->limit_change_notify_id); | |
1604 | + model->priv->expiration_change_notify_id = 0; | |
1605 | + | |
1606 | + if (model->priv->expiration_change_notify_id) | |
1607 | + gconf_client_notify_remove (model->priv->client, | |
1608 | + model->priv->expiration_change_notify_id); | |
1609 | + model->priv->expiration_change_notify_id = 0; | |
1610 | + | |
1611 | + g_object_unref (model->priv->client); | |
1612 | + model->priv->client = NULL; | |
1613 | + | |
1614 | + | |
1615 | + g_free (model->priv->path); | |
1616 | + model->priv->path = NULL; | |
1617 | + | |
1618 | + g_hash_table_destroy (model->priv->monitors); | |
1619 | + model->priv->monitors = NULL; | |
1620 | + | |
1621 | + if (model->priv->poll_timeout > 0) | |
1622 | + g_source_remove (model->priv->poll_timeout); | |
1623 | + model->priv->poll_timeout =0; | |
1624 | + | |
1625 | + g_free (model->priv); | |
1626 | + | |
1627 | + parent_class->finalize (object); | |
1628 | +} | |
1629 | + | |
1630 | +static void | |
1631 | +egg_recent_model_multi_set_property (GObject *object, | |
1632 | + guint prop_id, | |
1633 | + const GValue *value, | |
1634 | + GParamSpec *pspec) | |
1635 | +{ | |
1636 | + EggRecentModelMulti *model = EGG_RECENT_MODEL_MULTI (object); | |
1637 | + | |
1638 | + switch (prop_id) | |
1639 | + { | |
1640 | + case PROP_MIME_FILTERS: | |
1641 | + if (model->priv->mime_filter_values != NULL) | |
1642 | + egg_recent_model_multi_clear_mime_filter (model); | |
1643 | + | |
1644 | + model->priv->mime_filter_values = | |
1645 | + (GSList *)g_value_get_pointer (value); | |
1646 | + break; | |
1647 | + | |
1648 | + case PROP_GROUP_FILTERS: | |
1649 | + if (model->priv->group_filter_values != NULL) | |
1650 | + egg_recent_model_multi_clear_group_filter (model); | |
1651 | + | |
1652 | + model->priv->group_filter_values = | |
1653 | + (GSList *)g_value_get_pointer (value); | |
1654 | + break; | |
1655 | + | |
1656 | + case PROP_SCHEME_FILTERS: | |
1657 | + if (model->priv->scheme_filter_values != NULL) | |
1658 | + egg_recent_model_multi_clear_scheme_filter (model); | |
1659 | + | |
1660 | + model->priv->scheme_filter_values = | |
1661 | + (GSList *)g_value_get_pointer (value); | |
1662 | + break; | |
1663 | + | |
1664 | + case PROP_SORT_TYPE: | |
1665 | + model->priv->sort_type = g_value_get_int (value); | |
1666 | + break; | |
1667 | + | |
1668 | + case PROP_FILE_PATH: | |
1669 | + model->priv->path = g_strdup_printf ( | |
1670 | + "%s/%s", | |
1671 | + g_get_home_dir (), | |
1672 | + g_value_get_string (value) | |
1673 | + ); | |
1674 | + egg_recent_model_multi_monitor (model, TRUE); | |
1675 | + break; | |
1676 | + | |
1677 | + case PROP_LIMIT: | |
1678 | + egg_recent_model_multi_set_limit (model, | |
1679 | + g_value_get_int (value)); | |
1680 | + break; | |
1681 | + | |
1682 | + default: | |
1683 | + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | |
1684 | + break; | |
1685 | + } | |
1686 | +} | |
1687 | + | |
1688 | +static void | |
1689 | +egg_recent_model_multi_get_property (GObject *object, | |
1690 | + guint prop_id, | |
1691 | + GValue *value, | |
1692 | + GParamSpec *pspec) | |
1693 | +{ | |
1694 | + EggRecentModelMulti *model = EGG_RECENT_MODEL_MULTI (object); | |
1695 | + | |
1696 | + switch (prop_id) | |
1697 | + { | |
1698 | + case PROP_MIME_FILTERS: | |
1699 | + g_value_set_pointer (value, model->priv->mime_filter_values); | |
1700 | + break; | |
1701 | + | |
1702 | + case PROP_GROUP_FILTERS: | |
1703 | + g_value_set_pointer (value, model->priv->group_filter_values); | |
1704 | + break; | |
1705 | + | |
1706 | + case PROP_SCHEME_FILTERS: | |
1707 | + g_value_set_pointer (value, model->priv->scheme_filter_values); | |
1708 | + break; | |
1709 | + | |
1710 | + case PROP_SORT_TYPE: | |
1711 | + g_value_set_int (value, model->priv->sort_type); | |
1712 | + break; | |
1713 | + | |
1714 | + case PROP_FILE_PATH: | |
1715 | + g_value_set_string (value, model->priv->path); | |
1716 | + break; | |
1717 | + | |
1718 | + case PROP_LIMIT: | |
1719 | + g_value_set_int (value, model->priv->limit); | |
1720 | + break; | |
1721 | + | |
1722 | + default: | |
1723 | + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | |
1724 | + break; | |
1725 | + } | |
1726 | +} | |
1727 | + | |
1728 | +static void | |
1729 | +egg_recent_model_multi_class_init (EggRecentModelMultiClass * klass) | |
1730 | +{ | |
1731 | + GObjectClass *object_class; | |
1732 | + | |
1733 | + parent_class = g_type_class_peek_parent (klass); | |
1734 | + | |
1735 | + parent_class = g_type_class_peek_parent (klass); | |
1736 | + | |
1737 | + object_class = G_OBJECT_CLASS (klass); | |
1738 | + object_class->set_property = egg_recent_model_multi_set_property; | |
1739 | + object_class->get_property = egg_recent_model_multi_get_property; | |
1740 | + object_class->finalize = egg_recent_model_multi_finalize; | |
1741 | + | |
1742 | + model_signals[CHANGED] = g_signal_new ("changed", | |
1743 | + G_OBJECT_CLASS_TYPE (object_class), | |
1744 | + G_SIGNAL_RUN_LAST, | |
1745 | + G_STRUCT_OFFSET (EggRecentModelMultiClass, changed), | |
1746 | + NULL, NULL, | |
1747 | + g_cclosure_marshal_VOID__POINTER, | |
1748 | + G_TYPE_NONE, 1, | |
1749 | + G_TYPE_POINTER); | |
1750 | + | |
1751 | + | |
1752 | + g_object_class_install_property (object_class, | |
1753 | + PROP_MIME_FILTERS, | |
1754 | + g_param_spec_pointer ("mime-filters", | |
1755 | + "Mime Filters", | |
1756 | + "List of mime types to be allowed.", | |
1757 | + G_PARAM_READWRITE)); | |
1758 | + | |
1759 | + g_object_class_install_property (object_class, | |
1760 | + PROP_GROUP_FILTERS, | |
1761 | + g_param_spec_pointer ("group-filters", | |
1762 | + "Group Filters", | |
1763 | + "List of groups to be allowed.", | |
1764 | + G_PARAM_READWRITE)); | |
1765 | + | |
1766 | + g_object_class_install_property (object_class, | |
1767 | + PROP_SCHEME_FILTERS, | |
1768 | + g_param_spec_pointer ("scheme-filters", | |
1769 | + "Scheme Filters", | |
1770 | + "List of URI schemes to be allowed.", | |
1771 | + G_PARAM_READWRITE)); | |
1772 | + | |
1773 | + g_object_class_install_property (object_class, | |
1774 | + PROP_SORT_TYPE, | |
1775 | + g_param_spec_int ("sort-type", | |
1776 | + "Sort Type", | |
1777 | + "Type of sorting to be done.", | |
1778 | + 0, EGG_RECENT_MODEL_MULTI_SORT_NONE, | |
1779 | + EGG_RECENT_MODEL_MULTI_SORT_MRU, | |
1780 | + G_PARAM_READWRITE)); | |
1781 | + | |
1782 | + g_object_class_install_property (object_class, | |
1783 | + PROP_FILE_PATH, | |
1784 | + g_param_spec_string ("file-path", | |
1785 | + "File Path", | |
1786 | + "File path relative to $HOME.", | |
1787 | + EGG_RECENT_MODEL_MULTI_DEFAULT_FILE_PATH, | |
1788 | + G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY)); | |
1789 | + | |
1790 | + g_object_class_install_property (object_class, | |
1791 | + PROP_LIMIT, | |
1792 | + g_param_spec_int ("limit", | |
1793 | + "Limit", | |
1794 | + "Max number of items allowed.", | |
1795 | + -1, EGG_RECENT_MODEL_MULTI_MAX_ITEMS, | |
1796 | + EGG_RECENT_MODEL_MULTI_DEFAULT_LIMIT, | |
1797 | + G_PARAM_READWRITE)); | |
1798 | + | |
1799 | + klass->changed = NULL; | |
1800 | +} | |
1801 | + | |
1802 | + | |
1803 | + | |
1804 | +static void | |
1805 | +egg_recent_model_multi_limit_changed (GConfClient *client, guint cnxn_id, | |
1806 | + GConfEntry *entry, gpointer user_data) | |
1807 | +{ | |
1808 | + EggRecentModelMulti *model; | |
1809 | + GConfValue *value; | |
1810 | + | |
1811 | + model = EGG_RECENT_MODEL_MULTI (user_data); | |
1812 | + | |
1813 | + g_return_if_fail (model != NULL); | |
1814 | + | |
1815 | + if (model->priv->use_default_limit == FALSE) | |
1816 | + return; /* ignore this key */ | |
1817 | + | |
1818 | + /* the key was unset, and the schema has apparently failed */ | |
1819 | + if (entry == NULL) | |
1820 | + return; | |
1821 | + | |
1822 | + value = gconf_entry_get_value (entry); | |
1823 | + | |
1824 | + if (value->type != GCONF_VALUE_INT) { | |
1825 | + g_warning ("Expected GConfValue of type integer, " | |
1826 | + "got something else"); | |
1827 | + } | |
1828 | + | |
1829 | + | |
1830 | + egg_recent_model_multi_set_limit_internal (model, gconf_value_get_int (value)); | |
1831 | +} | |
1832 | + | |
1833 | +static void | |
1834 | +egg_recent_model_multi_expiration_changed (GConfClient *client, guint cnxn_id, | |
1835 | + GConfEntry *entry, gpointer user_data) | |
1836 | +{ | |
1837 | + | |
1838 | +} | |
1839 | + | |
1840 | +static void | |
1841 | +egg_recent_model_multi_init (EggRecentModelMulti * model) | |
1842 | +{ | |
1843 | + if (!gnome_vfs_init ()) { | |
1844 | + g_warning ("gnome-vfs initialization failed."); | |
1845 | + return; | |
1846 | + } | |
1847 | + | |
1848 | + | |
1849 | + model->priv = g_new0 (EggRecentModelMultiPrivate, 1); | |
1850 | + | |
1851 | + model->priv->mime_filter_values = NULL; | |
1852 | + model->priv->group_filter_values = NULL; | |
1853 | + model->priv->scheme_filter_values = NULL; | |
1854 | + | |
1855 | + model->priv->client = gconf_client_get_default (); | |
1856 | + gconf_client_add_dir (model->priv->client, EGG_RECENT_MODEL_MULTI_KEY_DIR, | |
1857 | + GCONF_CLIENT_PRELOAD_ONELEVEL, NULL); | |
1858 | + | |
1859 | + model->priv->limit_change_notify_id = | |
1860 | + gconf_client_notify_add (model->priv->client, | |
1861 | + EGG_RECENT_MODEL_MULTI_DEFAULT_LIMIT_KEY, | |
1862 | + egg_recent_model_multi_limit_changed, | |
1863 | + model, NULL, NULL); | |
1864 | + | |
1865 | + model->priv->expiration_change_notify_id = | |
1866 | + gconf_client_notify_add (model->priv->client, | |
1867 | + EGG_RECENT_MODEL_MULTI_EXPIRE_KEY, | |
1868 | + egg_recent_model_multi_expiration_changed, | |
1869 | + model, NULL, NULL); | |
1870 | + | |
1871 | + model->priv->expire_days = gconf_client_get_int ( | |
1872 | + model->priv->client, | |
1873 | + EGG_RECENT_MODEL_MULTI_EXPIRE_KEY, | |
1874 | + NULL); | |
1875 | + | |
1876 | +#if 0 | |
1877 | + /* keep this out, for now */ | |
1878 | + model->priv->limit = gconf_client_get_int ( | |
1879 | + model->priv->client, | |
1880 | + EGG_RECENT_MODEL_MULTI_DEFAULT_LIMIT_KEY, NULL); | |
1881 | + model->priv->use_default_limit = TRUE; | |
1882 | +#endif | |
1883 | + model->priv->limit = EGG_RECENT_MODEL_MULTI_DEFAULT_LIMIT; | |
1884 | + model->priv->use_default_limit = FALSE; | |
1885 | + | |
1886 | + model->priv->monitors = g_hash_table_new_full ( | |
1887 | + g_str_hash, g_str_equal, | |
1888 | + (GDestroyNotify) g_free, | |
1889 | + (GDestroyNotify) gnome_vfs_monitor_cancel); | |
1890 | + | |
1891 | + model->priv->monitor = NULL; | |
1892 | + model->priv->poll_timeout = 0; | |
1893 | + model->priv->last_mtime = 0; | |
1894 | +} | |
1895 | + | |
1896 | + | |
1897 | +/** | |
1898 | + * egg_recent_model_multi_new: | |
1899 | + * @sort: the type of sorting to use | |
1900 | + * @limit: maximum number of items in the list | |
1901 | + * | |
1902 | + * This creates a new EggRecentModelMulti object. | |
1903 | + * | |
1904 | + * Returns: a EggRecentModelMulti object | |
1905 | + */ | |
1906 | +EggRecentModelMulti * | |
1907 | +egg_recent_model_multi_new (EggRecentModelMultiSort sort) | |
1908 | +{ | |
1909 | + return egg_recent_model_multi_new_with_file_path (sort, EGG_RECENT_MODEL_MULTI_DEFAULT_FILE_PATH); | |
1910 | +} | |
1911 | + | |
1912 | +EggRecentModelMulti * | |
1913 | +egg_recent_model_multi_new_with_file_path (EggRecentModelMultiSort sort, const gchar *file_path) | |
1914 | +{ | |
1915 | + EggRecentModelMulti *model; | |
1916 | + | |
1917 | + if (! file_path) | |
1918 | + file_path = EGG_RECENT_MODEL_MULTI_DEFAULT_FILE_PATH; | |
1919 | + | |
1920 | + model = EGG_RECENT_MODEL_MULTI (g_object_new (egg_recent_model_multi_get_type (), | |
1921 | + "sort-type", sort, "file-path", file_path, NULL)); | |
1922 | + | |
1923 | + g_return_val_if_fail (model, NULL); | |
1924 | + | |
1925 | + return model; | |
1926 | +} | |
1927 | + | |
1928 | +/** | |
1929 | + * egg_recent_model_multi_add_full: | |
1930 | + * @model: A EggRecentModelMulti object. | |
1931 | + * @item: A EggRecentItem | |
1932 | + * | |
1933 | + * This function adds an item to the list of recently used URIs. | |
1934 | + * | |
1935 | + * Returns: gboolean | |
1936 | + */ | |
1937 | +gboolean | |
1938 | +egg_recent_model_multi_add_full (EggRecentModelMulti * model, EggRecentItem *item) | |
1939 | +{ | |
1940 | + FILE *file; | |
1941 | + GList *list = NULL; | |
1942 | + gboolean ret = FALSE; | |
1943 | + gboolean updated = FALSE; | |
1944 | + char *uri; | |
1945 | + time_t t; | |
1946 | + | |
1947 | + g_return_val_if_fail (model != NULL, FALSE); | |
1948 | + g_return_val_if_fail (EGG_IS_RECENT_MODEL_MULTI (model), FALSE); | |
1949 | + | |
1950 | + uri = egg_recent_item_get_uri (item); | |
1951 | + if (strncmp (uri, "recent-files://", strlen ("recent-files://")) == 0) { | |
1952 | + g_free (uri); | |
1953 | + return FALSE; | |
1954 | + } else { | |
1955 | + g_free (uri); | |
1956 | + } | |
1957 | + | |
1958 | + file = egg_recent_model_multi_open_file (model, TRUE); | |
1959 | + g_return_val_if_fail (file != NULL, FALSE); | |
1960 | + | |
1961 | + time (&t); | |
1962 | + egg_recent_item_set_timestamp (item, t); | |
1963 | + | |
1964 | + if (egg_recent_model_multi_lock_file (file)) { | |
1965 | + | |
1966 | + /* read existing stuff */ | |
1967 | + list = egg_recent_model_multi_read (model, file); | |
1968 | + | |
1969 | + /* if it's already there, we just update it */ | |
1970 | + updated = egg_recent_model_multi_update_item (list, item); | |
1971 | + | |
1972 | + if (!updated) { | |
1973 | + list = g_list_prepend (list, item); | |
1974 | + | |
1975 | + egg_recent_model_multi_enforce_limit (list, | |
1976 | + EGG_RECENT_MODEL_MULTI_MAX_ITEMS); | |
1977 | + } | |
1978 | + | |
1979 | + /* write new stuff */ | |
1980 | + if (!egg_recent_model_multi_write (model, file, list)) | |
1981 | + g_warning ("Write failed: %s", strerror (errno)); | |
1982 | + | |
1983 | + if (!updated) | |
1984 | + list = g_list_remove (list, item); | |
1985 | + | |
1986 | + EGG_RECENT_ITEM_LIST_UNREF (list); | |
1987 | + ret = TRUE; | |
1988 | + } else { | |
1989 | + g_warning ("Failed to lock: %s", strerror (errno)); | |
1990 | + fclose (file); | |
1991 | + return FALSE; | |
1992 | + } | |
1993 | + | |
1994 | + if (!egg_recent_model_multi_unlock_file (file)) | |
1995 | + g_warning ("Failed to unlock: %s", strerror (errno)); | |
1996 | + | |
1997 | + fclose (file); | |
1998 | + | |
1999 | + if (model->priv->monitor == NULL) { | |
2000 | + /* since monitoring isn't working, at least give a | |
2001 | + * local notification | |
2002 | + */ | |
2003 | + egg_recent_model_multi_changed (model); | |
2004 | + } | |
2005 | + | |
2006 | + return ret; | |
2007 | +} | |
2008 | + | |
2009 | +/** | |
2010 | + * egg_recent_model_multi_add: | |
2011 | + * @model: A EggRecentModelMulti object. | |
2012 | + * @uri: A string URI | |
2013 | + * | |
2014 | + * This function adds an item to the list of recently used URIs. | |
2015 | + * | |
2016 | + * Returns: gboolean | |
2017 | + */ | |
2018 | +gboolean | |
2019 | +egg_recent_model_multi_add (EggRecentModelMulti *model, const gchar *uri) | |
2020 | +{ | |
2021 | + EggRecentItem *item; | |
2022 | + gboolean ret = FALSE; | |
2023 | + | |
2024 | + g_return_val_if_fail (model != NULL, FALSE); | |
2025 | + g_return_val_if_fail (uri != NULL, FALSE); | |
2026 | + | |
2027 | + item = egg_recent_item_new_from_uri (uri); | |
2028 | + | |
2029 | + g_return_val_if_fail (item != NULL, FALSE); | |
2030 | + | |
2031 | + ret = egg_recent_model_multi_add_full (model, item); | |
2032 | + | |
2033 | + egg_recent_item_unref (item); | |
2034 | + | |
2035 | + return ret; | |
2036 | +} | |
2037 | + | |
2038 | + | |
2039 | + | |
2040 | +/** | |
2041 | + * egg_recent_model_multi_delete: | |
2042 | + * @model: A EggRecentModelMulti object. | |
2043 | + * @uri: The URI you want to delete. | |
2044 | + * | |
2045 | + * This function deletes a URI from the file of recently used URIs. | |
2046 | + * | |
2047 | + * Returns: gboolean | |
2048 | + */ | |
2049 | +gboolean | |
2050 | +egg_recent_model_multi_delete (EggRecentModelMulti * model, const gchar * uri) | |
2051 | +{ | |
2052 | + FILE *file; | |
2053 | + GList *list; | |
2054 | + unsigned int length; | |
2055 | + gboolean ret = FALSE; | |
2056 | + | |
2057 | + g_return_val_if_fail (model != NULL, FALSE); | |
2058 | + g_return_val_if_fail (EGG_IS_RECENT_MODEL_MULTI (model), FALSE); | |
2059 | + g_return_val_if_fail (uri != NULL, FALSE); | |
2060 | + | |
2061 | + file = egg_recent_model_multi_open_file (model, TRUE); | |
2062 | + g_return_val_if_fail (file != NULL, FALSE); | |
2063 | + | |
2064 | + if (egg_recent_model_multi_lock_file (file)) { | |
2065 | + list = egg_recent_model_multi_read (model, file); | |
2066 | + | |
2067 | + if (list == NULL) | |
2068 | + goto out; | |
2069 | + | |
2070 | + length = g_list_length (list); | |
2071 | + | |
2072 | + list = egg_recent_model_multi_delete_from_list (list, uri); | |
2073 | + | |
2074 | + if (length == g_list_length (list)) { | |
2075 | + /* nothing was deleted */ | |
2076 | + EGG_RECENT_ITEM_LIST_UNREF (list); | |
2077 | + } else { | |
2078 | + egg_recent_model_multi_write (model, file, list); | |
2079 | + EGG_RECENT_ITEM_LIST_UNREF (list); | |
2080 | + ret = TRUE; | |
2081 | + | |
2082 | + } | |
2083 | + } else { | |
2084 | + g_warning ("Failed to lock: %s", strerror (errno)); | |
2085 | + return FALSE; | |
2086 | + } | |
2087 | + | |
2088 | +out: | |
2089 | + | |
2090 | + if (!egg_recent_model_multi_unlock_file (file)) | |
2091 | + g_warning ("Failed to unlock: %s", strerror (errno)); | |
2092 | + | |
2093 | + fclose (file); | |
2094 | + | |
2095 | + g_hash_table_remove (model->priv->monitors, uri); | |
2096 | + | |
2097 | + if (model->priv->monitor == NULL && ret) { | |
2098 | + /* since monitoring isn't working, at least give a | |
2099 | + * local notification | |
2100 | + */ | |
2101 | + egg_recent_model_multi_changed (model); | |
2102 | + } | |
2103 | + | |
2104 | + return ret; | |
2105 | +} | |
2106 | + | |
2107 | + | |
2108 | +/** | |
2109 | + * egg_recent_model_multi_get_list: | |
2110 | + * @model: A EggRecentModelMulti object. | |
2111 | + * | |
2112 | + * This function gets the current contents of the file | |
2113 | + * | |
2114 | + * Returns: a GList | |
2115 | + */ | |
2116 | +GList * | |
2117 | +egg_recent_model_multi_get_list (EggRecentModelMulti *model) | |
2118 | +{ | |
2119 | + FILE *file; | |
2120 | + GList *list = NULL; | |
2121 | + | |
2122 | + file = egg_recent_model_multi_open_file (model, FALSE); | |
2123 | + if (file == NULL) | |
2124 | + return NULL; | |
2125 | + | |
2126 | + if (egg_recent_model_multi_lock_file (file)) | |
2127 | + list = egg_recent_model_multi_read (model, file); | |
2128 | + else { | |
2129 | + g_warning ("Failed to lock: %s", strerror (errno)); | |
2130 | + fclose (file); | |
2131 | + return NULL; | |
2132 | + } | |
2133 | + | |
2134 | + if (!egg_recent_model_multi_unlock_file (file)) | |
2135 | + g_warning ("Failed to unlock: %s", strerror (errno)); | |
2136 | + | |
2137 | + if (list != NULL) { | |
2138 | + list = egg_recent_model_multi_filter (model, list); | |
2139 | + list = egg_recent_model_multi_sort (model, list); | |
2140 | + | |
2141 | + egg_recent_model_multi_enforce_limit (list, model->priv->limit); | |
2142 | + } | |
2143 | + | |
2144 | + fclose (file); | |
2145 | + | |
2146 | + return list; | |
2147 | +} | |
2148 | + | |
2149 | + | |
2150 | + | |
2151 | +/** | |
2152 | + * egg_recent_model_multi_set_limit: | |
2153 | + * @model: A EggRecentModelMulti object. | |
2154 | + * @limit: The maximum length of the list | |
2155 | + * | |
2156 | + * This function sets the maximum length of the list. Note: This only affects | |
2157 | + * the length of the list emitted in the "changed" signal, not the list stored | |
2158 | + * on disk. | |
2159 | + * | |
2160 | + * Returns: void | |
2161 | + */ | |
2162 | +void | |
2163 | +egg_recent_model_multi_set_limit (EggRecentModelMulti *model, int limit) | |
2164 | +{ | |
2165 | + model->priv->use_default_limit = FALSE; | |
2166 | + | |
2167 | + egg_recent_model_multi_set_limit_internal (model, limit); | |
2168 | +} | |
2169 | + | |
2170 | +/** | |
2171 | + * egg_recent_model_multi_get_limit: | |
2172 | + * @model: A EggRecentModelMulti object. | |
2173 | + * | |
2174 | + * This function gets the maximum length of the list. | |
2175 | + * | |
2176 | + * Returns: int | |
2177 | + */ | |
2178 | +int | |
2179 | +egg_recent_model_multi_get_limit (EggRecentModelMulti *model) | |
2180 | +{ | |
2181 | + return model->priv->limit; | |
2182 | +} | |
2183 | + | |
2184 | + | |
2185 | +/** | |
2186 | + * egg_recent_model_multi_clear: | |
2187 | + * @model: A EggRecentModelMulti object. | |
2188 | + * | |
2189 | + * This function clears the contents of the file | |
2190 | + * | |
2191 | + * Returns: void | |
2192 | + */ | |
2193 | +void | |
2194 | +egg_recent_model_multi_clear (EggRecentModelMulti *model) | |
2195 | +{ | |
2196 | + FILE *file; | |
2197 | + int fd; | |
2198 | + | |
2199 | + file = egg_recent_model_multi_open_file (model, TRUE); | |
2200 | + g_return_if_fail (file != NULL); | |
2201 | + | |
2202 | + fd = fileno (file); | |
2203 | + | |
2204 | + if (egg_recent_model_multi_lock_file (file)) { | |
2205 | + ftruncate (fd, 0); | |
2206 | + } else { | |
2207 | + g_warning ("Failed to lock: %s", strerror (errno)); | |
2208 | + return; | |
2209 | + } | |
2210 | + | |
2211 | + if (!egg_recent_model_multi_unlock_file (file)) | |
2212 | + g_warning ("Failed to unlock: %s", strerror (errno)); | |
2213 | + | |
2214 | + fclose (file); | |
2215 | + | |
2216 | + if (model->priv->monitor == NULL) { | |
2217 | + /* since monitoring isn't working, at least give a | |
2218 | + * local notification | |
2219 | + */ | |
2220 | + egg_recent_model_multi_changed (model); | |
2221 | + } | |
2222 | +} | |
2223 | + | |
2224 | +static void | |
2225 | +egg_recent_model_multi_clear_mime_filter (EggRecentModelMulti *model) | |
2226 | +{ | |
2227 | + g_return_if_fail (model != NULL); | |
2228 | + | |
2229 | + if (model->priv->mime_filter_values != NULL) { | |
2230 | + g_slist_foreach (model->priv->mime_filter_values, | |
2231 | + (GFunc) g_pattern_spec_free, NULL); | |
2232 | + g_slist_free (model->priv->mime_filter_values); | |
2233 | + model->priv->mime_filter_values = NULL; | |
2234 | + } | |
2235 | +} | |
2236 | + | |
2237 | +/** | |
2238 | + * egg_recent_model_multi_set_filter_mime_types: | |
2239 | + * @model: A EggRecentModelMulti object. | |
2240 | + * | |
2241 | + * Sets which mime types are allowed in the list. | |
2242 | + * | |
2243 | + * Returns: void | |
2244 | + */ | |
2245 | +void | |
2246 | +egg_recent_model_multi_set_filter_mime_types (EggRecentModelMulti *model, | |
2247 | + ...) | |
2248 | +{ | |
2249 | + va_list valist; | |
2250 | + GSList *list = NULL; | |
2251 | + gchar *str; | |
2252 | + | |
2253 | + g_return_if_fail (model != NULL); | |
2254 | + | |
2255 | + egg_recent_model_multi_clear_mime_filter (model); | |
2256 | + | |
2257 | + va_start (valist, model); | |
2258 | + | |
2259 | + str = va_arg (valist, gchar*); | |
2260 | + | |
2261 | + while (str != NULL) { | |
2262 | + list = g_slist_prepend (list, g_pattern_spec_new (str)); | |
2263 | + | |
2264 | + str = va_arg (valist, gchar*); | |
2265 | + } | |
2266 | + | |
2267 | + va_end (valist); | |
2268 | + | |
2269 | + model->priv->mime_filter_values = list; | |
2270 | +} | |
2271 | + | |
2272 | +static void | |
2273 | +egg_recent_model_multi_clear_group_filter (EggRecentModelMulti *model) | |
2274 | +{ | |
2275 | + g_return_if_fail (model != NULL); | |
2276 | + | |
2277 | + if (model->priv->group_filter_values != NULL) { | |
2278 | + g_slist_foreach (model->priv->group_filter_values, (GFunc)g_free, NULL); | |
2279 | + g_slist_free (model->priv->group_filter_values); | |
2280 | + model->priv->group_filter_values = NULL; | |
2281 | + } | |
2282 | +} | |
2283 | + | |
2284 | +/** | |
2285 | + * egg_recent_model_multi_set_filter_groups: | |
2286 | + * @model: A EggRecentModelMulti object. | |
2287 | + * | |
2288 | + * Sets which groups are allowed in the list. | |
2289 | + * | |
2290 | + * Returns: void | |
2291 | + */ | |
2292 | +void | |
2293 | +egg_recent_model_multi_set_filter_groups (EggRecentModelMulti *model, | |
2294 | + ...) | |
2295 | +{ | |
2296 | + va_list valist; | |
2297 | + GSList *list = NULL; | |
2298 | + gchar *str; | |
2299 | + | |
2300 | + g_return_if_fail (model != NULL); | |
2301 | + | |
2302 | + egg_recent_model_multi_clear_group_filter (model); | |
2303 | + | |
2304 | + va_start (valist, model); | |
2305 | + | |
2306 | + str = va_arg (valist, gchar*); | |
2307 | + | |
2308 | + while (str != NULL) { | |
2309 | + list = g_slist_prepend (list, g_strdup (str)); | |
2310 | + | |
2311 | + str = va_arg (valist, gchar*); | |
2312 | + } | |
2313 | + | |
2314 | + va_end (valist); | |
2315 | + | |
2316 | + model->priv->group_filter_values = list; | |
2317 | +} | |
2318 | + | |
2319 | +static void | |
2320 | +egg_recent_model_multi_clear_scheme_filter (EggRecentModelMulti *model) | |
2321 | +{ | |
2322 | + g_return_if_fail (model != NULL); | |
2323 | + | |
2324 | + if (model->priv->scheme_filter_values != NULL) { | |
2325 | + g_slist_foreach (model->priv->scheme_filter_values, | |
2326 | + (GFunc) g_pattern_spec_free, NULL); | |
2327 | + g_slist_free (model->priv->scheme_filter_values); | |
2328 | + model->priv->scheme_filter_values = NULL; | |
2329 | + } | |
2330 | +} | |
2331 | + | |
2332 | +/** | |
2333 | + * egg_recent_model_multi_set_filter_uri_schemes: | |
2334 | + * @model: A EggRecentModelMulti object. | |
2335 | + * | |
2336 | + * Sets which URI schemes (file, http, ftp, etc) are allowed in the list. | |
2337 | + * | |
2338 | + * Returns: void | |
2339 | + */ | |
2340 | +void | |
2341 | +egg_recent_model_multi_set_filter_uri_schemes (EggRecentModelMulti *model, ...) | |
2342 | +{ | |
2343 | + va_list valist; | |
2344 | + GSList *list = NULL; | |
2345 | + gchar *str; | |
2346 | + | |
2347 | + g_return_if_fail (model != NULL); | |
2348 | + | |
2349 | + egg_recent_model_multi_clear_scheme_filter (model); | |
2350 | + | |
2351 | + va_start (valist, model); | |
2352 | + | |
2353 | + str = va_arg (valist, gchar*); | |
2354 | + | |
2355 | + while (str != NULL) { | |
2356 | + list = g_slist_prepend (list, g_pattern_spec_new (str)); | |
2357 | + | |
2358 | + str = va_arg (valist, gchar*); | |
2359 | + } | |
2360 | + | |
2361 | + va_end (valist); | |
2362 | + | |
2363 | + model->priv->scheme_filter_values = list; | |
2364 | +} | |
2365 | + | |
2366 | +/** | |
2367 | + * egg_recent_model_multi_set_sort: | |
2368 | + * @model: A EggRecentModelMulti object. | |
2369 | + * @sort: A EggRecentModelMultiSort type | |
2370 | + * | |
2371 | + * Sets the type of sorting to be used. | |
2372 | + * | |
2373 | + * Returns: void | |
2374 | + */ | |
2375 | +void | |
2376 | +egg_recent_model_multi_set_sort (EggRecentModelMulti *model, | |
2377 | + EggRecentModelMultiSort sort) | |
2378 | +{ | |
2379 | + g_return_if_fail (model != NULL); | |
2380 | + | |
2381 | + model->priv->sort_type = sort; | |
2382 | +} | |
2383 | + | |
2384 | +/** | |
2385 | + * egg_recent_model_multi_changed: | |
2386 | + * @model: A EggRecentModelMulti object. | |
2387 | + * | |
2388 | + * This function causes a "changed" signal to be emitted. | |
2389 | + * | |
2390 | + * Returns: void | |
2391 | + */ | |
2392 | +void | |
2393 | +egg_recent_model_multi_changed (EggRecentModelMulti *model) | |
2394 | +{ | |
2395 | + GList *list = NULL; | |
2396 | + | |
2397 | + if (model->priv->limit > 0) { | |
2398 | + list = egg_recent_model_multi_get_list (model); | |
2399 | + /* egg_recent_model_multi_monitor_list (model, list); */ | |
2400 | + | |
2401 | + g_signal_emit (G_OBJECT (model), model_signals[CHANGED], 0, | |
2402 | + list); | |
2403 | + } | |
2404 | + | |
2405 | + if (list) | |
2406 | + EGG_RECENT_ITEM_LIST_UNREF (list); | |
2407 | +} | |
2408 | + | |
2409 | +static void | |
2410 | +egg_recent_model_multi_remove_expired_list (EggRecentModelMulti *model, GList *list) | |
2411 | +{ | |
2412 | + time_t current_time; | |
2413 | + time_t day_seconds; | |
2414 | + | |
2415 | + time (¤t_time); | |
2416 | + day_seconds = model->priv->expire_days*24*60*60; | |
2417 | + | |
2418 | + while (list != NULL) { | |
2419 | + EggRecentItem *item = list->data; | |
2420 | + time_t timestamp; | |
2421 | + | |
2422 | + timestamp = egg_recent_item_get_timestamp (item); | |
2423 | + | |
2424 | + if ((timestamp+day_seconds) < current_time) { | |
2425 | + gchar *uri = egg_recent_item_get_uri (item); | |
2426 | + egg_recent_model_multi_delete (model, uri); | |
2427 | + | |
2428 | + g_strdup (uri); | |
2429 | + } | |
2430 | + | |
2431 | + list = list->next; | |
2432 | + } | |
2433 | +} | |
2434 | + | |
2435 | + | |
2436 | +/** | |
2437 | + * egg_recent_model_multi_remove_expired: | |
2438 | + * @model: A EggRecentModelMulti object. | |
2439 | + * | |
2440 | + * Goes through the entire list, and removes any items that are older than | |
2441 | + * the user-specified expiration period. | |
2442 | + * | |
2443 | + * Returns: void | |
2444 | + */ | |
2445 | +void | |
2446 | +egg_recent_model_multi_remove_expired (EggRecentModelMulti *model) | |
2447 | +{ | |
2448 | + FILE *file; | |
2449 | + GList *list=NULL; | |
2450 | + | |
2451 | + g_return_if_fail (model != NULL); | |
2452 | + | |
2453 | + file = egg_recent_model_multi_open_file (model, FALSE); | |
2454 | + if (file == NULL) | |
2455 | + return; | |
2456 | + | |
2457 | + if (egg_recent_model_multi_lock_file (file)) { | |
2458 | + list = egg_recent_model_multi_read (model, file); | |
2459 | + | |
2460 | + } else { | |
2461 | + g_warning ("Failed to lock: %s", strerror (errno)); | |
2462 | + return; | |
2463 | + } | |
2464 | + | |
2465 | + if (!egg_recent_model_multi_unlock_file (file)) | |
2466 | + g_warning ("Failed to unlock: %s", strerror (errno)); | |
2467 | + | |
2468 | + if (list != NULL) { | |
2469 | + egg_recent_model_multi_remove_expired_list (model, list); | |
2470 | + EGG_RECENT_ITEM_LIST_UNREF (list); | |
2471 | + } | |
2472 | + | |
2473 | + fclose (file); | |
2474 | +} | |
2475 | + | |
2476 | +/** | |
2477 | + * egg_recent_model_multi_get_type: | |
2478 | + * | |
2479 | + * This returns a GType representing a EggRecentModelMulti object. | |
2480 | + * | |
2481 | + * Returns: a GType | |
2482 | + */ | |
2483 | +GType | |
2484 | +egg_recent_model_multi_get_type (void) | |
2485 | +{ | |
2486 | + static GType egg_recent_model_multi_type = 0; | |
2487 | + | |
2488 | + if(!egg_recent_model_multi_type) { | |
2489 | + static const GTypeInfo egg_recent_model_multi_info = { | |
2490 | + sizeof (EggRecentModelMultiClass), | |
2491 | + NULL, /* base init */ | |
2492 | + NULL, /* base finalize */ | |
2493 | + (GClassInitFunc)egg_recent_model_multi_class_init, /* class init */ | |
2494 | + NULL, /* class finalize */ | |
2495 | + NULL, /* class data */ | |
2496 | + sizeof (EggRecentModelMulti), | |
2497 | + 0, | |
2498 | + (GInstanceInitFunc) egg_recent_model_multi_init | |
2499 | + }; | |
2500 | + | |
2501 | + egg_recent_model_multi_type = g_type_register_static (G_TYPE_OBJECT, | |
2502 | + "EggRecentModelMulti", | |
2503 | + &egg_recent_model_multi_info, 0); | |
2504 | + } | |
2505 | + | |
2506 | + return egg_recent_model_multi_type; | |
2507 | +} | |
2508 | + | |
2509 | diff -urN ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-model-multi.h ./libgnome-desktop/egg-recent-model-multi.h | |
2510 | --- ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-model-multi.h 1969-12-31 19:00:00.000000000 -0500 | |
2511 | +++ ./libgnome-desktop/egg-recent-model-multi.h 2006-05-08 16:48:27.000000000 -0400 | |
2512 | @@ -0,0 +1,81 @@ | |
2513 | +/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ | |
2514 | +#ifndef __EGG_RECENT_MODEL_MULTI_H__ | |
2515 | +#define __EGG_RECENT_MODEL_MULTI_H__ | |
2516 | + | |
2517 | +#include "egg-recent-item.h" | |
2518 | + | |
2519 | +G_BEGIN_DECLS | |
2520 | + | |
2521 | +#define EGG_TYPE_RECENT_MODEL_MULTI (egg_recent_model_multi_get_type ()) | |
2522 | +#define EGG_RECENT_MODEL_MULTI(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, EGG_TYPE_RECENT_MODEL_MULTI, EggRecentModelMulti) | |
2523 | +#define EGG_RECENT_MODEL_MULTI_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, EGG_TYPE_RECENT_MODEL_MULTI, EggRecentModelMultiClass) | |
2524 | +#define EGG_IS_RECENT_MODEL_MULTI(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, egg_recent_model_multi_get_type ()) | |
2525 | + | |
2526 | +typedef struct _EggRecentModelMulti EggRecentModelMulti; | |
2527 | +typedef struct _EggRecentModelMultiPrivate EggRecentModelMultiPrivate; | |
2528 | +typedef struct _EggRecentModelMultiClass EggRecentModelMultiClass; | |
2529 | + | |
2530 | +struct _EggRecentModelMulti { | |
2531 | + GObject parent_instance; | |
2532 | + | |
2533 | + EggRecentModelMultiPrivate *priv; | |
2534 | +}; | |
2535 | + | |
2536 | +struct _EggRecentModelMultiClass { | |
2537 | + GObjectClass parent_class; | |
2538 | + | |
2539 | + void (*changed) (EggRecentModelMulti *model, GList *list); | |
2540 | +}; | |
2541 | + | |
2542 | +typedef enum { | |
2543 | + EGG_RECENT_MODEL_MULTI_SORT_MRU, | |
2544 | + EGG_RECENT_MODEL_MULTI_SORT_LRU, | |
2545 | + EGG_RECENT_MODEL_MULTI_SORT_NONE | |
2546 | +} EggRecentModelMultiSort; | |
2547 | + | |
2548 | + | |
2549 | +/* Standard group names */ | |
2550 | +#define EGG_RECENT_GROUP_LAUNCHERS "Launchers" | |
2551 | + | |
2552 | + | |
2553 | +GType egg_recent_model_multi_get_type (void); | |
2554 | + | |
2555 | +/* constructors */ | |
2556 | +EggRecentModelMulti * egg_recent_model_multi_new (EggRecentModelMultiSort sort); | |
2557 | +EggRecentModelMulti * egg_recent_model_multi_new_with_file_path (EggRecentModelMultiSort sort, const gchar *file_path); | |
2558 | + | |
2559 | +/* public methods */ | |
2560 | +void egg_recent_model_multi_set_filter_mime_types (EggRecentModelMulti *model, | |
2561 | + ...); | |
2562 | + | |
2563 | +void egg_recent_model_multi_set_filter_groups (EggRecentModelMulti *model, ...); | |
2564 | + | |
2565 | +void egg_recent_model_multi_set_filter_uri_schemes (EggRecentModelMulti *model, | |
2566 | + ...); | |
2567 | + | |
2568 | +void egg_recent_model_multi_set_sort (EggRecentModelMulti *model, | |
2569 | + EggRecentModelMultiSort sort); | |
2570 | + | |
2571 | +gboolean egg_recent_model_multi_add_full (EggRecentModelMulti *model, | |
2572 | + EggRecentItem *item); | |
2573 | + | |
2574 | +gboolean egg_recent_model_multi_add (EggRecentModelMulti *model, | |
2575 | + const gchar *uri); | |
2576 | + | |
2577 | +gboolean egg_recent_model_multi_delete (EggRecentModelMulti *model, | |
2578 | + const gchar *uri); | |
2579 | + | |
2580 | +void egg_recent_model_multi_clear (EggRecentModelMulti *model); | |
2581 | + | |
2582 | +GList * egg_recent_model_multi_get_list (EggRecentModelMulti *model); | |
2583 | + | |
2584 | +void egg_recent_model_multi_changed (EggRecentModelMulti *model); | |
2585 | + | |
2586 | +void egg_recent_model_multi_set_limit (EggRecentModelMulti *model, int limit); | |
2587 | +int egg_recent_model_multi_get_limit (EggRecentModelMulti *model); | |
2588 | + | |
2589 | +void egg_recent_model_multi_remove_expired (EggRecentModelMulti *model); | |
2590 | + | |
2591 | +G_END_DECLS | |
2592 | + | |
2593 | +#endif /* __EGG_RECENT_MODEL_MULTI_H__ */ | |
2594 | diff -urN ../gnome-desktop-2.12.2-pristine/libgnome-desktop/gnome-desktop-item.c ./libgnome-desktop/gnome-desktop-item.c | |
2595 | --- ../gnome-desktop-2.12.2-pristine/libgnome-desktop/gnome-desktop-item.c 2005-11-04 03:43:42.000000000 -0500 | |
2596 | +++ ./libgnome-desktop/gnome-desktop-item.c 2006-05-08 17:07:23.000000000 -0400 | |
2597 | @@ -59,6 +59,9 @@ | |
2598 | #include <gtk/gtk.h> | |
2599 | #endif | |
2600 | ||
2601 | +#include "egg-recent-item.h" | |
2602 | +#include "egg-recent-model-multi.h" | |
2603 | + | |
2604 | #define sure_string(s) ((s)!=NULL?(s):"") | |
2605 | ||
2606 | struct _GnomeDesktopItem { | |
2607 | @@ -128,6 +131,8 @@ | |
2608 | const char *uri, | |
2609 | GError **error); | |
2610 | ||
2611 | +static void update_recently_used_apps (const GnomeDesktopItem *item); | |
2612 | + | |
2613 | static int | |
2614 | readbuf_getc (ReadBuf *rb) | |
2615 | { | |
2616 | @@ -2091,6 +2096,8 @@ | |
2617 | (flags & GNOME_DESKTOP_ITEM_LAUNCH_APPEND_PATHS), | |
2618 | error); | |
2619 | ||
2620 | + update_recently_used_apps (item); | |
2621 | + | |
2622 | return ret; | |
2623 | } | |
2624 | ||
2625 | @@ -4095,3 +4102,22 @@ | |
2626 | ||
2627 | return q; | |
2628 | } | |
2629 | + | |
2630 | +static void | |
2631 | +update_recently_used_apps (const GnomeDesktopItem *item) | |
2632 | +{ | |
2633 | + EggRecentModelMulti *model; | |
2634 | + | |
2635 | + | |
2636 | + if (! item) | |
2637 | + return; | |
2638 | + | |
2639 | + model = egg_recent_model_multi_new_with_file_path (EGG_RECENT_MODEL_MULTI_SORT_MRU, ".recently-used-apps"); | |
2640 | + | |
2641 | + if (! model) | |
2642 | + return; | |
2643 | + | |
2644 | + egg_recent_model_multi_add (model, gnome_desktop_item_get_location (item)); | |
2645 | + | |
2646 | + g_object_unref (G_OBJECT (model)); | |
2647 | +} | |
2648 | Files ../gnome-desktop-2.12.2-pristine/libgnome-desktop/.gnome-desktop-item.c.swp and ./libgnome-desktop/.gnome-desktop-item.c.swp differ | |
2649 | diff -urN ../gnome-desktop-2.12.2-pristine/libgnome-desktop/Makefile.am ./libgnome-desktop/Makefile.am | |
2650 | --- ../gnome-desktop-2.12.2-pristine/libgnome-desktop/Makefile.am 2004-11-18 12:42:53.000000000 -0500 | |
2651 | +++ ./libgnome-desktop/Makefile.am 2006-05-08 16:55:25.000000000 -0400 | |
2652 | @@ -16,9 +16,13 @@ | |
2653 | ||
2654 | noinst_PROGRAMS = test-ditem test-hint test-ditem-edit | |
2655 | ||
2656 | -libgnome_desktop_2_la_SOURCES = \ | |
2657 | - gnome-desktop-item.c \ | |
2658 | - gnome-ditem-edit.c \ | |
2659 | +libgnome_desktop_2_la_SOURCES = \ | |
2660 | + gnome-desktop-item.c \ | |
2661 | + gnome-ditem-edit.c \ | |
2662 | + egg-recent-item.h \ | |
2663 | + egg-recent-item.c \ | |
2664 | + egg-recent-model-multi.h \ | |
2665 | + egg-recent-model-multi.c \ | |
2b287125 PZ |
2666 | gnome-hint.c \ |
2667 | gnome-bg.c | |
8f66dda0 | 2668 |