static void ide_recent_projects_added (IdeRecentProjects *self, IdeProjectInfo *project_info) { g_autofree gchar *uri = NULL; GFile *file; g_assert (IDE_IS_RECENT_PROJECTS (self)); g_assert (IDE_IS_PROJECT_INFO (project_info)); file = ide_project_info_get_file (project_info); uri = g_file_get_uri (file); if (!g_hash_table_contains (self->recent_uris, uri)) { GSequenceIter *iter; gint position; iter = g_sequence_insert_sorted (self->projects, g_object_ref (project_info), (GCompareDataFunc)ide_project_info_compare, NULL); position = g_sequence_iter_get_position (iter); g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1); } }
gchar * ide_recent_projects_find_by_directory (IdeRecentProjects *self, const gchar *directory) { g_autoptr(GBookmarkFile) bookmarks = NULL; g_auto(GStrv) uris = NULL; gsize len = 0; g_return_val_if_fail (IDE_IS_RECENT_PROJECTS (self), NULL); g_return_val_if_fail (directory, NULL); if (!g_file_test (directory, G_FILE_TEST_IS_DIR)) return NULL; if (!(bookmarks = ide_recent_projects_get_bookmarks (self, NULL))) return NULL; uris = g_bookmark_file_get_uris (bookmarks, &len); for (gsize i = 0; i < len; i++) { /* TODO: Make a better compare that deals with trailing / */ if (g_str_has_prefix (uris[i], directory)) return g_strdup (uris[i]); } return NULL; }
static guint ide_recent_projects_get_n_items (GListModel *model) { g_assert (IDE_IS_RECENT_PROJECTS (model)); return g_sequence_get_length (IDE_RECENT_PROJECTS (model)->projects); }
/** * ide_recent_projects_remove: * @self: An #IdeRecentProjects * @project_infos: (transfer none) (element-type IdeProjectInfo): A #GList of #IdeProjectInfo. * * Removes the provided projects from the recent projects file. */ void ide_recent_projects_remove (IdeRecentProjects *self, GList *project_infos) { g_autoptr(GBookmarkFile) projects_file = NULL; g_autoptr(GError) error = NULL; GList *liter; g_return_if_fail (IDE_IS_RECENT_PROJECTS (self)); projects_file = ide_recent_projects_get_bookmarks (self, &error); if (projects_file == NULL) { g_warning ("Failed to load bookmarks file: %s", error->message); return; } for (liter = project_infos; liter; liter = liter->next) { IdeProjectInfo *project_info = liter->data; g_autofree gchar *file_uri = NULL; GSequenceIter *iter; GFile *file; g_assert (IDE_IS_PROJECT_INFO (liter->data)); iter = g_sequence_lookup (self->projects, project_info, (GCompareDataFunc)ide_project_info_compare, NULL); if (iter == NULL) { g_warning ("Project \"%s\" was not found, cannot remove.", ide_project_info_get_name (project_info)); g_clear_error (&error); continue; } file = ide_project_info_get_file (project_info); file_uri = g_file_get_uri (file); if (!g_bookmark_file_remove_item (projects_file, file_uri, &error)) { g_warning ("Failed to remove recent project: %s", error->message); g_clear_error (&error); continue; } g_sequence_remove (iter); } if (!g_bookmark_file_to_file (projects_file, self->file_uri, &error)) { g_warning ("Failed to save recent projects file: %s", error->message); return; } }
static guint ide_recent_projects_get_n_items (GListModel *model) { IdeRecentProjects *self = (IdeRecentProjects *)model; g_assert (IDE_IS_RECENT_PROJECTS (self)); return g_sequence_get_length (self->projects); }
static void ide_recent_projects__miner_discovered (IdeRecentProjects *self, IdeProjectInfo *project_info, IdeProjectMiner *miner) { g_assert (IDE_IS_PROJECT_MINER (miner)); g_assert (IDE_IS_RECENT_PROJECTS (self)); g_assert (IDE_IS_PROJECT_INFO (project_info)); ide_recent_projects_added (self, project_info); }
void ide_recent_projects_discover_async (IdeRecentProjects *self, gboolean recent_only, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_autoptr(GTask) task = NULL; gsize i; g_return_if_fail (IDE_IS_RECENT_PROJECTS (self)); g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); task = g_task_new (self, cancellable, callback, user_data); if (self->discovered) { g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, _("%s() may only be executed once"), G_STRFUNC); return; } self->discovered = TRUE; ide_recent_projects_load_recent (self); if (recent_only) { g_task_return_boolean (task, TRUE); return; } self->active = self->miners->len; if (self->active == 0) { g_task_return_boolean (task, TRUE); return; } for (i = 0; i < self->miners->len; i++) { IdeProjectMiner *miner; miner = g_ptr_array_index (self->miners, i); ide_project_miner_mine_async (miner, self->cancellable, ide_recent_projects__miner_mine_cb, g_object_ref (task)); } }
gboolean ide_recent_projects_discover_finish (IdeRecentProjects *self, GAsyncResult *result, GError **error) { GTask *task = (GTask *)result; g_return_val_if_fail (IDE_IS_RECENT_PROJECTS (self), FALSE); g_return_val_if_fail (G_IS_TASK (task), FALSE); return g_task_propagate_boolean (task, error); }
static gpointer ide_recent_projects_get_item (GListModel *model, guint position) { IdeRecentProjects *self = (IdeRecentProjects *)model; GSequenceIter *iter; g_assert (IDE_IS_RECENT_PROJECTS (self)); if ((iter = g_sequence_get_iter_at_pos (self->projects, position))) return g_object_ref (g_sequence_get (iter)); return NULL; }
static void ide_recent_projects_add_miner (IdeRecentProjects *self, IdeProjectMiner *miner) { g_assert (IDE_IS_RECENT_PROJECTS (self)); g_assert (IDE_IS_PROJECT_MINER (miner)); g_signal_connect_object (miner, "discovered", G_CALLBACK (ide_recent_projects__miner_discovered), self, G_CONNECT_SWAPPED); g_ptr_array_add (self->miners, g_object_ref (miner)); }
static void foreach_miner_func (PeasExtensionSet *set, PeasPluginInfo *plugin_info, PeasExtension *exten, gpointer user_data) { IdeRecentProjects *self = user_data; g_assert (PEAS_IS_EXTENSION_SET (set)); g_assert (plugin_info != NULL); g_assert (IDE_IS_PROJECT_MINER (exten)); g_assert (IDE_IS_RECENT_PROJECTS (self)); ide_recent_projects_add_miner (self, IDE_PROJECT_MINER (exten)); }
static void ide_recent_projects__miner_mine_cb (GObject *object, GAsyncResult *result, gpointer user_data) { IdeRecentProjects *self; g_autoptr(GTask) task = user_data; IdeProjectMiner *miner = (IdeProjectMiner *)object; g_assert (G_IS_TASK (task)); g_assert (IDE_IS_PROJECT_MINER (miner)); self = g_task_get_source_object (task); g_assert (IDE_IS_RECENT_PROJECTS (self)); ide_project_miner_mine_finish (miner, result, NULL); if (--self->active == 0) g_task_return_boolean (task, TRUE); }
/** * ide_recent_projects_get_projects: * * Gets a #GPtrArray containing the #IdeProjectInfo that have been discovered. * * Returns: (transfer container) (element-type IdeProjectInfo*): A #GPtrArray of #IdeProjectInfo. */ GPtrArray * ide_recent_projects_get_projects (IdeRecentProjects *self) { GSequenceIter *iter; GPtrArray *ret; g_return_val_if_fail (IDE_IS_RECENT_PROJECTS (self), NULL); ret = g_ptr_array_new_with_free_func (g_object_unref); for (iter = g_sequence_get_begin_iter (self->projects); !g_sequence_iter_is_end (iter); g_sequence_iter_next (iter)) { IdeProjectInfo *project_info; project_info = g_sequence_get (iter); g_ptr_array_add (ret, g_object_ref (project_info)); } return ret; }
static GBookmarkFile * ide_recent_projects_get_bookmarks (IdeRecentProjects *self, GError **error) { GBookmarkFile *bookmarks; g_assert (IDE_IS_RECENT_PROJECTS (self)); g_assert (error != NULL); bookmarks = g_bookmark_file_new (); if (!g_bookmark_file_load_from_file (bookmarks, self->file_uri, error)) { if (!g_error_matches (*error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { g_object_unref (bookmarks); return NULL; } } return bookmarks; }
static GBookmarkFile * ide_recent_projects_get_bookmarks (IdeRecentProjects *self, GError **error) { g_autoptr(GBookmarkFile) bookmarks = NULL; g_autoptr(GError) local_error = NULL; g_assert (IDE_IS_RECENT_PROJECTS (self)); bookmarks = g_bookmark_file_new (); if (!g_bookmark_file_load_from_file (bookmarks, self->file_uri, &local_error)) { if (!g_error_matches (local_error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { g_propagate_error (error, g_steal_pointer (&local_error)); return NULL; } } return g_steal_pointer (&bookmarks); }
/** * ide_recent_projects_remove: * @self: An #IdeRecentProjects * @project_infos: (transfer none) (element-type IdeProjectInfo): A #GList of #IdeProjectInfo. * * Removes the provided projects from the recent projects file. */ void ide_recent_projects_remove (IdeRecentProjects *self, GList *project_infos) { g_autoptr(GBookmarkFile) projects_file = NULL; g_autoptr(GError) error = NULL; GList *liter; g_return_if_fail (IDE_IS_RECENT_PROJECTS (self)); projects_file = ide_recent_projects_get_bookmarks (self, &error); if (projects_file == NULL) { g_warning ("Failed to load bookmarks file: %s", error->message); return; } for (liter = project_infos; liter; liter = liter->next) { IdeProjectInfo *project_info = liter->data; g_autofree gchar *file_uri = NULL; GSequenceIter *iter; GFile *file; g_assert (IDE_IS_PROJECT_INFO (liter->data)); iter = g_sequence_lookup (self->projects, project_info, (GCompareDataFunc)ide_project_info_compare, NULL); if (iter == NULL) { g_warning ("Project \"%s\" was not found, cannot remove.", ide_project_info_get_name (project_info)); g_clear_error (&error); continue; } file = ide_project_info_get_file (project_info); file_uri = g_file_get_uri (file); if (!g_bookmark_file_remove_item (projects_file, file_uri, &error)) { g_autofree gchar *with_slash = g_strdup_printf ("%s/", file_uri); /* Sometimes we don't get a match because the directory is missing a * trailing slash. Annoying, I know. See the following for the * upstream bug filed in GLib. * * https://bugzilla.gnome.org/show_bug.cgi?id=765449 */ if (!g_bookmark_file_remove_item (projects_file, with_slash, NULL)) { g_warning ("Failed to remove recent project: %s", error->message); g_clear_error (&error); continue; } g_clear_error (&error); } g_sequence_remove (iter); } if (!g_bookmark_file_to_file (projects_file, self->file_uri, &error)) { g_warning ("Failed to save recent projects file: %s", error->message); return; } }
static void ide_recent_projects_load_recent (IdeRecentProjects *self) { g_autoptr(GBookmarkFile) projects_file; g_autoptr(GError) error = NULL; gchar **uris; gssize z; g_assert (IDE_IS_RECENT_PROJECTS (self)); projects_file = ide_recent_projects_get_bookmarks (self, &error); if (projects_file == NULL) { g_warning ("Unable to open recent projects file: %s", error->message); return; } uris = g_bookmark_file_get_uris (projects_file, NULL); for (z = 0; uris[z]; z++) { g_autoptr(GDateTime) last_modified_at = NULL; g_autoptr(GFile) project_file = NULL; g_autoptr(GFile) directory = NULL; g_autoptr(GPtrArray) languages = NULL; g_autoptr(IdeProjectInfo) project_info = NULL; const gchar *description; const gchar *uri = uris[z]; const gchar *name; time_t modified; gchar **groups; gsize len; gsize i; groups = g_bookmark_file_get_groups (projects_file, uri, &len, NULL); for (i = 0; i < len; i++) { if (g_str_equal (groups [i], IDE_RECENT_PROJECTS_GROUP)) goto is_project; } continue; is_project: name = g_bookmark_file_get_title (projects_file, uri, NULL); description = g_bookmark_file_get_description (projects_file, uri, NULL); modified = g_bookmark_file_get_modified (projects_file, uri, NULL); last_modified_at = g_date_time_new_from_unix_local (modified); project_file = g_file_new_for_uri (uri); directory = g_file_get_parent (project_file); languages = g_ptr_array_new (); for (i = 0; i < len; i++) { if (g_str_has_prefix (groups [i], IDE_RECENT_PROJECTS_LANGUAGE_GROUP_PREFIX)) g_ptr_array_add (languages, groups [i] + strlen (IDE_RECENT_PROJECTS_LANGUAGE_GROUP_PREFIX)); } g_ptr_array_add (languages, NULL); project_info = g_object_new (IDE_TYPE_PROJECT_INFO, "description", description, "directory", directory, "file", project_file, "is-recent", TRUE, "languages", (gchar **)languages->pdata, "last-modified-at", last_modified_at, "name", name, NULL); ide_recent_projects_added (self, project_info); g_hash_table_insert (self->recent_uris, g_strdup (uri), NULL); } g_strfreev (uris); }
static void ide_recent_projects_load_recent (IdeRecentProjects *self) { g_autoptr(GBookmarkFile) projects_file = NULL; g_autoptr(GError) error = NULL; gboolean needs_sync = FALSE; gchar **uris; g_assert (IDE_IS_RECENT_PROJECTS (self)); if (!(projects_file = ide_recent_projects_get_bookmarks (self, &error))) { g_warning ("Unable to open recent projects file: %s", error->message); return; } uris = g_bookmark_file_get_uris (projects_file, NULL); for (gsize z = 0; uris[z]; z++) { g_autoptr(GDateTime) last_modified_at = NULL; g_autoptr(GFile) project_file = NULL; g_autoptr(GFile) directory = NULL; g_autoptr(GPtrArray) languages = NULL; g_autoptr(IdeProjectInfo) project_info = NULL; g_autofree gchar *name = NULL; g_autofree gchar *description = NULL; const gchar *build_system_hint = NULL; const gchar *build_system_name = NULL; const gchar *uri = uris[z]; const gchar *diruri = NULL; time_t modified; g_auto(GStrv) groups = NULL; gsize len; groups = g_bookmark_file_get_groups (projects_file, uri, &len, NULL); for (gsize i = 0; i < len; i++) { if (g_str_equal (groups [i], IDE_RECENT_PROJECTS_GROUP)) goto is_project; } continue; is_project: project_file = g_file_new_for_uri (uri); if (g_file_is_native (project_file) && !g_file_query_exists (project_file, NULL)) { g_bookmark_file_remove_item (projects_file, uri, NULL); needs_sync = TRUE; continue; } name = g_bookmark_file_get_title (projects_file, uri, NULL); description = g_bookmark_file_get_description (projects_file, uri, NULL); modified = g_bookmark_file_get_modified (projects_file, uri, NULL); last_modified_at = g_date_time_new_from_unix_local (modified); for (gsize i = 0; i < len; i++) { if (g_str_has_prefix (groups [i], IDE_RECENT_PROJECTS_DIRECTORY)) diruri = groups [i] + strlen (IDE_RECENT_PROJECTS_DIRECTORY); } if (diruri == NULL) { /* If the old project was a plain-ol'-directory, then we don't want * it's parent (which might be ~/Projects), instead reuse the project * file as the directory too. */ if (g_file_query_file_type (project_file, 0, NULL) == G_FILE_TYPE_DIRECTORY) directory = g_file_dup (project_file); else directory = g_file_get_parent (project_file); } else directory = g_file_new_for_uri (diruri); languages = g_ptr_array_new_with_free_func (g_free); for (gsize i = 0; i < len; i++) { if (g_str_has_prefix (groups [i], IDE_RECENT_PROJECTS_LANGUAGE_GROUP_PREFIX)) g_ptr_array_add (languages, g_strdup (groups [i] + strlen (IDE_RECENT_PROJECTS_LANGUAGE_GROUP_PREFIX))); else if (g_str_has_prefix (groups [i], IDE_RECENT_PROJECTS_BUILD_SYSTEM_GROUP_PREFIX)) build_system_name = groups [i] + strlen (IDE_RECENT_PROJECTS_BUILD_SYSTEM_GROUP_PREFIX); else if (g_str_has_prefix (groups [i], IDE_RECENT_PROJECTS_BUILD_SYSTEM_HINT_GROUP_PREFIX)) build_system_hint = groups [i] + strlen (IDE_RECENT_PROJECTS_BUILD_SYSTEM_HINT_GROUP_PREFIX); } /* Cleanup any extra space */ for (guint i = 0; i < languages->len; i++) g_strstrip ((gchar *)g_ptr_array_index (languages, i)); g_ptr_array_add (languages, NULL); project_info = g_object_new (IDE_TYPE_PROJECT_INFO, "build-system-hint", build_system_hint, "build-system-name", build_system_name, "description", description, "directory", directory, "file", project_file, "is-recent", TRUE, "languages", (gchar **)languages->pdata, "last-modified-at", last_modified_at, "name", name, NULL); ide_recent_projects_added (self, project_info); g_hash_table_insert (self->recent_uris, g_strdup (uri), NULL); } g_strfreev (uris); if (needs_sync) g_bookmark_file_to_file (projects_file, self->file_uri, NULL); }