[gnome-builder] meson: add UI to create a new toolchain



commit 29818b140aed69f07abad480ec1daaf1f63f179d
Author: Corentin Noël <corentin noel collabora co uk>
Date:   Tue May 8 11:05:17 2018 +0100

    meson: add UI to create a new toolchain

 src/plugins/meson/gbp-meson-tool-row.c             | 230 +++++++++++
 src/plugins/meson/gbp-meson-tool-row.h             |  36 ++
 src/plugins/meson/gbp-meson-tool-row.ui            |  35 ++
 ...gbp-meson-toolchain-edition-preferences-addin.c | 257 ++++++++++++
 ...gbp-meson-toolchain-edition-preferences-addin.h |  30 ++
 .../gbp-meson-toolchain-edition-preferences-row.c  | 460 +++++++++++++++++++++
 .../gbp-meson-toolchain-edition-preferences-row.h  |  35 ++
 .../gbp-meson-toolchain-edition-preferences-row.ui | 259 ++++++++++++
 src/plugins/meson/gbp-meson-utils.c                |  34 ++
 src/plugins/meson/gbp-meson-utils.h                |   2 +
 src/plugins/meson/meson-plugin.c                   |   2 +
 src/plugins/meson/meson.build                      |   6 +
 src/plugins/meson/meson.gresource.xml              |   4 +
 13 files changed, 1390 insertions(+)
