1
/* The GIMP -- an image manipulation program
2
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
5
* see http://freedesktop.org/Standards/recent-file-spec/
7
* This code is taken from libegg and has been adapted to the GIMP needs.
8
* The original author is James Willcox <jwillcox@cs.indiana.edu>,
9
* responsible for bugs in this version is Sven Neumann <sven@gimp.org>.
11
* This program is free software; you can redistribute it and/or modify
12
* it under the terms of the GNU General Public License as published by
13
* the Free Software Foundation; either version 2 of the License, or
14
* (at your option) any later version.
16
* This program is distributed in the hope that it will be useful,
17
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
* GNU General Public License for more details.
21
* You should have received a copy of the GNU General Public License
22
* along with this program; if not, write to the Free Software
23
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
36
#include <sys/types.h>
40
#include <glib-object.h>
42
#ifndef G_OS_WIN32 /* This code doesn't compile on win32 and the use of
43
* the freedesktop standard doesn't make much sense
44
* there anyway. If someone wants to contribute a win32
45
* specific implementation, that would be appreciated.
48
#include "config/config-types.h"
50
#include "config/gimpxmlparser.h"
52
#include "gimprecentitem.h"
53
#include "gimprecentlist.h"
56
#define GIMP_RECENT_LIST_FILE_NAME ".recently-used"
57
#define GIMP_RECENT_LIST_MAX_ITEMS 500
58
#define GIMP_RECENT_LIST_GROUP_GIMP "gimp"
61
#define GIMP_RECENT_ITEM_LIST_UNREF(list) \
62
g_list_foreach (list, (GFunc) gimp_recent_item_unref, NULL); \
70
GimpRecentItem *current_item;
87
#define TAG_RECENT_FILES "RecentFiles"
88
#define TAG_RECENT_ITEM "RecentItem"
90
#define TAG_MIME_TYPE "Mime-Type"
91
#define TAG_TIMESTAMP "Timestamp"
92
#define TAG_PRIVATE "Private"
93
#define TAG_GROUPS "Groups"
94
#define TAG_GROUP "Group"
97
static void start_element_handler (GMarkupParseContext *context,
98
const gchar *element_name,
99
const gchar **attribute_names,
100
const gchar **attribute_values,
103
static void end_element_handler (GMarkupParseContext *context,
104
const gchar *element_name,
107
static void text_handler (GMarkupParseContext *context,
113
static const GMarkupParser markup_parser =
115
start_element_handler,
123
gimp_recent_list_add_new_groups (GimpRecentItem *item,
124
GimpRecentItem *upd_item)
128
for (tmp = gimp_recent_item_get_groups (upd_item); tmp; tmp = tmp->next)
130
const gchar *group = tmp->data;
132
if (! gimp_recent_item_in_group (item, group))
133
gimp_recent_item_add_group (item, group);
138
gimp_recent_list_update_item (GList *items,
139
GimpRecentItem *upd_item)
141
const char *uri = gimp_recent_item_get_uri (upd_item);
144
for (tmp = items; tmp; tmp = tmp->next)
146
GimpRecentItem *item = tmp->data;
148
/* gnome_vfs_uris_match (gimp_recent_item_get_uri (item), uri) */
149
if (strcmp (gimp_recent_item_get_uri (item), uri) == 0)
151
gimp_recent_item_set_timestamp (item, (time_t) -1);
152
gimp_recent_list_add_new_groups (item, upd_item);
162
parse_info_init (ParseInfo *info)
164
info->states = g_slist_prepend (NULL, GINT_TO_POINTER (STATE_START));
169
parse_info_free (ParseInfo *info)
171
g_slist_free (info->states);
175
push_state (ParseInfo *info,
178
info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state));
182
pop_state (ParseInfo *info)
184
g_return_if_fail (info->states != NULL);
186
info->states = g_slist_remove (info->states, info->states->data);
190
peek_state (ParseInfo *info)
192
g_return_val_if_fail (info->states != NULL, STATE_START);
194
return GPOINTER_TO_INT (info->states->data);
197
#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
200
start_element_handler (GMarkupParseContext *context,
201
const gchar *element_name,
202
const gchar **attribute_names,
203
const gchar **attribute_values,
207
ParseInfo *info = user_data;
209
if (ELEMENT_IS (TAG_RECENT_FILES))
211
push_state (info, STATE_RECENT_FILES);
213
else if (ELEMENT_IS (TAG_RECENT_ITEM))
215
info->current_item = gimp_recent_item_new ();
216
push_state (info, STATE_RECENT_ITEM);
218
else if (ELEMENT_IS (TAG_URI))
220
push_state (info, STATE_URI);
222
else if (ELEMENT_IS (TAG_MIME_TYPE))
224
push_state (info, STATE_MIME_TYPE);
226
else if (ELEMENT_IS (TAG_TIMESTAMP))
228
push_state (info, STATE_TIMESTAMP);
230
else if (ELEMENT_IS (TAG_PRIVATE))
232
push_state (info, STATE_PRIVATE);
233
gimp_recent_item_set_private (info->current_item, TRUE);
235
else if (ELEMENT_IS (TAG_GROUPS))
237
push_state (info, STATE_GROUPS);
239
else if (ELEMENT_IS (TAG_GROUP))
241
push_state (info, STATE_GROUP);
246
end_element_handler (GMarkupParseContext *context,
247
const gchar *element_name,
251
ParseInfo *info = user_data;
253
switch (peek_state (info))
255
case STATE_RECENT_ITEM:
256
info->items = g_list_prepend (info->items, info->current_item);
267
text_handler (GMarkupParseContext *context,
273
ParseInfo *info = user_data;
275
switch (peek_state (info))
278
case STATE_RECENT_FILES:
279
case STATE_RECENT_ITEM:
285
gimp_recent_item_set_uri (info->current_item, text);
288
case STATE_MIME_TYPE:
289
gimp_recent_item_set_mime_type (info->current_item, text);
292
case STATE_TIMESTAMP:
293
gimp_recent_item_set_timestamp (info->current_item, (time_t) atoi (text));
297
gimp_recent_item_add_group (info->current_item, text);
303
gimp_recent_list_enforce_limit (GList *list,
309
/* limit < 0 means unlimited */
313
len = g_list_length (list);
319
end = g_list_nth (list, limit-1);
324
GIMP_RECENT_ITEM_LIST_UNREF (next);
329
gimp_recent_list_read (gint fd)
331
GimpXmlParser *parser;
334
GError *error = NULL;
336
lseek (fd, 0, SEEK_SET);
338
parse_info_init (&info);
340
parser = gimp_xml_parser_new (&markup_parser, &info);
342
if (! gimp_xml_parser_parse_fd (parser, fd, &error))
344
g_printerr ("%s", error->message);
345
g_error_free (error);
348
gimp_xml_parser_free (parser);
352
parse_info_free (&info);
354
return g_list_reverse (list);
358
gimp_recent_list_write_raw (gint fd,
359
const gchar *content,
363
gssize remaining = len;
365
lseek (fd, 0, SEEK_SET);
367
if (fstat (fd, &sbuf) < 0)
369
g_warning ("Couldn't stat XML document.");
373
if ((off_t) len < sbuf.st_size)
377
while (remaining > 0)
379
gssize written = write (fd, content, remaining);
381
if (written < 0 && errno != EINTR)
384
remaining -= written;
393
gimp_recent_list_write (gint fd,
399
string = g_string_new ("<?xml version=\"1.0\"?>\n");
400
string = g_string_append (string, "<" TAG_RECENT_FILES ">\n");
404
GimpRecentItem *item = list->data;
407
const gchar *mime_type;
411
uri = gimp_recent_item_get_uri_utf8 (item);
412
escaped_uri = g_markup_escape_text (uri, strlen (uri));
415
mime_type = gimp_recent_item_get_mime_type (item);
416
timestamp = gimp_recent_item_get_timestamp (item);
418
string = g_string_append (string, " <" TAG_RECENT_ITEM ">\n");
420
g_string_append_printf (string,
421
" <" TAG_URI ">%s</" TAG_URI ">\n", escaped_uri);
424
g_string_append_printf (string,
425
" <" TAG_MIME_TYPE ">%s</" TAG_MIME_TYPE ">\n", mime_type);
427
g_string_append_printf (string,
428
" <" TAG_MIME_TYPE "></" TAG_MIME_TYPE ">\n");
430
g_string_append_printf (string,
431
" <" TAG_TIMESTAMP ">%d</" TAG_TIMESTAMP ">\n", (gint) timestamp);
433
if (gimp_recent_item_get_private (item))
434
string = g_string_append (string,
435
" <" TAG_PRIVATE "/>\n");
437
groups = gimp_recent_item_get_groups (item);
441
/* write the groups */
442
string = g_string_append (string,
443
" <" TAG_GROUPS ">\n");
445
if (groups == NULL && gimp_recent_item_get_private (item))
446
g_warning ("Item with URI \"%s\" marked as private, but"
447
" does not belong to any groups.\n", uri);
451
const gchar *group = groups->data;
452
gchar *escaped_group;
454
escaped_group = g_markup_escape_text (group, strlen(group));
456
g_string_append_printf (string,
457
" <" TAG_GROUP ">%s</" TAG_GROUP ">\n",
460
g_free (escaped_group);
462
groups = groups->next;
465
string = g_string_append (string, " </" TAG_GROUPS ">\n");
468
string = g_string_append (string,
469
" </" TAG_RECENT_ITEM ">\n");
471
g_free (escaped_uri);
476
string = g_string_append (string, "</" TAG_RECENT_FILES ">");
478
success = gimp_recent_list_write_raw (fd, string->str, string->len);
480
g_string_free (string, TRUE);
486
gimp_recent_list_lock_file (gint fd)
490
/* Attempt to lock the file 5 times,
491
* waiting a random interval (< 1 second)
492
* in between attempts.
493
* We should really be doing asynchronous
494
* locking, but requires substantially larger
498
lseek (fd, 0, SEEK_SET);
500
for (i = 0; i < 5; i++)
504
if (lockf (fd, F_TLOCK, 0) == 0)
507
rand_interval = 1 + (gint) (10.0 * rand () / (RAND_MAX + 1.0));
509
g_usleep (100000 * rand_interval);
516
gimp_recent_list_unlock_file (gint fd)
518
lseek (fd, 0, SEEK_SET);
520
return (lockf (fd, F_ULOCK, 0) == 0) ? TRUE : FALSE;
524
gimp_recent_list_add_item (GimpRecentItem *item)
529
gboolean success = FALSE;
530
gboolean created = FALSE;
531
gboolean updated = FALSE;
533
home = g_get_home_dir ();
537
filename = g_build_filename (home, GIMP_RECENT_LIST_FILE_NAME, NULL);
539
fd = open (filename, O_RDWR);
543
fd = creat (filename, S_IRUSR | S_IWUSR);
552
if (gimp_recent_list_lock_file (fd))
557
list = gimp_recent_list_read (fd);
559
/* if it's already there, we just update it */
560
updated = gimp_recent_list_update_item (list, item);
564
list = g_list_prepend (list, item);
565
gimp_recent_list_enforce_limit (list, GIMP_RECENT_LIST_MAX_ITEMS);
568
/* write new stuff */
569
if (!gimp_recent_list_write (fd, list))
570
g_warning ("Write failed: %s", g_strerror (errno));
573
list = g_list_remove (list, item);
575
GIMP_RECENT_ITEM_LIST_UNREF (list);
580
g_warning ("Failed to lock: %s", g_strerror (errno));
585
if (! gimp_recent_list_unlock_file (fd))
586
g_warning ("Failed to unlock: %s", strerror (errno));
594
* gimp_recent_list_add_uri:
596
* @mime_type: a MIME type
598
* This function adds an item to the list of recently used URIs.
599
* See http://freedesktop.org/Standards/recent-file-spec/.
601
* On the Win32 platform, this call is unimplemented and will always
604
* Returns: %TRUE on success, %FALSE otherwise
607
gimp_recent_list_add_uri (const gchar *uri,
608
const gchar *mime_type)
610
GimpRecentItem *item;
613
g_return_val_if_fail (uri != NULL, FALSE);
616
! strlen (mime_type) ||
617
! g_utf8_validate (mime_type, -1, NULL))
620
item = gimp_recent_item_new_from_uri (uri);
624
gimp_recent_item_set_mime_type (item, mime_type);
625
gimp_recent_item_set_timestamp (item, -1);
626
gimp_recent_item_add_group (item, GIMP_RECENT_LIST_GROUP_GIMP);
628
success = gimp_recent_list_add_item (item);
630
gimp_recent_item_unref (item);
635
#else /* G_OS_WIN32 */
638
gimp_recent_list_add_uri (const gchar *uri,
639
const gchar *mime_type)