[gnome-builder] build-tools: new build perspective, panel, and tooling



commit 4a1f18b90666da1db2f98eeaf022f62add4daaa3
Author: Christian Hergert <chergert redhat com>
Date:   Sun Feb 14 20:54:58 2016 -0800

    build-tools: new build perspective, panel, and tooling
    
    This adds a new perspective, the build configuration perspective, to the
    workbench. Using this perspective you can create build configurations
    and tweak existing ones.
    
    The build panel now allows selecting a configuration.

 plugins/build-tools/Makefile.am                    |    6 +
 plugins/build-tools/gbp-build-configuration-row.c  |  153 +++++++
 plugins/build-tools/gbp-build-configuration-row.h  |   36 ++
 plugins/build-tools/gbp-build-configuration-row.ui |   33 ++
 plugins/build-tools/gbp-build-configuration-view.c |  417 +++++++++++++++++++
 plugins/build-tools/gbp-build-configuration-view.h |   38 ++
 .../build-tools/gbp-build-configuration-view.ui    |  268 ++++++++++++
 plugins/build-tools/gbp-build-log-panel.c          |   28 +-
 plugins/build-tools/gbp-build-log-panel.ui         |    3 -
 plugins/build-tools/gbp-build-panel.c              |  177 ++++-----
 plugins/build-tools/gbp-build-panel.ui             |  180 +++------
 plugins/build-tools/gbp-build-perspective.c        |  428 ++++++++++++++++++++
 plugins/build-tools/gbp-build-perspective.h        |   36 ++
 plugins/build-tools/gbp-build-perspective.ui       |   53 +++
 plugins/build-tools/gbp-build-tool.c               |   95 +++--
 plugins/build-tools/gbp-build-tools.gresource.xml  |    9 +-
 plugins/build-tools/gbp-build-workbench-addin.c    |  146 ++------
 plugins/build-tools/theme/Adwaita-dark.css         |    1 +
 plugins/build-tools/theme/Adwaita-shared.css       |   82 ++++
 plugins/build-tools/theme/Adwaita.css              |    1 +
 plugins/build-tools/theme/shared.css               |   44 --
 21 files changed, 1800 insertions(+), 434 deletions(-)
---
diff --git a/plugins/build-tools/Makefile.am b/plugins/build-tools/Makefile.am
index 7c7da83..a0c9fb7 100644
--- a/plugins/build-tools/Makefile.am
+++ b/plugins/build-tools/Makefile.am
@@ -10,12 +10,18 @@ plugin_LTLIBRARIES = libbuild-tools-plugin.la
 dist_plugin_DATA = build-tools.plugin
 
 libbuild_tools_plugin_la_SOURCES = \
+       gbp-build-configuration-row.c \
+       gbp-build-configuration-row.h \
+       gbp-build-configuration-view.c \
+       gbp-build-configuration-view.h \
        gbp-build-log-panel.c \
        gbp-build-log-panel.h \
        gbp-build-panel.c \
        gbp-build-panel.h \
        gbp-build-panel-row.c \
        gbp-build-panel-row.h \
+       gbp-build-perspective.c \
+       gbp-build-perspective.h \
        gbp-build-plugin.c \
        gbp-build-tool.c \
        gbp-build-tool.h \
