From 91a5bbaeee8db976c39af9bee7c840594a4b3c21 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Sat, 18 May 2019 12:12:19 -0700 Subject: [PATCH] libsysprof-ui: start on launch UI --- src/libsysprof-ui/libsysprof-ui.gresource.xml | 1 + src/libsysprof-ui/meson.build | 4 + .../sysprof-environ-editor-row.c | 278 +++++++++++++ .../sysprof-environ-editor-row.h | 38 ++ src/libsysprof-ui/sysprof-environ-editor.c | 318 +++++++++++++++ src/libsysprof-ui/sysprof-environ-editor.h | 38 ++ src/libsysprof-ui/sysprof-environ-variable.c | 185 +++++++++ src/libsysprof-ui/sysprof-environ-variable.h | 40 ++ src/libsysprof-ui/sysprof-environ.c | 377 ++++++++++++++++++ src/libsysprof-ui/sysprof-environ.h | 52 +++ .../sysprof-profiler-assistant.c | 32 ++ .../ui/sysprof-environ-editor-row.ui | 49 +++ .../ui/sysprof-profiler-assistant.ui | 147 ++++--- 13 files changed, 1503 insertions(+), 56 deletions(-) create mode 100644 src/libsysprof-ui/sysprof-environ-editor-row.c create mode 100644 src/libsysprof-ui/sysprof-environ-editor-row.h create mode 100644 src/libsysprof-ui/sysprof-environ-editor.c create mode 100644 src/libsysprof-ui/sysprof-environ-editor.h create mode 100644 src/libsysprof-ui/sysprof-environ-variable.c create mode 100644 src/libsysprof-ui/sysprof-environ-variable.h create mode 100644 src/libsysprof-ui/sysprof-environ.c create mode 100644 src/libsysprof-ui/sysprof-environ.h create mode 100644 src/libsysprof-ui/ui/sysprof-environ-editor-row.ui diff --git a/src/libsysprof-ui/libsysprof-ui.gresource.xml b/src/libsysprof-ui/libsysprof-ui.gresource.xml index 3e3df45f..95849177 100644 --- a/src/libsysprof-ui/libsysprof-ui.gresource.xml +++ b/src/libsysprof-ui/libsysprof-ui.gresource.xml @@ -14,6 +14,7 @@ ui/sysprof-display.ui ui/sysprof-details-view.ui ui/sysprof-empty-state-view.ui + ui/sysprof-environ-editor-row.ui ui/sysprof-failed-state-view.ui ui/sysprof-marks-view.ui ui/sysprof-process-model-row.ui diff --git a/src/libsysprof-ui/meson.build b/src/libsysprof-ui/meson.build index f64f7a3b..ddafd97f 100644 --- a/src/libsysprof-ui/meson.build +++ b/src/libsysprof-ui/meson.build @@ -30,6 +30,10 @@ libsysprof_ui_private_sources = [ 'sysprof-cairo.c', 'sysprof-cell-renderer-duration.c', 'sysprof-cell-renderer-percent.c', + 'sysprof-environ-editor-row.c', + 'sysprof-environ-editor.c', + 'sysprof-environ-variable.c', + 'sysprof-environ.c', 'sysprof-tab.c', 'sysprof-theme-manager.c', '../stackstash.c', diff --git a/src/libsysprof-ui/sysprof-environ-editor-row.c b/src/libsysprof-ui/sysprof-environ-editor-row.c new file mode 100644 index 00000000..d1ab1002 --- /dev/null +++ b/src/libsysprof-ui/sysprof-environ-editor-row.c @@ -0,0 +1,278 @@ +/* sysprof-environ-editor-row.c + * + * Copyright 2016-2019 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define G_LOG_DOMAIN "sysprof-environ-editor-row" + +#include "config.h" + +#include "sysprof-environ-editor-row.h" + +struct _SysprofEnvironEditorRow +{ + GtkListBoxRow parent_instance; + + SysprofEnvironVariable *variable; + + GtkEntry *key_entry; + GtkEntry *value_entry; + GtkButton *delete_button; + + GBinding *key_binding; + GBinding *value_binding; +}; + +enum { + PROP_0, + PROP_VARIABLE, + LAST_PROP +}; + +enum { + DELETE, + LAST_SIGNAL +}; + +G_DEFINE_TYPE (SysprofEnvironEditorRow, sysprof_environ_editor_row, GTK_TYPE_LIST_BOX_ROW) + +static GParamSpec *properties [LAST_PROP]; +static guint signals [LAST_SIGNAL]; + +static gboolean +null_safe_mapping (GBinding *binding, + const GValue *from_value, + GValue *to_value, + gpointer user_data) +{ + const gchar *str = g_value_get_string (from_value); + g_value_set_string (to_value, str ?: ""); + return TRUE; +} + +static void +sysprof_environ_editor_row_connect (SysprofEnvironEditorRow *self) +{ + g_assert (SYSPROF_IS_ENVIRON_EDITOR_ROW (self)); + g_assert (SYSPROF_IS_ENVIRON_VARIABLE (self->variable)); + + self->key_binding = + g_object_bind_property_full (self->variable, "key", self->key_entry, "text", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL, + null_safe_mapping, NULL, NULL, NULL); + + self->value_binding = + g_object_bind_property_full (self->variable, "value", self->value_entry, "text", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL, + null_safe_mapping, NULL, NULL, NULL); +} + +static void +sysprof_environ_editor_row_disconnect (SysprofEnvironEditorRow *self) +{ + g_assert (SYSPROF_IS_ENVIRON_EDITOR_ROW (self)); + g_assert (SYSPROF_IS_ENVIRON_VARIABLE (self->variable)); + + g_clear_pointer (&self->key_binding, g_binding_unbind); + g_clear_pointer (&self->value_binding, g_binding_unbind); +} + +static void +delete_button_clicked (GtkButton *button, + SysprofEnvironEditorRow *self) +{ + g_assert (GTK_IS_BUTTON (button)); + g_assert (SYSPROF_IS_ENVIRON_EDITOR_ROW (self)); + + g_signal_emit (self, signals [DELETE], 0); +} + +static void +key_entry_activate (GtkWidget *entry, + SysprofEnvironEditorRow *self) +{ + g_assert (GTK_IS_ENTRY (entry)); + g_assert (SYSPROF_IS_ENVIRON_EDITOR_ROW (self)); + + gtk_widget_grab_focus (GTK_WIDGET (self->value_entry)); +} + +static void +value_entry_activate (GtkWidget *entry, + SysprofEnvironEditorRow *self) +{ + GtkWidget *parent; + + g_assert (GTK_IS_ENTRY (entry)); + g_assert (SYSPROF_IS_ENVIRON_EDITOR_ROW (self)); + + gtk_widget_grab_focus (GTK_WIDGET (self)); + parent = gtk_widget_get_ancestor (GTK_WIDGET (self), GTK_TYPE_LIST_BOX); + g_signal_emit_by_name (parent, "move-cursor", GTK_MOVEMENT_DISPLAY_LINES, 1); +} + +static void +sysprof_environ_editor_row_destroy (GtkWidget *widget) +{ + SysprofEnvironEditorRow *self = (SysprofEnvironEditorRow *)widget; + + if (self->variable != NULL) + { + sysprof_environ_editor_row_disconnect (self); + g_clear_object (&self->variable); + } + + GTK_WIDGET_CLASS (sysprof_environ_editor_row_parent_class)->destroy (widget); +} + +static void +sysprof_environ_editor_row_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofEnvironEditorRow *self = SYSPROF_ENVIRON_EDITOR_ROW (object); + + switch (prop_id) + { + case PROP_VARIABLE: + g_value_set_object (value, sysprof_environ_editor_row_get_variable (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_environ_editor_row_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofEnvironEditorRow *self = SYSPROF_ENVIRON_EDITOR_ROW (object); + + switch (prop_id) + { + case PROP_VARIABLE: + sysprof_environ_editor_row_set_variable (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_environ_editor_row_class_init (SysprofEnvironEditorRowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->get_property = sysprof_environ_editor_row_get_property; + object_class->set_property = sysprof_environ_editor_row_set_property; + + widget_class->destroy = sysprof_environ_editor_row_destroy; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-environ-editor-row.ui"); + gtk_widget_class_bind_template_child (widget_class, SysprofEnvironEditorRow, delete_button); + gtk_widget_class_bind_template_child (widget_class, SysprofEnvironEditorRow, key_entry); + gtk_widget_class_bind_template_child (widget_class, SysprofEnvironEditorRow, value_entry); + + properties [PROP_VARIABLE] = + g_param_spec_object ("variable", + "Variable", + "Variable", + SYSPROF_TYPE_ENVIRON_VARIABLE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, LAST_PROP, properties); + + signals [DELETE] = + g_signal_new ("delete", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, G_TYPE_NONE, 0); +} + +static void +sysprof_environ_editor_row_init (SysprofEnvironEditorRow *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); + + g_signal_connect (self->delete_button, + "clicked", + G_CALLBACK (delete_button_clicked), + self); + + g_signal_connect (self->key_entry, + "activate", + G_CALLBACK (key_entry_activate), + self); + + g_signal_connect (self->value_entry, + "activate", + G_CALLBACK (value_entry_activate), + self); +} + +/** + * sysprof_environ_editor_row_get_variable: + * + * Returns: (transfer none) (nullable): An #SysprofEnvironVariable. + */ +SysprofEnvironVariable * +sysprof_environ_editor_row_get_variable (SysprofEnvironEditorRow *self) +{ + g_return_val_if_fail (SYSPROF_IS_ENVIRON_EDITOR_ROW (self), NULL); + + return self->variable; +} + +void +sysprof_environ_editor_row_set_variable (SysprofEnvironEditorRow *self, + SysprofEnvironVariable *variable) +{ + g_return_if_fail (SYSPROF_IS_ENVIRON_EDITOR_ROW (self)); + g_return_if_fail (!variable || SYSPROF_IS_ENVIRON_VARIABLE (variable)); + + if (variable != self->variable) + { + if (self->variable != NULL) + { + sysprof_environ_editor_row_disconnect (self); + g_clear_object (&self->variable); + } + + if (variable != NULL) + { + self->variable = g_object_ref (variable); + sysprof_environ_editor_row_connect (self); + } + + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_VARIABLE]); + } +} + +void +sysprof_environ_editor_row_start_editing (SysprofEnvironEditorRow *self) +{ + g_return_if_fail (SYSPROF_IS_ENVIRON_EDITOR_ROW (self)); + + gtk_widget_grab_focus (GTK_WIDGET (self->key_entry)); +} diff --git a/src/libsysprof-ui/sysprof-environ-editor-row.h b/src/libsysprof-ui/sysprof-environ-editor-row.h new file mode 100644 index 00000000..39437e5a --- /dev/null +++ b/src/libsysprof-ui/sysprof-environ-editor-row.h @@ -0,0 +1,38 @@ +/* ide-environ-editor-row.h + * + * Copyright 2016-2019 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-environ-variable.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_ENVIRON_EDITOR_ROW (sysprof_environ_editor_row_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofEnvironEditorRow, sysprof_environ_editor_row, SYSPROF, ENVIRON_EDITOR_ROW, GtkListBoxRow) + +SysprofEnvironVariable *sysprof_environ_editor_row_get_variable (SysprofEnvironEditorRow *self); +void sysprof_environ_editor_row_set_variable (SysprofEnvironEditorRow *self, + SysprofEnvironVariable *variable); +void sysprof_environ_editor_row_start_editing (SysprofEnvironEditorRow *self); + +G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-environ-editor.c b/src/libsysprof-ui/sysprof-environ-editor.c new file mode 100644 index 00000000..a8538de1 --- /dev/null +++ b/src/libsysprof-ui/sysprof-environ-editor.c @@ -0,0 +1,318 @@ +/* sysprof-environ-editor.c + * + * Copyright 2016-2019 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define G_LOG_DOMAIN "sysprof-environ-editor" + +#include "config.h" + +#include + +#include "sysprof-environ-editor.h" +#include "sysprof-environ-editor-row.h" + +struct _SysprofEnvironEditor +{ + GtkListBox parent_instance; + SysprofEnviron *environ; + GtkWidget *dummy_row; + SysprofEnvironVariable *dummy; +}; + +G_DEFINE_TYPE (SysprofEnvironEditor, sysprof_environ_editor, GTK_TYPE_LIST_BOX) + +enum { + PROP_0, + PROP_ENVIRON, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_environ_editor_delete_row (SysprofEnvironEditor *self, + SysprofEnvironEditorRow *row) +{ + SysprofEnvironVariable *variable; + + g_assert (SYSPROF_IS_ENVIRON_EDITOR (self)); + g_assert (SYSPROF_IS_ENVIRON_EDITOR_ROW (row)); + + variable = sysprof_environ_editor_row_get_variable (row); + sysprof_environ_remove (self->environ, variable); +} + +static GtkWidget * +sysprof_environ_editor_create_dummy_row (SysprofEnvironEditor *self) +{ + GtkWidget *row; + GtkWidget *label; + + g_assert (SYSPROF_IS_ENVIRON_EDITOR (self)); + + label = g_object_new (GTK_TYPE_LABEL, + "label", _("New variable…"), + "visible", TRUE, + "xalign", 0.0f, + NULL); + gtk_style_context_add_class (gtk_widget_get_style_context (label), "dim-label"); + + row = g_object_new (GTK_TYPE_LIST_BOX_ROW, + "child", label, + "visible", TRUE, + NULL); + + return row; +} + +static GtkWidget * +sysprof_environ_editor_create_row (gpointer item, + gpointer user_data) +{ + SysprofEnvironVariable *variable = item; + SysprofEnvironEditor *self = user_data; + SysprofEnvironEditorRow *row; + + g_assert (SYSPROF_IS_ENVIRON_EDITOR (self)); + g_assert (SYSPROF_IS_ENVIRON_VARIABLE (variable)); + + row = g_object_new (SYSPROF_TYPE_ENVIRON_EDITOR_ROW, + "variable", variable, + "visible", TRUE, + NULL); + + g_signal_connect_object (row, + "delete", + G_CALLBACK (sysprof_environ_editor_delete_row), + self, + G_CONNECT_SWAPPED); + + return GTK_WIDGET (row); +} + +static void +sysprof_environ_editor_disconnect (SysprofEnvironEditor *self) +{ + g_assert (SYSPROF_IS_ENVIRON_EDITOR (self)); + g_assert (SYSPROF_IS_ENVIRON (self->environ)); + + gtk_list_box_bind_model (GTK_LIST_BOX (self), NULL, NULL, NULL, NULL); + + g_clear_object (&self->dummy); +} + +static void +sysprof_environ_editor_connect (SysprofEnvironEditor *self) +{ + g_assert (SYSPROF_IS_ENVIRON_EDITOR (self)); + g_assert (SYSPROF_IS_ENVIRON (self->environ)); + + gtk_list_box_bind_model (GTK_LIST_BOX (self), + G_LIST_MODEL (self->environ), + sysprof_environ_editor_create_row, self, NULL); + + self->dummy_row = sysprof_environ_editor_create_dummy_row (self); + gtk_container_add (GTK_CONTAINER (self), self->dummy_row); +} + +static void +find_row_cb (GtkWidget *widget, + gpointer data) +{ + struct { + SysprofEnvironVariable *variable; + SysprofEnvironEditorRow *row; + } *lookup = data; + + g_assert (lookup != NULL); + g_assert (GTK_IS_LIST_BOX_ROW (widget)); + + if (SYSPROF_IS_ENVIRON_EDITOR_ROW (widget)) + { + SysprofEnvironVariable *variable; + + variable = sysprof_environ_editor_row_get_variable (SYSPROF_ENVIRON_EDITOR_ROW (widget)); + + if (variable == lookup->variable) + lookup->row = SYSPROF_ENVIRON_EDITOR_ROW (widget); + } +} + +static SysprofEnvironEditorRow * +find_row (SysprofEnvironEditor *self, + SysprofEnvironVariable *variable) +{ + struct { + SysprofEnvironVariable *variable; + SysprofEnvironEditorRow *row; + } lookup = { variable, NULL }; + + g_assert (SYSPROF_IS_ENVIRON_EDITOR (self)); + g_assert (SYSPROF_IS_ENVIRON_VARIABLE (variable)); + + gtk_container_foreach (GTK_CONTAINER (self), find_row_cb, &lookup); + + return lookup.row; +} + +static void +sysprof_environ_editor_row_activated (GtkListBox *list_box, + GtkListBoxRow *row) +{ + SysprofEnvironEditor *self = (SysprofEnvironEditor *)list_box; + + g_assert (GTK_IS_LIST_BOX (list_box)); + g_assert (GTK_IS_LIST_BOX_ROW (row)); + + if (self->environ == NULL) + return; + + if (self->dummy_row == GTK_WIDGET (row)) + { + g_autoptr(SysprofEnvironVariable) variable = NULL; + + variable = sysprof_environ_variable_new (NULL, NULL); + sysprof_environ_append (self->environ, variable); + sysprof_environ_editor_row_start_editing (find_row (self, variable)); + } +} + +static void +sysprof_environ_editor_destroy (GtkWidget *widget) +{ + SysprofEnvironEditor *self = (SysprofEnvironEditor *)widget; + + GTK_WIDGET_CLASS (sysprof_environ_editor_parent_class)->destroy (widget); + + g_clear_object (&self->environ); +} + +static void +sysprof_environ_editor_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofEnvironEditor *self = SYSPROF_ENVIRON_EDITOR (object); + + switch (prop_id) + { + case PROP_ENVIRON: + g_value_set_object (value, sysprof_environ_editor_get_environ (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + } +} + +static void +sysprof_environ_editor_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofEnvironEditor *self = SYSPROF_ENVIRON_EDITOR (object); + + switch (prop_id) + { + case PROP_ENVIRON: + sysprof_environ_editor_set_environ (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + } +} + +static void +sysprof_environ_editor_class_init (SysprofEnvironEditorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GtkListBoxClass *list_box_class = GTK_LIST_BOX_CLASS (klass); + + object_class->get_property = sysprof_environ_editor_get_property; + object_class->set_property = sysprof_environ_editor_set_property; + + widget_class->destroy = sysprof_environ_editor_destroy; + + list_box_class->row_activated = sysprof_environ_editor_row_activated; + + properties [PROP_ENVIRON] = + g_param_spec_object ("environ", + "Environment", + "Environment", + SYSPROF_TYPE_ENVIRON, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_environ_editor_init (SysprofEnvironEditor *self) +{ + gtk_list_box_set_selection_mode (GTK_LIST_BOX (self), GTK_SELECTION_NONE); +} + +GtkWidget * +sysprof_environ_editor_new (void) +{ + return g_object_new (SYSPROF_TYPE_ENVIRON_EDITOR, NULL); +} + +void +sysprof_environ_editor_set_environ (SysprofEnvironEditor *self, + SysprofEnviron *environ) +{ + g_return_if_fail (SYSPROF_IS_ENVIRON_EDITOR (self)); + g_return_if_fail (SYSPROF_IS_ENVIRON (environ)); + + if (self->environ != environ) + { + if (self->environ != NULL) + { + sysprof_environ_editor_disconnect (self); + g_clear_object (&self->environ); + } + + if (environ != NULL) + { + self->environ = g_object_ref (environ); + sysprof_environ_editor_connect (self); + } + + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ENVIRON]); + } +} + +/** + * sysprof_environ_editor_get_environ: + * + * Returns: (nullable) (transfer none): An #SysprofEnviron or %NULL. + * + * Since: 3.34 + */ +SysprofEnviron * +sysprof_environ_editor_get_environ (SysprofEnvironEditor *self) +{ + g_return_val_if_fail (SYSPROF_IS_ENVIRON_EDITOR (self), NULL); + + return self->environ; +} diff --git a/src/libsysprof-ui/sysprof-environ-editor.h b/src/libsysprof-ui/sysprof-environ-editor.h new file mode 100644 index 00000000..fe4239a6 --- /dev/null +++ b/src/libsysprof-ui/sysprof-environ-editor.h @@ -0,0 +1,38 @@ +/* sysprof-environ-editor.h + * + * Copyright 2016-2019 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-environ.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_ENVIRON_EDITOR (sysprof_environ_editor_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofEnvironEditor, sysprof_environ_editor, SYSPROF, ENVIRON_EDITOR, GtkListBox) + +GtkWidget *sysprof_environ_editor_new (void); +SysprofEnviron *sysprof_environ_editor_get_environ (SysprofEnvironEditor *self); +void sysprof_environ_editor_set_environ (SysprofEnvironEditor *self, + SysprofEnviron *environ); + +G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-environ-variable.c b/src/libsysprof-ui/sysprof-environ-variable.c new file mode 100644 index 00000000..660b3457 --- /dev/null +++ b/src/libsysprof-ui/sysprof-environ-variable.c @@ -0,0 +1,185 @@ +/* sysprof-environ-variable.c + * + * Copyright 2016-2019 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define G_LOG_DOMAIN "sysprof-environ-variable" + +#include "config.h" + +#include "sysprof-environ-variable.h" + +struct _SysprofEnvironVariable +{ + GObject parent_instance; + gchar *key; + gchar *value; +}; + +G_DEFINE_TYPE (SysprofEnvironVariable, sysprof_environ_variable, G_TYPE_OBJECT) + +enum { + PROP_0, + PROP_KEY, + PROP_VALUE, + LAST_PROP +}; + +static GParamSpec *properties [LAST_PROP]; + +static void +sysprof_environ_variable_finalize (GObject *object) +{ + SysprofEnvironVariable *self = (SysprofEnvironVariable *)object; + + g_clear_pointer (&self->key, g_free); + g_clear_pointer (&self->value, g_free); + + G_OBJECT_CLASS (sysprof_environ_variable_parent_class)->finalize (object); +} + +static void +sysprof_environ_variable_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofEnvironVariable *self = SYSPROF_ENVIRON_VARIABLE(object); + + switch (prop_id) + { + case PROP_KEY: + g_value_set_string (value, self->key); + break; + + case PROP_VALUE: + g_value_set_string (value, self->value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + } +} + +static void +sysprof_environ_variable_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofEnvironVariable *self = SYSPROF_ENVIRON_VARIABLE(object); + + switch (prop_id) + { + case PROP_KEY: + sysprof_environ_variable_set_key (self, g_value_get_string (value)); + break; + + case PROP_VALUE: + sysprof_environ_variable_set_value (self, g_value_get_string (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + } +} + +static void +sysprof_environ_variable_class_init (SysprofEnvironVariableClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_environ_variable_finalize; + object_class->get_property = sysprof_environ_variable_get_property; + object_class->set_property = sysprof_environ_variable_set_property; + + properties [PROP_KEY] = + g_param_spec_string ("key", + "Key", + "The key for the environment variable", + NULL, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_VALUE] = + g_param_spec_string ("value", + "Value", + "The value for the environment variable", + NULL, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, LAST_PROP, properties); +} + +static void +sysprof_environ_variable_init (SysprofEnvironVariable *self) +{ +} + +const gchar * +sysprof_environ_variable_get_key (SysprofEnvironVariable *self) +{ + g_return_val_if_fail (SYSPROF_IS_ENVIRON_VARIABLE (self), NULL); + + return self->key; +} + +void +sysprof_environ_variable_set_key (SysprofEnvironVariable *self, + const gchar *key) +{ + g_return_if_fail (SYSPROF_IS_ENVIRON_VARIABLE (self)); + + if (g_strcmp0 (key, self->key) != 0) + { + g_free (self->key); + self->key = g_strdup (key); + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_KEY]); + } +} + +const gchar * +sysprof_environ_variable_get_value (SysprofEnvironVariable *self) +{ + g_return_val_if_fail (SYSPROF_IS_ENVIRON_VARIABLE (self), NULL); + + return self->value; +} + +void +sysprof_environ_variable_set_value (SysprofEnvironVariable *self, + const gchar *value) +{ + g_return_if_fail (SYSPROF_IS_ENVIRON_VARIABLE (self)); + + if (g_strcmp0 (value, self->value) != 0) + { + g_free (self->value); + self->value = g_strdup (value); + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_VALUE]); + } +} + +SysprofEnvironVariable * +sysprof_environ_variable_new (const gchar *key, + const gchar *value) +{ + return g_object_new (SYSPROF_TYPE_ENVIRON_VARIABLE, + "key", key, + "value", value, + NULL); +} diff --git a/src/libsysprof-ui/sysprof-environ-variable.h b/src/libsysprof-ui/sysprof-environ-variable.h new file mode 100644 index 00000000..ee19876a --- /dev/null +++ b/src/libsysprof-ui/sysprof-environ-variable.h @@ -0,0 +1,40 @@ +/* sysprof-environ-variable.h + * + * Copyright 2016-2019 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_ENVIRON_VARIABLE (sysprof_environ_variable_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofEnvironVariable, sysprof_environ_variable, SYSPROF, ENVIRON_VARIABLE, GObject) + +SysprofEnvironVariable *sysprof_environ_variable_new (const gchar *key, + const gchar *value); +const gchar *sysprof_environ_variable_get_key (SysprofEnvironVariable *self); +void sysprof_environ_variable_set_key (SysprofEnvironVariable *self, + const gchar *key); +const gchar *sysprof_environ_variable_get_value (SysprofEnvironVariable *self); +void sysprof_environ_variable_set_value (SysprofEnvironVariable *self, + const gchar *value); + +G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-environ.c b/src/libsysprof-ui/sysprof-environ.c new file mode 100644 index 00000000..a87eeb1c --- /dev/null +++ b/src/libsysprof-ui/sysprof-environ.c @@ -0,0 +1,377 @@ +/* sysprof-environ.c + * + * Copyright 2016-2019 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define G_LOG_DOMAIN "sysprof-environ" + +#include "config.h" + +#include "sysprof-environ.h" +#include "sysprof-environ-variable.h" + +struct _SysprofEnviron +{ + GObject parent_instance; + GPtrArray *variables; +}; + +static void list_model_iface_init (GListModelInterface *iface); + +G_DEFINE_TYPE_EXTENDED (SysprofEnviron, sysprof_environ, G_TYPE_OBJECT, 0, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) + +enum { + CHANGED, + LAST_SIGNAL +}; + +static guint signals [LAST_SIGNAL]; + +static void +sysprof_environ_finalize (GObject *object) +{ + SysprofEnviron *self = (SysprofEnviron *)object; + + g_clear_pointer (&self->variables, g_ptr_array_unref); + + G_OBJECT_CLASS (sysprof_environ_parent_class)->finalize (object); +} + +static void +sysprof_environ_class_init (SysprofEnvironClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_environ_finalize; + + signals [CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + g_signal_set_va_marshaller (signals [CHANGED], + G_TYPE_FROM_CLASS (klass), + g_cclosure_marshal_VOID__VOIDv); +} + +static void +sysprof_environ_items_changed (SysprofEnviron *self) +{ + g_assert (SYSPROF_IS_ENVIRON (self)); + + g_signal_emit (self, signals [CHANGED], 0); +} + +static void +sysprof_environ_init (SysprofEnviron *self) +{ + self->variables = g_ptr_array_new_with_free_func (g_object_unref); + + g_signal_connect (self, + "items-changed", + G_CALLBACK (sysprof_environ_items_changed), + NULL); +} + +static GType +sysprof_environ_get_item_type (GListModel *model) +{ + return SYSPROF_TYPE_ENVIRON_VARIABLE; +} + +static gpointer +sysprof_environ_get_item (GListModel *model, + guint position) +{ + SysprofEnviron *self = (SysprofEnviron *)model; + + g_return_val_if_fail (SYSPROF_IS_ENVIRON (self), NULL); + g_return_val_if_fail (position < self->variables->len, NULL); + + return g_object_ref (g_ptr_array_index (self->variables, position)); +} + +static guint +sysprof_environ_get_n_items (GListModel *model) +{ + SysprofEnviron *self = (SysprofEnviron *)model; + + g_return_val_if_fail (SYSPROF_IS_ENVIRON (self), 0); + + return self->variables->len; +} + +static void +list_model_iface_init (GListModelInterface *iface) +{ + iface->get_n_items = sysprof_environ_get_n_items; + iface->get_item = sysprof_environ_get_item; + iface->get_item_type = sysprof_environ_get_item_type; +} + +static void +sysprof_environ_variable_notify (SysprofEnviron *self, + GParamSpec *pspec, + SysprofEnvironVariable *variable) +{ + g_assert (SYSPROF_IS_ENVIRON (self)); + + g_signal_emit (self, signals [CHANGED], 0); +} + +void +sysprof_environ_setenv (SysprofEnviron *self, + const gchar *key, + const gchar *value) +{ + guint i; + + g_return_if_fail (SYSPROF_IS_ENVIRON (self)); + g_return_if_fail (key != NULL); + + for (i = 0; i < self->variables->len; i++) + { + SysprofEnvironVariable *var = g_ptr_array_index (self->variables, i); + const gchar *var_key = sysprof_environ_variable_get_key (var); + + if (g_strcmp0 (key, var_key) == 0) + { + if (value == NULL) + { + g_ptr_array_remove_index (self->variables, i); + g_list_model_items_changed (G_LIST_MODEL (self), i, 1, 0); + return; + } + + sysprof_environ_variable_set_value (var, value); + return; + } + } + + if (value != NULL) + { + SysprofEnvironVariable *var; + guint position = self->variables->len; + + var = g_object_new (SYSPROF_TYPE_ENVIRON_VARIABLE, + "key", key, + "value", value, + NULL); + g_signal_connect_object (var, + "notify", + G_CALLBACK (sysprof_environ_variable_notify), + self, + G_CONNECT_SWAPPED); + g_ptr_array_add (self->variables, var); + g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1); + } +} + +const gchar * +sysprof_environ_getenv (SysprofEnviron *self, + const gchar *key) +{ + guint i; + + g_return_val_if_fail (SYSPROF_IS_ENVIRON (self), NULL); + g_return_val_if_fail (key != NULL, NULL); + + for (i = 0; i < self->variables->len; i++) + { + SysprofEnvironVariable *var = g_ptr_array_index (self->variables, i); + const gchar *var_key = sysprof_environ_variable_get_key (var); + + if (g_strcmp0 (key, var_key) == 0) + return sysprof_environ_variable_get_value (var); + } + + return NULL; +} + +/** + * sysprof_environ_get_environ: + * @self: An #SysprofEnviron + * + * Gets the environment as a set of key=value pairs, suitable for use + * in various GLib process functions. + * + * Returns: (transfer full): A newly allocated string array. + * + * Since: 3.32 + */ +gchar ** +sysprof_environ_get_environ (SysprofEnviron *self) +{ + GPtrArray *ar; + guint i; + + g_return_val_if_fail (SYSPROF_IS_ENVIRON (self), NULL); + + ar = g_ptr_array_new (); + + for (i = 0; i < self->variables->len; i++) + { + SysprofEnvironVariable *var = g_ptr_array_index (self->variables, i); + const gchar *key = sysprof_environ_variable_get_key (var); + const gchar *value = sysprof_environ_variable_get_value (var); + + if (value == NULL) + value = ""; + + if (key != NULL) + g_ptr_array_add (ar, g_strdup_printf ("%s=%s", key, value)); + } + + g_ptr_array_add (ar, NULL); + + return (gchar **)g_ptr_array_free (ar, FALSE); +} + +SysprofEnviron * +sysprof_environ_new (void) +{ + return g_object_new (SYSPROF_TYPE_ENVIRON, NULL); +} + +void +sysprof_environ_remove (SysprofEnviron *self, + SysprofEnvironVariable *variable) +{ + guint i; + + g_return_if_fail (SYSPROF_IS_ENVIRON (self)); + g_return_if_fail (SYSPROF_IS_ENVIRON_VARIABLE (variable)); + + for (i = 0; i < self->variables->len; i++) + { + SysprofEnvironVariable *item = g_ptr_array_index (self->variables, i); + + if (item == variable) + { + g_ptr_array_remove_index (self->variables, i); + g_list_model_items_changed (G_LIST_MODEL (self), i, 1, 0); + break; + } + } +} + +void +sysprof_environ_append (SysprofEnviron *self, + SysprofEnvironVariable *variable) +{ + guint position; + + g_return_if_fail (SYSPROF_IS_ENVIRON (self)); + g_return_if_fail (SYSPROF_IS_ENVIRON_VARIABLE (variable)); + + position = self->variables->len; + + g_signal_connect_object (variable, + "notify", + G_CALLBACK (sysprof_environ_variable_notify), + self, + G_CONNECT_SWAPPED); + g_ptr_array_add (self->variables, g_object_ref (variable)); + g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1); +} + +/** + * sysprof_environ_copy: + * @self: An #SysprofEnviron + * + * Copies the contents of #SysprofEnviron into a newly allocated #SysprofEnviron. + * + * Returns: (transfer full): An #SysprofEnviron. + * + * Since: 3.32 + */ +SysprofEnviron * +sysprof_environ_copy (SysprofEnviron *self) +{ + g_autoptr(SysprofEnviron) copy = NULL; + + g_return_val_if_fail (SYSPROF_IS_ENVIRON (self), NULL); + + copy = sysprof_environ_new (); + sysprof_environ_copy_into (self, copy, TRUE); + + return g_steal_pointer (©); +} + +void +sysprof_environ_copy_into (SysprofEnviron *self, + SysprofEnviron *dest, + gboolean replace) +{ + g_return_if_fail (SYSPROF_IS_ENVIRON (self)); + g_return_if_fail (SYSPROF_IS_ENVIRON (dest)); + + for (guint i = 0; i < self->variables->len; i++) + { + SysprofEnvironVariable *var = g_ptr_array_index (self->variables, i); + const gchar *key = sysprof_environ_variable_get_key (var); + const gchar *value = sysprof_environ_variable_get_value (var); + + if (replace || sysprof_environ_getenv (dest, key) == NULL) + sysprof_environ_setenv (dest, key, value); + } +} + +/** + * ide_environ_parse: + * @pair: the KEY=VALUE pair + * @key: (out) (optional): a location for a @key + * @value: (out) (optional): a location for a @value + * + * Parses a KEY=VALUE style key-pair into @key and @value. + * + * Returns: %TRUE if @pair was successfully parsed + * + * Since: 3.32 + */ +gboolean +ide_environ_parse (const gchar *pair, + gchar **key, + gchar **value) +{ + const gchar *eq; + + g_return_val_if_fail (pair != NULL, FALSE); + + if (key != NULL) + *key = NULL; + + if (value != NULL) + *value = NULL; + + if ((eq = strchr (pair, '='))) + { + if (key != NULL) + *key = g_strndup (pair, eq - pair); + + if (value != NULL) + *value = g_strdup (eq + 1); + + return TRUE; + } + + return FALSE; +} diff --git a/src/libsysprof-ui/sysprof-environ.h b/src/libsysprof-ui/sysprof-environ.h new file mode 100644 index 00000000..ef5e5f83 --- /dev/null +++ b/src/libsysprof-ui/sysprof-environ.h @@ -0,0 +1,52 @@ +/* sysprof-environ.h + * + * Copyright 2016-2019 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-environ-variable.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_ENVIRON (sysprof_environ_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofEnviron, sysprof_environ, SYSPROF, ENVIRON, GObject) + +gboolean ide_environ_parse (const gchar *pair, + gchar **key, + gchar **value); +SysprofEnviron *sysprof_environ_new (void); +void sysprof_environ_setenv (SysprofEnviron *self, + const gchar *key, + const gchar *value); +const gchar *sysprof_environ_getenv (SysprofEnviron *self, + const gchar *key); +gchar **sysprof_environ_get_environ (SysprofEnviron *self); +void sysprof_environ_append (SysprofEnviron *self, + SysprofEnvironVariable *variable); +void sysprof_environ_remove (SysprofEnviron *self, + SysprofEnvironVariable *variable); +SysprofEnviron *sysprof_environ_copy (SysprofEnviron *self); +void sysprof_environ_copy_into (SysprofEnviron *self, + SysprofEnviron *dest, + gboolean replace); + +G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-profiler-assistant.c b/src/libsysprof-ui/sysprof-profiler-assistant.c index 79d1c2b3..5558b246 100644 --- a/src/libsysprof-ui/sysprof-profiler-assistant.c +++ b/src/libsysprof-ui/sysprof-profiler-assistant.c @@ -24,6 +24,7 @@ #include +#include "sysprof-environ-editor.h" #include "sysprof-profiler-assistant.h" #include "sysprof-process-model-row.h" @@ -32,6 +33,7 @@ struct _SysprofProfilerAssistant GtkBin parent_instance; /* Template Objects */ + GtkEntry *command_line; GtkRevealer *process_revealer; GtkListBox *process_list_box; }; @@ -98,14 +100,38 @@ sysprof_profiler_assistant_row_activated_cb (SysprofProfilerAssistant *self, !sysprof_process_model_row_get_selected (row)); } +static void +sysprof_profiler_assistant_command_line_changed_cb (SysprofProfilerAssistant *self, + GtkEntry *entry) +{ + g_auto(GStrv) argv = NULL; + GtkStyleContext *style_context; + const gchar *text; + gint argc; + + g_assert (SYSPROF_IS_PROFILER_ASSISTANT (self)); + g_assert (GTK_IS_ENTRY (entry)); + + style_context = gtk_widget_get_style_context (GTK_WIDGET (entry)); + text = gtk_entry_get_text (entry); + + if (text == NULL || text[0] == 0 || g_shell_parse_argv (text, &argc, &argv, NULL)) + gtk_style_context_remove_class (style_context, GTK_STYLE_CLASS_ERROR); + else + gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_ERROR); +} + static void sysprof_profiler_assistant_class_init (SysprofProfilerAssistantClass *klass) { GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-profiler-assistant.ui"); + gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, command_line); gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, process_list_box); gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, process_revealer); + + g_type_ensure (SYSPROF_TYPE_ENVIRON_EDITOR); } static void @@ -113,6 +139,12 @@ sysprof_profiler_assistant_init (SysprofProfilerAssistant *self) { gtk_widget_init_template (GTK_WIDGET (self)); + g_signal_connect_object (self->command_line, + "changed", + G_CALLBACK (sysprof_profiler_assistant_command_line_changed_cb), + self, + G_CONNECT_SWAPPED); + g_signal_connect_object (self->process_list_box, "row-activated", G_CALLBACK (sysprof_profiler_assistant_row_activated_cb), diff --git a/src/libsysprof-ui/ui/sysprof-environ-editor-row.ui b/src/libsysprof-ui/ui/sysprof-environ-editor-row.ui new file mode 100644 index 00000000..56b176a2 --- /dev/null +++ b/src/libsysprof-ui/ui/sysprof-environ-editor-row.ui @@ -0,0 +1,49 @@ + + + diff --git a/src/libsysprof-ui/ui/sysprof-profiler-assistant.ui b/src/libsysprof-ui/ui/sysprof-profiler-assistant.ui index d4993358..dd2abc58 100644 --- a/src/libsysprof-ui/ui/sysprof-profiler-assistant.ui +++ b/src/libsysprof-ui/ui/sysprof-profiler-assistant.ui @@ -45,7 +45,7 @@ true - Enabling this will request that Sysprof generate callgraph information for all applications and the operating system kernel. This may not always be possible depending on the system configuration. + Enable to generate callgraph information for all applications and the operating system kernel. This may not be possible on some system system configurations. 6 10 true @@ -118,6 +118,7 @@ Launch Application 1.0 + start true + + + + + + true + + + 6 + 6 + 12 + vertical + true + + + Command Line + 0.0 + true + + + + + + + + + + true + + + + + Environment + 0.0 + true + + + + + + + + + + in + true + true + + + true + + + + + + + + center 2 - - - start - true - - - - view-more-symbolic - true - - - - - right - 3 - - - - - horizontal - true - 500 - true - - - - false - Existing Process - true - true - - - - - false - New Process - whole_system - true - true - - - - - center - 3 - - _Record