Browse Source

Merge remote-tracking branch 'remotes/upstream/master' into upstream

Héctor García 9 years ago
parent
commit
9728cd8dd3
14 changed files with 866 additions and 103 deletions
  1. 2 1
      AUTHORS
  2. 44 10
      ChangeLog
  3. 11 2
      NEWS
  4. 2 2
      configure.ac
  5. 55 0
      mssh.schemas
  6. 1 1
      src/Makefile.am
  7. 127 16
      src/mssh-gconf.c
  8. 22 10
      src/mssh-gconf.h
  9. 148 34
      src/mssh-pref.c
  10. 7 0
      src/mssh-terminal.c
  11. 2 0
      src/mssh-terminal.h
  12. 367 25
      src/mssh-window.c
  13. 10 1
      src/mssh-window.h
  14. 68 1
      src/mssh.c

+ 2 - 1
AUTHORS

@@ -8,6 +8,7 @@ Founder and Lead Developer:
 
 Contributors
 ----------------------------------------------------------------------------
+    Balint Deak  <[email protected]>
     Francisco Licerán <[email protected]>
     Mario J. Barchéin <[email protected]>
-    Oscar Fernandez <[email protected]>
+    Oscar Fernandez <[email protected]>

+ 44 - 10
ChangeLog

@@ -1,28 +1,62 @@
+2014-12-12 Héctor García  <[email protected]>
+
+	Upgrade deprecated funtions to support GTK 3.14
+
+	Set the focus on the global entry only if the terminal closed has it.
+
+2014-03-17  Héctor García  <[email protected]>
+
+	Release as mssh 2.0
+
+2014-03-14  Héctor García  <[email protected]>
+
+	Added support to defined backscroll lines for terminals. Patch from Balint Deak.
+
+	Added support to maximize a terminal (ctrl + shift + x). Patch from Balint Deak.
+
+	Added support to change bg y fg colors on selected terminal. Patch from Balint Deak.
+
+	Added support to create a new session. Patch from Balint Deak.
+
+2014-03-13  Héctor García  <[email protected]>
+
+	Added a new feature to the aliases file. Now you can define aliases for command line text to send using {}
+
 2014-01-20  Héctor García  <[email protected]>
 
-    Make paste with mouse middle button focus on terminal
+	Make paste with mouse middle button focus on terminal
 
 2014-01-15  Héctor García  <[email protected]>
 
-    Added patch from Oscar Fernandez to support comments on configuration archive
+	Added patch from Oscar Fernandez to support comments on configuration archive
 
-    Added patch from Mario J. Barchéin and Francisco Licerán to support port numbers on host
+	Added patch from Mario J. Barchéin and Francisco Licerán to support port numbers on host
 
-    Copy Debian's mssh man page to upstream and added Karl Goetz <[email protected]> patch
+	Copy Debian's mssh man page to upstream and added Karl Goetz <[email protected]> patch
 
-    Allow window to be smaller than 1024x768. Patch from Lorenzo Masini <[email protected]>
+	Allow window to be smaller than 1024x768. Patch from Lorenzo Masini <[email protected]>
 
 2014-01-15  Héctor García  <[email protected]>
 
-    Bumped version to 1.3 gtk2 deprecated symbols free
+	Bumped version to 2.0 gtk3 support
+
+	Changed from gdk_color_ to gdk_rgba_ functions
+
+	Changed from gtk_color_button to gtk_color_chooser
+
+	Change from gtk_vbox and gtk_hbox to gtk_box
+
+	Change colour_table from GTK_TABLE to GTK_GRID
+
+	Change table where to place terminals to grid
 
-    Replaced GDK_<keyname> with GDK_KEY_<keyname>
+	Replaced GDK_<keyname> with GDK_KEY_<keyname>
 
-    Changed direct access to use accessor for terminal->parent
+	Changed direct access to use accessor for terminal->parent
 
-    Changed deprecated gtk_menu_bar_append with gtk_menu_shell_append
+	Changed deprecated gtk_menu_bar_append with gtk_menu_shell_append
 
-    Migrate from vte_terminal_fork_command to vte_terminal_fork_command_full to make compile against GTK 2.24
+	Migrate from vte_terminal_fork_command to vte_terminal_fork_command_full to make compile against GTK 2.24
 
 2010-01-15  Bradley Smith  <[email protected]>
 

+ 11 - 2
NEWS

@@ -1,2 +1,11 @@
-Since around 15 Jan 2014 I (Héctor García) did take over code development and maintenance.
-Hope Bradley Smith can come around some time in the future to get his great project back.
+2014-03-14
+    New ctrl + shift + x combination to maximize a terminal.
+
+    New ctrl + shift + n combinatio to add a new server.
+
+    Now you can have different colors on select terminal. MUST update mssh.schema or mssh will crash.
+
+2014-01-17
+    Since around 15 Jan 2014 I (Héctor García) did take over code development and maintenance.
+
+    Hope Bradley Smith can come around some time in the future to get his great project back.

+ 2 - 2
configure.ac

@@ -1,10 +1,10 @@
-AC_INIT([MultiSSH], [1.3], [[email protected]], [mssh])
+AC_INIT([MultiSSH], [2.0], [[email protected]], [mssh])
 AM_CONFIG_HEADER([config.h])
 AM_INIT_AUTOMAKE
 
 AC_PROG_CC
 
-PKG_CHECK_MODULES(MSSH, [gtk+-2.0 vte gconf-2.0])
+PKG_CHECK_MODULES(MSSH, [gtk+-3.0 vte-2.90 gconf-2.0])
 AC_SUBST(MSSH_CFLAGS)
 AC_SUBST(MSSH_LIBS)
 

+ 55 - 0
mssh.schemas

@@ -33,6 +33,39 @@
                 <long>Background colour to use for all MSSH terminals</long>
             </locale>
         </schema>
+        <schema>
+            <key>/schemas/apps/mssh/recolor_focused</key>
+            <applyto>/apps/mssh/recolor_focused</applyto>
+            <owner>mssh</owner>
+            <type>bool</type>
+            <default>true</default>
+            <locale name="C">
+                <short>Recolor focused window</short>
+                <long>Use different Back- and Foreground color for focused window</long>
+            </locale>
+        </schema>
+        <schema>
+            <key>/schemas/apps/mssh/fg_colour_focus</key>
+            <applyto>/apps/mssh/fg_colour_focus</applyto>
+            <owner>mssh</owner>
+            <type>string</type>
+            <default>#ffffffffffff</default>
+            <locale name="C">
+                <short>Terminal foreground colour for the focused window</short>
+                <long>Foreground colour to use for the currently focused MSSH terminal</long>
+            </locale>
+        </schema>
+        <schema>
+            <key>/schemas/apps/mssh/bg_colour_focus</key>
+            <applyto>/apps/mssh/bg_colour_focus</applyto>
+            <owner>mssh</owner>
+            <type>string</type>
+            <default>#000000000000</default>
+            <locale name="C">
+                <short>Terminal background colour for the focused window</short>
+                <long>Background colour to use for the currently focused MSSH terminal</long>
+            </locale>
+        </schema>
         <schema>
             <key>/schemas/apps/mssh/coloums</key>
             <applyto>/apps/mssh/columns</applyto>
@@ -88,6 +121,17 @@
                 <long>Uses directional focus rather than linear focus</long>
             </locale>
         </schema>
+        <schema>
+            <key>/schemas/apps/mssh/use_pattern_expansion</key>
+            <applyto>/apps/mssh/use_pattern_expansion</applyto>
+            <owner>mssh</owner>
+            <type>bool</type>
+            <default>true</default>
+            <locale name="C">
+                <short>Allow expansion of curly braces when adding new sessions</short>
+                <long>Use curly bracket expansion when adding new sessions</long>
+            </locale>
+        </schema>              
         <schema>
             <key>/schemas/apps/mssh/modifier</key>
             <applyto>/apps/mssh/modifier</applyto>
@@ -99,5 +143,16 @@
                 <long>Modifier used in focus shortcuts</long>
             </locale>
         </schema>
+        <schema>
+            <key>/schemas/apps/mssh/backscroll_buffer_size</key>
+            <applyto>/apps/mssh/backscroll_buffer_size</applyto>
+            <owner>mssh</owner>
+            <type>int</type>
+            <default>5000</default>
+            <locale name="C">
+                <short>Backscroll buffer size</short>
+                <long>Number of lines to keep for every window as a backscroll buffer. Set to -1 to make it infinite</long>
+            </locale>
+        </schema>        
     </schemalist>
 </gconfschemafile>

+ 1 - 1
src/Makefile.am

