~jeremywootten/pantheon-files/use-templates

« back to all changes in this revision

Viewing changes to src/marlin-bookmark-list.c

Merge changes from fix-corruption-of-bookmarks-on-update branch

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Marlin
3
 
 *
4
 
 * Copyright (C) 1999, 2000 Eazel, Inc.
5
 
 *
6
 
 * Marlin is free software; you can redistribute it and/or
7
 
 * modify it under the terms of the GNU General Public License as
8
 
 * published by the Free Software Foundation; either version 2 of the
9
 
 * License, or (at your option) any later version.
10
 
 *
11
 
 * Marlin is distributed in the hope that it will be useful,
12
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 
 * General Public License for more details.
15
 
 *
16
 
 * You should have received a copy of the GNU General Public License
17
 
 * along with this program; if not, write to the Free Software
18
 
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
 
 *
20
 
 * Authors: John Sullivan <sullivan@eazel.com>
21
 
 *          ammonkey <am.monkeyd@gmail.com>
22
 
 */
23
 
 
24
 
/* nautilus-bookmark-list.c - implementation of centralized list of bookmarks.
25
 
*/
26
 
 
27
 
#include "marlin-bookmark-list.h"
28
 
 
29
 
#include <gio/gio.h>
30
 
#include <string.h>
31
 
#include "gof-file.h"
32
 
//#include "marlin-icons.h"
33
 
 
34
 
#define MAX_BOOKMARK_LENGTH 80
35
 
#define LOAD_JOB 1
36
 
#define SAVE_JOB 2
37
 
 
38
 
enum {
39
 
    CONTENTS_CHANGED,
40
 
    DELETED,
41
 
    LAST_SIGNAL
42
 
};
43
 
 
44
 
static guint signals[LAST_SIGNAL];
45
 
static char *window_geometry;
46
 
static MarlinBookmarkList *singleton = NULL;
47
 
 
48
 
/* forward declarations */
49
 
 
50
 
static void        marlin_bookmark_list_load_file     (MarlinBookmarkList *bookmarks);
51
 
static void        marlin_bookmark_list_save_file     (MarlinBookmarkList *bookmarks);
52
 
 
53
 
G_DEFINE_TYPE(MarlinBookmarkList, marlin_bookmark_list, G_TYPE_OBJECT)
54
 
 
55
 
static MarlinBookmark *
56
 
new_bookmark_from_uri (const char *uri, char *label)
57
 
{
58
 
    MarlinBookmark *bookmark;
59
 
    GOFFile *file;
60
 
    char *name;
61
 
    GIcon *icon;
62
 
    gboolean has_label;
63
 
    GFile *location;
64
 
 
65
 
    /*location = g_file_new_for_uri (uri);
66
 
    has_label = FALSE;
67
 
 
68
 
    new_bookmark = NULL;
69
 
    name = NULL;
70
 
 
71
 
    if (label != NULL) {
72
 
        name = g_strdup (label);
73
 
        has_label = TRUE;
74
 
    }*/
75
 
 
76
 
    /*if (!g_file_is_native (location))
77
 
        icon = g_themed_icon_new (MARLIN_ICON_FOLDER_REMOTE);*/
78
 
    //file = gof_file_get (location);
79
 
    file = gof_file_get_by_uri (uri);
80
 
    /*if (file != NULL) {
81
 
        if (name == NULL)
82
 
            name = g_strdup (file->display_name);
83
 
        if (icon == NULL)
84
 
            icon = file->icon;
85
 
    }
86
 
    if (name == NULL)
87
 
        name = g_file_get_parse_name (location);*/
88
 
 
89
 
    //new_bookmark = marlin_bookmark_new (location, name, has_label, icon);
90
 
    bookmark = marlin_bookmark_new (file, label);
91
 
 
92
 
    if (file != NULL)
93
 
        g_object_unref (file);
94
 
 
95
 
    //g_free (name);
96
 
    return bookmark;
97
 
}
98
 
 
99
 
