[gnome-builder] plugins/shellcmd: port to GTK 4 and run commands
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] plugins/shellcmd: port to GTK 4 and run commands
- Date: Tue, 12 Jul 2022 06:39:17 +0000 (UTC)
commit 2c666f0d8cc491375f5c5a7824698399a0c770bc
Author: Christian Hergert <chergert redhat com>
Date: Mon Jul 11 23:01:48 2022 -0700
plugins/shellcmd: port to GTK 4 and run commands
- Remove dazzle usage
- Port to GTK 4 API
- Port to new shortcut provider API
- Use IdeRunCommandPrivate and IdeRunCommand
- Add preferences addin to edit commands
- Implement locality support
.../shellcmd/gbp-shellcmd-application-addin.c | 91 --
src/plugins/shellcmd/gbp-shellcmd-command-dialog.c | 626 +++++++++++
...ation-addin.h => gbp-shellcmd-command-dialog.h} | 17 +-
.../shellcmd/gbp-shellcmd-command-dialog.ui | 272 +++++
src/plugins/shellcmd/gbp-shellcmd-command-editor.c | 286 -----
src/plugins/shellcmd/gbp-shellcmd-command-editor.h | 37 -
.../shellcmd/gbp-shellcmd-command-editor.ui | 209 ----
src/plugins/shellcmd/gbp-shellcmd-command-model.c | 543 +++++-----
src/plugins/shellcmd/gbp-shellcmd-command-model.h | 26 +-
.../shellcmd/gbp-shellcmd-command-provider.c | 309 ------
src/plugins/shellcmd/gbp-shellcmd-command-row.c | 93 --
src/plugins/shellcmd/gbp-shellcmd-command-row.h | 36 -
src/plugins/shellcmd/gbp-shellcmd-command-row.ui | 31 -
src/plugins/shellcmd/gbp-shellcmd-command.c | 1131 --------------------
src/plugins/shellcmd/gbp-shellcmd-command.h | 70 --
src/plugins/shellcmd/gbp-shellcmd-list.c | 186 ----
.../shellcmd/gbp-shellcmd-preferences-addin.c | 253 +++--
.../shellcmd/gbp-shellcmd-preferences-addin.h | 2 +-
.../shellcmd/gbp-shellcmd-run-command-provider.c | 109 ++
...-list.h => gbp-shellcmd-run-command-provider.h} | 14 +-
src/plugins/shellcmd/gbp-shellcmd-run-command.c | 416 +++++++
src/plugins/shellcmd/gbp-shellcmd-run-command.h | 57 +
.../shellcmd/gbp-shellcmd-shortcut-provider.c | 265 +++++
...provider.h => gbp-shellcmd-shortcut-provider.h} | 10 +-
src/plugins/shellcmd/meson.build | 20 +-
.../org.gnome.builder.shellcmd.command.gschema.xml | 35 +
.../org.gnome.builder.shellcmd.gschema.xml | 10 +
src/plugins/shellcmd/shellcmd-plugin.c | 22 +-
src/plugins/shellcmd/shellcmd.gresource.xml | 3 +-
src/plugins/shellcmd/shellcmd.plugin | 6 +-
30 files changed, 2269 insertions(+), 2916 deletions(-)
---
diff --git a/src/plugins/shellcmd/gbp-shellcmd-command-dialog.c
b/src/plugins/shellcmd/gbp-shellcmd-command-dialog.c
new file mode 100644
index 000000000..872069c4d
--- /dev/null
+++ b/src/plugins/shellcmd/gbp-shellcmd-command-dialog.c
@@ -0,0 +1,626 @@
+/* gbp-shellcmd-command-dialog.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-dialog"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include <libide-gtk.h>
+
+#include "gbp-shellcmd-command-dialog.h"
+#include "gbp-shellcmd-enums.h"
+
+struct _GbpShellcmdCommandDialog
+{
+ AdwWindow parent_instance;
+
+ GbpShellcmdRunCommand *command;
+
+ AdwEntryRow *argv;
+ AdwEntryRow *location;
+ AdwEntryRow *name;
+ AdwComboRow *locality;
+ GtkStringList *envvars;
+ GtkListBox *envvars_list_box;
+ GtkLabel *shortcut_label;
+ GtkButton *save;
+ GtkButton *delete_button;
+
+ char *accel;
+
+ guint delete_on_cancel : 1;
+};
+
+enum {
+ PROP_0,
+ PROP_COMMAND,
+ PROP_DELETE_ON_CANCEL,
+ N_PROPS
+};
+
+G_DEFINE_FINAL_TYPE (GbpShellcmdCommandDialog, gbp_shellcmd_command_dialog, ADW_TYPE_WINDOW)
+
+static GParamSpec *properties [N_PROPS];
+
+static char **
+string_list_to_strv (GtkStringList *strlist)
+{
+ g_autoptr(GStrvBuilder) builder = g_strv_builder_new ();
+ GListModel *model = G_LIST_MODEL (strlist);
+ guint n_items = g_list_model_get_n_items (model);
+
+ for (guint i = 0; i < n_items; i++)
+ {
+ g_autoptr(GtkStringObject) strobj = g_list_model_get_item (model, i);
+ const char *str = gtk_string_object_get_string (strobj);
+
+ g_strv_builder_add (builder, str);
+ }
+
+ return g_strv_builder_end (builder);
+}
+
+static void
+delete_envvar_cb (GbpShellcmdCommandDialog *self,
+ GtkButton *button)
+{
+ const char *envvar;
+ guint n_items;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND_DIALOG (self));
+ g_assert (GTK_IS_BUTTON (button));
+
+ envvar = g_object_get_data (G_OBJECT (button), "ENVVAR");
+ n_items = g_list_model_get_n_items (G_LIST_MODEL (self->envvars));
+
+ for (guint i = 0; i < n_items; i++)
+ {
+ g_autoptr(GtkStringObject) str = g_list_model_get_item (G_LIST_MODEL (self->envvars), i);
+
+ if (g_strcmp0 (envvar, gtk_string_object_get_string (str)) == 0)
+ {
+ gtk_string_list_remove (self->envvars, i);
+ break;
+ }
+ }
+}
+
+static GtkWidget *
+create_envvar_row_cb (gpointer item,
+ gpointer user_data)
+{
+ GbpShellcmdCommandDialog *self = user_data;
+ GtkStringObject *obj = item;
+ const char *str = gtk_string_object_get_string (obj);
+ g_autofree char *markup = NULL;
+ g_autofree char *escaped = NULL;
+ AdwActionRow *row;
+ GtkButton *button;
+
+ escaped = g_markup_escape_text (str, -1);
+ markup = g_strdup_printf ("<tt>%s</tt>", escaped);
+ row = g_object_new (ADW_TYPE_ACTION_ROW,
+ "title", markup,
+ "title-selectable", TRUE,
+ NULL);
+ button = g_object_new (GTK_TYPE_BUTTON,
+ "icon-name", "list-remove-symbolic",
+ "css-classes", IDE_STRV_INIT ("flat", "circular"),
+ "valign", GTK_ALIGN_CENTER,
+ NULL);
+ g_object_set_data_full (G_OBJECT (button),
+ "ENVVAR",
+ g_strdup (str),
+ g_free);
+ g_signal_connect_object (button,
+ "clicked",
+ G_CALLBACK (delete_envvar_cb),
+ self,
+ G_CONNECT_SWAPPED);
+ adw_action_row_add_suffix (row, GTK_WIDGET (button));
+
+ return GTK_WIDGET (row);
+}
+
+static void
+on_env_entry_changed_cb (GbpShellcmdCommandDialog *self,
+ IdeEntryPopover *popover)
+{
+ gboolean valid = FALSE;
+ const char *text;
+ const char *eq;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND_DIALOG (self));
+ g_assert (IDE_IS_ENTRY_POPOVER (popover));
+
+ text = ide_entry_popover_get_text (popover);
+ eq = strchr (text, '=');
+
+ if (eq != NULL && eq != text)
+ {
+ for (const char *iter = text; iter < eq; iter = g_utf8_next_char (iter))
+ {
+ gunichar ch = g_utf8_get_char (iter);
+
+ if (!g_unichar_isalnum (ch) && ch != '_')
+ goto failure;
+ }
+
+ if (g_ascii_isalpha (*text))
+ valid = TRUE;
+ }
+
+failure:
+ ide_entry_popover_set_ready (popover, valid);
+}
+
+static void
+on_env_entry_activate_cb (GbpShellcmdCommandDialog *self,
+ const char *text,
+ IdeEntryPopover *popover)
+{
+ IDE_ENTRY;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND_DIALOG (self));
+ g_assert (IDE_IS_ENTRY_POPOVER (popover));
+ g_assert (GTK_IS_STRING_LIST (self->envvars));
+
+ gtk_string_list_append (self->envvars, text);
+ ide_entry_popover_set_text (popover, "");
+
+ IDE_EXIT;
+}
+
+static char *
+normalize_argv (const char * const *argv)
+{
+ g_autofree char *joined = NULL;
+ g_auto(GStrv) parsed = NULL;
+ int argc;
+
+ if (argv == NULL || argv[0] == NULL)
+ return g_strdup ("");
+
+ /* The goal here is to only quote the argv if the string would
+ * parse back differently than it's initial form.
+ */
+ joined = g_strjoinv (" ", (char **)argv);
+ if (!g_shell_parse_argv (joined, &argc, &parsed, NULL) ||
+ !g_strv_equal ((const char * const *)parsed, argv))
+ {
+ GString *str = g_string_new (NULL);
+
+ for (guint i = 0; argv[i]; i++)
+ {
+ g_autofree char *quoted = g_shell_quote (argv[i]);
+
+ if (str->len > 0)
+ g_string_append_c (str, ' ');
+ g_string_append (str, quoted);
+ }
+
+ return g_string_free (str, FALSE);
+ }
+
+ return g_steal_pointer (&joined);
+}
+
+static void
+set_accel (GbpShellcmdCommandDialog *self,
+ const char *accel)
+{
+ g_autofree char *label = NULL;
+ guint keyval;
+ GdkModifierType state;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND_DIALOG (self));
+
+ if (ide_str_equal0 (self->accel, accel))
+ return;
+
+ g_free (self->accel);
+ self->accel = g_strdup (accel);
+
+ if (accel && gtk_accelerator_parse (accel, &keyval, &state))
+ label = gtk_accelerator_get_label (keyval, state);
+
+ gtk_label_set_label (self->shortcut_label, label);
+}
+
+static void
+gbp_shellcmd_command_dialog_set_command (GbpShellcmdCommandDialog *self,
+ GbpShellcmdRunCommand *command)
+{
+ g_autofree char *argvstr = NULL;
+ GbpShellcmdLocality locality;
+ const char * const *argv;
+ const char * const *env;
+ const char *accel;
+ const char *name;
+ const char *cwd;
+
+ IDE_ENTRY;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND_DIALOG (self));
+ g_assert (!command || GBP_IS_SHELLCMD_RUN_COMMAND (command));
+
+ if (!g_set_object (&self->command, command))
+ IDE_EXIT;
+
+ name = ide_run_command_get_display_name (IDE_RUN_COMMAND (command));
+ argv = ide_run_command_get_argv (IDE_RUN_COMMAND (command));
+ env = ide_run_command_get_environ (IDE_RUN_COMMAND (command));
+ cwd = ide_run_command_get_cwd (IDE_RUN_COMMAND (command));
+ accel = gbp_shellcmd_run_command_get_accelerator (command);
+ locality = gbp_shellcmd_run_command_get_locality (command);
+
+ argvstr = normalize_argv (argv);
+
+ gtk_editable_set_text (GTK_EDITABLE (self->argv), argvstr);
+ gtk_editable_set_text (GTK_EDITABLE (self->location), cwd);
+ gtk_editable_set_text (GTK_EDITABLE (self->name), name);
+ set_accel (self, accel);
+
+ /* locality value equates to position in list model for simplicity */
+ adw_combo_row_set_selected (self->locality, locality);
+
+ if (env != NULL)
+ {
+ for (guint i = 0; env[i]; i++)
+ gtk_string_list_append (self->envvars, env[i]);
+ }
+
+ IDE_EXIT;
+}
+
+static void
+on_shortcut_dialog_respnose (GbpShellcmdCommandDialog *self,
+ int response_id,
+ IdeShortcutAccelDialog *dialog)
+{
+ IDE_ENTRY;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND_DIALOG (self));
+ g_assert (IDE_IS_SHORTCUT_ACCEL_DIALOG (dialog));
+
+ if (response_id == GTK_RESPONSE_ACCEPT)
+ {
+ const char *accel;
+
+ accel = ide_shortcut_accel_dialog_get_accelerator (dialog);
+ set_accel (self, accel);
+ }
+
+ gtk_window_destroy (GTK_WINDOW (dialog));
+
+ IDE_EXIT;
+}
+
+static void
+on_shortcut_activated_cb (GbpShellcmdCommandDialog *self,
+ AdwActionRow *shortcut_row)
+{
+ IdeShortcutAccelDialog *dialog;
+ const char *name;
+
+ IDE_ENTRY;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND_DIALOG (self));
+ g_assert (ADW_IS_ACTION_ROW (shortcut_row));
+
+ name = gtk_editable_get_text (GTK_EDITABLE (self->name));
+ if (ide_str_empty0 (name))
+ name = _("Untitled Command");
+
+ dialog = g_object_new (IDE_TYPE_SHORTCUT_ACCEL_DIALOG,
+ "accelerator", self->accel,
+ "transient-for", self,
+ "modal", TRUE,
+ "shortcut-title", name,
+ "title", _("Set Shortcut"),
+ "use-header-bar", 1,
+ NULL);
+ g_signal_connect_object (dialog,
+ "response",
+ G_CALLBACK (on_shortcut_dialog_respnose),
+ self,
+ G_CONNECT_SWAPPED);
+ gtk_window_present (GTK_WINDOW (dialog));
+
+ IDE_EXIT;
+}
+
+static void
+command_delete_action (GtkWidget *widget,
+ const char *action_name,
+ GVariant *param)
+{
+ GbpShellcmdCommandDialog *self = (GbpShellcmdCommandDialog *)widget;
+
+ IDE_ENTRY;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND_DIALOG (self));
+
+ gbp_shellcmd_run_command_delete (self->command);
+
+ gtk_window_destroy (GTK_WINDOW (self));
+
+ IDE_EXIT;
+}
+
+static void
+command_cancel_action (GtkWidget *widget,
+ const char *action_name,
+ GVariant *param)
+{
+ GbpShellcmdCommandDialog *self = (GbpShellcmdCommandDialog *)widget;
+
+ IDE_ENTRY;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND_DIALOG (self));
+
+ if (self->delete_on_cancel)
+ gbp_shellcmd_run_command_delete (self->command);
+
+ gtk_window_destroy (GTK_WINDOW (self));
+
+ IDE_EXIT;
+}
+
+static void
+command_save_action (GtkWidget *widget,
+ const char *action_name,
+ GVariant *param)
+{
+ GbpShellcmdCommandDialog *self = (GbpShellcmdCommandDialog *)widget;
+ g_autoptr(GEnumClass) enum_class = NULL;
+ g_auto(GStrv) argv = NULL;
+ g_auto(GStrv) env = NULL;
+ const char *argvstr;
+ IdeEnumObject *item;
+ const char *nick;
+ GEnumValue *value;
+ int argc;
+
+ IDE_ENTRY;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND_DIALOG (self));
+
+ g_object_freeze_notify (G_OBJECT (self->command));
+
+ argvstr = gtk_editable_get_text (GTK_EDITABLE (self->argv));
+ if (g_shell_parse_argv (argvstr, &argc, &argv, NULL))
+ ide_run_command_set_argv (IDE_RUN_COMMAND (self->command), (const char * const *)argv);
+
+ ide_run_command_set_display_name (IDE_RUN_COMMAND (self->command),
+ gtk_editable_get_text (GTK_EDITABLE (self->name)));
+ ide_run_command_set_cwd (IDE_RUN_COMMAND (self->command),
+ gtk_editable_get_text (GTK_EDITABLE (self->location)));
+ gbp_shellcmd_run_command_set_accelerator (self->command, self->accel);
+
+ env = string_list_to_strv (self->envvars);
+ ide_run_command_set_environ (IDE_RUN_COMMAND (self->command),
+ (const char * const *)env);
+
+ item = adw_combo_row_get_selected_item (self->locality);
+ nick = ide_enum_object_get_nick (item);
+ enum_class = g_type_class_ref (GBP_TYPE_SHELLCMD_LOCALITY);
+ value = g_enum_get_value_by_nick (enum_class, nick);
+ gbp_shellcmd_run_command_set_locality (self->command, value->value);
+
+ g_object_thaw_notify (G_OBJECT (self->command));
+
+ gtk_window_destroy (GTK_WINDOW (self));
+
+ IDE_EXIT;
+}
+
+static void
+select_folder_response_cb (GtkFileChooserNative *native,
+ int response_id,
+ AdwEntryRow *row)
+{
+ g_assert (ADW_IS_ENTRY_ROW (row));
+ g_assert (GTK_IS_FILE_CHOOSER_NATIVE (native));
+
+ if (response_id == GTK_RESPONSE_ACCEPT)
+ {
+ g_autoptr(GFile) file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (native));
+ g_autofree char *path = ide_path_collapse (g_file_peek_path (file));
+
+ gtk_editable_set_text (GTK_EDITABLE (row), path);
+ }
+
+ gtk_native_dialog_destroy (GTK_NATIVE_DIALOG (native));
+}
+
+static void
+select_folder_action (GtkWidget *widget,
+ const char *action_name,
+ GVariant *param)
+{
+ GbpShellcmdCommandDialog *self = (GbpShellcmdCommandDialog *)widget;
+ GtkFileChooserNative *native;
+ g_autofree char *expanded = NULL;
+ g_autoptr(GFile) file = NULL;
+ const char *cwd;
+ GtkRoot *root;
+
+ g_assert (GBP_IS_SHELLCMD_COMMAND_DIALOG (self));
+
+ cwd = gtk_editable_get_text (GTK_EDITABLE (self->location));
+ expanded = ide_path_expand (cwd);
+ file = g_file_new_for_path (expanded);
+
+ root = gtk_widget_get_root (widget);
+ native = gtk_file_chooser_native_new (_("Select Working Directory"),
+ GTK_WINDOW (root),
+ GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
+ _("Select"),
+ _("Cancel"));
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (native), file, NULL);
+ g_signal_connect_object (native,
+ "response",
+ G_CALLBACK (select_folder_response_cb),
+ self->location,
+ 0);
+ gtk_native_dialog_show (GTK_NATIVE_DIALOG (native));
+}
+
+static void
+gbp_shellcmd_command_dialog_dispose (GObject *object)
+{
+ GbpShellcmdCommandDialog *self = (GbpShellcmdCommandDialog *)object;
+
+ g_clear_object (&self->command);
+ g_clear_pointer (&self->accel, g_free);
+
+ G_OBJECT_CLASS (gbp_shellcmd_command_dialog_parent_class)->dispose (object);
+}
+
+static void
+gbp_shellcmd_command_dialog_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GbpShellcmdCommandDialog *self = GBP_SHELLCMD_COMMAND_DIALOG (object);
+
+ switch (prop_id)
+ {
+ case PROP_COMMAND:
+ g_value_set_object (value, self->command);
+ break;
+
+ case PROP_DELETE_ON_CANCEL:
+ g_value_set_boolean (value, self->delete_on_cancel);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gbp_shellcmd_command_dialog_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GbpShellcmdCommandDialog *self = GBP_SHELLCMD_COMMAND_DIALOG (object);
+
+ switch (prop_id)
+ {
+ case PROP_COMMAND:
+ gbp_shellcmd_command_dialog_set_command (self, g_value_get_object (value));
+ break;
+
+ case PROP_DELETE_ON_CANCEL:
+ self->delete_on_cancel = g_value_get_boolean (value);
+ if (self->delete_on_cancel)
+ {
+ gtk_window_set_title (GTK_WINDOW (self), _("Create Command"));
+ gtk_button_set_label (self->save, _("Cre_ate"));
+ gtk_widget_hide (GTK_WIDGET (self->delete_button));
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gbp_shellcmd_command_dialog_class_init (GbpShellcmdCommandDialogClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = gbp_shellcmd_command_dialog_dispose;
+ object_class->get_property = gbp_shellcmd_command_dialog_get_property;
+ object_class->set_property = gbp_shellcmd_command_dialog_set_property;
+
+ properties [PROP_COMMAND] =
+ g_param_spec_object ("command", NULL, NULL,
+ GBP_TYPE_SHELLCMD_RUN_COMMAND,
+ (G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_DELETE_ON_CANCEL] =
+ g_param_spec_boolean ("delete-on-cancel", NULL, NULL, FALSE,
+ (G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ gtk_widget_class_install_action (widget_class, "command.save", NULL, command_save_action);
+ gtk_widget_class_install_action (widget_class, "command.delete", NULL, command_delete_action);
+ gtk_widget_class_install_action (widget_class, "command.cancel", NULL, command_cancel_action);
+ gtk_widget_class_install_action (widget_class, "command.select-folder", NULL, select_folder_action);
+
+ gtk_widget_class_add_binding_action (widget_class, GDK_KEY_Escape, 0, "command.cancel", NULL);
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/plugins/shellcmd/gbp-shellcmd-command-dialog.ui");
+ gtk_widget_class_bind_template_child (widget_class, GbpShellcmdCommandDialog, argv);
+ gtk_widget_class_bind_template_child (widget_class, GbpShellcmdCommandDialog, delete_button);
+ gtk_widget_class_bind_template_child (widget_class, GbpShellcmdCommandDialog, envvars);
+ gtk_widget_class_bind_template_child (widget_class, GbpShellcmdCommandDialog, envvars_list_box);
+ gtk_widget_class_bind_template_child (widget_class, GbpShellcmdCommandDialog, locality);
+ gtk_widget_class_bind_template_child (widget_class, GbpShellcmdCommandDialog, location);
+ gtk_widget_class_bind_template_child (widget_class, GbpShellcmdCommandDialog, name);
+ gtk_widget_class_bind_template_child (widget_class, GbpShellcmdCommandDialog, save);
+ gtk_widget_class_bind_template_child (widget_class, GbpShellcmdCommandDialog, shortcut_label);
+ gtk_widget_class_bind_template_callback (widget_class, on_env_entry_changed_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_env_entry_activate_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_shortcut_activated_cb);
+}
+
+static void
+gbp_shellcmd_command_dialog_init (GbpShellcmdCommandDialog *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+#if DEVELOPMENT_BUILD
+ gtk_widget_add_css_class (GTK_WIDGET (self), "devel");
+#endif
+
+ gtk_list_box_bind_model (self->envvars_list_box,
+ G_LIST_MODEL (self->envvars),
+ create_envvar_row_cb,
+ self, NULL);
+ ide_gtk_widget_hide_when_empty (GTK_WIDGET (self->envvars_list_box),
+ G_LIST_MODEL (self->envvars));
+}
+
+GbpShellcmdCommandDialog *
+gbp_shellcmd_command_dialog_new (GbpShellcmdRunCommand *command,
+ gboolean delete_on_cancel)
+{
+ g_return_val_if_fail (GBP_IS_SHELLCMD_RUN_COMMAND (command), NULL);
+
+ return g_object_new (GBP_TYPE_SHELLCMD_COMMAND_DIALOG,
+ "command", command,
+ "delete-on-cancel", delete_on_cancel,
+ NULL);
+}
diff --git a/src/plugins/shellcmd/gbp-shellcmd-application-addin.h
b/src/plugins/shellcmd/gbp-shellcmd-command-dialog.h
similarity index 56%
rename from src/plugins/shellcmd/gbp-shellcmd-application-addin.h
rename to src/plugins/shellcmd/gbp-shellcmd-command-dialog.h
index dc29e3132..abefa6af3 100644
--- a/src/plugins/shellcmd/gbp-shellcmd-application-addin.h
+++ b/src/plugins/shellcmd/gbp-shellcmd-command-dialog.h
@@ -1,6 +1,6 @@
-/* gbp-shellcmd-application-addin.h
+/* gbp-shellcmd-command-dialog.h
*
- * Copyright 2019 Christian Hergert <chergert redhat com>
+ * 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
@@ -20,16 +20,19 @@
#pragma once
-#include <libide-gui.h>
+#include <adwaita.h>
-#include "gbp-shellcmd-command-model.h"
+#include <libide-foundry.h>
+
+#include "gbp-shellcmd-run-command.h"
G_BEGIN_DECLS
-#define GBP_TYPE_SHELLCMD_APPLICATION_ADDIN (gbp_shellcmd_application_addin_get_type())
+#define GBP_TYPE_SHELLCMD_COMMAND_DIALOG (gbp_shellcmd_command_dialog_get_type())
-G_DECLARE_FINAL_TYPE (GbpShellcmdApplicationAddin, gbp_shellcmd_application_addin, GBP,
SHELLCMD_APPLICATION_ADDIN, GObject)
+G_DECLARE_FINAL_TYPE (GbpShellcmdCommandDialog, gbp_shellcmd_command_dialog, GBP, SHELLCMD_COMMAND_DIALOG,
AdwWindow)
-GbpShellcmdCommandModel *gbp_shellcmd_application_addin_get_model (GbpShellcmdApplicationAddin *self);
+GbpShellcmdCommandDialog *gbp_shellcmd_command_dialog_new (GbpShellcmdRunCommand *command,
+ gboolean delete_on_cancel);
G_END_DECLS
diff --git a/src/plugins/shellcmd/gbp-shellcmd-command-dialog.ui
b/src/plugins/shellcmd/gbp-shellcmd-command-dialog.ui
new file mode 100644
index 000000000..d12a9f3e4
--- /dev/null
+++ b/src/plugins/shellcmd/gbp-shellcmd-command-dialog.ui
@@ -0,0 +1,272 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="GbpShellcmdCommandDialog" parent="AdwWindow">
+ <property name="default-width">650</property>
+ <property name="default-height">650</property>
+ <property name="title" translatable="yes">Edit Command</property>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="AdwHeaderBar">
+ <property name="show-start-title-buttons">false</property>
+ <property name="show-end-title-buttons">false</property>
+ <child type="start">
+ <object class="GtkButton" id="cancel">
+ <property name="label" translatable="yes">_Cancel</property>
+ <property name="use-underline">true</property>
+ <property name="action-name">command.cancel</property>
+ </object>
+ </child>
+ <child type="end">
+ <object class="GtkButton" id="save">
+ <property name="label" translatable="yes">S_ave</property>
+ <property name="action-name">command.save</property>
+ <property name="use-underline">true</property>
+ <style>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwPreferencesPage">
+ <property name="vexpand">true</property>
+ <child>
+ <object class="AdwPreferencesGroup">
+ <child>
+ <object class="AdwEntryRow" id="name">
+ <property name="title" translatable="yes">Name</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwPreferencesGroup">
+ <child>
+ <object class="AdwActionRow" id="shortcut_row">
+ <signal name="activated" handler="on_shortcut_activated_cb" swapped="true"
object="GbpShellcmdCommandDialog"/>
+ <property name="activatable">true</property>
+ <property name="title" translatable="yes">Keyboard Shortcut</property>
+ <property name="subtitle" translatable="yes">An optional shourtcut to run the
command</property>
+ <property name="icon-name">preferences-desktop-keyboard-shortcuts-symbolic</property>
+ <child type="suffix">
+ <object class="GtkLabel" id="shortcut_label">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwPreferencesGroup">
+ <child>
+ <object class="AdwEntryRow" id="argv">
+ <property name="title" translatable="yes">Shell Command</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">You may use single or double quotes for
parameters.</property>
+ <property name="use-markup">true</property>
+ <property name="margin-top">12</property>
+ <property name="wrap">true</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="caption"/>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Environment</property>
+ <child>
+ <object class="AdwEntryRow" id="location">
+ <property name="title" translatable="yes">Working Directory</property>
+ <child type="suffix">
+ <object class="GtkButton">
+ <property name="action-name">command.select-folder</property>
+ <property name="valign">center</property>
+ <property name="icon-name">folder-symbolic</property>
+ <style>
+ <class name="flat"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">The command will be run from this location.
Use <tt>$BUILDDIR</tt>, <tt>$SRCDIR</tt>, or <tt>$HOME</tt> to define a
relative path.</property>
+ <property name="use-markup">true</property>
+ <property name="margin-top">12</property>
+ <property name="wrap">true</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="caption"/>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwPreferencesGroup">
+ <child>
+ <object class="AdwComboRow" id="locality">
+ <property name="title" translatable="yes">Locality</property>
+ <property name="subtitle" translatable="yes">Builder can run your command from a number
of localities including the host system or build containers.</property>
+ <property name="model">localities</property>
+ <property name="expression">
+ <lookup name="title" type="IdeEnumObject"/>
+ </property>
+ <property name="list-factory">
+ <object class="GtkBuilderListItemFactory">
+ <property name="bytes"><![CDATA[
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="GtkListItem">
+ <property name="child">
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="xalign">0</property>
+ <property name="hexpand">true</property>
+ <binding name="label">
+ <lookup name="title" type="IdeEnumObject">
+ <lookup name="item">GtkListItem</lookup>
+ </lookup>
+ </binding>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="xalign">0</property>
+ <property name="hexpand">true</property>
+ <style>
+ <class name="caption"/>
+ <class name="dim-label"/>
+ </style>
+ <binding name="label">
+ <lookup name="description" type="IdeEnumObject">
+ <lookup name="item">GtkListItem</lookup>
+ </lookup>
+ </binding>
+ </object>
+ </child>
+ </object>
+ </property>
+ </template>
+</interface>
+]]>
+ </property>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwPreferencesGroup">
+ <child>
+ <object class="GtkListBox" id="envvars_list_box">
+ <property name="selection-mode">none</property>
+ <style>
+ <class name="boxed-list"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuButton">
+ <property name="direction">left</property>
+ <property name="margin-top">12</property>
+ <property name="halign">end</property>
+ <property name="child">
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Add _Variable</property>
+ <property name="use-underline">true</property>
+ </object>
+ </property>
+ <property name="popover">
+ <object class="IdeEntryPopover">
+ <property name="title" translatable="yes">Add Variable</property>
+ <property name="button-text" translatable="yes">_Add</property>
+ <signal name="changed" handler="on_env_entry_changed_cb" swapped="true"
object="GbpShellcmdCommandDialog"/>
+ <signal name="activate" handler="on_env_entry_activate_cb" swapped="true"
object="GbpShellcmdCommandDialog"/>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="vexpand">true</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="vexpand">true</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="delete_button">
+ <property name="label" translatable="yes">_Delete Command</property>
+ <property name="use-underline">true</property>
+ <property name="action-name">command.delete</property>
+ <property name="halign">end</property>
+ <style>
+ <class name="destructive-action"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkSizeGroup">
+ <widgets>
+ <widget name="save"/>
+ <widget name="cancel"/>
+ </widgets>
+ </object>
+ <object class="GtkStringList" id="envvars">
+ </object>
+ <object class="GListStore" id="localities">
+ <child>
+ <object class="IdeEnumObject">
+ <property name="nick">subprocess</property>
+ <property name="title" translatable="yes">Subprocess</property>
+ <property name="description" translatable="yes">Runs the command as a subprocess of
Builder.</property>
+ </object>
+ </child>
+ <child>
+ <object class="IdeEnumObject">
+ <property name="nick">host</property>
+ <property name="title" translatable="yes">Host System</property>
+ <property name="description" translatable="yes">Runs the command on the host system.</property>
+ </object>
+ </child>
+ <child>
+ <object class="IdeEnumObject">
+ <property name="nick">pipeline</property>
+ <property name="title" translatable="yes">Build Pipeline</property>
+ <property name="description" translatable="yes">Runs the command within the build
pipeline.</property>
+ </object>
+ </child>
+ <child>
+ <object class="IdeEnumObject">
+ <property name="nick">runtime</property>
+ <property name="title" translatable="yes">As Target Application</property>
+ <property name="description" translatable="yes">Runs the command as if it were the target
application.</property>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/src/plugins/shellcmd/gbp-shellcmd-command-model.c
b/src/plugins/shellcmd/gbp-shellcmd-command-model.c
index 00b849aec..91797163a 100644
--- a/src/plugins/shellcmd/gbp-shellcmd-command-model.c
+++ b/src/plugins/shellcmd/gbp-shellcmd-command-model.c
@@ -1,6 +1,6 @@
/* gbp-shellcmd-command-model.c
*
- * Copyright 2019 Christian Hergert <chergert redhat com>
+ * 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
@@ -22,414 +22,349 @@
#include "config.h"
-#include <glib/gstdio.h>
-#include <libide-core.h>
-#include <libide-sourceview.h>
-#include <libide-threading.h>
-
-#include "gbp-shellcmd-command.h"
#include "gbp-shellcmd-command-model.h"
+#include "gbp-shellcmd-run-command.h"
+
+#define SHELLCMD_SETTINGS_BASE "/org/gnome/builder/shellcmd/"
struct _GbpShellcmdCommandModel
{
- GObject parent_instance;
-
- GPtrArray *items;
- GKeyFile *keyfile;
-
- guint queue_save;
-
- guint keybindings_changed : 1;
+ GObject parent_instance;
+ GSettings *settings;
+ char *key;
+ GHashTable *id_to_command;
+ char **ids;
+ guint n_items;
};
-static void list_model_iface_init (GListModelInterface *iface);
-
-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))
-
enum {
- KEYBINDINGS_CHANGED,
- N_SIGNALS
+ PROP_0,
+ PROP_KEY,
+ PROP_SETTINGS,
+ PROP_N_ITEMS,
+ N_PROPS
};
-static guint signals [N_SIGNALS];
+static GParamSpec *properties [N_PROPS];
-static gboolean
-gbp_shellcmd_command_model_queue_save_cb (gpointer data)
+static gpointer
+gbp_shellcmd_command_model_get_item (GListModel *model,
+ guint position)
{
- GbpShellcmdCommandModel *self = data;
- g_autoptr(GError) error = NULL;
+ GbpShellcmdCommandModel *self = (GbpShellcmdCommandModel *)model;
+ GbpShellcmdRunCommand *command;
+ const char *id;
g_assert (GBP_IS_SHELLCMD_COMMAND_MODEL (self));
- self->queue_save = 0;
+ if (position >= self->n_items)
+ return NULL;
- if (!gbp_shellcmd_command_model_save (self, NULL, &error))
- g_warning ("Failed to save external-commands: %s", error->message);
+ id = self->ids[position];
+ command = g_hash_table_lookup (self->id_to_command, id);
- /* Now ask everything to reload (as we might have new keybindings) */
- if (self->keybindings_changed)
+ if (command == NULL)
{
- g_signal_emit (self, signals [KEYBINDINGS_CHANGED], 0);
- self->keybindings_changed = FALSE;
+ 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_SOURCE_REMOVE;
+ return g_object_ref (command);
}
-static void
-gbp_shellcmd_command_model_queue_save (GbpShellcmdCommandModel *self)
+static guint
+gbp_shellcmd_command_model_get_n_items (GListModel *model)
{
- g_assert (GBP_IS_SHELLCMD_COMMAND_MODEL (self));
-
- g_object_ref (self);
-
- if (self->queue_save != 0)
- g_source_remove (self->queue_save);
-
- self->queue_save =
- g_timeout_add_seconds_full (G_PRIORITY_HIGH,
- 1,
- gbp_shellcmd_command_model_queue_save_cb,
- g_object_ref (self),
- g_object_unref);
-
- g_object_unref (self);
+ return GBP_SHELLCMD_COMMAND_MODEL (model)->n_items;
}
-static void
-on_command_changed_cb (GbpShellcmdCommandModel *self,
- GbpShellcmdCommand *command)
+static GType
+gbp_shellcmd_command_model_get_item_type (GListModel *model)
{
- g_assert (GBP_SHELLCMD_COMMAND_MODEL (self));
- g_assert (GBP_SHELLCMD_COMMAND (command));
-
- gbp_shellcmd_command_model_queue_save (self);
+ return IDE_TYPE_RUN_COMMAND;
}
static void
-on_command_shortcut_changed_cb (GbpShellcmdCommandModel *self,
- GParamSpec *pspec,
- GbpShellcmdCommand *command)
+list_model_iface_init (GListModelInterface *iface)
{
- g_assert (GBP_SHELLCMD_COMMAND_MODEL (self));
- g_assert (GBP_SHELLCMD_COMMAND (command));
-
- self->keybindings_changed = TRUE;
+ 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 void
-gbp_shellcmd_command_model_finalize (GObject *object)
+gbp_shellcmd_command_model_replace (GbpShellcmdCommandModel *self,
+ char **commands)
{
- GbpShellcmdCommandModel *self = (GbpShellcmdCommandModel *)object;
+ g_auto(GStrv) old_ids = NULL;
+ guint old_len;
- g_clear_pointer (&self->items, g_ptr_array_unref);
- g_clear_pointer (&self->keyfile, g_key_file_free);
+ g_assert (GBP_IS_SHELLCMD_COMMAND_MODEL (self));
+ g_assert (self->ids != NULL);
+ g_assert (commands != NULL);
- G_OBJECT_CLASS (gbp_shellcmd_command_model_parent_class)->finalize (object);
-}
+ if (g_strv_equal ((const char * const *)self->ids,
+ (const char * const *)commands))
+ {
+ g_strfreev (commands);
+ return;
+ }
-static void
-gbp_shellcmd_command_model_class_init (GbpShellcmdCommandModelClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ old_ids = g_steal_pointer (&self->ids);
+ old_len = self->n_items;
- object_class->finalize = gbp_shellcmd_command_model_finalize;
+ self->ids = g_steal_pointer (&commands);
+ self->n_items = g_strv_length (self->ids);
- signals [KEYBINDINGS_CHANGED] =
- g_signal_new ("keybindings-changed",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
-}
+ g_assert (g_strv_length (old_ids) == old_len);
+ g_assert (g_strv_length (self->ids) == self->n_items);
+ g_assert (g_hash_table_size (self->id_to_command) <= old_len);
-static void
-gbp_shellcmd_command_model_init (GbpShellcmdCommandModel *self)
-{
- self->items = g_ptr_array_new_with_free_func ((GDestroyNotify)ide_object_unref_and_destroy);
- self->keyfile = g_key_file_new ();
-}
+ for (guint i = 0; old_ids[i]; i++)
+ {
+ if (!g_strv_contains ((const char * const *)self->ids, old_ids[i]))
+ g_hash_table_remove (self->id_to_command, old_ids[i]);
+ }
-GbpShellcmdCommandModel *
-gbp_shellcmd_command_model_new (void)
-{
- return g_object_new (GBP_TYPE_SHELLCMD_COMMAND_MODEL, NULL);
-}
+ g_assert (g_hash_table_size (self->id_to_command) <= self->n_items);
-static GType
-gbp_shellcmd_command_model_get_item_type (GListModel *model)
-{
- return GBP_TYPE_SHELLCMD_COMMAND;
+ g_list_model_items_changed (G_LIST_MODEL (self), 0, old_len, self->n_items);
+
+ if (old_len != self->n_items)
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_N_ITEMS]);
}
-static gpointer
-gbp_shellcmd_command_model_get_item (GListModel *model,
- guint position)
+static void
+gbp_shellcmd_command_model_settings_changed_cb (GbpShellcmdCommandModel *self,
+ const char *key,
+ GSettings *settings)
{
- GbpShellcmdCommandModel *self = GBP_SHELLCMD_COMMAND_MODEL (model);
+ g_auto(GStrv) commands = NULL;
g_assert (GBP_IS_SHELLCMD_COMMAND_MODEL (self));
- g_assert (position < self->items->len);
+ g_assert (ide_str_equal0 (key, self->key));
+ g_assert (G_IS_SETTINGS (settings));
+
+ commands = g_settings_get_strv (settings, self->key);
- return g_object_ref (g_ptr_array_index (self->items, position));
+ gbp_shellcmd_command_model_replace (self, g_steal_pointer (&commands));
}
-static guint
-gbp_shellcmd_command_model_get_n_items (GListModel *model)
+static void
+gbp_shellcmd_command_model_constructed (GObject *object)
{
- GbpShellcmdCommandModel *self = GBP_SHELLCMD_COMMAND_MODEL (model);
+ GbpShellcmdCommandModel *self = (GbpShellcmdCommandModel *)object;
+ g_autofree char *signal_name = NULL;
- g_assert (GBP_IS_SHELLCMD_COMMAND_MODEL (self));
+ G_OBJECT_CLASS (gbp_shellcmd_command_model_parent_class)->constructed (object);
- return self->items->len;
-}
+ g_assert (self->key != NULL);
+ g_assert (G_IS_SETTINGS (self->settings));
-static void
-list_model_iface_init (GListModelInterface *iface)
-{
- iface->get_item = gbp_shellcmd_command_model_get_item;
- iface->get_item_type = gbp_shellcmd_command_model_get_item_type;
- iface->get_n_items = gbp_shellcmd_command_model_get_n_items;
-}
+ self->ids = g_settings_get_strv (self->settings, self->key);
+ self->n_items = g_strv_length (self->ids);
-static gchar *
-get_filename (void)
-{
- return g_build_filename (g_get_user_config_dir (),
- ide_get_program_name (),
- "external-commands",
- NULL);
+ 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);
}
static void
-set_items (GbpShellcmdCommandModel *self,
- GPtrArray *items)
+gbp_shellcmd_command_model_dispose (GObject *object)
{
- g_autoptr(GPtrArray) old_items = NULL;
+ GbpShellcmdCommandModel *self = (GbpShellcmdCommandModel *)object;
- g_assert (GBP_IS_SHELLCMD_COMMAND_MODEL (self));
- g_assert (items != NULL);
+ 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);
- old_items = g_steal_pointer (&self->items);
- self->items = g_ptr_array_ref (items);
+ g_clear_object (&self->settings);
- for (guint i = 0; i < items->len; i++)
- {
- GbpShellcmdCommand *command = g_ptr_array_index (items, i);
-
- g_signal_connect_object (command,
- "changed",
- G_CALLBACK (on_command_changed_cb),
- self,
- G_CONNECT_SWAPPED);
-
- g_signal_connect_object (command,
- "notify::shortcut",
- G_CALLBACK (on_command_shortcut_changed_cb),
- self,
- G_CONNECT_SWAPPED);
- }
-
- if (old_items->len || self->items->len)
- g_list_model_items_changed (G_LIST_MODEL (self), 0, old_items->len, self->items->len);
+ G_OBJECT_CLASS (gbp_shellcmd_command_model_parent_class)->dispose (object);
}
-gboolean
-gbp_shellcmd_command_model_load (GbpShellcmdCommandModel *self,
- GCancellable *cancellable,
- GError **error)
+static void
+gbp_shellcmd_command_model_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
- g_autofree gchar *path = NULL;
- g_autoptr(GPtrArray) items = NULL;
- g_autoptr(GKeyFile) keyfile = NULL;
- g_autoptr(GError) err = NULL;
- g_auto(GStrv) groups = NULL;
- gsize len;
-
- g_return_val_if_fail (GBP_IS_SHELLCMD_COMMAND_MODEL (self), FALSE);
- g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
-
- path = get_filename ();
- keyfile = g_key_file_new ();
- items = g_ptr_array_new_with_free_func ((GDestroyNotify)ide_object_unref_and_destroy);
-
- /* Parse keybindings keyfile from storage, but ignore if missing */
- if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_KEEP_COMMENTS, &err))
- {
- if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) ||
- g_error_matches (err, G_FILE_ERROR, G_FILE_ERROR_NOENT))
- return TRUE;
+ GbpShellcmdCommandModel *self = GBP_SHELLCMD_COMMAND_MODEL (object);
- g_propagate_error (error, g_steal_pointer (&err));
- return FALSE;
- }
-
- groups = g_key_file_get_groups (keyfile, &len);
-
- for (guint i = 0; i < len; i++)
+ switch (prop_id)
{
- g_autoptr(GbpShellcmdCommand) command = NULL;
- g_autoptr(GError) cmderr = NULL;
+ case PROP_KEY:
+ g_value_set_string (value, self->key);
+ break;
- if (!(command = gbp_shellcmd_command_from_key_file (keyfile, groups[i], &cmderr)))
- {
- g_warning ("Failed to parse command from group %s", groups[i]);
- continue;
- }
+ case PROP_SETTINGS:
+ g_value_set_object (value, self->settings);
+ break;
- g_ptr_array_add (items, g_steal_pointer (&command));
- }
+ case PROP_N_ITEMS:
+ g_value_set_uint (value, self->n_items);
+ break;
- g_clear_pointer (&self->keyfile, g_key_file_unref);
- self->keyfile = g_steal_pointer (&keyfile);
- set_items (self, items);
-
- return TRUE;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
}
-gboolean
-gbp_shellcmd_command_model_save (GbpShellcmdCommandModel *self,
- GCancellable *cancellable,
- GError **error)
+static void
+gbp_shellcmd_command_model_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
- g_autofree gchar *path = NULL;
- g_auto(GStrv) groups = NULL;
- gsize n_groups = 0;
+ GbpShellcmdCommandModel *self = GBP_SHELLCMD_COMMAND_MODEL (object);
- g_return_val_if_fail (GBP_IS_SHELLCMD_COMMAND_MODEL (self), FALSE);
- g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE);
- g_return_val_if_fail (self->keyfile != NULL, FALSE);
-
- path = get_filename ();
-
- for (guint i = 0; i < self->items->len; i++)
+ switch (prop_id)
{
- GbpShellcmdCommand *command = g_ptr_array_index (self->items, i);
- gbp_shellcmd_command_to_key_file (command, self->keyfile);
- }
+ case PROP_KEY:
+ self->key = g_value_dup_string (value);
+ break;
- groups = g_key_file_get_groups (self->keyfile, &n_groups);
+ case PROP_SETTINGS:
+ self->settings = g_value_dup_object (value);
+ break;
- if (n_groups == 0)
- {
- g_unlink (path);
- return TRUE;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
-
- return g_key_file_save_to_file (self->keyfile, path, error);
}
-/**
- * gbp_shellcmd_command_model_get_command:
- *
- * Returns: (transfer none) (nullable): an #GbpShellcmdCommand or %NULL
- */
-GbpShellcmdCommand *
-gbp_shellcmd_command_model_get_command (GbpShellcmdCommandModel *self,
- const gchar *command_id)
+static void
+gbp_shellcmd_command_model_class_init (GbpShellcmdCommandModelClass *klass)
{
- g_return_val_if_fail (GBP_IS_SHELLCMD_COMMAND_MODEL (self), NULL);
-
- for (guint i = 0; i < self->items->len; i++)
- {
- GbpShellcmdCommand *command = g_ptr_array_index (self->items, i);
- const gchar *id = gbp_shellcmd_command_get_id (command);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
- if (ide_str_equal0 (id, command_id))
- return command;
- }
+ 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);
+}
- return NULL;
+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);
}
-void
-gbp_shellcmd_command_model_query (GbpShellcmdCommandModel *self,
- GPtrArray *items,
- const gchar *typed_text)
+GbpShellcmdCommandModel *
+gbp_shellcmd_command_model_new (GSettings *settings,
+ const char *key)
{
- g_autofree gchar *q = NULL;
+ g_autoptr(GSettingsSchema) schema = NULL;
- g_return_if_fail (GBP_IS_SHELLCMD_COMMAND_MODEL (self));
- g_return_if_fail (items != NULL);
- g_return_if_fail (typed_text != NULL);
+ g_return_val_if_fail (G_SETTINGS (settings), NULL);
+ g_return_val_if_fail (key != NULL, NULL);
- q = g_utf8_casefold (typed_text, -1);
+ g_object_get (settings,
+ "settings-schema", &schema,
+ NULL);
- for (guint i = 0; i < self->items->len; i++)
- {
- GbpShellcmdCommand *command = g_ptr_array_index (self->items, i);
- g_autofree gchar *title = ide_command_get_title (IDE_COMMAND (command));
- const gchar *cmdstr = gbp_shellcmd_command_get_command (command);
- guint prio1 = G_MAXINT;
- guint prio2 = G_MAXINT;
-
- if (ide_completion_fuzzy_match (title, q, &prio1) ||
- ide_completion_fuzzy_match (cmdstr, q, &prio2))
- {
- GbpShellcmdCommand *copy = gbp_shellcmd_command_copy (command);
- gbp_shellcmd_command_set_priority (copy, MIN (prio1, prio2));
- g_ptr_array_add (items, g_steal_pointer (©));
- }
- }
+ 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);
}
-void
-gbp_shellcmd_command_model_add (GbpShellcmdCommandModel *self,
- GbpShellcmdCommand *command)
+GbpShellcmdCommandModel *
+gbp_shellcmd_command_model_new_for_app (void)
{
- guint position;
+ g_autoptr(GSettings) settings = NULL;
- g_return_if_fail (GBP_IS_SHELLCMD_COMMAND_MODEL (self));
- g_return_if_fail (GBP_IS_SHELLCMD_COMMAND (command));
+ settings = g_settings_new_with_path ("org.gnome.builder.shellcmd", SHELLCMD_SETTINGS_BASE);
+ return gbp_shellcmd_command_model_new (settings, "run-commands");
+}
- g_signal_connect_object (command,
- "changed",
- G_CALLBACK (on_command_changed_cb),
- self,
- G_CONNECT_SWAPPED);
+GbpShellcmdCommandModel *
+gbp_shellcmd_command_model_new_for_project (IdeContext *context)
+{
+ g_autofree char *project_id = NULL;
+ g_autofree char *project_settings_path = NULL;
+ g_autoptr(GSettings) settings = NULL;
- self->keybindings_changed = TRUE;
+ g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
- position = self->items->len;
- g_ptr_array_add (self->items, g_object_ref (command));
- g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1);
+ project_id = ide_context_dup_project_id (context);
+ project_settings_path = g_strconcat (SHELLCMD_SETTINGS_BASE"projects/", project_id, "/", NULL);
+ settings = g_settings_new_with_path ("org.gnome.builder.shellcmd", project_settings_path);
- gbp_shellcmd_command_model_queue_save (self);
+ return gbp_shellcmd_command_model_new (settings, "run-commands");
}
-void
-gbp_shellcmd_command_model_remove (GbpShellcmdCommandModel *self,
- GbpShellcmdCommand *command)
+GbpShellcmdRunCommand *
+gbp_shellcmd_run_command_create (IdeContext *context)
{
- g_return_if_fail (GBP_IS_SHELLCMD_COMMAND_MODEL (self));
- g_return_if_fail (GBP_IS_SHELLCMD_COMMAND (command));
+ g_autofree char *uuid = NULL;
+ g_autofree char *project_id = NULL;
+ g_autofree char *settings_path = NULL;
+ g_autofree char *parent_path = NULL;
+ g_autoptr(GStrvBuilder) builder = NULL;
+ g_autoptr(GSettings) settings = NULL;
+ g_auto(GStrv) strv = NULL;
- for (guint i = 0; i < self->items->len; i++)
- {
- GbpShellcmdCommand *ele = g_ptr_array_index (self->items, i);
+ g_return_val_if_fail (!context || IDE_IS_CONTEXT (context), NULL);
- if (ele == command)
- {
- const gchar *id = gbp_shellcmd_command_get_id (ele);
+ uuid = g_uuid_string_random ();
+ if (context != NULL)
+ project_id = ide_context_dup_project_id (context);
- self->keybindings_changed = TRUE;
+ if (project_id == NULL)
+ parent_path = g_strdup (SHELLCMD_SETTINGS_BASE);
+ else
+ parent_path = g_strconcat (SHELLCMD_SETTINGS_BASE"projects/", project_id, "/", NULL);
- if (id != NULL)
- g_key_file_remove_group (self->keyfile, id, NULL);
+ settings_path = g_strconcat (parent_path, uuid, "/", NULL);
+ settings = g_settings_new_with_path ("org.gnome.builder.shellcmd", parent_path);
+ strv = g_settings_get_strv (settings, "run-commands");
- g_signal_handlers_disconnect_by_func (command,
- G_CALLBACK (on_command_changed_cb),
- self);
+ builder = g_strv_builder_new ();
+ g_strv_builder_addv (builder, (const char **)strv);
+ g_strv_builder_add (builder, uuid);
- g_signal_handlers_disconnect_by_func (command,
- G_CALLBACK (on_command_shortcut_changed_cb),
- self);
+ g_clear_pointer (&strv, g_strfreev);
+ strv = g_strv_builder_end (builder);
- g_ptr_array_remove_index (self->items, i);
- g_list_model_items_changed (G_LIST_MODEL (self), i, 1, 0);
- gbp_shellcmd_command_model_queue_save (self);
+ g_settings_set_strv (settings, "run-commands", (const char * const *)strv);
- break;
- }
- }
+ return gbp_shellcmd_run_command_new (settings_path);
}
diff --git a/src/plugins/shellcmd/gbp-shellcmd-command-model.h
b/src/plugins/shellcmd/gbp-shellcmd-command-model.h
index 6c86f655a..6310c970f 100644
--- a/src/plugins/shellcmd/gbp-shellcmd-command-model.h
+++ b/src/plugins/shellcmd/gbp-shellcmd-command-model.h
@@ -1,6 +1,6 @@
/* gbp-shellcmd-command-model.h
*
- * Copyright 2019 Christian Hergert <chergert redhat com>
+ * 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
@@ -20,9 +20,7 @@
#pragma once
-#include <gio/gio.h>
-
-#include "gbp-shellcmd-command.h"
+#include <libide-core.h>
G_BEGIN_DECLS
@@ -30,21 +28,9 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (GbpShellcmdCommandModel, gbp_shellcmd_command_model, GBP, SHELLCMD_COMMAND_MODEL,
GObject)
-GbpShellcmdCommandModel *gbp_shellcmd_command_model_new (void);
-GbpShellcmdCommand *gbp_shellcmd_command_model_get_command (GbpShellcmdCommandModel *self,
- const gchar *command_id);
-void gbp_shellcmd_command_model_add (GbpShellcmdCommandModel *self,
- GbpShellcmdCommand *command);
-void gbp_shellcmd_command_model_remove (GbpShellcmdCommandModel *self,
- GbpShellcmdCommand *command);
-void gbp_shellcmd_command_model_query (GbpShellcmdCommandModel *self,
- GPtrArray *items,
- const gchar *typed_text);
-gboolean gbp_shellcmd_command_model_load (GbpShellcmdCommandModel *self,
- GCancellable *cancellable,
- GError **error);
-gboolean gbp_shellcmd_command_model_save (GbpShellcmdCommandModel *self,
- GCancellable *cancellable,
- GError **error);
+GbpShellcmdCommandModel *gbp_shellcmd_command_model_new_for_app (void);
+GbpShellcmdCommandModel *gbp_shellcmd_command_model_new_for_project (IdeContext *context);
+GbpShellcmdCommandModel *gbp_shellcmd_command_model_new (GSettings *settings,
+ const char *key);
G_END_DECLS
diff --git a/src/plugins/shellcmd/gbp-shellcmd-preferences-addin.c
b/src/plugins/shellcmd/gbp-shellcmd-preferences-addin.c
index c695ed217..28d8d8f01 100644
--- a/src/plugins/shellcmd/gbp-shellcmd-preferences-addin.c
+++ b/src/plugins/shellcmd/gbp-shellcmd-preferences-addin.c
@@ -1,6 +1,6 @@
/* gbp-shellcmd-preferences-addin.c
*
- * Copyright 2019 Christian Hergert <chergert redhat com>
+ * 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
@@ -22,112 +22,231 @@
#include "config.h"
-#include <dazzle.h>
#include <glib/gi18n.h>
+
#include <libide-gui.h>
-#include "gbp-shellcmd-application-addin.h"
-#include "gbp-shellcmd-command-editor.h"
+#include "gbp-shellcmd-command-dialog.h"
#include "gbp-shellcmd-command-model.h"
-#include "gbp-shellcmd-command-row.h"
-#include "gbp-shellcmd-list.h"
#include "gbp-shellcmd-preferences-addin.h"
+#include "gbp-shellcmd-run-command.h"
struct _GbpShellcmdPreferencesAddin
{
- GObject parent_instance;
-
- GbpShellcmdCommandEditor *editor;
+ GObject parent_instance;
+ IdePreferencesWindow *window;
+ GSettings *settings;
};
-static GbpShellcmdCommandModel *
-get_model (void)
+static GtkWidget *
+gbp_shellcmd_preferences_addin_create_row_cb (gpointer item,
+ gpointer item_data)
{
- GbpShellcmdApplicationAddin *app_addin;
- GbpShellcmdCommandModel *model;
+ GbpShellcmdRunCommand *command = item;
+ AdwActionRow *row;
+ GtkLabel *accel;
- app_addin = ide_application_find_addin_by_module_name (NULL, "shellcmd");
- g_assert (GBP_IS_SHELLCMD_APPLICATION_ADDIN (app_addin));
+ g_assert (GBP_IS_SHELLCMD_RUN_COMMAND (command));
- model = gbp_shellcmd_application_addin_get_model (app_addin);
- g_assert (GBP_IS_SHELLCMD_COMMAND_MODEL (model));
+ row = g_object_new (ADW_TYPE_ACTION_ROW,
+ "activatable", TRUE,
+ NULL);
+ g_object_bind_property (command, "display-name", row, "title",
+ G_BINDING_SYNC_CREATE);
+ g_object_bind_property (command, "subtitle", row, "subtitle",
+ G_BINDING_SYNC_CREATE);
- return model;
+ accel = g_object_new (GTK_TYPE_LABEL,
+ "margin-start", 6,
+ "margin-end", 6,
+ NULL);
+ g_object_bind_property (command, "accelerator-label", accel, "label",
+ G_BINDING_SYNC_CREATE);
+ adw_action_row_add_suffix (row, GTK_WIDGET (accel));
+ adw_action_row_add_suffix (row,
+ g_object_new (GTK_TYPE_IMAGE,
+ "icon-name", "go-next-symbolic",
+ NULL));
+ g_object_set_data_full (G_OBJECT (row),
+ "COMMAND",
+ g_object_ref (command),
+ g_object_unref);
+
+ return GTK_WIDGET (row);
}
static void
-on_command_selected_cb (GbpShellcmdPreferencesAddin *self,
- GbpShellcmdCommand *command,
- GbpShellcmdList *list)
+on_row_activated_cb (GtkListBox *list_box,
+ AdwActionRow *row,
+ IdePreferencesWindow *window)
{
- GtkWidget *preferences;
+ g_autoptr(GbpShellcmdRunCommand) new_command = NULL;
+ GbpShellcmdRunCommand *command;
+ GbpShellcmdCommandDialog *dialog;
- g_assert (GBP_IS_SHELLCMD_PREFERENCES_ADDIN (self));
- g_assert (!command || GBP_IS_SHELLCMD_COMMAND (command));
- g_assert (GBP_IS_SHELLCMD_LIST (list));
+ IDE_ENTRY;
- if (!(preferences = gtk_widget_get_ancestor (GTK_WIDGET (list), DZL_TYPE_PREFERENCES)))
- return;
+ g_assert (GTK_IS_LIST_BOX (list_box));
+ g_assert (ADW_IS_ACTION_ROW (row));
+ g_assert (IDE_IS_PREFERENCES_WINDOW (window));
- if (command != NULL)
+ command = g_object_get_data (G_OBJECT (row), "COMMAND");
+
+ g_assert (!command || GBP_IS_SHELLCMD_RUN_COMMAND (command));
+
+ if (command == NULL)
{
- g_autoptr(GHashTable) map = NULL;
+ IdePreferencesMode mode = ide_preferences_window_get_mode (window);
+ IdeContext *context = ide_preferences_window_get_context (window);
+
+ if (mode == IDE_PREFERENCES_MODE_PROJECT)
+ new_command = gbp_shellcmd_run_command_create (context);
+ else
+ new_command = gbp_shellcmd_run_command_create (NULL);
+
+ command = new_command;
+ }
+
+ dialog = gbp_shellcmd_command_dialog_new (command, new_command != NULL);
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (window));
+ gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+ gtk_window_present (GTK_WINDOW (dialog));
+
+ IDE_EXIT;
+}
+
+static void
+handle_shellcmd_list (const char *page_name,
+ const IdePreferenceItemEntry *entry,
+ AdwPreferencesGroup *group,
+ gpointer user_data)
+{
+ IdePreferencesWindow *window = user_data;
+ GbpShellcmdCommandModel *model;
+ IdePreferencesMode mode;
+ AdwActionRow *create_row;
+ IdeContext *context;
+ GtkListBox *list_box;
+ GtkLabel *label;
+
+ IDE_ENTRY;
- map = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
- g_hash_table_insert (map, (gchar *)"{id}", g_strdup (gbp_shellcmd_command_get_id (command)));
- dzl_preferences_set_page (DZL_PREFERENCES (preferences), "shellcmd.id", map);
+ g_assert (ide_str_equal0 (page_name, "commands"));
+ g_assert (ADW_IS_PREFERENCES_GROUP (group));
+ g_assert (IDE_IS_PREFERENCES_WINDOW (window));
+
+ context = ide_preferences_window_get_context (window);
+ mode = ide_preferences_window_get_mode (window);
+
+ if (mode == IDE_PREFERENCES_MODE_PROJECT)
+ {
+ model = gbp_shellcmd_command_model_new_for_project (context);
+ adw_preferences_group_set_title (group, _("Project Commands"));
+ }
+ else
+ {
+ model = gbp_shellcmd_command_model_new_for_app ();
+ adw_preferences_group_set_title (group, _("Shared Commands"));
}
- gbp_shellcmd_command_editor_set_command (self->editor, command);
+ list_box = g_object_new (GTK_TYPE_LIST_BOX,
+ "css-classes", IDE_STRV_INIT ("boxed-list"),
+ "selection-mode", GTK_SELECTION_NONE,
+ NULL);
+ create_row = g_object_new (ADW_TYPE_ACTION_ROW,
+ "activatable", TRUE,
+ "title", _("Create Command"),
+ "subtitle", _("Commands can be used to build, run, or modify your projects"),
+ NULL);
+ adw_action_row_add_suffix (create_row,
+ g_object_new (GTK_TYPE_IMAGE,
+ "icon-name", "go-next-symbolic",
+ NULL));
+ g_signal_connect_object (list_box,
+ "row-activated",
+ G_CALLBACK (on_row_activated_cb),
+ window,
+ 0);
+ gtk_list_box_append (list_box, GTK_WIDGET (create_row));
+ adw_preferences_group_add (group, GTK_WIDGET (list_box));
+
+ label = g_object_new (GTK_TYPE_LABEL,
+ "css-classes", IDE_STRV_INIT ("dim-label", "caption"),
+ "margin-top", 6,
+ "xalign", .0f,
+ NULL);
+ if (mode == IDE_PREFERENCES_MODE_PROJECT)
+ gtk_label_set_label (label,
+ _("These commands may be run from this project only."));
+ else
+ gtk_label_set_label (label,
+ _("These commands may be shared across any project in Builder."));
+ adw_preferences_group_add (group, GTK_WIDGET (label));
+
+ list_box = g_object_new (GTK_TYPE_LIST_BOX,
+ "css-classes", IDE_STRV_INIT ("boxed-list"),
+ "selection-mode", GTK_SELECTION_NONE,
+ "margin-top", 18,
+ NULL);
+ gtk_list_box_bind_model (list_box,
+ G_LIST_MODEL (model),
+ gbp_shellcmd_preferences_addin_create_row_cb,
+ NULL, NULL);
+ adw_preferences_group_add (group, GTK_WIDGET (list_box));
+ ide_gtk_widget_hide_when_empty (GTK_WIDGET (list_box), G_LIST_MODEL (model));
+ g_signal_connect_object (list_box,
+ "row-activated",
+ G_CALLBACK (on_row_activated_cb),
+ window,
+ 0);
+
+
+ IDE_EXIT;
}
+static const IdePreferenceGroupEntry groups[] = {
+ { "commands", "shellcmd", 0 },
+};
+
+static const IdePreferenceItemEntry items[] = {
+ { "commands", "shellcmd", "list", 0, handle_shellcmd_list },
+};
+
static void
-gbp_shellcmd_preferences_addin_load (IdePreferencesAddin *addin,
- DzlPreferences *prefs)
+gbp_shellcmd_preferences_addin_load (IdePreferencesAddin *addin,
+ IdePreferencesWindow *window,
+ IdeContext *context)
{
GbpShellcmdPreferencesAddin *self = (GbpShellcmdPreferencesAddin *)addin;
- GtkWidget *list;
+
+ IDE_ENTRY;
g_assert (GBP_IS_SHELLCMD_PREFERENCES_ADDIN (self));
- g_assert (DZL_IS_PREFERENCES (prefs));
-
- dzl_preferences_add_page (prefs, "shellcmd", _("External Commands"), 650);
- dzl_preferences_add_group (prefs, "shellcmd", "commands", _("External Commands"), 0);
-
- list = gbp_shellcmd_list_new (get_model ());
- g_signal_connect_object (list,
- "command-selected",
- G_CALLBACK (on_command_selected_cb),
- self,
- G_CONNECT_SWAPPED);
- dzl_preferences_add_custom (prefs, "shellcmd", "commands", list, NULL, 0);
-
- dzl_preferences_add_page (prefs, "shellcmd.id", NULL, 0);
- dzl_preferences_add_group (prefs, "shellcmd.id", "basic", _("Command"), 0);
-
- self->editor = g_object_new (GBP_TYPE_SHELLCMD_COMMAND_EDITOR,
- "visible", TRUE,
- NULL);
- g_signal_connect (self->editor,
- "destroy",
- G_CALLBACK (gtk_widget_destroyed),
- &self->editor);
- dzl_preferences_add_custom (prefs, "shellcmd.id", "basic", GTK_WIDGET (self->editor), NULL, 0);
+ g_assert (IDE_IS_PREFERENCES_WINDOW (window));
+
+ self->window = window;
+
+ ide_preferences_window_add_groups (window, groups, G_N_ELEMENTS (groups), GETTEXT_PACKAGE);
+ ide_preferences_window_add_items (window, items, G_N_ELEMENTS (items), window, NULL);
+
+ IDE_EXIT;
}
static void
-gbp_shellcmd_preferences_addin_unload (IdePreferencesAddin *addin,
- DzlPreferences *prefs)
+gbp_shellcmd_preferences_addin_unload (IdePreferencesAddin *addin,
+ IdePreferencesWindow *window,
+ IdeContext *context)
{
GbpShellcmdPreferencesAddin *self = (GbpShellcmdPreferencesAddin *)addin;
+ IDE_ENTRY;
+
g_assert (GBP_IS_SHELLCMD_PREFERENCES_ADDIN (self));
- g_assert (DZL_IS_PREFERENCES (prefs));
+ g_assert (IDE_IS_PREFERENCES_WINDOW (window));
- if (self->editor != NULL)
- gtk_widget_destroy (GTK_WIDGET (self->editor));
+ self->window = NULL;
- g_assert (self->editor == NULL);
+ IDE_EXIT;
}
static void
@@ -138,7 +257,7 @@ preferences_addin_iface_init (IdePreferencesAddinInterface *iface)
}
G_DEFINE_FINAL_TYPE_WITH_CODE (GbpShellcmdPreferencesAddin, gbp_shellcmd_preferences_addin, G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (IDE_TYPE_PREFERENCES_ADDIN, preferences_addin_iface_init))
+ G_IMPLEMENT_INTERFACE (IDE_TYPE_PREFERENCES_ADDIN,
preferences_addin_iface_init))
static void
gbp_shellcmd_preferences_addin_class_init (GbpShellcmdPreferencesAddinClass *klass)
diff --git a/src/plugins/shellcmd/gbp-shellcmd-preferences-addin.h
b/src/plugins/shellcmd/gbp-shellcmd-preferences-addin.h
index 6041fa0c1..f9520182e 100644
--- a/src/plugins/shellcmd/gbp-shellcmd-preferences-addin.h
+++ b/src/plugins/shellcmd/gbp-shellcmd-preferences-addin.h
@@ -1,6 +1,6 @@
/* gbp-shellcmd-preferences-addin.h
*
- * Copyright 2019 Christian Hergert <chergert redhat com>
+ * 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
diff --git a/src/plugins/shellcmd/gbp-shellcmd-run-command-provider.c
b/src/plugins/shellcmd/gbp-shellcmd-run-command-provider.c
new file mode 100644
index 000000000..93d28be31
--- /dev/null
+++ b/src/plugins/shellcmd/gbp-shellcmd-run-command-provider.c
@@ -0,0 +1,109 @@
+/* gbp-shellcmd-run-command-provider.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-run-command-provider"
+
+#include "config.h"
+
+#include <libide-threading.h>
+
+#include "gbp-shellcmd-command-model.h"
+#include "gbp-shellcmd-run-command.h"
+#include "gbp-shellcmd-run-command-provider.h"
+
+struct _GbpShellcmdRunCommandProvider
+{
+ IdeObject parent_instance;
+};
+
+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(IdeTask) task = NULL;
+ IdeContext *context;
+
+ IDE_ENTRY;
+
+ g_assert (GBP_IS_SHELLCMD_RUN_COMMAND_PROVIDER (provider));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = ide_task_new (provider, cancellable, callback, user_data);
+ ide_task_set_source_tag (task, gbp_shellcmd_run_command_provider_list_commands_async);
+
+ context = ide_object_get_context (IDE_OBJECT (provider));
+
+ app_commands = gbp_shellcmd_command_model_new_for_app ();
+ project_commands = gbp_shellcmd_command_model_new_for_project (context);
+
+ 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_object_new (GTK_TYPE_FLATTEN_LIST_MODEL,
+ "model", store,
+ NULL),
+ g_object_unref);
+
+ IDE_EXIT;
+}
+
+static GListModel *
+gbp_shellcmd_run_command_provider_list_commands_finish (IdeRunCommandProvider *provider,
+ GAsyncResult *result,
+ GError **error)
+{
+ GListModel *ret;
+
+ IDE_ENTRY;
+
+ g_assert (GBP_IS_SHELLCMD_RUN_COMMAND_PROVIDER (provider));
+ g_assert (IDE_IS_TASK (result));
+
+ ret = ide_task_propagate_pointer (IDE_TASK (result), error);
+
+ IDE_RETURN (ret);
+}
+
+static void
+run_command_provider_iface_init (IdeRunCommandProviderInterface *iface)
+{
+ iface->list_commands_async = gbp_shellcmd_run_command_provider_list_commands_async;
+ iface->list_commands_finish = gbp_shellcmd_run_command_provider_list_commands_finish;
+}
+
+G_DEFINE_FINAL_TYPE_WITH_CODE (GbpShellcmdRunCommandProvider, gbp_shellcmd_run_command_provider,
IDE_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (IDE_TYPE_RUN_COMMAND_PROVIDER,
run_command_provider_iface_init))
+
+static void
+gbp_shellcmd_run_command_provider_class_init (GbpShellcmdRunCommandProviderClass *klass)
+{
+}
+
+static void
+gbp_shellcmd_run_command_provider_init (GbpShellcmdRunCommandProvider *self)
+{
+}
diff --git a/src/plugins/shellcmd/gbp-shellcmd-list.h
b/src/plugins/shellcmd/gbp-shellcmd-run-command-provider.h
similarity index 67%
rename from src/plugins/shellcmd/gbp-shellcmd-list.h
rename to src/plugins/shellcmd/gbp-shellcmd-run-command-provider.h
index 2ac4cf91c..91e9ea4e9 100644
--- a/src/plugins/shellcmd/gbp-shellcmd-list.h
+++ b/src/plugins/shellcmd/gbp-shellcmd-run-command-provider.h
@@ -1,6 +1,6 @@
-/* gbp-shellcmd-list.h
+/* gbp-shellcmd-run-command-provider.h
*
- * Copyright 2019 Christian Hergert <chergert redhat com>
+ * 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
@@ -20,16 +20,12 @@
#pragma once
-#include <gtk/gtk.h>
-
-#include "gbp-shellcmd-command-model.h"
+#include <libide-foundry.h>
G_BEGIN_DECLS
-#define GBP_TYPE_SHELLCMD_LIST (gbp_shellcmd_list_get_type())
-
-G_DECLARE_FINAL_TYPE (GbpShellcmdList, gbp_shellcmd_list, GBP, SHELLCMD_LIST, GtkFrame)
+#define GBP_TYPE_SHELLCMD_RUN_COMMAND_PROVIDER (gbp_shellcmd_run_command_provider_get_type())
-GtkWidget *gbp_shellcmd_list_new (GbpShellcmdCommandModel *model);
+G_DECLARE_FINAL_TYPE (GbpShellcmdRunCommandProvider, gbp_shellcmd_run_command_provider, GBP,
SHELLCMD_RUN_COMMAND_PROVIDER, IdeObject)
G_END_DECLS
diff --git a/src/plugins/shellcmd/gbp-shellcmd-run-command.c b/src/plugins/shellcmd/gbp-shellcmd-run-command.c
new file mode 100644
index 000000000..293cbd080
--- /dev/null
+++ b/src/plugins/shellcmd/gbp-shellcmd-run-command.c
@@ -0,0 +1,416 @@
+/* gbp-shellcmd-run-command.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-run-command"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "gbp-shellcmd-enums.h"
+#include "gbp-shellcmd-run-command.h"
+
+struct _GbpShellcmdRunCommand
+{
+ IdeRunCommand parent_instance;
+
+ char *settings_path;
+ GSettings *settings;
+ char *id;
+ char *accelerator;
+
+ GbpShellcmdLocality locality;
+};
+
+enum {
+ PROP_0,
+ PROP_ACCELERATOR,
+ PROP_ACCELERATOR_LABEL,
+ PROP_LOCALITY,
+ PROP_SETTINGS_PATH,
+ PROP_SUBTITLE,
+ N_PROPS
+};
+
+G_DEFINE_FINAL_TYPE (GbpShellcmdRunCommand, gbp_shellcmd_run_command, IDE_TYPE_RUN_COMMAND)
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+gbp_shellcmd_run_command_prepare_to_run (IdeRunCommand *run_command,
+ IdeRunContext *run_context,
+ IdeContext *context)
+{
+ GbpShellcmdRunCommand *self = (GbpShellcmdRunCommand *)run_command;
+ IdePipeline *pipeline = NULL;
+ IdeRuntime *runtime = NULL;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (GBP_IS_SHELLCMD_RUN_COMMAND (self));
+ g_assert (IDE_IS_RUN_CONTEXT (run_context));
+ g_assert (IDE_IS_CONTEXT (context));
+
+ if (ide_context_has_project (context))
+ {
+ IdeBuildManager *build_manager = ide_build_manager_from_context (context);
+
+ if ((pipeline = ide_build_manager_get_pipeline (build_manager)))
+ runtime = ide_pipeline_get_runtime (pipeline);
+ }
+
+ switch (self->locality)
+ {
+ case GBP_SHELLCMD_LOCALITY_PIPELINE:
+ if (pipeline == NULL)
+ ide_run_context_push_error (run_context,
+ g_error_new (G_IO_ERROR,
+ G_IO_ERROR_NOT_INITIALIZED,
+ "No pipeline available for run command"));
+ else
+ ide_pipeline_prepare_run_context (pipeline, run_context);
+ break;
+
+ case GBP_SHELLCMD_LOCALITY_HOST:
+ ide_run_context_push_host (run_context);
+ break;
+
+ case GBP_SHELLCMD_LOCALITY_SUBPROCESS:
+ break;
+
+ case GBP_SHELLCMD_LOCALITY_RUNTIME: {
+ if (pipeline == NULL || runtime == NULL)
+ ide_run_context_push_error (run_context,
+ g_error_new (G_IO_ERROR,
+ G_IO_ERROR_NOT_INITIALIZED,
+ "No pipeline available for run command"));
+ else
+ ide_runtime_prepare_to_run (runtime, pipeline, run_context);
+ break;
+ }
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ IDE_RUN_COMMAND_CLASS (gbp_shellcmd_run_command_parent_class)->prepare_to_run (run_command, run_context,
context);
+
+ IDE_EXIT;
+}
+
+static void
+gbp_shellcmd_run_command_constructed (GObject *object)
+{
+ GbpShellcmdRunCommand *self = (GbpShellcmdRunCommand *)object;
+ g_autofree char *id = NULL;
+ g_auto(GStrv) path_split = NULL;
+ gsize n_parts;
+
+ g_assert (GBP_IS_SHELLCMD_RUN_COMMAND (self));
+ g_assert (self->settings_path != NULL);
+ g_assert (g_str_has_suffix (self->settings_path, "/"));
+
+ self->settings = g_settings_new_with_path ("org.gnome.builder.shellcmd.command", self->settings_path);
+
+ path_split = g_strsplit (self->settings_path, "/", 0);
+ n_parts = g_strv_length (path_split);
+ g_assert (n_parts >= 2);
+
+ self->id = g_strdup (path_split[n_parts-2]);
+ id = g_strdup_printf ("shellcmd:%s", self->id);
+
+ ide_run_command_set_id (IDE_RUN_COMMAND (self), id);
+ g_settings_bind (self->settings, "display-name", self, "display-name", G_SETTINGS_BIND_DEFAULT);
+ g_settings_bind (self->settings, "env", self, "environ", G_SETTINGS_BIND_DEFAULT);
+ g_settings_bind (self->settings, "argv", self, "argv", G_SETTINGS_BIND_DEFAULT);
+ g_settings_bind (self->settings, "cwd", self, "cwd", G_SETTINGS_BIND_DEFAULT);
+ g_settings_bind (self->settings, "accelerator", self, "accelerator", G_SETTINGS_BIND_DEFAULT);
+ g_settings_bind (self->settings, "locality", self, "locality", G_SETTINGS_BIND_DEFAULT);
+}
+
+static void
+subtitle_changed_cb (GbpShellcmdRunCommand *self)
+{
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SUBTITLE]);
+}
+
+static char *
+get_subtitle (GbpShellcmdRunCommand *self)
+{
+ g_autofree char *joined = NULL;
+ const char * const *argv;
+ const char *cwd;
+
+ g_assert (GBP_IS_SHELLCMD_RUN_COMMAND (self));
+
+ argv = ide_run_command_get_argv (IDE_RUN_COMMAND (self));
+ cwd = ide_run_command_get_cwd (IDE_RUN_COMMAND (self));
+
+ if (argv != NULL)
+ joined = g_strjoinv (" ", (char **)argv);
+
+ if (joined && cwd)
+ /* something like a bash prompt */
+ return g_strdup_printf ("<tt>%s> %s</tt>", cwd, joined);
+
+ if (cwd)
+ return g_strdup_printf ("%s> ", cwd);
+
+ return g_steal_pointer (&joined);
+}
+
+static void
+accelerator_label_changed_cb (GbpShellcmdRunCommand *self)
+{
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACCELERATOR_LABEL]);
+}
+
+static char *
+get_accelerator_label (GbpShellcmdRunCommand *self)
+{
+ GdkModifierType state;
+ guint keyval;
+
+ if (ide_str_empty0 (self->accelerator))
+ return NULL;
+
+ if (gtk_accelerator_parse (self->accelerator, &keyval, &state))
+ return gtk_accelerator_get_label (keyval, state);
+
+ return NULL;
+}
+
+static void
+gbp_shellcmd_run_command_dispose (GObject *object)
+{
+ GbpShellcmdRunCommand *self = (GbpShellcmdRunCommand *)object;
+
+ g_clear_pointer (&self->accelerator, g_free);
+ g_clear_pointer (&self->id, g_free);
+ g_clear_pointer (&self->settings_path, g_free);
+ g_clear_object (&self->settings);
+
+ G_OBJECT_CLASS (gbp_shellcmd_run_command_parent_class)->dispose (object);
+}
+
+static void
+gbp_shellcmd_run_command_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GbpShellcmdRunCommand *self = GBP_SHELLCMD_RUN_COMMAND (object);
+
+ switch (prop_id)
+ {
+ case PROP_ACCELERATOR:
+ g_value_set_string (value, gbp_shellcmd_run_command_get_accelerator (self));
+ break;
+
+ case PROP_ACCELERATOR_LABEL:
+ g_value_take_string (value, get_accelerator_label (self));
+ break;
+
+ case PROP_LOCALITY:
+ g_value_set_enum (value, gbp_shellcmd_run_command_get_locality (self));
+ break;
+
+ case PROP_SETTINGS_PATH:
+ g_value_set_string (value, self->settings_path);
+ break;
+
+ case PROP_SUBTITLE:
+ g_value_take_string (value, get_subtitle (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gbp_shellcmd_run_command_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GbpShellcmdRunCommand *self = GBP_SHELLCMD_RUN_COMMAND (object);
+
+ switch (prop_id)
+ {
+ case PROP_ACCELERATOR:
+ gbp_shellcmd_run_command_set_accelerator (self, g_value_get_string (value));
+ break;
+
+ case PROP_LOCALITY:
+ gbp_shellcmd_run_command_set_locality (self, g_value_get_enum (value));
+ break;
+
+ case PROP_SETTINGS_PATH:
+ self->settings_path = g_value_dup_string (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gbp_shellcmd_run_command_class_init (GbpShellcmdRunCommandClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ IdeRunCommandClass *run_command_class = IDE_RUN_COMMAND_CLASS (klass);
+
+ object_class->constructed = gbp_shellcmd_run_command_constructed;
+ object_class->dispose = gbp_shellcmd_run_command_dispose;
+ object_class->get_property = gbp_shellcmd_run_command_get_property;
+ object_class->set_property = gbp_shellcmd_run_command_set_property;
+
+ run_command_class->prepare_to_run = gbp_shellcmd_run_command_prepare_to_run;
+
+ properties [PROP_ACCELERATOR] =
+ g_param_spec_string ("accelerator", NULL, NULL, NULL,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_ACCELERATOR_LABEL] =
+ g_param_spec_string ("accelerator-label", NULL, NULL, NULL,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_LOCALITY] =
+ g_param_spec_enum ("locality", NULL, NULL,
+ GBP_TYPE_SHELLCMD_LOCALITY,
+ GBP_SHELLCMD_LOCALITY_PIPELINE,
+ (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_SETTINGS_PATH] =
+ g_param_spec_string ("settings-path", NULL, NULL, NULL,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_SUBTITLE] =
+ g_param_spec_string ("subtitle", NULL, NULL, NULL,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+gbp_shellcmd_run_command_init (GbpShellcmdRunCommand *self)
+{
+ self->locality = GBP_SHELLCMD_LOCALITY_PIPELINE;
+
+ ide_run_command_set_kind (IDE_RUN_COMMAND (self),
+ IDE_RUN_COMMAND_KIND_USER_DEFINED);
+
+ g_signal_connect (self, "notify::accelerator", G_CALLBACK (accelerator_label_changed_cb), NULL);
+ g_signal_connect (self, "notify::cwd", G_CALLBACK (subtitle_changed_cb), NULL);
+ g_signal_connect (self, "notify::argv", G_CALLBACK (subtitle_changed_cb), NULL);
+}
+
+GbpShellcmdRunCommand *
+gbp_shellcmd_run_command_new (const char *settings_path)
+{
+ return g_object_new (GBP_TYPE_SHELLCMD_RUN_COMMAND,
+ "settings-path", settings_path,
+ NULL);
+}
+
+void
+gbp_shellcmd_run_command_delete (GbpShellcmdRunCommand *self)
+{
+ g_autoptr(GSettingsSchema) schema = NULL;
+ g_autoptr(GStrvBuilder) builder = NULL;
+ g_autoptr(GSettings) list = NULL;
+ g_autoptr(GString) parent_path = NULL;
+ g_auto(GStrv) commands = NULL;
+ g_auto(GStrv) keys = NULL;
+
+ g_return_if_fail (GBP_IS_SHELLCMD_RUN_COMMAND (self));
+
+ /* Get parent settings path */
+ parent_path = g_string_new (self->settings_path);
+ if (parent_path->len)
+ g_string_truncate (parent_path, parent_path->len-1);
+ while (parent_path->len && parent_path->str[parent_path->len-1] != '/')
+ g_string_truncate (parent_path, parent_path->len-1);
+
+ /* First remove the item from the parent list of commands */
+ list = g_settings_new_with_path ("org.gnome.builder.shellcmd", parent_path->str);
+ commands = g_settings_get_strv (list, "run-commands");
+ builder = g_strv_builder_new ();
+ for (guint i = 0; commands[i]; i++)
+ {
+ if (!ide_str_equal0 (commands[i], self->id))
+ g_strv_builder_add (builder, commands[i]);
+ }
+ g_clear_pointer (&commands, g_strfreev);
+ commands = g_strv_builder_end (builder);
+ g_settings_set_strv (list, "run-commands", (const char * const *)commands);
+
+ /* Now reset the keys so the entry does not take up space in storage */
+ g_object_get (self->settings,
+ "settings-schema", &schema,
+ NULL);
+ keys = g_settings_schema_list_keys (schema);
+ for (guint i = 0; keys[i]; i++)
+ g_settings_reset (self->settings, keys[i]);
+}
+
+const char *
+gbp_shellcmd_run_command_get_accelerator (GbpShellcmdRunCommand *self)
+{
+ g_return_val_if_fail (GBP_IS_SHELLCMD_RUN_COMMAND (self), NULL);
+
+ return self->accelerator;
+}
+
+void
+gbp_shellcmd_run_command_set_accelerator (GbpShellcmdRunCommand *self,
+ const char *accelerator)
+{
+ g_return_if_fail (GBP_IS_SHELLCMD_RUN_COMMAND (self));
+
+ if (ide_set_string (&self->accelerator, accelerator))
+ {
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACCELERATOR]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACCELERATOR_LABEL]);
+ }
+}
+
+GbpShellcmdLocality
+gbp_shellcmd_run_command_get_locality (GbpShellcmdRunCommand *self)
+{
+ g_return_val_if_fail (GBP_IS_SHELLCMD_RUN_COMMAND (self), 0);
+
+ return self->locality;
+}
+
+void
+gbp_shellcmd_run_command_set_locality (GbpShellcmdRunCommand *self,
+ GbpShellcmdLocality locality)
+{
+ g_return_if_fail (GBP_IS_SHELLCMD_RUN_COMMAND (self));
+
+ if (locality != self->locality)
+ {
+ self->locality = locality;
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOCALITY]);
+ }
+}
diff --git a/src/plugins/shellcmd/gbp-shellcmd-run-command.h b/src/plugins/shellcmd/gbp-shellcmd-run-command.h
new file mode 100644
index 000000000..ee1a524b7
--- /dev/null
+++ b/src/plugins/shellcmd/gbp-shellcmd-run-command.h
@@ -0,0 +1,57 @@
+/* gbp-shellcmd-run-command.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 <libide-foundry.h>
+#include <libide-terminal.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GbpShellcmdLocality:
+ * %GBP_SHELLCMD_LOCALITY_SUBPROCESS: run as a subprocess of builder
+ * %GBP_SHELLCMD_LOCALITY_HOST: run on the host system, possibly bypassing container
+ * %GBP_SHELLCMD_LOCALITY_PIPELINE: run from build pipeline
+ * %GBP_SHELLCMD_LOCALITY_RUNTIME: run like a target application
+ */
+typedef enum
+{
+ GBP_SHELLCMD_LOCALITY_SUBPROCESS = 0,
+ GBP_SHELLCMD_LOCALITY_HOST,
+ GBP_SHELLCMD_LOCALITY_PIPELINE,
+ GBP_SHELLCMD_LOCALITY_RUNTIME,
+} GbpShellcmdLocality;
+
+#define GBP_TYPE_SHELLCMD_RUN_COMMAND (gbp_shellcmd_run_command_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpShellcmdRunCommand, gbp_shellcmd_run_command, GBP, SHELLCMD_RUN_COMMAND,
IdeRunCommand)
+
+GbpShellcmdRunCommand *gbp_shellcmd_run_command_new (const char *settings_path);
+GbpShellcmdRunCommand *gbp_shellcmd_run_command_create (IdeContext *context);
+void gbp_shellcmd_run_command_delete (GbpShellcmdRunCommand *self);
+const char *gbp_shellcmd_run_command_get_accelerator (GbpShellcmdRunCommand *self);
+void gbp_shellcmd_run_command_set_accelerator (GbpShellcmdRunCommand *self,
+ const char *accelerator);
+GbpShellcmdLocality gbp_shellcmd_run_command_get_locality (GbpShellcmdRunCommand *self);
+void gbp_shellcmd_run_command_set_locality (GbpShellcmdRunCommand *self,
+ GbpShellcmdLocality locality);
+
+G_END_DECLS
diff --git a/src/plugins/shellcmd/gbp-shellcmd-shortcut-provider.c
b/src/plugins/shellcmd/gbp-shellcmd-shortcut-provider.c
new file mode 100644
index 000000000..b96169602
--- /dev/null
+++ b/src/plugins/shellcmd/gbp-shellcmd-shortcut-provider.c
@@ -0,0 +1,265 @@
+/* gbp-shellcmd-shortcut-provider.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-shortcut-provider"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include <libide-editor.h>
+#include <libide-gui.h>
+#include <libide-terminal.h>
+
+#include "gbp-shellcmd-command-model.h"
+#include "gbp-shellcmd-run-command.h"
+#include "gbp-shellcmd-shortcut-provider.h"
+
+struct _GbpShellcmdShortcutProvider
+{
+ IdeObject parent_instance;
+ GListStore *model;
+};
+
+static gboolean
+gbp_shellcmd_shortcut_func (GtkWidget *widget,
+ GVariant *args,
+ gpointer user_data)
+{
+ GbpShellcmdRunCommand *run_command = user_data;
+ g_autoptr(IdePanelPosition) position = NULL;
+ g_autoptr(IdeTerminalLauncher) launcher = NULL;
+ IdeWorkspace *workspace;
+ IdeContext *context;
+ const char *title;
+ IdePage *page;
+
+ IDE_ENTRY;
+
+ g_assert (GTK_IS_WIDGET (widget));
+ g_assert (args == NULL);
+ g_assert (GBP_IS_SHELLCMD_RUN_COMMAND (run_command));
+
+ g_debug ("Shortcut triggered to run command “%s” which has accelerator %s",
+ ide_run_command_get_display_name (IDE_RUN_COMMAND (run_command)),
+ gbp_shellcmd_run_command_get_accelerator (run_command));
+
+ if (!(workspace = ide_widget_get_workspace (widget)) ||
+ !(context = ide_workspace_get_context (workspace)))
+ IDE_RETURN (FALSE);
+
+ if (!IDE_IS_PRIMARY_WORKSPACE (workspace) &&
+ !IDE_IS_EDITOR_WORKSPACE (workspace))
+ IDE_RETURN (FALSE);
+
+ if (!(title = ide_run_command_get_display_name (IDE_RUN_COMMAND (run_command))))
+ title = _("Untitled command");
+
+ launcher = ide_terminal_launcher_new (context, IDE_RUN_COMMAND (run_command));
+
+ page = g_object_new (IDE_TYPE_TERMINAL_PAGE,
+ "close-on-exit", FALSE,
+ "icon-name", "text-x-script-symbolic",
+ "launcher", launcher,
+ "manage-spawn", TRUE,
+ "respawn-on-exit", FALSE,
+ "title", title,
+ NULL);
+
+ position = ide_panel_position_new ();
+
+ ide_workspace_add_page (workspace, page, position);
+ panel_widget_raise (PANEL_WIDGET (page));
+ gtk_widget_grab_focus (GTK_WIDGET (page));
+
+ IDE_RETURN (TRUE);
+}
+
+static gboolean
+accelerator_to_trigger (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value,
+ gpointer user_data)
+{
+ const char *accel;
+
+ g_assert (G_IS_BINDING (binding));
+ g_assert (G_VALUE_HOLDS_STRING (from_value));
+
+ accel = g_value_get_string (from_value);
+
+ if (!ide_str_empty0 (accel))
+ {
+ GtkShortcutTrigger *trigger;
+
+ if ((trigger = gtk_shortcut_trigger_parse_string (accel)))
+ {
+ g_value_take_object (to_value, trigger);
+ return TRUE;
+ }
+ }
+
+ g_value_set_object (to_value, gtk_never_trigger_get ());
+
+ return TRUE;
+}
+
+static gpointer
+gbp_shellcmd_shortcut_provider_map_func (gpointer item,
+ gpointer user_data)
+{
+ g_autoptr(GbpShellcmdRunCommand) command = item;
+ g_autoptr(GtkShortcutAction) action = NULL;
+ GtkShortcut *ret;
+
+ g_assert (GBP_IS_SHELLCMD_RUN_COMMAND (command));
+ g_assert (user_data == NULL);
+
+ action = gtk_callback_action_new (gbp_shellcmd_shortcut_func,
+ g_object_ref (command),
+ g_object_unref);
+ ret = gtk_shortcut_new (NULL, g_steal_pointer (&action));
+
+ /* We want the accelerator to update when the command changes it */
+ g_object_bind_property_full (command, "accelerator", ret, "trigger",
+ G_BINDING_SYNC_CREATE,
+ accelerator_to_trigger, NULL,
+ NULL, NULL);
+
+ /* Our capture/bubble filters require a phase set. Keep in sync
+ * with ide_shortcut_is_phase().
+ */
+ g_object_set_data (G_OBJECT (ret),
+ "PHASE",
+ GINT_TO_POINTER (GTK_PHASE_BUBBLE));
+
+ return g_steal_pointer (&ret);
+}
+
+static void
+add_with_mapping (GListStore *store,
+ GListModel *commands,
+ gboolean prepend)
+{
+ g_autoptr(GtkMapListModel) map = NULL;
+
+ g_assert (G_IS_LIST_STORE (store));
+ g_assert (GBP_IS_SHELLCMD_COMMAND_MODEL (commands));
+
+ map = gtk_map_list_model_new (g_object_ref (commands),
+ gbp_shellcmd_shortcut_provider_map_func,
+ NULL, NULL);
+
+ if (prepend)
+ g_list_store_insert (store, 0, map);
+ else
+ g_list_store_append (store, map);
+}
+
+static void
+project_id_changed_cb (GbpShellcmdShortcutProvider *self,
+ GParamSpec *pspec,
+ IdeContext *context)
+{
+ g_autoptr(GbpShellcmdCommandModel) project_model = NULL;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (GBP_IS_SHELLCMD_SHORTCUT_PROVIDER (self));
+ g_assert (IDE_IS_CONTEXT (context));
+
+ if (g_list_model_get_n_items (G_LIST_MODEL (self->model)) > 1)
+ g_list_store_remove (self->model, 0);
+
+ if (ide_context_has_project (context))
+ {
+ project_model = gbp_shellcmd_command_model_new_for_project (context);
+ add_with_mapping (self->model, G_LIST_MODEL (project_model), TRUE);
+ }
+}
+
+static GListModel *
+gbp_shellcmd_shortcut_provider_list_shortcuts (IdeShortcutProvider *provider)
+{
+ GbpShellcmdShortcutProvider *self = (GbpShellcmdShortcutProvider *)provider;
+ GtkFlattenListModel *ret;
+
+ IDE_ENTRY;
+
+ g_assert (IDE_IS_MAIN_THREAD ());
+ g_assert (GBP_IS_SHELLCMD_SHORTCUT_PROVIDER (self));
+
+ if (self->model == NULL)
+ {
+ IdeContext *context = ide_object_get_context (IDE_OBJECT (self));
+ g_autoptr(GbpShellcmdCommandModel) app_model = gbp_shellcmd_command_model_new_for_app ();
+ g_autoptr(GbpShellcmdCommandModel) project_model = NULL;
+
+ self->model = g_list_store_new (G_TYPE_LIST_MODEL);
+
+ if (ide_context_has_project (context))
+ project_model = gbp_shellcmd_command_model_new_for_project (context);
+ else
+ g_signal_connect_object (context,
+ "notify::project-id",
+ G_CALLBACK (project_id_changed_cb),
+ self,
+ G_CONNECT_SWAPPED);
+
+ if (project_model != NULL)
+ add_with_mapping (self->model, G_LIST_MODEL (project_model), TRUE);
+ add_with_mapping (self->model, G_LIST_MODEL (app_model), FALSE);
+ }
+
+ ret = gtk_flatten_list_model_new (g_object_ref (G_LIST_MODEL (self->model)));
+
+ IDE_RETURN (G_LIST_MODEL (ret));
+}
+
+static void
+shortcut_provider_iface_init (IdeShortcutProviderInterface *iface)
+{
+ iface->list_shortcuts = gbp_shellcmd_shortcut_provider_list_shortcuts;
+}
+
+G_DEFINE_FINAL_TYPE_WITH_CODE (GbpShellcmdShortcutProvider, gbp_shellcmd_shortcut_provider, IDE_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (IDE_TYPE_SHORTCUT_PROVIDER,
shortcut_provider_iface_init))
+
+static void
+gbp_shellcmd_shortcut_provider_dispose (GObject *object)
+{
+ GbpShellcmdShortcutProvider *self = (GbpShellcmdShortcutProvider *)object;
+
+ g_clear_object (&self->model);
+
+ G_OBJECT_CLASS (gbp_shellcmd_shortcut_provider_parent_class)->dispose (object);
+}
+
+static void
+gbp_shellcmd_shortcut_provider_class_init (GbpShellcmdShortcutProviderClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gbp_shellcmd_shortcut_provider_dispose;
+}
+
+static void
+gbp_shellcmd_shortcut_provider_init (GbpShellcmdShortcutProvider *self)
+{
+}
diff --git a/src/plugins/shellcmd/gbp-shellcmd-command-provider.h
b/src/plugins/shellcmd/gbp-shellcmd-shortcut-provider.h
similarity index 69%
rename from src/plugins/shellcmd/gbp-shellcmd-command-provider.h
rename to src/plugins/shellcmd/gbp-shellcmd-shortcut-provider.h
index b744937a1..7707d7b61 100644
--- a/src/plugins/shellcmd/gbp-shellcmd-command-provider.h
+++ b/src/plugins/shellcmd/gbp-shellcmd-shortcut-provider.h
@@ -1,6 +1,6 @@
-/* gbp-shellcmd-command-provider.h
+/* gbp-shellcmd-shortcut-provider.h
*
- * Copyright 2019 Christian Hergert <chergert redhat com>
+ * 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
@@ -20,12 +20,12 @@
#pragma once
-#include <libide-gui.h>
+#include <libide-core.h>
G_BEGIN_DECLS
-#define GBP_TYPE_SHELLCMD_COMMAND_PROVIDER (gbp_shellcmd_command_provider_get_type())
+#define GBP_TYPE_SHELLCMD_SHORTCUT_PROVIDER (gbp_shellcmd_shortcut_provider_get_type())
-G_DECLARE_FINAL_TYPE (GbpShellcmdCommandProvider, gbp_shellcmd_command_provider, GBP,
SHELLCMD_COMMAND_PROVIDER, GObject)
+G_DECLARE_FINAL_TYPE (GbpShellcmdShortcutProvider, gbp_shellcmd_shortcut_provider, GBP,
SHELLCMD_SHORTCUT_PROVIDER, IdeObject)
G_END_DECLS
diff --git a/src/plugins/shellcmd/meson.build b/src/plugins/shellcmd/meson.build
index e6d03e030..23251b1a6 100644
--- a/src/plugins/shellcmd/meson.build
+++ b/src/plugins/shellcmd/meson.build
@@ -2,24 +2,23 @@ if get_option('plugin_shellcmd')
plugins_sources += files([
'shellcmd-plugin.c',
- 'gbp-shellcmd-application-addin.c',
- 'gbp-shellcmd-command.c',
- 'gbp-shellcmd-command-editor.c',
+ 'gbp-shellcmd-command-dialog.c',
'gbp-shellcmd-command-model.c',
- 'gbp-shellcmd-command-provider.c',
- 'gbp-shellcmd-command-row.c',
- 'gbp-shellcmd-list.c',
'gbp-shellcmd-preferences-addin.c',
+ 'gbp-shellcmd-run-command.c',
+ 'gbp-shellcmd-run-command-provider.c',
+ 'gbp-shellcmd-shortcut-provider.c',
])
plugin_shellcmd_enum_headers = [
- 'gbp-shellcmd-command.h',
+ 'gbp-shellcmd-run-command.h',
]
plugin_shellcmd_enums = gnome.mkenums_simple('gbp-shellcmd-enums',
body_prefix: '#include "config.h"',
- header_prefix: '#include <libide-gui.h>',
+ header_prefix: '#include <libide-core.h>',
sources: plugin_shellcmd_enum_headers,
+ install_header: false,
)
plugin_shellcmd_resources = gnome.compile_resources(
@@ -28,8 +27,11 @@ plugin_shellcmd_resources = gnome.compile_resources(
c_name: 'gbp_shellcmd',
)
-plugins_sources += plugin_shellcmd_enums
plugins_sources += plugin_shellcmd_resources
+plugins_sources += plugin_shellcmd_enums
plugins_include_directories += [include_directories('.')]
+install_data(['org.gnome.builder.shellcmd.gschema.xml'], install_dir: schema_dir)
+install_data(['org.gnome.builder.shellcmd.command.gschema.xml'], install_dir: schema_dir)
+
endif
diff --git a/src/plugins/shellcmd/org.gnome.builder.shellcmd.command.gschema.xml
b/src/plugins/shellcmd/org.gnome.builder.shellcmd.command.gschema.xml
new file mode 100644
index 000000000..24b537223
--- /dev/null
+++ b/src/plugins/shellcmd/org.gnome.builder.shellcmd.command.gschema.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schemalist>
+ <schema id="org.gnome.builder.shellcmd.command" gettext-domain="gnome-builder">
+ <key name="display-name" type="s">
+ <default>''</default>
+ <summary>Display Name</summary>
+ </key>
+ <key name="accelerator" type="s">
+ <default>''</default>
+ <summary>Keyboard Accelerator</summary>
+ </key>
+ <key name="cwd" type="s">
+ <default>'$BUILDDIR/'</default>
+ <summary>Current Working Directory</summary>
+ </key>
+ <key name="argv" type="as">
+ <default>[]</default>
+ <summary>Command Arguments</summary>
+ </key>
+ <key name="env" type="as">
+ <default>[]</default>
+ <summary>Command Environment</summary>
+ </key>
+ <key name="locality" type="s">
+ <choices>
+ <choice value="host"/>
+ <choice value="pipeline"/>
+ <choice value="runtime"/>
+ <choice value="subprocess"/>
+ </choices>
+ <default>'pipeline'</default>
+ <summary>Command Locality</summary>
+ </key>
+ </schema>
+</schemalist>
diff --git a/src/plugins/shellcmd/org.gnome.builder.shellcmd.gschema.xml
b/src/plugins/shellcmd/org.gnome.builder.shellcmd.gschema.xml
new file mode 100644
index 000000000..cb6abf007
--- /dev/null
+++ b/src/plugins/shellcmd/org.gnome.builder.shellcmd.gschema.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schemalist>
+ <schema id="org.gnome.builder.shellcmd" gettext-domain="gnome-builder">
+ <key name="run-commands" type="as">
+ <default>[]</default>
+ <summary>Run Commands</summary>
+ <description>A list of run-command ids to load for the application or project.</description>
+ </key>
+ </schema>
+</schemalist>
diff --git a/src/plugins/shellcmd/shellcmd-plugin.c b/src/plugins/shellcmd/shellcmd-plugin.c
index 26b841cc8..4a22fe1c2 100644
--- a/src/plugins/shellcmd/shellcmd-plugin.c
+++ b/src/plugins/shellcmd/shellcmd-plugin.c
@@ -1,6 +1,6 @@
/* shellcmd-plugin.c
*
- * Copyright 2019 Christian Hergert <chergert redhat com>
+ * Copyright 2019-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
@@ -20,23 +20,25 @@
#include "config.h"
-#include <libide-gui.h>
#include <libpeas/peas.h>
-#include "gbp-shellcmd-application-addin.h"
-#include "gbp-shellcmd-command-provider.h"
+#include <libide-foundry.h>
+#include <libide-gui.h>
+
#include "gbp-shellcmd-preferences-addin.h"
+#include "gbp-shellcmd-run-command-provider.h"
+#include "gbp-shellcmd-shortcut-provider.h"
_IDE_EXTERN void
_gbp_shellcmd_register_types (PeasObjectModule *module)
{
- peas_object_module_register_extension_type (module,
- IDE_TYPE_APPLICATION_ADDIN,
- GBP_TYPE_SHELLCMD_APPLICATION_ADDIN);
- peas_object_module_register_extension_type (module,
- IDE_TYPE_COMMAND_PROVIDER,
- GBP_TYPE_SHELLCMD_COMMAND_PROVIDER);
peas_object_module_register_extension_type (module,
IDE_TYPE_PREFERENCES_ADDIN,
GBP_TYPE_SHELLCMD_PREFERENCES_ADDIN);
+ peas_object_module_register_extension_type (module,
+ IDE_TYPE_RUN_COMMAND_PROVIDER,
+ GBP_TYPE_SHELLCMD_RUN_COMMAND_PROVIDER);
+ peas_object_module_register_extension_type (module,
+ IDE_TYPE_SHORTCUT_PROVIDER,
+ GBP_TYPE_SHELLCMD_SHORTCUT_PROVIDER);
}
diff --git a/src/plugins/shellcmd/shellcmd.gresource.xml b/src/plugins/shellcmd/shellcmd.gresource.xml
index 47849fb90..9a01bc34d 100644
--- a/src/plugins/shellcmd/shellcmd.gresource.xml
+++ b/src/plugins/shellcmd/shellcmd.gresource.xml
@@ -2,7 +2,6 @@
<gresources>
<gresource prefix="/plugins/shellcmd">
<file>shellcmd.plugin</file>
- <file preprocess="xml-stripblanks">gbp-shellcmd-command-editor.ui</file>
- <file preprocess="xml-stripblanks">gbp-shellcmd-command-row.ui</file>
+ <file preprocess="xml-stripblanks">gbp-shellcmd-command-dialog.ui</file>
</gresource>
</gresources>
diff --git a/src/plugins/shellcmd/shellcmd.plugin b/src/plugins/shellcmd/shellcmd.plugin
index bfd130ff7..8aa4d21fb 100644
--- a/src/plugins/shellcmd/shellcmd.plugin
+++ b/src/plugins/shellcmd/shellcmd.plugin
@@ -1,10 +1,10 @@
[Plugin]
Authors=Christian Hergert <christian hergert me>
Builtin=true
-Copyright=Copyright © 2019 Christian Hergert
-Depends=editor;
-Description=Run shell commands from your project
+Copyright=Copyright © 2019-2022 Christian Hergert
+Description=Use custom commands to run your project
Embedded=_gbp_shellcmd_register_types
Hidden=true
Module=shellcmd
Name=Shell Commands
+X-Preferences-Kind=application;project;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]