@@ -1,6 +1,6 @@
 AM_CFLAGS = -pedantic-errors -Werror -Wall -Wfatal-errors -Wwrite-strings
 
-INCLUDES = $(MSSH_CFLAGS)
+AM_CPPFLAGS = $(MSSH_CFLAGS)
 
 bin_PROGRAMS = mssh
 

+ 127 - 16
src/mssh-gconf.c

@@ -29,23 +29,18 @@ void mssh_gconf_notify_fg_colour(GConfClient *client, guint cnxn_id,
 {
     GConfValue *value;
     const gchar *colour_s;
-    GdkVisual *visual = gdk_visual_get_system();
-    GdkColormap *colour_map;
-    GdkColor colour;
+    GdkRGBA colour;
     int i;
 
     MSSHWindow *window = MSSH_WINDOW(data);
 
     value = gconf_entry_get_value(entry);
     colour_s = gconf_value_get_string(value);
-    colour_map = gdk_colormap_new(visual, TRUE);
-    gdk_colormap_alloc_color(colour_map, &colour, TRUE, TRUE);
-
-    gdk_color_parse(colour_s, &colour);
+    gdk_rgba_parse(&colour, colour_s);
 
     for(i = 0; i < window->terminals->len; i++)
     {
-        vte_terminal_set_color_foreground(VTE_TERMINAL(g_array_index(
+        vte_terminal_set_color_foreground_rgba(VTE_TERMINAL(g_array_index(
             window->terminals, MSSHTerminal*, i)), &colour);
     }
 }
@@ -55,27 +50,96 @@ void mssh_gconf_notify_bg_colour(GConfClient *client, guint cnxn_id,
 {
     GConfValue *value;
     const gchar *colour_s;
-    GdkVisual *visual = gdk_visual_get_system();
-    GdkColormap *colour_map;
-    GdkColor colour;
+    GdkRGBA colour;
     int i;
 
     MSSHWindow *window = MSSH_WINDOW(data);
 
     value = gconf_entry_get_value(entry);
     colour_s = gconf_value_get_string(value);
-    colour_map = gdk_colormap_new(visual, TRUE);
-    gdk_colormap_alloc_color(colour_map, &colour, TRUE, TRUE);
-
-    gdk_color_parse(colour_s, &colour);
+    gdk_rgba_parse(&colour, colour_s);
 
     for(i = 0; i < window->terminals->len; i++)
     {
-        vte_terminal_set_color_background(VTE_TERMINAL(g_array_index(
+        vte_terminal_set_color_background_rgba(VTE_TERMINAL(g_array_index(
             window->terminals, MSSHTerminal*, i)), &colour);
     }
 }
 
+void mssh_gconf_notify_fg_colour_focus(GConfClient *client, guint cnxn_id,
+    GConfEntry *entry, gpointer data)
+{
+    GConfValue *value;
+    const gchar *colour_s;
+    GdkRGBA colour;
+    int i;
+    int idx = -1;
+    GtkWidget *focus;
+
+    MSSHWindow *window = MSSH_WINDOW(data);
+
+    value = gconf_entry_get_value(entry);
+    colour_s = gconf_value_get_string(value);
+    gdk_rgba_parse(&colour, colour_s);
+
+    /* get the currently focused window */
+	focus = gtk_window_get_focus(GTK_WINDOW(window));
+
+    /* find the focused window in the terminal list */
+    for(i = 0; i < window->terminals->len; i++)
+    {
+        if(focus == GTK_WIDGET(g_array_index(window->terminals,
+            MSSHTerminal*, i)))
+        {
+            idx = i;
+            break;
+        }
+    }
+
+    if (idx != -1) {
+        /* found the currently focused terminal, update the color */
+        vte_terminal_set_color_foreground_rgba(VTE_TERMINAL(g_array_index(
+            window->terminals, MSSHTerminal*, idx)), &colour);
+    }
+}
+
+void mssh_gconf_notify_bg_colour_focus(GConfClient *client, guint cnxn_id,
+    GConfEntry *entry, gpointer data)
+{
+    GConfValue *value;
+    const gchar *colour_s;
+    GdkRGBA colour;
+    int i;
+    int idx = -1;
+	GtkWidget *focus;
+
+    MSSHWindow *window = MSSH_WINDOW(data);
+
+    value = gconf_entry_get_value(entry);
+    colour_s = gconf_value_get_string(value);
+    gdk_rgba_parse(&colour, colour_s);
+
+    /* get the currently focused window */
+    focus = gtk_window_get_focus(GTK_WINDOW(window));
+
+    /* find the focused window in the terminal list */
+    for(i = 0; i < window->terminals->len; i++)
+    {
+        if(focus == GTK_WIDGET(g_array_index(window->terminals,
+            MSSHTerminal*, i)))
+        {
+            idx = i;
+            break;
+        }
+    }
+
+    if (idx != -1) {
+        /* found the currently focused terminal, update the color */
+        vte_terminal_set_color_background_rgba(VTE_TERMINAL(g_array_index(
+            window->terminals, MSSHTerminal*, idx)), &colour);
+    }
+
+}
 void mssh_gconf_notify_columns(GConfClient *client, guint cnxn_id,
     GConfEntry *entry, gpointer data)
 {
@@ -149,6 +213,21 @@ void mssh_gconf_notify_close_ended(GConfClient *client, guint cnxn_id,
     }
 }
 
+void mssh_gconf_notify_recolor_focused(GConfClient *client, guint cnxn_id,
+    GConfEntry *entry, gpointer data)
+{
+    GConfValue *value;
+    gboolean recolor_focused;
+
+    MSSHWindow *window = MSSH_WINDOW(data);
+
+    value = gconf_entry_get_value(entry);
+    recolor_focused = gconf_value_get_bool(value);
+
+    window->recolor_focused = recolor_focused;
+
+}
+
 void mssh_gconf_notify_quit_all_ended(GConfClient *client, guint cnxn_id,
     GConfEntry *entry, gpointer data)
 {
@@ -212,3 +291,35 @@ void mssh_gconf_notify_modifier(GConfClient *client, guint cnxn_id,
             G_CALLBACK(mssh_window_focus), window, NULL));
     }
 }
+
+
+void mssh_gconf_backscroll_buffer_size(GConfClient *client, guint cnxn_id,
+    GConfEntry *entry, gpointer data)
+{
+    GConfValue *value;
+    gint backscroll_buffer_size;
+
+    MSSHWindow *window = MSSH_WINDOW(data);
+
+    int i;
+    int len = window->terminals->len;
+
+    value = gconf_entry_get_value(entry);
+    backscroll_buffer_size = gconf_value_get_int(value);
+
+
+    if (backscroll_buffer_size < -1)
+    {
+        backscroll_buffer_size = 5000;
+        gconf_client_set_int(client, MSSH_GCONF_KEY_BACKSCROLL_BUFFER_SIZE, backscroll_buffer_size,
+            NULL);
+    }
+
+    window->backscroll_buffer_size = backscroll_buffer_size;
+    /* reconfigure all terminals with the new size*/
+    for(i = 0; i < len; i++)
+    {
+        mssh_terminal_set_backscroll_size(g_array_index(window->terminals,
+            MSSHTerminal*, i), &backscroll_buffer_size);
+    }
+}

+ 22 - 10
src/mssh-gconf.h

@@ -3,16 +3,20 @@
 
 #include <gconf/gconf-client.h>
 
-#define MSSH_GCONF_PATH                 "/apps/mssh"
-#define MSSH_GCONF_KEY_FONT             MSSH_GCONF_PATH"/font"
-#define MSSH_GCONF_KEY_FG_COLOUR        MSSH_GCONF_PATH"/fg_colour"
-#define MSSH_GCONF_KEY_BG_COLOUR        MSSH_GCONF_PATH"/bg_colour"
-#define MSSH_GCONF_KEY_COLUMNS          MSSH_GCONF_PATH"/columns"
-#define MSSH_GCONF_KEY_TIMEOUT          MSSH_GCONF_PATH"/timeout"
-#define MSSH_GCONF_KEY_CLOSE_ENDED      MSSH_GCONF_PATH"/close_ended"
-#define MSSH_GCONF_KEY_QUIT_ALL_ENDED   MSSH_GCONF_PATH"/quit_all_ended"
-#define MSSH_GCONF_KEY_MODIFIER         MSSH_GCONF_PATH"/modifier"
-#define MSSH_GCONF_KEY_DIR_FOCUS        MSSH_GCONF_PATH"/dir_focus"
+#define MSSH_GCONF_PATH                 		"/apps/mssh"
+#define MSSH_GCONF_KEY_FONT             		MSSH_GCONF_PATH"/font"
+#define MSSH_GCONF_KEY_FG_COLOUR        		MSSH_GCONF_PATH"/fg_colour"
+#define MSSH_GCONF_KEY_BG_COLOUR        		MSSH_GCONF_PATH"/bg_colour"
+#define MSSH_GCONF_KEY_FG_COLOUR_FOCUS          MSSH_GCONF_PATH"/fg_colour_focus"
+#define MSSH_GCONF_KEY_BG_COLOUR_FOCUS          MSSH_GCONF_PATH"/bg_colour_focus"
+#define MSSH_GCONF_KEY_COLUMNS          		MSSH_GCONF_PATH"/columns"
+#define MSSH_GCONF_KEY_TIMEOUT          		MSSH_GCONF_PATH"/timeout"
+#define MSSH_GCONF_KEY_CLOSE_ENDED      		MSSH_GCONF_PATH"/close_ended"
+#define MSSH_GCONF_KEY_RECOLOR_FOCUSED          MSSH_GCONF_PATH"/recolor_focused"
+#define MSSH_GCONF_KEY_QUIT_ALL_ENDED   		MSSH_GCONF_PATH"/quit_all_ended"
+#define MSSH_GCONF_KEY_MODIFIER         		MSSH_GCONF_PATH"/modifier"
+#define MSSH_GCONF_KEY_DIR_FOCUS        		MSSH_GCONF_PATH"/dir_focus"
+#define MSSH_GCONF_KEY_BACKSCROLL_BUFFER_SIZE 	MSSH_GCONF_PATH"/backscroll_buffer_size"
 
 void mssh_gconf_notify_font(GConfClient *client, guint cnxn_id,
     GConfEntry *entry, gpointer data);
@@ -20,6 +24,10 @@ void mssh_gconf_notify_fg_colour(GConfClient *client, guint cnxn_id,
     GConfEntry *entry, gpointer data);
 void mssh_gconf_notify_bg_colour(GConfClient *client, guint cnxn_id,
     GConfEntry *entry, gpointer data);
+void mssh_gconf_notify_fg_colour_focus(GConfClient *client, guint cnxn_id,
+    GConfEntry *entry, gpointer data);
+void mssh_gconf_notify_bg_colour_focus(GConfClient *client, guint cnxn_id,
+    GConfEntry *entry, gpointer data);
 void mssh_gconf_notify_columns(GConfClient *client, guint cnxn_id,
     GConfEntry *entry, gpointer data);
 void mssh_gconf_notify_timeout(GConfClient *client, guint cnxn_id,
@@ -32,5 +40,9 @@ void mssh_gconf_notify_modifier(GConfClient *client, guint cnxn_id,
     GConfEntry *entry, gpointer data);
 void mssh_gconf_notify_dir_focus(GConfClient *client, guint cnxn_id,
     GConfEntry *entry, gpointer data);
+void mssh_gconf_backscroll_buffer_size(GConfClient *client, guint cnxn_id,
+    GConfEntry *entry, gpointer data);
+void mssh_gconf_notify_recolor_focused(GConfClient *client, guint cnxn_id,
+    GConfEntry *entry, gpointer data);
 
 #endif

+ 148 - 34
src/mssh-pref.c

@@ -36,13 +36,13 @@ static void mssh_pref_font_select(GtkWidget *widget, gpointer data)
 static void mssh_pref_fg_colour_select(GtkWidget *widget, gpointer data)
 {
     GConfClient *client;
-    GdkColor colour;
+    GdkRGBA colour;
     const gchar *colour_s;
 
     client = gconf_client_get_default();
 
-    gtk_color_button_get_color(GTK_COLOR_BUTTON(widget), &colour);
-    colour_s = gdk_color_to_string(&colour);
+    gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &colour);
+    colour_s = gdk_rgba_to_string(&colour);
 
     gconf_client_set_string(client, MSSH_GCONF_KEY_FG_COLOUR, colour_s,
         NULL);
@@ -51,18 +51,48 @@ static void mssh_pref_fg_colour_select(GtkWidget *widget, gpointer data)
 static void mssh_pref_bg_colour_select(GtkWidget *widget, gpointer data)
 {
     GConfClient *client;
-    GdkColor colour;
+    GdkRGBA colour;
     const gchar *colour_s;
 
     client = gconf_client_get_default();
 
-    gtk_color_button_get_color(GTK_COLOR_BUTTON(widget), &colour);
-    colour_s = gdk_color_to_string(&colour);
+    gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &colour);
+    colour_s = gdk_rgba_to_string(&colour);
 
     gconf_client_set_string(client, MSSH_GCONF_KEY_BG_COLOUR, colour_s,
         NULL);
 }
 
+static void mssh_pref_fg_colour_select_focus(GtkWidget *widget, gpointer data)
+{
+    GConfClient *client;
+    GdkRGBA colour;
+    const gchar *colour_s;
+
+    client = gconf_client_get_default();
+
+    gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &colour);
+    colour_s = gdk_rgba_to_string(&colour);
+
+
+    gconf_client_set_string(client, MSSH_GCONF_KEY_FG_COLOUR_FOCUS, colour_s,
+        NULL);
+}
+
+static void mssh_pref_bg_colour_select_focus(GtkWidget *widget, gpointer data)
+{
+    GConfClient *client;
+    GdkRGBA colour;
+    const gchar *colour_s;
+
+    client = gconf_client_get_default();
+
+    gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &colour);
+    colour_s = gdk_rgba_to_string(&colour);
+
+    gconf_client_set_string(client, MSSH_GCONF_KEY_BG_COLOUR_FOCUS, colour_s,
+        NULL);
+}
 static void mssh_pref_columns_select(GtkWidget *widget, gpointer data)
 {
     GConfClient *client;
@@ -75,6 +105,17 @@ static void mssh_pref_columns_select(GtkWidget *widget, gpointer data)
     gconf_client_set_int(client, MSSH_GCONF_KEY_COLUMNS, columns, NULL);
 }
 
+static void mssh_pref_backscroll_buffer_size_select(GtkWidget *widget, gpointer data)
+{
+    GConfClient *client;
+    gint backscroll_buffer_size;
+
+    client = gconf_client_get_default();
+
+    backscroll_buffer_size = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+
+    gconf_client_set_int(client, MSSH_GCONF_KEY_BACKSCROLL_BUFFER_SIZE, backscroll_buffer_size, NULL);
+}
 static void mssh_pref_timeout_select(GtkWidget *widget, gpointer data)
 {
     GConfClient *client;
@@ -99,6 +140,17 @@ static void mssh_pref_close_check(GtkWidget *widget, gpointer data)
     gconf_client_set_bool(client, MSSH_GCONF_KEY_CLOSE_ENDED, close, NULL);
 }
 
+static void mssh_pref_recolor_focused_check(GtkWidget *widget, gpointer data)
+{
+    GConfClient *client;
+    gboolean recolor_focused;
+
+    client = gconf_client_get_default();
+
+    recolor_focused = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+
+    gconf_client_set_bool(client, MSSH_GCONF_KEY_RECOLOR_FOCUSED, recolor_focused, NULL);
+}
 static void mssh_pref_exit_check(GtkWidget *widget, gpointer data)
 {
     GConfClient *client;
@@ -149,53 +201,64 @@ static void mssh_pref_init(MSSHPref* pref)
     GConfClient *client;
     GConfEntry *entry;
     GConfValue *value;
-    GdkVisual *visual = gdk_visual_get_system();
-    GdkColormap *colour_map = gdk_colormap_new(visual, TRUE);
-    GdkColor colour;
+    GdkRGBA colour;
     const gchar *colour_s;
 
-    GtkWidget *frame = gtk_vbox_new(FALSE, 5);
+    GtkWidget *frame = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
     GtkWidget *notebook = gtk_notebook_new();
-    GtkWidget *content = gtk_vbox_new(FALSE, 4);
+    GtkWidget *content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
 
-    GtkWidget *font_hbox = gtk_hbox_new(FALSE, 10);
+    GtkWidget *font_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
     GtkWidget *font_label = gtk_label_new("Font:");
     GtkWidget *font_select = gtk_font_button_new();
 
-    GtkWidget *colour_table = gtk_table_new(2, 2, FALSE);
+    GtkWidget *colour_table = gtk_grid_new();
     GtkWidget *bg_colour_label = gtk_label_new("Background:");
     GtkWidget *bg_colour_select = gtk_color_button_new();
     GtkWidget *fg_colour_label = gtk_label_new("Foreground:");
     GtkWidget *fg_colour_select = gtk_color_button_new();
 
+    GtkWidget *recolor_focused_check = gtk_check_button_new_with_label(
+        "Use different color for focused window");
+
+    GtkWidget *colour_table_focus = gtk_grid_new();
+    GtkWidget *bg_colour_label_focus = gtk_label_new("Background for focused window:");
+    GtkWidget *bg_colour_select_focus = gtk_color_button_new();
+    GtkWidget *fg_colour_label_focus = gtk_label_new("Foreground for focused window:");
+    GtkWidget *fg_colour_select_focus = gtk_color_button_new();
     GtkWidget *exit_check = gtk_check_button_new_with_label(
         "Quit after all sessions have ended");
     GtkWidget *close_check = gtk_check_button_new_with_label(
         "Close ended sessions");
 
-    GtkWidget *timeout_hbox = gtk_hbox_new(FALSE, 10);
+    GtkWidget *timeout_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
     GtkWidget *timeout_label1 = gtk_label_new(
         "Closed ended sessions after");
-    GtkObject *timeout_adj = gtk_adjustment_new(3, 0, 100, 1, 10, 0);
+    GtkAdjustment *timeout_adj = gtk_adjustment_new(3, 0, 100, 1, 10, 0);
     GtkWidget *timeout_select = gtk_spin_button_new(
         GTK_ADJUSTMENT(timeout_adj), 1, 0);
     GtkWidget *timeout_label2 = gtk_label_new("seconds");
 
-    GtkWidget *columns_hbox = gtk_hbox_new(FALSE, 10);
+    GtkWidget *columns_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
     GtkWidget *columns_label = gtk_label_new("Columns:");
-    GtkObject *columns_adj = gtk_adjustment_new(2, 1, 10, 1, 10, 0);
+    GtkAdjustment *columns_adj = gtk_adjustment_new(2, 1, 10, 1, 10, 0);
     GtkWidget *columns_select = gtk_spin_button_new(
         GTK_ADJUSTMENT(columns_adj), 1, 0);
 
-    GtkWidget *mod_hbox = gtk_hbox_new(FALSE, 10);
+    GtkWidget *backscroll_buffer_size_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
+    GtkWidget *backscroll_buffer_size_label = gtk_label_new("Scrollback Lines:");
+    GtkAdjustment *backscroll_buffer_size_adj = gtk_adjustment_new(5000, -1, 65535, 1, 100, 0);
+    GtkWidget *backscroll_buffer_size_select = gtk_spin_button_new(
+        GTK_ADJUSTMENT(backscroll_buffer_size_adj), 1, 0);  
+    GtkWidget *mod_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
     GtkWidget *mod_label = gtk_label_new("Modifier:");
     GtkWidget *mod_ctrl_check = gtk_check_button_new_with_label("Ctrl");
     GtkWidget *mod_alt_check = gtk_check_button_new_with_label("Alt");
     GtkWidget *mod_shift_check = gtk_check_button_new_with_label("Shift");
     GtkWidget *mod_super_check = gtk_check_button_new_with_label("Super");
 
-    GtkWidget *close_hbox = gtk_hbox_new(FALSE, 0);
-    GtkWidget *close_button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+    GtkWidget *close_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+    GtkWidget *close_button = gtk_button_new_with_label("Close");
 
     GtkWidget *dir_focus_check = gtk_check_button_new_with_label(
         "Use directional focus");
@@ -212,16 +275,27 @@ static void mssh_pref_init(MSSHPref* pref)
     gtk_box_pack_start(GTK_BOX(font_hbox), font_select, FALSE, TRUE, 0);
     gtk_box_pack_start(GTK_BOX(content), font_hbox, FALSE, TRUE, 0);
 
-    gtk_table_attach(GTK_TABLE(colour_table), bg_colour_label,
-        0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
-    gtk_table_attach(GTK_TABLE(colour_table), bg_colour_select,
-        1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
-    gtk_table_attach(GTK_TABLE(colour_table), fg_colour_label,
-        0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
-    gtk_table_attach(GTK_TABLE(colour_table), fg_colour_select,
-        1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
+    gtk_grid_attach(GTK_GRID(colour_table), bg_colour_label,
+        0, 0, 1, 1);
+    gtk_grid_attach(GTK_GRID(colour_table), bg_colour_select,
+        1, 0, 1, 1);
+    gtk_grid_attach(GTK_GRID(colour_table), fg_colour_label,
+        0, 1, 1, 1);
+    gtk_grid_attach(GTK_GRID(colour_table), fg_colour_select,
+        1, 1, 1, 1);
     gtk_box_pack_start(GTK_BOX(content), colour_table, FALSE, TRUE, 0);
 
+    gtk_box_pack_start(GTK_BOX(content), recolor_focused_check, FALSE, TRUE, 0);
+
+    gtk_grid_attach(GTK_GRID(colour_table_focus), bg_colour_label_focus,
+        0, 0, 1, 1);
+    gtk_grid_attach(GTK_GRID(colour_table_focus), bg_colour_select_focus,
+        1, 0, 1, 1);
+    gtk_grid_attach(GTK_GRID(colour_table_focus), fg_colour_label_focus,
+        0, 1, 1, 1);
+    gtk_grid_attach(GTK_GRID(colour_table_focus), fg_colour_select_focus,
+        1, 1, 1, 1);
+    gtk_box_pack_start(GTK_BOX(content), colour_table_focus, FALSE, TRUE, 0);
     gtk_box_pack_start(GTK_BOX(content), exit_check, FALSE, TRUE, 0);
     gtk_box_pack_start(GTK_BOX(content), close_check, FALSE, TRUE, 0);
     gtk_box_pack_start(GTK_BOX(content), dir_focus_check, FALSE, TRUE, 0);
@@ -240,6 +314,12 @@ static void mssh_pref_init(MSSHPref* pref)
         TRUE, 0);
     gtk_box_pack_start(GTK_BOX(content), columns_hbox, FALSE, TRUE, 0);
 
+    gtk_box_pack_start(GTK_BOX(backscroll_buffer_size_hbox), backscroll_buffer_size_label, FALSE,
+        TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(backscroll_buffer_size_hbox), backscroll_buffer_size_select, FALSE,
+        TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(content), backscroll_buffer_size_hbox, FALSE, TRUE, 0);
+
     gtk_box_pack_start(GTK_BOX(mod_hbox), mod_label, FALSE, TRUE, 0);
     gtk_box_pack_start(GTK_BOX(mod_hbox), mod_ctrl_check, FALSE, TRUE, 0);
     gtk_box_pack_start(GTK_BOX(mod_hbox), mod_alt_check, FALSE, TRUE, 0);
@@ -268,12 +348,20 @@ static void mssh_pref_init(MSSHPref* pref)
         G_CALLBACK(mssh_pref_fg_colour_select), NULL);
     g_signal_connect(G_OBJECT(bg_colour_select), "color-set",
         G_CALLBACK(mssh_pref_bg_colour_select), NULL);
+    g_signal_connect(G_OBJECT(fg_colour_select_focus), "color-set",
+        G_CALLBACK(mssh_pref_fg_colour_select_focus), NULL);
+    g_signal_connect(G_OBJECT(bg_colour_select_focus), "color-set",
+        G_CALLBACK(mssh_pref_bg_colour_select_focus), NULL);
     g_signal_connect(G_OBJECT(columns_select), "value-changed",
         G_CALLBACK(mssh_pref_columns_select), NULL);
+    g_signal_connect(G_OBJECT(backscroll_buffer_size_select), "value-changed",
+        G_CALLBACK(mssh_pref_backscroll_buffer_size_select), NULL);    
     g_signal_connect(G_OBJECT(timeout_select), "value-changed",
         G_CALLBACK(mssh_pref_timeout_select), NULL);
     g_signal_connect(G_OBJECT(close_check), "toggled",
         G_CALLBACK(mssh_pref_close_check), NULL);
+    g_signal_connect(G_OBJECT(recolor_focused_check), "toggled",
+        G_CALLBACK(mssh_pref_recolor_focused_check), NULL);
     g_signal_connect(G_OBJECT(exit_check), "toggled",
         G_CALLBACK(mssh_pref_exit_check), NULL);
      g_signal_connect(G_OBJECT(dir_focus_check), "toggled",
@@ -299,18 +387,32 @@ static void mssh_pref_init(MSSHPref* pref)
         TRUE, NULL);
     value = gconf_entry_get_value(entry);
     colour_s = gconf_value_get_string(value);
-    gdk_colormap_alloc_color(colour_map, &colour, TRUE, TRUE);
-    gdk_color_parse(colour_s, &colour);
-    gtk_color_button_set_color(GTK_COLOR_BUTTON(fg_colour_select),
+    gdk_rgba_parse(&colour, colour_s);
+    gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(fg_colour_select), 
         &colour);
 
     entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_BG_COLOUR, NULL,
         TRUE, NULL);
     value = gconf_entry_get_value(entry);
     colour_s = gconf_value_get_string(value);
-    gdk_colormap_alloc_color(colour_map, &colour, TRUE, TRUE);
-    gdk_color_parse(colour_s, &colour);
-    gtk_color_button_set_color(GTK_COLOR_BUTTON(bg_colour_select),
+    gdk_rgba_parse(&colour, colour_s);
+    gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(bg_colour_select),
+        &colour);
+
+    entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_FG_COLOUR_FOCUS, NULL,
+        TRUE, NULL);
+    value = gconf_entry_get_value(entry);
+    colour_s = gconf_value_get_string(value);
+    gdk_rgba_parse(&colour, colour_s);
+    gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(fg_colour_select_focus),
+        &colour);
+
+    entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_BG_COLOUR_FOCUS, NULL,
+        TRUE, NULL);
+    value = gconf_entry_get_value(entry);
+    colour_s = gconf_value_get_string(value);
+    gdk_rgba_parse(&colour, colour_s);
+    gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(bg_colour_select_focus),
         &colour);
 
     entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_COLUMNS, NULL,
@@ -319,6 +421,12 @@ static void mssh_pref_init(MSSHPref* pref)
     gtk_spin_button_set_value(GTK_SPIN_BUTTON(columns_select),
         gconf_value_get_int(value));
 
+    entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_BACKSCROLL_BUFFER_SIZE, NULL,
+        TRUE, NULL);
+    value = gconf_entry_get_value(entry);
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(backscroll_buffer_size_select),
+        gconf_value_get_int(value));
+
     entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_TIMEOUT, NULL,
         TRUE, NULL);
     value = gconf_entry_get_value(entry);