static GFile *
100
 
marlin_bookmark_list_get_file (void)
101
 
{
102
 
    char *filename;
103
 
    GFile *file;
104
 
 
105
 
    filename = g_build_filename (g_get_home_dir (), ".gtk-bookmarks", NULL);
106
 
    file = g_file_new_for_path (filename);
107
 
 
108
 
    g_free (filename);
109
 
 
110
 
    return file;
111
 
}
112
 
 
113
 
/* Initialization.  */
114
 
 
115
 
static void
116
 
bookmark_in_list_changed_callback (MarlinBookmark     *bookmark,
117
 
                                   MarlinBookmarkList *bookmarks)
118
 
{
119
 
    g_assert (MARLIN_IS_BOOKMARK (bookmark));
120
 
    g_assert (MARLIN_IS_BOOKMARK_LIST (bookmarks));
121
 
 
122
 
    /* Save changes so we'll have the good icon next time. */
123
 
    marlin_bookmark_list_save_file (bookmarks);
124
 
}
125
 
 
126
 
static void
127
 
bookmark_in_list_to_be_deleted_callback (MarlinBookmark *bookmark,
128
 
                                        MarlinBookmarkList *bookmarks)
129
 
{
130
 
    g_assert (MARLIN_IS_BOOKMARK (bookmark));
131
 
    g_assert (MARLIN_IS_BOOKMARK_LIST (bookmarks));
132
 
 
133
 
    g_debug ("%s - deleting %s from bookmark list",
134
 
            G_STRFUNC,
135
 
            g_file_get_uri (bookmark->file->location));
136
 
            
137
 
    marlin_bookmark_list_delete_items_with_uri (bookmarks,
138
 
                                                g_file_get_uri (bookmark->file->location));
139
 
}
140
 
 
141
 
static void
142
 
stop_monitoring_bookmark (MarlinBookmarkList *bookmarks,
143
 
                          MarlinBookmark     *bookmark)
144
 
{
145
 
    g_signal_handlers_disconnect_by_func (bookmark,
146
 
                                          bookmark_in_list_changed_callback,
147
 
                                          bookmarks);
148
 
    g_signal_handlers_disconnect_by_func (bookmark,
149
 
                                          bookmark_in_list_to_be_deleted_callback,
150
 
                                          bookmarks);
151
 
}
152
 
 
153
 
static void
154
 
stop_monitoring_one (gpointer data, gpointer user_data)
155
 
{
156
 
    g_assert (MARLIN_IS_BOOKMARK (data));
157
 
    g_assert (MARLIN_IS_BOOKMARK_LIST (user_data));
158
 
 
159
 
    stop_monitoring_bookmark (MARLIN_BOOKMARK_LIST (user_data),
160
 
                              MARLIN_BOOKMARK (data));
161
 
    g_object_unref (MARLIN_BOOKMARK (data));
162
 
}
163
 
 
164
 
static void
165
 
clear (MarlinBookmarkList *bookmarks)
166
 
{
167
 
    g_list_foreach (bookmarks->list, stop_monitoring_one, bookmarks);
168
 
    //TODO check this
169
 
    //g_list_foreach (bookmarks->list, (GFunc) g_object_unref, NULL);
170
 
    g_list_free (bookmarks->list);
171
 
    bookmarks->list = NULL;
172
 
}
173
 
 
174
 
static void
175
 
do_finalize (GObject *object)
176
 
{
177
 
    if (MARLIN_BOOKMARK_LIST (object)->monitor != NULL) {
178
 
        g_file_monitor_cancel (MARLIN_BOOKMARK_LIST (object)->monitor);
179
 
        MARLIN_BOOKMARK_LIST (object)->monitor = NULL;
180
 
    }
181
 
 
182
 
    g_queue_free (MARLIN_BOOKMARK_LIST (object)->pending_ops);
183
 
 
184
 
    clear (MARLIN_BOOKMARK_LIST (object));
185
 
 
186
 
    G_OBJECT_CLASS (marlin_bookmark_list_parent_class)->finalize (object);
187
 
}
188
 
 
189
 
