[hotssh] Implement tabs
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [hotssh] Implement tabs
- Date: Thu, 17 Oct 2013 03:48:41 +0000 (UTC)
commit 137a328d689c8e8b9082eb8f5b7d1ec650d60088
Author: Colin Walters <walters verbum org>
Date: Wed Oct 16 19:17:18 2013 -0400
Implement tabs
Everyone loves tabs!
Makefile-src.am | 8 +-
src/gears-menu.ui | 8 +-
src/hotssh-app.c | 2 +-
src/hotssh-tab.c | 616 ++++++++++++++++++++++++++++++++++++++++++++++
src/hotssh-tab.h | 33 +++
src/hotssh-win.c | 601 ++++++--------------------------------------
src/hotssh-win.h | 4 -
src/hotssh.gresource.xml | 1 +
src/tab.ui | 418 +++++++++++++++++++++++++++++++
src/window.ui | 423 +------------------------------
10 files changed, 1174 insertions(+), 940 deletions(-)
---
diff --git a/Makefile-src.am b/Makefile-src.am
index 9dc230a..7155d00 100644
--- a/Makefile-src.am
+++ b/Makefile-src.am
@@ -1,10 +1,16 @@
bin_PROGRAMS += hotssh
-hotssh_headers = $(addprefix src/,hotssh-app.h hotssh-win.h hotssh-prefs.h)
+hotssh_headers = $(addprefix src/, \
+ hotssh-app.h \
+ hotssh-tab.h \
+ hotssh-win.h \
+ hotssh-prefs.h \
+ )
hotssh_SOURCES = $(hotssh_headers) \
src/main.c \
src/hotssh-app.c \
+ src/hotssh-tab.c \
src/hotssh-win.c \
src/hotssh-prefs.c \
$(NULL)
diff --git a/src/gears-menu.ui b/src/gears-menu.ui
index fb0e929..3c5c710 100644
--- a/src/gears-menu.ui
+++ b/src/gears-menu.ui
@@ -4,12 +4,8 @@
<menu id="menu">
<section>
<item>
- <attribute name="label" translatable="yes">_Words</attribute>
- <attribute name="action">win.show-words</attribute>
- </item>
- <item>
- <attribute name="label" translatable="yes">_Lines</attribute>
- <attribute name="action">win.show-lines</attribute>
+ <attribute name="label" translatable="yes">_New Tab</attribute>
+ <attribute name="action">win.new-tab</attribute>
</item>
</section>
</menu>
diff --git a/src/hotssh-app.c b/src/hotssh-app.c
index 2fd3a99..be6451c 100644
--- a/src/hotssh-app.c
+++ b/src/hotssh-app.c
@@ -147,7 +147,7 @@ hotssh_app_command_line (GApplication *app,
g_option_context_free (context);
win = hotssh_window_new (HOTSSH_APP (app));
- hotssh_window_prefill (win, host, username);
+ gtk_widget_show_all ((GtkWidget*)win);
gtk_window_present (GTK_WINDOW (win));
return 0;
diff --git a/src/hotssh-tab.c b/src/hotssh-tab.c
new file mode 100644
index 0000000..734c199
--- /dev/null
+++ b/src/hotssh-tab.c
@@ -0,0 +1,616 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2013 Colin Walters <walters verbum org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "hotssh-tab.h"
+#include "gssh.h"
+
+#include "libgsystem.h"
+
+#include <libssh2.h>
+#include <vte/vte.h>
+#include <stdio.h>
+
+struct _HotSshTab
+{
+ GtkNotebook parent;
+};
+
+struct _HotSshTabClass
+{
+ GtkNotebookClass parent_class;
+};
+
+typedef struct _HotSshTabPrivate HotSshTabPrivate;
+
+typedef enum {
+ HOTSSH_TAB_PAGE_NEW_CONNECTION,
+ HOTSSH_TAB_PAGE_INTERSTITAL,
+ HOTSSH_TAB_PAGE_AUTH,
+ HOTSSH_TAB_PAGE_TERMINAL
+} HotSshTabPage;
+
+struct _HotSshTabPrivate
+{
+ GtkWidget *terminal;
+
+ /* Bound via template */
+ GtkWidget *host_entry;
+ GtkWidget *username_entry;
+ GtkWidget *connect_button;
+ GtkWidget *connection_text_container;
+ GtkWidget *connection_text;
+ GtkWidget *password_container;
+ GtkWidget *password_entry;
+ GtkWidget *password_submit;
+ GtkWidget *connect_cancel_button;
+ GtkWidget *auth_cancel_button;
+ GtkWidget *hostkey_container;
+ GtkWidget *hostkey_fingerprint_label;
+ GtkWidget *approve_hostkey_button;
+ GtkWidget *disconnect_button;
+ GtkWidget *terminal_box;
+
+ /* State */
+ HotSshTabPage active_page;
+
+ GSocketConnectable *address;
+ GSshConnection *connection;
+ GSshChannel *channel;
+
+ gboolean need_pty_size_request;
+ gboolean sent_pty_size_request;
+ gboolean have_outstanding_write;
+ gboolean have_outstanding_auth;
+ GQueue write_queue;
+
+ GCancellable *cancellable;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(HotSshTab, hotssh_tab, GTK_TYPE_NOTEBOOK);
+
+static void
+set_status (HotSshTab *self,
+ const char *text)
+{
+ HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+ gtk_label_set_text ((GtkLabel*)priv->connection_text, text);
+}
+
+static void
+reset_focus_state (HotSshTab *self)
+{
+ HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+ if (gtk_entry_get_text ((GtkEntry*)priv->host_entry)[0] == '\0')
+ gtk_widget_grab_focus (priv->host_entry);
+ else
+ gtk_widget_grab_focus (priv->username_entry);
+}
+
+static void
+state_reset_for_new_connection (HotSshTab *self)
+{
+ HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+ g_debug ("reset state");
+ g_clear_object (&priv->address);
+ g_clear_object (&priv->connection);
+ g_clear_object (&priv->cancellable);
+ vte_terminal_reset ((VteTerminal*)priv->terminal, TRUE, TRUE);
+ gtk_entry_set_text ((GtkEntry*)priv->password_entry, "");
+ reset_focus_state (self);
+ gtk_label_set_text ((GtkLabel*)priv->connection_text, "");
+ gtk_widget_show (priv->connection_text_container);
+ gtk_widget_hide (priv->hostkey_container);
+ gtk_widget_set_sensitive (priv->password_container, TRUE);
+ g_debug ("reset state done");
+}
+
+static void
+page_transition (HotSshTab *self,
+ HotSshTabPage new_page)
+{
+ HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+
+ if (new_page == priv->active_page)
+ return;
+
+ g_debug ("PAGE: %d => %d", priv->active_page, new_page);
+ g_assert (new_page >= HOTSSH_TAB_PAGE_NEW_CONNECTION &&
+ new_page <= HOTSSH_TAB_PAGE_TERMINAL);
+ priv->active_page = new_page;
+
+ if (priv->active_page == HOTSSH_TAB_PAGE_NEW_CONNECTION)
+ state_reset_for_new_connection (self);
+
+ gtk_notebook_set_current_page ((GtkNotebook*)self, (guint)new_page);
+
+ if (priv->active_page == HOTSSH_TAB_PAGE_TERMINAL)
+ gtk_widget_grab_focus ((GtkWidget*)priv->terminal);
+}
+
+static void
+page_transition_take_error (HotSshTab *self,
+ GError *error)
+{
+ set_status (self, error->message);
+ g_error_free (error);
+}
+
+static void
+on_istream_read_complete (GObject *src,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ HotSshTab *self = user_data;
+ HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+ GBytes *result = NULL;
+ GError *local_error = NULL;
+ const guint8 *buf;
+ gsize len;
+
+ result = g_input_stream_read_bytes_finish ((GInputStream*)src, res, &local_error);
+ if (!result)
+ goto out;
+
+ buf = g_bytes_get_data (result, &len);
+ g_debug ("read %u bytes", (guint)len);
+
+ vte_terminal_feed ((VteTerminal*)priv->terminal, (char*)buf, len);
+
+ g_input_stream_read_bytes_async ((GInputStream*)src, 8192, G_PRIORITY_DEFAULT,
+ priv->cancellable, on_istream_read_complete, self);
+
+ out:
+ if (local_error)
+ page_transition_take_error (self, local_error);
+}
+
+static void
+on_open_shell_complete (GObject *src,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ HotSshTab *self = user_data;
+ HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+ GError *local_error = NULL;
+ GInputStream *istream;
+
+ g_debug ("open shell complete");
+
+ priv->channel = gssh_connection_open_shell_finish ((GSshConnection*)src,
+ res, &local_error);
+ if (!priv->channel)
+ goto out;
+
+ page_transition (self, HOTSSH_TAB_PAGE_TERMINAL);
+
+ istream = g_io_stream_get_input_stream ((GIOStream*)priv->channel);
+
+ g_input_stream_read_bytes_async (istream, 8192, G_PRIORITY_DEFAULT,
+ priv->cancellable, on_istream_read_complete, self);
+
+ out:
+ if (local_error)
+ page_transition_take_error (self, local_error);
+}
+
+static void
+iterate_authentication_modes (HotSshTab *self);
+
+static void
+on_connection_state_notify (GSshConnection *conn,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ HotSshTab *self = HOTSSH_TAB (user_data);
+ HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+ GSshConnectionState new_state = gssh_connection_get_state (conn);
+
+ g_debug ("connection state: %u", new_state);
+
+ switch (new_state)
+ {
+ case GSSH_CONNECTION_STATE_DISCONNECTED:
+ page_transition (self, HOTSSH_TAB_PAGE_NEW_CONNECTION);
+ break;
+ case GSSH_CONNECTION_STATE_CONNECTING:
+ case GSSH_CONNECTION_STATE_HANDSHAKING:
+ case GSSH_CONNECTION_STATE_PREAUTH:
+ page_transition (self, HOTSSH_TAB_PAGE_INTERSTITAL);
+ break;
+ case GSSH_CONNECTION_STATE_AUTHENTICATION_REQUIRED:
+ page_transition (self, HOTSSH_TAB_PAGE_AUTH);
+ iterate_authentication_modes (self);
+ break;
+ case GSSH_CONNECTION_STATE_ERROR:
+ gtk_widget_hide (priv->hostkey_container);
+ gtk_widget_show (priv->connection_text_container);
+ page_transition (self, HOTSSH_TAB_PAGE_INTERSTITAL);
+ break;
+ case GSSH_CONNECTION_STATE_CONNECTED:
+ gssh_connection_open_shell_async (priv->connection, priv->cancellable,
+ on_open_shell_complete, self);
+ break;
+ }
+}
+
+static void
+on_password_auth_complete (GObject *src,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ HotSshTab *self = HOTSSH_TAB (user_data);
+ HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+ GError *local_error = NULL;
+
+ priv->have_outstanding_auth = FALSE;
+
+ if (!gssh_connection_auth_password_finish ((GSshConnection*)src, res, &local_error))
+ goto out;
+
+ g_debug ("password auth complete");
+
+ out:
+ if (local_error)
+ page_transition_take_error (self, local_error);
+}
+
+static void
+iterate_authentication_modes (HotSshTab *self)
+{
+ HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+ const char *const *authschemes =
+ gssh_connection_get_authentication_mechanisms (priv->connection);
+ const char *const*iter;
+ GError *local_error = NULL;
+
+ if (priv->have_outstanding_auth)
+ return;
+
+ for (iter = authschemes; iter && *iter; iter++)
+ {
+ const char *authscheme = *iter;
+ if (strcmp (authscheme, "password") == 0)
+ {
+ const char *password = gtk_entry_get_text ((GtkEntry*)priv->password_entry);
+ if (password && password[0])
+ {
+ gssh_connection_auth_password_async (priv->connection, password,
+ priv->cancellable,
+ on_password_auth_complete, self);
+ priv->have_outstanding_auth = TRUE;
+ break;
+ }
+ }
+ }
+
+ g_set_error (&local_error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "No more authentication mechanisms available");
+ page_transition_take_error (self, local_error);
+}
+
+static void
+on_connection_handshake (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ HotSshTab *self = HOTSSH_TAB (user_data);
+ HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+ GError *local_error = NULL;
+ GError **error = &local_error;
+ GBytes *hostkey_sha1_binary;
+ GString *buf;
+ guint i;
+ const guint8 *binbuf;
+ gsize len;
+ gs_free char *hostkey_sha1_text = NULL;
+
+ if (!gssh_connection_handshake_finish ((GSshConnection*)object, result, error))
+ goto out;
+
+ g_debug ("handshake complete");
+
+ hostkey_sha1_binary = gssh_connection_preauth_get_fingerprint_sha1 (priv->connection);
+ binbuf = g_bytes_get_data (hostkey_sha1_binary, &len);
+ buf = g_string_new ("");
+ for (i = 0; i < len; i++)
+ {
+ g_string_append_printf (buf, "%02X", binbuf[i]);
+ if (i < len - 1)
+ g_string_append_c (buf, ':');
+ }
+ hostkey_sha1_text = g_string_free (buf, FALSE);
+
+ gtk_label_set_text ((GtkLabel*)priv->hostkey_fingerprint_label,
+ hostkey_sha1_text);
+ gtk_widget_hide (priv->connection_text_container);
+ gtk_widget_show (priv->hostkey_container);
+ page_transition (self, HOTSSH_TAB_PAGE_INTERSTITAL);
+
+ out:
+ if (local_error)
+ page_transition_take_error (self, local_error);
+}
+
+static void
+on_connect (GtkButton *button,
+ HotSshTab *self)
+{
+ HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+ GError *local_error = NULL;
+ GError **error = &local_error;
+ const char *hostname;
+ const char *username;
+
+ page_transition (self, HOTSSH_TAB_PAGE_INTERSTITAL);
+ gtk_notebook_set_current_page ((GtkNotebook*)self, 1);
+
+ hostname = gtk_entry_get_text (GTK_ENTRY (priv->host_entry));
+ username = gtk_entry_get_text (GTK_ENTRY (priv->username_entry));
+
+ g_clear_object (&priv->cancellable);
+ priv->cancellable = g_cancellable_new ();
+
+ g_clear_object (&priv->address);
+ priv->address = g_network_address_parse (hostname, 22, error);
+ if (!priv->address)
+ {
+ page_transition_take_error (self, local_error);
+ return;
+ }
+
+ g_clear_object (&priv->connection);
+ priv->connection = gssh_connection_new (priv->address, username);
+ g_signal_connect_object (priv->connection, "notify::state",
+ G_CALLBACK (on_connection_state_notify),
+ self, 0);
+ g_debug ("connected, beginning handshake");
+ gssh_connection_handshake_async (priv->connection, priv->cancellable,
+ on_connection_handshake, self);
+}
+
+static void
+on_disconnect (GtkButton *button,
+ HotSshTab *self)
+{
+ HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+ page_transition (self, HOTSSH_TAB_PAGE_NEW_CONNECTION);
+ gtk_notebook_set_current_page ((GtkNotebook*)self, 0);
+}
+
+static void
+process_write_queue (HotSshTab *self);
+
+static void
+on_ostream_write_complete (GObject *src,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ HotSshTab *self = user_data;
+ HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+ gssize result;
+ GError *local_error = NULL;
+ GBytes *buf;
+ gsize bufsize;
+
+ priv->have_outstanding_write = FALSE;
+
+ result = g_output_stream_write_bytes_finish ((GOutputStream*)src, res, &local_error);
+ if (result < 0)
+ goto out;
+
+ buf = g_queue_pop_head (&priv->write_queue);
+ g_assert (buf != NULL);
+ bufsize = g_bytes_get_size (buf);
+
+ if (result == bufsize)
+ ;
+ else
+ {
+ GBytes *subbuf;
+ g_assert (result < bufsize);
+ subbuf = g_bytes_new_from_bytes (buf, result, bufsize - result);
+ g_queue_push_head (&priv->write_queue, subbuf);
+ }
+
+ process_write_queue (self);
+
+ out:
+ if (local_error)
+ page_transition_take_error (self, local_error);
+}
+
+static void
+process_write_queue (HotSshTab *self)
+{
+ HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+ if (!priv->have_outstanding_write && priv->write_queue.length > 0)
+ {
+ GOutputStream *ostream = g_io_stream_get_output_stream ((GIOStream*)priv->channel);
+ GBytes *buf = g_queue_peek_head (&priv->write_queue);
+
+ g_output_stream_write_bytes_async (ostream, buf, G_PRIORITY_DEFAULT,
+ priv->cancellable, on_ostream_write_complete, self);
+ priv->have_outstanding_write = TRUE;
+ }
+}
+
+static void
+on_terminal_commit (VteTerminal *vteterminal,
+ gchar *text,
+ guint size,
+ gpointer user_data)
+{
+ HotSshTab *self = user_data;
+ HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+
+ g_queue_push_tail (&priv->write_queue, g_bytes_new (text, size));
+ process_write_queue (self);
+}
+
+static void
+submit_password (HotSshTab *self)
+{
+ HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+
+ g_debug ("password submit");
+
+ gtk_widget_set_sensitive (priv->password_container, FALSE);
+
+ iterate_authentication_modes (self);
+}
+
+static void
+on_connect_cancel (GtkButton *button,
+ gpointer user_data)
+{
+ HotSshTab *self = user_data;
+ page_transition (self, HOTSSH_TAB_PAGE_NEW_CONNECTION);
+}
+
+static void
+on_approve_hostkey_clicked (GtkButton *button,
+ gpointer user_data)
+{
+ HotSshTab *self = user_data;
+ HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+
+ gssh_connection_preauth_continue (priv->connection);
+}
+
+static void
+send_pty_size_request (HotSshTab *self);
+
+static void
+on_pty_size_complete (GObject *src,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ HotSshTab *self = user_data;
+ HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+ GError *local_error = NULL;
+
+ g_debug ("pty size request complete");
+ priv->sent_pty_size_request = FALSE;
+
+ if (!gssh_channel_request_pty_size_finish (priv->channel, result, &local_error))
+ goto out;
+
+ if (priv->need_pty_size_request)
+ send_pty_size_request (self);
+
+ out:
+ if (local_error)
+ page_transition_take_error (self, local_error);
+}
+
+static void
+send_pty_size_request (HotSshTab *self)
+{
+ HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+ guint width = vte_terminal_get_column_count ((VteTerminal*)priv->terminal);
+ guint height = vte_terminal_get_row_count ((VteTerminal*)priv->terminal);
+
+ priv->need_pty_size_request = FALSE;
+ priv->sent_pty_size_request = TRUE;
+ gssh_channel_request_pty_size_async (priv->channel, width, height,
+ priv->cancellable, on_pty_size_complete, self);
+}
+
+static void
+on_terminal_size_allocate (GtkWidget *widget,
+ GdkRectangle *allocation,
+ gpointer user_data)
+{
+ HotSshTab *self = user_data;
+ HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+
+ if (priv->channel)
+ {
+ priv->need_pty_size_request = TRUE;
+ if (!priv->sent_pty_size_request)
+ send_pty_size_request (self);
+ }
+}
+
+static void
+hotssh_tab_init (HotSshTab *self)
+{
+ HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ g_signal_connect (priv->connect_button, "clicked", G_CALLBACK (on_connect), self);
+ g_signal_connect (priv->connect_cancel_button, "clicked", G_CALLBACK (on_connect_cancel), self);
+ g_signal_connect (priv->auth_cancel_button, "clicked", G_CALLBACK (on_connect_cancel), self);
+ g_signal_connect (priv->approve_hostkey_button, "clicked", G_CALLBACK (on_approve_hostkey_clicked), self);
+ g_signal_connect (priv->disconnect_button, "clicked", G_CALLBACK (on_disconnect), self);
+ g_signal_connect_swapped (priv->password_entry, "activate", G_CALLBACK (submit_password), self);
+ g_signal_connect_swapped (priv->password_submit, "clicked", G_CALLBACK (submit_password), self);
+
+ priv->terminal = vte_terminal_new ();
+ g_signal_connect ((GObject*)priv->terminal, "size-allocate", G_CALLBACK (on_terminal_size_allocate), self);
+ g_signal_connect ((GObject*)priv->terminal, "commit", G_CALLBACK (on_terminal_commit), self);
+ gtk_box_pack_start ((GtkBox*)priv->terminal_box, (GtkWidget*)priv->terminal, TRUE, TRUE, 0);
+ gtk_widget_show_all (priv->terminal_box);
+
+ g_queue_init (&priv->write_queue);
+}
+
+static void
+hotssh_tab_dispose (GObject *object)
+{
+ HotSshTab *self = HOTSSH_TAB (object);
+ HotSshTabPrivate *priv = hotssh_tab_get_instance_private (self);
+
+ page_transition (self, HOTSSH_TAB_PAGE_NEW_CONNECTION);
+
+ G_OBJECT_CLASS (hotssh_tab_parent_class)->dispose (object);
+}
+
+static void
+hotssh_tab_class_init (HotSshTabClass *class)
+{
+ G_OBJECT_CLASS (class)->dispose = hotssh_tab_dispose;
+
+ gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),
+ "/org/gnome/hotssh/tab.ui");
+
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab, host_entry);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab, username_entry);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab, connect_button);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab,
connection_text_container);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab, connection_text);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab, password_container);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab, password_entry);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab, password_submit);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab, connect_cancel_button);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab, auth_cancel_button);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab, hostkey_container);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab,
hostkey_fingerprint_label);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab, approve_hostkey_button);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab, disconnect_button);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshTab, terminal_box);
+}
+
+HotSshTab *
+hotssh_tab_new (void)
+{
+ return g_object_new (HOTSSH_TYPE_TAB, NULL);
+}
diff --git a/src/hotssh-tab.h b/src/hotssh-tab.h
new file mode 100644
index 0000000..d3f4037
--- /dev/null
+++ b/src/hotssh-tab.h
@@ -0,0 +1,33 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2013 Colin Walters <walters verbum org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#define HOTSSH_TYPE_TAB (hotssh_tab_get_type ())
+#define HOTSSH_TAB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), HOTSSH_TYPE_TAB, HotSshTab))
+
+typedef struct _HotSshTab HotSshTab;
+typedef struct _HotSshTabClass HotSshTabClass;
+
+
+GType hotssh_tab_get_type (void);
+HotSshTab *hotssh_tab_new (void);
diff --git a/src/hotssh-win.c b/src/hotssh-win.c
index d2cec3d..388378b 100644
--- a/src/hotssh-win.c
+++ b/src/hotssh-win.c
@@ -18,15 +18,19 @@
* Boston, MA 02111-1307, USA.
*/
-#include "hotssh-app.h"
#include "hotssh-win.h"
-#include "gssh.h"
+#include "hotssh-tab.h"
#include "libgsystem.h"
-#include <libssh2.h>
-#include <vte/vte.h>
-#include <stdio.h>
+static void hotssh_win_append_tab (HotSshWindow *self);
+static void new_tab_activated (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer user_data);
+
+static GActionEntry win_entries[] = {
+ { "new-tab", new_tab_activated, NULL, NULL, NULL }
+};
struct _HotSshWindow
{
@@ -40,541 +44,129 @@ struct _HotSshWindowClass
typedef struct _HotSshWindowPrivate HotSshWindowPrivate;
-typedef enum {
- HOTSSH_WINDOW_PAGE_NEW_CONNECTION,
- HOTSSH_WINDOW_PAGE_INTERSTITAL,
- HOTSSH_WINDOW_PAGE_AUTH,
- HOTSSH_WINDOW_PAGE_TERMINAL
-} HotSshWindowPage;
-
struct _HotSshWindowPrivate
{
GtkWidget *terminal;
GSettings *settings;
/* Bound via template */
- GtkWidget *toplevel_notebook;
- GtkWidget *host_entry;
- GtkWidget *username_entry;
- GtkWidget *connect_button;
- GtkWidget *connection_text_container;
- GtkWidget *connection_text;
- GtkWidget *password_container;
- GtkWidget *password_entry;
- GtkWidget *password_submit;
- GtkWidget *connect_cancel_button;
- GtkWidget *auth_cancel_button;
- GtkWidget *hostkey_container;
- GtkWidget *hostkey_fingerprint_label;
- GtkWidget *approve_hostkey_button;
- GtkWidget *disconnect_button;
- GtkWidget *terminal_box;
-
- /* State */
- HotSshWindowPage active_page;
-
- GSocketConnectable *address;
- GSshConnection *connection;
- GSshChannel *channel;
-
- gboolean need_pty_size_request;
- gboolean sent_pty_size_request;
- gboolean have_outstanding_write;
- gboolean have_outstanding_auth;
- GQueue write_queue;
-
- GCancellable *cancellable;
+ GtkWidget *main_notebook;
+ GtkWidget *gears;
};
G_DEFINE_TYPE_WITH_PRIVATE(HotSshWindow, hotssh_window, GTK_TYPE_APPLICATION_WINDOW);
-static void
-set_status (HotSshWindow *self,
- const char *text)
+static guint
+find_tab_index (HotSshWindow *self,
+ HotSshTab *tab)
{
HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
- gtk_label_set_text ((GtkLabel*)priv->connection_text, text);
-}
-
-static void
-reset_focus_state (HotSshWindow *self)
-{
- HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
- if (gtk_entry_get_text ((GtkEntry*)priv->host_entry)[0] == '\0')
- gtk_widget_grab_focus (priv->host_entry);
- else
- gtk_widget_grab_focus (priv->username_entry);
-}
-
-static void
-state_reset_for_new_connection (HotSshWindow *self)
-{
- HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
- g_debug ("reset state");
- g_clear_object (&priv->address);
- g_clear_object (&priv->connection);
- g_clear_object (&priv->cancellable);
- vte_terminal_reset ((VteTerminal*)priv->terminal, TRUE, TRUE);
- gtk_entry_set_text ((GtkEntry*)priv->password_entry, "");
- reset_focus_state (self);
- gtk_label_set_text ((GtkLabel*)priv->connection_text, "");
- gtk_widget_show (priv->connection_text_container);
- gtk_widget_hide (priv->hostkey_container);
- gtk_widget_set_sensitive (priv->password_container, TRUE);
- g_debug ("reset state done");
-}
-
-static void
-page_transition (HotSshWindow *self,
- HotSshWindowPage new_page)
-{
- HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
-
- if (new_page == priv->active_page)
- return;
-
- g_debug ("PAGE: %d => %d", priv->active_page, new_page);
- g_assert (new_page >= HOTSSH_WINDOW_PAGE_NEW_CONNECTION &&
- new_page <= HOTSSH_WINDOW_PAGE_TERMINAL);
- priv->active_page = new_page;
-
- if (priv->active_page == HOTSSH_WINDOW_PAGE_NEW_CONNECTION)
- state_reset_for_new_connection (self);
-
- gtk_notebook_set_current_page ((GtkNotebook*)priv->toplevel_notebook, (guint)new_page);
-
- if (priv->active_page == HOTSSH_WINDOW_PAGE_TERMINAL)
- gtk_widget_grab_focus ((GtkWidget*)priv->terminal);
-}
-
-static void
-page_transition_take_error (HotSshWindow *self,
- GError *error)
-{
- set_status (self, error->message);
- g_error_free (error);
-}
-
-static void
-on_istream_read_complete (GObject *src,
- GAsyncResult *res,
- gpointer user_data)
-{
- HotSshWindow *self = user_data;
- HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
- GBytes *result = NULL;
- GError *local_error = NULL;
- const guint8 *buf;
- gsize len;
-
- result = g_input_stream_read_bytes_finish ((GInputStream*)src, res, &local_error);
- if (!result)
- goto out;
-
- buf = g_bytes_get_data (result, &len);
- g_debug ("read %u bytes", (guint)len);
-
- vte_terminal_feed ((VteTerminal*)priv->terminal, (char*)buf, len);
-
- g_input_stream_read_bytes_async ((GInputStream*)src, 8192, G_PRIORITY_DEFAULT,
- priv->cancellable, on_istream_read_complete, self);
-
- out:
- if (local_error)
- page_transition_take_error (self, local_error);
-}
-
-static void
-on_open_shell_complete (GObject *src,
- GAsyncResult *res,
- gpointer user_data)
-{
- HotSshWindow *self = user_data;
- HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
- GError *local_error = NULL;
- GInputStream *istream;
-
- g_debug ("open shell complete");
-
- priv->channel = gssh_connection_open_shell_finish ((GSshConnection*)src,
- res, &local_error);
- if (!priv->channel)
- goto out;
-
- page_transition (self, HOTSSH_WINDOW_PAGE_TERMINAL);
-
- istream = g_io_stream_get_input_stream ((GIOStream*)priv->channel);
-
- g_input_stream_read_bytes_async (istream, 8192, G_PRIORITY_DEFAULT,
- priv->cancellable, on_istream_read_complete, self);
-
- out:
- if (local_error)
- page_transition_take_error (self, local_error);
-}
-
-static void
-iterate_authentication_modes (HotSshWindow *self);
-
-static void
-on_connection_state_notify (GSshConnection *conn,
- GParamSpec *pspec,
- gpointer user_data)
-{
- HotSshWindow *self = HOTSSH_WINDOW (user_data);
- HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
- GSshConnectionState new_state = gssh_connection_get_state (conn);
-
- g_debug ("connection state: %u", new_state);
-
- switch (new_state)
- {
- case GSSH_CONNECTION_STATE_DISCONNECTED:
- page_transition (self, HOTSSH_WINDOW_PAGE_NEW_CONNECTION);
- break;
- case GSSH_CONNECTION_STATE_CONNECTING:
- case GSSH_CONNECTION_STATE_HANDSHAKING:
- case GSSH_CONNECTION_STATE_PREAUTH:
- page_transition (self, HOTSSH_WINDOW_PAGE_INTERSTITAL);
- break;
- case GSSH_CONNECTION_STATE_AUTHENTICATION_REQUIRED:
- page_transition (self, HOTSSH_WINDOW_PAGE_AUTH);
- iterate_authentication_modes (self);
- break;
- case GSSH_CONNECTION_STATE_ERROR:
- gtk_widget_hide (priv->hostkey_container);
- gtk_widget_show (priv->connection_text_container);
- page_transition (self, HOTSSH_WINDOW_PAGE_INTERSTITAL);
- break;
- case GSSH_CONNECTION_STATE_CONNECTED:
- gssh_connection_open_shell_async (priv->connection, priv->cancellable,
- on_open_shell_complete, self);
- break;
- }
-}
-
-static void
-on_password_auth_complete (GObject *src,
- GAsyncResult *res,
- gpointer user_data)
-{
- HotSshWindow *self = HOTSSH_WINDOW (user_data);
- HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
- GError *local_error = NULL;
-
- priv->have_outstanding_auth = FALSE;
-
- if (!gssh_connection_auth_password_finish ((GSshConnection*)src, res, &local_error))
- goto out;
-
- g_debug ("password auth complete");
-
- out:
- if (local_error)
- page_transition_take_error (self, local_error);
-}
-
-static void
-iterate_authentication_modes (HotSshWindow *self)
-{
- HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
- const char *const *authschemes =
- gssh_connection_get_authentication_mechanisms (priv->connection);
- const char *const*iter;
- GError *local_error = NULL;
-
- if (priv->have_outstanding_auth)
- return;
-
- for (iter = authschemes; iter && *iter; iter++)
- {
- const char *authscheme = *iter;
- if (strcmp (authscheme, "password") == 0)
- {
- const char *password = gtk_entry_get_text ((GtkEntry*)priv->password_entry);
- if (password && password[0])
- {
- gssh_connection_auth_password_async (priv->connection, password,
- priv->cancellable,
- on_password_auth_complete, self);
- priv->have_outstanding_auth = TRUE;
- break;
- }
- }
- }
-
- g_set_error (&local_error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "No more authentication mechanisms available");
- page_transition_take_error (self, local_error);
-}
+ gint n_pages, i;
-static void
-on_connection_handshake (GObject *object,
- GAsyncResult *result,
- gpointer user_data)
-{
- HotSshWindow *self = HOTSSH_WINDOW (user_data);
- HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
- GError *local_error = NULL;
- GError **error = &local_error;
- GBytes *hostkey_sha1_binary;
- GString *buf;
- guint i;
- const guint8 *binbuf;
- gsize len;
- gs_free char *hostkey_sha1_text = NULL;
-
- if (!gssh_connection_handshake_finish ((GSshConnection*)object, result, error))
- goto out;
-
- g_debug ("handshake complete");
-
- hostkey_sha1_binary = gssh_connection_preauth_get_fingerprint_sha1 (priv->connection);
- binbuf = g_bytes_get_data (hostkey_sha1_binary, &len);
- buf = g_string_new ("");
- for (i = 0; i < len; i++)
+ n_pages = gtk_notebook_get_n_pages ((GtkNotebook*)priv->main_notebook);
+ for (i = 0; i < n_pages; i++)
{
- g_string_append_printf (buf, "%02X", binbuf[i]);
- if (i < len - 1)
- g_string_append_c (buf, ':');
+ GtkWidget *widget = gtk_notebook_get_nth_page ((GtkNotebook*)priv->main_notebook, i);
+ if (widget == (GtkWidget*)tab)
+ return (guint)i;
}
- hostkey_sha1_text = g_string_free (buf, FALSE);
-
- gtk_label_set_text ((GtkLabel*)priv->hostkey_fingerprint_label,
- hostkey_sha1_text);
- gtk_widget_hide (priv->connection_text_container);
- gtk_widget_show (priv->hostkey_container);
- page_transition (self, HOTSSH_WINDOW_PAGE_INTERSTITAL);
-
- out:
- if (local_error)
- page_transition_take_error (self, local_error);
+ g_assert_not_reached ();
}
static void
-on_connect (GtkButton *button,
- HotSshWindow *self)
+on_tab_close_button_clicked (GtkButton *button,
+ gpointer user_data)
{
- HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
- GError *local_error = NULL;
- GError **error = &local_error;
- const char *hostname;
- const char *username;
-
- page_transition (self, HOTSSH_WINDOW_PAGE_INTERSTITAL);
- gtk_notebook_set_current_page ((GtkNotebook*)priv->toplevel_notebook, 1);
-
- hostname = gtk_entry_get_text (GTK_ENTRY (priv->host_entry));
- username = gtk_entry_get_text (GTK_ENTRY (priv->username_entry));
+ HotSshTab *tab = user_data;
+ HotSshWindow *win = (HotSshWindow*)g_object_get_data ((GObject*)tab, "window");
+ HotSshWindowPrivate *priv = hotssh_window_get_instance_private (win);
+ guint index;
- g_clear_object (&priv->cancellable);
- priv->cancellable = g_cancellable_new ();
-
- g_clear_object (&priv->address);
- priv->address = g_network_address_parse (hostname, 22, error);
- if (!priv->address)
- {
- page_transition_take_error (self, local_error);
- return;
- }
-
- g_clear_object (&priv->connection);
- priv->connection = gssh_connection_new (priv->address, username);
- g_signal_connect_object (priv->connection, "notify::state",
- G_CALLBACK (on_connection_state_notify),
- self, 0);
- g_debug ("connected, beginning handshake");
- gssh_connection_handshake_async (priv->connection, priv->cancellable,
- on_connection_handshake, self);
+ index = find_tab_index (win, tab);
+ gtk_notebook_remove_page ((GtkNotebook*)priv->main_notebook, index);
}
-static void
-on_disconnect (GtkButton *button,
- HotSshWindow *self)
+static GtkWidget *
+create_tab_label (HotSshWindow *self,
+ HotSshTab *tab)
{
- HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
- page_transition (self, HOTSSH_WINDOW_PAGE_NEW_CONNECTION);
- gtk_notebook_set_current_page ((GtkNotebook*)priv->toplevel_notebook, 0);
-}
+ GtkContainer *label_box;
+ GtkLabel *label;
+ GtkButton *close_button;
+ GtkImage *close_image;
-static void
-process_write_queue (HotSshWindow *self);
+ label_box = (GtkContainer*)gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ label = (GtkLabel*)gtk_label_new ("New Connection");
+ gtk_label_set_single_line_mode (label, TRUE);
+ gtk_misc_set_alignment ((GtkMisc*)label, 0.0, 0.5);
+ gtk_misc_set_padding ((GtkMisc*)label, 0, 0);
+ gtk_box_pack_start ((GtkBox*)label_box, (GtkWidget*)label, FALSE, FALSE, 0);
-static void
-on_ostream_write_complete (GObject *src,
- GAsyncResult *res,
- gpointer user_data)
-{
- HotSshWindow *self = user_data;
- HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
- gssize result;
- GError *local_error = NULL;
- GBytes *buf;
- gsize bufsize;
+ close_button = (GtkButton*)gtk_button_new ();
+ gtk_widget_set_name ((GtkWidget*)close_button, "hotssh-tab-close-button");
+ gtk_button_set_focus_on_click (close_button, FALSE);
+ gtk_button_set_relief (close_button, GTK_RELIEF_NONE);
- priv->have_outstanding_write = FALSE;
+ close_image = (GtkImage*)gtk_image_new_from_icon_name ("window-close-symbolic",
+ GTK_ICON_SIZE_MENU);
+ gtk_widget_set_tooltip_text ((GtkWidget*)close_button, "Close tab");
+ g_signal_connect (close_button, "clicked",
+ G_CALLBACK (on_tab_close_button_clicked), tab);
+ gtk_container_add ((GtkContainer*)close_button, (GtkWidget*)close_image);
- result = g_output_stream_write_bytes_finish ((GOutputStream*)src, res, &local_error);
- if (result < 0)
- goto out;
-
- buf = g_queue_pop_head (&priv->write_queue);
- g_assert (buf != NULL);
- bufsize = g_bytes_get_size (buf);
-
- if (result == bufsize)
- ;
- else
- {
- GBytes *subbuf;
- g_assert (result < bufsize);
- subbuf = g_bytes_new_from_bytes (buf, result, bufsize - result);
- g_queue_push_head (&priv->write_queue, subbuf);
- }
-
- process_write_queue (self);
-
- out:
- if (local_error)
- page_transition_take_error (self, local_error);
-}
-
-static void
-process_write_queue (HotSshWindow *self)
-{
- HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
- if (!priv->have_outstanding_write && priv->write_queue.length > 0)
- {
- GOutputStream *ostream = g_io_stream_get_output_stream ((GIOStream*)priv->channel);
- GBytes *buf = g_queue_peek_head (&priv->write_queue);
-
- g_output_stream_write_bytes_async (ostream, buf, G_PRIORITY_DEFAULT,
- priv->cancellable, on_ostream_write_complete, self);
- priv->have_outstanding_write = TRUE;
- }
-}
-
-static void
-on_terminal_commit (VteTerminal *vteterminal,
- gchar *text,
- guint size,
- gpointer user_data)
-{
- HotSshWindow *self = user_data;
- HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
+ gtk_box_pack_start ((GtkBox*)label_box, (GtkWidget*)close_button, FALSE, FALSE, 0);
- g_queue_push_tail (&priv->write_queue, g_bytes_new (text, size));
- process_write_queue (self);
+ gtk_widget_show_all ((GtkWidget*)label_box);
+ return (GtkWidget*)label_box;
}
static void
-submit_password (HotSshWindow *self)
+hotssh_win_append_tab (HotSshWindow *self)
{
HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
+ GtkWidget *label;
+ HotSshTab *tab;
- g_debug ("password submit");
+ tab = hotssh_tab_new ();
+ g_object_set_data ((GObject*)tab, "window", self);
+ label = create_tab_label (self, tab);
- gtk_widget_set_sensitive (priv->password_container, FALSE);
+ gtk_notebook_append_page ((GtkNotebook*)priv->main_notebook,
+ (GtkWidget*)tab,
+ (GtkWidget*)label);
- iterate_authentication_modes (self);
+ gtk_widget_show_all (priv->main_notebook);
}
static void
-on_connect_cancel (GtkButton *button,
- gpointer user_data)
+new_tab_activated (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
HotSshWindow *self = user_data;
- page_transition (self, HOTSSH_WINDOW_PAGE_NEW_CONNECTION);
-}
-
-static void
-on_approve_hostkey_clicked (GtkButton *button,
- gpointer user_data)
-{
- HotSshWindow *self = user_data;
- HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
-
- gssh_connection_preauth_continue (priv->connection);
-}
-
-static void
-send_pty_size_request (HotSshWindow *self);
-
-static void
-on_pty_size_complete (GObject *src,
- GAsyncResult *result,
- gpointer user_data)
-{
- HotSshWindow *self = user_data;
- HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
- GError *local_error = NULL;
-
- g_debug ("pty size request complete");
- priv->sent_pty_size_request = FALSE;
-
- if (!gssh_channel_request_pty_size_finish (priv->channel, result, &local_error))
- goto out;
-
- if (priv->need_pty_size_request)
- send_pty_size_request (self);
-
- out:
- if (local_error)
- page_transition_take_error (self, local_error);
-}
-static void
-send_pty_size_request (HotSshWindow *self)
-{
- HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
- guint width = vte_terminal_get_column_count ((VteTerminal*)priv->terminal);
- guint height = vte_terminal_get_row_count ((VteTerminal*)priv->terminal);
-
- priv->need_pty_size_request = FALSE;
- priv->sent_pty_size_request = TRUE;
- gssh_channel_request_pty_size_async (priv->channel, width, height,
- priv->cancellable, on_pty_size_complete, self);
-}
-
-static void
-on_terminal_size_allocate (GtkWidget *widget,
- GdkRectangle *allocation,
- gpointer user_data)
-{
- HotSshWindow *self = user_data;
- HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
-
- if (priv->channel)
- {
- priv->need_pty_size_request = TRUE;
- if (!priv->sent_pty_size_request)
- send_pty_size_request (self);
- }
+ hotssh_win_append_tab (self);
}
static void
hotssh_window_init (HotSshWindow *self)
{
HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
+ GtkBuilder *builder;
+ GMenuModel *menu;
gtk_widget_init_template (GTK_WIDGET (self));
priv->settings = g_settings_new ("org.gnome.hotssh");
- g_signal_connect (priv->connect_button, "clicked", G_CALLBACK (on_connect), self);
- g_signal_connect (priv->connect_cancel_button, "clicked", G_CALLBACK (on_connect_cancel), self);
- g_signal_connect (priv->auth_cancel_button, "clicked", G_CALLBACK (on_connect_cancel), self);
- g_signal_connect (priv->approve_hostkey_button, "clicked", G_CALLBACK (on_approve_hostkey_clicked), self);
- g_signal_connect (priv->disconnect_button, "clicked", G_CALLBACK (on_disconnect), self);
- g_signal_connect_swapped (priv->password_entry, "activate", G_CALLBACK (submit_password), self);
- g_signal_connect_swapped (priv->password_submit, "clicked", G_CALLBACK (submit_password), self);
-
- priv->terminal = vte_terminal_new ();
- g_signal_connect ((GObject*)priv->terminal, "size-allocate", G_CALLBACK (on_terminal_size_allocate), self);
- g_signal_connect ((GObject*)priv->terminal, "commit", G_CALLBACK (on_terminal_commit), self);
- gtk_box_pack_start ((GtkBox*)priv->terminal_box, (GtkWidget*)priv->terminal, TRUE, TRUE, 0);
- gtk_widget_show_all (priv->terminal_box);
-
- g_queue_init (&priv->write_queue);
+ g_action_map_add_action_entries ((GActionMap*) self, win_entries,
+ G_N_ELEMENTS (win_entries), self);
+
+ builder = gtk_builder_new_from_resource ("/org/gnome/hotssh/gears-menu.ui");
+ menu = G_MENU_MODEL (gtk_builder_get_object (builder, "menu"));
+ gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (priv->gears), menu);
+ g_object_unref (builder);
+
+ hotssh_win_append_tab (self);
}
static void
@@ -583,8 +175,6 @@ hotssh_window_dispose (GObject *object)
HotSshWindow *self = HOTSSH_WINDOW (object);
HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
- page_transition (self, HOTSSH_WINDOW_PAGE_NEW_CONNECTION);
-
g_clear_object (&priv->settings);
G_OBJECT_CLASS (hotssh_window_parent_class)->dispose (object);
@@ -598,22 +188,8 @@ hotssh_window_class_init (HotSshWindowClass *class)
gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),
"/org/gnome/hotssh/window.ui");
- gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshWindow, toplevel_notebook);
- gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshWindow, host_entry);
- gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshWindow, username_entry);
- gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshWindow, connect_button);
- gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshWindow,
connection_text_container);
- gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshWindow, connection_text);
- gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshWindow, password_container);
- gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshWindow, password_entry);
- gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshWindow, password_submit);
- gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshWindow,
connect_cancel_button);
- gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshWindow, auth_cancel_button);
- gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshWindow, hostkey_container);
- gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshWindow,
hostkey_fingerprint_label);
- gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshWindow,
approve_hostkey_button);
- gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshWindow, disconnect_button);
- gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshWindow, terminal_box);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshWindow, main_notebook);
+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), HotSshWindow, gears);
}
HotSshWindow *
@@ -621,16 +197,3 @@ hotssh_window_new (HotSshApp *app)
{
return g_object_new (HOTSSH_TYPE_WINDOW, "application", app, NULL);
}
-
-void
-hotssh_window_prefill (HotSshWindow *self,
- const char *host,
- const char *username)
-{
- HotSshWindowPrivate *priv = hotssh_window_get_instance_private (self);
- if (host)
- gtk_entry_set_text ((GtkEntry*)priv->host_entry, host);
- if (username)
- gtk_entry_set_text ((GtkEntry*)priv->username_entry, username);
- reset_focus_state (self);
-}
diff --git a/src/hotssh-win.h b/src/hotssh-win.h
index 8e059ad..031d3eb 100644
--- a/src/hotssh-win.h
+++ b/src/hotssh-win.h
@@ -31,7 +31,3 @@ typedef struct _HotSshWindowClass HotSshWindowClass;
GType hotssh_window_get_type (void);
HotSshWindow *hotssh_window_new (HotSshApp *app);
-void hotssh_window_prefill (HotSshWindow *win,
- const char *host,
- const char *username);
-
diff --git a/src/hotssh.gresource.xml b/src/hotssh.gresource.xml
index 0e6b2fc..0d132e9 100644
--- a/src/hotssh.gresource.xml
+++ b/src/hotssh.gresource.xml
@@ -2,6 +2,7 @@
<gresources>
<gresource prefix="/org/gnome/hotssh">
<file preprocess="xml-stripblanks">window.ui</file>
+ <file preprocess="xml-stripblanks">tab.ui</file>
<file preprocess="xml-stripblanks">app-menu.ui</file>
<file preprocess="xml-stripblanks">gears-menu.ui</file>
<file preprocess="xml-stripblanks">prefs.ui</file>
diff --git a/src/tab.ui b/src/tab.ui
new file mode 100644
index 0000000..edcd289
--- /dev/null
+++ b/src/tab.ui
@@ -0,0 +1,418 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.15.4 on Wed Oct 16 20:25:38 2013 -->
+<interface>
+ <!-- interface-requires gtk+ 3.10 -->
+ <template class="HotSshTab" parent="GtkNotebook">
+ <property name="can_focus">False</property>
+ <property name="show_tabs">False</property>
+ <child>
+ <object class="GtkBox" id="content_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkEntry" id="host_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes">Host: </property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="username_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes">Username: </property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="connect_button">
+ <property name="label" translatable="yes">Connect</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="halign">end</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">page 1</property>
+ </object>
+ <packing>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="interstital_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkAlignment" id="connection_text_container">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="connection_text">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Connecting...</property>
+ <property name="justify">center</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="hostkey_container">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="hostkey_label_text">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Approval of newly seen host key required; RSA
key fingerprint is:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="hostkey_fingerprint_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label"
translatable="yes">00:--:--:--:--:--:--:--:--:--:--:--:--:--:--:00</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="approve_hostkey_button">
+ <property name="label" translatable="yes">Approve and Connect</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="xalign">0.55000001192092896</property>
+ <property name="yalign">0.57999998331069946</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="connect_cancel_button">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="halign">end</property>
+ <property name="use_stock">True</property>
+ <property name="image_position">right</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">page 2</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="box3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="baseline_position">top</property>
+ <child>
+ <object class="GtkBox" id="password_container">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">center</property>
+ <child>
+ <object class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes">Password:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="password_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="visibility">False</property>
+ <property name="input_purpose">password</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="password_submit">
+ <property name="label">gtk-connect</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="margin_left">8</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="auth_cancel_button">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="halign">end</property>
+ <property name="use_stock">True</property>
+ <property name="image_position">right</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">page 2</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="terminal_outer_vbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="terminal_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkStatusbar" id="connection_statusbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">10</property>
+ <property name="margin_right">10</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="disconnect_button">
+ <property name="label">gtk-disconnect</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ <property name="yalign">0.64999997615814209</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">page 3</property>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </template>
+</interface>
diff --git a/src/window.ui b/src/window.ui
index eb36bf9..f5de1f2 100644
--- a/src/window.ui
+++ b/src/window.ui
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.15.4 on Thu Oct 10 21:41:56 2013 -->
+<!-- Generated with glade 3.15.4 on Wed Oct 16 19:12:18 2013 -->
<interface>
<!-- interface-requires gtk+ 3.10 -->
<template class="HotSshWindow" parent="GtkApplicationWindow">
<property name="can_focus">False</property>
<property name="title" translatable="yes">HotSSH</property>
- <property name="default_width">600</property>
- <property name="default_height">400</property>
+ <property name="default_width">640</property>
+ <property name="default_height">480</property>
<child type="titlebar">
<object class="GtkHeaderBar" id="header-bar">
<property name="visible">True</property>
@@ -15,423 +15,28 @@
<style>
<class name="titlebar"/>
</style>
- </object>
- </child>
- <child>
- <object class="GtkNotebook" id="toplevel_notebook">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="show_tabs">False</property>
- <child>
- <object class="GtkBox" id="content_box">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkAlignment" id="alignment1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">center</property>
- <property name="valign">center</property>
- <child>
- <object class="GtkGrid" id="grid1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">center</property>
- <property name="valign">center</property>
- <child>
- <object class="GtkEntry" id="host_entry">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">0</property>
- <property name="width">1</property>
- <property name="height">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label4">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">end</property>
- <property name="label" translatable="yes">Host: </property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
- <property name="width">1</property>
- <property name="height">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="username_entry">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">1</property>
- <property name="width">1</property>
- <property name="height">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label5">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">end</property>
- <property name="label" translatable="yes">Username: </property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">1</property>
- <property name="width">1</property>
- <property name="height">1</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="connect_button">
- <property name="label" translatable="yes">Connect</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="halign">end</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- </child>
- <child type="tab">
- <object class="GtkLabel" id="label1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">page 1</property>
- </object>
- <packing>
- <property name="tab_fill">False</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox" id="box4">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkBox" id="interstital_box">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkAlignment" id="connection_text_container">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkLabel" id="connection_text">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Connecting...</property>
- <property name="justify">center</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox" id="hostkey_container">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="valign">center</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkLabel" id="hostkey_label_text">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Approval of newly seen host key required;
RSA key fingerprint is:</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="hostkey_fingerprint_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label"
translatable="yes">00:--:--:--:--:--:--:--:--:--:--:--:--:--:--:00</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="approve_hostkey_button">
- <property name="label" translatable="yes">Approve and Connect</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="halign">center</property>
- <property name="valign">center</property>
- <property name="xalign">0.55000001192092896</property>
- <property name="yalign">0.57999998331069946</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="connect_cancel_button">
- <property name="label">gtk-cancel</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="halign">end</property>
- <property name="use_stock">True</property>
- <property name="image_position">right</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child type="tab">
- <object class="GtkLabel" id="label7">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">page 2</property>
- </object>
- <packing>
- <property name="position">1</property>
- <property name="tab_fill">False</property>
- </packing>
- </child>
<child>
- <object class="GtkBox" id="box1">
+ <object class="GtkMenuButton" id="gears">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkBox" id="box3">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <property name="baseline_position">top</property>
- <child>
- <object class="GtkBox" id="password_container">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">center</property>
- <child>
- <object class="GtkLabel" id="label6">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">end</property>
- <property name="label" translatable="yes">Password:</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="password_entry">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="visibility">False</property>
- <property name="input_purpose">password</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="password_submit">
- <property name="label">gtk-connect</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="halign">center</property>
- <property name="valign">center</property>
- <property name="margin_left">8</property>
- <property name="use_stock">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
<child>
- <object class="GtkButton" id="auth_cancel_button">
- <property name="label">gtk-cancel</property>
+ <object class="GtkImage" id="gears-icon">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="halign">end</property>
- <property name="use_stock">True</property>
- <property name="image_position">right</property>
+ <property name="icon-name">emblem-system-symbolic</property>
+ <property name="icon-size">1</property>
</object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
</child>
</object>
<packing>
- <property name="position">2</property>
- </packing>
- </child>
- <child type="tab">
- <object class="GtkLabel" id="label2">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">page 2</property>
- </object>
- <packing>
- <property name="position">2</property>
- <property name="tab_fill">False</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox" id="terminal_outer_vbox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkBox" id="terminal_box">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <child>
- <placeholder/>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox" id="box2">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkStatusbar" id="connection_statusbar">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">10</property>
- <property name="margin_right">10</property>
- <property name="margin_top">6</property>
- <property name="margin_bottom">6</property>
- <property name="orientation">vertical</property>
- <property name="spacing">2</property>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="disconnect_button">
- <property name="label">gtk-disconnect</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_stock">True</property>
- <property name="yalign">0.64999997615814209</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="position">3</property>
- </packing>
- </child>
- <child type="tab">
- <object class="GtkLabel" id="label3">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">page 3</property>
- </object>
- <packing>
- <property name="position">3</property>
- <property name="tab_fill">False</property>
+ <property name="pack-type">end</property>
</packing>
</child>
</object>
</child>
+ <child>
+ <object class="GtkNotebook" id="main_notebook">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
</template>
</interface>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]