@@ -331,6 +439,12 @@ static void mssh_pref_init(MSSHPref* pref)
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(close_check),
         gconf_value_get_bool(value));
 
+    entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_RECOLOR_FOCUSED,
+        NULL, TRUE, NULL);
+    value = gconf_entry_get_value(entry);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(recolor_focused_check),
+        gconf_value_get_bool(value));
+
     entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_QUIT_ALL_ENDED,
             NULL, TRUE, NULL);
     value = gconf_entry_get_value(entry);

+ 7 - 0
src/mssh-terminal.c

@@ -27,9 +27,16 @@ gboolean mssh_terminal_isactive(MSSHTerminal *terminal)
         GTK_CHECK_MENU_ITEM(terminal->menu_item));
 }
 
+void mssh_terminal_set_backscroll_size(MSSHTerminal *terminal, gint *backscroll_buffer_size)
+{
+    /* use scrollback buffer from gconf*/
+    vte_terminal_set_scrollback_lines(VTE_TERMINAL(terminal), *backscroll_buffer_size);
+}
 void mssh_terminal_init_session(MSSHTerminal *terminal, char *hostname)
 {
     terminal->hostname = hostname;
+    /* use scrollback buffer from gconf*/
+    mssh_terminal_set_backscroll_size(terminal, &terminal->backscroll_buffer_size);
 
     terminal->menu_item = gtk_check_menu_item_new_with_label(
         terminal->hostname);

+ 2 - 0
src/mssh-terminal.h

@@ -23,6 +23,7 @@ typedef struct
     char *hostname;
     int started;
     int ended;
+    gint backscroll_buffer_size;
 } MSSHTerminal;
 
 typedef struct