static GObject *
190
 
do_constructor (GType type,
191
 
                guint n_construct_params,
192
 
                GObjectConstructParam *construct_params)
193
 
{
194
 
    GObject *retval;
195
 
 
196
 
    if (singleton != NULL) {
197
 
        return g_object_ref (singleton);
198
 
    }
199
 
 
200
 
    retval = G_OBJECT_CLASS (marlin_bookmark_list_parent_class)->constructor
201
 
        (type, n_construct_params, construct_params);
202
 
 
203
 
    singleton = MARLIN_BOOKMARK_LIST (retval);
204
 
    g_object_add_weak_pointer (retval, (gpointer) &singleton);
205
 
 
206
 
    return retval;
207
 
}
208
 
 
209
 
 
210
 
static void
211
 
marlin_bookmark_list_class_init (MarlinBookmarkListClass *class)
212
 
{
213
 
    GObjectClass *object_class = G_OBJECT_CLASS (class);
214
 
 
215
 
    object_class->finalize = do_finalize;
216
 
    object_class->constructor = do_constructor;
217
 
 
218
 
    signals[CONTENTS_CHANGED] =
219
 
        g_signal_new ("contents_changed",
220
 
                      G_TYPE_FROM_CLASS (object_class),
221
 
                      G_SIGNAL_RUN_LAST,
222
 
                      G_STRUCT_OFFSET (MarlinBookmarkListClass,
223
 
                                       contents_changed),
224
 
                      NULL, NULL,
225
 
                      g_cclosure_marshal_VOID__VOID,
226
 
                      G_TYPE_NONE, 0);
227
 
}
228
 
 
229
 
static void
230
 
bookmark_monitor_changed_cb (GFileMonitor      *monitor,
231
 
                             GFile             *child,
232
 
                             GFile             *other_file,
233
 
                             GFileMonitorEvent  eflags,
234
 
                             gpointer           user_data)
235
 
{
236
 
    if (eflags == G_FILE_MONITOR_EVENT_CHANGED ||
237
 
        eflags == G_FILE_MONITOR_EVENT_CREATED) {
238
 
        g_return_if_fail (MARLIN_IS_BOOKMARK_LIST (MARLIN_BOOKMARK_LIST (user_data)));
239
 
        marlin_bookmark_list_load_file (MARLIN_BOOKMARK_LIST (user_data));
240
 
    }
241
 
}
242
 
 
243
 
static void
244
 
marlin_bookmark_list_init (MarlinBookmarkList *bookmarks)
245
 
{
246
 
    GFile *file;
247
 
 
248
 
    bookmarks->pending_ops = g_queue_new ();
249
 
 
250
 
    marlin_bookmark_list_load_file (bookmarks);
251
 
 
252
 
    file = marlin_bookmark_list_get_file ();
253
 
    bookmarks->monitor = g_file_monitor_file (file, 0, NULL, NULL);
254
 
    g_file_monitor_set_rate_limit (bookmarks->monitor, 1000);
255
 
 
256
 
    g_signal_connect (bookmarks->monitor, "changed",
257
 
                      G_CALLBACK (bookmark_monitor_changed_cb), bookmarks);
258
 
    g_object_unref (file);
259
 
}
260
 
 
261
 
static void
262
 
insert_bookmark_internal (MarlinBookmarkList *bookmarks,
263
 
                          MarlinBookmark     *bookmark,
264
 
                          int                   index)
265
 
{
266
 
    bookmarks->list = g_list_insert (bookmarks->list, bookmark, index);
267
 
 
268
 
    g_signal_connect_object (bookmark, "contents_changed",
269
 
                             G_CALLBACK (bookmark_in_list_changed_callback),
270
 
                             bookmarks, 0);
271
 
                             
272
 
    g_signal_connect_object (bookmark, "deleted",
273
 
                             G_CALLBACK (bookmark_in_list_to_be_deleted_callback),
274
 
                             bookmarks, 0);
275
 
}
276
 
 
277
 
