[gnome-builder/wip/chergert/layout] editor: bring back stack controls



commit 78c896a46386aac87efd1f6e874e63dcb9375c06
Author: Christian Hergert <chergert redhat com>
Date:   Wed Jun 28 00:49:48 2017 -0700

    editor: bring back stack controls
    
    We need these until we have an alternate solution for accessing
    their features.

 libide/editor/ide-editor-layout-stack-addin.c     |  113 ++++++++
 libide/editor/ide-editor-layout-stack-addin.h     |   29 ++
 libide/editor/ide-editor-layout-stack-controls.c  |  300 +++++++++++++++++++++
 libide/editor/ide-editor-layout-stack-controls.h  |   51 ++++
 libide/editor/ide-editor-layout-stack-controls.ui |   85 ++++++
 libide/editor/ide-editor-plugin.c                 |    6 +-
 libide/layout/ide-layout-stack-addin.h            |    9 +-
 libide/layout/ide-layout-stack.c                  |   99 +++++++
 libide/libide.gresource.xml                       |    1 +
 libide/meson.build                                |    8 +-
 10 files changed, 691 insertions(+), 10 deletions(-)
---
diff --git a/libide/editor/ide-editor-layout-stack-addin.c b/libide/editor/ide-editor-layout-stack-addin.c
new file mode 100644
index 0000000..aec5260
--- /dev/null
+++ b/libide/editor/ide-editor-layout-stack-addin.c
@@ -0,0 +1,113 @@
+/* ide-editor-layout-stack-addin.c
+ *
+ * Copyright (C) 2017 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 "ide-editor-layout-stack-addin.h"
+
+#include <dazzle.h>
+
+#include "editor/ide-editor-layout-stack-addin.h"
+#include "editor/ide-editor-layout-stack-controls.h"
+#include "editor/ide-editor-view.h"
+#include "layout/ide-layout-stack-header.h"
+
+struct _IdeEditorLayoutStackAddin
+{
+  GObject                       parent_instance;
+  IdeEditorLayoutStackControls *controls;
+};
+
+static void
+ide_editor_layout_stack_addin_load (IdeLayoutStackAddin *addin,
+                                    IdeLayoutStack      *stack)
+{
+  IdeEditorLayoutStackAddin *self = (IdeEditorLayoutStackAddin *)addin;
+  GtkWidget *header;
+
+  g_assert (IDE_IS_EDITOR_LAYOUT_STACK_ADDIN (self));
+  g_assert (IDE_IS_LAYOUT_STACK (stack));
+
+  header = ide_layout_stack_get_titlebar (stack);
+
+  self->controls = g_object_new (IDE_TYPE_EDITOR_LAYOUT_STACK_CONTROLS, NULL);
+  g_signal_connect (self->controls,
+                    "destroy",
+                    G_CALLBACK (gtk_widget_destroyed),
+                    &self->controls);
+  gtk_container_add_with_properties (GTK_CONTAINER (header), GTK_WIDGET (self->controls),
+                                     "pack-type", GTK_PACK_END,
+                                     "priority", 100,
+                                     NULL);
+}
+
+static void
+ide_editor_layout_stack_addin_unload (IdeLayoutStackAddin *addin,
+                                      IdeLayoutStack      *stack)
+{
+  IdeEditorLayoutStackAddin *self = (IdeEditorLayoutStackAddin *)addin;
+
+  g_assert (IDE_IS_EDITOR_LAYOUT_STACK_ADDIN (self));
+  g_assert (IDE_IS_LAYOUT_STACK (stack));
+
+  if (self->controls != NULL)
+    gtk_widget_destroy (GTK_WIDGET (self->controls));
+}
+
+static void
+ide_editor_layout_stack_addin_set_view (IdeLayoutStackAddin *addin,
+                                        IdeLayoutView       *view)
+{
+  IdeEditorLayoutStackAddin *self = (IdeEditorLayoutStackAddin *)addin;
+
+  g_assert (IDE_IS_EDITOR_LAYOUT_STACK_ADDIN (self));
+  g_assert (!view || IDE_IS_LAYOUT_VIEW (view));
+
+  if (IDE_IS_EDITOR_VIEW (view))
+    {
+      ide_editor_layout_stack_controls_set_view (self->controls, IDE_EDITOR_VIEW (view));
+      gtk_widget_show (GTK_WIDGET (self->controls));
+    }
+  else
+    {
+      ide_editor_layout_stack_controls_set_view (self->controls, NULL);
+      gtk_widget_hide (GTK_WIDGET (self->controls));
+    }
+}
+
+static void
+layout_stack_addin_iface_init (IdeLayoutStackAddinInterface *iface)
+{
+  iface->load = ide_editor_layout_stack_addin_load;
+  iface->unload = ide_editor_layout_stack_addin_unload;
+  iface->set_view = ide_editor_layout_stack_addin_set_view;
+}
+
+G_DEFINE_TYPE_WITH_CODE (IdeEditorLayoutStackAddin,
+                         ide_editor_layout_stack_addin,
+                         G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_LAYOUT_STACK_ADDIN,
+                                                layout_stack_addin_iface_init))
+
+static void
+ide_editor_layout_stack_addin_class_init (IdeEditorLayoutStackAddinClass *klass)
+{
+}
+
+static void
+ide_editor_layout_stack_addin_init (IdeEditorLayoutStackAddin *self)
+{
+}
diff --git a/libide/editor/ide-editor-layout-stack-addin.h b/libide/editor/ide-editor-layout-stack-addin.h
new file mode 100644
index 0000000..0c4609a
--- /dev/null
+++ b/libide/editor/ide-editor-layout-stack-addin.h
@@ -0,0 +1,29 @@
+/* ide-editor-layout-stack-addin.h
+ *
+ * Copyright (C) 2017 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/>.
+ */
+
+#pragma once
+
+#include "layout/ide-layout-stack-addin.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_EDITOR_LAYOUT_STACK_ADDIN (ide_editor_layout_stack_addin_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeEditorLayoutStackAddin, ide_editor_layout_stack_addin, IDE, 
EDITOR_LAYOUT_STACK_ADDIN, GObject)
+
+G_END_DECLS
diff --git a/libide/editor/ide-editor-layout-stack-controls.c 
b/libide/editor/ide-editor-layout-stack-controls.c
new file mode 100644
index 0000000..c74c17c
--- /dev/null
+++ b/libide/editor/ide-editor-layout-stack-controls.c
@@ -0,0 +1,300 @@
+/* ide-editor-layout-stack-controls.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 "ide-editor-layout-stack-controls"
+
+#include <glib/gi18n.h>
+
+#include "editor/ide-editor-layout-stack-controls.h"
+#include "editor/ide-editor-private.h"
+
+G_DEFINE_TYPE (IdeEditorLayoutStackControls, ide_editor_layout_stack_controls, GTK_TYPE_BOX)
+
+static void
+document_cursor_moved (IdeEditorLayoutStackControls *self,
+                       const GtkTextIter            *iter,
+                       GtkTextBuffer                *buffer)
+{
+  IdeSourceView *source_view;
+  GtkTextIter bounds;
+  GtkTextMark *mark;
+  gchar str[32];
+  guint line;
+  gint column;
+  gint column2;
+
+  g_assert (IDE_IS_EDITOR_LAYOUT_STACK_CONTROLS (self));
+  g_assert (iter != NULL);
+  g_assert (IDE_IS_BUFFER (buffer));
+
+  if (self->view == NULL)
+    return;
+
+  source_view = ide_editor_view_get_view (self->view);
+
+  ide_source_view_get_visual_position (source_view, &line, (guint *)&column);
+
+  mark = gtk_text_buffer_get_selection_bound (buffer);
+  gtk_text_buffer_get_iter_at_mark (buffer, &bounds, mark);
+
+  g_snprintf (str, sizeof str, "%d", line + 1);
+  dzl_simple_label_set_label (self->line_label, str);
+
+  g_snprintf (str, sizeof str, "%d", column + 1);
+  dzl_simple_label_set_label (self->column_label, str);
+
+  if (!gtk_widget_has_focus (GTK_WIDGET (source_view)) ||
+      gtk_text_iter_equal (&bounds, iter) ||
+      (gtk_text_iter_get_line (iter) != gtk_text_iter_get_line (&bounds)))
+    {
+      gtk_widget_set_visible (GTK_WIDGET (self->range_label), FALSE);
+      return;
+    }
+
+  /* We have a selection that is on the same line.
+   * Lets give some detail as to how long the selection is.
+   */
+  column2 = gtk_source_view_get_visual_column (GTK_SOURCE_VIEW (source_view), &bounds);
+
+  g_snprintf (str, sizeof str, "%u", ABS (column2 - column));
+  gtk_label_set_label (self->range_label, str);
+  gtk_widget_set_visible (GTK_WIDGET (self->range_label), TRUE);
+}
+
+
+static void
+goto_line_activate (IdeEditorLayoutStackControls *self,
+                    const gchar                  *text,
+                    DzlSimplePopover             *popover)
+{
+  gint64 value;
+
+  g_assert (IDE_IS_EDITOR_LAYOUT_STACK_CONTROLS (self));
+  g_assert (DZL_IS_SIMPLE_POPOVER (popover));
+
+  if (self->view == NULL)
+    return;
+
+  if (!ide_str_empty0 (text))
+    {
+      value = g_ascii_strtoll (text, NULL, 10);
+
+      if ((value > 0) && (value < G_MAXINT))
+        {
+          IdeSourceView *source_view;
+          GtkTextBuffer *buffer = GTK_TEXT_BUFFER (self->view->buffer);
+          GtkTextIter iter;
+
+          source_view = ide_editor_view_get_view (self->view);
+
+          gtk_widget_grab_focus (GTK_WIDGET (self->view));
+          gtk_text_buffer_get_iter_at_line (buffer, &iter, value - 1);
+          gtk_text_buffer_select_range (buffer, &iter, &iter);
+          ide_source_view_scroll_to_iter (source_view, &iter, 0.25, TRUE, 1.0, 0.5, TRUE);
+        }
+    }
+}
+
+static gboolean
+goto_line_insert_text (IdeEditorLayoutStackControls *self,
+                       guint                         position,
+                       const gchar                  *chars,
+                       guint                         n_chars,
+                       DzlSimplePopover             *popover)
+{
+  g_assert (IDE_IS_EDITOR_LAYOUT_STACK_CONTROLS (self));
+  g_assert (DZL_IS_SIMPLE_POPOVER (popover));
+  g_assert (chars != NULL);
+
+  for (; *chars; chars = g_utf8_next_char (chars))
+    {
+      if (!g_unichar_isdigit (g_utf8_get_char (chars)))
+        return GDK_EVENT_STOP;
+    }
+
+  return GDK_EVENT_PROPAGATE;
+}
+
+static void
+goto_line_changed (IdeEditorLayoutStackControls *self,
+                   DzlSimplePopover             *popover)
+{
+  gchar *message;
+  const gchar *text;
+  GtkTextIter begin;
+  GtkTextIter end;
+
+  g_assert (IDE_IS_EDITOR_LAYOUT_STACK_CONTROLS (self));
+  g_assert (DZL_IS_SIMPLE_POPOVER (popover));
+
+  if (self->view == NULL)
+    return;
+
+  text = dzl_simple_popover_get_text (popover);
+
+  gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (self->view->buffer), &begin, &end);
+
+  if (!ide_str_empty0 (text))
+    {
+      gint64 value;
+
+      value = g_ascii_strtoll (text, NULL, 10);
+
+      if (value > 0)
+        {
+          if (value <= gtk_text_iter_get_line (&end) + 1)
+            {
+              dzl_simple_popover_set_message (popover, NULL);
+              dzl_simple_popover_set_ready (popover, TRUE);
+              return;
+            }
+        }
+    }
+
+  /* translators: the user selected a number outside the value range for the document. */
+  message = g_strdup_printf (_("Provide a number between 1 and %u"),
+                             gtk_text_iter_get_line (&end) + 1);
+  dzl_simple_popover_set_message (popover, message);
+  dzl_simple_popover_set_ready (popover, FALSE);
+
+  g_free (message);
+}
+
+static void
+warning_button_clicked (IdeEditorLayoutStackControls *self,
+                        GtkButton                    *button)
+{
+  IdeSourceView *source_view;
+
+  g_assert (IDE_IS_EDITOR_LAYOUT_STACK_CONTROLS (self));
+  g_assert (GTK_IS_BUTTON (button));
+
+  if (self->view == NULL)
+    return;
+
+  source_view = ide_editor_view_get_view (self->view);
+  gtk_widget_grab_focus (GTK_WIDGET (source_view));
+  g_signal_emit_by_name (source_view, "move-error", GTK_DIR_DOWN);
+}
+
+static void
+ide_editor_layout_stack_controls_finalize (GObject *object)
+{
+  IdeEditorLayoutStackControls *self = (IdeEditorLayoutStackControls *)object;
+
+  g_clear_object (&self->buffer_bindings);
+  g_clear_object (&self->buffer_signals);
+
+  self->view = NULL;
+
+  G_OBJECT_CLASS (ide_editor_layout_stack_controls_parent_class)->finalize (object);
+}
+
+static void
+ide_editor_layout_stack_controls_class_init (IdeEditorLayoutStackControlsClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = ide_editor_layout_stack_controls_finalize;
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/builder/ui/ide-editor-layout-stack-controls.ui");
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorLayoutStackControls, column_label);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorLayoutStackControls, goto_line_popover);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorLayoutStackControls, goto_line_button);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorLayoutStackControls, line_label);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorLayoutStackControls, range_label);
+  gtk_widget_class_bind_template_child (widget_class, IdeEditorLayoutStackControls, warning_button);
+}
+
+static void
+ide_editor_layout_stack_controls_init (IdeEditorLayoutStackControls *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  g_signal_connect_object (self->goto_line_popover,
+                           "activate",
+                           G_CALLBACK (goto_line_activate),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (self->goto_line_popover,
+                           "insert-text",
+                           G_CALLBACK (goto_line_insert_text),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (self->goto_line_popover,
+                           "changed",
+                           G_CALLBACK (goto_line_changed),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (self->warning_button,
+                           "clicked",
+                           G_CALLBACK (warning_button_clicked),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  self->buffer_bindings = dzl_binding_group_new ();
+
+  dzl_binding_group_bind (self->buffer_bindings, "has-diagnostics",
+                          self->warning_button, "visible",
+                          G_BINDING_SYNC_CREATE);
+
+  self->buffer_signals = dzl_signal_group_new (IDE_TYPE_BUFFER);
+
+  dzl_signal_group_connect_object (self->buffer_signals,
+                                   "cursor-moved",
+                                   G_CALLBACK (document_cursor_moved),
+                                   self,
+                                   G_CONNECT_SWAPPED);
+}
+
+void
+ide_editor_layout_stack_controls_set_view (IdeEditorLayoutStackControls *self,
+                                           IdeEditorView                *view)
+{
+  g_return_if_fail (IDE_IS_EDITOR_LAYOUT_STACK_CONTROLS (self));
+  g_return_if_fail (!view || IDE_IS_EDITOR_VIEW (view));
+
+  if (self->view == view)
+    return;
+
+  dzl_binding_group_set_source (self->buffer_bindings, NULL);
+  dzl_signal_group_set_target (self->buffer_signals, NULL);
+
+  if (self->view != NULL)
+    {
+      g_signal_handlers_disconnect_by_func (self->view,
+                                            G_CALLBACK (gtk_widget_destroyed),
+                                            &self->view);
+      self->view = NULL;
+    }
+
+  if (view != NULL)
+    {
+      self->view = view;
+      g_signal_connect (view,
+                        "destroy",
+                        G_CALLBACK (gtk_widget_destroyed),
+                        &self->view);
+      dzl_binding_group_set_source (self->buffer_bindings, view->buffer);
+      dzl_signal_group_set_target (self->buffer_signals, view->buffer);
+    }
+}
diff --git a/libide/editor/ide-editor-layout-stack-controls.h 
b/libide/editor/ide-editor-layout-stack-controls.h
new file mode 100644
index 0000000..f63d806
--- /dev/null
+++ b/libide/editor/ide-editor-layout-stack-controls.h
@@ -0,0 +1,51 @@
+/* ide-editor-layout-stack-controls.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/>.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+#include <dazzle.h>
+
+#include "editor/ide-editor-view.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_EDITOR_LAYOUT_STACK_CONTROLS (ide_editor_layout_stack_controls_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeEditorLayoutStackControls, ide_editor_layout_stack_controls, IDE, 
EDITOR_LAYOUT_STACK_CONTROLS, GtkBox)
+
+struct _IdeEditorLayoutStackControls
+{
+  GtkBox                parent_instance;
+
+  IdeEditorView        *view;
+  DzlBindingGroup      *buffer_bindings;
+  DzlSignalGroup       *buffer_signals;
+
+  DzlSimplePopover     *goto_line_popover;
+  GtkMenuButton        *goto_line_button;
+  GtkButton            *warning_button;
+  DzlSimpleLabel       *line_label;
+  DzlSimpleLabel       *column_label;
+  GtkLabel             *range_label;
+};
+
+void ide_editor_layout_stack_controls_set_view (IdeEditorLayoutStackControls *self,
+                                                IdeEditorView                *view);
+
+G_END_DECLS
diff --git a/libide/editor/ide-editor-layout-stack-controls.ui 
b/libide/editor/ide-editor-layout-stack-controls.ui
new file mode 100644
index 0000000..af07c68
--- /dev/null
+++ b/libide/editor/ide-editor-layout-stack-controls.ui
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="IdeEditorLayoutStackControls">
+    <property name="orientation">horizontal</property>
+    <child>
+      <object class="GtkButton" id="warning_button">
+        <property name="visible">false</property>
+        <child>
+          <object class="GtkImage">
+            <property name="icon-name">dialog-warning-symbolic</property>
+            <property name="visible">true</property>
+            <style>
+              <class name="dim-label"/>
+            </style>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="GtkMenuButton" id="goto_line_button">
+        <property name="popover">goto_line_popover</property>
+        <property name="focus-on-click">false</property>
+        <property name="tooltip-text" translatable="yes">Go to line number</property>
+        <property name="valign">baseline</property>
+        <property name="visible">true</property>
+        <child>
+          <object class="GtkBox">
+            <property name="valign">baseline</property>
+            <property name="visible">true</property>
+            <child type="center">
+              <object class="GtkLabel">
+                <property name="label">:</property>
+                <property name="visible">true</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+            </child>
+            <child>
+              <object class="DzlSimpleLabel" id="line_label">
+                <property name="label">1</property>
+                <property name="width-chars">3</property>
+                <property name="xalign">1.0</property>
+                <property name="valign">baseline</property>
+                <property name="visible">true</property>
+              </object>
+              <packing>
+                <property name="pack-type">start</property>
+              </packing>
+            </child>
+            <child>
+              <object class="DzlSimpleLabel" id="column_label">
+                <property name="label">1</property>
+                <property name="width-chars">3</property>
+                <property name="xalign">0.0</property>
+                <property name="valign">baseline</property>
+                <property name="visible">true</property>
+              </object>
+              <packing>
+                <property name="position">1</property>
+                <property name="pack-type">end</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="range_label">
+                <property name="valign">baseline</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="position">0</property>
+                <property name="pack-type">end</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="DzlSimplePopover" id="goto_line_popover">
+    <property name="title" translatable="yes">Go to Line</property>
+    <property name="button-text" translatable="yes">Go</property>
+  </object>
+</interface>
diff --git a/libide/editor/ide-editor-plugin.c b/libide/editor/ide-editor-plugin.c
index 59d2d7b..3449902 100644
--- a/libide/editor/ide-editor-plugin.c
+++ b/libide/editor/ide-editor-plugin.c
@@ -20,12 +20,12 @@
 
 #include <libpeas/peas.h>
 
+#include "editor/ide-editor-layout-stack-addin.h"
 #include "editor/ide-editor-workbench-addin.h"
 
 void
 ide_editor_register_types (PeasObjectModule *module)
 {
-  peas_object_module_register_extension_type (module,
-                                              IDE_TYPE_WORKBENCH_ADDIN,
-                                              IDE_TYPE_EDITOR_WORKBENCH_ADDIN);
+  peas_object_module_register_extension_type (module, IDE_TYPE_LAYOUT_STACK_ADDIN, 
IDE_TYPE_EDITOR_LAYOUT_STACK_ADDIN);
+  peas_object_module_register_extension_type (module, IDE_TYPE_WORKBENCH_ADDIN, 
IDE_TYPE_EDITOR_WORKBENCH_ADDIN);
 }
diff --git a/libide/layout/ide-layout-stack-addin.h b/libide/layout/ide-layout-stack-addin.h
index e021760..cee30c6 100644
--- a/libide/layout/ide-layout-stack-addin.h
+++ b/libide/layout/ide-layout-stack-addin.h
@@ -16,13 +16,12 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef IDE_LAYOUT_STACK_ADDIN_H
-#define IDE_LAYOUT_STACK_ADDIN_H
+#pragma once
 
 #include <gtk/gtk.h>
 
-#include "layout/ide-layout-stack.h"
-#include "layout/ide-layout-view.h"
+#include "ide-layout-stack.h"
+#include "ide-layout-view.h"
 
 G_BEGIN_DECLS
 
@@ -50,5 +49,3 @@ void ide_layout_stack_addin_set_view (IdeLayoutStackAddin *self,
                                       IdeLayoutView       *view);
 
 G_END_DECLS
-
-#endif /* IDE_LAYOUT_STACK_ADDIN_H */
diff --git a/libide/layout/ide-layout-stack.c b/libide/layout/ide-layout-stack.c
index 6db2a55..71c91c3 100644
--- a/libide/layout/ide-layout-stack.c
+++ b/libide/layout/ide-layout-stack.c
@@ -20,8 +20,10 @@
 
 #include <dazzle.h>
 #include <glib/gi18n.h>
+#include <libpeas/peas.h>
 
 #include "ide-layout-stack.h"
+#include "ide-layout-stack-addin.h"
 #include "ide-layout-stack-header.h"
 #include "ide-layout-private.h"
 #include "ide-shortcut-label.h"
@@ -49,6 +51,7 @@ typedef struct
   DzlBindingGroup      *bindings;
   DzlSignalGroup       *signals;
   GPtrArray            *views;
+  PeasExtensionSet     *addins;
 
   DzlBox               *empty_state;
   DzlEmptyState        *failed_state;
@@ -124,6 +127,23 @@ ide_layout_stack_bindings_notify_source (IdeLayoutStack  *self,
 }
 
 static void
+ide_layout_stack_notify_addin_of_view (PeasExtensionSet *set,
+                                       PeasPluginInfo   *plugin_info,
+                                       PeasExtension    *exten,
+                                       gpointer          user_data)
+{
+  IdeLayoutStackAddin *addin = (IdeLayoutStackAddin *)exten;
+  IdeLayoutView *view = user_data;
+
+  g_assert (PEAS_IS_EXTENSION_SET (set));
+  g_assert (plugin_info != NULL);
+  g_assert (IDE_IS_LAYOUT_STACK_ADDIN (addin));
+  g_assert (!view || IDE_IS_LAYOUT_VIEW (view));
+
+  ide_layout_stack_addin_set_view (addin, view);
+}
+
+static void
 ide_layout_stack_notify_visible_child (IdeLayoutStack *self,
                                        GParamSpec     *pspec,
                                        GtkStack       *stack)
@@ -134,6 +154,9 @@ ide_layout_stack_notify_visible_child (IdeLayoutStack *self,
   g_assert (IDE_IS_LAYOUT_STACK (self));
   g_assert (GTK_IS_STACK (stack));
 
+  if (gtk_widget_in_destruction (GTK_WIDGET (self)))
+    return;
+
   visible_child = gtk_stack_get_visible_child (priv->stack);
 
   /*
@@ -162,6 +185,10 @@ ide_layout_stack_notify_visible_child (IdeLayoutStack *self,
   /* Ensure action state is up to date */
   _ide_layout_stack_update_actions (self);
 
+  peas_extension_set_foreach (priv->addins,
+                              ide_layout_stack_notify_addin_of_view,
+                              visible_child);
+
   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_VISIBLE_CHILD]);
   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_VIEW]);
 }