@@ -42,6 +43,7 @@ GtkWidget* mssh_terminal_new(void);
 void mssh_terminal_destroy(MSSHTerminal *terminal);
 gboolean mssh_terminal_isactive(MSSHTerminal *terminal);
 void mssh_terminal_init_session(MSSHTerminal *terminal, char *hostname);
+void mssh_terminal_set_backscroll_size(MSSHTerminal *terminal, gint *backscroll_buffer_size);
 void mssh_terminal_start_session(MSSHTerminal *terminal, char **env);
 void mssh_terminal_send_host(MSSHTerminal *terminal);
 void mssh_terminal_send_string(MSSHTerminal *terminal, gchar *string);

+ 367 - 25
src/mssh-window.c

@@ -1,8 +1,10 @@
 #include <string.h>
 #include <stdlib.h>
+#include <stdio.h>
 
 #include <gconf/gconf-client.h>
 #include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
 
 #include "mssh-terminal.h"
 #include "mssh-pref.h"
@@ -11,6 +13,8 @@
 
 #include "config.h"
 
+#include <regex.h>  
+
 static void mssh_window_sendhost(GtkWidget *widget, gpointer data);
 static void mssh_window_destroy(GtkWidget *widget, gpointer data);
 static void mssh_window_pref(GtkWidget *widget, gpointer data);