/**
278
 
 * marlin_bookmark_list_append:
279
 
 *
280
 
 * Append a bookmark to a bookmark list.
281
 
 * @bookmarks: MarlinBookmarkList to append to.
282
 
 * @bookmark: Bookmark to append a copy of.
283
 
**/
284
 
void
285
 
marlin_bookmark_list_append (MarlinBookmarkList *bookmarks,
286
 
                             MarlinBookmark     *bookmark)
287
 
{
288
 
    g_return_if_fail (MARLIN_IS_BOOKMARK_LIST (bookmarks));
289
 
    g_return_if_fail (MARLIN_IS_BOOKMARK (bookmark));
290
 
 
291
 
    insert_bookmark_internal (bookmarks,
292
 
                              marlin_bookmark_copy (bookmark),
293
 
                              -1);
294
 
 
295
 
    marlin_bookmark_list_save_file (bookmarks);
296
 
}
297
 
 
298
 
/**
299
 
 * marlin_bookmark_list_contains:
300
 
 *
301
 
 * Check whether a bookmark with matching name and url is already in the list.
302
 
 * @bookmarks: MarlinBookmarkList to check contents of.
303
 
 * @bookmark: MarlinBookmark to match against.
304
 
 *
305
 
 * Return value: TRUE if matching bookmark is in list, FALSE otherwise
306
 
**/
307
 
gboolean
308
 
marlin_bookmark_list_contains (MarlinBookmarkList *bookmarks,
309
 
                               MarlinBookmark     *bookmark)
310
 
{
311
 
    g_return_val_if_fail (MARLIN_IS_BOOKMARK_LIST (bookmarks), FALSE);
312
 
    g_return_val_if_fail (MARLIN_IS_BOOKMARK (bookmark), FALSE);
313
 
 
314
 
    return g_list_find_custom (bookmarks->list,
315
 
                               (gpointer)bookmark,
316
 
                               marlin_bookmark_compare_with)
317
 
        != NULL;
318
 
}
319
 
 
320
 
/**
321
 
 * marlin_bookmark_list_delete_item_at:
322
 
 *
323
 
 * Delete the bookmark at the specified position.
324
 
 * @bookmarks: the list of bookmarks.
325
 
 * @index: index, must be less than length of list.
326
 
**/
327
 
void
328
 
marlin_bookmark_list_delete_item_at (MarlinBookmarkList *bookmarks,
329
 
                                     guint                 index)
330
 
{
331
 
    GList *doomed;
332
 
 
333
 
    g_return_if_fail (MARLIN_IS_BOOKMARK_LIST (bookmarks));
334
 
    g_return_if_fail (index < g_list_length (bookmarks->list));
335
 
 
336
 
    doomed = g_list_nth (bookmarks->list, index);
337
 
    bookmarks->list = g_list_remove_link (bookmarks->list, doomed);
338
 
 
339
 
    g_assert (MARLIN_IS_BOOKMARK (doomed->data));
340
 
    stop_monitoring_bookmark (bookmarks, MARLIN_BOOKMARK (doomed->data));
341
 
    //SPOTTED!
342
 
    g_object_unref (doomed->data);
343
 
    /*g_object_unref (doomed->data);
344
 
    g_object_unref (doomed->data);*/
345
 
 
346
 
    g_list_free_1 (doomed);
347
 
 
348
 
    marlin_bookmark_list_save_file (bookmarks);
349
 
}
350
 
 
351
 
/**
352
 
 * marlin_bookmark_list_move_item:
353
 
 *
354
 
 * Move the item from the given position to the destination.
355
 
 * @index: the index of the first bookmark.
356
 
 * @destination: the index of the second bookmark.
357
 
**/
358
 
