Browse Source

Imported Upstream version 1.2

Bradley Smith 14 years ago
parent
commit
f8496524ca
14 changed files with 1381 additions and 1001 deletions
  1. 20 0
      ChangeLog
  2. 2 2
      aclocal.m4
  3. 110 79
      configure
  4. 1 1
      configure.ac
  5. 22 1
      mssh.schemas
  6. 167 114
      src/mssh-gconf.c
  7. 21 15
      src/mssh-gconf.h
  8. 285 202
      src/mssh-pref.c
  9. 14 10
      src/mssh-pref.h
  10. 59 56
      src/mssh-terminal.c
  11. 19 19
      src/mssh-terminal.h
  12. 406 270
      src/mssh-window.c
  13. 27 19
      src/mssh-window.h
  14. 228 213
      src/mssh.c

+ 20 - 0
ChangeLog

@@ -1,3 +1,23 @@
+2010-01-15  Bradley Smith  <[email protected]>
+
+	Bump to version 1.2
+
+	Add directional focus option, and focus next window on session close.
+
+	Redo accel group when modifier changes.
+
+	Add directional focus.
+
+2010-01-14  Bradley Smith  <[email protected]>
+
+	Make sure focus is preserved when a session closes, restore back to global entry if focused window closed.
+
+	Add call to vte_terminal_set_word_chars.
+
+	Get rid of horrible tabs in files.
+
+	Add columns override option.
+
 2009-09-05  Bradley Smith  <[email protected]>
 
 	Bump version to 1.1.

+ 2 - 2
aclocal.m4