@@ -28,6 +32,12 @@ static void mssh_window_insert(GtkWidget *widget, gchar *new_text,
 static void mssh_window_add_session(MSSHWindow *window, char *hostname);
 static void mssh_window_init(MSSHWindow* window);
 static void mssh_window_class_init(MSSHWindowClass *klass);
+static void mssh_window_add(GtkWidget *widget, gpointer data);
+gboolean mssh_window_dialog_emit_response(GtkWidget *widget, GObject *acceleratable,
+    guint keyval, GdkModifierType modifier, gpointer data);
+static void mssh_window_maximize(GtkWidget *widget, gpointer data);
+static void mssh_window_restore_layout(GtkWidget *widget, gpointer data);
+void mssh_window_relayout_for_one(MSSHWindow *window, GtkWidget *t);
 
 G_DEFINE_TYPE(MSSHWindow, mssh_window, GTK_TYPE_WINDOW)
 
@@ -55,6 +65,24 @@ static void mssh_window_sendhost(GtkWidget *widget, gpointer data)
     }
 }
 
+static void mssh_window_sendcommand(GtkWidget *widget, gpointer data)
+{
+    int i;
+    char *command;
+
+    MSSHWindow *window = MSSH_WINDOW(data);
+    GtkMenuItem *item = (GtkMenuItem *)widget;
+
+    command = g_datalist_get_data(MSSH_WINDOW(data)->commands, gtk_menu_item_get_label (item));
+
+    for(i = 0; i < window->terminals->len; i++)
+    {
+        mssh_terminal_send_string(g_array_index(window->terminals,
+            MSSHTerminal*, i), command);
+    }
+
+}
+
 static void mssh_window_destroy(GtkWidget *widget, gpointer data)
 {
     gtk_main_quit();
@@ -108,10 +136,22 @@ static gboolean mssh_window_key_press(GtkWidget *widget,
 static gboolean mssh_window_entry_focused(GtkWidget *widget,
     GtkDirectionType dir, gpointer data)
 {
+    GConfClient *client;
+    GConfEntry *entry;
     MSSHWindow *window = MSSH_WINDOW(data);
 
     gtk_window_set_title(GTK_WINDOW(window), PACKAGE_NAME" - All");
+    window->last_focus = NULL;
+
+    /* clear the coloring for the focused window */
+    client = gconf_client_get_default();
 
+    entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_FG_COLOUR, NULL,
+        TRUE, NULL);
+    mssh_gconf_notify_fg_colour(client, 0, entry, window);
+    entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_BG_COLOUR, NULL,
+        TRUE, NULL);
+    mssh_gconf_notify_bg_colour(client, 0, entry, window);
     return FALSE;
 }
 
@@ -120,6 +160,8 @@ gboolean mssh_window_focus(GtkWidget *widget, GObject *acceleratable,
 {
     MSSHWindow *window = MSSH_WINDOW(data);
     GtkWidget *focus;
+    GConfClient *client;
+    GConfEntry *entry;
 
     int i, idx = -1, len = window->terminals->len;
     int wcols = window->columns_override ? window->columns_override :
@@ -138,6 +180,17 @@ gboolean mssh_window_focus(GtkWidget *widget, GObject *acceleratable,
         }
     }
 
+    client = gconf_client_get_default();
+
+    /* recolor the windows */
+    if (window->recolor_focused) {
+        entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_FG_COLOUR, NULL,
+            TRUE, NULL);
+        mssh_gconf_notify_fg_colour(client, 0, entry, window);
+        entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_BG_COLOUR, NULL,
+            TRUE, NULL);
+        mssh_gconf_notify_bg_colour(client, 0, entry, window);
+    }
     if(focus == window->global_entry && keyval == GDK_KEY_Down &&
         window->dir_focus)
         idx = 0;
