[hotssh] Implement tabs



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]