void
359
 
marlin_bookmark_list_move_item (MarlinBookmarkList *bookmarks,
360
 
                                guint index,
361
 
                                guint destination)
362
 
{
363
 
    GList *bookmark_item;
364
 
 
365
 
    if (index == destination) {
366
 
        return;
367
 
    }
368
 
 
369
 
    bookmark_item = g_list_nth (bookmarks->list, index);
370
 
    bookmarks->list = g_list_remove_link (bookmarks->list,
371
 
                                          bookmark_item);
372
 
 
373
 
    if (index < destination) {
374
 
        bookmarks->list = g_list_insert (bookmarks->list,
375
 
                                         bookmark_item->data,
376
 
                                         destination - 1);
377
 
    } else {
378
 
        bookmarks->list = g_list_insert (bookmarks->list,
379
 
                                         bookmark_item->data,
380
 
                                         destination);
381
 
    }
382
 
 
383
 
    marlin_bookmark_list_save_file (bookmarks);
384
 
}
385
 
 
386
 
/**
387
 
 * marlin_bookmark_list_delete_items_with_uri:
388
 
 *
389
 
 * Delete all bookmarks with the given uri.
390
 
 * @bookmarks: the list of bookmarks.
391
 
 * @uri: The uri to match.
392
 
**/
393
 
void
394
 
marlin_bookmark_list_delete_items_with_uri (MarlinBookmarkList *bookmarks,
395
 
                                            const char           *uri)
396
 
{
397
 
    GList *node, *next;
398
 
    gboolean list_changed;
399
 
    char *bookmark_uri;
400
 
 
401
 
    g_return_if_fail (MARLIN_IS_BOOKMARK_LIST (bookmarks));
402
 
    g_return_if_fail (uri != NULL);
403
 
 
404
 
    list_changed = FALSE;
405
 
    for (node = bookmarks->list; node != NULL;  node = next) {
406
 
        next = node->next;
407
 
 
408
 
        bookmark_uri = marlin_bookmark_get_uri (MARLIN_BOOKMARK (node->data));
409
 
        if (eel_strcmp (bookmark_uri, uri) == 0) {
410
 
            bookmarks->list = g_list_remove_link (bookmarks->list, node);
411
 
            stop_monitoring_bookmark (bookmarks, MARLIN_BOOKMARK (node->data));
412
 
            g_object_unref (node->data);
413
 
            g_list_free_1 (node);
414
 
            list_changed = TRUE;
415
 
        }
416
 
        g_free (bookmark_uri);
417
 
    }
418
 
 
419
 
    if (list_changed) {
420
 
        marlin_bookmark_list_save_file (bookmarks);
421
 
    }
422
 
}
423
 
 
424
 
/**
425
 
 * marlin_bookmark_list_insert_item:
426
 
 *
427
 
 * Insert a bookmark at a specified position.
428
 
 * @bookmarks: the list of bookmarks.
429
 
 * @index: the position to insert the bookmark at.
430
 
 * @new_bookmark: the bookmark to insert a copy of.
431
 
**/
432
 
void
433
 
marlin_bookmark_list_insert_item (MarlinBookmarkList *bookmarks,
434
 
                                  MarlinBookmark     *new_bookmark,
435
 
                                  guint                 index)
436
 
{
437
 
    g_return_if_fail (MARLIN_IS_BOOKMARK_LIST (bookmarks));
438
 
    g_return_if_fail (index <= g_list_length (bookmarks->list));
439
 
 
440
 
    insert_bookmark_internal (bookmarks,
441
 
                              marlin_bookmark_copy (new_bookmark),
442
 
                              index);
443
 
 
444
 
    marlin_bookmark_list_save_file (bookmarks);
445
 
}
446
 
 
447
 