@@ -216,14 +269,20 @@ static gboolean mssh_window_session_close(gpointer data)
     }
     else
     {
+        /* set the focus on the entry only if the terminal closed has it */
+        if ( gtk_window_get_focus(GTK_WINDOW(data_pair->window)) == GTK_WIDGET(data_pair->terminal) ) {
+            gtk_window_set_focus(GTK_WINDOW(data_pair->window), GTK_WIDGET(data_pair->window->global_entry));
+        }
+
         gtk_widget_destroy(data_pair->terminal->menu_item);
 
-        gtk_container_remove(GTK_CONTAINER(data_pair->window->table),
+        gtk_container_remove(GTK_CONTAINER(data_pair->window->grid),
             GTK_WIDGET(data_pair->terminal));
 
         g_array_remove_index(data_pair->window->terminals, idx);
 
         mssh_window_relayout(data_pair->window);
+
     }
 
     if(data_pair->window->terminals->len == 0 &&
@@ -264,6 +323,8 @@ static void mssh_window_session_focused(MSSHTerminal *terminal,
     char *title;
     size_t len;
 
+    GConfClient *client;
+    GConfEntry *entry;
     MSSHWindow *window = MSSH_WINDOW(data);
 
     len = strlen(PACKAGE_NAME" - ") + strlen(terminal->hostname) + 1;
@@ -274,6 +335,25 @@ static void mssh_window_session_focused(MSSHTerminal *terminal,
     gtk_window_set_title(GTK_WINDOW(window), title);
 
     free(title);
+    client = gconf_client_get_default();
+
+    /* recolor all windows */
+    entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_FG_COLOUR, NULL,
+        TRUE, NULL);
+    mssh_gconf_notify_fg_colour(client, 0, entry, window);
+    entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_BG_COLOUR, NULL,
+        TRUE, NULL);
+    mssh_gconf_notify_bg_colour(client, 0, entry, window);
+
+    /* recolor the focused window - if needed */
+    if (window->recolor_focused && window->is_maximized == 0) {
+        entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_FG_COLOUR_FOCUS, NULL,
+            TRUE, NULL);
+        mssh_gconf_notify_fg_colour_focus(client, 0, entry, window);
+        entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_BG_COLOUR_FOCUS, NULL,
+            TRUE, NULL);
+        mssh_gconf_notify_bg_colour_focus(client, 0, entry, window);
+    }
 }
 
 void mssh_window_relayout(MSSHWindow *window)
@@ -285,7 +365,7 @@ void mssh_window_relayout(MSSHWindow *window)
     int wcols = window->columns_override ? window->columns_override :
         window->columns;
     int cols = (len < wcols) ? len : wcols;
-    int rows = (len + 0.5) / cols;
+    int width = 1;
 
     focus = gtk_window_get_focus(GTK_WINDOW(window));
 
@@ -314,16 +394,28 @@ void mssh_window_relayout(MSSHWindow *window)
             MSSHTerminal*, i);
 
         g_object_ref(terminal);
-        if(gtk_widget_get_parent(GTK_WIDGET(terminal)) == GTK_WIDGET(window->table))
+        if(gtk_widget_get_parent(GTK_WIDGET(terminal)) == GTK_WIDGET(window->grid))
         {
-            gtk_container_remove(GTK_CONTAINER(window->table),
+            gtk_container_remove(GTK_CONTAINER(window->grid),
                 GTK_WIDGET(terminal));
         }
 
-        gtk_table_attach(GTK_TABLE(window->table), GTK_WIDGET(terminal),
-            (i % cols), (i == len - 1) ? cols : (i % cols) + 1, i / cols,
-            (i / cols) + 1,
-            GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 1, 1);
+        /* Set margins to terminal widget */
+        gtk_widget_set_margin_start(GTK_WIDGET(terminal), 1);
+        gtk_widget_set_margin_end(GTK_WIDGET(terminal), 1);
+        gtk_widget_set_margin_top(GTK_WIDGET(terminal), 1);
+        gtk_widget_set_margin_bottom(GTK_WIDGET(terminal), 1);
+
+        if (i == len - 1) {
+            width = cols - (i % cols);
+        }
+        gtk_grid_attach(GTK_GRID(window->grid), /* grid */
+                        GTK_WIDGET(terminal),   /* child */
+                        (i % cols),             /* left */
+                        i / cols,               /* top */
+                        width,                  /* width */
+                        1);                     /* height */
+
         g_object_unref(terminal);
 
         if(!terminal->started)
@@ -333,11 +425,6 @@ void mssh_window_relayout(MSSHWindow *window)
         }
     }
 
-    if(len > 0)
-    {
-        gtk_table_resize(GTK_TABLE(window->table), rows, cols);
-    }
-
     client = gconf_client_get_default();
 
     gtk_widget_show_all(GTK_WIDGET(window));
@@ -359,6 +446,7 @@ static void mssh_window_add_session(MSSHWindow *window, char *hostname)
 {
     MSSHTerminal *terminal = MSSH_TERMINAL(mssh_terminal_new());
 
+    terminal->backscroll_buffer_size = window->backscroll_buffer_size;
     g_array_append_val(window->terminals, terminal);
 
     g_signal_connect(G_OBJECT(terminal), "session-closed",
@@ -378,7 +466,7 @@ static void mssh_window_init(MSSHWindow* window)
 {
     GConfClient *client;
 
-    GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
+    GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
     GtkWidget *entry = gtk_entry_new();
 
     GtkWidget *menu_bar = gtk_menu_bar_new();
@@ -388,16 +476,13 @@ static void mssh_window_init(MSSHWindow* window)
     GtkWidget *file_item = gtk_menu_item_new_with_label("File");
     GtkWidget *edit_item = gtk_menu_item_new_with_label("Edit");
     GtkWidget *server_item = gtk_menu_item_new_with_label("Servers");
+    GtkWidget *command_item = gtk_menu_item_new_with_label("Commands");
 
-    GtkWidget *file_quit = gtk_image_menu_item_new_from_stock(
-        GTK_STOCK_QUIT, NULL);
-    GtkWidget *file_sendhost = gtk_image_menu_item_new_with_label(
-        "Send hostname");
-/*  GtkWidget *file_add = gtk_image_menu_item_new_with_label(
-        "Add session");*/
+    GtkWidget *file_quit = gtk_menu_item_new_with_mnemonic("_Quit");
+    GtkWidget *file_sendhost = gtk_menu_item_new_with_label("Send hostname");
+    GtkWidget *file_add = gtk_menu_item_new_with_label("Add session");
 
-    GtkWidget *edit_pref = gtk_image_menu_item_new_from_stock(
-        GTK_STOCK_PREFERENCES, NULL);
+    GtkWidget *edit_pref = gtk_menu_item_new_with_mnemonic("_Preferences");
 
     GtkAccelGroup *accel = gtk_accel_group_new();
 
@@ -405,23 +490,35 @@ static void mssh_window_init(MSSHWindow* window)
 
     window->server_menu = gtk_menu_new();
 
+    window->command_menu = gtk_menu_new();
+
     window->global_entry = entry;
 
     window->last_closed = -1;
 
     window->terminals = g_array_new(FALSE, TRUE, sizeof(MSSHTerminal*));
 
+    window->backscroll_buffer_size = 5000;
+
+    window->is_maximized = 0;
+
+   window->recolor_focused = FALSE;
+
     gtk_menu_item_set_submenu(GTK_MENU_ITEM(file_item), file_menu);
     gtk_menu_item_set_submenu(GTK_MENU_ITEM(edit_item), edit_menu);
     gtk_menu_item_set_submenu(GTK_MENU_ITEM(server_item),
         window->server_menu);
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(command_item),
+        window->command_menu);
 
-/*  gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), file_add);*/
+    gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), file_add);
     gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), file_sendhost);
     gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), file_quit);
     gtk_menu_shell_append(GTK_MENU_SHELL(edit_menu), edit_pref);
     g_signal_connect(G_OBJECT(file_sendhost), "activate",
         G_CALLBACK(mssh_window_sendhost), window);
+    g_signal_connect(G_OBJECT(file_add), "activate",
+        G_CALLBACK(mssh_window_add), window);
     g_signal_connect(G_OBJECT(file_quit), "activate",
         G_CALLBACK(mssh_window_destroy), window);
     g_signal_connect(G_OBJECT(edit_pref), "activate",
@@ -430,6 +527,7 @@ static void mssh_window_init(MSSHWindow* window)
     gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), file_item);
     gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), edit_item);
     gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), server_item);
+    gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), command_item);
 
     g_signal_connect(G_OBJECT(entry), "key-press-event",
         G_CALLBACK(mssh_window_key_press), window);
@@ -441,8 +539,10 @@ static void mssh_window_init(MSSHWindow* window)
     gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, TRUE, 0);
     gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, TRUE, 2);
 
