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);
    }
}
static gboolean
focus_in_event_cb (VteTerminal    *terminal,
                   GdkEvent       *event,
                   GbTerminalView *self)
{
  const gchar *title;

  g_assert (VTE_IS_TERMINAL (terminal));
  g_assert (GB_IS_TERMINAL_VIEW (self));

  self->bottom_has_focus = (terminal != self->terminal_top);

  title = gb_terminal_get_title (GB_VIEW (self));
  if (self->document)
    gb_terminal_document_set_title (self->document, title);

  if (terminal == self->terminal_top)
    {
      self->top_has_needs_attention = FALSE;
      gb_terminal_set_needs_attention (self, FALSE, GTK_POS_TOP);
    }
  else if (terminal == self->terminal_bottom)
    {
      self->bottom_has_needs_attention = FALSE;
      gb_terminal_set_needs_attention (self, FALSE, GTK_POS_BOTTOM);
    }

  g_object_notify (G_OBJECT (self), "title");

  return GDK_EVENT_PROPAGATE;
}
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));
    }
}
/**
 * gb_view_source_buffer_modified_changed:
 * @source: (in): A #GbViewSource.
 * @buffer: (in): A #GtkTextBuffer.
 *
 * Handle the "modified-changed" signal from @buffer. Notify the view that
 * it can be saved based on the modification status of @buffer.
 */
static void
gb_view_source_buffer_modified_changed (GbViewSource  *source,
                                        GtkTextBuffer *buffer)
{
    g_return_if_fail(GB_IS_VIEW_SOURCE(source));
    g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
    gb_view_set_can_save(GB_VIEW(source),
                         gtk_text_buffer_get_modified(buffer));
}
static gboolean
set_split_view (gpointer data)
{
  g_autoptr(GbEditorView) self = data;

  g_assert (GB_IS_EDITOR_VIEW (self));

  gb_view_set_split_view (GB_VIEW (self), (self->frame2 == NULL));

  return G_SOURCE_REMOVE;
}
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__navigate_to_load_cb (GObject      *object,
                                    GAsyncResult *result,
                                    gpointer      user_data)
{
  IdeBufferManager *buffer_manager = (IdeBufferManager *)object;
  GbViewStack *self;
  g_autoptr(GTask) task = user_data;
  IdeSourceLocation *location;
  g_autoptr(IdeBuffer) buffer = NULL;
  g_autoptr(GError) error = NULL;
  GtkWidget *active_view;

  g_assert (IDE_IS_BUFFER_MANAGER (buffer_manager));

  self = g_task_get_source_object (task);
  location = g_task_get_task_data (task);

  buffer = ide_buffer_manager_load_file_finish (buffer_manager, result, &error);

  if (buffer == NULL)
    {
      /* todo: message dialog? */
      g_warning ("%s", error->message);
      return;
    }

  g_assert (GB_IS_DOCUMENT (buffer));
  g_assert (location != NULL);

  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);

  g_task_return_boolean (task, TRUE);
}
static void
gb_editor_view_get_property (GObject    *object,
                             guint       prop_id,
                             GValue     *value,
                             GParamSpec *pspec)
{
  GbEditorView *self = GB_EDITOR_VIEW (object);

  switch (prop_id)
    {
    case PROP_DOCUMENT:
      g_value_set_object (value, gb_editor_view_get_document (GB_VIEW (self)));
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    }
}
static void
on_workbench_set_focus (GbCommandProvider *provider,
                        GtkWidget         *widget,
                        GbWorkbench       *workbench)
{
  g_return_if_fail (GB_IS_COMMAND_PROVIDER (provider));
  g_return_if_fail (GB_IS_WORKBENCH (workbench));
  g_return_if_fail (!widget || GTK_IS_WIDGET (widget));

  /* walk the hierarchy to find a tab */
  if (widget)
    while (!GB_IS_VIEW (widget))
      if (!(widget = gtk_widget_get_parent (widget)))
        break;

  if (GB_IS_VIEW (widget))
    gb_command_provider_set_active_view (provider,
                                         GB_VIEW (widget));
}
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]);
    }
}
/**
 * gb_view_source_init:
 * @: (in): A #GbViewSource.
 *
 * Initializes the newly created #GbViewSource instance.
 */