/**
448
 
 * marlin_bookmark_list_item_at:
449
 
 *
450
 
 * Get the bookmark at the specified position.
451
 
 * @bookmarks: the list of bookmarks.
452
 
 * @index: index, must be less than length of list.
453
 
 *
454
 
 * Return value: the bookmark at position @index in @bookmarks.
455
 
**/
456
 
MarlinBookmark *
457
 
marlin_bookmark_list_item_at (MarlinBookmarkList *bookmarks, guint index)
458
 
{
459
 
    g_return_val_if_fail (MARLIN_IS_BOOKMARK_LIST (bookmarks), NULL);
460
 
    g_return_val_if_fail (index < g_list_length (bookmarks->list), NULL);
461
 
 
462
 
    return MARLIN_BOOKMARK (g_list_nth_data (bookmarks->list, index));
463
 
}
464
 
 
465
 
/**
466
 
 * marlin_bookmark_list_length:
467
 
 *
468
 
 * Get the number of bookmarks in the list.
469
 
 * @bookmarks: the list of bookmarks.
470
 
 *
471
 
 * Return value: the length of the bookmark list.
472
 
**/
473
 
guint
474
 
marlin_bookmark_list_length (MarlinBookmarkList *bookmarks)
475
 
{
476
 
    g_return_val_if_fail (MARLIN_IS_BOOKMARK_LIST(bookmarks), 0);
477
 
 
478
 
    return g_list_length (bookmarks->list);
479
 
}
480
 
 
481
 
static GList *
482
 
marlin_bookmark_list_get_gof_files (MarlinBookmarkList *bookmarks)
483
 
{
484
 
    GList *files = NULL;
485
 
    GList *p;
486
 
 
487
 
    for (p = bookmarks->list; p!= NULL; p=p->next) {
488
 
        GOFFile *gof = MARLIN_BOOKMARK (p->data)->file;
489
 
        //g_message ("%s %s", G_STRFUNC, gof->uri);
490
 
        files = g_list_prepend (files, gof);
491
 
    }
492
 
 
493
 
    return files;
494
 
}
495
 
 
496
 
static void bookmark_list_content_changed (GList *files, MarlinBookmarkList *bookmarks)
497
 
{
498
 
    g_signal_emit (bookmarks, signals[CONTENTS_CHANGED], 0);
499
 
}
500
 
 
501
 
static void
502
 
load_file_finish (MarlinBookmarkList *bookmarks,
503
 
                  GObject *source,
504
 
                  GAsyncResult *res)
505
 
{
506
 
    GError *error = NULL;
507
 
    gchar *contents = NULL;
508
 
 
509
 
    g_file_load_contents_finish (G_FILE (source),
510
 
                                 res, &contents, NULL, NULL, &error);
511
 
 
512
 
    if (error == NULL) {
513
 
        char **lines;
514
 
        int i;
515
 
 
516
 
        lines = g_strsplit (contents, "\n", -1);
517
 
        for (i = 0; lines[i]; i++) {
518
 
            /* Ignore empty or invalid lines that cannot be parsed properly */
519
 
            if (lines[i][0] != '\0' && lines[i][0] != ' ') {
520
 
                /* gtk 2.7/2.8 might have labels appended to bookmarks which are separated by a space */
521
 
                /* we must seperate the bookmark uri and the potential label */
522
 
                char *space, *label;
523
 
 
524
 
                label = NULL;
525
 
                space = strchr (lines[i], ' ');
526
 
                if (space) {
527
 
                    *space = '\0';
528
 
                    label = g_strdup (space + 1);
529
 
                }
530
 
                insert_bookmark_internal (bookmarks,
531
 
                                          new_bookmark_from_uri (lines[i], label),
532
 
                                          -1);
533
 
 
534
 
                g_free (label);
535
 
            }
536
 
        }
537
 
        g_free (contents);
538
 
        g_strfreev (lines);
539
 
 
540
 
        //SPOTTED!
541
 
        /* emit contents_changed once all bookmark are ready */
542
 
        GList *files = marlin_bookmark_list_get_gof_files (bookmarks);
543
 
        gof_call_when_ready_new (files, bookmark_list_content_changed, bookmarks);
544
 
        g_list_free (files);
545
 
        g_signal_emit (bookmarks, signals[CONTENTS_CHANGED], 0);
546
 
    } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
547
 
        g_warning ("Could not load bookmark file: %s\n", error->message);
548
 
        g_error_free (error);
549
 
    }
