static void
gb_editor_view_actions_preview (GSimpleAction *action,
                                GVariant      *param,
                                gpointer       user_data)
{
  GbEditorView *self = user_data;
  GtkSourceLanguage *language;
  const gchar *lang_id = NULL;
  g_autoptr(GbDocument) document = NULL;

  g_assert (GB_IS_EDITOR_VIEW (self));

  language = gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (self->document));
  if (!language)
    return;

  lang_id = gtk_source_language_get_id (language);
  if (!lang_id)
    return;

#if 0
  if (g_str_equal (lang_id, "html"))
    {
      document = g_object_new (GB_TYPE_HTML_DOCUMENT,
                               "buffer", self->document,
                               NULL);
    }
  else if (g_str_equal (lang_id, "markdown"))
    {
      document = g_object_new (GB_TYPE_HTML_DOCUMENT,
                               "buffer", self->document,
                               NULL);
      gb_html_document_set_transform_func (GB_HTML_DOCUMENT (document),
                                           gb_html_markdown_transform);
    }
#endif

  if (document)
    {
      GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (self));

      while (parent && !GB_IS_VIEW_GRID (parent))
        parent = gtk_widget_get_parent (parent);

      if (parent == NULL)
        {
          while (parent && !GB_IS_VIEW_STACK (parent))
            parent = gtk_widget_get_parent (parent);
          g_assert (GB_IS_VIEW_STACK (parent));
          gb_view_stack_focus_document (GB_VIEW_STACK (parent), document);
          return;
        }

      g_assert (GB_IS_VIEW_GRID (parent));
      gb_view_grid_focus_document (GB_VIEW_GRID (parent), document);
    }
}
void
gb_view_stack_add (GtkContainer *container,
                   GtkWidget    *child)
{
  GbViewStack *self = (GbViewStack *)container;

  g_assert (GB_IS_VIEW_STACK (self));

  if (GB_IS_VIEW (child))
    {
      GtkWidget *controls;

      gtk_widget_set_sensitive (GTK_WIDGET (self->close_button), TRUE);
      gtk_widget_set_sensitive (GTK_WIDGET (self->document_button), TRUE);
      gtk_widget_set_visible (GTK_WIDGET (self->views_button), TRUE);

      self->focus_history = g_list_prepend (self->focus_history, child);
      controls = gb_view_get_controls (GB_VIEW (child));
      if (controls)
        gtk_container_add (GTK_CONTAINER (self->controls_stack), controls);
      gtk_container_add (GTK_CONTAINER (self->stack), child);
      gb_view_set_back_forward_list (GB_VIEW (child), self->back_forward_list);
      gb_view_stack_add_list_row (self, GB_VIEW (child));
      gtk_stack_set_visible_child (self->stack, child);
    }
  else
    {
      GTK_CONTAINER_CLASS (gb_view_stack_parent_class)->add (container, child);
    }
}
void
gb_view_stack_remove (GbViewStack *self,
                      GbView      *view)
{
  GtkWidget *controls;
  GtkWidget *focus_after_close = NULL;

  g_assert (GB_IS_VIEW_STACK (self));
  g_assert (GB_IS_VIEW (view));

  focus_after_close = g_list_nth_data (self->focus_history, 1);
  if (focus_after_close != NULL)
    g_object_ref (focus_after_close);

  gb_view_stack_remove_list_row (self, view);

  self->focus_history = g_list_remove (self->focus_history, view);
  controls = gb_view_get_controls (view);
  if (controls)
    gtk_container_remove (GTK_CONTAINER (self->controls_stack), controls);
  gtk_container_remove (GTK_CONTAINER (self->stack), GTK_WIDGET (view));

  if (focus_after_close != NULL)
    {
      gtk_stack_set_visible_child (self->stack, focus_after_close);
      gtk_widget_grab_focus (GTK_WIDGET (focus_after_close));
      g_clear_object (&focus_after_close);
    }
  else
    g_signal_emit (self, gSignals [EMPTY], 0);
}
static void
gb_view_stack_remove_list_row (GbViewStack *self,
                               GbView      *child)
{
  GList *children;
  GList *iter;

  g_assert (GB_IS_VIEW_STACK (self));
  g_assert (GB_IS_VIEW (child));

  children = gtk_container_get_children (GTK_CONTAINER (self->views_listbox));

  for (iter = children; iter; iter = iter->next)
    {
      GbView *view = g_object_get_data (iter->data, "GB_VIEW");

      if (view == child)
        {
          gtk_container_remove (GTK_CONTAINER (self->views_listbox), iter->data);
          break;
        }
    }

  g_list_free (children);
}
GtkWidget *
gb_view_stack_find_with_document (GbViewStack *self,
                                  GbDocument  *document)
{
  GtkWidget *ret = NULL;
  GList *iter;
  GList *children;

  g_return_val_if_fail (GB_IS_VIEW_STACK (self), NULL);
  g_return_val_if_fail (GB_IS_DOCUMENT (document), NULL);

  children = gtk_container_get_children (GTK_CONTAINER (self->stack));

  for (iter = children; iter; iter = iter->next)
    {
      GbView *view = iter->data;
      GbDocument *item;

      g_assert (GB_IS_VIEW (view));

      item = gb_view_get_document (view);

      if (item == document)
        {
          ret = GTK_WIDGET (view);
          break;
        }
    }

  g_list_free (children);

  return ret;
}
GtkWidget *
gb_view_stack_get_active_view (GbViewStack *self)
{
  g_return_val_if_fail (GB_IS_VIEW_STACK (self), NULL);

  return self->active_view;
}
/**
 * gb_view_stack_get_views:
 *
 * Gets the views belonging to this #GbViewStack.
 *
 * Returns: (transfer container): A #GList of #GbView.
 */
