[gnome-builder/wip/chergert/gobjgen] wip



commit 28cbeec74b36c84ac1e258d09f28d55a924c12e1
Author: Christian Hergert <chergert redhat com>
Date:   Fri Jul 22 17:32:55 2016 -0700

    wip

 configure.ac                                       |    2 +
 plugins/Makefile.am                                |    1 +
 plugins/gobject-templates/Makefile.am              |   48 ++
 plugins/gobject-templates/configure.ac             |   12 +
 plugins/gobject-templates/gbp-gobject-dialog.c     |  179 ++++++++
 plugins/gobject-templates/gbp-gobject-dialog.h     |   37 ++
 plugins/gobject-templates/gbp-gobject-dialog.ui    |  452 ++++++++++++++++++++
 .../gbp-gobject-property-editor.c                  |  127 ++++++
 .../gbp-gobject-property-editor.h                  |   33 ++
 .../gbp-gobject-property-editor.ui                 |  271 ++++++++++++
 plugins/gobject-templates/gbp-gobject-property.c   |  380 ++++++++++++++++
 plugins/gobject-templates/gbp-gobject-property.h   |   79 ++++
 plugins/gobject-templates/gbp-gobject-signal.c     |  111 +++++
 plugins/gobject-templates/gbp-gobject-signal.h     |   33 ++
 plugins/gobject-templates/gbp-gobject-spec.c       |  229 ++++++++++
 plugins/gobject-templates/gbp-gobject-spec.h       |   46 ++
 .../gbp-gobject-workbench-addin.c                  |  132 ++++++
 .../gbp-gobject-workbench-addin.h                  |   33 ++
 .../gobject-templates/gobject-templates-plugin.c   |   30 ++
 .../gobject-templates.gresource.xml                |    8 +
 plugins/gobject-templates/gobject-templates.plugin |    9 +
 plugins/gobject-templates/gtk/menus.ui             |   24 +
 22 files changed, 2276 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index ebe62c4..2b336e2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -295,6 +295,7 @@ m4_include([plugins/gcc/configure.ac])
 m4_include([plugins/gettext/configure.ac])
 m4_include([plugins/git/configure.ac])
 m4_include([plugins/gnome-code-assistance/configure.ac])
+m4_include([plugins/gobject-templates/configure.ac])
 m4_include([plugins/hello-cpp/configure.ac])
 m4_include([plugins/html-completion/configure.ac])
 m4_include([plugins/html-preview/configure.ac])
@@ -609,5 +610,6 @@ echo ""
 echo " Templates"
 echo ""
 echo "  Autotools ............................ : ${enable_autotools_templates}"
+echo "  GObject .............................. : ${enable_gobject_templates}"
 echo ""
 
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 0827d68..db83ac5 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -18,6 +18,7 @@ SUBDIRS = \
        gettext \
        git \
        gnome-code-assistance \
+       gobject-templates \
        hello-cpp \
        html-completion \
        html-preview \