diff --git a/plugins/build-tools/gbp-build-configuration-row.c 
b/plugins/build-tools/gbp-build-configuration-row.c
new file mode 100644
index 0000000..d77a860
--- /dev/null
+++ b/plugins/build-tools/gbp-build-configuration-row.c
@@ -0,0 +1,153 @@
+/* gbp-build-configuration-row.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gbp-build-configuration-row.h"
+
+struct _GbpBuildConfigurationRow
+{
+  GtkListBoxRow     parent_instance;
+
+  IdeConfiguration *configuration;
+
+  GtkLabel         *label;
+  GtkImage         *check_image;
+};
+
+enum {
+  PROP_0,
+  PROP_CONFIGURATION,
+  PROP_SELECTED,
+  LAST_PROP
+};
+
+G_DEFINE_TYPE (GbpBuildConfigurationRow, gbp_build_configuration_row, GTK_TYPE_LIST_BOX_ROW)
+
+static GParamSpec *properties [LAST_PROP];
+
+static void
+gbp_build_configuration_row_set_configuration (GbpBuildConfigurationRow *self,
+                                               IdeConfiguration         *configuration)
+{
+  g_assert (GBP_IS_BUILD_CONFIGURATION_ROW (self));
+  g_assert (IDE_IS_CONFIGURATION (configuration));
+
+  g_set_object (&self->configuration, configuration);
+
+  g_object_bind_property (configuration, "display-name", self->label, "label", G_BINDING_SYNC_CREATE);
+}
+
+static void
+gbp_build_configuration_row_finalize (GObject *object)
+{
+  GbpBuildConfigurationRow *self = (GbpBuildConfigurationRow *)object;
+
+  g_clear_object (&self->configuration);
+
+  G_OBJECT_CLASS (gbp_build_configuration_row_parent_class)->finalize (object);
+}
+
+static void
+gbp_build_configuration_row_get_property (GObject    *object,
+                                          guint       prop_id,
+                                          GValue     *value,
+                                          GParamSpec *pspec)
+{
+  GbpBuildConfigurationRow *self = GBP_BUILD_CONFIGURATION_ROW (object);
+
+  switch (prop_id)
+    {
+    case PROP_CONFIGURATION:
+      g_value_set_object (value, gbp_build_configuration_row_get_configuration (self));
+      break;
+
+    case PROP_SELECTED:
+      g_value_set_boolean (value, gtk_widget_get_visible (GTK_WIDGET (self->check_image)));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_build_configuration_row_set_property (GObject      *object,
+                                          guint         prop_id,
+                                          const GValue *value,
+                                          GParamSpec   *pspec)
+{
+  GbpBuildConfigurationRow *self = GBP_BUILD_CONFIGURATION_ROW (object);
+
+  switch (prop_id)
+    {
+    case PROP_CONFIGURATION:
+      gbp_build_configuration_row_set_configuration (self, g_value_get_object (value));
+      break;
+
+    case PROP_SELECTED:
+      gtk_widget_set_visible (GTK_WIDGET (self->check_image), g_value_get_boolean (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_build_configuration_row_class_init (GbpBuildConfigurationRowClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = gbp_build_configuration_row_finalize;
+  object_class->get_property = gbp_build_configuration_row_get_property;
+  object_class->set_property = gbp_build_configuration_row_set_property;
+
+  properties [PROP_CONFIGURATION] =
+    g_param_spec_object ("configuration",
+                         "Configuration",
+                         "The configuration this row represents",
+                         IDE_TYPE_CONFIGURATION,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_SELECTED] =
+    g_param_spec_boolean ("selected",
+                          "Selected",
+                          "If the row is selected",
+                          FALSE,
+                          (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, LAST_PROP, properties);
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/builder/plugins/build-tools-plugin/gbp-build-configuration-row.ui");
+  gtk_widget_class_bind_template_child (widget_class, GbpBuildConfigurationRow, check_image);
+  gtk_widget_class_bind_template_child (widget_class, GbpBuildConfigurationRow, label);
+}
+
+static void
+gbp_build_configuration_row_init (GbpBuildConfigurationRow *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+IdeConfiguration *
+gbp_build_configuration_row_get_configuration (GbpBuildConfigurationRow *self)
+{
+  g_return_val_if_fail (GBP_IS_BUILD_CONFIGURATION_ROW (self), NULL);
+
+  return self->configuration;
+}
diff --git a/plugins/build-tools/gbp-build-configuration-row.h 
b/plugins/build-tools/gbp-build-configuration-row.h
new file mode 100644
index 0000000..6bf4d65
--- /dev/null
+++ b/plugins/build-tools/gbp-build-configuration-row.h
@@ -0,0 +1,36 @@
+/* gbp-build-configuration-row.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GBP_BUILD_CONFIGURATION_ROW_H
+#define GBP_BUILD_CONFIGURATION_ROW_H
+
+#include <gtk/gtk.h>
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_BUILD_CONFIGURATION_ROW (gbp_build_configuration_row_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpBuildConfigurationRow, gbp_build_configuration_row, GBP, BUILD_CONFIGURATION_ROW, 
GtkListBoxRow)
+
+GtkWidget        *gbp_build_configuration_row_new               (IdeConfiguration         *configuration);
+IdeConfiguration *gbp_build_configuration_row_get_configuration (GbpBuildConfigurationRow *self);
+
+G_END_DECLS
+
+#endif /* GBP_BUILD_CONFIGURATION_ROW_H */
diff --git a/plugins/build-tools/gbp-build-configuration-row.ui 
b/plugins/build-tools/gbp-build-configuration-row.ui
new file mode 100644
index 0000000..10157e1
--- /dev/null
+++ b/plugins/build-tools/gbp-build-configuration-row.ui
@@ -0,0 +1,33 @@
+<interface>
+  <template class="GbpBuildConfigurationRow" parent="GtkListBoxRow">
+    <child>
+      <object class="GtkBox">
+        <property name="orientation">horizontal</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkLabel" id="label">
+            <property name="valign">baseline</property>
+            <property name="visible">true</property>
+            <property name="xalign">0.0</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkImage" id="check_image">
+            <property name="icon-name">object-select-symbolic</property>
+            <property name="margin-end">6</property>
+            <property name="margin-start">6</property>
+            <property name="valign">baseline</property>
+            <property name="visible">true</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkLabel" id="spacer">
+            <property name="hexpand">true</property>
+            <property name="valign">baseline</property>
+            <property name="visible">true</property>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/plugins/build-tools/gbp-build-configuration-view.c 
b/plugins/build-tools/gbp-build-configuration-view.c
new file mode 100644
index 0000000..fd76b74
--- /dev/null
+++ b/plugins/build-tools/gbp-build-configuration-view.c
@@ -0,0 +1,417 @@
+/* gbp-build-configuration-view.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <ide.h>
+#include <string.h>
+
+#include "gbp-build-configuration-view.h"
+
+#include "ide-internal.h"
+
+struct _GbpBuildConfigurationView
+{
+  EggColumnLayout       parent_instance;
+
+  IdeConfiguration     *configuration;
+
+  GBinding             *configure_binding;
+  GBinding             *display_name_binding;
+  GBinding             *prefix_binding;
+
+  GtkEntry             *configure_entry;
+  GtkListBox           *device_list_box;
+  GtkEntry             *display_name_entry;
+  IdeEnvironmentEditor *environment_editor;
+  GtkEntry             *prefix_entry;
+  GtkListBox           *runtime_list_box;
+};
+
+enum {
+  PROP_0,
+  PROP_CONFIGURATION,
+  LAST_PROP
+};
+
+G_DEFINE_TYPE (GbpBuildConfigurationView, gbp_build_configuration_view, EGG_TYPE_COLUMN_LAYOUT)
+
+static GParamSpec *properties [LAST_PROP];
+
+static gboolean
+map_pointer_to (GBinding     *binding,
+                const GValue *from_value,
+                GValue       *to_value,
+                gpointer      user_data)
+{
+  g_value_set_boolean (to_value, (user_data == g_value_get_object (from_value)));
+  return TRUE;
+}
+
+static GtkWidget *
+create_runtime_row (gpointer item,
+                    gpointer user_data)
+{
+  IdeRuntime *runtime = item;
+  IdeConfiguration *configuration = user_data;
+  GtkWidget *box;
+  GtkWidget *image;
+  GtkWidget *label;
+  GtkWidget *row;
+
+  g_assert (IDE_IS_RUNTIME (runtime));
+  g_assert (IDE_IS_CONFIGURATION (configuration));
+
+  box = g_object_new (GTK_TYPE_BOX,
+                      "spacing", 12,
+                      "visible", TRUE,
+                      NULL);
+
+  label = g_object_new (GTK_TYPE_LABEL,
+                        "use-markup", TRUE,
+                        "visible", TRUE,
+                        "xalign", 0.0f,
+                        NULL);
+  g_object_bind_property (runtime, "display-name", label, "label", G_BINDING_SYNC_CREATE);
+  gtk_container_add (GTK_CONTAINER (box), label);
+
+  image = g_object_new (GTK_TYPE_IMAGE,
+                        "icon-name", "object-select-symbolic",
+                        "visible", TRUE,
+                        NULL);
+  g_object_bind_property_full (configuration, "runtime",
+                               image, "visible",
+                               G_BINDING_SYNC_CREATE,
+                               map_pointer_to,
+                               NULL,
+                               g_object_ref (runtime),
+                               g_object_unref);
+  gtk_container_add (GTK_CONTAINER (box), image);
+
+  label = g_object_new (GTK_TYPE_LABEL,
+                        "hexpand", TRUE,
+                        "visible", TRUE,
+                        NULL);
+  gtk_container_add (GTK_CONTAINER (box), label);
+
+  row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
+                      "child", box,
+                      "visible", TRUE,
+                      NULL);
+
+  g_object_set_data (G_OBJECT (row), "IDE_RUNTIME", runtime);
+
+  return row;
+}
+
+static GtkWidget *
+create_device_row (gpointer item,
+                   gpointer user_data)
+{
+  IdeDevice *device = item;
+  IdeConfiguration *configuration = user_data;
+  GtkWidget *box;
+  GtkWidget *image;
+  GtkWidget *label;
+  GtkWidget *row;
+
+  g_assert (IDE_IS_DEVICE (device));
+  g_assert (IDE_IS_CONFIGURATION (configuration));
+
+  box = g_object_new (GTK_TYPE_BOX,
+                      "spacing", 12,
+                      "visible", TRUE,
+                      NULL);
+
+  label = g_object_new (GTK_TYPE_LABEL,
+                        "use-markup", TRUE,
+                        "visible", TRUE,
+                        "xalign", 0.0f,
+                        NULL);
+  g_object_bind_property (device, "display-name", label, "label", G_BINDING_SYNC_CREATE);
+  gtk_container_add (GTK_CONTAINER (box), label);
+
+  image = g_object_new (GTK_TYPE_IMAGE,
+                        "icon-name", "object-select-symbolic",
+                        "visible", TRUE,
+                        NULL);
+  g_object_bind_property_full (configuration, "device",
+                               image, "visible",
+                               G_BINDING_SYNC_CREATE,
+                               map_pointer_to,
+                               NULL,
+                               g_object_ref (device),
+                               g_object_unref);
+  gtk_container_add (GTK_CONTAINER (box), image);
+
+  label = g_object_new (GTK_TYPE_LABEL,
+                        "hexpand", TRUE,
+                        "visible", TRUE,
+                        NULL);
+  gtk_container_add (GTK_CONTAINER (box), label);
+
+  row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
+                      "child", box,
+                      "visible", TRUE,
+                      NULL);
+
+  g_object_set_data (G_OBJECT (row), "IDE_DEVICE", device);
+
+  return row;
+}
+
+static void
+device_row_activated (GbpBuildConfigurationView *self,
+                      GtkListBoxRow             *row,
+                      GtkListBox                *list_box)
+{
+  IdeDevice *device;
+
+  g_assert (GBP_IS_BUILD_CONFIGURATION_VIEW (self));
+  g_assert (GTK_IS_LIST_BOX_ROW (row));
+  g_assert (GTK_IS_LIST_BOX (list_box));
+
+  device = g_object_get_data (G_OBJECT (row), "IDE_DEVICE");
+
+  if (self->configuration != NULL)
+    ide_configuration_set_device (self->configuration, device);
+}
+
+static void
+runtime_row_activated (GbpBuildConfigurationView *self,
+                       GtkListBoxRow             *row,
+                       GtkListBox                *list_box)
+{
+  IdeRuntime *runtime;
+
+  g_assert (GBP_IS_BUILD_CONFIGURATION_VIEW (self));
+  g_assert (GTK_IS_LIST_BOX_ROW (row));
+  g_assert (GTK_IS_LIST_BOX (list_box));
+
+  runtime = g_object_get_data (G_OBJECT (row), "IDE_RUNTIME");
+
+  if (self->configuration != NULL)
+    ide_configuration_set_runtime (self->configuration, runtime);
+}
+
+static gboolean
+treat_null_as_empty (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
+gbp_build_configuration_view_connect (GbpBuildConfigurationView *self,
+                                      IdeConfiguration          *configuration)
+{
+  IdeRuntimeManager *runtime_manager;
+  IdeDeviceManager *device_manager;
+  IdeContext *context;
+  IdeEnvironment *environment;
+
+  g_assert (GBP_IS_BUILD_CONFIGURATION_VIEW (self));
+  g_assert (IDE_IS_CONFIGURATION (configuration));
+
+  context = ide_object_get_context (IDE_OBJECT (configuration));
+  runtime_manager = ide_context_get_runtime_manager (context);
+  device_manager = ide_context_get_device_manager (context);
+
+  self->display_name_binding =
+    g_object_bind_property_full (configuration, "display-name",
+                                 self->display_name_entry, "text",
+                                 G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
+                                 treat_null_as_empty, NULL, NULL, NULL);
+
+  self->configure_binding =
+    g_object_bind_property_full (configuration, "config-opts",
+                                 self->configure_entry, "text",
+                                 G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
+                                 treat_null_as_empty, NULL, NULL, NULL);
+
+  self->prefix_binding =
+    g_object_bind_property_full (configuration, "prefix",
+                                 self->prefix_entry, "text",
+                                 G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
+                                 treat_null_as_empty, NULL, NULL, NULL);
+
+  gtk_list_box_bind_model (self->runtime_list_box,
+                           G_LIST_MODEL (runtime_manager),
+                           create_runtime_row,
+                           g_object_ref (configuration),
+                           g_object_unref);
+
+  gtk_list_box_bind_model (self->device_list_box,
+                           G_LIST_MODEL (device_manager),
+                           create_device_row,
+                           g_object_ref (configuration),
+                           g_object_unref);
+
+  environment = ide_configuration_get_environment (configuration);
+  ide_environment_editor_set_environment (self->environment_editor, environment);
+}
+
+static void
+gbp_build_configuration_view_disconnect (GbpBuildConfigurationView *self,
+                                         IdeConfiguration          *configuration)
+{
+  g_assert (GBP_IS_BUILD_CONFIGURATION_VIEW (self));
+  g_assert (IDE_IS_CONFIGURATION (configuration));
+
+  gtk_list_box_bind_model (self->device_list_box, NULL, NULL, NULL, NULL);
+  gtk_list_box_bind_model (self->runtime_list_box, NULL, NULL, NULL, NULL);
+
+  g_clear_pointer (&self->configure_binding, g_binding_unbind);
+  g_clear_pointer (&self->display_name_binding, g_binding_unbind);
+  g_clear_pointer (&self->prefix_binding, g_binding_unbind);
+}
+
+static void
+gbp_build_configuration_view_destroy (GtkWidget *widget)
+{
+  GbpBuildConfigurationView *self = (GbpBuildConfigurationView *)widget;
+
+  if (self->configuration != NULL)
+    {
+      gbp_build_configuration_view_disconnect (self, self->configuration);
+      g_clear_object (&self->configuration);
+    }
+
+  GTK_WIDGET_CLASS (gbp_build_configuration_view_parent_class)->destroy (widget);
+}
+
+static void
+gbp_build_configuration_view_get_property (GObject    *object,
+                                           guint       prop_id,
+                                           GValue     *value,
+                                           GParamSpec *pspec)
+{
+  GbpBuildConfigurationView *self = GBP_BUILD_CONFIGURATION_VIEW (object);
+
+  switch (prop_id)
+    {
+    case PROP_CONFIGURATION:
+      g_value_set_object (value, gbp_build_configuration_view_get_configuration (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_build_configuration_view_set_property (GObject      *object,
+                                           guint         prop_id,
+                                           const GValue *value,
+                                           GParamSpec   *pspec)
+{
+  GbpBuildConfigurationView *self = GBP_BUILD_CONFIGURATION_VIEW (object);
+
+  switch (prop_id)
+    {
+    case PROP_CONFIGURATION:
+      gbp_build_configuration_view_set_configuration (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_build_configuration_view_class_init (GbpBuildConfigurationViewClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->get_property = gbp_build_configuration_view_get_property;
+  object_class->set_property = gbp_build_configuration_view_set_property;
+
+  properties [PROP_CONFIGURATION] =
+    g_param_spec_object ("configuration",
+                         "Configuration",
+                         "Configuration",
+                         IDE_TYPE_CONFIGURATION,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, LAST_PROP, properties);
+
+  widget_class->destroy = gbp_build_configuration_view_destroy;
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/builder/plugins/build-tools-plugin/gbp-build-configuration-view.ui");
+  gtk_widget_class_set_css_name (widget_class, "configurationview");
+  gtk_widget_class_bind_template_child (widget_class, GbpBuildConfigurationView, configure_entry);
+  gtk_widget_class_bind_template_child (widget_class, GbpBuildConfigurationView, device_list_box);
+  gtk_widget_class_bind_template_child (widget_class, GbpBuildConfigurationView, display_name_entry);
+  gtk_widget_class_bind_template_child (widget_class, GbpBuildConfigurationView, environment_editor);
+  gtk_widget_class_bind_template_child (widget_class, GbpBuildConfigurationView, prefix_entry);
+  gtk_widget_class_bind_template_child (widget_class, GbpBuildConfigurationView, runtime_list_box);
+}
+
+static void
+gbp_build_configuration_view_init (GbpBuildConfigurationView *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  g_signal_connect_object (self->device_list_box,
+                           "row-activated",
+                           G_CALLBACK (device_row_activated),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (self->runtime_list_box,
+                           "row-activated",
+                           G_CALLBACK (runtime_row_activated),
+                           self,
+                           G_CONNECT_SWAPPED);
+}
+
+IdeConfiguration *
+gbp_build_configuration_view_get_configuration (GbpBuildConfigurationView *self)
+{
+  g_return_val_if_fail (GBP_IS_BUILD_CONFIGURATION_VIEW (self), NULL);
+
+  return self->configuration;
+}
+
+void
+gbp_build_configuration_view_set_configuration (GbpBuildConfigurationView *self,
+                                                IdeConfiguration          *configuration)
+{
+  g_return_if_fail (GBP_IS_BUILD_CONFIGURATION_VIEW (self));
+  g_return_if_fail (!configuration || IDE_IS_CONFIGURATION (configuration));
+
+  if (self->configuration != configuration)
+    {
+      if (self->configuration != NULL)
+        {
+          gbp_build_configuration_view_disconnect (self, self->configuration);
+          g_clear_object (&self->configuration);
+        }
+
+      if (configuration != NULL)
+        {
+          self->configuration = g_object_ref (configuration);
+          gbp_build_configuration_view_connect (self, configuration);
+        }
+
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CONFIGURATION]);
+    }
+}
diff --git a/plugins/build-tools/gbp-build-configuration-view.h 
b/plugins/build-tools/gbp-build-configuration-view.h
new file mode 100644
index 0000000..37a00a1
--- /dev/null
+++ b/plugins/build-tools/gbp-build-configuration-view.h
@@ -0,0 +1,38 @@
+/* gbp-build-configuration-view.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GBP_BUILD_CONFIGURATION_VIEW_H
+#define GBP_BUILD_CONFIGURATION_VIEW_H
+
+#include <ide.h>
+
+#include "egg-column-layout.h"
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_BUILD_CONFIGURATION_VIEW (gbp_build_configuration_view_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpBuildConfigurationView, gbp_build_configuration_view, GBP, 
BUILD_CONFIGURATION_VIEW, EggColumnLayout)
+
+IdeConfiguration *gbp_build_configuration_view_get_configuration (GbpBuildConfigurationView *self);
+void              gbp_build_configuration_view_set_configuration (GbpBuildConfigurationView *self,
+                                                                  IdeConfiguration          *configuration);
+
+G_END_DECLS
+
+#endif /* GBP_BUILD_CONFIGURATION_VIEW_H */
diff --git a/plugins/build-tools/gbp-build-configuration-view.ui 
b/plugins/build-tools/gbp-build-configuration-view.ui
new file mode 100644
index 0000000..cb978f6
--- /dev/null
+++ b/plugins/build-tools/gbp-build-configuration-view.ui
@@ -0,0 +1,268 @@
+<interface>
+  <template class="GbpBuildConfigurationView" parent="EggColumnLayout">
+    <property name="border-width">24</property>
+    <child>
+      <object class="GtkBox">
+        <property name="orientation">vertical</property>
+        <property name="spacing">12</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkLabel">
+            <property name="label" translatable="yes">General</property>
+            <property name="xalign">0.0</property>
+            <property name="visible">true</property>
+            <attributes>
+              <attribute name="weight" value="bold"/>
+            </attributes>
+          </object>
+        </child>
+        <child>
+          <object class="GtkFrame">
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkListBox">
+                <property name="selection-mode">none</property>
+                <property name="visible">true</property>
+                <child>
+                  <object class="GtkListBoxRow">
+                    <property name="tooltip-text" translatable="yes">The name of the build 
configuration</property>
+                    <property name="visible">true</property>
+                    <child>
+                      <object class="GtkBox">
+                        <property name="spacing">12</property>
+                        <property name="orientation">horizontal</property>
+                        <property name="visible">true</property>
+                        <child>
+                          <object class="GtkLabel" id="label1">
+                            <property name="label" translatable="yes">Name</property>
+                            <property name="xalign">0.0</property>
+                            <property name="visible">true</property>
+                            <style>
+                              <class name="dim-label"/>
+                            </style>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkEntry" id="display_name_entry">
+                            <property name="has-frame">false</property>
+                            <property name="hexpand">true</property>
+                            <property name="visible">true</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkListBoxRow">
+                    <property name="tooltip-text" translatable="yes">The prefix to use when installiing the 
project</property>
+                    <property name="visible">true</property>
+                    <child>
+                      <object class="GtkBox">
+                        <property name="spacing">12</property>
+                        <property name="orientation">horizontal</property>
+                        <property name="visible">true</property>
+                        <child>
+                          <object class="GtkLabel" id="label2">
+                            <property name="label" translatable="yes">Installation Prefix</property>
+                            <property name="visible">true</property>
+                            <property name="xalign">0.0</property>
+                            <style>
+                              <class name="dim-label"/>
+                            </style>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkEntry" id="prefix_entry">
+                            <property name="visible">true</property>
+                            <property name="has-frame">false</property>
+                            <property name="hexpand">true</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkListBoxRow">
+                    <property name="tooltip-text" translatable="yes">Options to use when bootstrapping the 
project</property>
+                    <property name="visible">true</property>
+                    <child>
+                      <object class="GtkBox">
+                        <property name="spacing">12</property>
+                        <property name="orientation">horizontal</property>
+                        <property name="visible">true</property>
+                        <child>
+                          <object class="GtkLabel" id="label3">
+                            <property name="label" translatable="yes">Configure Options</property>
+                            <property name="visible">true</property>
+                            <property name="xalign">0.0</property>
+                            <style>
+                              <class name="dim-label"/>
+                            </style>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkEntry" id="configure_entry">
+                            <property name="visible">true</property>
+                            <property name="has-frame">false</property>
+                            <property name="hexpand">true</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="GtkFrame">
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkListBox">
+            <property name="selection-mode">none</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkListBoxRow">
+                <property name="visible">true</property>
+                <child>
+                  <object class="GtkBox">
+                    <property name="spacing">12</property>
+                    <property name="orientation">horizontal</property>
+                    <property name="visible">true</property>
+                    <child>
+                      <object class="GtkButton">
+                        <property name="action-name">perspective.duplicate-configuration</property>
+                        <property name="label" translatable="yes">Duplicate Configuration</property>
+                        <property name="valign">center</property>
+                        <property name="visible">true</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="hexpand">true</property>
+                        <property name="visible">true</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkButton">
+                        <property name="action-name">perspective.delete-configuration</property>
+                        <property name="label" translatable="yes">Delete Configuration</property>
+                        <property name="valign">center</property>
+                        <property name="visible">true</property>
+                        <style>
+                          <class name="destructive-action"/>
+                        </style>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="GtkBox">
+        <property name="orientation">vertical</property>
+        <property name="spacing">12</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkLabel">
+            <property name="label" translatable="yes">Device</property>
+            <property name="xalign">0.0</property>
+            <property name="visible">true</property>
+            <attributes>
+              <attribute name="weight" value="bold"/>
+            </attributes>
+          </object>
+        </child>
+        <child>
+          <object class="GtkFrame">
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkListBox" id="device_list_box">
+                <property name="selection-mode">none</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="GtkBox">
+        <property name="orientation">vertical</property>
+        <property name="spacing">12</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkLabel">
+            <property name="label" translatable="yes">Runtime</property>
+            <property name="xalign">0.0</property>
+            <property name="visible">true</property>
+            <attributes>
+              <attribute name="weight" value="bold"/>
+            </attributes>
+          </object>
+        </child>
+        <child>
+          <object class="GtkFrame">
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkListBox" id="runtime_list_box">
+                <property name="selection-mode">none</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="GtkBox">
+        <property name="orientation">vertical</property>
+        <property name="spacing">12</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkLabel">
+            <property name="label" translatable="yes">Environment</property>
+            <property name="xalign">0.0</property>
+            <property name="visible">true</property>
+            <attributes>
+              <attribute name="weight" value="bold"/>
+            </attributes>
+          </object>
+        </child>
+        <child>
+          <object class="GtkFrame">
+            <property name="visible">true</property>
+            <child>
+              <object class="IdeEnvironmentEditor" id="environment_editor">
+                <property name="visible">true</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GtkSizeGroup">
+    <property name="mode">horizontal</property>
+    <widgets>
+      <widget name="label1"/>
+      <widget name="label2"/>
+      <widget name="label3"/>
+    </widgets>
+  </object>
+  <object class="GtkListStore" id="environ_list_store">
+    <columns>
+      <column type="gchararray"/>
+      <column type="gchararray"/>
+    </columns>
+  </object>
+</interface>
diff --git a/plugins/build-tools/gbp-build-log-panel.c b/plugins/build-tools/gbp-build-log-panel.c
index c3130b8..6cfe715 100644
--- a/plugins/build-tools/gbp-build-log-panel.c
+++ b/plugins/build-tools/gbp-build-log-panel.c
@@ -50,6 +50,22 @@ G_DEFINE_TYPE (GbpBuildLogPanel, gbp_build_log_panel, GTK_TYPE_BIN)
 static GParamSpec *properties [LAST_PROP];
 
 static void
