diff --git a/docs/reference/glib/glib-sections.txt.in b/docs/reference/glib/glib-sections.txt.in index 2b500257c..77fabc857 100644 --- a/docs/reference/glib/glib-sections.txt.in +++ b/docs/reference/glib/glib-sections.txt.in @@ -2152,6 +2152,7 @@ GBookmarkFile G_BOOKMARK_FILE_ERROR GBookmarkFileError g_bookmark_file_new +g_bookmark_file_copy g_bookmark_file_free g_bookmark_file_load_from_file g_bookmark_file_load_from_data diff --git a/docs/reference/gobject/gobject-sections.txt b/docs/reference/gobject/gobject-sections.txt index 6fd650394..9acbd7de7 100644 --- a/docs/reference/gobject/gobject-sections.txt +++ b/docs/reference/gobject/gobject-sections.txt @@ -417,6 +417,7 @@ G_TYPE_OPTION_GROUP G_TYPE_URI G_TYPE_TREE G_TYPE_PATTERN_SPEC +G_TYPE_BOOKMARK_FILE G_TYPE_IS_BOXED @@ -453,6 +454,7 @@ g_option_group_get_type g_uri_get_type g_tree_get_type g_pattern_spec_get_type +g_bookmark_file_get_type
diff --git a/glib/gbookmarkfile.c b/glib/gbookmarkfile.c index 68618fb1c..3988e3653 100644 --- a/glib/gbookmarkfile.c +++ b/glib/gbookmarkfile.c @@ -287,6 +287,24 @@ bookmark_app_info_free (BookmarkAppInfo *app_info) g_slice_free (BookmarkAppInfo, app_info); } +static BookmarkAppInfo * +bookmark_app_info_copy (BookmarkAppInfo *app_info) +{ + BookmarkAppInfo *copy; + + if (!app_info) + return NULL; + + copy = bookmark_app_info_new (app_info->name); + copy->count = app_info->count; + copy->exec = g_strdup (app_info->exec); + + if (app_info->stamp) + copy->stamp = g_date_time_ref (app_info->stamp); + + return copy; +} + static gchar * bookmark_app_info_dump (BookmarkAppInfo *app_info) { @@ -300,14 +318,27 @@ bookmark_app_info_dump (BookmarkAppInfo *app_info) name = g_markup_escape_text (app_info->name, -1); exec = g_markup_escape_text (app_info->exec, -1); - modified = g_date_time_format_iso8601 (app_info->stamp); count = g_strdup_printf ("%u", app_info->count); + if (app_info->stamp) + { + char *tmp; + + tmp = g_date_time_format_iso8601 (app_info->stamp); + modified = g_strconcat (" " BOOKMARK_MODIFIED_ATTRIBUTE "=\"", tmp, "\"", + NULL); + g_free (tmp); + } + else + { + modified = g_strdup (""); + } + retval = g_strconcat (" " "<" BOOKMARK_NAMESPACE_NAME ":" BOOKMARK_APPLICATION_ELEMENT " " BOOKMARK_NAME_ATTRIBUTE "=\"", name, "\"" - " " BOOKMARK_EXEC_ATTRIBUTE "=\"", exec, "\"" - " " BOOKMARK_MODIFIED_ATTRIBUTE "=\"", modified, "\"" + " " BOOKMARK_EXEC_ATTRIBUTE "=\"", exec, "\"", + modified, " " BOOKMARK_COUNT_ATTRIBUTE "=\"", count, "\"/>\n", NULL); @@ -369,6 +400,37 @@ bookmark_metadata_free (BookmarkMetadata *metadata) g_slice_free (BookmarkMetadata, metadata); } +static BookmarkMetadata * +bookmark_metadata_copy (BookmarkMetadata *metadata) +{ + BookmarkMetadata *copy; + GList *l; + + if (!metadata) + return NULL; + + copy = bookmark_metadata_new (); + copy->is_private = metadata->is_private; + copy->mime_type = g_strdup (metadata->mime_type); + copy->icon_href = g_strdup (metadata->icon_href); + copy->icon_mime = g_strdup (metadata->icon_mime); + + copy->groups = g_list_copy_deep (metadata->groups, (GCopyFunc) g_strdup, NULL); + copy->applications = + g_list_copy_deep (metadata->applications, (GCopyFunc) bookmark_app_info_copy, NULL); + + for (l = copy->applications; l; l = l->next) + { + BookmarkAppInfo *app_info = l->data; + g_hash_table_insert (copy->apps_by_name, app_info->name, app_info); + } + + g_assert (g_hash_table_size (copy->apps_by_name) == + g_hash_table_size (metadata->apps_by_name)); + + return copy; +} + static gchar * bookmark_metadata_dump (BookmarkMetadata *metadata) { @@ -543,6 +605,31 @@ bookmark_item_free (BookmarkItem *item) g_slice_free (BookmarkItem, item); } +static BookmarkItem * +bookmark_item_copy (BookmarkItem *item) +{ + BookmarkItem* copy; + + if (!item) + return NULL; + + copy = bookmark_item_new (item->uri); + + copy->title = g_strdup (item->title); + copy->description = g_strdup (item->description); + + copy->metadata = bookmark_metadata_copy (item->metadata); + + if (item->added) + copy->added = g_date_time_ref (item->added); + if (item->modified) + copy->modified = g_date_time_ref (item->modified); + if (item->visited) + copy->visited = g_date_time_ref (item->visited); + + return copy; +} + static void bookmark_item_touch_modified (BookmarkItem *item) { @@ -696,12 +783,7 @@ g_bookmark_file_clear (GBookmarkFile *bookmark) g_list_free_full (bookmark->items, (GDestroyNotify) bookmark_item_free); bookmark->items = NULL; - if (bookmark->items_by_uri) - { - g_hash_table_destroy (bookmark->items_by_uri); - - bookmark->items_by_uri = NULL; - } + g_clear_pointer (&bookmark->items_by_uri, g_hash_table_unref); } struct _ParseData @@ -1671,6 +1753,42 @@ g_bookmark_file_new (void) return bookmark; } +/** + * g_bookmark_file_copy: + * @bookmark: A #GBookmarkFile + * + * Deeply copies a @bookmark #GBookmarkFile object to a new one. + * + * Returns: (transfer full): the copy of @bookmark. Use + * g_bookmark_free() when finished using it. + * + * Since: 2.76 + */ +GBookmarkFile * +g_bookmark_file_copy (GBookmarkFile *bookmark) +{ + GBookmarkFile *copy; + GList *l; + + g_return_val_if_fail (bookmark != NULL, NULL); + + copy = g_bookmark_file_new (); + copy->title = g_strdup (bookmark->title); + copy->description = g_strdup (bookmark->description); + copy->items = g_list_copy_deep (bookmark->items, (GCopyFunc) bookmark_item_copy, NULL); + + for (l = copy->items; l; l = l->next) + { + BookmarkItem *item = l->data; + g_hash_table_insert (copy->items_by_uri, item->uri, item); + } + + g_assert (g_hash_table_size (copy->items_by_uri) == + g_hash_table_size (bookmark->items_by_uri)); + + return copy; +} + /** * g_bookmark_file_free: * @bookmark: a #GBookmarkFile diff --git a/glib/gbookmarkfile.h b/glib/gbookmarkfile.h index e40186202..f753420ed 100644 --- a/glib/gbookmarkfile.h +++ b/glib/gbookmarkfile.h @@ -84,6 +84,9 @@ GBookmarkFile *g_bookmark_file_new (void); GLIB_AVAILABLE_IN_ALL void g_bookmark_file_free (GBookmarkFile *bookmark); +GLIB_AVAILABLE_IN_2_76 +GBookmarkFile *g_bookmark_file_copy (GBookmarkFile *bookmark); + GLIB_AVAILABLE_IN_ALL gboolean g_bookmark_file_load_from_file (GBookmarkFile *bookmark, const gchar *filename, diff --git a/glib/tests/bookmarkfile.c b/glib/tests/bookmarkfile.c index 389bc6370..b96e58503 100644 --- a/glib/tests/bookmarkfile.c +++ b/glib/tests/bookmarkfile.c @@ -1245,6 +1245,99 @@ test_file (gconstpointer d) g_assert_true (success == (strstr (filename, "fail") == NULL)); } +static void +test_file_copy (gconstpointer d) +{ + const gchar *filename = d; + GBookmarkFile *bookmark_file; + GBookmarkFile *copy; + gboolean success; + gchar *data; + gchar *copy_data; + gsize length; + gsize copy_length; + GError *error = NULL; + + bookmark_file = g_bookmark_file_new (); + g_assert_nonnull (bookmark_file); + + success = test_load (bookmark_file, filename); + g_assert_true (success == (strstr (filename, "fail") == NULL)); + + copy = g_bookmark_file_copy (bookmark_file); + g_assert_nonnull (copy); + + if (g_str_has_suffix (filename, "fail-08.xbel") || + g_str_has_suffix (filename, "fail-06.xbel") || + g_str_has_suffix (filename, "fail-07.xbel") || + g_str_has_suffix (filename, "fail-09.xbel") || + g_str_has_suffix (filename, "fail-10.xbel") || + g_str_has_suffix (filename, "fail-11.xbel") || + g_str_has_suffix (filename, "fail-39.xbel")) + { + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*no registered applications*skipping*"); + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*no registered applications*skipping*"); + } + + data = g_bookmark_file_to_data (bookmark_file, &length, &error); + g_assert_no_error (error); + + copy_data = g_bookmark_file_to_data (copy, ©_length, &error); + g_assert_no_error (error); + + g_test_assert_expected_messages (); + + g_assert_cmpuint (length, ==, copy_length); + g_assert_cmpstr (data, ==, copy_data); + + if (success) + { + GBookmarkFile *modified_copy; + gchar *modified_data; + gchar *modified_copy_data; + gsize modified_length; + gsize modified_copy_length; + + test_modify (bookmark_file); + test_modify (copy); + + modified_data = g_bookmark_file_to_data (bookmark_file, + &modified_length, + &error); + g_assert_no_error (error); + + modified_copy_data = g_bookmark_file_to_data (copy, + &modified_copy_length, + &error); + g_assert_no_error (error); + + g_assert_cmpstr (data, !=, modified_data); + g_assert_cmpstr (copy_data, !=, modified_copy_data); + + g_free (modified_copy_data); + modified_copy = g_bookmark_file_copy (bookmark_file); + modified_copy_data = g_bookmark_file_to_data (modified_copy, + &modified_copy_length, + &error); + g_assert_no_error (error); + + g_assert_cmpuint (modified_length, ==, modified_copy_length); + g_assert_cmpstr (modified_data, ==, modified_copy_data); + + g_free (modified_data); + g_free (modified_copy_data); + g_bookmark_file_free (modified_copy); + } + + g_bookmark_file_free (bookmark_file); + g_bookmark_file_free (copy); + + g_free (data); + g_free (copy_data); +} + int main (int argc, char *argv[]) { @@ -1275,12 +1368,17 @@ main (int argc, char *argv[]) g_assert_no_error (error); while ((name = g_dir_read_name (dir)) != NULL) { + gchar *filename; if (!g_str_has_suffix (name, ".xbel")) continue; + filename = g_test_build_filename (G_TEST_DIST, "bookmarks", name, NULL); + path = g_strdup_printf ("/bookmarks/parse/%s", name); - g_test_add_data_func_full (path, g_test_build_filename (G_TEST_DIST, "bookmarks", name, NULL), - test_file, g_free); + g_test_add_data_func_full (path, filename, test_file, g_free); + g_free (path); + path = g_strdup_printf ("/bookmarks/copy/%s", name); + g_test_add_data_func_full (path, g_strdup (filename), test_file_copy, g_free); g_free (path); } g_dir_close (dir); diff --git a/gobject/gboxed.c b/gobject/gboxed.c index 44da11bf6..04d283a7e 100644 --- a/gobject/gboxed.c +++ b/gobject/gboxed.c @@ -166,6 +166,7 @@ G_DEFINE_BOXED_TYPE (GDateTime, g_date_time, g_date_time_ref, g_date_time_unref) G_DEFINE_BOXED_TYPE (GTimeZone, g_time_zone, g_time_zone_ref, g_time_zone_unref) G_DEFINE_BOXED_TYPE (GKeyFile, g_key_file, g_key_file_ref, g_key_file_unref) G_DEFINE_BOXED_TYPE (GMappedFile, g_mapped_file, g_mapped_file_ref, g_mapped_file_unref) +G_DEFINE_BOXED_TYPE (GBookmarkFile, g_bookmark_file, g_bookmark_file_copy, g_bookmark_file_free) G_DEFINE_BOXED_TYPE (GMainLoop, g_main_loop, g_main_loop_ref, g_main_loop_unref) G_DEFINE_BOXED_TYPE (GMainContext, g_main_context, g_main_context_ref, g_main_context_unref) diff --git a/gobject/glib-types.h b/gobject/glib-types.h index 020f5d4f7..21081d2b5 100644 --- a/gobject/glib-types.h +++ b/gobject/glib-types.h @@ -326,6 +326,15 @@ typedef gsize GType; */ #define G_TYPE_PATTERN_SPEC (g_pattern_spec_get_type ()) +/** + * G_TYPE_BOOKMARK_FILE: + * + * The #GType for a boxed type holding a #GBookmarkFile. + * + * Since: 2.76 + */ +#define G_TYPE_BOOKMARK_FILE (g_bookmark_file_get_type ()) + GOBJECT_AVAILABLE_IN_ALL GType g_date_get_type (void) G_GNUC_CONST; GOBJECT_AVAILABLE_IN_ALL @@ -388,6 +397,8 @@ GOBJECT_AVAILABLE_IN_2_68 GType g_tree_get_type (void) G_GNUC_CONST; GOBJECT_AVAILABLE_IN_2_70 GType g_pattern_spec_get_type (void) G_GNUC_CONST; +GOBJECT_AVAILABLE_IN_2_76 +GType g_bookmark_file_get_type (void) G_GNUC_CONST; GOBJECT_DEPRECATED_FOR('G_TYPE_VARIANT') GType g_variant_get_gtype (void) G_GNUC_CONST;