---
diff --git a/src/plugins/meson/gbp-meson-tool-row.c b/src/plugins/meson/gbp-meson-tool-row.c
new file mode 100644
index 000000000..c409f3ce8
--- /dev/null
+++ b/src/plugins/meson/gbp-meson-tool-row.c
@@ -0,0 +1,230 @@
+/* gbp-meson-tool-row.c
+ *
+ * Copyright (C) 2018 Corentin Noël <corentin noel collabora com>
+ * Copyright (C) 2018 Collabora Ltd.
+ *
+ * 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/>.
+ */
+
+#define G_LOG_DOMAIN "gbp-meson-tool-row"
+
+#include <glib/gi18n.h>
+
+#include "gbp-meson-tool-row.h"
+#include "gbp-meson-utils.h"
+
+struct _GbpMesonToolRow
+{
+  GtkListBoxRow parent_instance;
+  gchar *tool_path;
+  gchar *tool_id;
+  gchar *lang_id;
+
+  GtkLabel *name_label;
+  GtkButton *delete_button;
+};
+
+G_DEFINE_TYPE (GbpMesonToolRow, gbp_meson_tool_row, GTK_TYPE_LIST_BOX_ROW)
+
+enum {
+  PROP_0,
+  PROP_TOOL_PATH,
+  PROP_TOOL_ID,
+  PROP_LANG_ID,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+enum {
+  TOOL_REMOVED,
+  N_SIGNALS
+};
+
+static guint signals [N_SIGNALS];
+
+GbpMesonToolRow *
+gbp_meson_tool_row_new (const gchar *tool_id,
+                        const gchar *tool_path,
+                        const gchar *lang_id)
+{
+  GbpMesonToolRow *tool_row;
+
+  g_return_val_if_fail (tool_id != NULL, NULL);
+  g_return_val_if_fail (tool_path != NULL, NULL);
+
+  tool_row = g_object_new (GBP_TYPE_MESON_TOOL_ROW,
+                           "tool-id", tool_id,
+                           "tool-path", tool_path,
+                           "lang-id", lang_id,
+                           "visible", TRUE,
+                           NULL);
+
+  return tool_row;
+}
+
+static void
+meson_tool_row_delete (GbpMesonToolRow *self,
+                       gpointer         user_data)
+{
+  g_assert (GBP_IS_MESON_TOOL_ROW (self));
+
+  g_signal_emit (self, signals[TOOL_REMOVED], 0);
+
+  gtk_widget_destroy (GTK_WIDGET (self));
+}
+
+const gchar *
+gbp_meson_tool_row_get_tool_id (GbpMesonToolRow *tool_row)
+{
+  g_return_val_if_fail (GBP_IS_MESON_TOOL_ROW (tool_row), NULL);
+
+  return tool_row->tool_id;
+}
+
+static void
+gbp_meson_tool_row_get_property (GObject    *object,
+                                          guint       prop_id,
+                                          GValue     *value,
+                                          GParamSpec *pspec)
+{
+  GbpMesonToolRow *self = GBP_MESON_TOOL_ROW (object);
+
+  switch (prop_id)
+    {
+    case PROP_TOOL_PATH:
+      g_value_set_string (value, self->tool_path);
+      break;
+
+    case PROP_TOOL_ID:
+      g_value_set_string (value, self->tool_id);
+      break;
+
+    case PROP_LANG_ID:
+      g_value_set_string (value, self->lang_id);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_meson_tool_row_set_property (GObject      *object,
+                                          guint         prop_id,
+                                          const GValue *value,
+                                          GParamSpec   *pspec)
+{
+  GbpMesonToolRow *self = GBP_MESON_TOOL_ROW (object);
+
+  switch (prop_id)
+    {
+    case PROP_TOOL_PATH:
+      self->tool_path = g_value_dup_string (value);
+      break;
+
+    case PROP_TOOL_ID:
+      self->tool_id = g_value_dup_string (value);
+      break;
+
+    case PROP_LANG_ID:
+      self->lang_id = g_value_dup_string (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_meson_tool_row_finalize (GObject *object)
+{
+  GbpMesonToolRow *self = (GbpMesonToolRow *) object;
+
+  g_clear_pointer (&self->tool_path, g_free);
+  g_clear_pointer (&self->tool_id, g_free);
+  g_clear_pointer (&self->lang_id, g_free);
+
+  G_OBJECT_CLASS (gbp_meson_tool_row_parent_class)->finalize (object);
+}
+
+static void
+gbp_meson_tool_row_constructed (GObject *object)
+{
+  GbpMesonToolRow *self = (GbpMesonToolRow *) object;
+  const gchar *tool_name = gbp_meson_get_tool_display_name (self->tool_id);
+  if (self->lang_id != NULL && g_strcmp0 (self->lang_id, "*") != 0)
+    {
+      g_autofree gchar *complete_name = g_strdup_printf ("%s (%s)", tool_name, self->lang_id);
+      gtk_label_set_label (self->name_label, complete_name);
+    }
+  else
+    gtk_label_set_label (self->name_label, tool_name);
+}
+
+static void
+gbp_meson_tool_row_class_init (GbpMesonToolRowClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = gbp_meson_tool_row_finalize;
+  object_class->get_property = gbp_meson_tool_row_get_property;
+  object_class->set_property = gbp_meson_tool_row_set_property;
+  object_class->constructed = gbp_meson_tool_row_constructed;
+
+  properties [PROP_TOOL_PATH] =
+    g_param_spec_string ("tool-path",
+                         "Tool Path",
+                         "The absolute path of the tool",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_TOOL_ID] =
+    g_param_spec_string ("tool-id",
+                         "Tool ID",
+                         "The internal identifier of the tool",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_LANG_ID] =
+    g_param_spec_string ("lang-id",
+                         "Tool Path",
+                         "The language the tool should be used for",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+
+  signals [TOOL_REMOVED] =
+    g_signal_new_class_handler ("tool-removed",
+                                G_TYPE_FROM_CLASS (klass),
+                                G_SIGNAL_RUN_FIRST,
+                                NULL, NULL, NULL, NULL,
+                                G_TYPE_NONE,
+                                0);
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/builder/plugins/meson-plugin/gbp-meson-tool-row.ui");
+  gtk_widget_class_bind_template_child (widget_class, GbpMesonToolRow, name_label);
+  gtk_widget_class_bind_template_child (widget_class, GbpMesonToolRow, delete_button);
+}
+
+static void
+gbp_meson_tool_row_init (GbpMesonToolRow *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  g_signal_connect_swapped (self->delete_button, "clicked", G_CALLBACK (meson_tool_row_delete), self);
+  g_object_bind_property (self, "tool-path", self, "tooltip-text", G_BINDING_DEFAULT);
+}
diff --git a/src/plugins/meson/gbp-meson-tool-row.h b/src/plugins/meson/gbp-meson-tool-row.h
new file mode 100644
index 000000000..4c75f444d
--- /dev/null
+++ b/src/plugins/meson/gbp-meson-tool-row.h
@@ -0,0 +1,36 @@
+/* gbp-meson-tool-row.h
+ *
+ * Copyright (C) 2018 Corentin Noël <corentin noel collabora com>
+ * Copyright (C) 2018 Collabora Ltd.
+ *
+ * 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/>.
+ */
+
+#pragma once
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_MESON_TOOL_ROW (gbp_meson_tool_row_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpMesonToolRow, gbp_meson_tool_row, GBP, MESON_TOOL_ROW, GtkListBoxRow)
+
+GbpMesonToolRow  *gbp_meson_tool_row_new (const gchar *tool_id,
+                                          const gchar *tool_path,
+                                          const gchar *lang_id);
+
+const gchar      *gbp_meson_tool_row_get_tool_id (GbpMesonToolRow *tool_row);
+
+G_END_DECLS
diff --git a/src/plugins/meson/gbp-meson-tool-row.ui b/src/plugins/meson/gbp-meson-tool-row.ui
new file mode 100644
index 000000000..2b615cdd5
--- /dev/null
+++ b/src/plugins/meson/gbp-meson-tool-row.ui
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.18 -->
+  <template class="GbpMesonToolRow" parent="GtkListBoxRow">
+    <child>
+      <object class="GtkGrid" id="grid">
+        <property name="orientation">horizontal</property>
+        <property name="column-spacing">12</property>
+        <property name="margin">6</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkLabel" id="name_label">
+            <property name="hexpand">true</property>
+            <property name="visible">true</property>
+            <property name="xalign">0.0</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkButton" id="delete_button">
+            <style>
+              <class name="destructive-action"/>
+            </style>
+            <property name="image">delete-image</property>
+            <property name="visible">true</property>
+            <property name="tooltip_text" translatable="yes">Delete Tool</property>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GtkImage" id="delete-image">
+    <property name="icon-name">edit-delete-symbolic</property>
+    <property name="visible">true</property>
+  </object>
+</interface>
diff --git a/src/plugins/meson/gbp-meson-toolchain-edition-preferences-addin.c 
b/src/plugins/meson/gbp-meson-toolchain-edition-preferences-addin.c
new file mode 100644
index 000000000..27f2a90a2
--- /dev/null
+++ b/src/plugins/meson/gbp-meson-toolchain-edition-preferences-addin.c
@@ -0,0 +1,257 @@
+/* gbp-meson-toolchain-edition-preferences.c
+ *
+ * Copyright (C) 2018 Corentin Noël <corentin noel collabora com>
+ * Copyright (C) 2018 Collabora Ltd.
+ *
+ * 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, eitIher 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/>.
+ */
+
+#define G_LOG_DOMAIN "gbp-meson-toolchain-edition-preferences-addin"
+
+#include <glib/gi18n.h>
+
+#include "gbp-meson-toolchain-edition-preferences-addin.h"
+#include "gbp-meson-toolchain-edition-preferences-row.h"
+
+struct _GbpMesonToolchainEditionPreferencesAddin
+{
+  GObject         parent_instance;
+
+  GArray         *ids;
+  DzlPreferences *preferences;
+  GCancellable   *cancellable;
+};
+
+static void
+meson_toolchain_edition_preferences_add_new (GbpMesonToolchainEditionPreferencesAddin *self,
+                                             GtkWidget                                *emitter)
+{
+  g_autofree gchar *user_folder_path = NULL;
+  g_autofree gchar *new_target = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GFile) file = NULL;
+  g_autoptr(GFileOutputStream) output_stream = NULL;
+  GbpMesonToolchainEditionPreferencesRow *pref_row;
+  guint id = 0;
+
+  g_assert (GBP_IS_MESON_TOOLCHAIN_EDITION_PREFERENCES_ADDIN (self));
+  g_assert (DZL_IS_PREFERENCES_BIN (emitter));
+
+  user_folder_path = g_build_filename (g_get_user_data_dir (), "meson", "cross", NULL);
+  for (uint i = 0; i < UINT_MAX; i++)
+    {
+      g_autofree gchar *possible_target = g_strdup_printf ("new_file%u", i);
+      g_autofree gchar *possible_path = g_build_filename (user_folder_path, possible_target, NULL);
+      if (!g_file_test (possible_path, G_FILE_TEST_EXISTS)) {
+        new_target = g_steal_pointer (&possible_path);
+        break;
+      }
+    }
+
+  pref_row = g_object_new (GBP_TYPE_MESON_TOOLCHAIN_EDITION_PREFERENCES_ROW,
+                           "toolchain-path", new_target,
+                           "visible", TRUE,
+                           NULL);
+
+  file = g_file_new_for_path (new_target);
+  output_stream = g_file_create (file, G_FILE_CREATE_NONE, NULL, &error);
+
+  id = dzl_preferences_add_custom (self->preferences, "sdk", "toolchain", GTK_WIDGET (pref_row), "", 1);
+  g_array_append_val (self->ids, id);
+
+  gbp_meson_toolchain_edition_preferences_row_show_popup (pref_row);
+}
+
+static GtkWidget *
+toolchain_edition_preferences_get_add_widget (GbpMesonToolchainEditionPreferencesAddin *self)
+{
+  GtkWidget *bin = NULL;
+  GtkWidget *grid = NULL;
+  GtkWidget *label = NULL;
+  GtkWidget *subtitle = NULL;
+  GtkWidget *image = NULL;
+
+  bin = g_object_new (DZL_TYPE_PREFERENCES_BIN,
+                      "visible", TRUE,
+                      NULL);
+
+  grid = g_object_new (GTK_TYPE_GRID,
+                       "visible", TRUE,
+                       NULL);
+
+  label = g_object_new (GTK_TYPE_LABEL,
+                        "visible", TRUE,
+                        "label", _("Add toolchain"),
+                        "xalign", 0.0f,
+                        "hexpand", TRUE,
+                        NULL);
+
+  subtitle = g_object_new (GTK_TYPE_LABEL,
+                           "visible", TRUE,
+                           "label", g_markup_printf_escaped ("<small>%s</small>", _("Define a new custom 
toolchain targeting a specific platform")),
+                           "use-markup", TRUE,
+                           "xalign", 0.0f,
+                           "hexpand", TRUE,
+                           NULL);
+
+  gtk_style_context_add_class (gtk_widget_get_style_context (subtitle), GTK_STYLE_CLASS_DIM_LABEL);
+
+  image = g_object_new (GTK_TYPE_IMAGE,
+                        "visible", TRUE,
+                        "icon-name", "list-add-symbolic",
+                        "valign", GTK_ALIGN_CENTER,
+                        NULL);
+
+  gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
+  gtk_grid_attach (GTK_GRID (grid), subtitle, 0, 1, 1, 1);
+  gtk_grid_attach (GTK_GRID (grid), image, 1, 0, 1, 2);
+
+  gtk_container_add (GTK_CONTAINER (bin), grid);
+
+  g_signal_connect_object (bin,
+                           "preference-activated",
+                           G_CALLBACK (meson_toolchain_edition_preferences_add_new),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  return bin;
+}
+
+void
+gbp_meson_toolchain_edition_preferences_addin_load_finish (GObject      *object,
+                                                           GAsyncResult *result,
+                                                           gpointer      user_data)
+{
+  GFile *folder = (GFile *)object;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GPtrArray) ret = NULL;
+  GbpMesonToolchainEditionPreferencesAddin *self = (GbpMesonToolchainEditionPreferencesAddin *)user_data;
+
+  g_assert (G_IS_FILE (folder));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (GBP_IS_MESON_TOOLCHAIN_EDITION_PREFERENCES_ADDIN (self));
+
+  ret = ide_g_file_find_finish (folder, result, &error);
+  IDE_PTR_ARRAY_SET_FREE_FUNC (ret, g_object_unref);
+
+  if (ret == NULL)
+    return;
+
+  for (guint i = 0; i < ret->len; i++)
+    {
+      guint id = 0;
+      GFile *file = g_ptr_array_index (ret, i);
+      g_autoptr(GError) load_error = NULL;
+      g_autoptr(GbpMesonToolchainEditionPreferencesRow) pref_row = NULL;
+      g_autofree gchar *path = NULL;
+
+      pref_row = g_object_new (GBP_TYPE_MESON_TOOLCHAIN_EDITION_PREFERENCES_ROW,
+                               "visible", TRUE,
+                               NULL);
+
+      path = g_file_get_path (file);
+      if (!gbp_meson_toolchain_edition_preferences_row_load_file (g_object_ref_sink (pref_row), path, 
&load_error))
+        continue;
+
+      id = dzl_preferences_add_custom (self->preferences, "sdk", "toolchain", GTK_WIDGET (pref_row), NULL, 
i);
+      g_array_append_val (self->ids, id);
+    }
+  
+}
+
+static void
+gbp_meson_toolchain_edition_preferences_addin_load (IdePreferencesAddin *addin,
+                                                    DzlPreferences      *preferences)
+{
+  GbpMesonToolchainEditionPreferencesAddin *self = (GbpMesonToolchainEditionPreferencesAddin *)addin;
+  GtkWidget *widget = NULL;
+  guint id = 0;
+  g_autofree gchar *user_folder_path = NULL;
+  g_autoptr(GFile) user_folder = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_MESON_TOOLCHAIN_EDITION_PREFERENCES_ADDIN (self));
+  g_assert (DZL_IS_PREFERENCES (preferences));
+
+  self->ids = g_array_new (FALSE, FALSE, sizeof (guint));
+  self->preferences = preferences;
+  self->cancellable = g_cancellable_new ();
+
+  dzl_preferences_add_list_group (preferences, "sdk", "toolchain", _("Toolchain"), GTK_SELECTION_NONE, 0);
+
+  widget = toolchain_edition_preferences_get_add_widget (self);
+  id = dzl_preferences_add_custom (preferences, "sdk", "toolchain", widget, "", 0);
+
+  g_array_append_val (self->ids, id);
+
+  user_folder_path = g_build_filename (g_get_user_data_dir (), "meson", "cross", NULL);
+  user_folder = g_file_new_for_path (user_folder_path);
+  ide_g_file_find_async (user_folder,
+                         "*",
+                         self->cancellable,
+                         gbp_meson_toolchain_edition_preferences_addin_load_finish,
+                         self);
+
+  IDE_EXIT;
+}
+
+static void
+gbp_meson_toolchain_edition_preferences_addin_unload (IdePreferencesAddin *addin,
+                                      DzlPreferences      *preferences)
+{
+  GbpMesonToolchainEditionPreferencesAddin *self = (GbpMesonToolchainEditionPreferencesAddin *)addin;
+
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_MESON_TOOLCHAIN_EDITION_PREFERENCES_ADDIN (self));
+  g_assert (DZL_IS_PREFERENCES (preferences));
+
+  /* Clear preferences so reload code doesn't try to
+   * make forward progress updating items.
+   */
+  self->preferences = NULL;
+
+  for (guint i = 0; i < self->ids->len; i++)
+    {
+      guint id = g_array_index (self->ids, guint, i);
+
+      dzl_preferences_remove_id (preferences, id);
+    }
+
+  g_clear_pointer (&self->ids, g_array_unref);
+  g_clear_pointer (&self->cancellable, g_object_unref);
+
+  IDE_EXIT;
+}
+
+static void
+preferences_addin_iface_init (IdePreferencesAddinInterface *iface)
+{
+  iface->load = gbp_meson_toolchain_edition_preferences_addin_load;
+  iface->unload = gbp_meson_toolchain_edition_preferences_addin_unload;
+}
+
+G_DEFINE_TYPE_EXTENDED (GbpMesonToolchainEditionPreferencesAddin, 
gbp_meson_toolchain_edition_preferences_addin, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (IDE_TYPE_PREFERENCES_ADDIN, preferences_addin_iface_init))
+
+static void
+gbp_meson_toolchain_edition_preferences_addin_class_init (GbpMesonToolchainEditionPreferencesAddinClass 
*klass)
+{
+}
+
+static void
+gbp_meson_toolchain_edition_preferences_addin_init (GbpMesonToolchainEditionPreferencesAddin *self)
+{
+}
diff --git a/src/plugins/meson/gbp-meson-toolchain-edition-preferences-addin.h 
b/src/plugins/meson/gbp-meson-toolchain-edition-preferences-addin.h
new file mode 100644
index 000000000..1d0e1e322
--- /dev/null
+++ b/src/plugins/meson/gbp-meson-toolchain-edition-preferences-addin.h
@@ -0,0 +1,30 @@
+/* gbp-meson-toolchain-edition-preferences.h
+ *
+ * Copyright (C) 2018 Corentin Noël <corentin noel collabora com>
+ * Copyright (C) 2018 Collabora Ltd.
+ *
+ * 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, eitIher 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/>.
+ */
+
+#pragma once
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_MESON_TOOLCHAIN_EDITION_PREFERENCES_ADDIN 
(gbp_meson_toolchain_edition_preferences_addin_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpMesonToolchainEditionPreferencesAddin, 
gbp_meson_toolchain_edition_preferences_addin, GBP, MESON_TOOLCHAIN_EDITION_PREFERENCES_ADDIN, GObject)
+
+G_END_DECLS
diff --git a/src/plugins/meson/gbp-meson-toolchain-edition-preferences-row.c 
b/src/plugins/meson/gbp-meson-toolchain-edition-preferences-row.c
new file mode 100644
index 000000000..52a61731c
--- /dev/null
+++ b/src/plugins/meson/gbp-meson-toolchain-edition-preferences-row.c
@@ -0,0 +1,460 @@
+/* gbp-meson-toolchain-edition-preferences-row.c
+ *
+ * Copyright (C) 2018 Corentin Noël <corentin noel collabora com>
+ * Copyright (C) 2018 Collabora Ltd.
+ *
+ * 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/>.
+ */
+
+#define G_LOG_DOMAIN "gbp-meson-toolchain-edition-preferences-row"
+
+#include <glib/gi18n.h>
+
+#include "gbp-meson-toolchain-edition-preferences-row.h"
+#include "gbp-meson-tool-row.h"
+#include "gbp-meson-utils.h"
+
+struct _GbpMesonToolchainEditionPreferencesRow
+{
+  DzlPreferencesBin parent_instance;
+  gchar *toolchain_path;
+  GtkLabel *display_name;
+  GtkEntry *name_entry;
+  DzlFileChooserEntry *sysroot_entry;
+  GtkComboBox *arch_combobox;
+  GtkComboBox *tool_combobox;
+  GtkComboBox *lang_combobox;
+  DzlFileChooserEntry *path_entry;
+  GtkListBox *tools_listbox;
+  GtkButton *add_button;
+  GtkButton *delete_button;
+  GtkPopover *popover;
+};
+
+G_DEFINE_TYPE (GbpMesonToolchainEditionPreferencesRow, gbp_meson_toolchain_edition_preferences_row, 
DZL_TYPE_PREFERENCES_BIN)
+
+enum {
+  PROP_0,
+  PROP_TOOLCHAIN_PATH,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+meson_toolchain_edition_preferences_row_name_changed (GbpMesonToolchainEditionPreferencesRow *self,
+                                                      gpointer                                user_data)
+{
+  const gchar *entry_text;
+  g_autofree gchar *user_folder_path = NULL;
+  g_autofree gchar *possible_path = NULL;
+  GtkStyleContext *style_context;
+
+  g_assert (GBP_IS_MESON_TOOLCHAIN_EDITION_PREFERENCES_ROW (self));
+  g_assert (GTK_IS_ENTRY (user_data));
+
+  style_context = gtk_widget_get_style_context (GTK_WIDGET (user_data));
+  entry_text = gtk_entry_get_text (GTK_ENTRY (user_data));
+  user_folder_path = g_build_filename (g_get_user_data_dir (), "meson", "cross", NULL);
+  possible_path = g_build_filename (user_folder_path, entry_text, NULL);
+  if (g_strcmp0 (possible_path, self->toolchain_path) == 0)
+    {
+      if (gtk_style_context_has_class (style_context, GTK_STYLE_CLASS_ERROR))
+        gtk_style_context_remove_class (style_context, GTK_STYLE_CLASS_ERROR);
+
+      return;
+    }
+
+  if (!g_file_test (possible_path, G_FILE_TEST_EXISTS))
+    {
+      g_autoptr(GFile) source = g_file_new_for_path (self->toolchain_path);
+      g_autoptr(GFile) destination = g_file_new_for_path (possible_path);
+      g_autoptr (GError) error = NULL;
+
+      if (!g_file_move (source, destination, G_FILE_COPY_NONE, NULL, NULL, NULL, &error))
+        {
+          gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_ERROR);
+          g_message ("Unable to rename file: %s", error->message);
+          return;
+        }
+
+      if (gtk_style_context_has_class (style_context, GTK_STYLE_CLASS_ERROR))
+        gtk_style_context_remove_class (style_context, GTK_STYLE_CLASS_ERROR);
+
+      g_object_set (self, "toolchain-path", possible_path, NULL);
+      gtk_label_set_label (self->display_name, entry_text);
+    }
+  else
+    gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_ERROR);
+
+}
+
+static void
+meson_toolchain_edition_preferences_row_arch_changed (GbpMesonToolchainEditionPreferencesRow *self,
+                                                      gpointer                                user_data)
+{
+  g_auto (GStrv) parts = NULL;
+  g_autoptr(GKeyFile) keyfile = g_key_file_new ();
+  g_autoptr(GError) error = NULL;
+  const gchar *entry_text;
+
+  g_assert (GBP_IS_MESON_TOOLCHAIN_EDITION_PREFERENCES_ROW (self));
+  g_assert (GTK_IS_COMBO_BOX (user_data));
+
+  entry_text = gtk_entry_get_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (self->arch_combobox))));
+  parts = g_strsplit (entry_text, "-", 2);
+
+  if (!g_key_file_load_from_file (keyfile, self->toolchain_path, 
G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS, &error))
+    {
+      g_message ("Unable to load file \"%s\": %s", self->toolchain_path, error->message);
+      return;
+    }
+
+  gbp_meson_key_file_set_string_quoted (keyfile, "host_machine", "cpu_family", parts[0]);
+  gbp_meson_key_file_set_string_quoted (keyfile, "host_machine", "cpu", parts[0]);
+  gbp_meson_key_file_set_string_quoted (keyfile, "host_machine", "system", parts[1]);
+  if (!g_key_file_has_key (keyfile, "host_machine", "endian", NULL))
+    gbp_meson_key_file_set_string_quoted (keyfile, "host_machine", "endian", "little");
+
+  if (!g_key_file_save_to_file (keyfile, self->toolchain_path, &error))
+    {
+      g_message ("Unable to remove tool: %s", error->message);
+      return;
+    }
+}
+
+static void
+meson_toolchain_edition_preferences_row_tool_changed (GbpMesonToolchainEditionPreferencesRow *self,
+                                                      gpointer                                user_data)
+{
+  const gchar *active_id;
+  gboolean lang_sensitive;
+
+  g_assert (GBP_IS_MESON_TOOLCHAIN_EDITION_PREFERENCES_ROW (self));
+  g_assert (GTK_IS_COMBO_BOX (user_data));
+
+  active_id = gtk_combo_box_get_active_id (GTK_COMBO_BOX(user_data));
+  lang_sensitive = g_strcmp0 (active_id, IDE_TOOLCHAIN_TOOL_CC) == 0;
+  gtk_widget_set_sensitive (GTK_WIDGET(self->lang_combobox), lang_sensitive);
+}
+
+static void
+meson_toolchain_edition_preferences_row_clicked (GbpMesonToolchainEditionPreferencesRow *self,
+                                                 gpointer                                user_data)
+{
+  g_assert (GBP_IS_MESON_TOOLCHAIN_EDITION_PREFERENCES_ROW (self));
+
+  gbp_meson_toolchain_edition_preferences_row_show_popup (self);
+}
+
+static void
+meson_tool_deleted (GbpMesonToolchainEditionPreferencesRow *self,
+                    gpointer                                user_data)
+{
+  GbpMesonToolRow *tool_row = (GbpMesonToolRow *)user_data;
+  g_autoptr(GKeyFile) keyfile = g_key_file_new ();
+  g_autoptr(GError) error = NULL;
+
+  g_assert (GBP_IS_MESON_TOOLCHAIN_EDITION_PREFERENCES_ROW (self));
+  g_assert (GBP_IS_MESON_TOOL_ROW (tool_row));
+
+  if (!g_key_file_load_from_file (keyfile, self->toolchain_path, 
G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS, &error))
+    {
+      g_message ("Unable to load file \"%s\": %s", self->toolchain_path, error->message);
+      return;
+    }
+
+  if (!g_key_file_remove_key (keyfile, "binaries", gbp_meson_get_tool_binary_name 
(gbp_meson_tool_row_get_tool_id (tool_row)), &error))
+    {
+      g_message ("Unable to remove tool: %s", error->message);
+      return;
+    }
+
+  if (!g_key_file_save_to_file (keyfile, self->toolchain_path, &error))
+    {
+      g_message ("Unable to remove tool: %s", error->message);
+      return;
+    }
+}
+
+static void
+meson_toolchain_edition_preferences_row_add_tool (GbpMesonToolchainEditionPreferencesRow *self,
+                                                  gpointer                                user_data)
+{
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GFile) tool_file = NULL;
+  g_autoptr(GKeyFile) keyfile = g_key_file_new ();
+  g_autofree gchar *tool_path = NULL;
+  const gchar *tool_id;
+  const gchar *lang_id;
+  GbpMesonToolRow *tool_row;
+
+  g_assert (GBP_IS_MESON_TOOLCHAIN_EDITION_PREFERENCES_ROW (self));
+
+  tool_id = gtk_combo_box_get_active_id (self->tool_combobox);
+  lang_id = gtk_combo_box_get_active_id (self->lang_combobox);
+  tool_file = dzl_file_chooser_entry_get_file (self->path_entry);
+  tool_path = g_file_get_path (tool_file);
+
+  if (!g_key_file_load_from_file (keyfile, self->toolchain_path, 
G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS, &error))
+    return;
+
+  if (g_strcmp0 (tool_id, IDE_TOOLCHAIN_TOOL_CC) == 0)
+    gbp_meson_key_file_set_string_quoted (keyfile, "binaries", lang_id, tool_path);
+  else
+    gbp_meson_key_file_set_string_quoted (keyfile, "binaries", gbp_meson_get_tool_binary_name (tool_id), 
tool_path);
+
+  g_key_file_save_to_file (keyfile, self->toolchain_path, &error);
+
+  tool_row = gbp_meson_tool_row_new (tool_id, tool_path, lang_id);
+  gtk_container_add (GTK_CONTAINER (self->tools_listbox), GTK_WIDGET (tool_row));
+  g_signal_connect_swapped (tool_row, "tool-removed", G_CALLBACK (meson_tool_deleted), self);
+}
+
+static void
+meson_toolchain_edition_preferences_row_delete (GbpMesonToolchainEditionPreferencesRow *self,
+                                                gpointer                                user_data)
+{
+  g_autoptr(GFile) file = NULL;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (GBP_IS_MESON_TOOLCHAIN_EDITION_PREFERENCES_ROW (self));
+
+  file = g_file_new_for_path (self->toolchain_path);
+  if (!g_file_delete (file, NULL, &error))
+    {
+      g_message ("Error removing \"%s\": %s", self->toolchain_path, error->message);
+      return;
+    }
+
+  /* The row is wrapped into a GtkListBoxRow that won't be removed when child is destroyed */
+  gtk_widget_destroy (gtk_widget_get_parent (GTK_WIDGET (self)));
+}
+
+gboolean
+gbp_meson_toolchain_edition_preferences_row_load_file (GbpMesonToolchainEditionPreferencesRow  *self,
+                                                       const gchar                             *file_path,
+                                                       GError                                 **error)
+{
+  g_autofree gchar *arch = NULL;
+  g_autofree gchar *system = NULL;
+  g_autoptr(GKeyFile) keyfile = g_key_file_new ();
+  g_autoptr(IdeTriplet) triplet = NULL;
+  g_autoptr(GError) list_error = NULL;
+  g_auto(GStrv) binaries = NULL;
+
+  g_return_val_if_fail (GBP_IS_MESON_TOOLCHAIN_EDITION_PREFERENCES_ROW (self), FALSE);
+  g_return_val_if_fail (file_path != NULL, FALSE);
+  g_return_val_if_fail (error != NULL && *error == NULL, FALSE);
+
+  g_object_set (G_OBJECT (self), "toolchain-path", file_path, NULL);
+  if (!g_key_file_load_from_file (keyfile, self->toolchain_path, 
G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS, error))
+    return FALSE;
+
+  arch = gbp_meson_key_file_get_string_quoted (keyfile, "host_machine", "cpu_family", error);
+  if (arch == NULL)
+    return FALSE;
+
+  system = gbp_meson_key_file_get_string_quoted (keyfile, "host_machine", "system", error);
+  if (system == NULL)
+    return FALSE;
+
+  triplet = ide_triplet_new_with_triplet (arch, system, NULL);
+  gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (self->arch_combobox))), 
ide_triplet_get_full_name (triplet));
+
+  binaries = g_key_file_get_keys (keyfile, "binaries", NULL, &list_error);
+  if (binaries == NULL)
+    return TRUE;
+
+  for (int i = 0; binaries[i] != NULL; i++)
+    {
+      const gchar *lang = binaries[i];
+      const gchar *tool_id;
+      g_autoptr(GError) key_error = NULL;
+      g_autofree gchar *exec_path = gbp_meson_key_file_get_string_quoted (keyfile, "binaries", lang, 
&key_error);
+      GbpMesonToolRow *tool_row;
+
+      tool_id = gbp_meson_get_tool_id_from_binary (lang);
+      if (g_strcmp0 (tool_id, IDE_TOOLCHAIN_TOOL_CC) == 0)
+        tool_row = gbp_meson_tool_row_new (tool_id, exec_path, gbp_meson_get_toolchain_language (lang));
+      else
+        tool_row = gbp_meson_tool_row_new (tool_id, exec_path, IDE_TOOLCHAIN_LANGUAGE_ANY);
+
+      gtk_container_add (GTK_CONTAINER (self->tools_listbox), GTK_WIDGET (tool_row));
+      g_signal_connect_swapped (tool_row, "tool-removed", G_CALLBACK (meson_tool_deleted), self);
+    }
+
+  return TRUE;
+}
+
+static void
+gbp_meson_toolchain_edition_preferences_row_get_property (GObject    *object,
+                                                          guint       prop_id,
+                                                          GValue     *value,
+                                                          GParamSpec *pspec)
+{
+  GbpMesonToolchainEditionPreferencesRow *self = GBP_MESON_TOOLCHAIN_EDITION_PREFERENCES_ROW (object);
+
+  switch (prop_id)
+    {
+    case PROP_TOOLCHAIN_PATH:
+      g_value_set_string (value, self->toolchain_path);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_meson_toolchain_edition_preferences_row_set_property (GObject      *object,
+                                                          guint         prop_id,
+                                                          const GValue *value,
+                                                          GParamSpec   *pspec)
+{
+  GbpMesonToolchainEditionPreferencesRow *self = GBP_MESON_TOOLCHAIN_EDITION_PREFERENCES_ROW (object);
+  g_autofree gchar *user_folder_path = NULL;
+  g_autoptr(GFile) user_folder = NULL;
+  g_autoptr(GFile) file = NULL;
+  g_autofree gchar *row_name = NULL;
+
+  switch (prop_id)
+    {
+    case PROP_TOOLCHAIN_PATH:
+      if (self->toolchain_path != NULL)
+        g_clear_pointer (&self->toolchain_path, g_free);
+
+      self->toolchain_path = g_value_dup_string (value);
+
+      user_folder_path = g_build_filename (g_get_user_data_dir (), "meson", "cross", NULL);
+      user_folder = g_file_new_for_path (user_folder_path);
+      file = g_file_new_for_path (self->toolchain_path);
+
+      row_name = g_file_get_relative_path (user_folder, file);
+      gtk_entry_set_text (self->name_entry, row_name);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_meson_toolchain_edition_preferences_row_finalize (GObject *object)
+{
+  GbpMesonToolchainEditionPreferencesRow *self = (GbpMesonToolchainEditionPreferencesRow *) object;
+
+  g_clear_pointer (&self->toolchain_path, g_free);
+
+  G_OBJECT_CLASS (gbp_meson_toolchain_edition_preferences_row_parent_class)->finalize (object);
+}
+
+/**
+ * gbp_meson_toolchain_edition_preferences_row_show_popup:
+ * @self: a #GbpMesonToolchainEditionPreferencesRow
+ *
+ * Requests the configuration popover the be shown over the widget.
+ */
+void
+gbp_meson_toolchain_edition_preferences_row_show_popup (GbpMesonToolchainEditionPreferencesRow *self)
+{
+  g_return_if_fail (GBP_IS_MESON_TOOLCHAIN_EDITION_PREFERENCES_ROW (self));
+  g_return_if_fail (GTK_IS_POPOVER (self->popover));
+
+  gtk_popover_popup (self->popover);
+  gtk_popover_set_modal (self->popover, TRUE);
+}
+
+static void
+gbp_meson_toolchain_edition_preferences_row_constructed (GObject *object)
+{
+  g_autofree gchar *value = NULL;
+  GbpMesonToolchainEditionPreferencesRow *self = (GbpMesonToolchainEditionPreferencesRow *) object;
+  GtkWidget *label;
+
+  g_object_bind_property (self->name_entry, "text", self->display_name, "label", 0);
+
+  g_signal_connect_object (self->name_entry,
+                           "changed",
+                           G_CALLBACK (meson_toolchain_edition_preferences_row_name_changed),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (self->arch_combobox,
+                           "changed",
+                           G_CALLBACK (meson_toolchain_edition_preferences_row_arch_changed),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (self->tool_combobox,
+                           "changed",
+                           G_CALLBACK (meson_toolchain_edition_preferences_row_tool_changed),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  label = g_object_new (GTK_TYPE_LABEL,
+                        "label", _("No Provided Tool"),
+                        "visible", TRUE,
+                        NULL);
+  gtk_list_box_set_placeholder (self->tools_listbox, label);
+
+  /*g_signal_connect_object (sysroot_manager,
+                           "target-changed",
+                           G_CALLBACK (toolchain_edition_preferences_row_target_changed),
+                           self,
+                           G_CONNECT_SWAPPED);*/
+}
+
+static void
+gbp_meson_toolchain_edition_preferences_row_class_init (GbpMesonToolchainEditionPreferencesRowClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = gbp_meson_toolchain_edition_preferences_row_finalize;
+  object_class->get_property = gbp_meson_toolchain_edition_preferences_row_get_property;
+  object_class->set_property = gbp_meson_toolchain_edition_preferences_row_set_property;
+  object_class->constructed = gbp_meson_toolchain_edition_preferences_row_constructed;
+
+  properties [PROP_TOOLCHAIN_PATH] =
+    g_param_spec_string ("toolchain-path",
+                         "Toolchain Path",
+                         "The absolute path of the toolchain definition file.",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/builder/plugins/meson-plugin/gbp-meson-toolchain-edition-preferences-row.ui");
+  gtk_widget_class_bind_template_child (widget_class, GbpMesonToolchainEditionPreferencesRow, display_name);
+  gtk_widget_class_bind_template_child (widget_class, GbpMesonToolchainEditionPreferencesRow, popover);
+  gtk_widget_class_bind_template_child (widget_class, GbpMesonToolchainEditionPreferencesRow, name_entry);
+  gtk_widget_class_bind_template_child (widget_class, GbpMesonToolchainEditionPreferencesRow, tools_listbox);
+  gtk_widget_class_bind_template_child (widget_class, GbpMesonToolchainEditionPreferencesRow, arch_combobox);
+  gtk_widget_class_bind_template_child (widget_class, GbpMesonToolchainEditionPreferencesRow, tool_combobox);
+  gtk_widget_class_bind_template_child (widget_class, GbpMesonToolchainEditionPreferencesRow, lang_combobox);
+  gtk_widget_class_bind_template_child (widget_class, GbpMesonToolchainEditionPreferencesRow, add_button);
+  gtk_widget_class_bind_template_child (widget_class, GbpMesonToolchainEditionPreferencesRow, delete_button);
+  gtk_widget_class_bind_template_child (widget_class, GbpMesonToolchainEditionPreferencesRow, path_entry);
+}
+
+static void
+gbp_meson_toolchain_edition_preferences_row_init (GbpMesonToolchainEditionPreferencesRow *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  g_signal_connect (self, "preference-activated", G_CALLBACK 
(meson_toolchain_edition_preferences_row_clicked), NULL);
+  g_signal_connect_swapped (self->add_button, "clicked", G_CALLBACK 
(meson_toolchain_edition_preferences_row_add_tool), self);
+  g_signal_connect_swapped (self->delete_button, "clicked", G_CALLBACK 
(meson_toolchain_edition_preferences_row_delete), self);
+}
diff --git a/src/plugins/meson/gbp-meson-toolchain-edition-preferences-row.h 
b/src/plugins/meson/gbp-meson-toolchain-edition-preferences-row.h
new file mode 100644
index 000000000..cf52a9a82
--- /dev/null
+++ b/src/plugins/meson/gbp-meson-toolchain-edition-preferences-row.h
@@ -0,0 +1,35 @@
+/* gbp-meson-toolchain-edition-preferences-row.h
+ *
+ * Copyright (C) 2018 Corentin Noël <corentin noel collabora com>
+ * Copyright (C) 2018 Collabora Ltd.
+ *
+ * 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/>.
+ */
+
+#pragma once
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_MESON_TOOLCHAIN_EDITION_PREFERENCES_ROW 
(gbp_meson_toolchain_edition_preferences_row_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpMesonToolchainEditionPreferencesRow, gbp_meson_toolchain_edition_preferences_row, 
GBP, MESON_TOOLCHAIN_EDITION_PREFERENCES_ROW, DzlPreferencesBin)
+
+void     gbp_meson_toolchain_edition_preferences_row_show_popup (GbpMesonToolchainEditionPreferencesRow  
*self);
+gboolean gbp_meson_toolchain_edition_preferences_row_load_file  (GbpMesonToolchainEditionPreferencesRow  
*self,
+                                                                 const gchar                             
*file_path,
+                                                                 GError                                 
**error);
+
+G_END_DECLS
diff --git a/src/plugins/meson/gbp-meson-toolchain-edition-preferences-row.ui 
b/src/plugins/meson/gbp-meson-toolchain-edition-preferences-row.ui
new file mode 100644
index 000000000..35645158c
--- /dev/null
+++ b/src/plugins/meson/gbp-meson-toolchain-edition-preferences-row.ui
@@ -0,0 +1,259 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.18 -->
+  <template class="GbpMesonToolchainEditionPreferencesRow" parent="DzlPreferencesBin">
+    <child>
+      <object class="GtkGrid" id="grid">
+        <property name="orientation">horizontal</property>
+        <property name="column-spacing">12</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkLabel" id="display_name">
+            <property name="hexpand">true</property>
+            <property name="visible">true</property>
+            <property name="xalign">0.0</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkImage" id="image">
+            <property name="icon-name">pan-down-symbolic</property>
+            <property name="visible">true</property>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GtkListStore" id="arch_liststore">
+    <columns>
+      <column type="gchararray"/>
+    </columns>
+    <data>
+      <row>
+        <col id="0">i386-linux-gnu</col>
+      </row>
+      <row>
+        <col id="0">x86_64-linux-gnu</col>
+      </row>
+      <row>
+        <col id="0">arm-linux-gnu</col>
+      </row>
+      <row>
+        <col id="0">aarch64-linux-gnu</col>
+      </row>
+    </data>
+  </object>
+  <object class="GtkImage" id="add-image">
+    <property name="visible">true</property>
+    <property name="can_focus">false</property>
+    <property name="icon_name">list-add-symbolic</property>
+  </object>
+  <object class="GtkPopover" id="popover">
+    <property name="relative-to">image</property>
+    <property name="position">bottom</property>
+    <property name="can-focus">true</property>
+    <child>
+      <object class="GtkGrid">
+        <property name="border-width">12</property>
+        <property name="orientation">horizontal</property>
+        <property name="column-spacing">12</property>
+        <property name="row-spacing">12</property>
+        <property name="margin">6</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkLabel">
+            <style>
+              <class name="dim-label"/>
+            </style>
+            <property name="visible">true</property>
+            <property name="xalign">1.0</property>
+            <property name="label" translatable="yes">Name</property>
+            <property name="tooltip-text" translatable="yes">A name to identify the sysroot.</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkEntry" id="name_entry">
+            <property name="visible">true</property>
+            <property name="hexpand">true</property>
+            <property name="can-focus">true</property>
+          </object>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel">
+            <style>
+              <class name="dim-label"/>
+            </style>
+            <property name="visible">true</property>
+            <property name="xalign">1.0</property>
+            <property name="label" translatable="yes">Architecture</property>
+            <property name="tooltip-text" translatable="yes">The system architecture of the 
sysroot.</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkComboBox" id="arch_combobox">
+            <property name="visible">true</property>
+            <property name="hexpand">true</property>
+            <property name="has-entry">true</property>
+            <property name="model">arch_liststore</property>
+            <property name="id-column">0</property>
+            <property name="entry-text-column">0</property>
+            <property name="active">-1</property>
+          </object>
+          <packing>
+            <property name="left_attach">1</property>
+            <property name="top_attach">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkScrolledWindow">
+            <property name="visible">true</property>
+            <property name="can_focus">true</property>
+            <property name="shadow_type">in</property>
+            <property name="hscrollbar_policy">never</property>
+            <property name="height_request">100</property>
+            <child>
+              <object class="GtkViewport">
+                <property name="visible">true</property>
+                <property name="can_focus">false</property>
+                <child>
+                  <object class="GtkListBox" id="tools_listbox">
+                    <property name="visible">true</property>
+                    <property name="can_focus">false</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">2</property>
+            <property name="width">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label" translatable="yes">Add Tool:</property>
+            <property name="xalign">0</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">3</property>
+            <property name="width">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkGrid">
+            <property name="visible">true</property>
+            <property name="can_focus">false</property>
+            <style>
+              <class name="linked"/>
+            </style>
+            <child>
+              <object class="GtkComboBoxText" id="tool_combobox">
+                <property name="width_request">150</property>
+                <property name="visible">true</property>
+                <property name="can_focus">false</property>
+                <property name="active">0</property>
+                <items>
+                  <item id="cc" translatable="yes">Compiler</item>
+                  <item id="cpp" translatable="yes">Preprocessor</item>
+                  <item id="ar" translatable="yes">Archiver</item>
+                  <item id="ld" translatable="yes">Linker</item>
+                  <item id="strip" translatable="yes">Strip</item>
+                  <item id="exec" translatable="yes">Executable wrapper</item>
+                  <item id="pkg-config" translatable="yes">Package Config</item>
+                </items>
+              </object>
+            </child>
+            <child>
+              <object class="GtkComboBoxText" id="lang_combobox">
+                <property name="width_request">50</property>
+                <property name="visible">true</property>
+                <property name="can_focus">false</property>
+                <property name="active">0</property>
+                <property name="has_entry">true</property>
+                <items>
+                  <item id="*" translatable="yes">Any language</item>
+                  <item id="c" translatable="yes">C</item>
+                  <item id="c++" translatable="yes">C++</item>
+                  <item id="python" translatable="yes">Python</item>
+                  <item id="vala" translatable="yes">Vala</item>
+                  <item id="fortran" translatable="yes">Fortran</item>
+                  <item id="d" translatable="yes">D</item>
+                </items>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">4</property>
+            <property name="width">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkGrid">
+            <property name="visible">true</property>
+            <property name="can_focus">false</property>
+            <property name="orientation">horizontal</property>
+            <child>
+              <object class="DzlFileChooserEntry" id="path_entry">
+                <property name="visible">true</property>
+                <property name="hexpand">true</property>
+                <property name="can-focus">true</property>
+                <property name="local-only">true</property>
+                <property name="action">GTK_FILE_CHOOSER_ACTION_OPEN</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkButton" id="add_button">
+                <property name="visible">true</property>
+                <property name="can_focus">true</property>
+                <property name="receives_default">true</property>
+                <property name="image">add-image</property>
+                <property name="always_show_image">true</property>
+                <property name="tooltip_text" translatable="yes">Add Tool</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">5</property>
+            <property name="width">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="delete_button">
+            <style>
+              <class name="destructive-action"/>
+            </style>
+            <property name="label" translatable="yes">Delete</property>
+            <property name="visible">true</property>
+            <property name="hexpand">true</property>
+            <property name="can-focus">true</property>
+            <property name="halign">end</property>
+            <property name="margin-top">6</property>
+            <property name="tooltip_text" translatable="yes">Delete Toolchain</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">6</property>
+            <property name="width">2</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/src/plugins/meson/gbp-meson-utils.c b/src/plugins/meson/gbp-meson-utils.c
index dffdc8167..192135079 100644
--- a/src/plugins/meson/gbp-meson-utils.c
+++ b/src/plugins/meson/gbp-meson-utils.c
@@ -87,6 +87,40 @@ gbp_meson_get_toolchain_language (const gchar *meson_tool_name)
   return meson_tool_name;
 }
 
+const gchar *
+gbp_meson_get_tool_display_name (const gchar *tool_id)
+{
+  g_return_val_if_fail (tool_id != NULL, NULL);
+
+  if (g_strcmp0 (tool_id, IDE_TOOLCHAIN_TOOL_CC) == 0)
+    return _("Compiler");
+  if (g_strcmp0 (tool_id, IDE_TOOLCHAIN_TOOL_CPP) == 0)
+    return _("Preprocessor");
+  if (g_strcmp0 (tool_id, IDE_TOOLCHAIN_TOOL_AR) == 0)
+    return _("Archiver");
+  if (g_strcmp0 (tool_id, IDE_TOOLCHAIN_TOOL_LD) == 0)
+    return _("Linker");
+  if (g_strcmp0 (tool_id, IDE_TOOLCHAIN_TOOL_STRIP) == 0)
+    return _("Strip");
+  if (g_strcmp0 (tool_id, IDE_TOOLCHAIN_TOOL_EXEC) == 0)
+    return _("Executable wrapper");
+  if (g_strcmp0 (tool_id, IDE_TOOLCHAIN_TOOL_PKG_CONFIG) == 0)
+    return _("Package Config");
+
+  return tool_id;
+}
+
+const gchar *
+gbp_meson_get_tool_binary_name (const gchar *tool_id)
+{
+  if (g_strcmp0 (tool_id, IDE_TOOLCHAIN_TOOL_PKG_CONFIG) == 0)
+    return "pkgconfig";
+  else if (g_strcmp0 (tool_id, IDE_TOOLCHAIN_TOOL_EXEC) == 0)
+    return "exe_wrapper";
+  else
+    return tool_id;
+}
+
 const gchar *
 gbp_meson_get_tool_id_from_binary (const gchar *meson_tool_name)
 {
diff --git a/src/plugins/meson/gbp-meson-utils.h b/src/plugins/meson/gbp-meson-utils.h
index 2f078f102..a17e87686 100644
--- a/src/plugins/meson/gbp-meson-utils.h
+++ b/src/plugins/meson/gbp-meson-utils.h
@@ -36,6 +36,8 @@ gchar       *gbp_meson_key_file_get_string_quoted       (GKeyFile     *key_file,
                                                          const gchar  *key,
                                                          GError      **error);
 const gchar *gbp_meson_get_toolchain_language           (const gchar  *meson_tool_name);
+const gchar *gbp_meson_get_tool_display_name            (const gchar  *tool_id);
+const gchar *gbp_meson_get_tool_binary_name             (const gchar  *tool_id);
 const gchar *gbp_meson_get_tool_id_from_binary          (const gchar  *meson_tool_name);
 
 G_END_DECLS
diff --git a/src/plugins/meson/meson-plugin.c b/src/plugins/meson/meson-plugin.c
index 03ea314e1..3af544153 100644
--- a/src/plugins/meson/meson-plugin.c
+++ b/src/plugins/meson/meson-plugin.c
@@ -24,6 +24,7 @@
 #include "gbp-meson-pipeline-addin.h"
 #include "gbp-meson-test-provider.h"
 #include "gbp-meson-toolchain-provider.h"
+#include "gbp-meson-toolchain-edition-preferences-addin.h"
 
 void
 gbp_meson_register_types (PeasObjectModule *module)
@@ -33,4 +34,5 @@ gbp_meson_register_types (PeasObjectModule *module)
   peas_object_module_register_extension_type (module, IDE_TYPE_BUILD_TARGET_PROVIDER, 
GBP_TYPE_MESON_BUILD_TARGET_PROVIDER);
   peas_object_module_register_extension_type (module, IDE_TYPE_TEST_PROVIDER, GBP_TYPE_MESON_TEST_PROVIDER);
   peas_object_module_register_extension_type (module, IDE_TYPE_TOOLCHAIN_PROVIDER, 
GBP_TYPE_MESON_TOOLCHAIN_PROVIDER);
+  peas_object_module_register_extension_type (module, IDE_TYPE_PREFERENCES_ADDIN, 
GBP_TYPE_MESON_TOOLCHAIN_EDITION_PREFERENCES_ADDIN);
 }
diff --git a/src/plugins/meson/meson.build b/src/plugins/meson/meson.build
index 6f1cd23f0..90319af7a 100644
--- a/src/plugins/meson/meson.build
+++ b/src/plugins/meson/meson.build
@@ -8,6 +8,10 @@ meson_resources = gnome.compile_resources(
 
 meson_sources = [
   'meson-plugin.c',
+  'gbp-meson-toolchain-edition-preferences-addin.c',
+  'gbp-meson-toolchain-edition-preferences-addin.h',
+  'gbp-meson-toolchain-edition-preferences-row.c',
+  'gbp-meson-toolchain-edition-preferences-row.h',
   'gbp-meson-build-stage-cross-file.c',
   'gbp-meson-build-stage-cross-file.h',
   'gbp-meson-build-system.c',
@@ -26,6 +30,8 @@ meson_sources = [
   'gbp-meson-toolchain.h',
   'gbp-meson-toolchain-provider.c',
   'gbp-meson-toolchain-provider.h',
+  'gbp-meson-tool-row.c',
+  'gbp-meson-tool-row.h',
   'gbp-meson-utils.c',
   'gbp-meson-utils.h',
 ]
diff --git a/src/plugins/meson/meson.gresource.xml b/src/plugins/meson/meson.gresource.xml
index f9a72e6a9..a74ad8587 100644
--- a/src/plugins/meson/meson.gresource.xml
+++ b/src/plugins/meson/meson.gresource.xml
@@ -3,4 +3,8 @@
   <gresource prefix="/org/gnome/builder/plugins">
     <file>meson.plugin</file>
   </gresource>
+  <gresource prefix="/org/gnome/builder/plugins/meson-plugin">
+    <file>gbp-meson-toolchain-edition-preferences-row.ui</file>
+    <file>gbp-meson-tool-row.ui</file>
+  </gresource>
 </gresources>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]