static void ide_run_manager_run_cb (GObject *object, GAsyncResult *result, gpointer user_data) { IdeRunner *runner = (IdeRunner *)object; g_autoptr(GTask) task = user_data; IdeRunManager *self; GError *error = NULL; IDE_ENTRY; g_assert (IDE_IS_RUNNER (runner)); g_assert (G_IS_TASK (task)); self = g_task_get_source_object (task); if (!ide_runner_run_finish (runner, result, &error)) { g_task_return_error (task, error); IDE_GOTO (failure); } g_task_return_boolean (task, TRUE); failure: g_signal_emit (self, signals [STOPPED], 0); IDE_EXIT; }
static void ide_editor_perspective_focus_location_cb (GObject *object, GAsyncResult *result, gpointer user_data) { IdeBufferManager *bufmgr = (IdeBufferManager *)object; FocusLocation *state = user_data; GError *error = NULL; IDE_ENTRY; g_assert (IDE_IS_BUFFER_MANAGER (bufmgr)); g_assert (state != NULL); g_assert (IDE_IS_EDITOR_PERSPECTIVE (state->self)); g_assert (state->location != NULL); if (!ide_buffer_manager_load_file_finish (bufmgr, result, &error)) { /* TODO: display warning breifly to the user in the frame? */ g_warning ("%s", error->message); g_clear_error (&error); IDE_GOTO (cleanup); } /* try again now that we have loaded */ ide_editor_perspective_focus_location_full (state->self, state->location, FALSE); cleanup: g_clear_object (&state->self); g_clear_pointer (&state->location, ide_source_location_unref); g_slice_free (FocusLocation, state); IDE_EXIT; }
static void buffer_delete_range_cb (GbpGitBufferChangeMonitor *self, GtkTextIter *begin, GtkTextIter *end, IdeBuffer *buffer) { guint begin_line; g_assert (GBP_IS_GIT_BUFFER_CHANGE_MONITOR (self)); g_assert (begin != NULL); g_assert (end != NULL); g_assert (IDE_IS_BUFFER (buffer)); begin_line = gtk_text_iter_get_line (begin); /* * We need to recalculate the diff when text is deleted if: * * 1) The range includes a newline. * 2) The current line change is set to NONE. * * Technically we need to do it on every change to be more correct, but that * wastes a lot of power. So instead, we'll be a bit lazy about it here and * pick up the other changes on a much more conservative timeout, generated * by gbp_git_buffer_change_monitor__buffer_changed_cb(). */ if (begin_line != gtk_text_iter_get_line (end)) IDE_GOTO (recalculate); if (self->cache == NULL || !line_cache_get_mark (self->cache, begin_line)) IDE_GOTO (recalculate); return; recalculate: /* * We need to wait for the delete to occur, so mark it as necessary and let * gbp_git_buffer_change_monitor__buffer_delete_range_after_cb perform the * operation. */ self->delete_range_requires_recalculation = TRUE; }
static gchar * ide_xml_indenter_maybe_unindent (IdeXmlIndenter *xml, GtkTextIter *begin, GtkTextIter *end) { GtkTextIter tmp; gunichar ch; g_return_val_if_fail (IDE_IS_XML_INDENTER (xml), NULL); g_return_val_if_fail (begin, NULL); g_return_val_if_fail (end, NULL); tmp = *begin; if (!gtk_text_iter_backward_char (&tmp)) return NULL; if (('/' == gtk_text_iter_get_char (&tmp)) && gtk_text_iter_backward_char (&tmp) && ('<' == gtk_text_iter_get_char (&tmp)) && (ch = text_iter_peek_prev_char (&tmp)) && ((ch == ' ') || (ch == '\t'))) { if (ch == '\t') { gtk_text_iter_backward_char (&tmp); *begin = tmp; return g_strdup ("</"); } else { gint count = xml->indent_width; while (count > 0) { if (!gtk_text_iter_backward_char (&tmp) || !(ch = gtk_text_iter_get_char (&tmp)) || (ch != ' ')) return NULL; count--; if (count == 0) IDE_GOTO (success); } } } return NULL; success: *begin = tmp; return g_strdup ("</"); }
static void gb_terminal_view_wait_cb (GObject *object, GAsyncResult *result, gpointer user_data) { IdeSubprocess *subprocess = (IdeSubprocess *)object; VteTerminal *terminal = user_data; GbTerminalView *self; g_autoptr(GError) error = NULL; IDE_ENTRY; g_assert (IDE_IS_SUBPROCESS (subprocess)); g_assert (G_IS_ASYNC_RESULT (result)); g_assert (VTE_IS_TERMINAL (terminal)); if (!ide_subprocess_wait_finish (subprocess, result, &error)) { g_warning ("%s", error->message); IDE_GOTO (failure); } self = (GbTerminalView *)gtk_widget_get_ancestor (GTK_WIDGET (terminal), GB_TYPE_TERMINAL_VIEW); if (self == NULL) IDE_GOTO (failure); if (!ide_widget_action (GTK_WIDGET (self), "view-stack", "close", NULL)) { if (!gtk_widget_in_destruction (GTK_WIDGET (terminal))) gb_terminal_respawn (self, terminal); } failure: g_clear_object (&terminal); IDE_EXIT; }
static gchar * ide_xml_indenter_indent (IdeXmlIndenter *xml, GtkTextIter *begin, GtkTextIter *end, gint *cursor_offset) { GtkTextIter match_begin; GString *str; guint offset; g_return_val_if_fail (IDE_IS_XML_INDENTER (xml), NULL); g_return_val_if_fail (begin, NULL); g_return_val_if_fail (end, NULL); str = g_string_new (NULL); if (text_iter_backward_to_element_start (begin, &match_begin)) { offset = gtk_text_iter_get_line_offset (&match_begin); build_indent (xml, offset + xml->indent_width, &match_begin, str); /* * If immediately after our cursor is a closing tag, we will move it to * a line after our indent line. */ if ('<' == gtk_text_iter_get_char (end) && '/' == text_iter_peek_next_char (end)) { GString *str2; str2 = g_string_new (NULL); build_indent (xml, offset, &match_begin, str2); g_string_append (str, "\n"); g_string_append (str, str2->str); *cursor_offset = -str2->len - 1; g_string_free (str2, TRUE); } IDE_GOTO (cleanup); } /* do nothing */ cleanup: return g_string_free (str, (str->len == 0)); }
static gboolean text_iter_backward_to_element_start (const GtkTextIter *iter, GtkTextIter *match_begin) { GtkTextIter tmp = *iter; gboolean ret = FALSE; gint depth = 0; g_return_val_if_fail (iter, FALSE); g_return_val_if_fail (match_begin, FALSE); while (gtk_text_iter_backward_char (&tmp)) { gunichar ch; ch = gtk_text_iter_get_char (&tmp); if ((ch == '/') && (text_iter_peek_prev_char (&tmp) == '<')) { gtk_text_iter_backward_char (&tmp); depth++; } else if ((ch == '/') && (text_iter_peek_next_char (&tmp) == '>')) { depth++; } else if ((ch == '<') && (text_iter_peek_next_char (&tmp) != '!')) { depth--; if (depth < 0) { *match_begin = tmp; ret = TRUE; IDE_GOTO (cleanup); } } } cleanup: return ret; }
static gboolean text_iter_in_cdata (const GtkTextIter *location) { GtkTextIter iter = *location; gboolean ret = FALSE; if (gtk_text_iter_backward_search (&iter, "<![CDATA[", GTK_TEXT_SEARCH_TEXT_ONLY, NULL, &iter, NULL)) { if (!gtk_text_iter_forward_search (&iter, "]]>", GTK_TEXT_SEARCH_TEXT_ONLY, NULL, NULL, location)) { ret = TRUE; IDE_GOTO (cleanup); } } cleanup: return ret; }
static void ide_langserv_completion_provider_complete_cb (GObject *object, GAsyncResult *result, gpointer user_data) { IdeLangservClient *client = (IdeLangservClient *)object; g_autoptr(CompletionState) state = user_data; g_autoptr(GVariant) return_value = NULL; g_autoptr(GError) error = NULL; GVariant *node; GList *list = NULL; GVariantIter iter; IDE_ENTRY; g_assert (IDE_IS_LANGSERV_CLIENT (client)); g_assert (G_IS_ASYNC_RESULT (result)); g_assert (state != NULL); g_assert (IDE_IS_LANGSERV_COMPLETION_PROVIDER (state->self)); g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (state->context)); if (!ide_langserv_client_call_finish (client, result, &return_value, &error)) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_message ("%s", error->message); IDE_GOTO (failure); } /* * TODO: We will want to make a much more optimized version of this using * the other completion result work we've done. */ g_variant_iter_init (&iter, return_value); while (g_variant_iter_loop (&iter, "v", &node)) { g_autoptr(GtkSourceCompletionItem) item = NULL; g_autofree gchar *full_label = NULL; const gchar *label; const gchar *detail; const gchar *icon_name = NULL; gboolean success; gint64 kind = 0; success = JSONRPC_MESSAGE_PARSE (node, "label", JSONRPC_MESSAGE_GET_STRING (&label), "detail", JSONRPC_MESSAGE_GET_STRING (&detail) ); if (!success) { IDE_TRACE_MSG ("Failed to extract completion item from node"); continue; } /* Optional kind field */ JSONRPC_MESSAGE_PARSE (node, "kind", JSONRPC_MESSAGE_GET_INT64 (&kind)); kind = ide_langserv_decode_completion_kind (kind); if (kind != IDE_SYMBOL_NONE) icon_name = ide_symbol_kind_get_icon_name (kind); if (label != NULL && detail != NULL) full_label = g_strdup_printf ("%s : %s", label, detail); else full_label = g_strdup (label); //item = gtk_source_completion_item_new (full_label, label, NULL, NULL); item = g_object_new (GTK_SOURCE_TYPE_COMPLETION_ITEM, "icon-name", icon_name, "label", full_label, "text", label, NULL); list = g_list_prepend (list, g_steal_pointer (&item)); } failure: gtk_source_completion_context_add_proposals (state->context, GTK_SOURCE_COMPLETION_PROVIDER (state->self), list, TRUE); g_list_free_full (list, g_object_unref); IDE_EXIT; }
static void ide_ctags_completion_provider_populate (GtkSourceCompletionProvider *provider, GtkSourceCompletionContext *context) { IdeCtagsCompletionProvider *self = (IdeCtagsCompletionProvider *)provider; const gchar * const *allowed; g_autofree gchar *casefold = NULL; gint word_len; guint i; guint j; IDE_ENTRY; g_assert (IDE_IS_CTAGS_COMPLETION_PROVIDER (self)); g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (context)); g_clear_pointer (&self->current_word, g_free); self->current_word = ide_completion_provider_context_current_word (context); allowed = get_allowed_suffixes (context); if (self->results != NULL) { if (ide_completion_results_replay (self->results, self->current_word)) { ide_completion_results_present (self->results, provider, context); IDE_EXIT; } g_clear_pointer (&self->results, g_object_unref); } word_len = strlen (self->current_word); if (word_len < self->minimum_word_size) IDE_GOTO (word_too_small); casefold = g_utf8_casefold (self->current_word, -1); self->results = ide_completion_results_new (self->current_word); for (i = 0; i < self->indexes->len; i++) { g_autofree gchar *copy = g_strdup (self->current_word); IdeCtagsIndex *index = g_ptr_array_index (self->indexes, i); const IdeCtagsIndexEntry *entries = NULL; const gchar *last_name = NULL; guint tmp_len = word_len; gsize n_entries = 0; while (entries == NULL && *copy) { if (!(entries = ide_ctags_index_lookup_prefix (index, copy, &n_entries))) copy [--tmp_len] = '\0'; } if ((entries == NULL) || (n_entries == 0)) continue; for (j = 0; j < n_entries; j++) { const IdeCtagsIndexEntry *entry = &entries [j]; IdeCtagsCompletionItem *item; if (ide_str_equal0 (entry->name, last_name)) continue; last_name = entry->name; if (!ide_ctags_is_allowed (entry, allowed)) continue; item = ide_ctags_completion_item_new (self, entry); if (!ide_completion_item_match (IDE_COMPLETION_ITEM (item), self->current_word, casefold)) { g_object_unref (item); continue; } ide_completion_results_take_proposal (self->results, IDE_COMPLETION_ITEM (item)); } } ide_completion_results_present (self->results, provider, context); IDE_EXIT; word_too_small: gtk_source_completion_context_add_proposals (context, provider, NULL, TRUE); IDE_EXIT; }
static void gb_terminal_respawn (GbTerminalView *self, VteTerminal *terminal) { g_autoptr(GPtrArray) args = NULL; g_autoptr(IdeSubprocess) subprocess = NULL; g_autoptr(IdeSubprocessLauncher) launcher = NULL; g_autofree gchar *workpath = NULL; g_autofree gchar *shell = NULL; GtkWidget *toplevel; GError *error = NULL; IdeContext *context; IdeVcs *vcs; VtePty *pty = NULL; GFile *workdir; gint64 now; int tty_fd = -1; gint stdout_fd = -1; gint stderr_fd = -1; IDE_ENTRY; g_assert (GB_IS_TERMINAL_VIEW (self)); vte_terminal_reset (terminal, TRUE, TRUE); toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self)); if (!IDE_IS_WORKBENCH (toplevel)) IDE_EXIT; /* Prevent flapping */ now = g_get_monotonic_time (); if ((now - self->last_respawn) < (G_USEC_PER_SEC / 10)) IDE_EXIT; self->last_respawn = now; context = ide_workbench_get_context (IDE_WORKBENCH (toplevel)); vcs = ide_context_get_vcs (context); workdir = ide_vcs_get_working_directory (vcs); workpath = g_file_get_path (workdir); shell = gb_terminal_view_discover_shell (NULL, &error); if (shell == NULL) { g_warning ("Failed to discover user shell: %s", error->message); /* We prefer bash in flatpak over sh */ if (ide_is_flatpak ()) shell = g_strdup ("/bin/bash"); else shell = vte_get_user_shell (); g_clear_error (&error); } args = g_ptr_array_new (); g_ptr_array_add (args, (gchar *)shell); g_ptr_array_add (args, NULL); pty = vte_terminal_pty_new_sync (terminal, VTE_PTY_DEFAULT | VTE_PTY_NO_LASTLOG | VTE_PTY_NO_UTMP | VTE_PTY_NO_WTMP, NULL, &error); if (pty == NULL) IDE_GOTO (failure); vte_terminal_set_pty (terminal, pty); if (-1 == (tty_fd = gb_vte_pty_create_slave (pty))) IDE_GOTO (failure); /* dup() is safe as it will inherit O_CLOEXEC */ if (-1 == (stdout_fd = dup (tty_fd)) || -1 == (stderr_fd = dup (tty_fd))) IDE_GOTO (failure); /* XXX: It would be nice to allow using the runtimes launcher */ launcher = ide_subprocess_launcher_new (0); ide_subprocess_launcher_set_run_on_host (launcher, TRUE); ide_subprocess_launcher_set_clear_env (launcher, FALSE); ide_subprocess_launcher_set_cwd (launcher, workpath); ide_subprocess_launcher_push_args (launcher, (const gchar * const *)args->pdata); ide_subprocess_launcher_take_stdin_fd (launcher, tty_fd); ide_subprocess_launcher_take_stdout_fd (launcher, stdout_fd); ide_subprocess_launcher_take_stderr_fd (launcher, stderr_fd); ide_subprocess_launcher_setenv (launcher, "TERM", "xterm-256color", TRUE); ide_subprocess_launcher_setenv (launcher, "INSIDE_GNOME_BUILDER", PACKAGE_VERSION, TRUE); ide_subprocess_launcher_setenv (launcher, "SHELL", shell, TRUE); tty_fd = -1; stdout_fd = -1; stderr_fd = -1; if (NULL == (subprocess = ide_subprocess_launcher_spawn (launcher, NULL, &error))) IDE_GOTO (failure); ide_subprocess_wait_async (subprocess, NULL, gb_terminal_view_wait_cb, g_object_ref (terminal)); failure: if (tty_fd != -1) close (tty_fd); if (stdout_fd != -1) close (stdout_fd); g_clear_object (&pty); if (error != NULL) { g_warning ("%s", error->message); g_clear_error (&error); } IDE_EXIT; }
static gboolean ide_highlight_engine_tick (IdeHighlightEngine *self) { GtkTextBuffer *buffer; GtkTextIter iter; GtkTextIter invalid_begin; GtkTextIter invalid_end; GSList *tags_iter; IDE_PROBE; g_assert (IDE_IS_HIGHLIGHT_ENGINE (self)); g_assert (self->buffer != NULL); g_assert (self->highlighter != NULL); g_assert (self->invalid_begin != NULL); g_assert (self->invalid_end != NULL); self->quanta_expiration = g_get_monotonic_time () + HIGHLIGHT_QUANTA_USEC; buffer = GTK_TEXT_BUFFER (self->buffer); gtk_text_buffer_get_iter_at_mark (buffer, &invalid_begin, self->invalid_begin); gtk_text_buffer_get_iter_at_mark (buffer, &invalid_end, self->invalid_end); IDE_TRACE_MSG ("Highlight Range [%u:%u,%u:%u] (%s)", gtk_text_iter_get_line (&invalid_begin), gtk_text_iter_get_line_offset (&invalid_begin), gtk_text_iter_get_line (&invalid_end), gtk_text_iter_get_line_offset (&invalid_end), G_OBJECT_TYPE_NAME (self->highlighter)); if (gtk_text_iter_compare (&invalid_begin, &invalid_end) >= 0) IDE_GOTO (up_to_date); /*Clear all our tags*/ for (tags_iter = self->private_tags; tags_iter; tags_iter = tags_iter->next) gtk_text_buffer_remove_tag (buffer, GTK_TEXT_TAG (tags_iter->data), &invalid_begin, &invalid_end); iter = invalid_begin; ide_highlighter_update (self->highlighter, ide_highlight_engine_apply_style, &invalid_begin, &invalid_end, &iter); if (gtk_text_iter_compare (&iter, &invalid_end) >= 0) IDE_GOTO (up_to_date); /* Stop processing until further instruction if no movement was made */ if (gtk_text_iter_equal (&iter, &invalid_begin)) return FALSE; gtk_text_buffer_move_mark (buffer, self->invalid_begin, &iter); return TRUE; up_to_date: gtk_text_buffer_get_start_iter (buffer, &iter); gtk_text_buffer_move_mark (buffer, self->invalid_begin, &iter); gtk_text_buffer_move_mark (buffer, self->invalid_end, &iter); return FALSE; }
static void ide_ctags_completion_provider_populate (GtkSourceCompletionProvider *provider, GtkSourceCompletionContext *context) { IdeCtagsCompletionProvider *self = (IdeCtagsCompletionProvider *)provider; g_autofree gchar *word = NULL; const IdeCtagsIndexEntry *entries; const gchar * const *allowed; g_autoptr(GPtrArray) ar = NULL; IdeCtagsIndexEntry *last = NULL; GtkSourceBuffer *buffer; gsize n_entries; GtkTextIter iter; GList *list = NULL; gsize i; gsize j; IDE_ENTRY; g_assert (IDE_IS_CTAGS_COMPLETION_PROVIDER (self)); g_assert (GTK_SOURCE_IS_COMPLETION_CONTEXT (context)); if (self->indexes->len == 0) IDE_GOTO (failure); if (!g_settings_get_boolean (self->settings, "ctags-autocompletion")) IDE_GOTO (failure); if (!gtk_source_completion_context_get_iter (context, &iter)) IDE_GOTO (failure); buffer = GTK_SOURCE_BUFFER (gtk_text_iter_get_buffer (&iter)); allowed = get_allowed_suffixes (buffer); word = get_word_to_cursor (&iter); if (ide_str_empty0 (word) || strlen (word) < self->minimum_word_size) IDE_GOTO (failure); if (strlen (word) < 3) IDE_GOTO (failure); ar = g_ptr_array_new (); IDE_TRACE_MSG ("Searching for %s", word); for (j = 0; j < self->indexes->len; j++) { IdeCtagsIndex *index = g_ptr_array_index (self->indexes, j); entries = ide_ctags_index_lookup_prefix (index, word, &n_entries); if ((entries == NULL) || (n_entries == 0)) continue; for (i = 0; i < n_entries; i++) { const IdeCtagsIndexEntry *entry = &entries [i]; if (is_allowed (entry, allowed)) g_ptr_array_add (ar, (gpointer)entry); } } g_ptr_array_sort (ar, sort_wrapper); for (i = ar->len; i > 0; i--) { GtkSourceCompletionProposal *item; IdeCtagsIndexEntry *entry = g_ptr_array_index (ar, i - 1); /* * NOTE: * * We walk backwards in this ptrarray so that we can use g_list_prepend() for O(1) access. * I think everyone agrees that using GList for passing completion data around was not * a great choice, but it is what we have to work with. */ /* * Ignore this item if the previous one looks really similar. * We take the first item instead of the last since the first item (when walking backwards) * tends to be more likely to be the one we care about (based on lexicographical * ordering. For example, something in "gtk-2.0" is less useful than "gtk-3.0". * * This is done here instead of during our initial object creation so that * we can merge items between different indexes. It often happens that the * same headers are included in multiple tags files. */ if ((last != NULL) && too_similar (entry, last)) continue; /* * NOTE: * * Autocompletion is very performance sensitive code. The smallest amount of * extra work has a very negative impact on interactivity. We are trying to * avoid a couple things here based on how completion works. * * 1) Avoiding referencing or copying things. * Since the provider will always outlive the completion item, we use * borrowed references for as much as we can. * 2) We delay the work of looking up icons until they are requested. * No sense in doing that work before hand. */ item = ide_ctags_completion_item_new (entry, self, context); list = g_list_prepend (list, item); last = entry; } failure: gtk_source_completion_context_add_proposals (context, provider, list, TRUE); g_list_free_full (list, g_object_unref); IDE_EXIT; }
static void ide_frame_pan_end (IdeFrame *self, GdkEventSequence *sequence, GtkGesturePan *gesture) { IdeFramePrivate *priv = ide_frame_get_instance_private (self); IdeFramePrivate *dest_priv; IdeFrame *dest; GtkAllocation alloc; GtkWidget *grid; GtkWidget *column; gdouble x, y; gint direction; gint index = 0; IDE_ENTRY; g_assert (IDE_IS_FRAME (self)); g_assert (GTK_IS_GESTURE_PAN (gesture)); if (priv->pan_theatric == NULL || priv->pan_page == NULL) IDE_GOTO (cleanup); gtk_widget_get_allocation (GTK_WIDGET (self), &alloc); gtk_gesture_drag_get_offset (GTK_GESTURE_DRAG (gesture), &x, &y); if (x > DISTANCE_THRESHOLD (&alloc)) direction = 1; else if (x < -DISTANCE_THRESHOLD (&alloc)) direction = -1; else direction = 0; grid = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_GRID); g_assert (grid != NULL); g_assert (IDE_IS_GRID (grid)); column = gtk_widget_get_ancestor (GTK_WIDGET (self), IDE_TYPE_GRID_COLUMN); g_assert (column != NULL); g_assert (IDE_IS_GRID_COLUMN (column)); gtk_container_child_get (GTK_CONTAINER (grid), GTK_WIDGET (column), "index", &index, NULL); dest = _ide_grid_get_nth_stack (IDE_GRID (grid), index + direction); dest_priv = ide_frame_get_instance_private (dest); g_assert (dest != NULL); g_assert (IDE_IS_FRAME (dest)); gtk_widget_get_allocation (GTK_WIDGET (dest), &alloc); if (!is_uninitialized (&alloc)) { AnimationState *state; state = g_slice_new0 (AnimationState); state->source = g_object_ref (self); state->dest = g_object_ref (dest); state->page = g_object_ref (priv->pan_page); state->theatric = g_object_ref (priv->pan_theatric); gtk_widget_translate_coordinates (GTK_WIDGET (dest_priv->top_stack), grid, 0, 0, &alloc.x, &alloc.y); /* * Use EASE_OUT_CUBIC, because user initiated the beginning of the * acceleration curve just by swiping. No need to duplicate. */ dzl_object_animate_full (state->theatric, DZL_ANIMATION_EASE_OUT_CUBIC, TRANSITION_DURATION, gtk_widget_get_frame_clock (GTK_WIDGET (self)), animation_state_complete, state, "x", alloc.x, "width", alloc.width, NULL); if (dest != self) { g_ptr_array_add (priv->in_transition, g_object_ref (priv->pan_page)); gtk_container_remove (GTK_CONTAINER (priv->stack), GTK_WIDGET (priv->pan_page)); } IDE_TRACE_MSG ("Animating transition to %s column", dest != self ? "another" : "same"); } else { g_autoptr(IdePage) page = g_object_ref (priv->pan_page); IDE_TRACE_MSG ("Moving page to a previously non-existant column"); gtk_container_remove (GTK_CONTAINER (priv->stack), GTK_WIDGET (page)); gtk_widget_show (GTK_WIDGET (page)); gtk_container_add (GTK_CONTAINER (dest_priv->stack), GTK_WIDGET (page)); } cleanup: g_clear_object (&priv->pan_theatric); g_clear_object (&priv->pan_page); gtk_widget_queue_draw (gtk_widget_get_toplevel (GTK_WIDGET (self))); ide_frame_set_cursor (self, "arrow"); IDE_EXIT; }