@@ -311,6 +338,75 @@ ide_layout_stack_real_agree_to_close_finish (IdeLayoutStack *self,
 }
 
 static void
+ide_layout_stack_addin_added (PeasExtensionSet *set,
+                              PeasPluginInfo   *plugin_info,
+                              PeasExtension    *exten,
+                              gpointer          user_data)
+{
+  IdeLayoutStackAddin *addin = (IdeLayoutStackAddin *)exten;
+  IdeLayoutStack *self = user_data;
+  IdeLayoutView *visible_child;
+
+  g_assert (IDE_IS_LAYOUT_STACK (self));
+  g_assert (PEAS_IS_EXTENSION_SET (set));
+  g_assert (plugin_info != NULL);
+  g_assert (IDE_IS_LAYOUT_STACK_ADDIN (addin));
+
+  ide_layout_stack_addin_load (addin, self);
+
+  visible_child = ide_layout_stack_get_visible_child (self);
+
+  if (visible_child != NULL)
+    ide_layout_stack_addin_set_view (addin, visible_child);
+}
+
+static void
+ide_layout_stack_addin_removed (PeasExtensionSet *set,
+                                PeasPluginInfo   *plugin_info,
+                                PeasExtension    *exten,
+                                gpointer          user_data)
+{
+  IdeLayoutStackAddin *addin = (IdeLayoutStackAddin *)exten;
+  IdeLayoutStack *self = user_data;
+
+  g_assert (IDE_IS_LAYOUT_STACK (self));
+  g_assert (PEAS_IS_EXTENSION_SET (set));
+  g_assert (plugin_info != NULL);
+  g_assert (IDE_IS_LAYOUT_STACK_ADDIN (addin));
+
+  ide_layout_stack_addin_unload (addin, self);
+}
+
+static void
+ide_layout_stack_constructed (GObject *object)
+{
+  IdeLayoutStack *self = (IdeLayoutStack *)object;
+  IdeLayoutStackPrivate *priv = ide_layout_stack_get_instance_private (self);
+
+  g_assert (IDE_IS_LAYOUT_STACK (self));
+
+  G_OBJECT_CLASS (ide_layout_stack_parent_class)->constructed (object);
+
+  priv->addins = peas_extension_set_new (peas_engine_get_default (),
+                                         IDE_TYPE_LAYOUT_STACK_ADDIN,
+                                         NULL);
+
+  g_signal_connect (priv->addins,
+                    "extension-added",
+                    G_CALLBACK (ide_layout_stack_addin_added),
+                    self);
+
+  g_signal_connect (priv->addins,
+                    "extension-removed",
+                    G_CALLBACK (ide_layout_stack_addin_removed),
+                    self);
+
+  peas_extension_set_foreach (priv->addins,
+                              ide_layout_stack_addin_added,
+                              self);
+}
+
+static void
 ide_layout_stack_destroy (GtkWidget *widget)
 {
   IdeLayoutStack *self = (IdeLayoutStack *)widget;
@@ -318,6 +414,8 @@ ide_layout_stack_destroy (GtkWidget *widget)
 
   g_assert (IDE_IS_LAYOUT_STACK (self));
 
+  g_clear_object (&priv->addins);
+
   if (priv->bindings != NULL)
     {
       dzl_binding_group_set_source (priv->bindings, NULL);
@@ -384,6 +482,7 @@ ide_layout_stack_class_init (IdeLayoutStackClass *klass)
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
 
+  object_class->constructed = ide_layout_stack_constructed;
   object_class->get_property = ide_layout_stack_get_property;
   object_class->set_property = ide_layout_stack_set_property;
 
diff --git a/libide/libide.gresource.xml b/libide/libide.gresource.xml
index ac4239e..c47f8ef 100644
--- a/libide/libide.gresource.xml
+++ b/libide/libide.gresource.xml
@@ -55,6 +55,7 @@
     <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-layout-stack-header.ui">layout/ide-layout-stack-header.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-layout-stack.ui">layout/ide-layout-stack.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-editor-perspective.ui">editor/ide-editor-perspective.ui</file>
+    <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-editor-layout-stack-controls.ui">editor/ide-editor-layout-stack-controls.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-editor-search-bar.ui">editor/ide-editor-search-bar.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-editor-spell-widget.ui">spellcheck/ide-editor-spell-widget.ui</file>
     <file compressed="true" preprocess="xml-stripblanks" 
alias="ide-editor-view.ui">editor/ide-editor-view.ui</file>
diff --git a/libide/meson.build b/libide/meson.build
index 5c870a5..099bf97 100644
--- a/libide/meson.build
+++ b/libide/meson.build
@@ -126,6 +126,7 @@ libide_public_headers = [
   'layout/ide-layout-grid.h',
   'layout/ide-layout-grid-column.h',
   'layout/ide-layout-stack.h',
+  'layout/ide-layout-stack-addin.h',
   'layout/ide-layout-stack-header.h',
   'layout/ide-layout-view.h',
   'layout/ide-layout-pane.h',
@@ -324,8 +325,9 @@ libide_public_sources = [
   'layout/ide-layout-grid.c',
   'layout/ide-layout-grid-column.c',
   'layout/ide-layout-pane.c',
-  'layout/ide-layout-stack-header.c',
   'layout/ide-layout-stack.c',
+  'layout/ide-layout-stack-addin.c',
+  'layout/ide-layout-stack-header.c',
   'layout/ide-layout-view.c',
   'layout/ide-layout.c',
   'local/ide-local-device.c',
@@ -456,6 +458,10 @@ libide_sources = libide_generated_headers + libide_public_sources + [
   'buildui/ide-environment-editor-row.h',
   'buildui/ide-environment-editor.c',
   'buildui/ide-environment-editor.h',
+  'editor/ide-editor-layout-stack-addin.c',
+  'editor/ide-editor-layout-stack-addin.h',
+  'editor/ide-editor-layout-stack-controls.c',
+  'editor/ide-editor-layout-stack-controls.h',
   'editor/ide-editor-perspective-actions.c',
   'editor/ide-editor-perspective-shortcuts.c',
   'editor/ide-editor-plugin.c',


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