550
 
}
551
 
 
552
 
static void
553
 
load_file_async (MarlinBookmarkList *self,
554
 
                 GAsyncReadyCallback callback)
555
 
{
556
 
    GFile *file;
557
 
 
558
 
    file = marlin_bookmark_list_get_file ();
559
 
 
560
 
    /* Wipe out old list. */
561
 
    clear (self);
562
 
 
563
 
    /* keep the bookmark list alive */
564
 
    g_object_ref (self);
565
 
    g_file_load_contents_async (file, NULL, callback, self);
566
 
 
567
 
    g_object_unref (file);
568
 
}
569
 
 
570
 
static void
571
 
save_file_finish (MarlinBookmarkList *bookmarks,
572
 
                  GObject *source,
573
 
                  GAsyncResult *res)
574
 
{
575
 
    GError *error = NULL;
576
 
    GFile *file;
577
 
 
578
 
    g_file_replace_contents_finish (G_FILE (source),
579
 
                                    res, NULL, &error);
580
 
 
581
 
    if (error != NULL) {
582
 
        g_warning ("Unable to replace contents of the bookmarks file: %s",
583
 
                   error->message);
584
 
        g_error_free (error);
585
 
    }
586
 
 
587
 
    file = marlin_bookmark_list_get_file ();
588
 
 
589
 
    /* re-enable bookmark file monitoring */
590
 
    bookmarks->monitor = g_file_monitor_file (file, 0, NULL, NULL);
591
 
    g_file_monitor_set_rate_limit (bookmarks->monitor, 1000);
592
 
    g_signal_connect (bookmarks->monitor, "changed",
593
 
                      G_CALLBACK (bookmark_monitor_changed_cb), bookmarks);
594
 
 
595
 
    g_object_unref (file);
596
 
}
597
 
 
598
 
static void
599
 
save_file_async (MarlinBookmarkList *bookmarks,
600
 
                 GAsyncReadyCallback callback)
601
 
{
602
 
    GFile *file;
603
 
    GList *l;
604
 
    GString *bookmark_string;
605
 
 
606
 
    /* temporarily disable bookmark file monitoring when writing file */
607
 
    if (bookmarks->monitor != NULL) {
608
 
        g_file_monitor_cancel (bookmarks->monitor);
609
 
        bookmarks->monitor = NULL;
610
 
    }
611
 
 
612
 
    file = marlin_bookmark_list_get_file ();
613
 
    bookmark_string = g_string_new (NULL);
614
 
 
615
 
    for (l = bookmarks->list; l; l = l->next) {
616
 
        MarlinBookmark *bookmark;
617
 
 
618
 
        bookmark = MARLIN_BOOKMARK (l->data);
619
 
 
620
 
        /* make sure we save label if it has one for compatibility with GTK 2.7 and 2.8 */
621
 
        if (marlin_bookmark_get_has_custom_name (bookmark)) {
622
 
            char *label, *uri;
623
 
            label = marlin_bookmark_get_name (bookmark);
624
 
            uri = marlin_bookmark_get_uri (bookmark);
625
 
            g_string_append_printf (bookmark_string,
626
 
                                    "%s %s\n", uri, label);
627
 
            g_free (uri);
628
 
            g_free (label);
629
 
        } else {
630
 
            char *uri;
631
 
            uri = marlin_bookmark_get_uri (bookmark);
632
 
            g_string_append_printf (bookmark_string, "%s\n", uri);
633
 
            g_free (uri);
634
 
        }
635
 
    }
636
 
 
637
 
    /* keep the bookmark list alive */
638
 
    g_object_ref (bookmarks);
639
 
    g_file_replace_contents_async (file, bookmark_string->str,
640
 
                                   bookmark_string->len, NULL,
641
 
                                   FALSE, 0, NULL, callback,
642
 
                                   bookmarks);
643
 
 
644
 
    g_object_unref (file);
645
 
}
646
 
 
647
 
