1 /*
2 * This file is part of the Main Menu.
3 *
4 * Copyright (c) 2007 Novell, Inc.
5 *
6 * The Main Menu is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
10 *
11 * The Main Menu is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * the Main Menu; if not, write to the Free Software Foundation, Inc., 51
18 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "bookmark-agent.h"
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #else
26 # define PACKAGE "mate-main-menu"
27 #endif
28
29 #include <gtk/gtk.h>
30
31 #include <string.h>
32 #include <stdlib.h>
33 #include <glib/gi18n-lib.h>
34 #include <glib/gstdio.h>
35 #include <gio/gio.h>
36
37 #include "libslab-utils.h"
38
39 #define USER_APPS_STORE_FILE_NAME "applications.xbel"
40 #define USER_DOCS_STORE_FILE_NAME "documents.xbel"
41 #define USER_DIRS_STORE_FILE_NAME "places.xbel"
42 #define SYSTEM_STORE_FILE_NAME "system-items.xbel"
43 #define CALC_TEMPLATE_FILE_NAME "empty.ots"
44 #define WRITER_TEMPLATE_FILE_NAME "empty.ott"
45
46 #define GTK_BOOKMARKS_FILE "bookmarks"
47
48 #define TYPE_IS_RECENT(type) ((type) == BOOKMARK_STORE_RECENT_APPS || (type) == BOOKMARK_STORE_RECENT_DOCS)
49
50 typedef struct {
51 BookmarkStoreType type;
52
53 BookmarkItem **items;
54 gint n_items;
55 BookmarkStoreStatus status;
56
57 GBookmarkFile *store;
58 gboolean needs_sync;
59
60 gchar *store_path;
61 gchar *user_store_path;
62 gboolean user_modifiable;
63 gboolean reorderable;
64 const gchar *store_filename;
65
66 GFileMonitor *store_monitor;
67 GFileMonitor *user_store_monitor;
68
69 void (* update_path) (BookmarkAgent *);
70 void (* load_store) (BookmarkAgent *);
71 void (* save_store) (BookmarkAgent *);
72 void (* create_item) (BookmarkAgent *, const gchar *);
73
74 gchar *gtk_store_path;
75 GFileMonitor *gtk_store_monitor;
76 } BookmarkAgentPrivate;
77
78 enum {
79 PROP_0,
80 PROP_ITEMS,
81 PROP_STATUS
82 };
83
84 static BookmarkAgent *instances [BOOKMARK_STORE_N_TYPES];
85
86 static BookmarkAgentClass *bookmark_agent_parent_class = NULL;
87
88 static void bookmark_agent_base_init (BookmarkAgentClass *);
89 static void bookmark_agent_class_init (BookmarkAgentClass *);
90 static void bookmark_agent_init (BookmarkAgent *);
91 static BookmarkAgent *bookmark_agent_new (BookmarkStoreType );
92
93 static void get_property (GObject *, guint, GValue *, GParamSpec *);
94 static void set_property (GObject *, guint, const GValue *, GParamSpec *);
95 static void finalize (GObject *);
96
97 static void update_agent (BookmarkAgent *);
98 static void update_items (BookmarkAgent *);
99 static void save_store (BookmarkAgent *);
100 static gint get_rank (BookmarkAgent *, const gchar *);
101 static void set_rank (BookmarkAgent *, const gchar *, gint);
102
103 static void load_xbel_store (BookmarkAgent *);
104 static void load_places_store (BookmarkAgent *);
105 static void update_user_spec_path (BookmarkAgent *);
106 static void save_xbel_store (BookmarkAgent *);
107 static void create_app_item (BookmarkAgent *, const gchar *);
108 static void create_doc_item (BookmarkAgent *, const gchar *);
109 static void create_dir_item (BookmarkAgent *, const gchar *);
110
111 static void store_monitor_cb (GFileMonitor *, GFile *, GFile *,
112 GFileMonitorEvent, gpointer);
113 static void weak_destroy_cb (gpointer, GObject *);
114
115 static gchar *find_package_data_file (const gchar *filename);
116
117 static gint BookmarkAgent_private_offset;
118
bookmark_agent_get_instance_private(BookmarkAgent * this)119 static inline gpointer bookmark_agent_get_instance_private (BookmarkAgent *this)
120 {
121 return (G_STRUCT_MEMBER_P (this, BookmarkAgent_private_offset));
122 }
123
124 GType
bookmark_agent_get_type()125 bookmark_agent_get_type ()
126 {
127 static GType g_define_type_id = 0;
128
129 if (G_UNLIKELY (g_define_type_id == 0)) {
130 static const GTypeInfo info = {
131 sizeof (BookmarkAgentClass),
132 (GBaseInitFunc) bookmark_agent_base_init,
133 NULL,
134 (GClassInitFunc) bookmark_agent_class_init,
135 NULL, NULL,
136 sizeof (BookmarkAgent), 0,
137 (GInstanceInitFunc) bookmark_agent_init,
138 NULL
139 };
140
141 g_define_type_id = g_type_register_static (
142 G_TYPE_OBJECT, "BookmarkAgent", & info, 0);
143 G_ADD_PRIVATE (BookmarkAgent);
144 }
145
146 return g_define_type_id;
147 }
148
149 gboolean
bookmark_agent_has_item(BookmarkAgent * this,const gchar * uri)150 bookmark_agent_has_item (BookmarkAgent *this, const gchar *uri)
151 {
152 BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
153 return g_bookmark_file_has_item (priv->store, uri);
154 }
155
156 void
bookmark_agent_add_item(BookmarkAgent * this,const BookmarkItem * item)157 bookmark_agent_add_item (BookmarkAgent *this, const BookmarkItem *item)
158 {
159 BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
160
161 if (! item)
162 return;
163
164 g_return_if_fail (priv->user_modifiable);
165 g_return_if_fail (item->uri);
166 g_return_if_fail (item->mime_type);
167
168 g_bookmark_file_set_mime_type (priv->store, item->uri, item->mime_type);
169
170 if (item->mtime)
171 g_bookmark_file_set_modified_date_time (priv->store, item->uri, item->mtime);
172
173 if (item->title)
174 g_bookmark_file_set_title (priv->store, item->uri, item->title);
175
176 g_bookmark_file_add_application (priv->store, item->uri, item->app_name, item->app_exec);
177
178 set_rank (this, item->uri, g_bookmark_file_get_size (priv->store) - 1);
179
180 save_store (this);
181 }
182
183 void
bookmark_agent_move_item(BookmarkAgent * this,const gchar * uri,const gchar * uri_new)184 bookmark_agent_move_item (BookmarkAgent *this, const gchar *uri, const gchar *uri_new)
185 {
186 BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
187
188 GError *error = NULL;
189
190 if (! TYPE_IS_RECENT (priv->type))
191 return;
192
193 gtk_recent_manager_move_item (gtk_recent_manager_get_default (), uri, uri_new, &error);
194 if (error) {
195 g_warning ("Unable to update %s with renamed file, [%s] -> [%s]: %s",
196 priv->store_path, uri, uri_new, error->message);
197 g_error_free (error);
198 }
199 }
200
201 void
bookmark_agent_purge_items(BookmarkAgent * this)202 bookmark_agent_purge_items (BookmarkAgent *this)
203 {
204 BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
205
206 GError *error = NULL;
207
208 gchar **uris = NULL;
209 gsize uris_len;
210 gint i;
211 g_return_if_fail (priv->user_modifiable);
212
213 uris = g_bookmark_file_get_uris (priv->store, &uris_len);
214 if (TYPE_IS_RECENT (priv->type)) {
215 for (i = 0; i < uris_len; i++) {
216 gtk_recent_manager_remove_item (gtk_recent_manager_get_default (), uris [i], &error);
217 if (error) {
218 g_warning ("Unable to remove [%s] from %s: %s",
219 priv->store_path, uris [i], error->message);
220 g_error_free (error);
221 }
222 }
223 } else {
224 for (i = 0; i < uris_len; i++) {
225 g_bookmark_file_remove_item (priv->store, uris [i], NULL);
226 }
227 save_store (this);
228 }
229 g_strfreev (uris);
230 }
231
232 void
bookmark_agent_remove_item(BookmarkAgent * this,const gchar * uri)233 bookmark_agent_remove_item (BookmarkAgent *this, const gchar *uri)
234 {
235 BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
236 gint rank;
237
238 GError *error = NULL;
239
240 gchar **uris = NULL;
241 gint rank_i;
242 gint i;
243
244
245 g_return_if_fail (priv->user_modifiable);
246
247 if (! bookmark_agent_has_item (this, uri))
248 return;
249
250 if (TYPE_IS_RECENT (priv->type)) {
251 gtk_recent_manager_remove_item (gtk_recent_manager_get_default (), uri, &error);
252 if (error) {
253 g_warning ("Unable to remove [%s] from %s: %s", priv->store_path, uri, error->message);
254 g_error_free (error);
255 }
256 }
257 else {
258 rank = get_rank (this, uri);
259
260 g_bookmark_file_remove_item (priv->store, uri, NULL);
261
262 if (rank >= 0) {
263 uris = g_bookmark_file_get_uris (priv->store, NULL);
264
265 for (i = 0; uris && uris [i]; ++i) {
266 rank_i = get_rank (this, uris [i]);
267
268 if (rank_i > rank)
269 set_rank (this, uris [i], rank_i - 1);
270 }
271
272 g_strfreev (uris);
273 }
274
275 save_store (this);
276 }
277 }
278
279 void
bookmark_agent_reorder_items(BookmarkAgent * this,const gchar ** uris)280 bookmark_agent_reorder_items (BookmarkAgent *this, const gchar **uris)
281 {
282 BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
283
284 gint i;
285
286
287 g_return_if_fail (priv->reorderable);
288
289 for (i = 0; uris && uris [i]; ++i)
290 set_rank (this, uris [i], i);
291
292 save_store (this);
293 }
294
295 static GList *
make_items_from_bookmark_file(BookmarkAgent * this,GBookmarkFile * store)296 make_items_from_bookmark_file (BookmarkAgent *this, GBookmarkFile *store)
297 {
298 BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
299 gchar **uris;
300 gint i;
301 GList *items_ordered;
302
303 if (!store)
304 return NULL;
305
306 uris = g_bookmark_file_get_uris (store, NULL);
307 items_ordered = NULL;
308
309 for (i = 0; uris && uris [i]; ++i) {
310 gboolean include;
311
312 if (priv->type == BOOKMARK_STORE_RECENT_APPS)
313 include = g_bookmark_file_has_group (store, uris [i], "recently-used-apps", NULL);
314 else
315 include = ! g_bookmark_file_get_is_private (store, uris [i], NULL);
316
317 if (include) {
318 BookmarkItem *item;
319
320 item = g_new0 (BookmarkItem, 1);
321
322 item->uri = g_strdup (uris [i]);
323 item->mime_type = g_bookmark_file_get_mime_type (store, uris [i], NULL);
324 item->mtime = g_bookmark_file_get_modified_date_time (store, uris [i], NULL);
325
326 items_ordered = g_list_prepend (items_ordered, item);
327 }
328 }
329
330 items_ordered = g_list_sort (items_ordered, g_date_time_compare);
331
332 g_strfreev (uris);
333
334 return items_ordered;
335 }
336
337 void
bookmark_agent_update_from_bookmark_file(BookmarkAgent * this,GBookmarkFile * store)338 bookmark_agent_update_from_bookmark_file (BookmarkAgent *this, GBookmarkFile *store)
339 {
340 BookmarkAgentPrivate *priv;
341 GList *items_ordered;
342 GList *node;
343
344 g_return_if_fail (IS_BOOKMARK_AGENT (this));
345
346 priv = bookmark_agent_get_instance_private (this);
347
348 items_ordered = make_items_from_bookmark_file (this, store);
349
350 g_bookmark_file_free (priv->store);
351 priv->store = g_bookmark_file_new ();
352
353 for (node = items_ordered; node; node = node->next) {
354 BookmarkItem *item;
355
356 item = (BookmarkItem *) node->data;
357
358 g_bookmark_file_set_mime_type (priv->store, item->uri, item->mime_type);
359 g_bookmark_file_set_modified_date_time (priv->store, item->uri, item->mtime);
360
361 bookmark_item_free (item);
362 }
363
364 g_list_free (items_ordered);
365
366 update_items (this);
367 }
368
369 void
bookmark_item_free(BookmarkItem * item)370 bookmark_item_free (BookmarkItem *item)
371 {
372 if (! item)
373 return;
374
375 g_free (item->uri);
376 g_free (item->title);
377 g_free (item->mime_type);
378 g_free (item->icon);
379 g_free (item->app_name);
380 g_free (item->app_exec);
381 g_free (item);
382 }
383
384 static void
bookmark_agent_base_init(BookmarkAgentClass * this_class)385 bookmark_agent_base_init (BookmarkAgentClass *this_class)
386 {
387 gint i;
388
389 for (i = 0; i < BOOKMARK_STORE_N_TYPES; ++i)
390 instances [i] = NULL;
391 }
392
393 static void
bookmark_agent_class_init(BookmarkAgentClass * this_class)394 bookmark_agent_class_init (BookmarkAgentClass *this_class)
395 {
396 GObjectClass *g_obj_class = G_OBJECT_CLASS (this_class);
397
398 GParamSpec *items_pspec;
399 GParamSpec *status_pspec;
400
401 if (BookmarkAgent_private_offset != 0)
402 g_type_class_adjust_private_offset (this_class, &BookmarkAgent_private_offset);
403
404 g_obj_class->get_property = get_property;
405 g_obj_class->set_property = set_property;
406 g_obj_class->finalize = finalize;
407
408 items_pspec = g_param_spec_pointer (
409 BOOKMARK_AGENT_ITEMS_PROP, BOOKMARK_AGENT_ITEMS_PROP,
410 "the null-terminated list which contains the bookmark items in this store",
411 G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
412
413 status_pspec = g_param_spec_int (
414 BOOKMARK_AGENT_STORE_STATUS_PROP, BOOKMARK_AGENT_STORE_STATUS_PROP, "the status of the store",
415 BOOKMARK_STORE_DEFAULT_ONLY, BOOKMARK_STORE_USER, BOOKMARK_STORE_DEFAULT,
416 G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
417
418 g_object_class_install_property (g_obj_class, PROP_ITEMS, items_pspec);
419 g_object_class_install_property (g_obj_class, PROP_STATUS, status_pspec);
420
421 bookmark_agent_parent_class = g_type_class_peek_parent (this_class);
422 }
423
424 static void
bookmark_agent_init(BookmarkAgent * this)425 bookmark_agent_init (BookmarkAgent *this)
426 {
427 BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
428
429 priv->type = -1;
430
431 priv->items = NULL;
432 priv->n_items = 0;
433 priv->status = BOOKMARK_STORE_ABSENT;
434
435 priv->store = NULL;
436 priv->needs_sync = FALSE;
437
438 priv->store_path = NULL;
439 priv->user_store_path = NULL;
440 priv->user_modifiable = FALSE;
441 priv->reorderable = FALSE;
442 priv->store_filename = NULL;
443
444 priv->store_monitor = NULL;
445 priv->user_store_monitor = NULL;
446
447 priv->update_path = NULL;
448 priv->load_store = NULL;
449 priv->save_store = NULL;
450 priv->create_item = NULL;
451
452 priv->gtk_store_path = NULL;
453 priv->gtk_store_monitor = NULL;
454 }
455
456 static BookmarkAgent *
bookmark_agent_new(BookmarkStoreType type)457 bookmark_agent_new (BookmarkStoreType type)
458 {
459 BookmarkAgent *this;
460 BookmarkAgentPrivate *priv;
461 GFile *gtk_store_file;
462
463 this = g_object_new (BOOKMARK_AGENT_TYPE, NULL);
464 priv = bookmark_agent_get_instance_private (this);
465
466 priv->type = type;
467 priv->store = g_bookmark_file_new ();
468
469 switch (type) {
470 case BOOKMARK_STORE_USER_APPS:
471 priv->store_filename = USER_APPS_STORE_FILE_NAME;
472 priv->create_item = create_app_item;
473
474 break;
475
476 case BOOKMARK_STORE_USER_DOCS:
477 priv->store_filename = USER_DOCS_STORE_FILE_NAME;
478 priv->create_item = create_doc_item;
479
480 break;
481
482 case BOOKMARK_STORE_USER_DIRS:
483 priv->store_filename = USER_DIRS_STORE_FILE_NAME;
484 priv->create_item = create_dir_item;
485
486 priv->user_modifiable = TRUE;
487 priv->reorderable = FALSE;
488
489 priv->load_store = load_places_store;
490
491 priv->gtk_store_path = g_build_filename (g_get_user_config_dir (),
492 "gtk-3.0", GTK_BOOKMARKS_FILE, NULL);
493 gtk_store_file = g_file_new_for_path (priv->gtk_store_path);
494 priv->gtk_store_monitor = g_file_monitor_file (gtk_store_file,
495 0, NULL, NULL);
496 if (priv->gtk_store_monitor) {
497 g_signal_connect (priv->gtk_store_monitor, "changed",
498 G_CALLBACK (store_monitor_cb), this);
499 }
500
501 g_object_unref (gtk_store_file);
502
503 break;
504
505 case BOOKMARK_STORE_RECENT_APPS:
506 case BOOKMARK_STORE_RECENT_DOCS:
507 priv->user_modifiable = TRUE;
508 priv->reorderable = FALSE;
509
510 priv->store_path = g_build_filename (g_get_user_data_dir (), "recently-used.xbel", NULL);
511
512 break;
513
514 case BOOKMARK_STORE_SYSTEM:
515 priv->store_filename = SYSTEM_STORE_FILE_NAME;
516 priv->create_item = create_app_item;
517
518 break;
519
520 default:
521 break;
522 }
523
524 if (
525 type == BOOKMARK_STORE_USER_APPS || type == BOOKMARK_STORE_USER_DOCS ||
526 type == BOOKMARK_STORE_USER_DIRS || type == BOOKMARK_STORE_SYSTEM)
527 {
528 priv->user_modifiable = TRUE;
529
530 priv->user_store_path = g_build_filename (
531 g_get_user_data_dir (), PACKAGE, priv->store_filename, NULL);
532
533 priv->update_path = update_user_spec_path;
534 }
535
536 if (type == BOOKMARK_STORE_USER_APPS || type == BOOKMARK_STORE_USER_DOCS || type == BOOKMARK_STORE_SYSTEM) {
537 priv->reorderable = TRUE;
538 priv->load_store = load_xbel_store;
539 priv->save_store = save_xbel_store;
540 }
541
542 update_agent (this);
543
544 return this;
545 }
546
547 BookmarkAgent *
bookmark_agent_get_instance(BookmarkStoreType type)548 bookmark_agent_get_instance (BookmarkStoreType type)
549 {
550 g_return_val_if_fail (0 <= type, NULL);
551 g_return_val_if_fail (type < BOOKMARK_STORE_N_TYPES, NULL);
552
553 if (! instances [type]) {
554 instances [type] = bookmark_agent_new (type);
555 g_object_weak_ref (G_OBJECT (instances [type]), weak_destroy_cb, GINT_TO_POINTER (type));
556 }
557 else
558 g_object_ref (G_OBJECT (instances [type]));
559
560 return instances [type];
561 }
562
563 static void
get_property(GObject * g_obj,guint prop_id,GValue * value,GParamSpec * pspec)564 get_property (GObject *g_obj, guint prop_id, GValue *value, GParamSpec *pspec)
565 {
566 BookmarkAgent *this = BOOKMARK_AGENT (g_obj);
567 BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
568
569
570 switch (prop_id) {
571 case PROP_ITEMS:
572 g_value_set_pointer (value, priv->items);
573 break;
574
575 case PROP_STATUS:
576 g_value_set_int (value, priv->status);
577 break;
578 }
579 }
580
581 static void
set_property(GObject * g_obj,guint prop_id,const GValue * value,GParamSpec * pspec)582 set_property (GObject *g_obj, guint prop_id, const GValue *value, GParamSpec *pspec)
583 {
584 /* no writeable properties */
585 }
586
587 static void
finalize(GObject * g_obj)588 finalize (GObject *g_obj)
589 {
590 BookmarkAgent *this = BOOKMARK_AGENT (g_obj);
591 BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
592
593 gint i;
594
595
596 for (i = 0; priv->items && priv->items [i]; ++i)
597 bookmark_item_free (priv->items [i]);
598
599 g_free (priv->items);
600 g_free (priv->store_path);
601 g_free (priv->user_store_path);
602 g_free (priv->gtk_store_path);
603
604 if (priv->store_monitor) {
605 g_signal_handlers_disconnect_by_func (priv->store_monitor, store_monitor_cb, this);
606 g_file_monitor_cancel (priv->store_monitor);
607 g_object_unref (priv->store_monitor);
608 }
609
610 if (priv->user_store_monitor) {
611 g_signal_handlers_disconnect_by_func (priv->user_store_monitor, store_monitor_cb, this);
612 g_file_monitor_cancel (priv->user_store_monitor);
613 g_object_unref (priv->user_store_monitor);
614 }
615
616 if (priv->gtk_store_monitor) {
617 g_signal_handlers_disconnect_by_func (priv->gtk_store_monitor, store_monitor_cb, this);
618 g_file_monitor_cancel (priv->gtk_store_monitor);
619 g_object_unref (priv->gtk_store_monitor);
620 }
621
622 g_bookmark_file_free (priv->store);
623
624 G_OBJECT_CLASS (bookmark_agent_parent_class)->finalize (g_obj);
625 }
626
627 static void
update_agent(BookmarkAgent * this)628 update_agent (BookmarkAgent *this)
629 {
630 BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
631
632 if (priv->update_path)
633 priv->update_path (this);
634
635 if (priv->load_store)
636 priv->load_store (this);
637
638 update_items (this);
639 }
640
641 static void
update_items(BookmarkAgent * this)642 update_items (BookmarkAgent *this)
643 {
644 BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
645
646 gchar **uris = NULL;
647 gchar **uris_ordered = NULL;
648 gsize n_uris = 0;
649 gint rank = -1;
650 gint rank_corr = -1;
651 gboolean needs_update = FALSE;
652 gboolean store_corrupted = FALSE;
653 gchar *new_title, *old_title;
654
655 gint i;
656
657
658 uris = g_bookmark_file_get_uris (priv->store, & n_uris);
659 uris_ordered = g_new0 (gchar *, n_uris + 1);
660 uris_ordered [n_uris] = NULL;
661
662 for (i = 0; uris && uris [i]; ++i) {
663 rank = get_rank (this, uris [i]);
664
665 if (rank < 0 || rank >= n_uris)
666 rank = i;
667
668 if (uris_ordered [rank]) {
669 store_corrupted = TRUE;
670 rank_corr = rank;
671
672 for (rank = 0; rank < n_uris; ++rank)
673 if (! uris_ordered [rank])
674 break;
675
676 g_warning (
677 "store corruption [%s] - multiple uris with same rank (%d): [%s] [%s], moving latter to %d",
678 priv->store_path, rank_corr, uris_ordered [rank_corr], uris [i], rank);
679 }
680
681 set_rank (this, uris [i], rank);
682
683 uris_ordered [rank] = uris [i];
684 }
685
686 if (priv->n_items != n_uris)
687 needs_update = TRUE;
688
689 for (i = 0; ! needs_update && uris_ordered && uris_ordered [i]; ++i) {
690 if (priv->type == BOOKMARK_STORE_USER_DIRS) {
691 new_title = g_bookmark_file_get_title (priv->store, uris_ordered [i], NULL);
692 old_title = priv->items [i]->title;
693 if (!new_title && !old_title) {
694 if (strcmp (priv->items [i]->uri, uris_ordered [i]))
695 needs_update = TRUE;
696 }
697 else if ((new_title && !old_title) || (!new_title && old_title))
698 needs_update = TRUE;
699 else if (strcmp (old_title, new_title))
700 needs_update = TRUE;
701 g_free (new_title);
702 }
703 else if (strcmp (priv->items [i]->uri, uris_ordered [i]))
704 needs_update = TRUE;
705 }
706
707 if (needs_update) {
708 for (i = 0; priv->items && priv->items [i]; ++i)
709 bookmark_item_free (priv->items [i]);
710
711 g_free (priv->items);
712
713 priv->n_items = n_uris;
714 priv->items = g_new0 (BookmarkItem *, priv->n_items + 1);
715
716 for (i = 0; uris_ordered && uris_ordered [i]; ++i) {
717 priv->items [i] = g_new0 (BookmarkItem, 1);
718 priv->items [i]->uri = g_strdup (uris_ordered [i]);
719 priv->items [i]->title = g_bookmark_file_get_title (priv->store, uris_ordered [i], NULL);
720 priv->items [i]->mime_type = g_bookmark_file_get_mime_type (priv->store, uris_ordered [i], NULL);
721 priv->items [i]->mtime = g_bookmark_file_get_modified_date_time (priv->store, uris_ordered [i], NULL);
722 priv->items [i]->app_name = NULL;
723 priv->items [i]->app_exec = NULL;
724
725 g_bookmark_file_get_icon (priv->store, uris_ordered [i], & priv->items [i]->icon, NULL, NULL);
726 }
727
728 /* Since the bookmark store for recently-used items is updated by the caller of BookmarkAgent,
729 * we don't emit notifications in that case. The caller will know when to update itself.
730 */
731 if (!TYPE_IS_RECENT (priv->type))
732 g_object_notify (G_OBJECT (this), BOOKMARK_AGENT_ITEMS_PROP);
733 }
734
735 if (store_corrupted)
736 save_store (this);
737
738 g_strfreev (uris);
739 g_free (uris_ordered);
740 }
741
742 static void
save_store(BookmarkAgent * this)743 save_store (BookmarkAgent *this)
744 {
745 BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
746
747 gchar *dir;
748
749
750 g_return_if_fail (priv->user_modifiable);
751
752 priv->needs_sync = TRUE;
753 priv->update_path (this);
754
755 dir = g_path_get_dirname (priv->store_path);
756 g_mkdir_with_parents (dir, 0700);
757 g_free (dir);
758
759 priv->save_store (this);
760 update_items (this);
761 }
762
763 static gint
get_rank(BookmarkAgent * this,const gchar * uri)764 get_rank (BookmarkAgent *this, const gchar *uri)
765 {
766 BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
767
768 gchar **groups;
769 gint rank;
770
771 gint i;
772
773
774 if (! priv->reorderable)
775 return -1;
776
777 groups = g_bookmark_file_get_groups (priv->store, uri, NULL, NULL);
778 rank = -1;
779
780 for (i = 0; groups && groups [i]; ++i) {
781 if (g_str_has_prefix (groups [i], "rank-")) {
782 if (rank >= 0)
783 g_warning (
784 "store corruption - multiple ranks for same uri: [%s] [%s]",
785 priv->store_path, uri);
786
787 rank = atoi (& groups [i] [5]);
788 }
789 }
790
791 g_strfreev (groups);
792
793 return rank;
794 }
795
796 static void
set_rank(BookmarkAgent * this,const gchar * uri,gint rank)797 set_rank (BookmarkAgent *this, const gchar *uri, gint rank)
798 {
799 BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
800
801 gchar **groups;
802 gchar *group;
803
804 gint i;
805
806
807 if (! (priv->reorderable && bookmark_agent_has_item (this, uri)))
808 return;
809
810 groups = g_bookmark_file_get_groups (priv->store, uri, NULL, NULL);
811
812 for (i = 0; groups && groups [i]; ++i)
813 if (g_str_has_prefix (groups [i], "rank-"))
814 g_bookmark_file_remove_group (priv->store, uri, groups [i], NULL);
815
816 g_strfreev (groups);
817
818 group = g_strdup_printf ("rank-%d", rank);
819 g_bookmark_file_add_group (priv->store, uri, group);
820 g_free (group);
821 }
822
823 static void
load_xbel_store(BookmarkAgent * this)824 load_xbel_store (BookmarkAgent *this)
825 {
826 BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
827
828 gchar **uris = NULL;
829
830 GError *error = NULL;
831
832 gint i;
833 gboolean success;
834
835 if (!priv->store_path)
836 success = FALSE;
837 else {
838 success = g_bookmark_file_load_from_file (priv->store, priv->store_path, & error);
839 }
840
841 if (!success) {
842 g_bookmark_file_free (priv->store);
843 priv->store = g_bookmark_file_new ();
844
845 if (error) {
846 g_debug ("Couldn't load bookmark file [%s]: %s", priv->store_path, error->message);
847 g_error_free (error);
848 } else {
849 g_debug ("Couldn't load bookmark file [NULL]");
850 }
851 return;
852 }
853
854 uris = g_bookmark_file_get_uris (priv->store, NULL);
855
856 for (i = 0; uris && uris [i]; ++i)
857 priv->create_item (this, uris [i]);
858
859 g_strfreev (uris);
860 }
861
862 static void
load_places_store(BookmarkAgent * this)863 load_places_store (BookmarkAgent *this)
864 {
865 BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
866
867 gchar **uris;
868 gchar **groups;
869 gchar **bookmarks = NULL;
870
871 gchar *buf, *label, *uri;
872
873 gint i, j, bookmark_len;
874
875 load_xbel_store (this);
876
877 uris = g_bookmark_file_get_uris (priv->store, NULL);
878
879 for (i = 0; uris && uris [i]; ++i) {
880 groups = g_bookmark_file_get_groups (priv->store, uris [i], NULL, NULL);
881
882 for (j = 0; groups && groups [j]; ++j) {
883 if (! strcmp (groups [j], "gtk-bookmarks")) {
884 g_bookmark_file_remove_item (priv->store, uris [i], NULL);
885
886 break;
887 }
888 }
889
890 g_strfreev (groups);
891 }
892
893 g_strfreev (uris);
894
895 g_file_get_contents (priv->gtk_store_path, & buf, NULL, NULL);
896
897 if (buf) {
898 bookmarks = g_strsplit (buf, "\n", -1);
899 g_free (buf);
900 }
901
902 for (i = 0; bookmarks && bookmarks [i]; ++i) {
903 bookmark_len = strlen (bookmarks [i]);
904 if (bookmark_len > 0) {
905 label = strstr (bookmarks[i], " ");
906 if (label != NULL)
907 uri = g_strndup (bookmarks [i], bookmark_len - strlen (label));
908 else
909 uri = bookmarks [i];
910 g_bookmark_file_add_group (priv->store, uri, "gtk-bookmarks");
911 priv->create_item (this, uri);
912 if (label != NULL) {
913 label++;
914 if (strlen (label) > 0)
915 g_bookmark_file_set_title (priv->store, uri, label);
916 g_free (uri);
917 }
918 }
919 }
920
921 g_strfreev (bookmarks);
922 }
923
924 static gchar *
find_package_data_file(const gchar * filename)925 find_package_data_file (const gchar *filename)
926 {
927 const gchar * const *dirs = NULL;
928 gchar *path = NULL;
929 gint i;
930
931
932 dirs = g_get_system_data_dirs ();
933
934 for (i = 0; ! path && dirs && dirs [i]; ++i) {
935 path = g_build_filename (dirs [i], PACKAGE, filename, NULL);
936
937 if (! g_file_test (path, G_FILE_TEST_EXISTS)) {
938 g_free (path);
939 path = NULL;
940 }
941 }
942
943 return path;
944 }
945
946 static void
update_user_spec_path(BookmarkAgent * this)947 update_user_spec_path (BookmarkAgent *this)
948 {
949 BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
950
951 gboolean use_user_path;
952 gchar *path = NULL;
953
954 BookmarkStoreStatus status;
955
956 use_user_path = priv->user_modifiable &&
957 (priv->needs_sync || g_file_test (priv->user_store_path, G_FILE_TEST_EXISTS));
958
959 if (use_user_path)
960 path = g_strdup (priv->user_store_path);
961 else
962 path = find_package_data_file (priv->store_filename);
963
964 if (use_user_path)
965 status = BOOKMARK_STORE_USER;
966 else if (path && priv->user_modifiable)
967 status = BOOKMARK_STORE_DEFAULT;
968 else if (path)
969 status = BOOKMARK_STORE_DEFAULT_ONLY;
970 else
971 status = BOOKMARK_STORE_ABSENT;
972
973 if (priv->status != status) {
974 priv->status = status;
975 g_object_notify (G_OBJECT (this), BOOKMARK_AGENT_STORE_STATUS_PROP);
976
977 if (priv->user_store_monitor) {
978 g_file_monitor_cancel (priv->user_store_monitor);
979 g_object_unref (priv->user_store_monitor);
980 priv->user_store_monitor = NULL;
981 }
982
983 if (priv->status == BOOKMARK_STORE_DEFAULT) {
984 GFile *user_store_file;
985
986 user_store_file = g_file_new_for_path (priv->user_store_path);
987 priv->user_store_monitor = g_file_monitor_file (user_store_file,
988 0, NULL, NULL);
989 if (priv->user_store_monitor) {
990 g_signal_connect (priv->user_store_monitor, "changed",
991 G_CALLBACK (store_monitor_cb), this);
992 }
993
994 g_object_unref (user_store_file);
995 }
996 }
997
998 if (g_strcmp0 (priv->store_path, path)) {
999 g_free (priv->store_path);
1000 priv->store_path = path;
1001
1002 if (priv->store_monitor) {
1003 g_file_monitor_cancel (priv->store_monitor);
1004 g_object_unref (priv->store_monitor);
1005 }
1006
1007 if (priv->store_path) {
1008 GFile *store_file;
1009
1010 store_file = g_file_new_for_path (priv->store_path);
1011 priv->store_monitor = g_file_monitor_file (store_file,
1012 0, NULL, NULL);
1013 if (priv->store_monitor) {
1014 g_signal_connect (priv->store_monitor, "changed",
1015 G_CALLBACK (store_monitor_cb), this);
1016 }
1017
1018 g_object_unref (store_file);
1019 }
1020 }
1021 else
1022 g_free (path);
1023 }
1024
1025 static void
save_xbel_store(BookmarkAgent * this)1026 save_xbel_store (BookmarkAgent *this)
1027 {
1028 BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
1029
1030 GError *error = NULL;
1031
1032 if (g_bookmark_file_to_file (priv->store, priv->store_path, &error))
1033 return;
1034
1035 if (error) {
1036 g_warning ("Couldn't save bookmark file [%s]: %s", priv->store_path, error->message);
1037 g_error_free (error);
1038 } else {
1039 g_warning ("Couldn't save bookmark file [%s]", priv->store_path);
1040 }
1041 }
1042
1043 static void
create_app_item(BookmarkAgent * this,const gchar * uri)1044 create_app_item (BookmarkAgent *this, const gchar *uri)
1045 {
1046 BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
1047
1048 MateDesktopItem *ditem;
1049 gchar *uri_new = NULL;
1050
1051 ditem = libslab_mate_desktop_item_new_from_unknown_id (uri);
1052
1053 if (ditem) {
1054 uri_new = g_strdup (mate_desktop_item_get_location (ditem));
1055 mate_desktop_item_unref (ditem);
1056 }
1057
1058 if (! uri_new)
1059 return;
1060
1061 if (g_strcmp0 (uri, uri_new))
1062 g_bookmark_file_move_item (priv->store, uri, uri_new, NULL);
1063
1064 g_free (uri_new);
1065 }
1066
1067 static void
create_doc_item(BookmarkAgent * this,const gchar * uri)1068 create_doc_item (BookmarkAgent *this, const gchar *uri)
1069 {
1070 BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
1071
1072 gchar *uri_new = NULL;
1073
1074 if ((strcmp (uri, "BLANK_SPREADSHEET") == 0) || (strcmp (uri, "BLANK_DOCUMENT") == 0)) {
1075 gchar *template = NULL;
1076 gchar *file;
1077
1078 gchar *dir = g_strdup (g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
1079 if (!dir)
1080 dir = g_build_filename (g_get_home_dir (), "Documents", NULL);
1081
1082 if (strcmp (uri, "BLANK_SPREADSHEET") == 0) {
1083 g_bookmark_file_set_title (priv->store, uri, "BLANK_SPREADSHEET");
1084 file = g_strconcat (_("New Spreadsheet"), ".ots", NULL);
1085 template = find_package_data_file (CALC_TEMPLATE_FILE_NAME);
1086 } else {
1087 g_bookmark_file_set_title (priv->store, uri, "BLANK_DOCUMENT");
1088 file = g_strconcat (_("New Document"), ".ott", NULL);
1089 template = find_package_data_file (WRITER_TEMPLATE_FILE_NAME);
1090 }
1091
1092 gchar *path = g_build_filename (dir, file, NULL);
1093 if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
1094 g_mkdir_with_parents (dir, 0700);
1095
1096 if (template != NULL) {
1097 gchar *contents;
1098 gsize length;
1099
1100 if (g_file_get_contents (template, &contents, &length, NULL))
1101 g_file_set_contents (path, contents, length, NULL);
1102
1103 g_free (contents);
1104 } else {
1105 fclose (g_fopen (path, "w"));
1106 }
1107 }
1108
1109 uri_new = g_filename_to_uri (path, NULL, NULL);
1110
1111 g_free (dir);
1112 g_free (file);
1113 g_free (path);
1114 g_free (template);
1115 }
1116
1117 if (!uri_new)
1118 return;
1119
1120 if (g_strcmp0 (uri, uri_new))
1121 g_bookmark_file_move_item (priv->store, uri, uri_new, NULL);
1122
1123 g_free (uri_new);
1124 }
1125
1126 static void
create_dir_item(BookmarkAgent * this,const gchar * uri)1127 create_dir_item (BookmarkAgent *this, const gchar *uri)
1128 {
1129 BookmarkAgentPrivate *priv = bookmark_agent_get_instance_private (this);
1130
1131 gchar *uri_new = NULL;
1132 gchar *path = NULL;
1133 gchar *name = NULL;
1134 gchar *icon = NULL;
1135
1136 gchar *search_string = NULL;
1137
1138 gboolean gotta_free_name = FALSE;
1139
1140 if (strcmp (uri, "HOME") == 0) {
1141 uri_new = g_filename_to_uri (g_get_home_dir (), NULL, NULL);
1142 name = g_strdup (C_("Home folder", "Home"));
1143 gotta_free_name = TRUE;
1144 icon = "user-home";
1145 } else if (strcmp (uri, "DOCUMENTS") == 0) {
1146 path = g_strdup (g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
1147 if (!path)
1148 path = g_build_filename (g_get_home_dir (), "Documents", NULL);
1149 name = _("Documents");
1150 uri_new = g_filename_to_uri (path, NULL, NULL);
1151 } else if (strcmp (uri, "DESKTOP") == 0) {
1152 path = g_strdup (g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP));
1153 if (!path)
1154 path = g_build_filename (g_get_home_dir (), "Desktop", NULL);
1155 name = _("Desktop");
1156 uri_new = g_filename_to_uri (path, NULL, NULL);
1157 icon = "user-desktop";
1158 } else if (strcmp (uri, "file:///") == 0) {
1159 icon = "drive-harddisk";
1160 name = _("File System");
1161 } else if (strcmp (uri, "network:") == 0) {
1162 icon = "network-workgroup";
1163 name = _("Network Servers");
1164 } else if (g_str_has_prefix (uri, "x-caja-search")) {
1165 icon = "system-search";
1166
1167 path = g_build_filename (g_get_user_data_dir (), "caja", "searches", & uri [21], NULL);
1168
1169 if (g_file_test (path, G_FILE_TEST_EXISTS)) {
1170 gchar *buf = NULL;
1171 g_file_get_contents (path, &buf, NULL, NULL);
1172
1173 gchar *tag_open_ptr = NULL;
1174 gchar *tag_close_ptr = NULL;
1175
1176 if (buf) {
1177 tag_open_ptr = strstr (buf, "<text>");
1178 tag_close_ptr = strstr (buf, "</text>");
1179 }
1180
1181 if (tag_open_ptr && tag_close_ptr) {
1182 tag_close_ptr [0] = '\0';
1183 tag_close_ptr [0] = 'a';
1184 search_string = g_strdup_printf ("\"%s\"", &tag_open_ptr[6]);
1185 }
1186
1187 g_free (buf);
1188 }
1189
1190 if (search_string) {
1191 name = search_string;
1192 gotta_free_name = TRUE;
1193 } else {
1194 name = _("Search");
1195 }
1196 }
1197
1198 if (icon)
1199 g_bookmark_file_set_icon (priv->store, uri, icon, "image/png");
1200
1201 if (name)
1202 g_bookmark_file_set_title (priv->store, uri, name);
1203
1204 if (uri_new && g_strcmp0 (uri, uri_new))
1205 g_bookmark_file_move_item (priv->store, uri, uri_new, NULL);
1206
1207 if (gotta_free_name) {
1208 g_free (name);
1209 }
1210
1211 g_free (path);
1212 g_free (uri_new);
1213 }
1214
1215 static void
store_monitor_cb(GFileMonitor * mon,GFile * f1,GFile * f2,GFileMonitorEvent event_type,gpointer user_data)1216 store_monitor_cb (GFileMonitor *mon, GFile *f1, GFile *f2,
1217 GFileMonitorEvent event_type, gpointer user_data)
1218 {
1219 update_agent (BOOKMARK_AGENT (user_data));
1220 }
1221
1222 static void
weak_destroy_cb(gpointer data,GObject * g_obj)1223 weak_destroy_cb (gpointer data, GObject *g_obj)
1224 {
1225 instances [GPOINTER_TO_INT (data)] = NULL;
1226 }
1227