[gnome-builder/wip/gtk4-port] plugins/shellcmd: add model which can live update
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/gtk4-port] plugins/shellcmd: add model which can live update
- Date: Sat, 11 Jun 2022 03:58:21 +0000 (UTC)
commit 538f32e181d0f6523ca9172e83f31705fc1ecc76
Author: Christian Hergert <chergert redhat com>
Date: Fri Jun 10 20:58:15 2022 -0700
plugins/shellcmd: add model which can live update
This way we don't have to requery things to keep them in sync. We can just
expect that the shellcmd list model will update with GSettings.
src/plugins/shellcmd/gbp-shellcmd-command-model.c | 302 +++++++++++++++++++++
src/plugins/shellcmd/gbp-shellcmd-command-model.h | 34 +++
.../shellcmd/gbp-shellcmd-run-command-provider.c | 54 ++--
src/plugins/shellcmd/meson.build | 1 +
4 files changed, 356 insertions(+), 35 deletions(-)
---
diff --git a/src/plugins/shellcmd/gbp-shellcmd-command-model.c
b/src/plugins/shellcmd/gbp-shellcmd-command-model.c
new file mode 100644
index 000000000..fbfb27422
--- /dev/null
+++ b/src/plugins/shellcmd/gbp-shellcmd-command-model.c
@@ -0,0 +1,302 @@
+/* gbp-shellcmd-command-model.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-shellcmd-command-model"
+
+#include "config.h"
+
+#include "gbp-shellcmd-command-model.h"
+#include "gbp-shellcmd-run-command.h"
+
+struct _GbpShellcmdCommandModel
+{
+ GObject parent_instance;
+ GSettings *settings;
+ char *key;
+ GHashTable *id_to_command;
+ char **ids;
+ guint n_items;
+};
+
+enum {
+ PROP_0,
+ PROP_KEY,
+ PROP_SETTINGS,
+ PROP_N_ITEMS,
+ N_PROPS
+};
+
+static gpointer
+gbp_shellcmd_command_model_get_item (GListModel *model,
+ guint position)
+{
+ GbpShellcmdCommandModel *self = (GbpShellcmdCommandModel *)model;
+ GbpShellcmdRunCommand *command;
+ const char *id;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND_MODEL (self));
+
+ if (position >= self->n_items)
+ return NULL;
+
+ id = self->ids[position];
+ command = g_hash_table_lookup (self->id_to_command, id);
+
+ if (command == NULL)
+ {
+ g_autofree char *base_path = NULL;
+ g_autofree char *settings_path = NULL;
+
+ g_object_get (self->settings,
+ "path", &base_path,
+ NULL);
+ settings_path = g_strconcat (base_path, id, "/", NULL);
+ command = gbp_shellcmd_run_command_new (settings_path);
+ g_hash_table_insert (self->id_to_command, g_strdup (id), command);
+ }
+
+ return g_object_ref (command);
+}
+
+static guint
+gbp_shellcmd_command_model_get_n_items (GListModel *model)
+{
+ return GBP_SHELLCMD_COMMAND_MODEL (model)->n_items;
+}
+
+static GType
+gbp_shellcmd_command_model_get_item_type (GListModel *model)
+{
+ return IDE_TYPE_RUN_COMMAND;
+}
+
+static void
+list_model_iface_init (GListModelInterface *iface)
+{
+ iface->get_n_items = gbp_shellcmd_command_model_get_n_items;
+ iface->get_item = gbp_shellcmd_command_model_get_item;
+ iface->get_item_type = gbp_shellcmd_command_model_get_item_type;
+}
+
+G_DEFINE_FINAL_TYPE_WITH_CODE (GbpShellcmdCommandModel, gbp_shellcmd_command_model, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+gbp_shellcmd_command_model_replace (GbpShellcmdCommandModel *self,
+ char **commands)
+{
+ guint old_len;
+ guint new_len;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND_MODEL (self));
+ g_assert (self->ids != NULL);
+ g_assert (commands != NULL);
+
+ if (g_strv_equal ((const char * const *)self->ids,
+ (const char * const *)commands))
+ {
+ g_strfreev (commands);
+ return;
+ }
+
+ old_len = g_strv_length (self->ids);
+ new_len = g_strv_length (commands);
+
+ for (guint i = 0; i < self->n_items; i++)
+ {
+ if (!g_strv_contains ((const char * const *)commands, self->ids[i]))
+ g_hash_table_remove (self->id_to_command, self->ids[i]);
+ }
+
+ g_strfreev (self->ids);
+ self->ids = g_steal_pointer (&commands);
+ self->n_items = new_len;
+
+ g_list_model_items_changed (G_LIST_MODEL (self), 0, old_len, new_len);
+
+ if (old_len != new_len)
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_N_ITEMS]);
+}
+
+static void
+gbp_shellcmd_command_model_settings_changed_cb (GbpShellcmdCommandModel *self,
+ const char *key,
+ GSettings *settings)
+{
+ g_auto(GStrv) commands = NULL;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND_MODEL (self));
+ g_assert (ide_str_equal0 (key, self->key));
+ g_assert (G_IS_SETTINGS (settings));
+
+ commands = g_settings_get_strv (settings, key);
+
+ gbp_shellcmd_command_model_replace (self, g_steal_pointer (&commands));
+}
+
+static void
+gbp_shellcmd_command_model_constructed (GObject *object)
+{
+ GbpShellcmdCommandModel *self = (GbpShellcmdCommandModel *)object;
+ g_autofree char *signal_name = NULL;
+ g_auto(GStrv) commands = NULL;
+
+ G_OBJECT_CLASS (gbp_shellcmd_command_model_parent_class)->constructed (object);
+
+ g_assert (self->key != NULL);
+ g_assert (G_IS_SETTINGS (self->settings));
+
+ signal_name = g_strconcat ("changed::", self->key, NULL);
+
+ g_signal_connect_object (self->settings,
+ signal_name,
+ G_CALLBACK (gbp_shellcmd_command_model_settings_changed_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ self->ids = g_settings_get_strv (self->settings, self->key);
+ self->n_items = g_strv_length (self->ids);
+}
+
+static void
+gbp_shellcmd_command_model_dispose (GObject *object)
+{
+ GbpShellcmdCommandModel *self = (GbpShellcmdCommandModel *)object;
+
+ g_clear_pointer (&self->key, g_free);
+ g_clear_pointer (&self->id_to_command, g_hash_table_unref);
+ g_clear_pointer (&self->ids, g_strfreev);
+
+ g_clear_object (&self->settings);
+
+ G_OBJECT_CLASS (gbp_shellcmd_command_model_parent_class)->dispose (object);
+}
+
+static void
+gbp_shellcmd_command_model_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GbpShellcmdCommandModel *self = GBP_SHELLCMD_COMMAND_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_KEY:
+ g_value_set_string (value, self->key);
+ break;
+
+ case PROP_SETTINGS:
+ g_value_set_object (value, self->settings);
+ break;
+
+ case PROP_N_ITEMS:
+ g_value_set_uint (value, self->n_items);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gbp_shellcmd_command_model_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GbpShellcmdCommandModel *self = GBP_SHELLCMD_COMMAND_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_KEY:
+ self->key = g_value_dup_string (value);
+ break;
+
+ case PROP_SETTINGS:
+ self->settings = g_value_dup_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gbp_shellcmd_command_model_class_init (GbpShellcmdCommandModelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = gbp_shellcmd_command_model_constructed;
+ object_class->dispose = gbp_shellcmd_command_model_dispose;
+ object_class->get_property = gbp_shellcmd_command_model_get_property;
+ object_class->set_property = gbp_shellcmd_command_model_set_property;
+
+ properties [PROP_KEY] =
+ g_param_spec_string ("key", NULL, NULL,
+ NULL,
+ (G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_SETTINGS] =
+ g_param_spec_object ("settings", NULL, NULL,
+ G_TYPE_SETTINGS,
+ (G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_N_ITEMS] =
+ g_param_spec_uint ("n-items", NULL, NULL,
+ 0, G_MAXUINT, 0,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+gbp_shellcmd_command_model_init (GbpShellcmdCommandModel *self)
+{
+ self->id_to_command = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+}
+
+GbpShellcmdCommandModel *
+gbp_shellcmd_command_model_new (GSettings *settings,
+ const char *key)
+{
+ g_autoptr(GSettingsSchema) schema = NULL;
+
+ g_return_val_if_fail (G_SETTINGS (settings), NULL);
+ g_return_val_if_fail (key != NULL, NULL);
+
+ g_object_get (settings,
+ "settings-schema", &schema,
+ NULL);
+
+ g_return_val_if_fail (schema != NULL, NULL);
+ g_return_val_if_fail (g_settings_schema_has_key (schema, key), NULL);
+
+ return g_object_new (GBP_TYPE_SHELLCMD_COMMAND_MODEL,
+ "settings", settings,
+ "key", key,
+ NULL);
+}
diff --git a/src/plugins/shellcmd/gbp-shellcmd-command-model.h
b/src/plugins/shellcmd/gbp-shellcmd-command-model.h
new file mode 100644
index 000000000..201c739e0
--- /dev/null
+++ b/src/plugins/shellcmd/gbp-shellcmd-command-model.h
@@ -0,0 +1,34 @@
+/* gbp-shellcmd-command-model.h
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_SHELLCMD_COMMAND_MODEL (gbp_shellcmd_command_model_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpShellcmdCommandModel, gbp_shellcmd_command_model, GBP, SHELLCMD_COMMAND_MODEL,
GObject)
+
+GbpShellcmdCommandModel *gbp_shellcmd_command_model_new (GSettings *settings,
+ const char *key);
+
+G_END_DECLS
diff --git a/src/plugins/shellcmd/gbp-shellcmd-run-command-provider.c
b/src/plugins/shellcmd/gbp-shellcmd-run-command-provider.c
index 0193b3620..72e4a393c 100644
--- a/src/plugins/shellcmd/gbp-shellcmd-run-command-provider.c
+++ b/src/plugins/shellcmd/gbp-shellcmd-run-command-provider.c
@@ -25,6 +25,7 @@
#include <libide-threading.h>
+#include "gbp-shellcmd-command-model.h"
#include "gbp-shellcmd-run-command.h"
#include "gbp-shellcmd-run-command-provider.h"
@@ -33,41 +34,17 @@ struct _GbpShellcmdRunCommandProvider
IdeObject parent_instance;
};
-static void
-gbp_shellcmd_run_command_provider_populate (GListStore *store,
- const char *settings_path)
-{
- g_autoptr(GSettings) settings = NULL;
- g_auto(GStrv) run_commands = NULL;
-
- IDE_ENTRY;
-
- g_assert (G_IS_LIST_STORE (store));
- g_assert (settings_path != NULL);
-
- IDE_TRACE_MSG ("Adding commands to GListStore %p from %s", store, settings_path);
-
- settings = g_settings_new_with_path ("org.gnome.builder.shellcmd", settings_path);
- run_commands = g_settings_get_strv (settings, "run-commands");
-
- for (guint i = 0; run_commands[i]; i++)
- {
- g_autofree char *run_settings_path = g_strconcat (settings_path, run_commands[i], "/", NULL);
- g_autoptr(GbpShellcmdRunCommand) run_command = gbp_shellcmd_run_command_new (run_settings_path);
-
- g_list_store_append (store, run_command);
- }
-
- IDE_EXIT;
-}
-
static void
gbp_shellcmd_run_command_provider_list_commands_async (IdeRunCommandProvider *provider,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
+ g_autoptr(GbpShellcmdCommandModel) app_commands = NULL;
+ g_autoptr(GbpShellcmdCommandModel) project_commands = NULL;
g_autoptr(GListStore) store = NULL;
+ g_autoptr(GSettings) app_settings = NULL;
+ g_autoptr(GSettings) project_settings = NULL;
g_autoptr(IdeTask) task = NULL;
g_autofree char *project_id = NULL;
g_autofree char *project_settings_path = NULL;
@@ -81,18 +58,25 @@ gbp_shellcmd_run_command_provider_list_commands_async (IdeRunCommandProvider *pr
task = ide_task_new (provider, cancellable, callback, user_data);
ide_task_set_source_tag (task, gbp_shellcmd_run_command_provider_list_commands_async);
- store = g_list_store_new (IDE_TYPE_RUN_COMMAND);
-
- /* Add project shell commands so they resolve first */
context = ide_object_get_context (IDE_OBJECT (provider));
project_id = ide_context_dup_project_id (context);
project_settings_path = g_strconcat (SHELLCMD_SETTINGS_BASE, "projects/", project_id, "/", NULL);
- gbp_shellcmd_run_command_provider_populate (store, project_settings_path);
- /* Then application-wide commands for lower priority */
- gbp_shellcmd_run_command_provider_populate (store, SHELLCMD_SETTINGS_BASE);
+ app_settings = g_settings_new_with_path ("org.gnome.builder.shellcmd", SHELLCMD_SETTINGS_BASE);
+ project_settings = g_settings_new_with_path ("org.gnome.builder.shellcmd", project_settings_path);
+
+ app_commands = gbp_shellcmd_command_model_new (app_settings, "run-commands");
+ project_commands = gbp_shellcmd_command_model_new (project_settings, "run-commands");
+
+ store = g_list_store_new (G_TYPE_LIST_MODEL);
+ g_list_store_append (store, project_commands);
+ g_list_store_append (store, app_commands);
- ide_task_return_pointer (task, g_steal_pointer (&store), g_object_unref);
+ ide_task_return_pointer (task,
+ g_object_new (GTK_TYPE_FLATTEN_LIST_MODEL,
+ "model", store,
+ NULL),
+ g_object_unref);
IDE_EXIT;
}
diff --git a/src/plugins/shellcmd/meson.build b/src/plugins/shellcmd/meson.build
index 75abde37c..17fa70330 100644
--- a/src/plugins/shellcmd/meson.build
+++ b/src/plugins/shellcmd/meson.build
@@ -2,6 +2,7 @@ if get_option('plugin_shellcmd')
plugins_sources += files([
'shellcmd-plugin.c',
+ 'gbp-shellcmd-command-model.c',
'gbp-shellcmd-list.c',
'gbp-shellcmd-preferences-addin.c',
'gbp-shellcmd-run-command.c',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]