@@ -13,8 +13,8 @@
 
 m4_ifndef([AC_AUTOCONF_VERSION],
   [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
-m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.64],,
-[m4_warning([this file was generated for autoconf 2.64.
+m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.65],,
+[m4_warning([this file was generated for autoconf 2.65.
 You have another version of autoconf.  It may work, but is not guaranteed to.
 If you have problems, you may need to regenerate the build system entirely.
 To do so, use the procedure documented by the package, typically `autoreconf'.])])

+ 110 - 79
configure

@@ -1,12 +1,14 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.64 for MultiSSH 1.1.
+# Generated by GNU Autoconf 2.65 for MultiSSH 1.2.
 #
 # Report bugs to <[email protected]>.
 #
+#
 # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
-# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software
-# Foundation, Inc.
+# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+#
 #
 # This configure script is free software; the Free Software Foundation
 # gives unlimited permission to copy, distribute and modify it.
@@ -526,7 +528,8 @@ as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
 as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
 
 
-exec 7<&0 </dev/null 6>&1
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
 
 # Name of the host.
 # hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
@@ -548,8 +551,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='MultiSSH'
 PACKAGE_TARNAME='mssh'
-PACKAGE_VERSION='1.1'
-PACKAGE_STRING='MultiSSH 1.1'
+PACKAGE_VERSION='1.2'
+PACKAGE_STRING='MultiSSH 1.2'
 PACKAGE_BUGREPORT='[email protected]'
 PACKAGE_URL=''
 
@@ -1202,7 +1205,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures MultiSSH 1.1 to adapt to many kinds of systems.
+\`configure' configures MultiSSH 1.2 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1268,7 +1271,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of MultiSSH 1.1:";;
+     short | recursive ) echo "Configuration of MultiSSH 1.2:";;
    esac
   cat <<\_ACEOF
 
@@ -1295,7 +1298,7 @@ Some influential environment variables:
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
               nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
-  CPPFLAGS    C/C++/Objective C preprocessor flags, e.g. -I<include dir> if
+  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
               you have headers in a nonstandard directory <include dir>
   PKG_CONFIG  path to pkg-config utility
   MSSH_CFLAGS C compiler flags for MSSH, overriding pkg-config
@@ -1367,8 +1370,8 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-MultiSSH configure 1.1
-generated by GNU Autoconf 2.64
+MultiSSH configure 1.2
+generated by GNU Autoconf 2.65
 
 Copyright (C) 2009 Free Software Foundation, Inc.
 This configure script is free software; the Free Software Foundation
@@ -1415,15 +1418,15 @@ sed 's/^/| /' conftest.$ac_ext >&5
 	ac_retval=1
 fi
   eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
-  return $ac_retval
+  as_fn_set_status $ac_retval
 
 } # ac_fn_c_try_compile
 cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by MultiSSH $as_me 1.1, which was
-generated by GNU Autoconf 2.64.  Invocation command line was
+It was created by MultiSSH $as_me 1.2, which was
+generated by GNU Autoconf 2.65.  Invocation command line was
 
   $ $0 $@
 
@@ -1676,7 +1679,7 @@ fi
 for ac_site_file in "$ac_site_file1" "$ac_site_file2"
 do
   test "x$ac_site_file" = xNONE && continue
-  if test -r "$ac_site_file"; then
+  if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
     { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
 $as_echo "$as_me: loading site script $ac_site_file" >&6;}
     sed 's/^/| /' "$ac_site_file" >&5
@@ -1685,9 +1688,9 @@ $as_echo "$as_me: loading site script $ac_site_file" >&6;}
 done
 
 if test -r "$cache_file"; then
-  # Some versions of bash will fail to source /dev/null (special
-  # files actually), so we avoid doing that.
-  if test -f "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special files
+  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.
+  if test /dev/null != "$cache_file" && test -f "$cache_file"; then
     { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
 $as_echo "$as_me: loading cache $cache_file" >&6;}
     case $cache_file in
@@ -2108,6 +2111,7 @@ IFS=$as_save_IFS
 
 fi
 
+  test -d ./--version && rmdir ./--version
   if test "${ac_cv_path_mkdir+set}" = set; then
     MKDIR_P="$ac_cv_path_mkdir -p"
   else
@@ -2115,7 +2119,6 @@ fi
     # value for MKDIR_P within a source directory, because that will
     # break other packages using the cache if that directory is
     # removed, or if the value is a relative name.
-    test -d ./--version && rmdir ./--version
     MKDIR_P="$ac_install_sh -d"
   fi
 fi
@@ -2232,7 +2235,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='mssh'
- VERSION='1.1'
+ VERSION='1.2'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -2595,32 +2598,30 @@ $as_echo "$ac_try_echo"; } >&5
 ... rest of stderr output deleted ...
          10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
-    rm -f conftest.er1 conftest.err
   fi
+  rm -f conftest.er1 conftest.err
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }
 done
 
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-#include <stdio.h>
+
 int
 main ()
 {
-FILE *f = fopen ("conftest.out", "w");
- return ferror (f) || fclose (f) != 0;
 
   ;
   return 0;
 }
 _ACEOF
 ac_clean_files_save=$ac_clean_files
-ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out conftest.out"
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
 # Try to create an executable without -o first, disregard a.out.
 # It will help us diagnose broken compilers, and finding out an intuition
 # of exeext.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
-$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
 ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
 
 # The possible output files:
@@ -2682,10 +2683,10 @@ test "$ac_cv_exeext" = no && ac_cv_exeext=
 else
   ac_file=''
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
-$as_echo "$ac_file" >&6; }
 if test -z "$ac_file"; then :
-  $as_echo "$as_me: failed program was:" >&5
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
 sed 's/^/| /' conftest.$ac_ext >&5
 
 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
@@ -2693,51 +2694,18 @@ $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
 { as_fn_set_status 77
 as_fn_error "C compiler cannot create executables
 See \`config.log' for more details." "$LINENO" 5; }; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
 fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
 ac_exeext=$ac_cv_exeext
 
-# Check that the compiler produces executables we can run.  If not, either
-# the compiler is broken, or we cross compile.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
-$as_echo_n "checking whether the C compiler works... " >&6; }
-# If not cross compiling, check that we can run a simple program.
-if test "$cross_compiling" != yes; then
-  if { ac_try='./$ac_file'
-  { { case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
-  (eval "$ac_try") 2>&5
-  ac_status=$?
-  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
-  test $ac_status = 0; }; }; then
-    cross_compiling=no
-  else
-    if test "$cross_compiling" = maybe; then
-	cross_compiling=yes
-    else
-	{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error "cannot run C compiled programs.
-If you meant to cross compile, use \`--host'.
-See \`config.log' for more details." "$LINENO" 5; }
-    fi
-  fi
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-
-rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out conftest.out
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
 ac_clean_files=$ac_clean_files_save
-# Check that the compiler produces executables we can run.  If not, either
-# the compiler is broken, or we cross compile.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
-$as_echo_n "checking whether we are cross compiling... " >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
-$as_echo "$cross_compiling" >&6; }
-
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
 $as_echo_n "checking for suffix of executables... " >&6; }
 if { { ac_try="$ac_link"
@@ -2770,13 +2738,72 @@ $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
 as_fn_error "cannot compute suffix of executables: cannot compile and link
 See \`config.log' for more details." "$LINENO" 5; }
 fi
-rm -f conftest$ac_cv_exeext
+rm -f conftest conftest$ac_cv_exeext
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
 $as_echo "$ac_cv_exeext" >&6; }
 
 rm -f conftest.$ac_ext
 EXEEXT=$ac_cv_exeext
 ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+  { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+  if { ac_try='./conftest$ac_cv_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+	cross_compiling=yes
+    else
+	{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." "$LINENO" 5; }
+    fi
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
 $as_echo_n "checking for suffix of object files... " >&6; }
 if test "${ac_cv_objext+set}" = set; then :
@@ -4063,8 +4090,8 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by MultiSSH $as_me 1.1, which was
-generated by GNU Autoconf 2.64.  Invocation command line was
+This file was extended by MultiSSH $as_me 1.2, which was
+generated by GNU Autoconf 2.65.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
   CONFIG_HEADERS  = $CONFIG_HEADERS
@@ -4104,6 +4131,7 @@ Usage: $0 [OPTION]... [TAG]...
 
   -h, --help       print this help, then exit
   -V, --version    print version number and configuration settings, then exit
+      --config     print configuration, then exit
   -q, --quiet, --silent
                    do not print progress messages
   -d, --debug      don't remove temporary files
@@ -4126,10 +4154,11 @@ Report bugs to <[email protected]>."
 
 _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-MultiSSH config.status 1.1
-configured by $0, generated by GNU Autoconf 2.64,
-  with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
+MultiSSH config.status 1.2
+configured by $0, generated by GNU Autoconf 2.65,
+  with options \\"\$ac_cs_config\\"
 
 Copyright (C) 2009 Free Software Foundation, Inc.
 This config.status script is free software; the Free Software Foundation
@@ -4167,6 +4196,8 @@ do
     ac_cs_recheck=: ;;
   --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
     $as_echo "$ac_cs_version"; exit ;;
+  --config | --confi | --conf | --con | --co | --c )
+    $as_echo "$ac_cs_config"; exit ;;
   --debug | --debu | --deb | --de | --d | -d )
     debug=: ;;
   --file | --fil | --fi | --f )
@@ -4355,7 +4386,7 @@ s/'"$ac_delim"'$//
 t delim
 :nl
 h
-s/\(.\{148\}\).*/\1/
+s/\(.\{148\}\)..*/\1/
 t more1
 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
 p
@@ -4369,7 +4400,7 @@ s/.\{148\}//
 t nl
 :delim
 h
-s/\(.\{148\}\).*/\1/
+s/\(.\{148\}\)..*/\1/
 t more2
 s/["\\]/\\&/g; s/^/"/; s/$/"/
 p

+ 1 - 1
configure.ac

@@ -1,4 +1,4 @@
-AC_INIT([MultiSSH], [1.1], [[email protected]], [mssh])
+AC_INIT([MultiSSH], [1.2], [[email protected]], [mssh])
 AM_CONFIG_HEADER([config.h])
 AM_INIT_AUTOMAKE
 

+ 22 - 1
mssh.schemas

@@ -77,6 +77,27 @@
                 <long>Quit MSSH when all ssh sessions have ended</long>
             </locale>
         </schema>
-
+        <schema>
+            <key>/schemas/apps/mssh/dir_focus</key>
+            <applyto>/apps/mssh/dir_focus</applyto>
+            <owner>mssh</owner>
+            <type>bool</type>
+            <default>true</default>
+            <locale name="C">
+                <short>Use directional focus</short>
+                <long>Uses directional focus rather than linear focus</long>
+            </locale>
+        </schema>
+        <schema>
+            <key>/schemas/apps/mssh/modifier</key>
+            <applyto>/apps/mssh/modifier</applyto>
+            <owner>mssh</owner>
+            <type>int</type>
+            <default>8</default>
+            <locale name="C">
+                <short>Shortcut modifier</short>
+                <long>Modifier used in focus shortcuts</long>
+            </locale>
+        </schema>
     </schemalist>
 </gconfschemafile>

+ 167 - 114
src/mssh-gconf.c

@@ -1,161 +1,214 @@
 #include <vte/vte.h>
+#include <gdk/gdkkeysyms.h>
 
 #include "mssh-gconf.h"
 #include "mssh-window.h"
 #include "mssh-terminal.h"
 
 void mssh_gconf_notify_font(GConfClient *client, guint cnxn_id,
-	GConfEntry *entry, gpointer data)
+    GConfEntry *entry, gpointer data)
 {
-	GConfValue *value;
-	const gchar *font;
-	int i;
+    GConfValue *value;
+    const gchar *font;
+    int i;
 
-	MSSHWindow *window = MSSH_WINDOW(data);
+    MSSHWindow *window = MSSH_WINDOW(data);
 
-	value = gconf_entry_get_value(entry);
-	font = gconf_value_get_string(value);
+    value = gconf_entry_get_value(entry);
+    font = gconf_value_get_string(value);
 
-	for(i = 0; i < window->terminals->len; i++)
-	{
-		vte_terminal_set_font_from_string(VTE_TERMINAL(g_array_index(
-			window->terminals, MSSHTerminal*, i)), font);
-	}
+    for(i = 0; i < window->terminals->len; i++)
+    {
+        vte_terminal_set_font_from_string(VTE_TERMINAL(g_array_index(
+            window->terminals, MSSHTerminal*, i)), font);
+    }
 }
 
 void mssh_gconf_notify_fg_colour(GConfClient *client, guint cnxn_id,
-	GConfEntry *entry, gpointer data)
+    GConfEntry *entry, gpointer data)
 {
-	GConfValue *value;
-	const gchar *colour_s;
-	GdkVisual *visual = gdk_visual_get_system();
-	GdkColormap *colour_map;
-	GdkColor 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);
-
-	for(i = 0; i < window->terminals->len; i++)
-	{
-		vte_terminal_set_color_foreground(VTE_TERMINAL(g_array_index(
-			window->terminals, MSSHTerminal*, i)), &colour);
-	}
+    GConfValue *value;
+    const gchar *colour_s;
+    GdkVisual *visual = gdk_visual_get_system();
+    GdkColormap *colour_map;
+    GdkColor 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);
+
+    for(i = 0; i < window->terminals->len; i++)
+    {
+        vte_terminal_set_color_foreground(VTE_TERMINAL(g_array_index(
+            window->terminals, MSSHTerminal*, i)), &colour);
+    }
 }
 
 void mssh_gconf_notify_bg_colour(GConfClient *client, guint cnxn_id,
-	GConfEntry *entry, gpointer data)
+    GConfEntry *entry, gpointer data)
 {
-	GConfValue *value;
-	const gchar *colour_s;
-	GdkVisual *visual = gdk_visual_get_system();
-	GdkColormap *colour_map;
-	GdkColor 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);
-
-	for(i = 0; i < window->terminals->len; i++)
-	{
-		vte_terminal_set_color_background(VTE_TERMINAL(g_array_index(
-			window->terminals, MSSHTerminal*, i)), &colour);
-	}
+    GConfValue *value;
+    const gchar *colour_s;
+    GdkVisual *visual = gdk_visual_get_system();
+    GdkColormap *colour_map;
+    GdkColor 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);
+
+    for(i = 0; i < window->terminals->len; i++)
+    {
+        vte_terminal_set_color_background(VTE_TERMINAL(g_array_index(
+            window->terminals, MSSHTerminal*, i)), &colour);
+    }
 }
 
 void mssh_gconf_notify_columns(GConfClient *client, guint cnxn_id,
-	GConfEntry *entry, gpointer data)
+    GConfEntry *entry, gpointer data)
 {
-	GConfValue *value;
-	int columns;
+    GConfValue *value;
+    int columns;
 
-	MSSHWindow *window = MSSH_WINDOW(data);
+    MSSHWindow *window = MSSH_WINDOW(data);
 
-	value = gconf_entry_get_value(entry);
-	columns = gconf_value_get_int(value);
+    value = gconf_entry_get_value(entry);
+    columns = gconf_value_get_int(value);
 
-	if(columns <= 0)
-	{
-		columns = 1;
-		gconf_client_set_int(client, MSSH_GCONF_KEY_COLUMNS, columns,
-			NULL);
-	}
+    if(columns <= 0)
+    {
+        columns = 1;
+        gconf_client_set_int(client, MSSH_GCONF_KEY_COLUMNS, columns,
+            NULL);
+    }
 
-	window->columns = columns;
-	mssh_window_relayout(window);
+    window->columns = columns;
+    mssh_window_relayout(window);
 }
 
 void mssh_gconf_notify_timeout(GConfClient *client, guint cnxn_id,
-	GConfEntry *entry, gpointer data)
+    GConfEntry *entry, gpointer data)
 {
-	GConfValue *value;
-	int timeout;
+    GConfValue *value;
+    int timeout;
 
-	MSSHWindow *window = MSSH_WINDOW(data);
+    MSSHWindow *window = MSSH_WINDOW(data);
 
-	value = gconf_entry_get_value(entry);
-	timeout = gconf_value_get_int(value);
+    value = gconf_entry_get_value(entry);
+    timeout = gconf_value_get_int(value);
 
-	if(timeout < 0)
-	{
-		timeout = 0;
-		gconf_client_set_int(client, MSSH_GCONF_KEY_TIMEOUT, timeout,
-			NULL);
-	}
+    if(timeout < 0)
+    {
+        timeout = 0;
+        gconf_client_set_int(client, MSSH_GCONF_KEY_TIMEOUT, timeout,
+            NULL);
+    }
 
-	window->timeout = timeout;
-	mssh_window_relayout(window);
+    window->timeout = timeout;
+    mssh_window_relayout(window);
 }
 
 void mssh_gconf_notify_close_ended(GConfClient *client, guint cnxn_id,
-	GConfEntry *entry, gpointer data)
+    GConfEntry *entry, gpointer data)
 {
-	GConfValue *value;
-	gboolean close_ended;
-	int i;
-
-	MSSHWindow *window = MSSH_WINDOW(data);
-
-	value = gconf_entry_get_value(entry);
-	close_ended = gconf_value_get_bool(value);
-
-	window->close_ended_sessions = close_ended;
-
-	if(close_ended)
-	{
-		for(i = 0; i < window->terminals->len; i++)
-		{
-			MSSHTerminal *terminal = g_array_index(window->terminals,
-				MSSHTerminal*, i);
-
-			if(terminal->ended)
-			{
-				mssh_window_session_closed(terminal, window);
-			}
-		}
-	}
+    GConfValue *value;
+    gboolean close_ended;
+    int i;
+
+    MSSHWindow *window = MSSH_WINDOW(data);
+
+    value = gconf_entry_get_value(entry);
+    close_ended = gconf_value_get_bool(value);
+
+    window->close_ended_sessions = close_ended;
+
+    if(close_ended)
+    {
+        for(i = 0; i < window->terminals->len; i++)
+        {
+            MSSHTerminal *terminal = g_array_index(window->terminals,
+                MSSHTerminal*, i);
+
+            if(terminal->ended)
+            {
+                mssh_window_session_closed(terminal, window);
+            }
+        }
+    }
 }
 
 void mssh_gconf_notify_quit_all_ended(GConfClient *client, guint cnxn_id,
-	GConfEntry *entry, gpointer data)
+    GConfEntry *entry, gpointer data)
 {
-	GConfValue *value;
+    GConfValue *value;
 
-	MSSHWindow *window = MSSH_WINDOW(data);
+    MSSHWindow *window = MSSH_WINDOW(data);
 
-	value = gconf_entry_get_value(entry);
+    value = gconf_entry_get_value(entry);
 
-	window->exit_on_all_closed = gconf_value_get_bool(value);
+    window->exit_on_all_closed = gconf_value_get_bool(value);
+}
+
+void mssh_gconf_notify_dir_focus(GConfClient *client, guint cnxn_id,
+    GConfEntry *entry, gpointer data)
+{
+    GConfValue *value;
+
+    MSSHWindow *window = MSSH_WINDOW(data);
+
+    value = gconf_entry_get_value(entry);
+
+    window->dir_focus = gconf_value_get_bool(value);
+}
+
+void mssh_gconf_notify_modifier(GConfClient *client, guint cnxn_id,
+    GConfEntry *entry, gpointer data)
+{
+    GConfValue *value;
+
+    MSSHWindow *window = MSSH_WINDOW(data);
+
+    value = gconf_entry_get_value(entry);
+
+    if(window->accel)
+    {
+        gtk_accel_group_disconnect_key(window->accel, GDK_Up,
+            window->modifier);
+        gtk_accel_group_disconnect_key(window->accel, GDK_Down,
+            window->modifier);
+        gtk_accel_group_disconnect_key(window->accel, GDK_Left,
+            window->modifier);
+        gtk_accel_group_disconnect_key(window->accel, GDK_Right,
+            window->modifier);
+    }
+
+    window->modifier = gconf_value_get_int(value);
+
+    if(window->accel)
+    {
+        gtk_accel_group_connect(window->accel, GDK_Up, window->modifier,
+            GTK_ACCEL_VISIBLE, g_cclosure_new(
+            G_CALLBACK(mssh_window_focus), window, NULL));
+        gtk_accel_group_connect(window->accel, GDK_Down, window->modifier,
+            GTK_ACCEL_VISIBLE, g_cclosure_new(
+            G_CALLBACK(mssh_window_focus), window, NULL));
+        gtk_accel_group_connect(window->accel, GDK_Left, window->modifier,
+            GTK_ACCEL_VISIBLE, g_cclosure_new(
+            G_CALLBACK(mssh_window_focus), window, NULL));
+        gtk_accel_group_connect(window->accel, GDK_Right, window->modifier,
+            GTK_ACCEL_VISIBLE, g_cclosure_new(
+            G_CALLBACK(mssh_window_focus), window, NULL));
+    }
 }

+ 21 - 15
src/mssh-gconf.h

@@ -3,28 +3,34 @@
 
 #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_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"
 
 void mssh_gconf_notify_font(GConfClient *client, guint cnxn_id,
-	GConfEntry *entry, gpointer data);
+    GConfEntry *entry, gpointer data);
 void mssh_gconf_notify_fg_colour(GConfClient *client, guint cnxn_id,
-	GConfEntry *entry, gpointer data);
+    GConfEntry *entry, gpointer data);
 void mssh_gconf_notify_bg_colour(GConfClient *client, guint cnxn_id,
-	GConfEntry *entry, gpointer data);
+    GConfEntry *entry, gpointer data);
 void mssh_gconf_notify_columns(GConfClient *client, guint cnxn_id,
-	GConfEntry *entry, gpointer data);
+    GConfEntry *entry, gpointer data);
 void mssh_gconf_notify_timeout(GConfClient *client, guint cnxn_id,
-	GConfEntry *entry, gpointer data);
+    GConfEntry *entry, gpointer data);
 void mssh_gconf_notify_close_ended(GConfClient *client, guint cnxn_id,
-	GConfEntry *entry, gpointer data);
+    GConfEntry *entry, gpointer data);
 void mssh_gconf_notify_quit_all_ended(GConfClient *client, guint cnxn_id,
-	GConfEntry *entry, gpointer data);
+    GConfEntry *entry, gpointer data);
+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);
 
 #endif

+ 285 - 202
src/mssh-pref.c

@@ -11,266 +11,349 @@ G_DEFINE_TYPE(MSSHPref, mssh_pref, GTK_TYPE_WINDOW)
 
 GtkWidget* mssh_pref_new(void)
 {
-	return g_object_new(MSSH_TYPE_PREF, NULL);
+    return g_object_new(MSSH_TYPE_PREF, NULL);
 }
 
 static void mssh_pref_close(GtkWidget *widget, gpointer data)
 {
-	MSSHPref *pref = MSSH_PREF(data);
+    MSSHPref *pref = MSSH_PREF(data);
 
-	gtk_widget_destroy(GTK_WIDGET(pref));
+    gtk_widget_destroy(GTK_WIDGET(pref));
 }
 
 static void mssh_pref_font_select(GtkWidget *widget, gpointer data)
 {
-	GConfClient *client;
-	const gchar *font;
+    GConfClient *client;
+    const gchar *font;
 
-	client = gconf_client_get_default();
+    client = gconf_client_get_default();
 
-	font = gtk_font_button_get_font_name(GTK_FONT_BUTTON(widget));
+    font = gtk_font_button_get_font_name(GTK_FONT_BUTTON(widget));
 
-	gconf_client_set_string(client, MSSH_GCONF_KEY_FONT, font, NULL);
+    gconf_client_set_string(client, MSSH_GCONF_KEY_FONT, font, NULL);
 }
 
 static void mssh_pref_fg_colour_select(GtkWidget *widget, gpointer data)
 {
-	GConfClient *client;
-	GdkColor colour;
-	const gchar *colour_s;
+    GConfClient *client;
+    GdkColor colour;
+    const gchar *colour_s;
 
-	client = gconf_client_get_default();
+    client = gconf_client_get_default();
 
-	gtk_color_button_get_color(GTK_COLOR_BUTTON(widget), &colour);
-	colour_s = gdk_color_to_string(&colour);
+    gtk_color_button_get_color(GTK_COLOR_BUTTON(widget), &colour);
+    colour_s = gdk_color_to_string(&colour);
 
-	gconf_client_set_string(client, MSSH_GCONF_KEY_FG_COLOUR, colour_s,
-		NULL);
+    gconf_client_set_string(client, MSSH_GCONF_KEY_FG_COLOUR, colour_s,
+        NULL);
 }
 
 static void mssh_pref_bg_colour_select(GtkWidget *widget, gpointer data)
 {
-	GConfClient *client;
-	GdkColor colour;
-	const gchar *colour_s;
+    GConfClient *client;
+    GdkColor colour;
+    const gchar *colour_s;
 
-	client = gconf_client_get_default();
+    client = gconf_client_get_default();
 
-	gtk_color_button_get_color(GTK_COLOR_BUTTON(widget), &colour);
-	colour_s = gdk_color_to_string(&colour);
+    gtk_color_button_get_color(GTK_COLOR_BUTTON(widget), &colour);
+    colour_s = gdk_color_to_string(&colour);
 
-	gconf_client_set_string(client, MSSH_GCONF_KEY_BG_COLOUR, colour_s,
-		NULL);
+    gconf_client_set_string(client, MSSH_GCONF_KEY_BG_COLOUR, colour_s,
+        NULL);
 }
 
 static void mssh_pref_columns_select(GtkWidget *widget, gpointer data)
 {
-	GConfClient *client;
-	int columns;
+    GConfClient *client;
+    int columns;
 
-	client = gconf_client_get_default();
+    client = gconf_client_get_default();
 
-	columns = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+    columns = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
 
-	gconf_client_set_int(client, MSSH_GCONF_KEY_COLUMNS, columns, NULL);
+    gconf_client_set_int(client, MSSH_GCONF_KEY_COLUMNS, columns, NULL);
 }
 
 static void mssh_pref_timeout_select(GtkWidget *widget, gpointer data)
 {
-	GConfClient *client;
-	int timeout;
+    GConfClient *client;
+    int timeout;
 
-	client = gconf_client_get_default();
+    client = gconf_client_get_default();
 
-	timeout = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+    timeout = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
 
-	gconf_client_set_int(client, MSSH_GCONF_KEY_TIMEOUT, timeout, NULL);
+    gconf_client_set_int(client, MSSH_GCONF_KEY_TIMEOUT, timeout, NULL);
 }
 
 static void mssh_pref_close_check(GtkWidget *widget, gpointer data)
 {
-	GConfClient *client;
-	gboolean close;
+    GConfClient *client;
+    gboolean close;
 
-	client = gconf_client_get_default();
+    client = gconf_client_get_default();
 
-	close = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+    close = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
 
-	gconf_client_set_bool(client, MSSH_GCONF_KEY_CLOSE_ENDED, close, NULL);
+    gconf_client_set_bool(client, MSSH_GCONF_KEY_CLOSE_ENDED, close, NULL);
 }
 
 static void mssh_pref_exit_check(GtkWidget *widget, gpointer data)
 {
-	GConfClient *client;
-	gboolean close;
+    GConfClient *client;
+    gboolean close;
 
-	client = gconf_client_get_default();
+    client = gconf_client_get_default();
 
-	close = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+    close = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
 
-	gconf_client_set_bool(client, MSSH_GCONF_KEY_QUIT_ALL_ENDED, close,
-		NULL);
+    gconf_client_set_bool(client, MSSH_GCONF_KEY_QUIT_ALL_ENDED, close,
+        NULL);
+}
+
+static void mssh_pref_dir_focus_check(GtkWidget *widget, gpointer data)
+{
+    GConfClient *client;
+    gboolean focus;
+
+    client = gconf_client_get_default();
+
+    focus = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+
+    gconf_client_set_bool(client, MSSH_GCONF_KEY_DIR_FOCUS, focus, NULL);
+}
+
+static void mssh_pref_modifier_check(GtkWidget *widget, gpointer data)
+{
+    GConfClient *client;
+    gboolean ctrl, alt, shift, super;
+    gint val;
+
+    MSSHPref * pref = MSSH_PREF(data);
+
+    client = gconf_client_get_default();
+
+    ctrl = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pref->ctrl));
+    shift = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pref->shift));
+    alt = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pref->alt));
+    super = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pref->super));
+
+    val = (shift << 0) | (ctrl << 2) | (alt << 3) | (super << 26);
+
+    gconf_client_set_int(client, MSSH_GCONF_KEY_MODIFIER, val, NULL);
 }
 
 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;
-	const gchar *colour_s;
-
-	GtkWidget *frame = gtk_vbox_new(FALSE, 5);
-	GtkWidget *notebook = gtk_notebook_new();
-	GtkWidget *content = gtk_vbox_new(FALSE, 4);
-
-	GtkWidget *font_hbox = gtk_hbox_new(FALSE, 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 *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 *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_label1 = gtk_label_new(
-		"Closed ended sessions after");
-	GtkObject *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_label = gtk_label_new("Columns:");
-	GtkObject *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 *close_hbox = gtk_hbox_new(FALSE, 0);
-	GtkWidget *close_button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
-
-	gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
-	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), content, NULL);
-
-	gtk_box_pack_start(GTK_BOX(font_hbox), font_label, FALSE, TRUE, 0);
-	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_box_pack_start(GTK_BOX(content), colour_table, 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(timeout_hbox), timeout_label1, FALSE,
-		TRUE, 0);
-	gtk_box_pack_start(GTK_BOX(timeout_hbox), timeout_select, FALSE,
-		TRUE, 0);
-	gtk_box_pack_start(GTK_BOX(timeout_hbox), timeout_label2, FALSE,
-		TRUE, 0);
-	gtk_box_pack_start(GTK_BOX(content), timeout_hbox, FALSE, TRUE, 0);
-
-	gtk_box_pack_start(GTK_BOX(columns_hbox), columns_label, FALSE,
-		TRUE, 0);
-	gtk_box_pack_start(GTK_BOX(columns_hbox), columns_select, FALSE,
-		TRUE, 0);
-	gtk_box_pack_start(GTK_BOX(content), columns_hbox, FALSE, TRUE, 0);
-
-	gtk_box_pack_end(GTK_BOX(close_hbox), close_button, FALSE, TRUE, 0);
-
-	gtk_box_pack_start(GTK_BOX(frame), notebook, TRUE, TRUE, 0);
-	gtk_box_pack_start(GTK_BOX(frame), close_hbox, FALSE, TRUE, 0);
-
-	gtk_container_add(GTK_CONTAINER(pref), frame);
-
-	g_signal_connect(G_OBJECT(close_button), "clicked",
-		G_CALLBACK(mssh_pref_close), pref);
-
-	gtk_container_set_border_width(GTK_CONTAINER(content), 6);
-	gtk_container_set_border_width(GTK_CONTAINER(pref), 15);
-	gtk_window_set_title(GTK_WINDOW(pref), "Preferences");
-	gtk_window_set_resizable(GTK_WINDOW(pref), FALSE);
-
-	g_signal_connect(G_OBJECT(font_select), "font-set",
-		G_CALLBACK(mssh_pref_font_select), NULL);
-	g_signal_connect(G_OBJECT(fg_colour_select), "color-set",
-		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(columns_select), "value-changed",
-		G_CALLBACK(mssh_pref_columns_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(exit_check), "toggled",
-		G_CALLBACK(mssh_pref_exit_check), NULL);
-
-	client = gconf_client_get_default();
-
-	entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_FONT, NULL,
-		TRUE, NULL);
-	value = gconf_entry_get_value(entry);
-	gtk_font_button_set_font_name(GTK_FONT_BUTTON(font_select),
-		gconf_value_get_string(value));
-
-	entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_FG_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(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),
-		&colour);
-
-	entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_COLUMNS, NULL,
-		TRUE, NULL);
-	value = gconf_entry_get_value(entry);
-	gtk_spin_button_set_value(GTK_SPIN_BUTTON(columns_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);
-	gtk_spin_button_set_value(GTK_SPIN_BUTTON(timeout_select),
-		gconf_value_get_int(value));
-
-	entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_CLOSE_ENDED,
-		NULL, TRUE, NULL);
-	value = gconf_entry_get_value(entry);
-	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(close_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);
-	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(exit_check),
-		gconf_value_get_bool(value));
+    GConfClient *client;
+    GConfEntry *entry;
+    GConfValue *value;
+    GdkVisual *visual = gdk_visual_get_system();
+    GdkColormap *colour_map = gdk_colormap_new(visual, TRUE);
+    GdkColor colour;
+    const gchar *colour_s;
+
+    GtkWidget *frame = gtk_vbox_new(FALSE, 5);
+    GtkWidget *notebook = gtk_notebook_new();
+    GtkWidget *content = gtk_vbox_new(FALSE, 4);
+
+    GtkWidget *font_hbox = gtk_hbox_new(FALSE, 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 *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 *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_label1 = gtk_label_new(
+        "Closed ended sessions after");
+    GtkObject *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_label = gtk_label_new("Columns:");
+    GtkObject *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 *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 *dir_focus_check = gtk_check_button_new_with_label(
+        "Use directional focus");
+
+    pref->ctrl = mod_ctrl_check;
+    pref->shift = mod_shift_check;
+    pref->alt = mod_alt_check;
+    pref->super = mod_super_check;
+
+    gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
+    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), content, NULL);
+
+    gtk_box_pack_start(GTK_BOX(font_hbox), font_label, FALSE, TRUE, 0);
+    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_box_pack_start(GTK_BOX(content), colour_table, 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);
+
+    gtk_box_pack_start(GTK_BOX(timeout_hbox), timeout_label1, FALSE,
+        TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(timeout_hbox), timeout_select, FALSE,
+        TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(timeout_hbox), timeout_label2, FALSE,
+        TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(content), timeout_hbox, FALSE, TRUE, 0);
+
+    gtk_box_pack_start(GTK_BOX(columns_hbox), columns_label, FALSE,
+        TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(columns_hbox), columns_select, FALSE,
+        TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(content), columns_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);
+    gtk_box_pack_start(GTK_BOX(mod_hbox), mod_shift_check, FALSE, TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(mod_hbox), mod_super_check, FALSE, TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(content), mod_hbox, FALSE, TRUE, 0);
+
+    gtk_box_pack_end(GTK_BOX(close_hbox), close_button, FALSE, TRUE, 0);
+
+    gtk_box_pack_start(GTK_BOX(frame), notebook, TRUE, TRUE, 0);
+    gtk_box_pack_start(GTK_BOX(frame), close_hbox, FALSE, TRUE, 0);
+
+    gtk_container_add(GTK_CONTAINER(pref), frame);
+
+    g_signal_connect(G_OBJECT(close_button), "clicked",
+        G_CALLBACK(mssh_pref_close), pref);
+
+    gtk_container_set_border_width(GTK_CONTAINER(content), 6);
+    gtk_container_set_border_width(GTK_CONTAINER(pref), 15);
+    gtk_window_set_title(GTK_WINDOW(pref), "Preferences");
+    gtk_window_set_resizable(GTK_WINDOW(pref), FALSE);
+
+    g_signal_connect(G_OBJECT(font_select), "font-set",
+        G_CALLBACK(mssh_pref_font_select), NULL);
+    g_signal_connect(G_OBJECT(fg_colour_select), "color-set",
+        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(columns_select), "value-changed",
+        G_CALLBACK(mssh_pref_columns_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(exit_check), "toggled",
+        G_CALLBACK(mssh_pref_exit_check), NULL);
+     g_signal_connect(G_OBJECT(dir_focus_check), "toggled",
+        G_CALLBACK(mssh_pref_dir_focus_check), NULL);
+    g_signal_connect(G_OBJECT(mod_ctrl_check), "toggled",
+        G_CALLBACK(mssh_pref_modifier_check), pref);
+    g_signal_connect(G_OBJECT(mod_alt_check), "toggled",
+        G_CALLBACK(mssh_pref_modifier_check), pref);
+    g_signal_connect(G_OBJECT(mod_shift_check), "toggled",
+        G_CALLBACK(mssh_pref_modifier_check), pref);
+    g_signal_connect(G_OBJECT(mod_super_check), "toggled",
+        G_CALLBACK(mssh_pref_modifier_check), pref);
+
+    client = gconf_client_get_default();
+
+    entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_FONT, NULL,
+        TRUE, NULL);
+    value = gconf_entry_get_value(entry);
+    gtk_font_button_set_font_name(GTK_FONT_BUTTON(font_select),
+        gconf_value_get_string(value));
+
+    entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_FG_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(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),
+        &colour);
+
+    entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_COLUMNS, NULL,
+        TRUE, NULL);
+    value = gconf_entry_get_value(entry);
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(columns_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);
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(timeout_select),
+        gconf_value_get_int(value));
+
+    entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_CLOSE_ENDED,
+        NULL, TRUE, NULL);
+    value = gconf_entry_get_value(entry);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(close_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);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(exit_check),
+        gconf_value_get_bool(value));
+
+    entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_DIR_FOCUS,
+            NULL, TRUE, NULL);
+    value = gconf_entry_get_value(entry);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dir_focus_check),
+        gconf_value_get_bool(value));
+
+    entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_MODIFIER,
+            NULL, TRUE, NULL);
+    value = gconf_entry_get_value(entry);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mod_ctrl_check),
+        (gconf_value_get_int(value) & GDK_CONTROL_MASK) ? 1 : 0);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mod_shift_check),
+        (gconf_value_get_int(value) & GDK_SHIFT_MASK) ? 1 : 0);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mod_alt_check),
+        (gconf_value_get_int(value) & GDK_MOD1_MASK) ? 1 : 0);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mod_super_check),
+        (gconf_value_get_int(value) & GDK_SUPER_MASK) ? 1 : 0);
 }
 
 static void mssh_pref_class_init(MSSHPrefClass *klass)

+ 14 - 10
src/mssh-pref.h

@@ -5,24 +5,28 @@
 
 G_BEGIN_DECLS
 
-#define MSSH_TYPE_PREF				mssh_pref_get_type()
-#define MSSH_PREF(obj)				G_TYPE_CHECK_INSTANCE_CAST(obj,\
-	MSSH_TYPE_PREF, MSSHPref)
-#define MSSH_PREF_CLASS(klass)		G_TYPE_CHECK_CLASS_CAST(klass,\
-	MSSH_PREF_TYPE, MSSHPrefClass)
-#define IS_MSSH_PREF(obj)			G_TYPE_CHECK_INSTANCE_TYPE(obj,\
-	MSSH_TYPE_PREF)
+#define MSSH_TYPE_PREF              mssh_pref_get_type()
+#define MSSH_PREF(obj)              G_TYPE_CHECK_INSTANCE_CAST(obj,\
+    MSSH_TYPE_PREF, MSSHPref)
+#define MSSH_PREF_CLASS(klass)      G_TYPE_CHECK_CLASS_CAST(klass,\
+    MSSH_PREF_TYPE, MSSHPrefClass)
+#define IS_MSSH_PREF(obj)           G_TYPE_CHECK_INSTANCE_TYPE(obj,\
+    MSSH_TYPE_PREF)
 #define IS_MSSH_PREF_CLASS(klass) G_TYPE_CHECK_CLASS_TYPE(klass,\
-	MSSH_TYPE_PREF)
+    MSSH_TYPE_PREF)
 
 typedef struct
 {
-	GtkWindow widget;
+    GtkWindow widget;
+    GtkWidget *ctrl;
+    GtkWidget *alt;
+    GtkWidget *shift;
+    GtkWidget *super;
 } MSSHPref;
 
 typedef struct
 {
-	GtkWindowClass parent_class;
+    GtkWindowClass parent_class;
 } MSSHPrefClass;
 
 GType mssh_pref_get_type(void) G_GNUC_CONST;

+ 59 - 56
src/mssh-terminal.c

@@ -9,120 +9,123 @@ static void mssh_terminal_init(MSSHTerminal* terminal);
 static void mssh_terminal_class_init(MSSHTerminalClass *klass);
 static void mssh_terminal_child_exited(VteTerminal *vte, gpointer data);
 static gboolean mssh_terminal_focused(GtkWidget *widget,
-	GtkDirectionType dir, gpointer data);
+    GtkDirectionType dir, gpointer data);
 
 GtkWidget* mssh_terminal_new()
 {
-	return g_object_new(MSSH_TYPE_TERMINAL, NULL);
+    return g_object_new(MSSH_TYPE_TERMINAL, NULL);
 }
 
 void mssh_terminal_destroy(MSSHTerminal *terminal)
 {
-	free(terminal->hostname);
+    free(terminal->hostname);
 }
 
 gboolean mssh_terminal_isactive(MSSHTerminal *terminal)
 {
-	return gtk_check_menu_item_get_active(
-		GTK_CHECK_MENU_ITEM(terminal->menu_item));
+    return gtk_check_menu_item_get_active(
+        GTK_CHECK_MENU_ITEM(terminal->menu_item));
 }
 
 void mssh_terminal_init_session(MSSHTerminal *terminal, char *hostname)
 {
-	terminal->hostname = hostname;
+    terminal->hostname = hostname;
 
-	terminal->menu_item = gtk_check_menu_item_new_with_label(
-		terminal->hostname);
+    terminal->menu_item = gtk_check_menu_item_new_with_label(
+        terminal->hostname);
 
-	gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(
-		terminal->menu_item), TRUE);
+    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(
+        terminal->menu_item), TRUE);
 }
 
 void mssh_terminal_start_session(MSSHTerminal *terminal, char **env)
 {
-	char *args[3];
+    char *args[3];
 
-	args[0] = strdup("ssh");
-	args[1] = terminal->hostname;
-	args[2] = NULL;
+    args[0] = strdup("ssh");
+    args[1] = terminal->hostname;
+    args[2] = NULL;
 
-	vte_terminal_fork_command(VTE_TERMINAL(terminal), "ssh", args,
-		env, NULL, FALSE, FALSE, FALSE);
+    vte_terminal_fork_command(VTE_TERMINAL(terminal), "ssh", args,
+        env, NULL, FALSE, FALSE, FALSE);
 
-	free(args[0]);
+    free(args[0]);
 }
 
 void mssh_terminal_send_host(MSSHTerminal *terminal)
 {
-	if(mssh_terminal_isactive(terminal))
-	{
-		vte_terminal_feed_child(VTE_TERMINAL(terminal),
-			terminal->hostname, strlen(terminal->hostname));
-	}
+    if(mssh_terminal_isactive(terminal))
+    {
+        vte_terminal_feed_child(VTE_TERMINAL(terminal),
+            terminal->hostname, strlen(terminal->hostname));
+    }
 }
 
 void mssh_terminal_send_string(MSSHTerminal *terminal, gchar *string)
 {
-	if(mssh_terminal_isactive(terminal))
-	{
-		vte_terminal_feed_child(VTE_TERMINAL(terminal),	string,
-			strlen(string));
-	}
+    if(mssh_terminal_isactive(terminal))
+    {
+        vte_terminal_feed_child(VTE_TERMINAL(terminal), string,
+            strlen(string));
+    }
 }
 
 void mssh_terminal_send_data(MSSHTerminal *terminal, GdkEventKey *event)
 {
-	gboolean dummy;
+    gboolean dummy;
 
-	if(mssh_terminal_isactive(terminal))
-	{
-		g_signal_emit_by_name(terminal, "key-press-event", event, &dummy);
-	}
+    if(mssh_terminal_isactive(terminal))
+    {
+        g_signal_emit_by_name(terminal, "key-press-event", event, &dummy);
+    }
 }
 
 static void mssh_terminal_init(MSSHTerminal* terminal)
 {
-	terminal->started = 0;
-	terminal->ended = 0;
+    terminal->started = 0;
+    terminal->ended = 0;
 
-	g_signal_connect(G_OBJECT(terminal), "child-exited",
-		G_CALLBACK(mssh_terminal_child_exited), terminal);
-	g_signal_connect(G_OBJECT(terminal), "focus-in-event",
-		G_CALLBACK(mssh_terminal_focused), terminal);
+    vte_terminal_set_word_chars(VTE_TERMINAL(terminal),
+        "-A-Za-z0-9,./?%&#:_=+@~");
+
+    g_signal_connect(G_OBJECT(terminal), "child-exited",
+        G_CALLBACK(mssh_terminal_child_exited), terminal);
+    g_signal_connect(G_OBJECT(terminal), "focus-in-event",
+        G_CALLBACK(mssh_terminal_focused), terminal);
 }
 
 static void mssh_terminal_class_init(MSSHTerminalClass *klass)
 {
-	klass->session_closed_signal = g_signal_new("session-closed",
-		G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST,
-		G_STRUCT_OFFSET(MSSHTerminalClass, session_closed), NULL, NULL,
-		g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL);
-
-	klass->session_focused_signal = g_signal_new("session-focused",
-		G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST,
-		G_STRUCT_OFFSET(MSSHTerminalClass, session_focused), NULL, NULL,
-		g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL);
+    klass->session_closed_signal = g_signal_new("session-closed",
+        G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST,
+        G_STRUCT_OFFSET(MSSHTerminalClass, session_closed), NULL, NULL,
+        g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL);
+
+    klass->session_focused_signal = g_signal_new("session-focused",
+        G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST,
+        G_STRUCT_OFFSET(MSSHTerminalClass, session_focused), NULL, NULL,
+        g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL);
 }
 
 static void mssh_terminal_child_exited(VteTerminal *vte, gpointer data)
 {
-	char msg[] = "\r\n[Child Exited]\r\n";
+    char msg[] = "\r\n[Child Exited]\r\n";
 
-	MSSHTerminal *terminal = MSSH_TERMINAL(data);
+    MSSHTerminal *terminal = MSSH_TERMINAL(data);
 
-	terminal->ended = 1;
+    terminal->ended = 1;
 
-	vte_terminal_feed(vte, msg, strlen(msg));
+    vte_terminal_feed(vte, msg, strlen(msg));
 
-	g_signal_emit_by_name(terminal, "session-closed");
+    g_signal_emit_by_name(terminal, "session-closed");
 }
 
 static gboolean mssh_terminal_focused(GtkWidget *widget,
-	GtkDirectionType dir, gpointer data)
+    GtkDirectionType dir, gpointer data)
 {
-	MSSHTerminal *terminal = MSSH_TERMINAL(data);
+    MSSHTerminal *terminal = MSSH_TERMINAL(data);
 
-	g_signal_emit_by_name(terminal, "session-focused");
+    g_signal_emit_by_name(terminal, "session-focused");
 
-	return FALSE;
+    return FALSE;
 }

+ 19 - 19
src/mssh-terminal.h

@@ -6,34 +6,34 @@
 
 G_BEGIN_DECLS
 
-#define MSSH_TYPE_TERMINAL				mssh_terminal_get_type()
-#define MSSH_TERMINAL(obj)				G_TYPE_CHECK_INSTANCE_CAST(obj, \
-	MSSH_TYPE_TERMINAL, MSSHTerminal)
-#define MSSH_TERMINAL_CLASS(klass)		G_TYPE_CHECK_CLASS_CAST(klass, \
-	MSSH_TERMINAL_TYPE, MSSHTerminalClass)
-#define IS_MSSH_TERMINAL(obj)			G_TYPE_CHECK_INSTANCE_TYPE(obj, \
-	MSSH_TYPE_TERMINAL)
-#define IS_MSSH_TERMINAL_CLASS(klass)	G_TYPE_CHECK_CLASS_TYPE(klass, \
-	MSSH_TYPE_TERMINAL)
+#define MSSH_TYPE_TERMINAL              mssh_terminal_get_type()
+#define MSSH_TERMINAL(obj)              G_TYPE_CHECK_INSTANCE_CAST(obj, \
+    MSSH_TYPE_TERMINAL, MSSHTerminal)
+#define MSSH_TERMINAL_CLASS(klass)      G_TYPE_CHECK_CLASS_CAST(klass, \
+    MSSH_TERMINAL_TYPE, MSSHTerminalClass)
+#define IS_MSSH_TERMINAL(obj)           G_TYPE_CHECK_INSTANCE_TYPE(obj, \
+    MSSH_TYPE_TERMINAL)
+#define IS_MSSH_TERMINAL_CLASS(klass)   G_TYPE_CHECK_CLASS_TYPE(klass, \
+    MSSH_TYPE_TERMINAL)
 
 typedef struct
 {
-	VteTerminal vte;
-	GtkWidget *menu_item;
-	char *hostname;
-	int started;
-	int ended;
+    VteTerminal vte;
+    GtkWidget *menu_item;
+    char *hostname;
+    int started;
+    int ended;
 } MSSHTerminal;
 
 typedef struct
 {
-	VteTerminalClass parent_class;
+    VteTerminalClass parent_class;
 
-	guint session_closed_signal;
-	guint session_focused_signal;
+    guint session_closed_signal;
+    guint session_focused_signal;
 
-	void (*session_closed)(MSSHTerminal *terminal);
-	void (*session_focused)(MSSHTerminal *terminal);
+    void (*session_closed)(MSSHTerminal *terminal);
+    void (*session_focused)(MSSHTerminal *terminal);
 } MSSHTerminalClass;
 
 GType mssh_terminal_get_type(void) G_GNUC_CONST;

+ 406 - 270
src/mssh-window.c

@@ -15,14 +15,14 @@ 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);
 static gboolean mssh_window_key_press(GtkWidget *widget,
-	GdkEventKey *event, gpointer data);
+    GdkEventKey *event, gpointer data);
 static gboolean mssh_window_entry_focused(GtkWidget *widget,
-	GtkDirectionType dir, gpointer data);
+    GtkDirectionType dir, gpointer data);
 static gboolean mssh_window_session_close(gpointer data);
 static void mssh_window_session_focused(MSSHTerminal *terminal,
-	gpointer data);
+    gpointer data);
 static void mssh_window_insert(GtkWidget *widget, gchar *new_text,
-	gint new_text_length, gint *position, gpointer data);
+    gint new_text_length, gint *position, gpointer data);
 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);
@@ -31,349 +31,485 @@ G_DEFINE_TYPE(MSSHWindow, mssh_window, GTK_TYPE_WINDOW)
 
 struct WinTermPair
 {
-	MSSHWindow *window;
-	MSSHTerminal *terminal;
+    MSSHWindow *window;
+    MSSHTerminal *terminal;
 };
 
 GtkWidget* mssh_window_new(void)
 {
-	return g_object_new(MSSH_TYPE_WINDOW, NULL);
+    return g_object_new(MSSH_TYPE_WINDOW, NULL);
 }
 
 static void mssh_window_sendhost(GtkWidget *widget, gpointer data)
 {
-	int i;
+    int i;
 
-	MSSHWindow *window = MSSH_WINDOW(data);
+    MSSHWindow *window = MSSH_WINDOW(data);
 
-	for(i = 0; i < window->terminals->len; i++)
-	{
-		mssh_terminal_send_host(g_array_index(window->terminals,
-			MSSHTerminal*, i));
-	}
+    for(i = 0; i < window->terminals->len; i++)
+    {
+        mssh_terminal_send_host(g_array_index(window->terminals,
+            MSSHTerminal*, i));
+    }
 }
 
 static void mssh_window_destroy(GtkWidget *widget, gpointer data)
 {
-	gtk_main_quit();
+    gtk_main_quit();
 }
 
 static void mssh_window_pref(GtkWidget *widget, gpointer data)
 {
-	MSSHWindow *window = MSSH_WINDOW(data);
-	GtkWidget *pref = mssh_pref_new();
+    MSSHWindow *window = MSSH_WINDOW(data);
+    GtkWidget *pref = mssh_pref_new();
 
-	gtk_window_set_transient_for(GTK_WINDOW(pref), GTK_WINDOW(window));
-	gtk_window_set_modal(GTK_WINDOW(pref), TRUE);
-	gtk_window_set_position(GTK_WINDOW(pref),
-		GTK_WIN_POS_CENTER_ON_PARENT);
+    gtk_window_set_transient_for(GTK_WINDOW(pref), GTK_WINDOW(window));
+    gtk_window_set_modal(GTK_WINDOW(pref), TRUE);
+    gtk_window_set_position(GTK_WINDOW(pref),
+        GTK_WIN_POS_CENTER_ON_PARENT);
 
-	gtk_widget_show_all(pref);
+    gtk_widget_show_all(pref);
 }
 
 static void mssh_window_insert(GtkWidget *widget, gchar *new_text,
-	gint new_text_length, gint *position, gpointer data)
+    gint new_text_length, gint *position, gpointer data)
 {
-	int i;
+    int i;
 
-	MSSHWindow *window = MSSH_WINDOW(data);
+    MSSHWindow *window = MSSH_WINDOW(data);
 
-	for(i = 0; i < window->terminals->len; i++)
-	{
-		mssh_terminal_send_string(g_array_index(window->terminals,
-			MSSHTerminal*, i), new_text);
-	}
+    for(i = 0; i < window->terminals->len; i++)
+    {
+        mssh_terminal_send_string(g_array_index(window->terminals,
+            MSSHTerminal*, i), new_text);
+    }
 
-	g_signal_stop_emission_by_name(G_OBJECT(widget), "insert-text");
+    g_signal_stop_emission_by_name(G_OBJECT(widget), "insert-text");
 }
 
 static gboolean mssh_window_key_press(GtkWidget *widget,
-	GdkEventKey *event, gpointer data)
+    GdkEventKey *event, gpointer data)
 {
-	int i;
+    int i;
 
-	MSSHWindow *window = MSSH_WINDOW(data);
+    MSSHWindow *window = MSSH_WINDOW(data);
 
-	for(i = 0; i < window->terminals->len; i++)
-	{
-		mssh_terminal_send_data(g_array_index(window->terminals,
-			MSSHTerminal*, i), event);
-	}
+    for(i = 0; i < window->terminals->len; i++)
+    {
+        mssh_terminal_send_data(g_array_index(window->terminals,
+            MSSHTerminal*, i), event);
+    }
 
-	return TRUE;
+    return TRUE;
 }
 
 static gboolean mssh_window_entry_focused(GtkWidget *widget,
-	GtkDirectionType dir, gpointer data)
+    GtkDirectionType dir, gpointer data)
 {
-	MSSHWindow *window = MSSH_WINDOW(data);
+    MSSHWindow *window = MSSH_WINDOW(data);
 
-	gtk_window_set_title(GTK_WINDOW(window), PACKAGE_NAME" - All");
+    gtk_window_set_title(GTK_WINDOW(window), PACKAGE_NAME" - All");
 
-	return FALSE;
+    return FALSE;
+}
+
+gboolean mssh_window_focus(GtkWidget *widget, GObject *acceleratable,
+    guint keyval, GdkModifierType modifier, gpointer data)
+{
+    MSSHWindow *window = MSSH_WINDOW(data);
+    GtkWidget *focus;
+
+    int i, idx = -1, len = window->terminals->len;
+    int wcols = window->columns_override ? window->columns_override :
+        window->columns;
+    int cols = (len < wcols) ? len : wcols;
+
+    focus = gtk_window_get_focus(GTK_WINDOW(window));
+
+    for(i = 0; i < len; i++)
+    {
+        if(focus == GTK_WIDGET(g_array_index(window->terminals,
+            MSSHTerminal*, i)))
+        {
+            idx = i;
+            break;
+        }
+    }
+
+    if(focus == window->global_entry && keyval == GDK_Down &&
+        window->dir_focus)
+        idx = 0;
+    else if(idx == -1 && window->dir_focus)
+        return TRUE;
+    else
+    {
+        switch(keyval)
+        {
+        case GDK_Up:
+            if(window->dir_focus)
+                idx = idx - cols;
+            break;
+        case GDK_Down:
+            if(window->dir_focus)
+            {
+                if((idx + cols >= len) && (idx < len -
+                    (len % cols) ? (len % cols) : cols))
+                    idx = len - 1;
+                else
+                    idx = idx + cols;
+            }
+            break;
+        case GDK_Left:
+            if(idx % cols != 0 || !window->dir_focus)
+                idx = idx - 1;
+            break;
+        case GDK_Right:
+            if(idx % cols != cols - 1 || !window->dir_focus)
+                idx = idx + 1;
+            break;
+        }
+    }
+
+    if(idx >= len && !window->dir_focus)
+        focus = window->global_entry;
+
+    if(idx < -1 && !window->dir_focus)
+        idx = len - 1;
+
+    if(idx < 0)
+        focus = window->global_entry;
+    else if(idx < len)
+    {
+        focus = GTK_WIDGET(g_array_index(window->terminals,
+            MSSHTerminal*, idx));
+    }
+
+    gtk_window_set_focus(GTK_WINDOW(window), focus);
+
+    return TRUE;
 }
 
 static gboolean mssh_window_session_close(gpointer data)
 {
-	int i, idx = -1;
-
-	struct WinTermPair *data_pair = (struct WinTermPair*)data;
-
-	for(i = 0; i < data_pair->window->terminals->len; i++)
-	{
-		if(data_pair->terminal == g_array_index(
-			data_pair->window->terminals, MSSHTerminal*, i))
-		{
-			idx = i;
-			break;
-		}
-	}
-
-	if(idx == -1)
-	{
-		fprintf(stderr,
-			"mssh: Fatal Error: Can't find terminal to remove!\n");
-	}
-	else
-	{
-		gtk_widget_destroy(data_pair->terminal->menu_item);
-
-		gtk_container_remove(GTK_CONTAINER(data_pair->window->table),
-			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 &&
-		data_pair->window->exit_on_all_closed)
-	{
-		mssh_window_destroy(NULL, (void*)data_pair->window);
-	}
-
-	free(data_pair);
-
-	return FALSE;
+    int i, idx = -1;
+
+    struct WinTermPair *data_pair = (struct WinTermPair*)data;
+
+    for(i = 0; i < data_pair->window->terminals->len; i++)
+    {
+        if(data_pair->terminal == g_array_index(
+            data_pair->window->terminals, MSSHTerminal*, i))
+        {
+            idx = i;
+            break;
+        }
+    }
+
+    data_pair->window->last_closed = idx;
+
+    if(idx == -1)
+    {
+        fprintf(stderr,
+            "mssh: Fatal Error: Can't find terminal to remove!\n");
+    }
+    else
+    {
+        gtk_widget_destroy(data_pair->terminal->menu_item);
+
+        gtk_container_remove(GTK_CONTAINER(data_pair->window->table),
+            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 &&
+        data_pair->window->exit_on_all_closed)
+    {
+        mssh_window_destroy(NULL, (void*)data_pair->window);
+    }
+
+    free(data_pair);
+
+    return FALSE;
 }
 
 void mssh_window_session_closed(MSSHTerminal *terminal, gpointer data)
 {
-	struct WinTermPair *data_pair = malloc(sizeof(struct WinTermPair));
-	data_pair->terminal = terminal;
-	data_pair->window = MSSH_WINDOW(data);
-
-	if(data_pair->window->close_ended_sessions)
-	{
-		g_timeout_add_seconds(data_pair->window->timeout,
-			mssh_window_session_close, data_pair);
-	}
+    struct WinTermPair *data_pair = malloc(sizeof(struct WinTermPair));
+    data_pair->terminal = terminal;
+    data_pair->window = MSSH_WINDOW(data);
+
+    if(data_pair->window->close_ended_sessions)
+    {
+        g_timeout_add_seconds(data_pair->window->timeout,
+            mssh_window_session_close, data_pair);
+    }
 }
 
 static void mssh_window_session_focused(MSSHTerminal *terminal,
-	gpointer data)
+    gpointer data)
 {
-	char *title;
-	size_t len;
+    char *title;
+    size_t len;
 
-	MSSHWindow *window = MSSH_WINDOW(data);
+    MSSHWindow *window = MSSH_WINDOW(data);
 
-	len = strlen(PACKAGE_NAME" - ") + strlen(terminal->hostname) + 1;
-	title = malloc(len);
+    len = strlen(PACKAGE_NAME" - ") + strlen(terminal->hostname) + 1;
+    title = malloc(len);
 
-	snprintf(title, len, PACKAGE_NAME" - %s", terminal->hostname);
+    snprintf(title, len, PACKAGE_NAME" - %s", terminal->hostname);
 
-	gtk_window_set_title(GTK_WINDOW(window), title);
+    gtk_window_set_title(GTK_WINDOW(window), title);
 
-	free(title);
+    free(title);
 }
 
 void mssh_window_relayout(MSSHWindow *window)
 {
-	GConfClient *client;
-	GConfEntry *entry;
-	int i, len = window->terminals->len;
-	int cols = (len < window->columns) ? len : window->columns;
-	int rows = (len + 0.5) / cols;
-
-	for(i = 0; i < len; i++)
-	{
-		MSSHTerminal *terminal = g_array_index(window->terminals,
-			MSSHTerminal*, i);
-
-		g_object_ref(terminal);
-		if(GTK_WIDGET(terminal)->parent == GTK_WIDGET(window->table))
-		{
-			gtk_container_remove(GTK_CONTAINER(window->table),
-				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);
-		g_object_unref(terminal);
-
-		if(!terminal->started)
-		{
-			mssh_terminal_start_session(terminal, window->env);
-			terminal->started = 1;
-		}
-	}
-
-	if(len > 0)
-	{
-		gtk_table_resize(GTK_TABLE(window->table), rows, cols);
-	}
-
-	client = gconf_client_get_default();
-
-	gtk_widget_show_all(GTK_WIDGET(window));
-
-	entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_FONT, NULL,
-		TRUE, NULL);
-	mssh_gconf_notify_font(client, 0, entry, window);
-	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);
+    GConfClient *client;
+    GConfEntry *entry;
+    GtkWidget *focus;
+    int i, len = window->terminals->len;
+    int wcols = window->columns_override ? window->columns_override :
+        window->columns;
+    int cols = (len < wcols) ? len : wcols;
+    int rows = (len + 0.5) / cols;
+
+    focus = gtk_window_get_focus(GTK_WINDOW(window));
+
+    if(!focus)
+    {
+        if(window->last_closed < 0)
+            window->last_closed = 0;
+
+        if(len == 0)
+            focus = window->global_entry;
+        else if(window->last_closed < len)
+        {
+            focus = GTK_WIDGET(g_array_index(window->terminals,
+                MSSHTerminal*, window->last_closed));
+        }
+        else
+        {
+            focus = GTK_WIDGET(g_array_index(window->terminals,
+                MSSHTerminal*, 0));
+        }
+    }
+
+    for(i = 0; i < len; i++)
+    {
+        MSSHTerminal *terminal = g_array_index(window->terminals,
+            MSSHTerminal*, i);
+
+        g_object_ref(terminal);
+        if(GTK_WIDGET(terminal)->parent == GTK_WIDGET(window->table))
+        {
+            gtk_container_remove(GTK_CONTAINER(window->table),
+                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);
+        g_object_unref(terminal);
+
+        if(!terminal->started)
+        {
+            mssh_terminal_start_session(terminal, window->env);
+            terminal->started = 1;
+        }
+    }
+
+    if(len > 0)
+    {
+        gtk_table_resize(GTK_TABLE(window->table), rows, cols);
+    }
+
+    client = gconf_client_get_default();
+
+    gtk_widget_show_all(GTK_WIDGET(window));
+
+    entry = gconf_client_get_entry(client, MSSH_GCONF_KEY_FONT, NULL,
+        TRUE, NULL);
+    mssh_gconf_notify_font(client, 0, entry, window);
+    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);
+
+    gtk_window_set_focus(GTK_WINDOW(window), GTK_WIDGET(focus));
 }
 
 static void mssh_window_add_session(MSSHWindow *window, char *hostname)
 {
-	MSSHTerminal *terminal = MSSH_TERMINAL(mssh_terminal_new());
+    MSSHTerminal *terminal = MSSH_TERMINAL(mssh_terminal_new());
 
-	g_array_append_val(window->terminals, terminal);
+    g_array_append_val(window->terminals, terminal);
 
-	g_signal_connect(G_OBJECT(terminal), "session-closed",
-		G_CALLBACK(mssh_window_session_closed), window);
-	g_signal_connect(G_OBJECT(terminal), "session-focused",
-		G_CALLBACK(mssh_window_session_focused), window);
+    g_signal_connect(G_OBJECT(terminal), "session-closed",
+        G_CALLBACK(mssh_window_session_closed), window);
+    g_signal_connect(G_OBJECT(terminal), "session-focused",
+        G_CALLBACK(mssh_window_session_focused), window);
 
-	mssh_terminal_init_session(terminal, hostname);
+    mssh_terminal_init_session(terminal, hostname);
 
-	gtk_menu_shell_append(GTK_MENU_SHELL(window->server_menu),
-		terminal->menu_item);
+    gtk_menu_shell_append(GTK_MENU_SHELL(window->server_menu),
+        terminal->menu_item);
 }
 
 static void mssh_window_init(MSSHWindow* window)
 {
-	GConfClient *client;
-
-	GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
-	GtkWidget *entry = gtk_entry_new();
-
-	GtkWidget *menu_bar = gtk_menu_bar_new();
-	GtkWidget *file_menu = gtk_menu_new();
-	GtkWidget *edit_menu = gtk_menu_new();
-
-	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 *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 *edit_pref = gtk_image_menu_item_new_from_stock(
-		GTK_STOCK_PREFERENCES, NULL);
-
-	window->server_menu = gtk_menu_new();
-
-	window->terminals = g_array_new(FALSE, TRUE, sizeof(MSSHTerminal*));
-
-	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_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_quit), "activate",
-		G_CALLBACK(mssh_window_destroy), window);
-	g_signal_connect(G_OBJECT(edit_pref), "activate",
-		G_CALLBACK(mssh_window_pref), window);
-
-	gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), file_item);
-	gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), edit_item);
-	gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), server_item);
-
-	g_signal_connect(G_OBJECT(entry), "key-press-event",
-		G_CALLBACK(mssh_window_key_press), window);
-	g_signal_connect(G_OBJECT(entry), "insert-text",
-		G_CALLBACK(mssh_window_insert), window);
-	g_signal_connect(G_OBJECT(entry), "focus-in-event",
-		G_CALLBACK(mssh_window_entry_focused), 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);
-
-	gtk_container_add(GTK_CONTAINER(window), vbox);
-
-	gtk_widget_set_size_request(GTK_WIDGET(window), 1024, 768);
-	gtk_window_set_title(GTK_WINDOW(window), PACKAGE_NAME);
-
-	client = gconf_client_get_default();
-
-	gconf_client_add_dir(client, MSSH_GCONF_PATH,
-		GCONF_CLIENT_PRELOAD_RECURSIVE, NULL);
-
-	gconf_client_notify_add(client, MSSH_GCONF_KEY_FONT,
-		mssh_gconf_notify_font, window, NULL, NULL);
-	gconf_client_notify_add(client, MSSH_GCONF_KEY_FG_COLOUR,
-		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_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_QUIT_ALL_ENDED,
-		mssh_gconf_notify_quit_all_ended, 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_QUIT_ALL_ENDED);
+    GConfClient *client;
+
+    GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
+    GtkWidget *entry = gtk_entry_new();
+
+    GtkWidget *menu_bar = gtk_menu_bar_new();
+    GtkWidget *file_menu = gtk_menu_new();
+    GtkWidget *edit_menu = gtk_menu_new();
+
+    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 *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 *edit_pref = gtk_image_menu_item_new_from_stock(
+        GTK_STOCK_PREFERENCES, NULL);
+
+    GtkAccelGroup *accel = gtk_accel_group_new();
+
+    window->accel = NULL;
+
+    window->server_menu = gtk_menu_new();
+
+    window->global_entry = entry;
+
+    window->last_closed = -1;
+
+    window->terminals = g_array_new(FALSE, TRUE, sizeof(MSSHTerminal*));
+
+    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_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_quit), "activate",
+        G_CALLBACK(mssh_window_destroy), window);
+    g_signal_connect(G_OBJECT(edit_pref), "activate",
+        G_CALLBACK(mssh_window_pref), window);
+
+    gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), file_item);
+    gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), edit_item);
+    gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), server_item);
+
+    g_signal_connect(G_OBJECT(entry), "key-press-event",
+        G_CALLBACK(mssh_window_key_press), window);
+    g_signal_connect(G_OBJECT(entry), "insert-text",
+        G_CALLBACK(mssh_window_insert), window);
+    g_signal_connect(G_OBJECT(entry), "focus-in-event",
+        G_CALLBACK(mssh_window_entry_focused), 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);
+
+    gtk_container_add(GTK_CONTAINER(window), vbox);
+
+    gtk_widget_set_size_request(GTK_WIDGET(window), 1024, 768);
+    gtk_window_set_title(GTK_WINDOW(window), PACKAGE_NAME);
+
+    client = gconf_client_get_default();
+
+    gconf_client_add_dir(client, MSSH_GCONF_PATH,
+        GCONF_CLIENT_PRELOAD_RECURSIVE, NULL);
+
+    gconf_client_notify_add(client, MSSH_GCONF_KEY_FONT,
+        mssh_gconf_notify_font, window, NULL, NULL);
+    gconf_client_notify_add(client, MSSH_GCONF_KEY_FG_COLOUR,
+        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_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_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(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_QUIT_ALL_ENDED);
+    gconf_client_notify(client, MSSH_GCONF_KEY_DIR_FOCUS);
+    gconf_client_notify(client, MSSH_GCONF_KEY_MODIFIER);
+
+    gtk_accel_group_connect(accel, GDK_Up, window->modifier,
+        GTK_ACCEL_VISIBLE, g_cclosure_new(
+        G_CALLBACK(mssh_window_focus), window, NULL));
+    gtk_accel_group_connect(accel, GDK_Down, window->modifier,
+        GTK_ACCEL_VISIBLE, g_cclosure_new(
+        G_CALLBACK(mssh_window_focus), window, NULL));
+    gtk_accel_group_connect(accel, GDK_Left, window->modifier,
+        GTK_ACCEL_VISIBLE, g_cclosure_new(
+        G_CALLBACK(mssh_window_focus), window, NULL));
+    gtk_accel_group_connect(accel, GDK_Right, window->modifier,
+        GTK_ACCEL_VISIBLE, g_cclosure_new(
+        G_CALLBACK(mssh_window_focus), window, NULL));
+
+    window->accel = accel;
+
+    gtk_window_add_accel_group(GTK_WINDOW(window), accel);
 }
 
 void mssh_window_start_session(MSSHWindow* window, char **env,
-	GArray *hosts)
+    GArray *hosts, long cols)
 {
-	int i, j, k;
-	int nhosts = hosts->len;
-	int rows = (nhosts / 2) + (nhosts % 2);
-
-	window->env = env;
-
-	for(i = 0; i < rows; i++)
-	{
-		for(j = 0; j < 2; j++)
-		{
-			k = j + i*2;
-			if(k < nhosts)
-			{
-				mssh_window_add_session(window, g_array_index(hosts,
-					char*, k));
-			}
-		}
-	}
-
-	mssh_window_relayout(window);
+    int i, j, k;
+    int nhosts = hosts->len;
+    int rows = (nhosts / 2) + (nhosts % 2);
+
+    window->env = env;
+    window->columns_override = cols;
+
+    for(i = 0; i < rows; i++)
+    {
+        for(j = 0; j < 2; j++)
+        {
+            k = j + i*2;
+            if(k < nhosts)
+            {
+                mssh_window_add_session(window, g_array_index(hosts,
+                    char*, k));
+            }
+        }
+    }
+
+    mssh_window_relayout(window);
 }
 
 static void mssh_window_class_init(MSSHWindowClass *klass)

+ 27 - 19
src/mssh-window.h

@@ -8,41 +8,49 @@
 
 G_BEGIN_DECLS
 
-#define MSSH_TYPE_WINDOW			mssh_window_get_type()
-#define MSSH_WINDOW(obj)			G_TYPE_CHECK_INSTANCE_CAST(obj,\
-	MSSH_TYPE_WINDOW, MSSHWindow)
-#define MSSH_WINDOW_CLASS(klass)	G_TYPE_CHECK_CLASS_CAST(klass,\
-	MSSH_WINDOW_TYPE, MSSHWindowClass)
-#define IS_MSSH_WINDOW(obj)			G_TYPE_CHECK_INSTANCE_TYPE(obj,\
-	MSSH_TYPE_WINDOW)
+#define MSSH_TYPE_WINDOW            mssh_window_get_type()
+#define MSSH_WINDOW(obj)            G_TYPE_CHECK_INSTANCE_CAST(obj,\
+    MSSH_TYPE_WINDOW, MSSHWindow)
+#define MSSH_WINDOW_CLASS(klass)    G_TYPE_CHECK_CLASS_CAST(klass,\
+    MSSH_WINDOW_TYPE, MSSHWindowClass)
+#define IS_MSSH_WINDOW(obj)         G_TYPE_CHECK_INSTANCE_TYPE(obj,\
+    MSSH_TYPE_WINDOW)
 #define IS_MSSH_WINDOW_CLASS(klass) G_TYPE_CHECK_CLASS_TYPE(klass,\
-	MSSH_TYPE_WINDOW)
+    MSSH_TYPE_WINDOW)
 
 typedef struct
 {
-	GtkWindow widget;
-	GtkWidget *table;
-	GtkWidget *server_menu;
-	GArray *terminals;
-	char **env;
-	int columns;
-	int timeout;
-	gboolean close_ended_sessions;
-	gboolean exit_on_all_closed;
+    GtkWindow widget;
+    GtkWidget *table;
+    GtkWidget *server_menu;
+    GtkWidget *global_entry;
+    GtkAccelGroup *accel;
+    GArray *terminals;
+    char **env;
+    int columns;
+    int columns_override;
+    int timeout;
+    gboolean close_ended_sessions;
+    gboolean exit_on_all_closed;
+    gint modifier;
+    gint dir_focus;
+    gint last_closed;
 } MSSHWindow;
 
 typedef struct
 {
-	GtkWindowClass parent_class;
+    GtkWindowClass parent_class;
 } MSSHWindowClass;
 
 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);
+    GArray *hosts, long cols);
 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);
 
 G_END_DECLS
 

+ 228 - 213
src/mssh.c

@@ -2,37 +2,40 @@
 #include <stdlib.h>
 #include <string.h>
 #include <getopt.h>
+#include <errno.h>
 
 #include <gtk/gtk.h>
 
 #include "config.h"
 #include "mssh-window.h"
 
-#define CONFFILE	".mssh_clusters"
-#define PKGINFO		PACKAGE_NAME " " VERSION
-#define COPYRIGHT	"Copyright (C) 2009 Bradley Smith <[email protected]>"
+#define CONFFILE    ".mssh_clusters"
+#define PKGINFO     PACKAGE_NAME " " VERSION
+#define COPYRIGHT   "Copyright (C) 2009 Bradley Smith <[email protected]>"
 
 static void on_mssh_destroy(GtkWidget *widget, gpointer data)
 {
-	gtk_widget_hide(widget);
-	gtk_main_quit();
+    gtk_widget_hide(widget);
+    gtk_main_quit();
 }
 
 void usage(const char *argv0)
 {
-	fprintf(stderr, "%s\n", PKGINFO);
-	fprintf(stderr, "%s\n", COPYRIGHT);
-	fprintf(stderr, "An ssh client to issue the same commands to multiple servers\n\n");
-	fprintf(stderr, "Usage: %s [OPTION]... (-a ALIAS | HOSTS)\n\n",
-		argv0);
-	fprintf(stderr,
-		"  -a, --alias=ALIAS    Open hosts associated with named alias\n");
-	fprintf(stderr,
-		"  -h, --help           Display this help and exit\n");
-	fprintf(stderr,
-		"  -V, --version        Output version information and exit\n");
-	fprintf(stderr, "\nReport bugs to <%s>.\n", PACKAGE_BUGREPORT);
-	exit(EXIT_FAILURE);
+    fprintf(stderr, "%s\n", PKGINFO);
+    fprintf(stderr, "%s\n", COPYRIGHT);
+    fprintf(stderr, "An ssh client to issue the same commands to multiple servers\n\n");
+    fprintf(stderr, "Usage: %s [OPTION]... (-a ALIAS | HOSTS)\n\n",
+        argv0);
+    fprintf(stderr,
+        "  -a, --alias=ALIAS    Open hosts associated with named alias\n");
+    fprintf(stderr,
+        "  -c, --columns=NUM    Override gconf for number of columns\n");
+    fprintf(stderr,
+        "  -h, --help           Display this help and exit\n");
+    fprintf(stderr,
+        "  -V, --version        Output version information and exit\n");
+    fprintf(stderr, "\nReport bugs to <%s>.\n", PACKAGE_BUGREPORT);
+    exit(EXIT_FAILURE);
 }
 
 static char *fgetline(FILE *stream)
@@ -45,7 +48,7 @@ static char *fgetline(FILE *stream)
     if((buf = malloc(len)) == NULL)
     {
         perror("malloc");
-		exit(EXIT_FAILURE);
+        exit(EXIT_FAILURE);
     }
 
     while((c = fgetc(stream)) != EOF)
@@ -56,7 +59,7 @@ static char *fgetline(FILE *stream)
             if((buf = realloc(buf, len)) == NULL)
             {
                 perror("realloc");
-				exit(EXIT_FAILURE);
+                exit(EXIT_FAILURE);
             }
         }
         if(c == '\n')
@@ -70,211 +73,223 @@ static char *fgetline(FILE *stream)
         }
     }
 
-	if(c == EOF)
-	{
-		free(buf);
-		return NULL;
-	}
+    if(c == EOF)
+    {
+        free(buf);
+        return NULL;
+    }
 
     return buf;
 }
 
 void append_alias(char *alias, GArray *hosts, GData **aliases, int lineno)
 {
-	int i;
-	GArray *fetched;
-
-	if((fetched = g_datalist_get_data(aliases, alias)) == NULL)
-	{
-		printf("Line %d: Alias '%s' not defined\n", lineno, alias);
-		exit(EXIT_FAILURE);
-	}
-
-	for(i = 0; i < fetched->len; i++)
-	{
-		g_array_append_val(hosts, g_array_index(fetched, char*, i));
-	}
+    int i;
+    GArray *fetched;
+
+    if((fetched = g_datalist_get_data(aliases, alias)) == NULL)
+    {
+        printf("Line %d: Alias '%s' not defined\n", lineno, alias);
+        exit(EXIT_FAILURE);
+    }
+
+    for(i = 0; i < fetched->len; i++)
+    {
+        g_array_append_val(hosts, g_array_index(fetched, char*, i));
+    }
 }
 
 GData **parse_aliases(char *conffile)
 {
-	FILE *file;
-	char *line;
-	int lineno = 0;
-
-	GData **aliases = malloc(sizeof(GData*));
-	g_datalist_init(aliases);
-
-	if((file = fopen(conffile, "r")) == NULL)
-		return aliases;
-
-	while((line = fgetline(file)) != NULL)
-	{
-		char *sep, *alias, *hoststr, *tmp;
-		GArray *hosts;
-
-		lineno++;
-
-		if(strcmp(line, "") == 0)
-			continue;
-
-		if((sep = strchr(line, ':')) == NULL)
-		{
-			printf("Line %d: Failed to parse line '%s'\n", lineno, line);
-			exit(EXIT_FAILURE);
-		}
-
-		*sep = '\0';
-		alias = line;
-		hoststr = sep + 1;
-
-		if((tmp = strtok(hoststr, " ")) == NULL)
-		{
-			printf("Line %d: Alias '%s' specifies no hosts\n", lineno,
-				alias);
-			exit(EXIT_FAILURE);
-		}
-
-		hosts = g_array_new(FALSE, TRUE, sizeof(char*));
-
-		do
-		{
-			if(tmp[0] == '[' && tmp[strlen(tmp) - 1] == ']')
-			{
-				tmp++;
-				tmp[strlen(tmp) - 1] = '\0';
-				append_alias(tmp, hosts, aliases, lineno);
-			}
-			else
-				g_array_append_val(hosts, tmp);
-		}
-		while((tmp = strtok(NULL, " ")) != NULL);
-
-		g_datalist_set_data(aliases, alias, hosts);
-	}
-
-	return aliases;
+    FILE *file;
+    char *line;
+    int lineno = 0;
+
+    GData **aliases = malloc(sizeof(GData*));
+    g_datalist_init(aliases);
+
+    if((file = fopen(conffile, "r")) == NULL)
+        return aliases;
+
+    while((line = fgetline(file)) != NULL)
+    {
+        char *sep, *alias, *hoststr, *tmp;
+        GArray *hosts;
+
+        lineno++;
+
+        if(strcmp(line, "") == 0)
+            continue;
+
+        if((sep = strchr(line, ':')) == NULL)
+        {
+            printf("Line %d: Failed to parse line '%s'\n", lineno, line);
+            exit(EXIT_FAILURE);
+        }
+
+        *sep = '\0';
+        alias = line;
+        hoststr = sep + 1;
+
+        if((tmp = strtok(hoststr, " ")) == NULL)
+        {
+            printf("Line %d: Alias '%s' specifies no hosts\n", lineno,
+                alias);
+            exit(EXIT_FAILURE);
+        }
+
+        hosts = g_array_new(FALSE, TRUE, sizeof(char*));
+
+        do
+        {
+            if(tmp[0] == '[' && tmp[strlen(tmp) - 1] == ']')
+            {
+                tmp++;
+                tmp[strlen(tmp) - 1] = '\0';
+                append_alias(tmp, hosts, aliases, lineno);
+            }
+            else
+                g_array_append_val(hosts, tmp);
+        }
+        while((tmp = strtok(NULL, " ")) != NULL);
+
+        g_datalist_set_data(aliases, alias, hosts);
+    }
+
+    return aliases;
 }
 
 int main(int argc, char* argv[], char* env[])
 {
-	GtkWidget* window;
-	int c, option_index = 0;
-	char *home, *conffile;
-	GData **aliases = NULL;
-	GArray *hosts = NULL;
-
-	static struct option long_options[] =
-	{
-		{"alias",	required_argument,	0, 'a'},
-		{"help",	no_argument,		0, 'h'},
-		{"version",	no_argument,		0, 'V'},
-		{0, 0, 0, 0}
-	};
-
-	if((home = getenv("HOME")) != NULL)
-	{
-		int len = strlen(home) + strlen(CONFFILE) + 2;
-
-		conffile = malloc(len);
-		snprintf(conffile, len, "%s/%s", home, CONFFILE);
-
-		aliases = parse_aliases(conffile);
-		free(conffile);
-	}
-	else
-	{
-		fprintf(stderr,
-			"Warning: $HOME not set, not reading config file\n");
-	}
-
-	for(;;)
-	{
-		c = getopt_long(argc, argv, "a:hV", long_options, &option_index);
-
-		if(c == -1)
-			break;
-
-		switch(c)
-		{
-		case 'a':
-			if(aliases && (hosts = g_datalist_get_data(aliases,
-				optarg)) == NULL)
-			{
-				fprintf(stderr, "Alias '%s' not found\n", optarg);
-				usage(argv[0]);
-			}
-			break;
-		case 'h':
-			usage(argv[0]);
-			break;
-		case 'V':
-			printf("%s\n\n", PKGINFO);
-			printf("%s\n\n", COPYRIGHT);
-			printf("Redistribution and use in source and binary forms, with or without\n");
-			printf("modification, are permitted provided that the following conditions are met:\n");
-			printf("\n");
-			printf("    1. Redistributions of source code must retain the copyright notice,\n");
-			printf("       this list of conditions and the following disclaimer.\n");
-			printf("    2. Redistributions in binary form must reproduce the copyright notice,\n");
-			printf("       this list of conditions and the following disclaimer in the\n");
-			printf("       documentation and/or other materials provided with the distribution.\n");
-			printf("    3. The name of the author may not be used to endorse or promote\n");
-			printf("       products derived from this software without specific prior written\n");
-			printf("       permission.\n");
-			printf("\n");
-			printf("THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n");
-			printf("IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n");
-			printf("OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN\n");
-			printf("NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n");
-			printf("SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n");
-			printf("TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n");
-			printf("PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n");
-			printf("LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n");
-			printf("NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n");
-			printf("SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n");
-
-			exit(EXIT_SUCCESS);
-			break;
-		case '?':
-			printf("\n");
-			usage(argv[0]);
-			exit(EXIT_FAILURE);
-			break;
-		default:
-			abort();
-		}
-	}
-
-	if(hosts == NULL)
-	{
-		hosts = g_array_new(FALSE, TRUE, sizeof(char*));
-		if (optind < argc)
-		{
-			while (optind < argc)
-			{
-				char *host = strdup(argv[optind++]);
-				g_array_append_val(hosts, host);
-			}
-		}
-		else
-		{
-			fprintf(stderr, "No hosts specified\n\n");
-			usage(argv[0]);
-		}
-	}
-
-	gtk_init(&argc, &argv);
-
-	window = GTK_WIDGET(mssh_window_new());
-
-	g_signal_connect(G_OBJECT(window), "destroy",
-		G_CALLBACK(on_mssh_destroy), NULL);
-
-	mssh_window_start_session(MSSH_WINDOW(window), env, hosts);
-
-	gtk_widget_show_all(window);
-	gtk_main();
-
-	return 0;
+    GtkWidget* window;
+    int c, option_index = 0;
+    char *home, *conffile;
+    long cols = 0;
+    GData **aliases = NULL;
+    GArray *hosts = NULL;
+
+    static struct option long_options[] =
+    {
+        {"alias",   required_argument,  0, 'a'},
+        {"columns", required_argument,  0, 'c'},
+        {"help",    no_argument,        0, 'h'},
+        {"version", no_argument,        0, 'V'},
+        {0, 0, 0, 0}
+    };
+
+    if((home = getenv("HOME")) != NULL)
+    {
+        int len = strlen(home) + strlen(CONFFILE) + 2;
+
+        conffile = malloc(len);
+        snprintf(conffile, len, "%s/%s", home, CONFFILE);
+
+        aliases = parse_aliases(conffile);
+        free(conffile);
+    }
+    else
+    {
+        fprintf(stderr,
+            "Warning: $HOME not set, not reading config file\n");
+    }
+
+    for(;;)
+    {
+        c = getopt_long(argc, argv, "a:c:hV", long_options, &option_index);
+
+        if(c == -1)
+            break;
+
+        switch(c)
+        {
+        case 'a':
+            if(aliases && (hosts = g_datalist_get_data(aliases,
+                optarg)) == NULL)
+            {
+                fprintf(stderr, "Alias '%s' not found\n\n", optarg);
+                usage(argv[0]);
+            }
+            break;
+        case 'c':
+            errno = 0;
+            cols = strtol(optarg, NULL, 10);
+            if(cols <= 0 || errno != 0)
+            {
+                fprintf(stderr, "Invalid number of columns '%s'\n\n",
+                    optarg);
+                usage(argv[0]);
+            }
+            break;
+        case 'h':
+            usage(argv[0]);
+            break;
+        case 'V':
+            printf("%s\n\n", PKGINFO);
+            printf("%s\n\n", COPYRIGHT);
+            printf("Redistribution and use in source and binary forms, with or without\n");
+            printf("modification, are permitted provided that the following conditions are met:\n");
+            printf("\n");
+            printf("    1. Redistributions of source code must retain the copyright notice,\n");
+            printf("       this list of conditions and the following disclaimer.\n");
+            printf("    2. Redistributions in binary form must reproduce the copyright notice,\n");
+            printf("       this list of conditions and the following disclaimer in the\n");
+            printf("       documentation and/or other materials provided with the distribution.\n");
+            printf("    3. The name of the author may not be used to endorse or promote\n");
+            printf("       products derived from this software without specific prior written\n");
+            printf("       permission.\n");
+            printf("\n");
+            printf("THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n");
+            printf("IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n");
+            printf("OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN\n");
+            printf("NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n");
+            printf("SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n");
+            printf("TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n");
+            printf("PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n");
+            printf("LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n");
+            printf("NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n");
+            printf("SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n");
+
+            exit(EXIT_SUCCESS);
+            break;
+        case '?':
+            printf("\n");
+            usage(argv[0]);
+            exit(EXIT_FAILURE);
+            break;
+        default:
+            abort();
+        }
+    }
+
+    if(hosts == NULL)
+    {
+        hosts = g_array_new(FALSE, TRUE, sizeof(char*));
+        if (optind < argc)
+        {
+            while (optind < argc)
+            {
+                char *host = strdup(argv[optind++]);
+                g_array_append_val(hosts, host);
+            }
+        }
+        else
+        {
+            fprintf(stderr, "No hosts specified\n\n");
+            usage(argv[0]);
+        }
+    }
+
+    gtk_init(&argc, &argv);
+
+    window = GTK_WIDGET(mssh_window_new());
+
+    g_signal_connect(G_OBJECT(window), "destroy",
+        G_CALLBACK(on_mssh_destroy), NULL);
+
+    mssh_window_start_session(MSSH_WINDOW(window), env, hosts, cols);
+
+    gtk_widget_show_all(window);
+    gtk_main();
+
+    return 0;
 }