static void
gb_view_source_init (GbViewSource *source)
{
    GbViewSourcePrivate *priv;
    GtkWidget *overlay;

    ENTRY;

    source->priv = priv =
                       G_TYPE_INSTANCE_GET_PRIVATE(source,
                               GB_TYPE_VIEW_SOURCE,
                               GbViewSourcePrivate);

    g_object_set(source,
                 "icon-name", GTK_STOCK_FILE,
                 "name", _("Unsaved Document"),
                 NULL);

    priv->buffer = gtk_source_buffer_new(NULL);
    g_signal_connect_swapped(priv->buffer,
                             "notify::cursor-position",
                             G_CALLBACK(gb_view_source_notify_cursor),
                             source);
    g_signal_connect_swapped(priv->buffer, "modified-changed",
                             G_CALLBACK(gb_view_source_buffer_modified_changed),
                             source);

    overlay = g_object_new(GTK_TYPE_OVERLAY,
                           "visible", TRUE,
                           NULL);
    g_signal_connect(overlay, "get-child-position",
                     G_CALLBACK(gb_view_source_position_progress),
                     NULL);
    gtk_container_add_with_properties(GTK_CONTAINER(source), overlay,
                                      "left-attach", 0,
                                      "width", 1,
                                      "top-attach", 0,
                                      "height", 1,
                                      NULL);

    priv->vpaned = g_object_new(GTK_TYPE_VPANED,
                                "visible", TRUE,
                                NULL);
    gtk_container_add(GTK_CONTAINER(overlay), priv->vpaned);

    priv->progress = g_object_new(GB_TYPE_PROGRESS_BAR,
                                  "visible", FALSE,
                                  NULL);
    gtk_overlay_add_overlay(GTK_OVERLAY(overlay), priv->progress);

    priv->scroller1 = g_object_new(gb_get_scrolled_window_gtype(),
                                   "expand", TRUE,
                                   "visible", TRUE,
                                   NULL);
    gtk_container_add_with_properties(GTK_CONTAINER(priv->vpaned), priv->scroller1,
                                      "shrink", FALSE,
                                      "resize", TRUE,
                                      NULL);

    priv->source1 = gb_view_source_create_source_view(source);
    gtk_container_add(GTK_CONTAINER(priv->scroller1), priv->source1);

    {
        GtkSourceLanguageManager *langs = gtk_source_language_manager_get_default();
        const gchar * const * ids = gtk_source_language_manager_get_language_ids(langs);
        GtkSourceLanguage *lang;
        const gchar *section;
        GHashTable *section_menu;
        GPtrArray *sorted;
        GtkWidget *controls = gb_view_get_controls(GB_VIEW(source));
        GtkWidget *menu;
        GtkWidget *mitem;
        GtkWidget *submenu;
        guint i;

        section_menu = g_hash_table_new(g_str_hash, g_str_equal);
        menu = gtk_menu_new();

        sorted = g_ptr_array_new();
        for (i = 0; ids[i]; i++) {
            lang = gtk_source_language_manager_get_language(langs, ids[i]);
            g_ptr_array_add(sorted, lang);
        }
        g_ptr_array_sort(sorted, lang_sort_func);
        for (i = 0; i < sorted->len; i++) {
            lang = g_ptr_array_index(sorted, i);
            section = gtk_source_language_get_section(lang);
            if (!(submenu = g_hash_table_lookup(section_menu, section))) {
                submenu = gtk_menu_new();
                mitem = g_object_new(GTK_TYPE_MENU_ITEM,
                                     "label", section,
                                     "submenu", submenu,
                                     "visible", TRUE,
                                     NULL);
                gtk_container_add(GTK_CONTAINER(menu), mitem);
                g_hash_table_insert(section_menu, (gchar*)section, submenu);
            }
            mitem = g_object_new(GTK_TYPE_MENU_ITEM,
                                 "label", gtk_source_language_get_name(lang),
                                 "visible", TRUE,
                                 NULL);
            g_signal_connect_swapped(mitem, "activate",
                                     G_CALLBACK(gb_view_source_lang_activate),
                                     source);
            g_object_set_data(G_OBJECT(mitem), "language-id",
                              (gchar *)gtk_source_language_get_id(lang));
            gtk_container_add(GTK_CONTAINER(submenu), mitem);
        }
        g_ptr_array_free(sorted, TRUE);

        priv->lang_combo = g_object_new(GB_TYPE_MENU_BUTTON,
                                        "hexpand", FALSE,
                                        "label", "",
                                        "menu", menu,
                                        "tooltip-text", _("Set source language."),
                                        "visible", TRUE,
                                        NULL);
        gb_widget_shrink_font(
            gb_menu_button_get_label_widget(
                GB_MENU_BUTTON(priv->lang_combo)));

        gtk_style_context_add_class(gtk_widget_get_style_context(priv->lang_combo),
                                    GB_STYLE_CLASS_TOP_BAR);
        gtk_container_add_with_properties(GTK_CONTAINER(controls), priv->lang_combo,
                                          "expand", FALSE,
                                          NULL);

        g_hash_table_unref(section_menu);
    }

    {
        GtkWidget *controls = gb_view_get_controls(GB_VIEW(source));
        GtkWidget *b;
        GtkWidget *item;
        GtkWidget *menu;
        GtkWidget *group = NULL;
        gboolean active;
        gchar *str;
        int tabs[] = { 2, 3, 4, 5, 8, 0 };
        int i;

        b = g_object_new(GB_TYPE_MENU_BUTTON,
                         "hexpand", FALSE,
                         "label", "4",
                         "tooltip-text", _("Set tab-character width."),
                         "visible", TRUE,
                         "width-request", 45,
                         NULL);

        menu = gtk_menu_new();
        for (i = 0; tabs[i]; i++) {
            str = g_strdup_printf("%d", tabs[i]);
            item = g_object_new(GTK_TYPE_RADIO_MENU_ITEM,
                                "group", group,
                                "label", str,
                                "visible", TRUE,
                                NULL);
            if (!group) {
                group = item;
            }
            active = (tabs[i] == gtk_source_view_get_tab_width(
                          GTK_SOURCE_VIEW(priv->source1)));
            g_object_set(item, "active", active, NULL);
            if (active) {
                g_object_set(b, "label", str, NULL);
            }
            g_signal_connect(item, "activate",
                             G_CALLBACK(gb_view_source_tab_size_activate),
                             source);
            gtk_container_add(GTK_CONTAINER(menu), item);
            g_free(str);
        }
        g_object_set(b, "menu", menu, NULL);

        item = g_object_new(GTK_TYPE_SEPARATOR_MENU_ITEM,
                            "visible", TRUE,
                            NULL);
        gtk_container_add(GTK_CONTAINER(menu), item);

        item = g_object_new(GTK_TYPE_CHECK_MENU_ITEM,
                            "active", TRUE,
                            "label", _("Use Spaces"),
                            "visible", TRUE,
                            NULL);
        g_signal_connect(item, "toggled",
                         G_CALLBACK(gb_view_source_spaces_toggled),
                         source);
        gtk_container_add(GTK_CONTAINER(menu), item);

        gb_widget_shrink_font(gb_menu_button_get_label_widget(GB_MENU_BUTTON(b)));
        gtk_style_context_add_class(gtk_widget_get_style_context(b),
                                    GB_STYLE_CLASS_TOP_BAR);
        gtk_container_add_with_properties(GTK_CONTAINER(controls), b,
                                          "expand", FALSE,
                                          NULL);
        priv->size_combo = b;
    }

    {
        GtkWidget *controls = gb_view_get_controls(GB_VIEW(source));
        GtkWidget *img;
        GtkWidget *b;

        img = g_object_new(GTK_TYPE_IMAGE,
                           "icon-name", "gb-split",
                           "icon-size", GTK_ICON_SIZE_MENU,
                           "visible", TRUE,
                           NULL);
        b = g_object_new(GTK_TYPE_TOGGLE_BUTTON,
                         "child", img,
                         "tooltip-text", _("Split the editor into two views."),
                         "visible", TRUE,
                         NULL);
        gtk_style_context_add_class(gtk_widget_get_style_context(b),
                                    GB_STYLE_CLASS_TOP_BAR);
        gtk_container_add_with_properties(GTK_CONTAINER(controls), b,
                                          "expand", FALSE,
                                          NULL);
        g_signal_connect(b, "toggled",
                         G_CALLBACK(split_toggled),
                         source);
    }

    {
        GtkWidget *hbox;
        GtkWidget *vp;
        GtkWidget *c;

        hbox = g_object_new(GTK_TYPE_HBOX,
                            "height-request", 30,
                            "visible", TRUE,
                            NULL);
        gtk_container_add_with_properties(GTK_CONTAINER(source), hbox,
                                          "height", 1,
                                          "left-attach", 0,
                                          "top-attach", 1,
                                          "width", 1,
                                          NULL);

        gtk_container_add(GTK_CONTAINER(hbox),
                          (c = g_object_new(GTK_TYPE_COMBO_BOX,
                                            "visible", TRUE,
                                            NULL)));
        gtk_style_context_add_class(gtk_widget_get_style_context(c),
                                    GB_STYLE_CLASS_BOTTOM_BAR);

        vp = g_object_new(GTK_TYPE_VIEWPORT,
                          "visible", TRUE,
                          NULL);
        gtk_style_context_add_class(gtk_widget_get_style_context(vp),
                                    GB_STYLE_CLASS_BOTTOM_BAR);
        gtk_container_add_with_properties(GTK_CONTAINER(hbox), vp,
                                          "expand", FALSE,
                                          "fill", TRUE,
                                          NULL);

        priv->pos_label = g_object_new(GTK_TYPE_LABEL,
                                       "label", _("Line 1, Column 1"),
                                       "visible", TRUE,
                                       "xalign", 1.0f,
                                       "xpad", 6,
                                       NULL);
        gb_widget_shrink_font(priv->pos_label);
        gtk_container_add(GTK_CONTAINER(vp), priv->pos_label);
    }

    priv->theme_handler =
        g_signal_connect_swapped(gtk_settings_get_default(),
                                 "notify::gtk-application-prefer-dark-theme",
                                 G_CALLBACK(gb_view_source_update_style),
                                 source);

    EXIT;
}
static void
gb_view_source_set_file_attribs (GbViewSource *source,
                                 GFile        *file)
{
    GtkSourceLanguageManager *lm;
    GbViewSourcePrivate *priv;
    GtkSourceLanguage *lang;
    const gchar *icon_name;
    GFileInfo *info;
    gchar *path;

    ENTRY;

    g_return_if_fail(GB_IS_VIEW_SOURCE(source));
    g_return_if_fail(G_IS_FILE(file));

    priv = source->priv;

    if (!(path = g_file_get_path(file))) {
        path = g_file_get_uri(file);
    }

    if ((icon_name = gb_path_get_icon_name(path))) {
        gb_view_set_icon_name(GB_VIEW(source), icon_name);
    }

    info = g_file_query_info(file, "standard::*", 0, NULL, NULL);

    /*
     * Get the relative path to the file from the project.
     */
    {
        const gchar *directory;
        GtkWidget *window;
        GbProject *project;

        gb_view_set_name(GB_VIEW(source), path);

        window = gtk_widget_get_toplevel(GTK_WIDGET(source));
        if (GB_IS_WINDOW(window)) {
            project = gb_window_get_project(GB_WINDOW(window));
            directory = gb_project_get_directory(project);
            if (g_str_has_prefix(path, directory)) {
                gb_view_set_name(GB_VIEW(source), path + strlen(directory));
            }
        }
    }

    lm = gtk_source_language_manager_get_default();
    lang = gtk_source_language_manager_guess_language(
               lm, path, g_file_info_get_content_type(info));
    if (lang) {
        gb_menu_button_set_label(GB_MENU_BUTTON(priv->lang_combo),
                                 gtk_source_language_get_name(lang));
    } else {
        gb_menu_button_set_label(GB_MENU_BUTTON(priv->lang_combo), "");
    }
    gtk_source_buffer_set_language(priv->buffer, lang);

    g_object_unref(info);
    g_free(path);

    EXIT;
}