-    window->table = gtk_table_new(1, 1, TRUE);
-    gtk_box_pack_start(GTK_BOX(vbox), window->table, TRUE, TRUE, 0);
+    window->grid = gtk_grid_new();
+    gtk_grid_set_row_homogeneous(GTK_GRID(window->grid), TRUE);
+    gtk_grid_set_column_homogeneous(GTK_GRID(window->grid), TRUE);
+    gtk_box_pack_start(GTK_BOX(vbox), window->grid, TRUE, TRUE, 0);
 
     gtk_container_add(GTK_CONTAINER(window), vbox);
 
@@ -461,25 +561,35 @@ static void mssh_window_init(MSSHWindow* window)
         mssh_gconf_notify_fg_colour, window, NULL, NULL);
     gconf_client_notify_add(client, MSSH_GCONF_KEY_BG_COLOUR,
         mssh_gconf_notify_bg_colour, window, NULL, NULL);
+    gconf_client_notify_add(client, MSSH_GCONF_KEY_FG_COLOUR_FOCUS,
+        mssh_gconf_notify_fg_colour_focus, window, NULL, NULL);
+    gconf_client_notify_add(client, MSSH_GCONF_KEY_BG_COLOUR_FOCUS,
+        mssh_gconf_notify_bg_colour_focus, window, NULL, NULL);
     gconf_client_notify_add(client, MSSH_GCONF_KEY_COLUMNS,
         mssh_gconf_notify_columns, window, NULL, NULL);
     gconf_client_notify_add(client, MSSH_GCONF_KEY_TIMEOUT,
         mssh_gconf_notify_timeout, window, NULL, NULL);
     gconf_client_notify_add(client, MSSH_GCONF_KEY_CLOSE_ENDED,
         mssh_gconf_notify_close_ended, window, NULL, NULL);
+    gconf_client_notify_add(client, MSSH_GCONF_KEY_RECOLOR_FOCUSED,
+        mssh_gconf_notify_recolor_focused, window, NULL, NULL);
     gconf_client_notify_add(client, MSSH_GCONF_KEY_QUIT_ALL_ENDED,
         mssh_gconf_notify_quit_all_ended, window, NULL, NULL);
     gconf_client_notify_add(client, MSSH_GCONF_KEY_DIR_FOCUS,
         mssh_gconf_notify_dir_focus, window, NULL, NULL);
     gconf_client_notify_add(client, MSSH_GCONF_KEY_MODIFIER,
         mssh_gconf_notify_modifier, window, NULL, NULL);
+    gconf_client_notify_add(client, MSSH_GCONF_KEY_BACKSCROLL_BUFFER_SIZE,
+        mssh_gconf_backscroll_buffer_size, window, NULL, NULL);
 
     gconf_client_notify(client, MSSH_GCONF_KEY_COLUMNS);
     gconf_client_notify(client, MSSH_GCONF_KEY_TIMEOUT);
     gconf_client_notify(client, MSSH_GCONF_KEY_CLOSE_ENDED);
+    gconf_client_notify(client, MSSH_GCONF_KEY_RECOLOR_FOCUSED);
     gconf_client_notify(client, MSSH_GCONF_KEY_QUIT_ALL_ENDED);
     gconf_client_notify(client, MSSH_GCONF_KEY_DIR_FOCUS);
     gconf_client_notify(client, MSSH_GCONF_KEY_MODIFIER);