diff --git a/plugins/gobject-templates/Makefile.am b/plugins/gobject-templates/Makefile.am
new file mode 100644
index 0000000..6be8520
--- /dev/null
+++ b/plugins/gobject-templates/Makefile.am
@@ -0,0 +1,48 @@
+if ENABLE_GOBJECT_TEMPLATES
+
+DISTCLEANFILES =
+BUILT_SOURCES =
+CLEANFILES =
+EXTRA_DIST = $(plugin_DATA)
+
+plugindir = $(libdir)/gnome-builder/plugins
+plugin_LTLIBRARIES = libgobject-templates.la
+dist_plugin_DATA = gobject-templates.plugin
+
+libgobject_templates_la_SOURCES = \
+       gobject-templates-plugin.c \
+       gbp-gobject-dialog.c \
+       gbp-gobject-dialog.h \
+       gbp-gobject-property.c \
+       gbp-gobject-property.h \
+       gbp-gobject-property-editor.c \
+       gbp-gobject-property-editor.h \
+       gbp-gobject-signal.c \
+       gbp-gobject-signal.h \
+       gbp-gobject-spec.c \
+       gbp-gobject-spec.h \
+       gbp-gobject-workbench-addin.c \
+       gbp-gobject-workbench-addin.h \
+       $(NULL)
+
+nodist_libgobject_templates_la_SOURCES = \
+       resources.c \
+       resources.h
+
+libgobject_templates_la_CFLAGS = \
+       $(PLUGIN_CFLAGS) \
+       $(NULL)
+
+libgobject_templates_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+
+glib_resources_c = resources.c
+glib_resources_h = resources.h
+glib_resources_xml = gobject-templates.gresource.xml
+glib_resources_namespace = gbp_gobject_templates
+include $(top_srcdir)/build/autotools/Makefile.am.gresources
+
+include $(top_srcdir)/plugins/Makefile.plugin
+
+endif
+
+-include $(top_srcdir)/git.mk
diff --git a/plugins/gobject-templates/configure.ac b/plugins/gobject-templates/configure.ac
new file mode 100644
index 0000000..28a0a47
--- /dev/null
+++ b/plugins/gobject-templates/configure.ac
@@ -0,0 +1,12 @@
+# --enable-gobject-templates=yes/no
+AC_ARG_ENABLE([gobject-templates],
+              [AS_HELP_STRING([--enable-gobject-templates=@<:@yes/no@:>@],
+                              [Build with support for gobject templates.])],
+              [enable_gobject_templates=$enableval],
+              [enable_gobject_templates=yes])
+
+# for if ENABLE_GOBJECT_TEMPLATES in Makefile.am
+AM_CONDITIONAL(ENABLE_GOBJECT_TEMPLATES, test x$enable_gobject_templates != xno)
+
+# Ensure our makefile is generated by autoconf
+AC_CONFIG_FILES([plugins/gobject-templates/Makefile])
diff --git a/plugins/gobject-templates/gbp-gobject-dialog.c b/plugins/gobject-templates/gbp-gobject-dialog.c
new file mode 100644
index 0000000..91a85b5
--- /dev/null
+++ b/plugins/gobject-templates/gbp-gobject-dialog.c
@@ -0,0 +1,179 @@
+/* gbp-gobject-dialog.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/>.
+ */
+
+#define G_LOG_DOMAIN "gbp-gobject-dialog"
+
+#include <egg-file-chooser-entry.h>
+#include <egg-state-machine.h>
+#include <glib/gi18n.h>
+#include <ide.h>
+
+#include "gbp-gobject-dialog.h"
+#include "gbp-gobject-property.h"
+#include "gbp-gobject-property-editor.h"
+
+struct _GbpGobjectDialog
+{
+  GtkWindow            parent_instance;
+
+  GFile               *directory;
+
+  GtkEntry            *name_entry;
+  GtkEntry            *namespace_entry;
+  GtkEntry            *classname_entry;
+  GtkTreeView         *properties_treeview;
+  GtkTreeView         *signals_treeview;
+  EggStateMachine     *language_state;
+  EggFileChooserEntry *location_entry;
+  GtkButton           *button_ok;
+};
+
+G_DEFINE_TYPE (GbpGobjectDialog, gbp_gobject_dialog, GTK_TYPE_WINDOW)
+
+enum {
+  PROP_0,
+  PROP_DIRECTORY,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+GtkWidget *
+gbp_gobject_dialog_new (void)
+{
+  return g_object_new (GBP_TYPE_GOBJECT_DIALOG,
+                       "use-header-bar", TRUE,
+                       NULL);
+}
+
+/**
+ * gbp_gobject_dialog_get_directory:
+ *
+ * Returns: (nullable) (transfer none): A #GFile or %NULL.
+ */
+GFile *
+gbp_gobject_dialog_get_directory (GbpGobjectDialog *self)
+{
+  g_return_val_if_fail (GBP_IS_GOBJECT_DIALOG (self), NULL);
+
+  return self->directory;
+}
+
+void
+gbp_gobject_dialog_set_directory (GbpGobjectDialog *self,
+                                  GFile            *directory)
+{
+  g_return_if_fail (GBP_IS_GOBJECT_DIALOG (self));
+  g_return_if_fail (!directory || G_IS_FILE (directory));
+
+  if (g_set_object (&self->directory, directory))
+    {
+      egg_file_chooser_entry_set_file (self->location_entry, directory);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DIRECTORY]);
+    }
+}
+
+static void
+gbp_gobject_dialog_finalize (GObject *object)
+{
+  GbpGobjectDialog *self = (GbpGobjectDialog *)object;
+
+  g_clear_object (&self->directory);
+
+  G_OBJECT_CLASS (gbp_gobject_dialog_parent_class)->finalize (object);
+}
+
+static void
+gbp_gobject_dialog_get_property (GObject    *object,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  GbpGobjectDialog *self = GBP_GOBJECT_DIALOG (object);
+
+  switch (prop_id)
+    {
+    case PROP_DIRECTORY:
+      g_value_set_object (value, gbp_gobject_dialog_get_directory (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_gobject_dialog_set_property (GObject      *object,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+  GbpGobjectDialog *self = GBP_GOBJECT_DIALOG (object);
+
+  switch (prop_id)
+    {
+    case PROP_DIRECTORY:
+      gbp_gobject_dialog_set_directory (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_gobject_dialog_class_init (GbpGobjectDialogClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = gbp_gobject_dialog_finalize;
+  object_class->get_property = gbp_gobject_dialog_get_property;
+  object_class->set_property = gbp_gobject_dialog_set_property;
+
+  properties [PROP_DIRECTORY] =
+    g_param_spec_object ("directory",
+                         "Directory",
+                         "Directory",
+                         G_TYPE_FILE,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/builder/plugins/gobject-templates/gbp-gobject-dialog.ui");
+  gtk_widget_class_bind_template_child (widget_class, GbpGobjectDialog, language_state);
+  gtk_widget_class_bind_template_child (widget_class, GbpGobjectDialog, button_ok);
+
+  g_type_ensure (GBP_TYPE_GOBJECT_PROPERTY);
+  g_type_ensure (GBP_TYPE_GOBJECT_PROPERTY_EDITOR);
+}
+
+static void
+gbp_gobject_dialog_init (GbpGobjectDialog *self)
+{
+  g_autoptr(GSimpleActionGroup) group = NULL;
+  g_autoptr(GAction) language_state_action = NULL;
+
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  group = g_simple_action_group_new ();
+  language_state_action = egg_state_machine_create_action (self->language_state, "language");
+  g_action_map_add_action (G_ACTION_MAP (group), language_state_action);
+
+  gtk_widget_insert_action_group (GTK_WIDGET (self), "dialog", G_ACTION_GROUP (group));
+}
diff --git a/plugins/gobject-templates/gbp-gobject-dialog.h b/plugins/gobject-templates/gbp-gobject-dialog.h
new file mode 100644
index 0000000..ed67ecd
--- /dev/null
+++ b/plugins/gobject-templates/gbp-gobject-dialog.h
@@ -0,0 +1,37 @@
+/* gbp-gobject-dialog.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_GOBJECT_DIALOG_H
+#define GBP_GOBJECT_DIALOG_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_GOBJECT_DIALOG (gbp_gobject_dialog_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpGobjectDialog, gbp_gobject_dialog, GBP, GOBJECT_DIALOG, GtkWindow)
+
+GtkWidget *gbp_gobject_dialog_new           (void);
+GFile     *gbp_gobject_dialog_get_directory (GbpGobjectDialog *self);
+void       gbp_gobject_dialog_set_directory (GbpGobjectDialog *self,
+                                             GFile            *directory);
+
+G_END_DECLS
+
+#endif /* GBP_GOBJECT_DIALOG_H */
diff --git a/plugins/gobject-templates/gbp-gobject-dialog.ui b/plugins/gobject-templates/gbp-gobject-dialog.ui
new file mode 100644
index 0000000..6eb69d6
--- /dev/null
+++ b/plugins/gobject-templates/gbp-gobject-dialog.ui
@@ -0,0 +1,452 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="GbpGobjectDialog" parent="GtkWindow">
+    <property name="default-width">730</property>
+    <property name="default-height">450</property>
+    <child type="action">
+      <object class="GtkButton" id="button_cancel">
+        <property name="visible">true</property>
+        <property name="label" translatable="yes">Cancel</property>
+      </object>
+    </child>
+    <child type="action">
+      <object class="GtkButton" id="button_ok">
+        <property name="visible">true</property>
+        <property name="can-default">true</property>
+        <property name="label" translatable="yes">Create</property>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="cancel">button_cancel</action-widget>
+      <action-widget response="ok" default="true">button_ok</action-widget>
+    </action-widgets>
+    <child internal-child="vbox">
+      <object class="GtkBox">
+        <child>
+          <object class="GtkScrolledWindow">
+            <property name="hscrollbar-policy">never</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkBox">
+                <property name="border-width">24</property>
+                <property name="orientation">vertical</property>
+                <property name="visible">true</property>
+
+                <!-- Name -->
+                <child>
+                  <object class="GtkBox">
+                    <property name="orientation">horizontal</property>
+                    <property name="spacing">10</property>
+                    <property name="visible">true</property>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="label" translatable="yes">Name</property>
+                        <property name="halign">end</property>
+                        <property name="hexpand">true</property>
+                        <property name="visible">true</property>
+                      </object>
+                    </child>
+                    <child type="center">
+                      <object class="GtkEntry" id="name_entry">
+                        <property name="sensitive" bind-source="details_revealer" 
bind-property="reveal-child" bind-flags="bidirectional|invert-boolean"/>
+                        <property name="visible">true</property>
+                        <property name="width-chars">40</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkToggleButton">
+                        <property name="active" bind-source="details_revealer" bind-property="reveal-child" 
bind-flags="bidirectional">false</property>
+                        <property name="halign">start</property>
+                        <property name="hexpand">true</property>
+                        <property name="visible">true</property>
+                        <style>
+                          <class name="flat"/>
+                          <class name="image-button"/>
+                        </style>
+                        <child>
+                          <object class="GtkImage">
+                            <property name="icon-name">view-more-symbolic</property>
+                            <property name="visible">true</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="pack-type">end</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+
+                <!-- Description of name entry -->
+                <child>
+                  <object class="GtkBox">
+                    <property name="orientation">horizontal</property>
+                    <property name="spacing">10</property>
+                    <property name="visible">true</property>
+                    <property name="margin-top">6</property>
+                    <child type="center">
+                      <object class="GtkLabel" id="name_desc_label">
+                        <property name="label" translatable="yes">Unique name for the class including the 
namespace such as “MyObject” or “MyWidget”.</property>
+                        <property name="max-width-chars">40</property>
+                        <property name="visible">true</property>
+                        <property name="xalign">0.0</property>
+                        <property name="wrap">true</property>
+                        <style>
+                          <class name="dim-label"/>
+                        </style>
+                        <attributes>
+                          <attribute name="scale" value="0.833333"/>
+                        </attributes>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+
+                <!-- Revealer for manual namespace/class entry -->
+                <child>
+                  <object class="GtkRevealer" id="details_revealer">
+                    <property name="reveal-child">false</property>
+                    <property name="visible">true</property>
+                    <child>
+                      <object class="GtkBox">
+                        <property name="orientation">vertical</property>
+                        <property name="visible">true</property>
+
+                        <!-- Namespace -->
+                        <child>
+                          <object class="GtkBox">
+                            <property name="orientation">horizontal</property>
+                            <property name="spacing">10</property>
+                            <property name="visible">true</property>
+                            <property name="margin-top">24</property>
+                            <child>
+                              <object class="GtkLabel" id="namespace_label">
+                                <property name="label" translatable="yes">Namespace</property>
+                                <property name="halign">end</property>
+                                <property name="hexpand">true</property>
+                                <property name="visible">true</property>
+                              </object>
+                            </child>
+                            <child type="center">
+                              <object class="GtkEntry" id="namespace_entry">
+                                <property name="visible">true</property>
+                                <property name="width-chars">40</property>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+
+                        <!-- Class -->
+                        <child>
+                          <object class="GtkBox">
+                            <property name="orientation">horizontal</property>
+                            <property name="spacing">10</property>
+                            <property name="visible">true</property>
+                            <property name="margin-top">24</property>
+                            <child>
+                              <object class="GtkLabel">
+                                <property name="label" translatable="yes">Class</property>
+                                <property name="halign">end</property>
+                                <property name="hexpand">true</property>
+                                <property name="visible">true</property>
+                              </object>
+                            </child>
+                            <child type="center">
+                              <object class="GtkEntry" id="class_entry">
+                                <property name="visible">true</property>
+                                <property name="width-chars">40</property>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+
+                        <!-- Parent Type -->
+                        <child>
+                          <object class="GtkBox">
+                            <property name="orientation">horizontal</property>
+                            <property name="spacing">10</property>
+                            <property name="visible">true</property>
+                            <property name="margin-top">24</property>
+                            <property name="margin-bottom">32</property>
+                            <child>
+                              <object class="GtkLabel">
+                                <property name="label" translatable="yes">Inherits</property>
+                                <property name="halign">end</property>
+                                <property name="hexpand">true</property>
+                                <property name="visible">true</property>
+                              </object>
+                            </child>
+                            <child type="center">
+                              <object class="GtkEntry" id="parent_entry">
+                                <property name="text">GObject</property>
+                                <property name="visible">true</property>
+                                <property name="width-chars">40</property>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+
+                      </object>
+                    </child>
+                  </object>
+                </child>
+
+                <!-- Location -->
+                <child>
+                  <object class="GtkBox">
+                    <property name="orientation">horizontal</property>
+                    <property name="spacing">10</property>
+                    <property name="visible">true</property>
+                    <property name="margin-top">24</property>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="label" translatable="yes">Location</property>
+                        <property name="halign">end</property>
+                        <property name="hexpand">true</property>
+                        <property name="visible">true</property>
+                      </object>
+                    </child>
+                    <child type="center">
+                      <object class="EggFileChooserEntry" id="location_entry">
+                        <property name="action">create-folder</property>
+                        <property name="visible">true</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+
+                <!-- Language -->
+                <child>
+                  <object class="GtkBox">
+                    <property name="orientation">horizontal</property>
+                    <property name="spacing">10</property>
+                    <property name="visible">true</property>
+                    <property name="margin-top">24</property>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="label" translatable="yes">Language</property>
+                        <property name="halign">end</property>
+                        <property name="hexpand">true</property>
+                        <property name="visible">true</property>
+                      </object>
+                    </child>
+                    <child type="center">
+                      <object class="GtkBox" id="language_box">
+                        <property name="orientation">horizontal</property>
+                        <property name="homogeneous">true</property>
+                        <property name="visible">true</property>
+                        <style>
+                          <class name="linked"/>
+                        </style>
+                        <child>
+                          <object class="GtkRadioButton" id="lang_c">
+                            <property name="draw-indicator">false</property>
+                            <property name="group">lang_c</property>
+                            <property name="action-name">dialog.language</property>
+                            <property name="action-target">'c'</property>
+                            <property name="hexpand">true</property>
+                            <property name="label" translatable="yes">C</property>
+                            <property name="visible">true</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="lang_cpp">
+                            <property name="draw-indicator">false</property>
+                            <property name="group">lang_c</property>
+                            <property name="action-name">dialog.language</property>
+                            <property name="action-target">'cpp'</property>
+                            <property name="hexpand">true</property>
+                            <property name="label" translatable="yes">C++</property>
+                            <property name="visible">true</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="lang_python">
+                            <property name="draw-indicator">false</property>
+                            <property name="group">lang_c</property>
+                            <property name="action-name">dialog.language</property>
+                            <property name="action-target">'python'</property>
+                            <property name="hexpand">true</property>
+                            <property name="label" translatable="yes">Python</property>
+                            <property name="visible">true</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkRadioButton" id="lang_vala">
+                            <property name="draw-indicator">false</property>
+                            <property name="group">lang_c</property>
+                            <property name="action-name">dialog.language</property>
+                            <property name="action-target">'vala'</property>
+                            <property name="hexpand">true</property>
+                            <property name="label" translatable="yes">Vala</property>
+                            <property name="visible">true</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+
+                <!-- Properties -->
+                <child>
+                  <object class="GtkBox">
+                    <property name="orientation">horizontal</property>
+                    <property name="spacing">10</property>
+                    <property name="visible">true</property>
+                    <property name="margin-top">24</property>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="label" translatable="yes">Properties</property>
+                        <property name="halign">end</property>
+                        <property name="hexpand">true</property>
+                        <property name="visible">true</property>
+                      </object>
+                    </child>
+                    <child type="center">
+                      <object class="GtkBox" id="properties_switch_container">
+                        <property name="visible">true</property>
+                        <child>
+                          <object class="GtkSwitch" id="properties_switch">
+                            <property name="halign">start</property>
+                            <property name="valign">center</property>
+                            <property name="visible">true</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkMenuButton">
+                        <property name="direction">left</property>
+                        <property name="popover">properties_popover</property>
+                        <property name="sensitive" bind-source="properties_switch" bind-property="active" 
bind-flags="sync-create"/>
+                        <property name="halign">start</property>
+                        <property name="hexpand">true</property>
+                        <property name="visible">true</property>
+                        <child>
+                          <object class="GtkImage">
+                            <property name="icon-name">list-add-symbolic</property>
+                            <property name="visible">true</property>
+                          </object>
+                        </child>
+                        <style>
+                          <class name="flat"/>
+                          <class name="image-button"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="pack-type">end</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+
+                <!-- Signals -->
+                <child>
+                  <object class="GtkBox">
+                    <property name="orientation">horizontal</property>
+                    <property name="spacing">10</property>
+                    <property name="visible">true</property>
+                    <property name="margin-top">24</property>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="label" translatable="yes">Signals</property>
+                        <property name="halign">end</property>
+                        <property name="hexpand">true</property>
+                        <property name="visible">true</property>
+                      </object>
+                    </child>
+                    <child type="center">
+                      <object class="GtkBox" id="signals_switch_container">
+                        <property name="visible">true</property>
+                        <child>
+                          <object class="GtkSwitch" id="signals_switch">
+                            <property name="halign">start</property>
+                            <property name="valign">center</property>
+                            <property name="visible">true</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkButton">
+                        <property name="sensitive" bind-source="signals_switch" bind-property="active" 
bind-flags="sync-create"/>
+                        <property name="halign">start</property>
+                        <property name="hexpand">true</property>
+                        <property name="visible">true</property>
+                        <child>
+                          <object class="GtkImage">
+                            <property name="icon-name">list-add-symbolic</property>
+                            <property name="visible">true</property>
+                          </object>
+                        </child>
+                        <style>
+                          <class name="flat"/>
+                          <class name="image-button"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="pack-type">end</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+
+              </object>
+            </child>
+          </object>
+        </child>
+
+      </object>
+    </child>
+  </template>
+
+  <!-- Sizing to keep the center column all the same -->
+  <object class="GtkSizeGroup">
+    <property name="mode">horizontal</property>
+    <widgets>
+      <widget name="language_box"/>
+      <widget name="location_entry"/>
+      <widget name="namespace_entry"/>
+      <widget name="name_desc_label"/>
+      <widget name="parent_entry"/>
+      <widget name="class_entry"/>
+      <widget name="properties_switch_container"/>
+      <widget name="signals_switch_container"/>
+    </widgets>
+  </object>
+
+  <object class="EggStateMachine" id="language_state">
+    <property name="state">c</property>
+    <states>
+      <state name="c">
+        <object id="namespace_label">
+          <property name="label" translatable="yes">Namespace</property>
+        </object>
+      </state>
+      <state name="cpp">
+        <object id="namespace_label">
+          <property name="label" translatable="yes">Namespace</property>
+        </object>
+      </state>
+      <state name="python">
+        <object id="namespace_label">
+          <property name="label" translatable="yes">Package</property>
+        </object>
+      </state>
+      <state name="vala">
+        <object id="namespace_label">
+          <property name="label" translatable="yes">Namespace</property>
+        </object>
+      </state>
+    </states>
+  </object>
+
+  <!-- Popover for creating or editing a property -->
+  <object class="GtkPopover" id="properties_popover">
+    <child>
+      <object class="GbpGobjectPropertyEditor" id="property_editor">
+        <property name="visible">true</property>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/plugins/gobject-templates/gbp-gobject-property-editor.c 
b/plugins/gobject-templates/gbp-gobject-property-editor.c
new file mode 100644
index 0000000..7cbce22
--- /dev/null
+++ b/plugins/gobject-templates/gbp-gobject-property-editor.c
@@ -0,0 +1,127 @@
+/* gbp-gobject-property-editor.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/>.
+ */
+
+#define G_LOG_DOMAIN "gbp-gobject-property-editor"
+
+#include "gbp-gobject-property.h"
+#include "gbp-gobject-property-editor.h"
+
+struct _GbpGobjectPropertyEditor
+{
+  GtkBin              parent_instance;
+  GbpGobjectProperty *property;
+};
+
+G_DEFINE_TYPE (GbpGobjectPropertyEditor, gbp_gobject_property_editor, GTK_TYPE_BIN)
+
+enum {
+  PROP_0,
+  PROP_PROPERTY,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+gbp_gobject_property_editor_set_property (GbpGobjectPropertyEditor *self,
+                                          GbpGobjectProperty       *property)
+{
+  g_assert (GBP_IS_GOBJECT_PROPERTY_EDITOR (self));
+  g_assert (!property || GBP_IS_GOBJECT_PROPERTY (self));
+
+  if (g_set_object (&self->property, property))
+    {
+    }
+}
+
+static void
+gbp_gobject_property_editor_finalize (GObject *object)
+{
+  GbpGobjectPropertyEditor *self = (GbpGobjectPropertyEditor *)object;
+
+  g_clear_object (&self->property);
+
+  G_OBJECT_CLASS (gbp_gobject_property_editor_parent_class)->finalize (object);
+}
+
+static void
+gbp_gobject_property_editor_do_get_property (GObject    *object,
+                                             guint       prop_id,
+                                             GValue     *value,
+                                             GParamSpec *pspec)
+{
+  GbpGobjectPropertyEditor *self = GBP_GOBJECT_PROPERTY_EDITOR (object);
+
+  switch (prop_id)
+    {
+    case PROP_PROPERTY:
+      g_value_set_object (value, self->property);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_gobject_property_editor_do_set_property (GObject      *object,
+                                             guint         prop_id,
+                                             const GValue *value,
+                                             GParamSpec   *pspec)
+{
+  GbpGobjectPropertyEditor *self = GBP_GOBJECT_PROPERTY_EDITOR (object);
+
+  switch (prop_id)
+    {
+    case PROP_PROPERTY:
+      gbp_gobject_property_editor_set_property (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_gobject_property_editor_class_init (GbpGobjectPropertyEditorClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = gbp_gobject_property_editor_finalize;
+  object_class->get_property = gbp_gobject_property_editor_do_get_property;
+  object_class->set_property = gbp_gobject_property_editor_do_set_property;
+
+  properties [PROP_PROPERTY] =
+    g_param_spec_object ("property",
+                         "Property",
+                         "Property",
+                         GBP_TYPE_GOBJECT_PROPERTY,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+
+  gtk_widget_class_set_template_from_resource (widget_class,
+                                               
"/org/gnome/builder/plugins/gobject-templates/gbp-gobject-property-editor.ui");
+}
+
+static void
+gbp_gobject_property_editor_init (GbpGobjectPropertyEditor *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+}
diff --git a/plugins/gobject-templates/gbp-gobject-property-editor.h 
b/plugins/gobject-templates/gbp-gobject-property-editor.h
new file mode 100644
index 0000000..3c4b530
--- /dev/null
+++ b/plugins/gobject-templates/gbp-gobject-property-editor.h
@@ -0,0 +1,33 @@
+/* gbp-gobject-property-editor.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_GOBJECT_PROPERTY_EDITOR_H
+#define GBP_GOBJECT_PROPERTY_EDITOR_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_GOBJECT_PROPERTY_EDITOR (gbp_gobject_property_editor_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpGobjectPropertyEditor, gbp_gobject_property_editor, GBP, GOBJECT_PROPERTY_EDITOR, 
GtkBin)
+
+G_END_DECLS
+
+#endif /* GBP_GOBJECT_PROPERTY_EDITOR_H */
+
diff --git a/plugins/gobject-templates/gbp-gobject-property-editor.ui 
b/plugins/gobject-templates/gbp-gobject-property-editor.ui
new file mode 100644
index 0000000..a35175c
--- /dev/null
+++ b/plugins/gobject-templates/gbp-gobject-property-editor.ui
@@ -0,0 +1,271 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="GbpGobjectPropertyEditor" parent="GtkBin">
+    <property name="width-request">475</property>
+    <child>
+      <object class="GtkBox">
+        <property name="orientation">vertical</property>
+        <property name="border-width">24</property>
+        <property name="visible">true</property>
+
+        <!-- Name -->
+        <child>
+          <object class="GtkBox">
+            <property name="orientation">horizontal</property>
+            <property name="spacing">12</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="label" translatable="yes">Name</property>
+                <property name="halign">end</property>
+                <property name="hexpand">true</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+            <child type="center">
+              <object class="GtkEntry" id="name_entry">
+                <property name="width-chars">25</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+          </object>
+        </child>
+
+        <!-- Name Description Text -->
+        <child>
+          <object class="GtkBox">
+            <property name="orientation">horizontal</property>
+            <property name="margin-top">6</property>
+            <property name="visible">true</property>
+            <child type="center">
+              <object class="GtkLabel" id="name_help_label">
+                <property name="label" translatable="yes">Unique name that should only contain alpha 
characters or -</property>
+                <property name="wrap">true</property>
+                <property name="visible">true</property>
+                <property name="max-width-chars">25</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+                <attributes>
+                  <attribute name="scale" value="0.833333"/>
+                </attributes>
+              </object>
+            </child>
+          </object>
+        </child>
+
+        <!-- Type -->
+        <child>
+          <object class="GtkBox">
+            <property name="orientation">horizontal</property>
+            <property name="spacing">12</property>
+            <property name="margin-top">18</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="label" translatable="yes">Kind</property>
+                <property name="halign">end</property>
+                <property name="hexpand">true</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+            <child type="center">
+              <object class="GtkComboBoxText" id="kind_combobox">
+                <property name="active-id">string</property>
+                <property name="visible">true</property>
+                <items>
+                  <item translatable="yes" id="boolean">Boolean</item>
+                  <item translatable="yes" id="boxed">Boxed</item>
+                  <item translatable="yes" id="char">Character</item>
+                  <item translatable="yes" id="double">Double</item>
+                  <item translatable="yes" id="enum">Enum</item>
+                  <item translatable="yes" id="flags">Flags</item>
+                  <item translatable="yes" id="float">Float</item>
+                  <item translatable="yes" id="int">Signed Integer</item>
+                  <item translatable="yes" id="int64">Signed 64-bit Integer</item>
+                  <item translatable="yes" id="long">Signed Long</item>
+                  <item translatable="yes" id="object">Object</item>
+                  <item translatable="yes" id="pointer">Pointer</item>
+                  <item translatable="yes" id="string">String</item>
+                  <item translatable="yes" id="uint">Unsinged Integer</item>
+                  <item translatable="yes" id="uint64">Unsigned 64-bit Integer</item>
+                  <item translatable="yes" id="ulong">Unsigned Long</item>
+                  <item translatable="yes" id="unichar">Unicode Character</item>
+                  <item translatable="yes" id="variant">Variant</item>
+                </items>
+              </object>
+            </child>
+          </object>
+        </child>
+
+        <!-- Default -->
+        <child>
+          <object class="GtkBox">
+            <property name="orientation">horizontal</property>
+            <property name="spacing">12</property>
+            <property name="margin-top">18</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="label" translatable="yes">Default</property>
+                <property name="halign">end</property>
+                <property name="hexpand">true</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+            <child type="center">
+              <object class="GtkEntry" id="default_entry">
+                <property name="visible">true</property>
+              </object>
+            </child>
+          </object>
+        </child>
+
+        <!-- Minimum -->
+        <child>
+          <object class="GtkBox">
+            <property name="orientation">horizontal</property>
+            <property name="spacing">12</property>
+            <property name="margin-top">6</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="label" translatable="yes">Minimum</property>
+                <property name="halign">end</property>
+                <property name="hexpand">true</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+            <child type="center">
+              <object class="GtkEntry" id="min_entry">
+                <property name="visible">true</property>
+              </object>
+            </child>
+          </object>
+        </child>
+
+        <!-- Maximum -->
+        <child>
+          <object class="GtkBox">
+            <property name="orientation">horizontal</property>
+            <property name="spacing">12</property>
+            <property name="margin-top">6</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="label" translatable="yes">Maximum</property>
+                <property name="halign">end</property>
+                <property name="hexpand">true</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+            <child type="center">
+              <object class="GtkEntry" id="max_entry">
+                <property name="visible">true</property>
+              </object>
+            </child>
+          </object>
+        </child>
+
+        <!-- Read -->
+        <child>
+          <object class="GtkBox">
+            <property name="orientation">horizontal</property>
+            <property name="spacing">12</property>
+            <property name="visible">true</property>
+            <property name="margin-top">18</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="label" translatable="yes">Readable</property>
+                <property name="halign">end</property>
+                <property name="hexpand">true</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+            <child type="center">
+              <object class="GtkBox" id="readable_container">
+                <property name="visible">true</property>
+                <child>
+                  <object class="GtkSwitch" id="readable_switch">
+                    <property name="active">true</property>
+                    <property name="halign">start</property>
+                    <property name="valign">center</property>
+                    <property name="visible">true</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+
+        <!-- Read -->
+        <child>
+          <object class="GtkBox">
+            <property name="orientation">horizontal</property>
+            <property name="spacing">12</property>
+            <property name="visible">true</property>
+            <property name="margin-top">6</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="label" translatable="yes">Writable</property>
+                <property name="halign">end</property>
+                <property name="hexpand">true</property>
+                <property name="visible">true</property>
+              </object>
+            </child>
+            <child type="center">
+              <object class="GtkBox" id="writable_container">
+                <property name="visible">true</property>
+                <child>
+                  <object class="GtkSwitch" id="writable_switch">
+                    <property name="active">true</property>
+                    <property name="halign">start</property>
+                    <property name="valign">center</property>
+                    <property name="visible">true</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+
+        <!-- Create button -->
+        <child>
+          <object class="GtkBox">
+            <property name="margin-top">24</property>
+            <property name="orientation">horizontal</property>
+            <property name="visible">true</property>
+            <child>
+              <object class="GtkButton">
+                <property name="width-request">75</property>
+                <property name="label" translatable="yes">Add</property>
+                <property name="visible">true</property>
+                <style>
+                  <class name="suggested-action"/>
+                </style>
+              </object>
+              <packing>
+                <property name="pack-type">end</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+
+      </object>
+    </child>
+  </template>
+
+  <object class="GtkSizeGroup">
+    <property name="mode">horizontal</property>
+    <widgets>
+      <widget name="name_entry"/>
+      <widget name="name_help_label"/>
+      <widget name="kind_combobox"/>
+      <widget name="max_entry"/>
+      <widget name="min_entry"/>
+      <widget name="default_entry"/>
+      <widget name="readable_container"/>
+      <widget name="writable_container"/>
+    </widgets>
+  </object>
+</interface>
diff --git a/plugins/gobject-templates/gbp-gobject-property.c 
b/plugins/gobject-templates/gbp-gobject-property.c
new file mode 100644
index 0000000..7aa746a
--- /dev/null
+++ b/plugins/gobject-templates/gbp-gobject-property.c
@@ -0,0 +1,380 @@
+/* gbp-gobject-property.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/>.
+ */
+
+#define G_LOG_DOMAIN "gbp-gobject-property"
+
+#include "gbp-gobject-property.h"
+
+struct _GbpGobjectProperty
+{
+  GObject parent_instance;
+
+  GbpGobjectPropertyKind kind;
+
+  gchar *name;
+  gchar *default_;
+  gchar *minimum;
+  gchar *maximum;
+
+  guint readable : 1;
+  guint writable : 1;
+};
+
+enum {
+  PROP_0,
+  PROP_DEFAULT,
+  PROP_KIND,
+  PROP_MAXIMUM,
+  PROP_MINIMUM,
+  PROP_NAME,
+  PROP_READABLE,
+  PROP_WRITABLE,
+  N_PROPS
+};
+
+G_DEFINE_TYPE (GbpGobjectProperty, gbp_gobject_property, G_TYPE_OBJECT)
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+gbp_gobject_property_finalize (GObject *object)
+{
+  GbpGobjectProperty *self = (GbpGobjectProperty *)object;
+
+  g_clear_pointer (&self->name, g_free);
+  g_clear_pointer (&self->default_, g_free);
+  g_clear_pointer (&self->minimum, g_free);
+  g_clear_pointer (&self->maximum, g_free);
+
+  G_OBJECT_CLASS (gbp_gobject_property_parent_class)->finalize (object);
+}
+
+static void
+gbp_gobject_property_get_property (GObject    *object,
+                                   guint       prop_id,
+                                   GValue     *value,
+                                   GParamSpec *pspec)
+{
+  GbpGobjectProperty *self = GBP_GOBJECT_PROPERTY (object);
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      g_value_set_string (value, self->name);
+      break;
+
+    case PROP_DEFAULT:
+      g_value_set_string (value, self->default_);
+      break;
+
+    case PROP_MINIMUM:
+      g_value_set_string (value, self->minimum);
+      break;
+
+    case PROP_MAXIMUM:
+      g_value_set_string (value, self->maximum);
+      break;
+
+    case PROP_READABLE:
+      g_value_set_boolean (value, self->readable);
+      break;
+
+    case PROP_WRITABLE:
+      g_value_set_boolean (value, self->writable);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_gobject_property_set_property (GObject      *object,
+                                   guint         prop_id,
+                                   const GValue *value,
+                                   GParamSpec   *pspec)
+{
+  GbpGobjectProperty *self = GBP_GOBJECT_PROPERTY (object);
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      gbp_gobject_property_set_name (self, g_value_get_string (value));
+      break;
+
+    case PROP_DEFAULT:
+      gbp_gobject_property_set_default (self, g_value_get_string (value));
+      break;
+
+    case PROP_MINIMUM:
+      gbp_gobject_property_set_minimum (self, g_value_get_string (value));
+      break;
+
+    case PROP_MAXIMUM:
+      gbp_gobject_property_set_maximum (self, g_value_get_string (value));
+      break;
+
+    case PROP_READABLE:
+      gbp_gobject_property_set_readable (self, g_value_get_boolean (value));
+      break;
+
+    case PROP_WRITABLE:
+      gbp_gobject_property_set_writable (self, g_value_get_boolean (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_gobject_property_class_init (GbpGobjectPropertyClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gbp_gobject_property_finalize;
+  object_class->get_property = gbp_gobject_property_get_property;
+  object_class->set_property = gbp_gobject_property_set_property;
+
+  properties [PROP_KIND] =
+    g_param_spec_enum ("kind",
+                       NULL,
+                       NULL,
+                       GBP_TYPE_GOBJECT_PROPERTY_KIND,
+                       GBP_GOBJECT_PROPERTY_STRING,
+                       (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_NAME] =
+    g_param_spec_string ("name",
+                         NULL,
+                         NULL,
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_DEFAULT] =
+    g_param_spec_string ("default",
+                         NULL,
+                         NULL,
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_MINIMUM] =
+    g_param_spec_string ("minimum",
+                         NULL,
+                         NULL,
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_MAXIMUM] =
+    g_param_spec_string ("maximum",
+                         NULL,
+                         NULL,
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_READABLE] =
+    g_param_spec_boolean ("readable",
+                          NULL,
+                          NULL,
+                          TRUE,
+                          (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_WRITABLE] =
+    g_param_spec_boolean ("writable",
+                          NULL,
+                          NULL,
+                          TRUE,
+                          (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+gbp_gobject_property_init (GbpGobjectProperty *self)
+{
+  self->readable = TRUE;
+  self->writable = TRUE;
+}
+
+GbpGobjectProperty *
+gbp_gobject_property_new (void)
+{
+  return g_object_new (GBP_TYPE_GOBJECT_PROPERTY, NULL);
+}
+
+GbpGobjectPropertyKind
+gbp_gobject_property_get_kind (GbpGobjectProperty     *self)
+{
+  return self->kind;
+}
+
+void
+gbp_gobject_property_set_kind (GbpGobjectProperty     *self,
+                               GbpGobjectPropertyKind  kind)
+{
+  if (kind != self->kind)
+    {
+      self->kind = kind;
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_KIND]);
+    }
+}
+
+const gchar *
+gbp_gobject_property_get_name (GbpGobjectProperty *self)
+{
+  return self->name;
+}
+
+void
+gbp_gobject_property_set_name (GbpGobjectProperty *self,
+                               const gchar        *name)
+{
+  if (g_strcmp0 (name, self->name) != 0)
+    {
+      g_free (self->name);
+      self->name = g_strdup (name);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_NAME]);
+    }
+}
+
+const gchar *
+gbp_gobject_property_get_default (GbpGobjectProperty *self)
+{
+  return self->default_;
+}
+
+void
+gbp_gobject_property_set_default (GbpGobjectProperty *self,
+                                  const gchar        *default_)
+{
+  if (g_strcmp0 (default_, self->default_) != 0)
+    {
+      g_free (self->default_);
+      self->default_ = g_strdup (default_);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DEFAULT]);
+    }
+}
+
+const gchar *
+gbp_gobject_property_get_minimum (GbpGobjectProperty *self)
+{
+  return self->minimum;
+}
+
+void
+gbp_gobject_property_set_minimum (GbpGobjectProperty *self,
+                                  const gchar        *minimum)
+{
+  if (g_strcmp0 (minimum, self->minimum) != 0)
+    {
+      g_free (self->minimum);
+      self->minimum = g_strdup (minimum);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MINIMUM]);
+    }
+}
+
+const gchar *
+gbp_gobject_property_get_maximum (GbpGobjectProperty *self)
+{
+  return self->maximum;
+}
+
+void
+gbp_gobject_property_set_maximum (GbpGobjectProperty *self,
+                                  const gchar        *maximum)
+{
+  if (g_strcmp0 (maximum, self->maximum) != 0)
+    {
+      g_free (self->maximum);
+      self->maximum = g_strdup (maximum);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MAXIMUM]);
+    }
+}
+
+gboolean
+gbp_gobject_property_get_readable (GbpGobjectProperty *self)
+{
+  return self->readable;
+}
+
+void
+gbp_gobject_property_set_readable (GbpGobjectProperty *self,
+                                   gboolean            readable)
+{
+  readable = !!readable;
+
+  if (self->readable != readable)
+    {
+      self->readable = readable;
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_READABLE]);
+    }
+}
+
+gboolean
+gbp_gobject_property_get_writable (GbpGobjectProperty *self)
+{
+  return self->writable;
+}
+
+void
+gbp_gobject_property_set_writable (GbpGobjectProperty *self,
+                                   gboolean            writable)
+{
+  writable = !!writable;
+
+  if (self->writable != writable)
+    {
+      self->writable = writable;
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_WRITABLE]);
+    }
+}
+
+GType
+gbp_gobject_property_kind_get_type (void)
+{
+  static GType type_id = 0;
+
+  if (g_once_init_enter (&type_id))
+    {
+      static const GEnumValue values[] = {
+        { GBP_GOBJECT_PROPERTY_BOOLEAN, "GBP_GOBJECT_PROPERTY_BOOLEAN", "boolean" },
+        { GBP_GOBJECT_PROPERTY_BOXED, "GBP_GOBJECT_PROPERTY_BOXED", "boxed" },
+        { GBP_GOBJECT_PROPERTY_CHAR, "GBP_GOBJECT_PROPERTY_CHAR", "char" },
+        { GBP_GOBJECT_PROPERTY_DOUBLE, "GBP_GOBJECT_PROPERTY_DOUBLE", "double" },
+        { GBP_GOBJECT_PROPERTY_ENUM, "GBP_GOBJECT_PROPERTY_ENUM", "enum" },
+        { GBP_GOBJECT_PROPERTY_FLAGS, "GBP_GOBJECT_PROPERTY_FLAGS", "flags" },
+        { GBP_GOBJECT_PROPERTY_FLOAT, "GBP_GOBJECT_PROPERTY_FLOAT", "float" },
+        { GBP_GOBJECT_PROPERTY_INT, "GBP_GOBJECT_PROPERTY_INT", "int" },
+        { GBP_GOBJECT_PROPERTY_INT64, "GBP_GOBJECT_PROPERTY_INT64", "int64" },
+        { GBP_GOBJECT_PROPERTY_LONG, "GBP_GOBJECT_PROPERTY_LONG", "long" },
+        { GBP_GOBJECT_PROPERTY_OBJECT, "GBP_GOBJECT_PROPERTY_OBJECT", "object" },
+        { GBP_GOBJECT_PROPERTY_POINTER, "GBP_GOBJECT_PROPERTY_POINTER", "pointer" },
+        { GBP_GOBJECT_PROPERTY_STRING, "GBP_GOBJECT_PROPERTY_STRING", "string" },
+        { GBP_GOBJECT_PROPERTY_UINT, "GBP_GOBJECT_PROPERTY_UINT", "uint" },
+        { GBP_GOBJECT_PROPERTY_UINT64, "GBP_GOBJECT_PROPERTY_UINT64", "uint64" },
+        { GBP_GOBJECT_PROPERTY_ULONG, "GBP_GOBJECT_PROPERTY_ULONG", "ulong" },
+        { GBP_GOBJECT_PROPERTY_UNICHAR, "GBP_GOBJECT_PROPERTY_UNICHAR", "unichar" },
+        { GBP_GOBJECT_PROPERTY_VARIANT, "GBP_GOBJECT_PROPERTY_VARIANT", "variant" },
+        { 0 }
+      };
+      GType _type_id = g_enum_register_static ("GbpGobjectPropertyKind", values);
+      g_once_init_leave (&type_id, _type_id);
+    }
+
+  return type_id;
+}
diff --git a/plugins/gobject-templates/gbp-gobject-property.h 
b/plugins/gobject-templates/gbp-gobject-property.h
new file mode 100644
index 0000000..79cc8d9
--- /dev/null
+++ b/plugins/gobject-templates/gbp-gobject-property.h
@@ -0,0 +1,79 @@
+/* gbp-gobject-property.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_GOBJECT_PROPERTY_H
+#define GBP_GOBJECT_PROPERTY_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+  GBP_GOBJECT_PROPERTY_BOOLEAN,
+  GBP_GOBJECT_PROPERTY_BOXED,
+  GBP_GOBJECT_PROPERTY_CHAR,
+  GBP_GOBJECT_PROPERTY_DOUBLE,
+  GBP_GOBJECT_PROPERTY_ENUM,
+  GBP_GOBJECT_PROPERTY_FLAGS,
+  GBP_GOBJECT_PROPERTY_FLOAT,
+  GBP_GOBJECT_PROPERTY_INT,
+  GBP_GOBJECT_PROPERTY_INT64,
+  GBP_GOBJECT_PROPERTY_LONG,
+  GBP_GOBJECT_PROPERTY_OBJECT,
+  GBP_GOBJECT_PROPERTY_POINTER,
+  GBP_GOBJECT_PROPERTY_STRING,
+  GBP_GOBJECT_PROPERTY_UINT,
+  GBP_GOBJECT_PROPERTY_UINT64,
+  GBP_GOBJECT_PROPERTY_ULONG,
+  GBP_GOBJECT_PROPERTY_UNICHAR,
+  GBP_GOBJECT_PROPERTY_VARIANT,
+} GbpGobjectPropertyKind;
+
+#define GBP_TYPE_GOBJECT_PROPERTY (gbp_gobject_property_get_type())
+#define GBP_TYPE_GOBJECT_PROPERTY_KIND (gbp_gobject_property_kind_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpGobjectProperty, gbp_gobject_property, GBP, GOBJECT_PROPERTY, GObject)
+
+GType                   gbp_gobject_property_kind_get_type (void);
+GbpGobjectProperty     *gbp_gobject_property_new           (void);
+GbpGobjectPropertyKind  gbp_gobject_property_get_kind      (GbpGobjectProperty     *self);
+void                    gbp_gobject_property_set_kind      (GbpGobjectProperty     *self,
+                                                            GbpGobjectPropertyKind  kind);
+const gchar            *gbp_gobject_property_get_name      (GbpGobjectProperty     *self);
+void                    gbp_gobject_property_set_name      (GbpGobjectProperty     *self,
+                                                            const gchar            *name);
+const gchar            *gbp_gobject_property_get_default   (GbpGobjectProperty     *self);
+void                    gbp_gobject_property_set_default   (GbpGobjectProperty     *self,
+                                                            const gchar            *default_);
+const gchar            *gbp_gobject_property_get_minimum   (GbpGobjectProperty     *self);
+void                    gbp_gobject_property_set_minimum   (GbpGobjectProperty     *self,
+                                                            const gchar            *minimum);
+const gchar            *gbp_gobject_property_get_maximum   (GbpGobjectProperty     *self);
+void                    gbp_gobject_property_set_maximum   (GbpGobjectProperty     *self,
+                                                            const gchar            *maximum);
+gboolean                gbp_gobject_property_get_readable  (GbpGobjectProperty     *self);
+void                    gbp_gobject_property_set_readable  (GbpGobjectProperty     *self,
+                                                            gboolean                readable);
+gboolean                gbp_gobject_property_get_writable  (GbpGobjectProperty     *self);
+void                    gbp_gobject_property_set_writable  (GbpGobjectProperty     *self,
+                                                            gboolean                writable);
+
+G_END_DECLS
+
+#endif /* GBP_GOBJECT_PROPERTY_H */
diff --git a/plugins/gobject-templates/gbp-gobject-signal.c b/plugins/gobject-templates/gbp-gobject-signal.c
new file mode 100644
index 0000000..a5eb99c
--- /dev/null
+++ b/plugins/gobject-templates/gbp-gobject-signal.c
@@ -0,0 +1,111 @@
+/* gbp-gobject-signal.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/>.
+ */
+
+#define G_LOG_DOMAIN "gbp-gobject-signal"
+
+#include "gbp-gobject-signal.h"
+
+struct _GbpGobjectSignal
+{
+  GObject parent_instance;
+
+  gchar *name;
+};
+
+G_DEFINE_TYPE (GbpGobjectSignal, gbp_gobject_signal, G_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_NAME,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+gbp_gobject_signal_finalize (GObject *object)
+{
+  GbpGobjectSignal *self = (GbpGobjectSignal *)object;
+
+  g_clear_pointer (&self->name, g_free);
+
+  G_OBJECT_CLASS (gbp_gobject_signal_parent_class)->finalize (object);
+}
+
+static void
+gbp_gobject_signal_get_property (GObject    *object,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  GbpGobjectSignal *self = GBP_GOBJECT_SIGNAL (object);
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      g_value_set_string (value, self->name);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_gobject_signal_set_property (GObject      *object,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+  GbpGobjectSignal *self = GBP_GOBJECT_SIGNAL (object);
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      g_free (self->name);
+      self->name = g_value_dup_string (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_gobject_signal_class_init (GbpGobjectSignalClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gbp_gobject_signal_finalize;
+  object_class->get_property = gbp_gobject_signal_get_property;
+  object_class->set_property = gbp_gobject_signal_set_property;
+
+  properties [PROP_NAME] =
+    g_param_spec_string ("name",
+                         "Name",
+                         "Name",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+gbp_gobject_signal_init (GbpGobjectSignal *self)
+{
+}
diff --git a/plugins/gobject-templates/gbp-gobject-signal.h b/plugins/gobject-templates/gbp-gobject-signal.h
new file mode 100644
index 0000000..1217d45
--- /dev/null
+++ b/plugins/gobject-templates/gbp-gobject-signal.h
@@ -0,0 +1,33 @@
+/* gbp-gobject-signal.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_GOBJECT_SIGNAL_H
+#define GBP_GOBJECT_SIGNAL_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_GOBJECT_SIGNAL (gbp_gobject_signal_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpGobjectSignal, gbp_gobject_signal, GBP, GOBJECT_SIGNAL, GObject)
+
+G_END_DECLS
+
+#endif /* GBP_GOBJECT_SIGNAL_H */
+
diff --git a/plugins/gobject-templates/gbp-gobject-spec.c b/plugins/gobject-templates/gbp-gobject-spec.c
new file mode 100644
index 0000000..d0240b1
--- /dev/null
+++ b/plugins/gobject-templates/gbp-gobject-spec.c
@@ -0,0 +1,229 @@
+/* gbp-gobject-spec.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/>.
+ */
+
+#define G_LOG_DOMAIN "gbp-gobject-spec"
+
+#include "gbp-gobject-spec.h"
+
+struct _GbpGobjectSpec
+{
+  GObject     parent_instance;
+
+  gchar      *name;
+  gchar      *parent_name;
+
+  GListStore *properties;
+  GListStore *signals;
+};
+
+G_DEFINE_TYPE (GbpGobjectSpec, gbp_gobject_spec, G_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_NAME,
+  PROP_PARENT_NAME,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+gbp_gobject_spec_finalize (GObject *object)
+{
+  GbpGobjectSpec *self = (GbpGobjectSpec *)object;
+
+  g_clear_object (&self->properties);
+  g_clear_object (&self->signals);
+  g_clear_pointer (&self->name, g_free);
+  g_clear_pointer (&self->parent_name, g_free);
+
+  G_OBJECT_CLASS (gbp_gobject_spec_parent_class)->finalize (object);
+}
+
+static void
+gbp_gobject_spec_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+  GbpGobjectSpec *self = GBP_GOBJECT_SPEC (object);
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      g_value_set_string (value, self->name);
+      break;
+
+    case PROP_PARENT_NAME:
+      g_value_set_string (value, self->parent_name);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_gobject_spec_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  GbpGobjectSpec *self = GBP_GOBJECT_SPEC (object);
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      g_free (self->name);
+      self->name = g_value_dup_string (value);
+      break;
+
+    case PROP_PARENT_NAME:
+      g_free (self->parent_name);
+      self->parent_name = g_value_dup_string (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_gobject_spec_class_init (GbpGobjectSpecClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gbp_gobject_spec_finalize;
+  object_class->get_property = gbp_gobject_spec_get_property;
+  object_class->set_property = gbp_gobject_spec_set_property;
+
+  properties [PROP_NAME] =
+    g_param_spec_string ("name",
+                         "Name",
+                         "Name",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_PARENT_NAME] =
+    g_param_spec_string ("parent-name",
+                         "Parent Name",
+                         "Parent Name",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+gbp_gobject_spec_init (GbpGobjectSpec *self)
+{
+  self->properties = g_list_store_new (GBP_TYPE_GOBJECT_PROPERTY);
+  self->signals = g_list_store_new (GBP_TYPE_GOBJECT_SIGNAL);
+}
+
+GListModel *
+gbp_gobject_spec_get_properties (GbpGobjectSpec *self)
+{
+  g_return_val_if_fail (GBP_IS_GOBJECT_SPEC (self), NULL);
+
+  return G_LIST_MODEL (self->properties);
+}
+
+void
+gbp_gobject_spec_add_property (GbpGobjectSpec     *self,
+                               GbpGobjectProperty *property)
+{
+  g_return_if_fail (GBP_IS_GOBJECT_SPEC (self));
+  g_return_if_fail (GBP_IS_GOBJECT_PROPERTY (property));
+
+  g_list_store_append (self->properties, property);
+}
+
+void
+gbp_gobject_spec_remove_property (GbpGobjectSpec     *self,
+                                  GbpGobjectProperty *property)
+{
+  GListModel *model;
+  guint n_items;
+  guint i;
+
+  g_return_if_fail (GBP_IS_GOBJECT_SPEC (self));
+  g_return_if_fail (GBP_IS_GOBJECT_PROPERTY (property));
+
+  model = G_LIST_MODEL (self->properties);
+  n_items = g_list_model_get_n_items (model);
+
+  for (i = 0; i < n_items; i++)
+    {
+      g_autoptr(GbpGobjectProperty) item = NULL;
+
+      item = g_list_model_get_item (model, i);
+
+      if (item == property)
+        {
+          g_list_store_remove (self->properties, i);
+          return;
+        }
+    }
+}
+
+GListModel *
+gbp_gobject_spec_get_signals (GbpGobjectSpec *self)
+{
+  g_return_val_if_fail (GBP_IS_GOBJECT_SPEC (self), NULL);
+
+  return G_LIST_MODEL (self->signals);
+}
+
+void
+gbp_gobject_spec_add_signal (GbpGobjectSpec   *self,
+                             GbpGobjectSignal *signal)
+{
+  g_return_if_fail (GBP_IS_GOBJECT_SPEC (self));
+  g_return_if_fail (GBP_IS_GOBJECT_SIGNAL (signal));
+
+  g_list_store_append (self->signals, signal);
+}
+
+void
+gbp_gobject_spec_remove_signal (GbpGobjectSpec   *self,
+                                GbpGobjectSignal *signal)
+{
+  GListModel *model;
+  guint n_items;
+  guint i;
+
+  g_return_if_fail (GBP_IS_GOBJECT_SPEC (self));
+  g_return_if_fail (GBP_IS_GOBJECT_SIGNAL (signal));
+
+  model = G_LIST_MODEL (self->signals);
+  n_items = g_list_model_get_n_items (model);
+
+  for (i = 0; i < n_items; i++)
+    {
+      g_autoptr(GbpGobjectSignal) item = NULL;
+
+      item = g_list_model_get_item (model, i);
+
+      if (item == signal)
+        {
+          g_list_store_remove (self->signals, i);
+          return;
+        }
+    }
+}
diff --git a/plugins/gobject-templates/gbp-gobject-spec.h b/plugins/gobject-templates/gbp-gobject-spec.h
new file mode 100644
index 0000000..1229ba9
--- /dev/null
+++ b/plugins/gobject-templates/gbp-gobject-spec.h
@@ -0,0 +1,46 @@
+/* gbp-gobject-spec.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_GOBJECT_SPEC_H
+#define GBP_GOBJECT_SPEC_H
+
+#include <gio/gio.h>
+
+#include "gbp-gobject-property.h"
+#include "gbp-gobject-signal.h"
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_GOBJECT_SPEC (gbp_gobject_spec_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpGobjectSpec, gbp_gobject_spec, GBP, GOBJECT_SPEC, GObject)
+
+GListModel *gbp_gobject_spec_get_properties  (GbpGobjectSpec     *self);
+GListModel *gbp_gobject_spec_get_signals     (GbpGobjectSpec     *self);
+void        gbp_gobject_spec_add_property    (GbpGobjectSpec     *self,
+                                              GbpGobjectProperty *property);
+void        gbp_gobject_spec_remove_property (GbpGobjectSpec     *self,
+                                              GbpGobjectProperty *property);
+void        gbp_gobject_spec_add_signal      (GbpGobjectSpec     *self,
+                                              GbpGobjectSignal   *signal);
+void        gbp_gobject_spec_remove_signal   (GbpGobjectSpec     *self,
+                                              GbpGobjectSignal   *signal);
+
+G_END_DECLS
+
+#endif /* GBP_GOBJECT_SPEC_H */
diff --git a/plugins/gobject-templates/gbp-gobject-workbench-addin.c 
b/plugins/gobject-templates/gbp-gobject-workbench-addin.c
new file mode 100644
index 0000000..356b7da
--- /dev/null
+++ b/plugins/gobject-templates/gbp-gobject-workbench-addin.c
@@ -0,0 +1,132 @@
+/* gbp-gobject-workbench-addin.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/>.
+ */
+
+#define G_LOG_DOMAIN "gbp-gobject-workbench-addin"
+
+#include <glib/gi18n.h>
+
+#include "gbp-gobject-dialog.h"
+#include "gbp-gobject-workbench-addin.h"
+
+struct _GbpGobjectWorkbenchAddin
+{
+  GObject parent_instance;
+
+  IdeWorkbench *workbench;
+};
+
+static void workbench_addin_iface_init (IdeWorkbenchAddinInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (GbpGobjectWorkbenchAddin, gbp_gobject_workbench_addin, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (IDE_TYPE_WORKBENCH_ADDIN,
+                                               workbench_addin_iface_init))
+
+static void
+gbp_gobject_workbench_addin_class_init (GbpGobjectWorkbenchAddinClass *klass)
+{
+}
+
+static void
+gbp_gobject_workbench_addin_init (GbpGobjectWorkbenchAddin *self)
+{
+}
+
+static void
+dialog_response_cb (GbpGobjectWorkbenchAddin *self,
+                    gint                      response_id,
+                    GbpGobjectDialog         *dialog)
+{
+  g_assert (GBP_IS_GOBJECT_WORKBENCH_ADDIN (self));
+  g_assert (GBP_IS_GOBJECT_DIALOG (dialog));
+
+  gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static void
+new_gobject_activate (GSimpleAction *action,
+                      GVariant      *param,
+                      gpointer       user_data)
+{
+  GbpGobjectWorkbenchAddin *self = user_data;
+  GbpGobjectDialog *dialog;
+
+  g_assert (G_IS_SIMPLE_ACTION (action));
+  g_assert (param == NULL);
+  g_assert (GBP_IS_GOBJECT_WORKBENCH_ADDIN (self));
+
+  dialog = g_object_new (GBP_TYPE_GOBJECT_DIALOG,
+                         "modal", TRUE,
+                         "transient-for", self->workbench,
+                         "title", _("New Class"),
+                         "use-header-bar", TRUE,
+                         NULL);
+
+  g_signal_connect_object (dialog,
+                           "response",
+                           G_CALLBACK (dialog_response_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  gtk_window_present (GTK_WINDOW (dialog));
+}
+
+static void
+gbp_gobject_workbench_addin_load (IdeWorkbenchAddin *addin,
+                                  IdeWorkbench      *workbench)
+{
+  GbpGobjectWorkbenchAddin *self = (GbpGobjectWorkbenchAddin *)addin;
+  g_autoptr(GSimpleActionGroup) group = NULL;
+  static const GActionEntry entries[] = {
+    { "new-gobject", new_gobject_activate },
+  };
+
+  g_assert (IDE_IS_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_WORKBENCH (workbench));
+
+  self->workbench = workbench;
+
+  group = g_simple_action_group_new ();
+  g_action_map_add_action_entries (G_ACTION_MAP (group),
+                                   entries,
+                                   G_N_ELEMENTS (entries),
+                                   self);
+  gtk_widget_insert_action_group (GTK_WIDGET (workbench),
+                                  "gobject-templates",
+                                  G_ACTION_GROUP (group));
+}
+
+static void
+gbp_gobject_workbench_addin_unload (IdeWorkbenchAddin *addin,
+                                    IdeWorkbench      *workbench)
+{
+  GbpGobjectWorkbenchAddin *self = (GbpGobjectWorkbenchAddin *)addin;
+
+  g_assert (IDE_IS_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_WORKBENCH (workbench));
+
+  self->workbench = NULL;
+
+  gtk_widget_insert_action_group (GTK_WIDGET (workbench), "gobject-templates", NULL);
+}
+
+static void
+workbench_addin_iface_init (IdeWorkbenchAddinInterface *iface)
+{
+  iface->load = gbp_gobject_workbench_addin_load;
+  iface->unload = gbp_gobject_workbench_addin_unload;
+}
diff --git a/plugins/gobject-templates/gbp-gobject-workbench-addin.h 
b/plugins/gobject-templates/gbp-gobject-workbench-addin.h
new file mode 100644
index 0000000..9601914
--- /dev/null
+++ b/plugins/gobject-templates/gbp-gobject-workbench-addin.h
@@ -0,0 +1,33 @@
+/* gbp-gobject-workbench-addin.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_GOBJECT_WORKBENCH_ADDIN_H
+#define GBP_GOBJECT_WORKBENCH_ADDIN_H
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_GOBJECT_WORKBENCH_ADDIN (gbp_gobject_workbench_addin_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpGobjectWorkbenchAddin, gbp_gobject_workbench_addin, GBP, GOBJECT_WORKBENCH_ADDIN, 
GObject)
+
+G_END_DECLS
+
+#endif /* GBP_GOBJECT_WORKBENCH_ADDIN_H */
+
diff --git a/plugins/gobject-templates/gobject-templates-plugin.c 
b/plugins/gobject-templates/gobject-templates-plugin.c
new file mode 100644
index 0000000..7847786
--- /dev/null
+++ b/plugins/gobject-templates/gobject-templates-plugin.c
@@ -0,0 +1,30 @@
+/* gobject-templates-plugin.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 <libpeas/peas.h>
+
+#include "gbp-gobject-workbench-addin.h"
+
+void
+peas_register_types (PeasObjectModule *module)
+{
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_WORKBENCH_ADDIN,
+                                              GBP_TYPE_GOBJECT_WORKBENCH_ADDIN);
+}
diff --git a/plugins/gobject-templates/gobject-templates.gresource.xml 
b/plugins/gobject-templates/gobject-templates.gresource.xml
new file mode 100644
index 0000000..130361d
--- /dev/null
+++ b/plugins/gobject-templates/gobject-templates.gresource.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/builder/plugins/gobject-templates">
+    <file compressed="true">gtk/menus.ui</file>
+    <file compressed="true">gbp-gobject-dialog.ui</file>
+    <file compressed="true">gbp-gobject-property-editor.ui</file>
+  </gresource>
+</gresources>
diff --git a/plugins/gobject-templates/gobject-templates.plugin 
b/plugins/gobject-templates/gobject-templates.plugin
new file mode 100644
index 0000000..a24eb45
--- /dev/null
+++ b/plugins/gobject-templates/gobject-templates.plugin
@@ -0,0 +1,9 @@
+[Plugin]
+Module=gobject-templates
+Name=GObject Templates
+Description=Create new GObjects using templates
+Authors=Christian Hergert <christian hergert me>
+Copyright=Copyright © 2015 Christian Hergert
+Depends=project-tree-plugin
+Hidden=true
+Builtin=true
diff --git a/plugins/gobject-templates/gtk/menus.ui b/plugins/gobject-templates/gtk/menus.ui
new file mode 100644
index 0000000..63177cb
--- /dev/null
+++ b/plugins/gobject-templates/gtk/menus.ui
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <menu id="gb-project-tree-popup-menu">
+    <section id="gb-project-tree-new-section">
+      <submenu id="gb-project-tree-new-section-submenu">
+        <section id="gb-project-tree-new-gobject-section">
+          <attribute name="after">gb-project-tree-new-empty-file-section</attribute>
+          <item>
+            <attribute name="label" translatable="yes">Class</attribute>
+            <attribute name="action">gobject-templates.new-gobject</attribute>
+          </item>
+          <item>
+            <attribute name="label" translatable="yes">Interface</attribute>
+            <attribute name="action">gobject-templates.new-ginterface</attribute>
+          </item>
+          <item>
+            <attribute name="label" translatable="yes">Widget</attribute>
+            <attribute name="action">gobject-templates.new-gtkwidget</attribute>
+          </item>
+        </section>
+      </submenu>
+    </section>
+  </menu>
+</interface>



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