static void
648
 
process_next_op (MarlinBookmarkList *bookmarks);
649
 
 
650
 
static void
651
 
op_processed_cb (GObject *source,
652
 
                 GAsyncResult *res,
653
 
                 gpointer user_data)
654
 
{
655
 
    MarlinBookmarkList *self = user_data;
656
 
    int op;
657
 
 
658
 
    op = GPOINTER_TO_INT (g_queue_pop_tail (self->pending_ops));
659
 
 
660
 
    if (op == LOAD_JOB) {
661
 
        load_file_finish (self, source, res);
662
 
    } else {
663
 
        save_file_finish (self, source, res);
664
 
    }
665
 
 
666
 
    if (!g_queue_is_empty (self->pending_ops)) {
667
 
        process_next_op (self);
668
 
    }
669
 
 
670
 
    /* release the reference acquired during the _async method */
671
 
    g_object_unref (self);
672
 
}
673
 
 
674
 
static void
675
 
process_next_op (MarlinBookmarkList *bookmarks)
676
 
{
677
 
    gint op;
678
 
 
679
 
    op = GPOINTER_TO_INT (g_queue_peek_tail (bookmarks->pending_ops));
680
 
 
681
 
    if (op == LOAD_JOB) {
682
 
        load_file_async (bookmarks, op_processed_cb);
683
 
    } else {
684
 
        save_file_async (bookmarks, op_processed_cb);
685
 
    }
686
 
}
687
 
 
688
 
/**
689
 
 * marlin_bookmark_list_load_file:
690
 
 *
691
 
 * Reads bookmarks from file, clobbering contents in memory.
692
 
 * @bookmarks: the list of bookmarks to fill with file contents.
693
 
**/
694
 
static void
695
 
marlin_bookmark_list_load_file (MarlinBookmarkList *bookmarks)
696
 
{
697
 
    g_queue_push_head (bookmarks->pending_ops, GINT_TO_POINTER (LOAD_JOB));
698
 
 
699
 
    if (g_queue_get_length (bookmarks->pending_ops) == 1) {
700
 
        process_next_op (bookmarks);
701
 
    }
702
 
}
703
 
 
704
 
/**
705
 
 * marlin_bookmark_list_save_file:
706
 
 *
707
 
 * Save bookmarks to disk.
708
 
 * @bookmarks: the list of bookmarks to save.
709
 
**/
710
 
static void
711
 
marlin_bookmark_list_save_file (MarlinBookmarkList *bookmarks)
712
 
{
713
 
    g_signal_emit (bookmarks, signals[CONTENTS_CHANGED], 0);
714
 
 
715
 
    g_queue_push_head (bookmarks->pending_ops, GINT_TO_POINTER (SAVE_JOB));
716
 
 
717
 
    if (g_queue_get_length (bookmarks->pending_ops) == 1) {
718
 
        process_next_op (bookmarks);
719
 
    }
720
 
}
721
 
 
722
 
 
723
 
/**
724
 
 * marlin_bookmark_list_new:
725
 
 *
726
 
 * Create a new bookmark_list, with contents read from disk.
727
 
 *
728
 
 * Return value: A pointer to the new widget.
729
 
**/
730
 
MarlinBookmarkList *
731
 
marlin_bookmark_list_new (void)
732
 
{
733
 
    MarlinBookmarkList *list;
734
 
 
735
 
    list = MARLIN_BOOKMARK_LIST (g_object_new (MARLIN_TYPE_BOOKMARK_LIST, NULL));
736
 
 
737
 
    return list;
738
 
}