GList *
gb_view_stack_get_views (GbViewStack *self)
{
  g_return_val_if_fail (GB_IS_VIEW_STACK (self), NULL);

  return gtk_container_get_children (GTK_CONTAINER (self->stack));
}
static gboolean
gb_view_stack_is_empty (GbViewStack *self)
{
  g_return_val_if_fail (GB_IS_VIEW_STACK (self), FALSE);

  return (self->focus_history == NULL);
}
static void
gb_view_stack_move_top_list_row (GbViewStack *self,
                                 GbView      *view)
{
  GList *children;
  GList *iter;

  g_assert (GB_IS_VIEW_STACK (self));
  g_assert (GB_IS_VIEW (view));

  children = gtk_container_get_children (GTK_CONTAINER (self->views_listbox));

  for (iter = children; iter; iter = iter->next)
    {
      GtkWidget *row = iter->data;
      GbView *item = g_object_get_data (G_OBJECT (row), "GB_VIEW");

      if (item == view)
        {
          g_object_ref (row);
          gtk_container_remove (GTK_CONTAINER (self->views_listbox), row);
          gtk_list_box_prepend (self->views_listbox, row);
          gtk_list_box_select_row (self->views_listbox, GTK_LIST_BOX_ROW (row));
          g_object_unref (row);
          break;
        }
    }

  g_list_free (children);
}
static void
gb_view_stack_hierarchy_changed (GtkWidget *widget,
                                 GtkWidget *old_toplevel)
{
  GbViewStack *self = (GbViewStack *)widget;
  GtkWidget *toplevel;

  g_assert (GB_IS_VIEW_STACK (self));

  if (GB_IS_WORKBENCH (old_toplevel))
    {
      g_signal_handlers_disconnect_by_func (old_toplevel,
                                            G_CALLBACK (gb_view_stack_on_workbench_unload),
                                            self);
    }

  toplevel = gtk_widget_get_toplevel (widget);

  if (GB_IS_WORKBENCH (toplevel))
    {
      g_signal_connect (toplevel,
                        "unload",
                        G_CALLBACK (gb_view_stack_on_workbench_unload),
                        self);
    }
}
static void
gb_view_stack_grab_focus (GtkWidget *widget)
{
  GbViewStack *self = (GbViewStack *)widget;
  GtkWidget *visible_child;

  g_assert (GB_IS_VIEW_STACK (self));

  visible_child = gtk_stack_get_visible_child (self->stack);
  if (visible_child)
    gtk_widget_grab_focus (visible_child);
}
static void
gb_view_stack_real_remove (GtkContainer *container,
                           GtkWidget    *child)
{
  GbViewStack *self = (GbViewStack *)container;

  g_assert (GB_IS_VIEW_STACK (self));

  if (GB_IS_VIEW (child))
    gb_view_stack_remove (self, GB_VIEW (child));
  else
    GTK_CONTAINER_CLASS (gb_view_stack_parent_class)->remove (container, child);
}
static void
gb_view_stack_real_empty (GbViewStack *self)
{
  g_assert (GB_IS_VIEW_STACK (self));

  /* its possible for a widget to be added during "empty" emission. */
  if (gb_view_stack_is_empty (self) && !self->destroyed)
    {
      gtk_widget_set_sensitive (GTK_WIDGET (self->close_button), FALSE);
      gtk_widget_set_sensitive (GTK_WIDGET (self->document_button), FALSE);
      gtk_widget_set_visible (GTK_WIDGET (self->modified_label), FALSE);
      gtk_widget_set_visible (GTK_WIDGET (self->views_button), FALSE);
    }
}
static void
navigate_to_cb (GbViewStack        *self,
                IdeBackForwardItem *item,
                IdeBackForwardList *back_forward_list)
{
  IdeSourceLocation *srcloc;

  g_assert (GB_IS_VIEW_STACK (self));
  g_assert (IDE_IS_BACK_FORWARD_ITEM (item));
  g_assert (IDE_IS_BACK_FORWARD_LIST (back_forward_list));

  srcloc = ide_back_forward_item_get_location (item);
  gb_view_stack_focus_location (self, srcloc);
}
static void
gb_view_stack__notify_visible_child (GbViewStack *self,
                                     GParamSpec  *pspec,
                                     GtkStack    *stack)
{
  GtkWidget *visible_child;

  g_assert (GB_IS_VIEW_STACK (self));
  g_assert (GTK_IS_STACK (stack));

  visible_child = gtk_stack_get_visible_child (stack);

  gb_view_stack_set_active_view (self, visible_child);
}
void
gb_view_stack_focus_location (GbViewStack       *self,
                              IdeSourceLocation *location)
{
  IdeBufferManager *buffer_manager;
  IdeBuffer *buffer;
  IdeFile *file;
  GFile *gfile;

  g_return_if_fail (GB_IS_VIEW_STACK (self));
  g_return_if_fail (location != NULL);

  if (self->context == NULL)
    return;

  file = ide_source_location_get_file (location);
  gfile = ide_file_get_file (file);

  g_assert (file != NULL);
  g_assert (IDE_IS_FILE (file));

  gfile = ide_file_get_file (file);

  buffer_manager = ide_context_get_buffer_manager (self->context);
  buffer = ide_buffer_manager_find_buffer (buffer_manager, gfile);

  if (buffer != NULL && GB_IS_DOCUMENT (buffer))
    {
      GtkWidget *active_view;

      gb_view_stack_focus_document (self, GB_DOCUMENT (buffer));
      active_view = gb_view_stack_get_active_view (self);
      g_assert (GB_DOCUMENT (buffer) == gb_view_get_document (GB_VIEW (active_view)));
      gb_view_navigate_to (GB_VIEW (active_view), location);
    }
  else
    {
      g_autoptr(GTask) task = NULL;

      task = g_task_new (self, NULL, NULL, NULL);
      g_task_set_task_data (task, ide_source_location_ref (location),
                            (GDestroyNotify)ide_source_location_unref);
      ide_buffer_manager_load_file_async (buffer_manager, file, FALSE, NULL, NULL,
                                          gb_view_stack__navigate_to_load_cb,
                                          g_object_ref (task));
    }
}
static void
gb_view_stack_on_workbench_unload (GbWorkbench *workbench,
                                   IdeContext  *context,
                                   GbViewStack *self)
{
  IdeBackForwardList *back_forward_list;

  g_assert (GB_IS_WORKBENCH (workbench));
  g_assert (IDE_IS_CONTEXT (context));
  g_assert (GB_IS_VIEW_STACK (self));

  if (self->back_forward_list)
    {
      back_forward_list = ide_context_get_back_forward_list (context);
      ide_back_forward_list_merge (back_forward_list, self->back_forward_list);
    }
}
static void
gb_view_stack_add_list_row (GbViewStack *self,
                            GbView      *child)
{
  GtkWidget *row;
  GtkWidget *label;
  GtkWidget *box;

  g_assert (GB_IS_VIEW_STACK (self));
  g_assert (GB_IS_VIEW (child));

  row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
                      "visible", TRUE,
                      NULL);
  g_object_set_data (G_OBJECT (row), "GB_VIEW", child);

  box = g_object_new (GTK_TYPE_BOX,
                      "orientation", GTK_ORIENTATION_HORIZONTAL,
                      "visible", TRUE,
                      NULL);
  gtk_container_add (GTK_CONTAINER (row), box);

  label = g_object_new (GTK_TYPE_LABEL,
                        "margin-bottom", 3,
                        "margin-end", 6,
                        "margin-start", 6,
                        "margin-top", 3,
                        "visible", TRUE,
                        "xalign", 0.0f,
                        NULL);
  g_object_bind_property (child, "title", label, "label", G_BINDING_SYNC_CREATE);
  gtk_container_add (GTK_CONTAINER (box), label);

  label = g_object_new (GTK_TYPE_LABEL,
                        "visible", FALSE,
                        "label", "•",
                        "margin-start", 3,
                        "margin-end", 3,
                        NULL);
  g_object_bind_property (child, "modified", label, "visible", G_BINDING_SYNC_CREATE);
  gtk_container_add (GTK_CONTAINER (box), label);

  gtk_container_add (GTK_CONTAINER (self->views_listbox), row);
}
void
gb_view_stack_raise_document (GbViewStack *self,
                              GbDocument  *document,
                              gboolean     focus)
{
  GtkWidget *view;

  g_return_if_fail (GB_IS_VIEW_STACK (self));
  g_return_if_fail (GB_IS_DOCUMENT (document));

  view = gb_view_stack_find_with_document (self, document);

  if (view != NULL && GB_IS_VIEW (view))
    {
      gb_view_stack_set_active_view (self, view);
      if (focus)
        gtk_widget_grab_focus (view);
      return;
    }

  view = gb_document_create_view (document);

  if (view == NULL)
    {
      g_warning ("Document %s failed to create a view",
                 gb_document_get_title (document));
      return;
    }

  if (!GB_IS_VIEW (view))
    {
      g_warning ("Document %s did not create a GbView instance.",
                 gb_document_get_title (document));
      return;
    }

  gb_view_stack_add (GTK_CONTAINER (self), view);
  gb_view_stack_set_active_view (self, view);

  if (focus)
    gtk_widget_grab_focus (view);
}
GbDocument *
gb_view_stack_find_document_typed (GbViewStack *self,
                                   GType        document_type)
{
  GList *iter;

  g_return_val_if_fail (GB_IS_VIEW_STACK (self), NULL);
  g_return_val_if_fail (g_type_is_a (document_type, GB_TYPE_DOCUMENT), NULL);

  for (iter = self->focus_history; iter; iter = iter->next)
    {
      GbDocument *document;

      document = gb_view_get_document (iter->data);
      if (g_type_is_a (G_TYPE_FROM_INSTANCE (document), document_type))
        return document;
    }

  return NULL;
}
static void
gb_view_stack__views_listbox_row_activated_cb (GbViewStack   *self,
                                               GtkListBoxRow *row,
                                               GtkListBox    *list_box)
{
  GbView *view;

  g_assert (GB_IS_VIEW_STACK (self));
  g_assert (GTK_IS_LIST_BOX_ROW (row));
  g_assert (GTK_IS_LIST_BOX (list_box));

  view = g_object_get_data (G_OBJECT (row), "GB_VIEW");

  if (GB_IS_VIEW (view))
    {
      gtk_widget_hide (GTK_WIDGET (self->views_popover));
      gb_view_stack_set_active_view (self, GTK_WIDGET (view));
      gtk_widget_grab_focus (GTK_WIDGET (view));
    }
}
void
gb_view_stack_set_active_view (GbViewStack *self,
                               GtkWidget   *active_view)
{
  g_return_if_fail (GB_IS_VIEW_STACK (self));
  g_return_if_fail (!active_view || GB_IS_VIEW (active_view));

  if (self->destroyed)
    return;

  if (self->active_view != active_view)
    {
      if (self->active_view)
        {
          if (self->title_binding)
            g_binding_unbind (self->title_binding);
          ide_clear_weak_pointer (&self->title_binding);
          if (self->modified_binding)
            g_binding_unbind (self->modified_binding);
          ide_clear_weak_pointer (&self->modified_binding);
          gtk_label_set_label (self->title_label, NULL);
          ide_clear_weak_pointer (&self->active_view);
          gtk_widget_hide (GTK_WIDGET (self->controls_stack));
        }

      if (active_view)
        {
          GtkWidget *controls;
          GBinding *binding;
          GActionGroup *group;

          ide_set_weak_pointer (&self->active_view, active_view);
          if (active_view != gtk_stack_get_visible_child (self->stack))
            gtk_stack_set_visible_child (self->stack, active_view);

          self->focus_history = g_list_remove (self->focus_history, active_view);
          self->focus_history = g_list_prepend (self->focus_history, active_view);

          binding = g_object_bind_property (active_view, "title",
                                            self->title_label, "label",
                                            G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
          ide_set_weak_pointer (&self->title_binding, binding);

          binding = g_object_bind_property (active_view, "modified",
                                            self->modified_label, "visible",
                                            G_BINDING_SYNC_CREATE);
          ide_set_weak_pointer (&self->modified_binding, binding);

          controls = gb_view_get_controls (GB_VIEW (active_view));
          if (controls)
            {
              gtk_stack_set_visible_child (self->controls_stack, controls);
              gtk_widget_show (GTK_WIDGET (self->controls_stack));
            }

          group = gtk_widget_get_action_group (active_view, "view");
          if (group)
            gtk_widget_insert_action_group (GTK_WIDGET (self), "view", group);

          gb_view_stack_move_top_list_row (self, GB_VIEW (active_view));
        }

      g_object_notify_by_pspec (G_OBJECT (self), gParamSpecs [PROP_ACTIVE_VIEW]);
    }
}