+gbp_build_log_panel_reset_buffer (GbpBuildLogPanel *self)
+{
+  g_assert (GBP_IS_BUILD_LOG_PANEL (self));
+
+  g_clear_object (&self->buffer);
+
+  self->buffer = gtk_text_buffer_new (NULL);
+  self->stderr_tag = gtk_text_buffer_create_tag (self->buffer,
+                                                 "stderr-tag",
+                                                 "foreground", "#ff0000",
+                                                 "weight", PANGO_WEIGHT_BOLD,
+                                                 NULL);
+  gtk_text_view_set_buffer (self->text_view, self->buffer);
+}
+
+static void
 gbp_build_log_panel_log (GbpBuildLogPanel  *self,
                          IdeBuildResultLog  log,
                          const gchar       *message,
@@ -86,10 +102,11 @@ gbp_build_log_panel_set_result (GbpBuildLogPanel *self,
                                 IdeBuildResult   *result)
 {
   g_return_if_fail (GBP_IS_BUILD_LOG_PANEL (self));
+  g_return_if_fail (!result || IDE_IS_BUILD_RESULT (result));
 
   if (g_set_object (&self->result, result))
     {
-      gtk_text_buffer_set_text (self->buffer, "", 0);
+      gbp_build_log_panel_reset_buffer (self);
       egg_signal_group_set_target (self->signals, result);
     }
 }
@@ -132,6 +149,8 @@ gbp_build_log_panel_finalize (GObject *object)
 {
   GbpBuildLogPanel *self = (GbpBuildLogPanel *)object;
 
+  self->stderr_tag = NULL;
+
   g_clear_object (&self->result);
   g_clear_object (&self->signals);
   g_clear_object (&self->css);
@@ -191,7 +210,6 @@ gbp_build_log_panel_class_init (GbpBuildLogPanelClass *klass)
   gtk_widget_class_set_css_name (widget_class, "buildlogpanel");
   gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/builder/plugins/build-tools-plugin/gbp-build-log-panel.ui");
   gtk_widget_class_bind_template_child (widget_class, GbpBuildLogPanel, text_view);
-  gtk_widget_class_bind_template_child (widget_class, GbpBuildLogPanel, buffer);
 
   properties [PROP_RESULT] =
     g_param_spec_object ("result",
@@ -210,12 +228,6 @@ gbp_build_log_panel_init (GbpBuildLogPanel *self)
 
   gtk_widget_init_template (GTK_WIDGET (self));
 
-  self->stderr_tag = gtk_text_buffer_create_tag (self->buffer,
-                                                 "stderr-tag",
-                                                 "foreground", "#ff0000",
-                                                 "weight", PANGO_WEIGHT_BOLD,
-                                                 NULL);
-
   self->signals = egg_signal_group_new (IDE_TYPE_BUILD_RESULT);
 
   egg_signal_group_connect_object (self->signals,
diff --git a/plugins/build-tools/gbp-build-log-panel.ui b/plugins/build-tools/gbp-build-log-panel.ui
index ed50bb8..3dee976 100644
--- a/plugins/build-tools/gbp-build-log-panel.ui
+++ b/plugins/build-tools/gbp-build-log-panel.ui
@@ -7,13 +7,10 @@
           <object class="GtkTextView" id="text_view">
             <property name="cursor-visible">false</property>
             <property name="editable">false</property>
-            <property name="buffer">buffer</property>
             <property name="visible">true</property>
           </object>
         </child>
       </object>
     </child>
   </template>
-  <object class="GtkTextBuffer" id="buffer">
-  </object>
 </interface>
diff --git a/plugins/build-tools/gbp-build-panel.c b/plugins/build-tools/gbp-build-panel.c
index 4a84da0..1b703bd 100644
--- a/plugins/build-tools/gbp-build-panel.c
+++ b/plugins/build-tools/gbp-build-panel.c
@@ -22,6 +22,7 @@
 #include "egg-binding-group.h"
 #include "egg-signal-group.h"
 
+#include "gbp-build-configuration-row.h"
 #include "gbp-build-panel.h"
 #include "gbp-build-panel-row.h"
 
@@ -33,17 +34,14 @@ struct _GbpBuildPanel
   EggSignalGroup   *signals;
   EggBindingGroup  *bindings;
 
-  IdeDevice        *device;
-
+  GtkListBox       *configurations;
+  GtkLabel         *configuration_label;
+  GtkPopover       *configuration_popover;
   GtkListBox       *diagnostics;
+  GtkLabel         *errors_label;
+  GtkLabel         *running_time_label;
   GtkRevealer      *status_revealer;
   GtkLabel         *status_label;
-  GtkLabel         *running_time_label;
-  GtkMenuButton    *device_button;
-  GtkLabel         *device_label;
-  GtkListBox       *devices;
-  GtkPopover       *device_popover;
-  GtkLabel         *errors_label;
   GtkLabel         *warnings_label;
 
   guint             running_time_source;
@@ -56,79 +54,70 @@ G_DEFINE_TYPE (GbpBuildPanel, gbp_build_panel, GTK_TYPE_BIN)
 
 enum {
   PROP_0,
-  PROP_DEVICE,
-  PROP_DEVICE_MANAGER,
+  PROP_CONFIGURATION_MANAGER,
   PROP_RESULT,
   LAST_PROP
 };
 
 static GParamSpec *properties [LAST_PROP];
 
-static GtkWidget *
-create_device_row (gpointer item,
-                   gpointer user_data)
+static gboolean
+map_current_to_bool (GBinding     *binding,
+                     const GValue *from_value,
+                     GValue       *to_value,
+                     gpointer      user_data)
 {
-  GtkListBoxRow *row;
-  IdeDevice *device = item;
-  const gchar *type;
-  const gchar *name;
-  GtkLabel *label;
-  gchar *str;
-
-  g_assert (IDE_IS_DEVICE (device));
+  IdeConfiguration *configuration = user_data;
+  IdeConfiguration *current;
 
-  row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
-                      "visible", TRUE,
-                      NULL);
-
-  g_object_set_data_full (G_OBJECT (row),
-                          "IDE_DEVICE_ID",
-                          g_strdup (ide_device_get_id (device)),
-                          g_free);
-
-  name = ide_device_get_display_name (device);
-  type = ide_device_get_system_type (device);
-  str = g_strdup_printf ("%s (%s)", name, type);
-
-  label = g_object_new (GTK_TYPE_LABEL,
-                        "label", str,
-                        "xalign", 0.0f,
-                        "visible", TRUE,
-                        NULL);
-  gtk_container_add (GTK_CONTAINER (row), GTK_WIDGET (label));
+  g_assert (IDE_IS_CONFIGURATION (configuration));
 
-  g_free (str);
+  current = g_value_get_object (from_value);
+  g_value_set_boolean (to_value, (configuration == current));
 
-  return GTK_WIDGET (row);
+  return TRUE;
 }
 
-static void
-gbp_build_panel_set_device (GbpBuildPanel *self,
-                            IdeDevice     *device)
+static GtkWidget *
+create_configuration_row (gpointer item,
+                          gpointer user_data)
 {
-  g_return_if_fail (GBP_IS_BUILD_PANEL (self));
-  g_return_if_fail (!device || IDE_IS_DEVICE (device));
+  IdeConfiguration *configuration = item;
+  IdeConfigurationManager *manager = user_data;
+  GtkWidget *ret;
 
-  if (g_set_object (&self->device, device))
-    {
-      const gchar *name = NULL;
+  g_assert (IDE_IS_CONFIGURATION (configuration));
+  g_assert (IDE_IS_CONFIGURATION_MANAGER (manager));
 
-      if (device != NULL)
-        name = ide_device_get_display_name (device);
-      gtk_label_set_label (self->device_label, name);
-    }
+  ret = g_object_new (GBP_TYPE_BUILD_CONFIGURATION_ROW,
+                      "configuration", configuration,
+                      "visible", TRUE,
+                      NULL);
+
+  g_object_bind_property_full (manager, "current", ret, "selected",
+                               G_BINDING_SYNC_CREATE,
+                               map_current_to_bool, NULL,
+                               g_object_ref (configuration), g_object_unref);
+
+  return ret;
 }
 
 static void
-gbp_build_panel_set_device_manager (GbpBuildPanel    *self,
-                                    IdeDeviceManager *device_manager)
+gbp_build_panel_set_configuration_manager (GbpBuildPanel           *self,
+                                           IdeConfigurationManager *configuration_manager)
 {
-  g_return_if_fail (GBP_IS_BUILD_PANEL (self));
-  g_return_if_fail (!device_manager || IDE_IS_DEVICE_MANAGER (device_manager));
+  g_assert (GBP_IS_BUILD_PANEL (self));
+  g_assert (IDE_IS_CONFIGURATION_MANAGER (configuration_manager));
 
-  gtk_list_box_bind_model (self->devices,
-                           G_LIST_MODEL (device_manager),
-                           create_device_row, NULL, NULL);
+  gtk_list_box_bind_model (self->configurations,
+                           G_LIST_MODEL (configuration_manager),
+                           create_configuration_row,
+                           g_object_ref (configuration_manager),
+                           g_object_unref);
+
+  g_object_bind_property (configuration_manager, "current-display-name",
+                          self->configuration_label, "label",
+                          G_BINDING_SYNC_CREATE);
 }
 
 void
@@ -281,22 +270,26 @@ gbp_build_panel_notify_running (GbpBuildPanel  *self,
 }
 
 static void
-gbp_build_panel_device_activated (GbpBuildPanel *self,
-                                  GtkListBoxRow *row,
-                                  GtkListBox    *list_box)
+gbp_build_panel_configuration_activated (GbpBuildPanel *self,
+                                         GtkListBoxRow *row,
+                                         GtkListBox    *list_box)
 {
-  const gchar *id;
+  IdeConfigurationManager *manager;
+  IdeConfiguration *config;
+  IdeWorkbench *workbench;
+  IdeContext *context;
 
   g_assert (GBP_IS_BUILD_PANEL (self));
   g_assert (GTK_IS_LIST_BOX_ROW (row));
   g_assert (GTK_IS_LIST_BOX (list_box));
 
-  if ((id = g_object_get_data (G_OBJECT (row), "IDE_DEVICE_ID")))
-    ide_widget_action (GTK_WIDGET (self),
-                       "build-tools", "device",
-                       g_variant_new_string (id));
+  workbench = ide_widget_get_workbench (GTK_WIDGET (self));
+  context = ide_workbench_get_context (workbench);
+  manager = ide_context_get_configuration_manager (context);
+  config = gbp_build_configuration_row_get_configuration (GBP_BUILD_CONFIGURATION_ROW (row));
+  ide_configuration_manager_set_current (manager, config);
 
-  gtk_widget_hide (GTK_WIDGET (self->device_popover));
+  gtk_widget_hide (GTK_WIDGET (self->configuration_popover));
 }
 
 static void
@@ -339,7 +332,6 @@ gbp_build_panel_destroy (GtkWidget *widget)
 
   g_clear_object (&self->bindings);
   g_clear_object (&self->signals);
-  g_clear_object (&self->device);
 
   GTK_WIDGET_CLASS (gbp_build_panel_parent_class)->destroy (widget);
 }
@@ -354,10 +346,6 @@ gbp_build_panel_get_property (GObject    *object,
 
   switch (prop_id)
     {
-    case PROP_DEVICE:
-      g_value_set_object (value, self->device);
-      break;
-
     case PROP_RESULT:
       g_value_set_object (value, self->result);
       break;
@@ -377,12 +365,8 @@ gbp_build_panel_set_property (GObject      *object,
 
   switch (prop_id)
     {
-    case PROP_DEVICE:
-      gbp_build_panel_set_device (self, g_value_get_object (value));
-      break;
-
-    case PROP_DEVICE_MANAGER:
-      gbp_build_panel_set_device_manager (self, g_value_get_object (value));
+    case PROP_CONFIGURATION_MANAGER:
+      gbp_build_panel_set_configuration_manager (self, g_value_get_object (value));
       break;
 
     case PROP_RESULT:
@@ -405,18 +389,11 @@ gbp_build_panel_class_init (GbpBuildPanelClass *klass)
 
   widget_class->destroy = gbp_build_panel_destroy;
 
-  properties [PROP_DEVICE] =
-    g_param_spec_object ("device",
-                         "Device",
-                         "Device",
-                         IDE_TYPE_DEVICE,
-                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
-  properties [PROP_DEVICE_MANAGER] =
-    g_param_spec_object ("device-manager",
-                         "Device Manager",
-                         "Device Manager",
-                         IDE_TYPE_DEVICE_MANAGER,
+  properties [PROP_CONFIGURATION_MANAGER] =
+    g_param_spec_object ("configuration-manager",
+                         "Configuration Manager",
+                         "Configuration Manager",
+                         IDE_TYPE_CONFIGURATION_MANAGER,
                          (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
 
   properties [PROP_RESULT] =
@@ -430,10 +407,9 @@ gbp_build_panel_class_init (GbpBuildPanelClass *klass)
 
   gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/builder/plugins/build-tools-plugin/gbp-build-panel.ui");
   gtk_widget_class_set_css_name (widget_class, "buildpanel");
-  gtk_widget_class_bind_template_child (widget_class, GbpBuildPanel, device_button);
-  gtk_widget_class_bind_template_child (widget_class, GbpBuildPanel, device_label);
-  gtk_widget_class_bind_template_child (widget_class, GbpBuildPanel, device_popover);
-  gtk_widget_class_bind_template_child (widget_class, GbpBuildPanel, devices);
+  gtk_widget_class_bind_template_child (widget_class, GbpBuildPanel, configurations);
+  gtk_widget_class_bind_template_child (widget_class, GbpBuildPanel, configuration_label);
+  gtk_widget_class_bind_template_child (widget_class, GbpBuildPanel, configuration_popover);
   gtk_widget_class_bind_template_child (widget_class, GbpBuildPanel, diagnostics);
   gtk_widget_class_bind_template_child (widget_class, GbpBuildPanel, errors_label);
   gtk_widget_class_bind_template_child (widget_class, GbpBuildPanel, running_time_label);
@@ -461,9 +437,9 @@ gbp_build_panel_init (GbpBuildPanel *self)
                                    self,
                                    G_CONNECT_SWAPPED);
 
-  g_signal_connect_object (self->devices,
+  g_signal_connect_object (self->configurations,
                            "row-activated",
-                           G_CALLBACK (gbp_build_panel_device_activated),
+                           G_CALLBACK (gbp_build_panel_configuration_activated),
                            self,
                            G_CONNECT_SWAPPED);
 
@@ -475,7 +451,6 @@ gbp_build_panel_init (GbpBuildPanel *self)
 
   self->bindings = egg_binding_group_new ();
 
-  egg_binding_group_bind (self->bindings, "mode",
-                          self->status_label, "label",
+  egg_binding_group_bind (self->bindings, "mode", self->status_label, "label",
                           G_BINDING_SYNC_CREATE);
 }
diff --git a/plugins/build-tools/gbp-build-panel.ui b/plugins/build-tools/gbp-build-panel.ui
index 8cbc219..9b29a5c 100644
--- a/plugins/build-tools/gbp-build-panel.ui
+++ b/plugins/build-tools/gbp-build-panel.ui
@@ -5,150 +5,57 @@
         <property name="visible">true</property>
         <property name="orientation">vertical</property>
         <child>
-          <object class="GtkGrid">
-            <property name="border-width">12</property>
-            <property name="column-spacing">12</property>
-            <property name="column-homogeneous">true</property>
-            <property name="row-spacing">3</property>
+          <object class="GtkMenuButton" id="configuration_button">
+            <property name="margin">12</property>
+            <property name="popover">configuration_popover</property>
             <property name="visible">true</property>
+            <style>
+              <class name="flat"/>
+            </style>
             <child>
-              <object class="GtkLabel" id="label1">
-                <property name="label" translatable="yes">Device:</property>
-                <property name="xalign">1.0</property>
+              <object class="GtkLabel" id="configuration_label">
+                <property name="ellipsize">middle</property>
                 <property name="visible">true</property>
-                <style>
-                  <class name="dim-label"/>
-                </style>
-              </object>
-              <packing>
-                <property name="left-attach">0</property>
-                <property name="top-attach">0</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkLabel" id="label2">
-                <property name="label" translatable="yes">Framework:</property>
-                <property name="xalign">1.0</property>
-                <property name="visible">false</property>
-                <style>
-                  <class name="dim-label"/>
-                </style>
               </object>
-              <packing>
-                <property name="left-attach">0</property>
-                <property name="top-attach">1</property>
-              </packing>
             </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">true</property>
+            <property name="margin-end">12</property>
+            <property name="margin-start">12</property>
+            <property name="margin-bottom">12</property>
+            <property name="halign">end</property>
+            <property name="hexpand">true</property>
+            <property name="spacing">6</property>
+            <property name="homogeneous">true</property>
             <child>
-              <object class="GtkLabel" id="label3">
-                <property name="label" translatable="yes">Packaging:</property>
-                <property name="xalign">1.0</property>
-                <property name="visible">false</property>
+              <object class="GtkButton">
+                <property name="action-name">build-tools.build</property>
+                <property name="label" translatable="yes">_Build</property>
+                <property name="use-underline">true</property>
+                <property name="visible">true</property>
                 <style>
-                  <class name="dim-label"/>
+                  <class name="suggested-action"/>
                 </style>
               </object>
-              <packing>
-                <property name="left-attach">0</property>
-                <property name="top-attach">2</property>
-              </packing>
             </child>
             <child>
-              <object class="GtkBox">
+              <object class="GtkButton">
+                <property name="action-name">build-tools.rebuild</property>
+                <property name="label" translatable="yes">_Rebuild</property>
+                <property name="use-underline">true</property>
                 <property name="visible">true</property>
-                <property name="margin-top">12</property>
-                <property name="halign">end</property>
-                <property name="hexpand">true</property>
-                <property name="spacing">6</property>
-                <property name="homogeneous">true</property>
-                <child>
-                  <object class="GtkButton">
-                    <property name="action-name">build-tools.build</property>
-                    <property name="label" translatable="yes">_Build</property>
-                    <property name="use-underline">true</property>
-                    <property name="visible">true</property>
-                    <style>
-                      <class name="suggested-action"/>
-                    </style>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkButton">
-                    <property name="action-name">build-tools.rebuild</property>
-                    <property name="label" translatable="yes">_Rebuild</property>
-                    <property name="use-underline">true</property>
-                    <property name="visible">true</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkButton">
-                    <property name="action-name">build-tools.clean</property>
-                    <property name="label" translatable="yes">_Clean</property>
-                    <property name="use-underline">true</property>
-                    <property name="visible">true</property>
-                  </object>
-                </child>
               </object>
-              <packing>
-                <property name="left-attach">0</property>
-                <property name="top-attach">3</property>
-                <property name="width">2</property>
-              </packing>
             </child>
             <child>
-              <object class="GtkMenuButton" id="device_button">
-                <property name="focus-on-click">false</property>
-                <property name="popover">device_popover</property>
+              <object class="GtkButton">
+                <property name="action-name">build-tools.clean</property>
+                <property name="label" translatable="yes">_Clean</property>
+                <property name="use-underline">true</property>
                 <property name="visible">true</property>
-                <style>
-                  <class name="flat"/>
-                </style>
-                <child>
-                  <object class="GtkBox">
-                    <property name="spacing">6</property>
-                    <property name="visible">true</property>
-                    <child>
-                      <object class="GtkLabel" id="device_label">
-                        <property name="visible">true</property>
-                        <property name="hexpand">false</property>
-                        <property name="xalign">0.0</property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkImage">
-                        <property name="icon-name">pan-down-symbolic</property>
-                        <property name="visible">true</property>
-                        <property name="hexpand">false</property>
-                        <property name="xalign">0.0</property>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-              </object>
-              <packing>
-                <property name="left-attach">1</property>
-                <property name="top-attach">0</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkMenuButton">
-                <property name="label" translatable="yes">framework</property>
-                <property name="visible">false</property>
-              </object>
-              <packing>
-                <property name="left-attach">1</property>
-                <property name="top-attach">1</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkMenuButton">
-                <property name="label" translatable="yes">packaging</property>
-                <property name="visible">false</property>
               </object>
-              <packing>
-                <property name="left-attach">1</property>
-                <property name="top-attach">2</property>
-              </packing>
             </child>
           </object>
         </child>
@@ -359,4 +266,23 @@
       </object>
     </child>
   </object>
+  <object class="GtkPopover" id="configuration_popover">
+    <child>
+      <object class="EggScrolledWindow">
+        <property name="min-content-width">250</property>
+        <property name="max-content-width">300</property>
+        <property name="max-content-height">300</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkListBox" id="configurations">
+            <property name="selection-mode">none</property>
+            <property name="visible">true</property>
+            <style>
+              <class name="buildpanel"/>
+            </style>
+          </object>
+        </child>
+      </object>
+    </child>
+  </object>
 </interface>
diff --git a/plugins/build-tools/gbp-build-perspective.c b/plugins/build-tools/gbp-build-perspective.c
new file mode 100644
index 0000000..28de2bc
--- /dev/null
+++ b/plugins/build-tools/gbp-build-perspective.c
@@ -0,0 +1,428 @@
+/* gbp-build-perspective.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "gbp-build-configuration-view.h"
+#include "gbp-build-perspective.h"
+
+struct _GbpBuildPerspective
+{
+  GtkBin                     parent_instance;
+
+  GActionGroup              *actions;
+  IdeConfiguration          *configuration;
+  IdeConfigurationManager   *configuration_manager;
+
+  GtkListBox                *list_box;
+  GbpBuildConfigurationView *view;
+};
+
+enum {
+  PROP_0,
+  PROP_CONFIGURATION,
+  PROP_CONFIGURATION_MANAGER,
+  LAST_PROP
+};
+
+static void perspective_iface_init (IdePerspectiveInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GbpBuildPerspective, gbp_build_perspective, GTK_TYPE_BIN, 0,
+                        G_IMPLEMENT_INTERFACE (IDE_TYPE_PERSPECTIVE, perspective_iface_init))
+
+static GParamSpec *properties [LAST_PROP];
+
+static gboolean
+map_pointer_to (GBinding     *binding,
+                const GValue *from_value,
+                GValue       *to_value,
+                gpointer      user_data)
+{
+  g_value_set_boolean (to_value, (user_data == g_value_get_object (from_value)));
+  return TRUE;
+}
+
+static GtkWidget *
+create_configuration_row (gpointer item,
+                          gpointer user_data)
+{
+  IdeConfigurationManager *manager = user_data;
+  IdeConfiguration *configuration = item;
+  GtkWidget *row;
+  GtkWidget *box;
+  GtkWidget *label;
+  GtkWidget *image;
+
+  g_assert (IDE_IS_CONFIGURATION (configuration));
+  g_assert (IDE_IS_CONFIGURATION_MANAGER (manager));
+
+  row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
+                      "visible", TRUE,
+                      NULL);
+  g_object_set_data_full (G_OBJECT (row), "IDE_CONFIGURATION",
+                          g_object_ref (configuration), g_object_unref);
+
+  box = g_object_new (GTK_TYPE_BOX,
+                      "orientation", GTK_ORIENTATION_HORIZONTAL,
+                      "visible", TRUE,
+                      NULL);
+  gtk_container_add (GTK_CONTAINER (row), box);
+
+  label = g_object_new (GTK_TYPE_LABEL,
+                        "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
+                        "visible", TRUE,
+                        "xalign", 0.0f,
+                        NULL);
+  g_object_bind_property (configuration, "display-name",
+                          label, "label",
+                          G_BINDING_SYNC_CREATE);
+  gtk_container_add (GTK_CONTAINER (box), label);
+
+  image = g_object_new (GTK_TYPE_IMAGE,
+                        "icon-name", "object-select-symbolic",
+                        "xpad", 6,
+                        NULL);
+  g_object_bind_property_full (manager, "current", image, "visible",
+                               G_BINDING_SYNC_CREATE,
+                               map_pointer_to, NULL, configuration, NULL);
+  gtk_container_add (GTK_CONTAINER (box), image);
+
+  label = g_object_new (GTK_TYPE_LABEL,
+                        "hexpand", TRUE,
+                        "visible", TRUE,
+                        NULL);
+  gtk_container_add (GTK_CONTAINER (box), label);
+
+  return row;
+}
+
+static void
+gbp_build_perspective_set_configuration_manager (GbpBuildPerspective     *self,
+                                                 IdeConfigurationManager *manager)
+{
+  g_assert (GBP_IS_BUILD_PERSPECTIVE (self));
+  g_assert (IDE_IS_CONFIGURATION_MANAGER (manager));
+
+  g_set_object (&self->configuration_manager, manager);
+  gtk_list_box_bind_model (self->list_box,
+                           G_LIST_MODEL (manager),
+                           create_configuration_row,
+                           g_object_ref (manager),
+                           g_object_unref);
+}
+
+static void
+gbp_build_perspective_row_selected (GbpBuildPerspective *self,
+                                    GtkListBoxRow       *row,
+                                    GtkListBox          *list_box)
+{
+  g_assert (GBP_IS_BUILD_PERSPECTIVE (self));
+  g_assert (!row || GTK_IS_LIST_BOX_ROW (row));
+  g_assert (GTK_IS_LIST_BOX (list_box));
+
+  if (row != NULL)
+    {
+      IdeConfiguration *configuration;
+
+      configuration = g_object_get_data (G_OBJECT (row), "IDE_CONFIGURATION");
+      g_set_object (&self->configuration, configuration);
+      gbp_build_configuration_view_set_configuration (self->view, configuration);
+    }
+}
+
+static void
+gbp_build_perspective_row_activated (GbpBuildPerspective *self,
+                                     GtkListBoxRow       *row,
+                                     GtkListBox          *list_box)
+{
+  IdeConfiguration *configuration;
+
+  g_assert (GBP_IS_BUILD_PERSPECTIVE (self));
+  g_assert (GTK_IS_LIST_BOX_ROW (row));
+  g_assert (GTK_IS_LIST_BOX (list_box));
+
+
+  configuration = g_object_get_data (G_OBJECT (row), "IDE_CONFIGURATION");
+  ide_configuration_manager_set_current (self->configuration_manager, configuration);
+}
+
+static void
+duplicate_configuration (GSimpleAction *action,
+                         GVariant      *variant,
+                         gpointer       user_data)
+{
+  GbpBuildPerspective *self = user_data;
+
+  g_assert (GBP_IS_BUILD_PERSPECTIVE (self));
+
+  if (self->configuration != NULL)
+    {
+      g_autoptr(IdeConfiguration) copy = NULL;
+
+      copy = ide_configuration_duplicate (self->configuration);
+      ide_configuration_manager_add (self->configuration_manager, copy);
+    }
+}
+
+static void
+delete_configuration (GSimpleAction *action,
+                      GVariant      *variant,
+                      gpointer       user_data)
+{
+  GbpBuildPerspective *self = user_data;
+
+  g_assert (GBP_IS_BUILD_PERSPECTIVE (self));
+
+  if (self->configuration != NULL)
+    {
+      g_autoptr(IdeConfiguration) config = NULL;
+
+      /*
+       * Make sure we hold onto a reference during the call, as it is likely
+       * self->configuration will change during this call.
+       */
+      config = g_object_ref (self->configuration);
+      ide_configuration_manager_remove (self->configuration_manager, config);
+
+      /*
+       * Switch to the first configuration in the list. The configuration
+       * manager should have added a new "default" configuration if we
+       * deleted the last configuration, so we should just get the 0th
+       * index.
+       */
+      if (g_list_model_get_n_items (G_LIST_MODEL (self->configuration_manager)) > 0)
+        {
+          g_autoptr(IdeConfiguration) first = NULL;
+
+          first = g_list_model_get_item (G_LIST_MODEL (self->configuration_manager), 0);
+          gbp_build_perspective_set_configuration (self, first);
+        }
+    }
+}
+
+static void
+gbp_build_perspective_finalize (GObject *object)
+{
+  GbpBuildPerspective *self = (GbpBuildPerspective *)object;
+
+  g_clear_object (&self->actions);
+  g_clear_object (&self->configuration);
+
+  G_OBJECT_CLASS (gbp_build_perspective_parent_class)->finalize (object);
+}
+
+static void
+gbp_build_perspective_get_property (GObject    *object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+  GbpBuildPerspective *self = GBP_BUILD_PERSPECTIVE (object);
+
+  switch (prop_id)
+    {
+    case PROP_CONFIGURATION:
+      g_value_set_object (value, gbp_build_perspective_get_configuration (self));
+      break;
+
+    case PROP_CONFIGURATION_MANAGER:
+      g_value_set_object (value, self->configuration_manager);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_build_perspective_set_property (GObject      *object,
+                                    guint         prop_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+  GbpBuildPerspective *self = GBP_BUILD_PERSPECTIVE (object);
+
+  switch (prop_id)
+    {
+    case PROP_CONFIGURATION:
+      gbp_build_perspective_set_configuration (self, g_value_get_object (value));
+      break;
+
+    case PROP_CONFIGURATION_MANAGER:
+      gbp_build_perspective_set_configuration_manager (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_build_perspective_class_init (GbpBuildPerspectiveClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = gbp_build_perspective_finalize;
+  object_class->get_property = gbp_build_perspective_get_property;
+  object_class->set_property = gbp_build_perspective_set_property;
+
+  properties [PROP_CONFIGURATION_MANAGER] =
+    g_param_spec_object ("configuration-manager",
+                         "Configuration Manager",
+                         "Configuration Manager",
+                         IDE_TYPE_CONFIGURATION_MANAGER,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_CONFIGURATION] =
+    g_param_spec_object ("configuration",
+                         "Configuration",
+                         "The configuration to edit",
+                         IDE_TYPE_CONFIGURATION,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, LAST_PROP, properties);
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/builder/plugins/build-tools-plugin/gbp-build-perspective.ui");
+  gtk_widget_class_set_css_name (widget_class, "buildperspective");
+  gtk_widget_class_bind_template_child (widget_class, GbpBuildPerspective, list_box);
+  gtk_widget_class_bind_template_child (widget_class, GbpBuildPerspective, view);
+
+  g_type_ensure (GBP_TYPE_BUILD_CONFIGURATION_VIEW);
+}
+
+static void
+gbp_build_perspective_init (GbpBuildPerspective *self)
+{
+  static GActionEntry actions[] = {
+    { "delete-configuration", delete_configuration },
+    { "duplicate-configuration", duplicate_configuration },
+  };
+
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  g_signal_connect_object (self->list_box,
+                           "row-selected",
+                           G_CALLBACK (gbp_build_perspective_row_selected),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (self->list_box,
+                           "row-activated",
+                           G_CALLBACK (gbp_build_perspective_row_activated),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  self->actions = G_ACTION_GROUP (g_simple_action_group_new ());
+  g_action_map_add_action_entries (G_ACTION_MAP (self->actions), actions,
+                                   G_N_ELEMENTS (actions), self);
+}
+
+GtkWidget *
+gbp_build_perspective_new (void)
+{
+  return g_object_new (GBP_TYPE_BUILD_PERSPECTIVE, NULL);
+}
+
+IdeConfiguration *
+gbp_build_perspective_get_configuration (GbpBuildPerspective *self)
+{
+  g_return_val_if_fail (GBP_IS_BUILD_PERSPECTIVE (self), NULL);
+
+  return self->configuration;
+}
+
+static void
+find_configuration_row (GtkWidget *widget,
+                        gpointer   data)
+{
+  struct {
+    IdeConfiguration *config;
+    GtkWidget        *row;
+  } *lookup = data;
+
+  if (lookup->row != NULL)
+    return;
+
+  if (lookup->config == g_object_get_data (G_OBJECT (widget), "IDE_CONFIGURATION"))
+    lookup->row = widget;
+}
+
+void
+gbp_build_perspective_set_configuration (GbpBuildPerspective *self,
+                                         IdeConfiguration    *configuration)
+{
+  struct {
+    IdeConfiguration *config;
+    GtkWidget        *row;
+  } lookup = { configuration, NULL };
+
+  g_return_if_fail (GBP_IS_BUILD_PERSPECTIVE (self));
+  g_return_if_fail (!configuration || IDE_IS_CONFIGURATION (configuration));
+
+  gtk_container_foreach (GTK_CONTAINER (self->list_box),
+                         find_configuration_row,
+                         &lookup);
+
+  if (GTK_IS_LIST_BOX_ROW (lookup.row))
+    gtk_list_box_select_row (self->list_box, GTK_LIST_BOX_ROW (lookup.row));
+}
+
+static gchar *
+gbp_build_perspective_get_icon_name (IdePerspective *perspective)
+{
+  return g_strdup ("builder-build-configure-symbolic");
+}
+
+static gchar *
+gbp_build_perspective_get_title (IdePerspective *perspective)
+{
+  return g_strdup ("Build Configuration");
+}
+
+static gchar *
+gbp_build_perspective_get_id (IdePerspective *perspective)
+{
+  return g_strdup ("buildperspective");
+}
+
+static gint
+gbp_build_perspective_get_priority (IdePerspective *perspective)
+{
+  return 80000;
+}
+
+static GActionGroup *
+gbp_build_perspective_get_actions (IdePerspective *perspective)
+{
+  GbpBuildPerspective *self = (GbpBuildPerspective *)perspective;
+
+  g_assert (GBP_IS_BUILD_PERSPECTIVE (self));
+
+  return g_object_ref (self->actions);
+}
+
+static void
+perspective_iface_init (IdePerspectiveInterface *iface)
+{
+  iface->get_actions = gbp_build_perspective_get_actions;
+  iface->get_icon_name = gbp_build_perspective_get_icon_name;
+  iface->get_title = gbp_build_perspective_get_title;
+  iface->get_id = gbp_build_perspective_get_id;
+  iface->get_priority = gbp_build_perspective_get_priority;
+}
diff --git a/plugins/build-tools/gbp-build-perspective.h b/plugins/build-tools/gbp-build-perspective.h
new file mode 100644
index 0000000..22242f7
--- /dev/null
+++ b/plugins/build-tools/gbp-build-perspective.h
@@ -0,0 +1,36 @@
+/* gbp-build-perspective.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GBP_BUILD_PERSPECTIVE_H
+#define GBP_BUILD_PERSPECTIVE_H
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_BUILD_PERSPECTIVE (gbp_build_perspective_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpBuildPerspective, gbp_build_perspective, GBP, BUILD_PERSPECTIVE, GtkBin)
+
+IdeConfiguration *gbp_build_perspective_get_configuration (GbpBuildPerspective *self);
+void              gbp_build_perspective_set_configuration (GbpBuildPerspective *self,
+                                                           IdeConfiguration    *configuration);
+
+G_END_DECLS
+
+#endif /* GBP_BUILD_PERSPECTIVE_H */
diff --git a/plugins/build-tools/gbp-build-perspective.ui b/plugins/build-tools/gbp-build-perspective.ui
new file mode 100644
index 0000000..da4490a
--- /dev/null
+++ b/plugins/build-tools/gbp-build-perspective.ui
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.18 -->
+  <template class="GbpBuildPerspective" parent="GtkBin">
+    <child>
+      <object class="GtkBox">
+        <property name="orientation">horizontal</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkScrolledWindow">
+            <property name="hscrollbar-policy">never</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkListBox" id="list_box">
+                <property name="selection-mode">browse</property>
+                <property name="activate-on-single-click">false</property>
+                <property name="width-request">200</property>
+                <property name="visible">true</property>
+                <style>
+                  <class name="sidebar"/>
+                </style>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkScrolledWindow">
+            <property name="hscrollbar-policy">never</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GbpBuildConfigurationView" id="view">
+                <property name="expand">true</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="IdeWorkbenchHeaderBar" id="titlebar">
+    <property name="visible">true</property>
+    <child type="title">
+      <object class="GtkSearchEntry" id="search_entry">
+        <property name="hexpand">true</property>
+        <property name="margin-end">6</property>
+        <property name="margin-start">6</property>
+        <property name="max-width-chars">50</property>
+        <property name="visible">true</property>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/plugins/build-tools/gbp-build-tool.c b/plugins/build-tools/gbp-build-tool.c
index b24dea6..6e886ac 100644
--- a/plugins/build-tools/gbp-build-tool.c
+++ b/plugins/build-tools/gbp-build-tool.c
@@ -30,6 +30,12 @@ struct _GbpBuildTool
   gint64  build_start;
 };
 
+static gint                  parallel = -1;
+static IdeBuilderBuildFlags  flags;
+static gchar                *configuration_id;
+static gchar                *device_id;
+static gchar                *runtime_id;
+
 static void application_tool_init (IdeApplicationToolInterface *iface);
 
 G_DEFINE_TYPE_EXTENDED (GbpBuildTool, gbp_build_tool, G_TYPE_OBJECT, 0,
@@ -60,15 +66,17 @@ gbp_build_tool_log (GbpBuildTool      *self,
 
 static void
 print_build_info (IdeContext *context,
-                  IdeDevice  *device)
+                  IdeConfiguration *configuration)
 {
   IdeProject *project;
   IdeBuildSystem *build_system;
+  IdeDevice *device;
   IdeVcs *vcs;
+  g_auto(GStrv) env = NULL;
+  const gchar *dev_id;
   const gchar *project_name;
   const gchar *vcs_name;
   const gchar *build_system_name;
-  const gchar *device_id;
   const gchar *system_type;
   g_autofree gchar *build_date = NULL;
   GTimeVal tv;
@@ -82,9 +90,12 @@ print_build_info (IdeContext *context,
   build_system = ide_context_get_build_system (context);
   build_system_name = g_type_name (G_TYPE_FROM_INSTANCE (build_system));
 
-  device_id = ide_device_get_id (device);
+  device = ide_configuration_get_device (configuration);
+  dev_id = ide_device_get_id (device);
   system_type = ide_device_get_system_type (device);
 
+  env = ide_configuration_get_environ (configuration);
+
   g_get_current_time (&tv);
   build_date = g_time_val_to_iso8601 (&tv);
 
@@ -93,7 +104,14 @@ print_build_info (IdeContext *context,
   g_printerr (_(" Version Control System: %s\n"), vcs_name);
   g_printerr (_("           Build System: %s\n"), build_system_name);
   g_printerr (_("    Build Date and Time: %s\n"), build_date);
-  g_printerr (_("    Building for Device: %s (%s)\n"), device_id, system_type);
+  g_printerr (_("    Building for Device: %s (%s)\n"), dev_id, system_type);
+
+  if (env && env [0])
+    {
+      g_autofree gchar *envstr = g_strjoinv (" ", env);
+      g_printerr (_("            Environment: %s\n"), envstr);
+    }
+
   g_printerr (_("========================\n"));
 }
 
@@ -155,12 +173,10 @@ gbp_build_tool_new_context_cb (GObject      *object,
   g_autoptr(IdeBuilder) builder = NULL;
   g_autoptr(IdeBuildResult) build_result = NULL;
   g_autoptr(IdeDevice) device = NULL;
-  IdeDeviceManager *device_manager;
+  g_autoptr(IdeConfiguration) configuration = NULL;
+  IdeConfigurationManager *configuration_manager;
   IdeBuildSystem *build_system;
   GbpBuildTool *self;
-  IdeBuilderBuildFlags flags;
-  GKeyFile *config;
-  const gchar *device_id;
   GError *error = NULL;
 
   g_assert (G_IS_TASK (task));
@@ -175,14 +191,20 @@ gbp_build_tool_new_context_cb (GObject      *object,
       return;
     }
 
-  config = g_object_get_data (G_OBJECT (task), "CONFIG");
-  flags = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (task), "FLAGS"));
+  configuration_manager = ide_context_get_configuration_manager (context);
 
-  device_id = g_object_get_data (G_OBJECT (task), "DEVICE_ID");
-  device_manager = ide_context_get_device_manager (context);
-  device = ide_device_manager_get_device (device_manager, device_id);
+  if (configuration_id != NULL)
+    configuration = ide_configuration_manager_get_configuration (configuration_manager, configuration_id);
+  else if (device_id && runtime_id)
+    configuration = ide_configuration_new (context, "command-line-build", device_id, runtime_id);
+  else if (device_id)
+    configuration = ide_configuration_new (context, "command-line-build", device_id, "host");
+  else if (runtime_id)
+    configuration = ide_configuration_new (context, "command-line-build", "local", runtime_id);
+  else
+    configuration = ide_configuration_manager_get_current (configuration_manager);
 
-  if (device == NULL)
+  if (!ide_configuration_get_device (configuration))
     {
       /* TODO: Wait for devices to settle. */
       g_task_return_new_error (task,
@@ -193,12 +215,27 @@ gbp_build_tool_new_context_cb (GObject      *object,
       return;
     }
 
-  print_build_info (context, device);
+  if (!ide_configuration_get_runtime (configuration))
+    {
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_NOT_FOUND,
+                               _("Failed to locate runtime \"%s\""),
+                               runtime_id);
+      return;
+    }
+
+  if (parallel > -1)
+    {
+      /* TODO: put this into IdeConfiguration:parallel: */
+      g_autofree gchar *str = g_strdup_printf ("%d", parallel);
+      ide_configuration_setenv (configuration, "PARALLEL", str);
+    }
 
-  /* TODO: Support custom configs */
+  print_build_info (context, configuration);
 
   build_system = ide_context_get_build_system (context);
-  builder = ide_build_system_get_builder (build_system, config, device, &error);
+  builder = ide_build_system_get_builder (build_system, configuration, &error);
 
   if (builder == NULL)
     {
@@ -240,14 +277,10 @@ gbp_build_tool_run_async (IdeApplicationTool  *tool,
   GbpBuildTool *self = (GbpBuildTool *)tool;
   g_autoptr(GTask) task = NULL;
   g_autofree gchar *project_path = NULL;
-  g_autofree gchar *device_id = NULL;
   g_autoptr(GFile) project_file = NULL;
   g_autoptr(GOptionContext) opt_context = NULL;
-  g_autoptr(GKeyFile) config = NULL;
   g_auto(GStrv) strv = NULL;
   gboolean clean = FALSE;
-  gint parallel = -1;
-  IdeBuilderBuildFlags flags = 0;
   GError *error = NULL;
   const GOptionEntry entries[] = {
     { "clean", 'c', 0, G_OPTION_ARG_NONE, &clean,
@@ -255,9 +288,15 @@ gbp_build_tool_run_async (IdeApplicationTool  *tool,
     { "device", 'd', 0, G_OPTION_ARG_STRING, &device_id,
       N_("The ID of the device to build for"),
       N_("local") },
+    { "runtime", 'r', 0, G_OPTION_ARG_STRING, &runtime_id,
+      N_("The runtime to use for building"),
+      N_("host") },
     { "parallel", 'j', 0, G_OPTION_ARG_INT, &parallel,
       N_("Number of workers to use when building"),
       N_("N") },
+    { "configuration", 't', 0, G_OPTION_ARG_STRING, &configuration_id,
+      N_("The configuration to use from .buildconfig"),
+      N_("CONFIG_ID") },
     { "project", 'p', 0, G_OPTION_ARG_FILENAME, &project_path,
       N_("Path to project file, defaults to current directory"),
       N_("PATH") },
@@ -288,17 +327,11 @@ gbp_build_tool_run_async (IdeApplicationTool  *tool,
   if (device_id == NULL)
     device_id = g_strdup ("local");
 
-  config = g_key_file_new ();
-
-  if (parallel >= -1)
-    g_key_file_set_integer (config, "parallel", "workers", parallel);
-
   if (clean)
-    flags |= IDE_BUILDER_BUILD_FLAGS_CLEAN;
-
-  g_object_set_data_full (G_OBJECT (task), "DEVICE_ID", g_strdup (device_id), g_free);
-  g_object_set_data_full (G_OBJECT (task), "CONFIG", g_key_file_ref (config), 
(GDestroyNotify)g_key_file_unref);
-  g_object_set_data (G_OBJECT (task), "FLAGS", GINT_TO_POINTER (flags));
+    {
+      flags |= IDE_BUILDER_BUILD_FLAGS_FORCE_CLEAN;
+      flags |= IDE_BUILDER_BUILD_FLAGS_NO_BUILD;
+    }
 
   ide_context_new_async (project_file,
                          cancellable,
diff --git a/plugins/build-tools/gbp-build-tools.gresource.xml 
b/plugins/build-tools/gbp-build-tools.gresource.xml
index fa21ac1..7204608 100644
--- a/plugins/build-tools/gbp-build-tools.gresource.xml
+++ b/plugins/build-tools/gbp-build-tools.gresource.xml
@@ -2,9 +2,14 @@
 <gresources>
   <gresource prefix="/org/gnome/builder/plugins/build-tools-plugin">
     <file>gtk/menus.ui</file>
-    <file>theme/shared.css</file>
+    <file>theme/Adwaita.css</file>
+    <file>theme/Adwaita-dark.css</file>
+    <file>theme/Adwaita-shared.css</file>
+    <file>gbp-build-configuration-row.ui</file>
+    <file>gbp-build-configuration-view.ui</file>
     <file>gbp-build-log-panel.ui</file>
-    <file>gbp-build-panel.ui</file>
     <file>gbp-build-panel-row.ui</file>
+    <file>gbp-build-panel.ui</file>
+    <file>gbp-build-perspective.ui</file>
   </gresource>
 </gresources>
diff --git a/plugins/build-tools/gbp-build-workbench-addin.c b/plugins/build-tools/gbp-build-workbench-addin.c
index df558f8..52369dc 100644
--- a/plugins/build-tools/gbp-build-workbench-addin.c
+++ b/plugins/build-tools/gbp-build-workbench-addin.c
@@ -20,8 +20,9 @@
 
 #include "egg-binding-group.h"
 
-#include "gbp-build-panel.h"
 #include "gbp-build-log-panel.h"
+#include "gbp-build-panel.h"
+#include "gbp-build-perspective.h"
 #include "gbp-build-workbench-addin.h"
 
 struct _GbpBuildWorkbenchAddin
@@ -38,18 +39,15 @@ struct _GbpBuildWorkbenchAddin
   IdeBuildResult     *result;
   GSimpleActionGroup *actions;
   GCancellable       *cancellable;
-  IdeDevice          *device;
 };
 
 static void workbench_addin_iface_init (IdeWorkbenchAddinInterface *iface);
 
 G_DEFINE_TYPE_EXTENDED (GbpBuildWorkbenchAddin, gbp_build_workbench_addin, G_TYPE_OBJECT, 0,
-                        G_IMPLEMENT_INTERFACE (IDE_TYPE_WORKBENCH_ADDIN,
-                                               workbench_addin_iface_init))
+                        G_IMPLEMENT_INTERFACE (IDE_TYPE_WORKBENCH_ADDIN, workbench_addin_iface_init))
 
 enum {
   PROP_0,
-  PROP_DEVICE,
   PROP_RESULT,
   LAST_PROP
 };
@@ -57,25 +55,6 @@ enum {
 static GParamSpec *properties [LAST_PROP];
 
 static void
-gbp_build_workbench_addin_set_device (GbpBuildWorkbenchAddin *self,
-                                      IdeDevice              *device)
-{
-  g_assert (GBP_IS_BUILD_WORKBENCH_ADDIN (self));
-  g_assert (IDE_IS_DEVICE (device));
-
-  if (g_set_object (&self->device, device))
-    {
-      const gchar *id = ide_device_get_id (device);
-      GAction *action;
-
-      action = g_action_map_lookup_action (G_ACTION_MAP (self->actions), "device");
-      g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_string (id));
-
-      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DEVICE]);
-    }
-}
-
-static void
 gbp_build_workbench_addin_set_result (GbpBuildWorkbenchAddin *self,
                                       IdeBuildResult         *result)
 {
@@ -148,6 +127,8 @@ gbp_build_workbench_addin_do_build (GbpBuildWorkbenchAddin *self,
 {
   g_autoptr(IdeBuilder) builder = NULL;
   g_autoptr(GError) error = NULL;
+  IdeConfigurationManager *config_manager;
+  IdeConfiguration *configuration;
   IdeBuildSystem *build_system;
   IdeWorkbench *workbench;
   IdeContext *context;
@@ -164,7 +145,10 @@ gbp_build_workbench_addin_do_build (GbpBuildWorkbenchAddin *self,
   workbench = ide_widget_get_workbench (GTK_WIDGET (self->panel));
   context = ide_workbench_get_context (workbench);
   build_system = ide_context_get_build_system (context);
-  builder = ide_build_system_get_builder (build_system, NULL, self->device, &error);
+  config_manager = ide_context_get_configuration_manager (context);
+  configuration = ide_configuration_manager_get_current (config_manager);
+
+  builder = ide_build_system_get_builder (build_system, configuration, &error);
 
   if (error != NULL)
     {
@@ -217,7 +201,7 @@ gbp_build_workbench_addin_rebuild (GSimpleAction *action,
   g_assert (G_IS_SIMPLE_ACTION (action));
   g_assert (GBP_IS_BUILD_WORKBENCH_ADDIN (self));
 
-  gbp_build_workbench_addin_do_build (self, IDE_BUILDER_BUILD_FLAGS_FORCE_REBUILD);
+  gbp_build_workbench_addin_do_build (self, IDE_BUILDER_BUILD_FLAGS_FORCE_CLEAN);
 }
 
 static void
@@ -230,7 +214,9 @@ gbp_build_workbench_addin_clean (GSimpleAction *action,
   g_assert (G_IS_SIMPLE_ACTION (action));
   g_assert (GBP_IS_BUILD_WORKBENCH_ADDIN (self));
 
-  gbp_build_workbench_addin_do_build (self, IDE_BUILDER_BUILD_FLAGS_CLEAN);
+  gbp_build_workbench_addin_do_build (self,
+                                      (IDE_BUILDER_BUILD_FLAGS_FORCE_CLEAN |
+                                       IDE_BUILDER_BUILD_FLAGS_NO_BUILD));
 }
 
 static void
@@ -246,68 +232,24 @@ gbp_build_workbench_addin_cancel (GSimpleAction *action,
     g_cancellable_cancel (self->cancellable);
 }
 
-static void
-gbp_build_workbench_addin_deploy (GSimpleAction *action,
-                                  GVariant      *param,
-                                  gpointer       user_data)
-{
-}
-
-static void
-gbp_build_workbench_addin_export (GSimpleAction *action,
-                                  GVariant      *param,
-                                  gpointer       user_data)
-{
-}
-
-static void
-gbp_build_workbench_addin_device (GSimpleAction *action,
-                                  GVariant      *param,
-                                  gpointer       user_data)
-{
-  GbpBuildWorkbenchAddin *self = user_data;
-  IdeDeviceManager *device_manager;
-  IdeContext *context;
-  IdeDevice *device;
-  const gchar *id;
-
-  g_assert (GBP_IS_BUILD_WORKBENCH_ADDIN (self));
-  g_assert (IDE_IS_WORKBENCH (self->workbench));
-
-  id = g_variant_get_string (param, NULL);
-  if (id == NULL)
-    id = "local";
-
-  context = ide_workbench_get_context (self->workbench);
-  device_manager = ide_context_get_device_manager (context);
-  device = ide_device_manager_get_device (device_manager, id);
-
-  if (device == NULL)
-    device = ide_device_manager_get_device (device_manager, "local");
-
-  gbp_build_workbench_addin_set_device (self, device);
-}
-
 static const GActionEntry actions[] = {
   { "build", gbp_build_workbench_addin_build },
   { "rebuild", gbp_build_workbench_addin_rebuild },
   { "clean", gbp_build_workbench_addin_clean },
   { "cancel-build", gbp_build_workbench_addin_cancel },
-  { "deploy", gbp_build_workbench_addin_deploy },
-  { "export", gbp_build_workbench_addin_export },
-  { "device", NULL, "s", "'local'", gbp_build_workbench_addin_device },
 };
 
 static void
 gbp_build_workbench_addin_load (IdeWorkbenchAddin *addin,
                                 IdeWorkbench      *workbench)
 {
+  IdeConfigurationManager *configuration_manager;
   GbpBuildWorkbenchAddin *self = (GbpBuildWorkbenchAddin *)addin;
+  IdeConfiguration *configuration;
   IdePerspective *editor;
-  GtkWidget *pane;
   IdeContext *context;
-  IdeDeviceManager *device_manager;
-  IdeDevice *device;
+  GtkWidget *build_perspective;
+  GtkWidget *pane;
 
   g_assert (IDE_IS_WORKBENCH_ADDIN (addin));
   g_assert (GBP_IS_BUILD_WORKBENCH_ADDIN (self));
@@ -316,33 +258,33 @@ gbp_build_workbench_addin_load (IdeWorkbenchAddin *addin,
   self->workbench = workbench;
 
   context = ide_workbench_get_context (workbench);
-  device_manager = ide_context_get_device_manager (context);
-  device = ide_device_manager_get_device (device_manager, "local");
+  configuration_manager = ide_context_get_configuration_manager (context);
+  configuration = ide_configuration_manager_get_current (configuration_manager);
 
   editor = ide_workbench_get_perspective_by_name (workbench, "editor");
   pane = ide_layout_get_right_pane (IDE_LAYOUT (editor));
   self->panel = g_object_new (GBP_TYPE_BUILD_PANEL,
-                              "device", device,
-                              "device-manager", device_manager,
+                              "configuration-manager", configuration_manager,
                               "visible", TRUE,
                               NULL);
-  ide_layout_pane_add_page (IDE_LAYOUT_PANE (pane),
-                            GTK_WIDGET (self->panel),
-                            _("Build"), NULL);
+  ide_layout_pane_add_page (IDE_LAYOUT_PANE (pane), GTK_WIDGET (self->panel), _("Build"), NULL);
 
   pane = ide_layout_get_bottom_pane (IDE_LAYOUT (editor));
   self->build_log_panel = g_object_new (GBP_TYPE_BUILD_LOG_PANEL, NULL);
-  ide_layout_pane_add_page (IDE_LAYOUT_PANE (pane),
-                            GTK_WIDGET (self->build_log_panel),
+  ide_layout_pane_add_page (IDE_LAYOUT_PANE (pane), GTK_WIDGET (self->build_log_panel),
                             _("Build Output"), NULL);
 
   gtk_widget_insert_action_group (GTK_WIDGET (workbench), "build-tools",
                                   G_ACTION_GROUP (self->actions));
 
   g_object_bind_property (self, "result", self->panel, "result", 0);
-  g_object_bind_property (self, "device", self->panel, "device", 0);
 
-  gbp_build_workbench_addin_set_device (self, device);
+  build_perspective = g_object_new (GBP_TYPE_BUILD_PERSPECTIVE,
+                                    "configuration-manager", configuration_manager,
+                                    "configuration", configuration,
+                                    "visible", TRUE,
+                                    NULL);
+  ide_workbench_add_perspective (workbench, IDE_PERSPECTIVE (build_perspective));
 }
 
 static void
@@ -379,10 +321,6 @@ gbp_build_workbench_addin_get_property (GObject    *object,
 
   switch (prop_id)
     {
-    case PROP_DEVICE:
-      g_value_set_object (value, self->device);
-      break;
-
     case PROP_RESULT:
       g_value_set_object (value, self->result);
       break;
@@ -393,30 +331,10 @@ gbp_build_workbench_addin_get_property (GObject    *object,
 }
 
 static void
-gbp_build_workbench_addin_set_property (GObject      *object,
-                                        guint         prop_id,
-                                        const GValue *value,
-                                        GParamSpec   *pspec)
-{
-  GbpBuildWorkbenchAddin *self = GBP_BUILD_WORKBENCH_ADDIN(object);
-
-  switch (prop_id)
-    {
-    case PROP_DEVICE:
-      gbp_build_workbench_addin_set_device (self, g_value_get_object (value));
-      break;
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
-    }
-}
-
-static void
 gbp_build_workbench_addin_finalize (GObject *object)
 {
   GbpBuildWorkbenchAddin *self = (GbpBuildWorkbenchAddin *)object;
 
-  g_clear_object (&self->device);
   g_clear_object (&self->bindings);
   g_clear_object (&self->actions);
   g_clear_object (&self->result);
@@ -432,14 +350,6 @@ gbp_build_workbench_addin_class_init (GbpBuildWorkbenchAddinClass *klass)
 
   object_class->finalize = gbp_build_workbench_addin_finalize;
   object_class->get_property = gbp_build_workbench_addin_get_property;
-  object_class->set_property = gbp_build_workbench_addin_set_property;
-
-  properties [PROP_DEVICE] =
-    g_param_spec_object ("device",
-                         "Device",
-                         "The device the build is for",
-                         IDE_TYPE_DEVICE,
-                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   properties [PROP_RESULT] =
     g_param_spec_object ("result",
diff --git a/plugins/build-tools/theme/Adwaita-dark.css b/plugins/build-tools/theme/Adwaita-dark.css
new file mode 100644
index 0000000..1fea00a
--- /dev/null
+++ b/plugins/build-tools/theme/Adwaita-dark.css
@@ -0,0 +1 @@
+ import url("resource:///org/gnome/builder/plugins/build-tools-plugin/theme/Adwaita-shared.css");
diff --git a/plugins/build-tools/theme/Adwaita-shared.css b/plugins/build-tools/theme/Adwaita-shared.css
new file mode 100644
index 0000000..834fd14
--- /dev/null
+++ b/plugins/build-tools/theme/Adwaita-shared.css
@@ -0,0 +1,82 @@
+/*
+buildpanel button.popup:active,
+buildpanel button.popup:checked,
+buildpanel button.popup {
+  background-image: none;
+  background-color: transparent;
+  box-shadow: none;
+  border-style: none;
+  margin: 0;
+  padding: 0;
+}
+*/
+
+list.buildpanel row {
+  padding: 10px;
+  border-bottom: 1px solid alpha(@borders, 0.4);
+}
+
+list.buildpanel row:last-child {
+  border-bottom: none;
+}
+
+buildpanel list row {
+  padding: 6px;
+  border-bottom: 1px solid alpha(@borders, 0.4);
+}
+
+buildpanel list row:last-child {
+  border-bottom: none;
+}
+
+buildpanel list row label.file {
+  opacity: 0.5;
+}
+
+/*
+buildpanel list row image {
+  opacity: 0.5;
+}
+*/
+
+buildlogpanel textview {
+  padding: 3px;
+}
+
+buildpanel box.build-status {
+  background: shade(@theme_bg_color, 0.96);
+}
+
+
+buildperspective list.sidebar row {
+  padding: 10px;
+  border-bottom: 1px solid alpha(@borders, 0.2);
+}
+buildperspective list.sidebar row:last-child {
+  border-bottom: none;
+}
+buildperspective list.sidebar {
+  background: @theme_base_color;
+  border-right: 1px solid alpha(@borders, 0.4);
+}
+
+
+configurationview list row {
+  padding: 10px;
+  border-bottom: 1px solid alpha(@borders, 0.4);
+}
+configurationview list row:last-child {
+  border-bottom: none;
+}
+configurationview list row entry {
+  background: transparent;
+  border: none;
+  padding: 0;
+  margin: 0;
+}
+
+
+/* hrmm, we can use this to get row separators */
+configurationview treeview {
+  border-bottom: 1px solid alpha(@borders, 0.4);
+}
diff --git a/plugins/build-tools/theme/Adwaita.css b/plugins/build-tools/theme/Adwaita.css
new file mode 100644
index 0000000..1fea00a
--- /dev/null
+++ b/plugins/build-tools/theme/Adwaita.css
@@ -0,0 +1 @@
+ import url("resource:///org/gnome/builder/plugins/build-tools-plugin/theme/Adwaita-shared.css");


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