+    gconf_client_notify(client, MSSH_GCONF_KEY_BACKSCROLL_BUFFER_SIZE);
 
     gtk_accel_group_connect(accel, GDK_KEY_Up, window->modifier,
         GTK_ACCEL_VISIBLE, g_cclosure_new(
@@ -494,6 +604,16 @@ static void mssh_window_init(MSSHWindow* window)
         GTK_ACCEL_VISIBLE, g_cclosure_new(
         G_CALLBACK(mssh_window_focus), window, NULL));
 
+    /* bind Ctrl + Shift + x to toggling maximize terminal */
+    gtk_accel_group_connect(accel, GDK_KEY_x, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
+        GTK_ACCEL_VISIBLE, g_cclosure_new(
+        G_CALLBACK(mssh_window_toggle_maximize), window, NULL));
+
+    /* bind Ctrl + Shift + N to show the dialog for adding new sessions */
+    gtk_accel_group_connect(accel, GDK_KEY_n, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
+        GTK_ACCEL_VISIBLE, g_cclosure_new(
+        G_CALLBACK(mssh_window_add), window, NULL));
+
     window->accel = accel;
 
     gtk_window_add_accel_group(GTK_WINDOW(window), accel);
@@ -525,6 +645,228 @@ void mssh_window_start_session(MSSHWindow* window, char **env,
     mssh_window_relayout(window);
 }
 
+void mssh_window_add_command(GQuark key_id, gpointer data, gpointer user_data)
+{
+    GtkWidget *menu_item;
+    GtkWidget* window = (GtkWidget *)user_data;
+
+    menu_item = gtk_menu_item_new_with_label(g_quark_to_string (key_id));
+
+    gtk_menu_shell_append(GTK_MENU_SHELL(MSSH_WINDOW(window)->command_menu), menu_item);
+    g_signal_connect(G_OBJECT(menu_item), "activate",
+        G_CALLBACK(mssh_window_sendcommand), window);
+}
+
 static void mssh_window_class_init(MSSHWindowClass *klass)
 {
 }
+
+void mssh_window_relayout_for_one(MSSHWindow *window, GtkWidget *t)
+{
+
+    GConfClient *client;
+    GConfEntry *entry;
+    int len = window->terminals->len;
+    int wcols = window->columns_override ? window->columns_override :
+        window->columns;
+    int cols = (len < wcols) ? len : wcols;
+    int rows = (len + 1) / cols;
+
+    /* get the terminal widget */
+    GtkWidget *terminal = GTK_WIDGET(t);
+
+    g_object_ref(terminal);
+
+    /* remove the widget from the container temporarily */
+    gtk_container_remove(GTK_CONTAINER(window->grid), GTK_WIDGET(terminal));
+
+    /* add it back again, now resized */
+    gtk_grid_attach(GTK_GRID(window->grid), GTK_WIDGET(terminal), 0, 0, cols, rows);
+
+    /* make the terminal focused */
+    gtk_window_set_focus(GTK_WINDOW(window), GTK_WIDGET(terminal));
+
+    /* remove the coloring */
+    if (window->recolor_focused) {
+
+        client = gconf_client_get_default();
+
+        entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_FG_COLOUR, NULL,
+            TRUE, NULL);
+        mssh_gconf_notify_fg_colour(client, 0, entry, window);
+        entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_BG_COLOUR, NULL,
+            TRUE, NULL);
+        mssh_gconf_notify_bg_colour(client, 0, entry, window);
+    }
+
+    g_object_unref(terminal);
+
+}
+
+gboolean mssh_window_toggle_maximize(GtkWidget *widget, GObject *acceleratable,
+    guint keyval, GdkModifierType modifier, gpointer data)
+{
+
+    MSSHWindow *window = MSSH_WINDOW(data);
+
+    if (window->is_maximized) {
+        /* toggle restore */
+        mssh_window_restore_layout(widget, data);
+    } else {
+        /* toggle maximize */
+        mssh_window_maximize(widget, data);
+    }
+    return TRUE;
+}
+
+static void mssh_window_maximize(GtkWidget *widget, gpointer data)
+{
+
+    /* find the id of the currently focused window (if any) */
+    MSSHWindow *window = MSSH_WINDOW(data);
+
+    int i;
+    int idx = -1;
+    int len = window->terminals->len;
+    GConfClient *client;
+    GConfEntry *entry;
+
+    /* get the currently focused window */
+    GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(window));
+    /* save the currently focused window so we can restore it later */
+    window->last_focus = focus;
+
+    /* find the focused window in the terminal list */
+    for(i = 0; i < len; i++)
+    {
+        if(focus == GTK_WIDGET(g_array_index(window->terminals,
+            MSSHTerminal*, i)))
+        {
+            idx = i;
+            break;
+        }
+    }
+
+    /* recolor the window with the normal color */
+    if (window->recolor_focused) {
+        client = gconf_client_get_default();
+
+        entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_FG_COLOUR, NULL,
+            TRUE, NULL);
+        mssh_gconf_notify_fg_colour(client, 0, entry, window);
+        entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_BG_COLOUR, NULL,
+            TRUE, NULL);
+        mssh_gconf_notify_bg_colour(client, 0, entry, window);
+    }
+    if (idx == -1) {
+        /* there's no window focused, do nothing */
+    } else {
+        /* call relayout, it will reposition the widget to occupy the whole table */
+        mssh_window_relayout_for_one(window, GTK_WIDGET(g_array_index(window->terminals,
+                    MSSHTerminal*, idx)));
+        window->is_maximized = 1;
+    }
+}
+
+static void mssh_window_restore_layout(GtkWidget *widget, gpointer data)
+{
+
+    GConfClient *client;
+    GConfEntry *entry;
+    /* get the window */
+    MSSHWindow *window = MSSH_WINDOW(data);
+
+    /* just call relayout */
+    mssh_window_relayout(window);
+    window->is_maximized = 0;
+
+    /* restore the focus */
+    if (window->last_focus != NULL) {
+        gtk_window_set_focus(GTK_WINDOW(window), window->last_focus);
+    }
+
+    /* recolor the focused window - if needed */
+    client = gconf_client_get_default();
+    if (window->recolor_focused && window->is_maximized == 0) {
+        entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_FG_COLOUR_FOCUS, NULL,
+            TRUE, NULL);
+        mssh_gconf_notify_fg_colour_focus(client, 0, entry, window);
+        entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_BG_COLOUR_FOCUS, NULL,
+            TRUE, NULL);
+        mssh_gconf_notify_bg_colour_focus(client, 0, entry, window);
+	}
+}
+
+/* show a popup window for adding new sessions  */
+static void mssh_window_add(GtkWidget *widget, gpointer data)
+{
+
+    MSSHWindow *window = MSSH_WINDOW(data);
+    GtkWidget *dialog, *label, *content_area, *button_add;
+	GtkWidget *new_session_entry;
+	gint result;
+    
+    /* create new dialog */
+    dialog = gtk_dialog_new();
+    /* get the content area that will be packed */
+    content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+    /* label for text */
+    label = gtk_label_new ("Add new session with hostname: ");
+
+    /* Add the label and entry, and show everything we've added to the dialog */
+    new_session_entry = gtk_entry_new();
+    gtk_entry_set_max_length (GTK_ENTRY(new_session_entry), 255);
+
+    /* pack the widgets */
+    gtk_container_add (GTK_CONTAINER (content_area), label);
+    gtk_container_add (GTK_CONTAINER (content_area), new_session_entry);
+    /* add two buttons */
+    button_add = gtk_dialog_add_button(GTK_DIALOG(dialog), "Add", GTK_RESPONSE_ACCEPT);
+    gtk_dialog_add_button(GTK_DIALOG(dialog), "Cancel", GTK_RESPONSE_CANCEL);
+    /* make the add button the default */
+    gtk_widget_grab_default(button_add);
+    /* set dialog properties (modal, etc) */
+    gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
+    gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
+    gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(window));
+    /* set it's title */
+    gtk_window_set_title(GTK_WINDOW(dialog), "Add new session");
+
+    /* catch the activate signal (hitting enter) */
+    g_signal_connect(G_OBJECT(new_session_entry), "activate",
+        G_CALLBACK(mssh_window_dialog_emit_response), window);
+
+    /* show the dialog and it's widgets */
+    gtk_widget_show_all (dialog);
+
+    /* wait for input */
+    result = gtk_dialog_run (GTK_DIALOG (dialog));
+    switch (result)
+      {
+        case GTK_RESPONSE_ACCEPT:
+           mssh_window_add_session(window, (gchar*) gtk_entry_get_text(GTK_ENTRY(new_session_entry)));
+           /* relayout */
+           mssh_window_relayout(window);
+           break;
+        default:
+           /* do nothing */
+           break;
+      }
+    gtk_widget_destroy (dialog);
+}
+
+/* catch the 'activate' signal of the entry (return has been pushed)  */
+/* emit the response for accept, simulating a mouse click on the add button  */
+gboolean mssh_window_dialog_emit_response(GtkWidget *widget, GObject *acceleratable,
+    guint keyval, GdkModifierType modifier, gpointer data)
+{
+
+    /* get the dialog by getting the parent of the parent for the emitting (entry) widget */
+    GtkWidget *vbox = gtk_widget_get_parent(widget);
+    GtkWidget *dialog = gtk_widget_get_parent(vbox);
+    /* emit the response signal simulating the clicking of 'ok'  */
+    gtk_dialog_response (GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
+
+    return TRUE;
+}

+ 10 - 1
src/mssh-window.h

@@ -21,8 +21,9 @@ G_BEGIN_DECLS
 typedef struct
 {
     GtkWindow widget;
-    GtkWidget *table;
+    GtkWidget *grid;
     GtkWidget *server_menu;
+    GtkWidget *command_menu;
     GtkWidget *global_entry;
     GtkAccelGroup *accel;
     GArray *terminals;
@@ -35,6 +36,11 @@ typedef struct
     gint modifier;
     gint dir_focus;
     gint last_closed;
+    gint backscroll_buffer_size;
+    GtkWidget *last_focus;
+    int is_maximized;
+    gboolean recolor_focused;
+    GData **commands;
 } MSSHWindow;
 
 typedef struct
@@ -47,10 +53,13 @@ GType mssh_window_get_type(void) G_GNUC_CONST;
 GtkWidget* mssh_window_new(void);
 void mssh_window_start_session(MSSHWindow* window, char **env,
     GArray *hosts, long cols);
+void mssh_window_add_command(GQuark key_id, gpointer data, gpointer user_data);
 void mssh_window_relayout(MSSHWindow *window);
 void mssh_window_session_closed(MSSHTerminal *terminal, gpointer data);
 gboolean mssh_window_focus(GtkWidget *widget, GObject *acceleratable,
     guint keyval, GdkModifierType modifier, gpointer data);
+gboolean mssh_window_toggle_maximize(GtkWidget *widget, GObject *acceleratable,
+    guint keyval, GdkModifierType modifier, gpointer data);
 
 G_END_DECLS
 

+ 68 - 1
src/mssh.c

@@ -121,7 +121,10 @@ GData **parse_aliases(char *conffile)
         if(strcmp(line, "") == 0)
             continue;
 
-        if( *line == '#')
+        if(*line == '#')
+            continue;
+
+        if(*line == '{')
             continue;
 
         if((sep = strchr(line, ':')) == NULL)
@@ -162,6 +165,65 @@ GData **parse_aliases(char *conffile)
     return aliases;
 }
 
+GData **parse_commands(char *conffile)
+{
+    FILE *file;
+    char *line;
+    int lineno = 0;
+
+    GData **commands = malloc(sizeof(GData*));
+    g_datalist_init(commands);
+
+    if((file = fopen(conffile, "r")) == NULL)
+        return commands;
+
+    while((line = fgetline(file)) != NULL)
+    {
+        char *sep, *command, *commandline;
+
+        lineno++;
+
+        if(strcmp(line, "") == 0)
+            continue;
+
+        if(*line == '#')
+            continue;
+
+        if(*line != '{')
+            continue;
+
+        if((sep = strchr(line, '}')) == NULL)
+        {
+            printf("Line %d: Failed to parse line '%s'\n", lineno, line);
+            exit(EXIT_FAILURE);
+        }
+
+        *sep = '\0';
+        command = line + 1;
+
+        if((commandline = index(sep + 1, ' ')) == NULL)
+        {
+            printf("Line %d: Command Alias '%s' specifies no command\n", lineno,
+                command);
+            exit(EXIT_FAILURE);
+        }
+        while ( *commandline == ' ' ) { commandline++; }
+
+        sep = commandline;
+        while ( ( sep = index(sep, '\\') ) != NULL ) {
+            if ( *(sep+1) == 'n' ) {
+                *sep = ' ';
+                *(sep+1) = '\n';
+            }
+            sep++;
+        }
+
+        g_datalist_set_data(commands, command, commandline);
+    }
+
+    return commands;
+}
+
 int main(int argc, char* argv[], char* env[])
 {
     GtkWidget* window;
@@ -169,6 +231,7 @@ int main(int argc, char* argv[], char* env[])
     char *home, *conffile;
     long cols = 0;
     GData **aliases = NULL;
+    GData **commands = NULL;
     GArray *hosts = NULL;
 
     static struct option long_options[] =
@@ -188,6 +251,7 @@ int main(int argc, char* argv[], char* env[])
         snprintf(conffile, len, "%s/%s", home, CONFFILE);
 
         aliases = parse_aliases(conffile);
+        commands = parse_commands(conffile);
         free(conffile);
     }
     else
@@ -289,7 +353,10 @@ int main(int argc, char* argv[], char* env[])
     g_signal_connect(G_OBJECT(window), "destroy",
         G_CALLBACK(on_mssh_destroy), NULL);
 
+    MSSH_WINDOW(window)->commands = commands;
+
     mssh_window_start_session(MSSH_WINDOW(window), env, hosts, cols);
+    g_datalist_foreach(commands, mssh_window_add_command, window);
 
     gtk_widget_show_all(window);
     gtk_main();