about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorJames Booth <boothj5@gmail.com>2012-11-25 02:20:26 +0000
committerJames Booth <boothj5@gmail.com>2012-11-25 02:20:26 +0000
commita28e0ee6501cb059fa85c7bf62151987aed58838 (patch)
tree095fc1745289b451585f4d3883aa8625543c8e10
parent24e5e7c786019de25e3b6194a11bcb29d068ef71 (diff)
parent921f026cba6bae0f7450eea0c32b5ea36716cd0a (diff)
downloadprofani-tty-a28e0ee6501cb059fa85c7bf62151987aed58838.tar.gz
Merge branch 'master' into wheel
-rw-r--r--Makefile.am3
-rw-r--r--docs/profanity.150
-rw-r--r--src/chat_session.c30
-rw-r--r--src/command.c63
-rw-r--r--src/input_win.c67
-rw-r--r--src/jabber.c72
-rw-r--r--src/preferences.c327
-rw-r--r--src/preferences.h29
-rw-r--r--src/profanity.c3
-rw-r--r--src/status_bar.c81
-rw-r--r--src/theme.c303
-rw-r--r--src/theme.h66
-rw-r--r--src/title_bar.c1
-rw-r--r--src/ui.h26
-rw-r--r--src/window.c67
-rw-r--r--src/window.h37
-rw-r--r--src/windows.c785
-rw-r--r--themes/aqua27
-rw-r--r--themes/hacker27
-rw-r--r--themes/headache27
-rw-r--r--themes/original27
-rw-r--r--themes/redsplash2
-rw-r--r--themes/whiteness27
-rw-r--r--themes/yellowsplash2
24 files changed, 1342 insertions, 807 deletions
diff --git a/Makefile.am b/Makefile.am
index e0e3b01b..181ffcb2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -8,7 +8,8 @@ profanity_SOURCES = src/command.c src/contact.c src/history.c src/jabber.h \
 	src/main.c src/profanity.h src/prof_history.h src/chat_log.c \
 	src/chat_log.h src/tinyurl.c src/tinyurl.h src/chat_session.c \
 	src/chat_session.h src/release.c src/release.h src/room_chat.c \
-	src/room_chat.h src/stanza.c src/stanza.h src/parser.c src/parser.h
+	src/room_chat.h src/stanza.c src/stanza.h src/parser.c src/parser.h \
+	src/theme.c src/theme.h src/window.c src/window.h
 
 TESTS = tests/testsuite
 check_PROGRAMS = tests/testsuite
diff --git a/docs/profanity.1 b/docs/profanity.1
index 552613c5..a1ad39ca 100644
--- a/docs/profanity.1
+++ b/docs/profanity.1
@@ -1,17 +1,17 @@
 .TH Profanity 1 "August 2012" "Profanity XMPP client"
 .SH NAME
-Profanity \- a simple console based XMPP chat client for Linux.
+Profanity \- a simple console based XMPP chat client.
 .SH SYNOPSIS
 .B profanity
 [-vhd] [-l level]
 .SH DESCRIPTION
 .B Profanity
 is a simple lightweight console based XMPP chat client.  It's emphasis is 
-on having a simple and configurable UI, see the homepage
+on having a simple and configurable command driven UI, see the homepage
 at:
 .br
 .PP
-<http://www.boothj5.com/profanity.shtml>
+<http://www.profanity.im>
 .SH OPTIONS
 .TP
 .BI "\-v, \-\-version"
@@ -66,6 +66,8 @@ beep=false
 .br
 flash=false 
 .br
+theme=mytheme
+.br
 showsplash=false 
 .br
 notify=true 
@@ -81,6 +83,26 @@ remind=15
 vercheck=true
 .br
 .PP
+[connections]
+.br
+logins=user1@server1.org;myuser@server2.com;
+.PP
+.RE
+The [ui] section contains basic preferences, you can find more help on these
+settings by typing "/help beep" for example in Profanity.
+.PP
+The [connections] section is the list of users that will appear in tab
+completion for the /connect command.  This list is automatically populated
+when you successfully log in as a user.
+.SH THEMES
+Themes files for
+.B Profanity
+are stored in
+.I ~/.profanity/themes
+, see the following example:
+.br
+.RS
+.PP
 [colours]
 .br
 bkgnd=default
@@ -117,23 +139,27 @@ xa=cyan
 .br
 offline=red
 .br
-.PP
-[connections]
+typing=yellow
+.br
+gone=red
+.br
+error=red
+.br
+incoming=yellow
+.br
+roominfo=yellow
+.br
+me=yellow
+.br
+them=green
 .br
-logins=user1@server1.org;myuser@server2.com;
 .PP
 .RE
-The [ui] section contains basic preferences, you can find more help on these
-settings by typing "/help beep" for example in Profanity.
-.PP
 The [colours] section allows you to tailor the colours used in Profanity to
 suit your terminal preferences.  Available colours are black, white, red, 
 green, blue, yellow, cyan, magenta and default, where default is the default
 forground or background colour on your terminal.
 .PP
-The [connections] section is the list of users that will appear in tab
-completion for the /connect command.  This list is automatically populated
-when you successfully log in as a user.
 .SH BUGS
 Bugs can either be reported by sending a mail directly to:
 .br
diff --git a/src/chat_session.c b/src/chat_session.c
index 9a62c298..a2650b10 100644
--- a/src/chat_session.c
+++ b/src/chat_session.c
@@ -95,9 +95,7 @@ chat_session_set_composing(const char * const recipient)
 {
     ChatSession session = g_hash_table_lookup(sessions, recipient);
 
-    if (session == NULL) {
-        log_error("No chat session found for %s.", recipient);
-    } else {
+    if (session != NULL) {
         if (session->state != CHAT_STATE_COMPOSING) {
             session->sent = FALSE;
         }
@@ -111,9 +109,7 @@ chat_session_no_activity(const char * const recipient)
 {
     ChatSession session = g_hash_table_lookup(sessions, recipient);
 
-    if (session == NULL) {
-        log_error("No chat session found for %s.", recipient);
-    } else {
+    if (session != NULL) {
         if (session->active_timer != NULL) {
             gdouble elapsed = g_timer_elapsed(session->active_timer, NULL);
 
@@ -146,9 +142,7 @@ chat_session_set_sent(const char * const recipient)
 {
     ChatSession session = g_hash_table_lookup(sessions, recipient);
 
-    if (session == NULL) {
-        log_error("No chat session found for %s.", recipient);
-    } else {
+    if (session != NULL) {
         session->sent = TRUE;
     }
 }
@@ -159,7 +153,6 @@ chat_session_get_sent(const char * const recipient)
     ChatSession session = g_hash_table_lookup(sessions, recipient);
 
     if (session == NULL) {
-        log_error("No chat session found for %s.", recipient);
         return FALSE;
     } else {
         return session->sent;
@@ -178,7 +171,6 @@ chat_session_is_inactive(const char * const recipient)
     ChatSession session = g_hash_table_lookup(sessions, recipient);
 
     if (session == NULL) {
-        log_error("No chat session found for %s.", recipient);
         return FALSE;
     } else {
         return (session->state == CHAT_STATE_INACTIVE);
@@ -191,7 +183,6 @@ chat_session_is_active(const char * const recipient)
     ChatSession session = g_hash_table_lookup(sessions, recipient);
 
     if (session == NULL) {
-        log_error("No chat session found for %s.", recipient);
         return FALSE;
     } else {
         return (session->state == CHAT_STATE_ACTIVE);
@@ -203,9 +194,7 @@ chat_session_set_active(const char * const recipient)
 {
     ChatSession session = g_hash_table_lookup(sessions, recipient);
 
-    if (session == NULL) {
-        log_error("No chat session found for %s.", recipient);
-    } else {
+    if (session != NULL) {
         session->state = CHAT_STATE_ACTIVE;
         g_timer_start(session->active_timer);
         session->sent = TRUE;
@@ -218,7 +207,6 @@ chat_session_is_paused(const char * const recipient)
     ChatSession session = g_hash_table_lookup(sessions, recipient);
 
     if (session == NULL) {
-        log_error("No chat session found for %s.", recipient);
         return FALSE;
     } else {
         return (session->state == CHAT_STATE_PAUSED);
@@ -231,7 +219,6 @@ chat_session_is_gone(const char * const recipient)
     ChatSession session = g_hash_table_lookup(sessions, recipient);
 
     if (session == NULL) {
-        log_error("No chat session found for %s.", recipient);
         return FALSE;
     } else {
         return (session->state == CHAT_STATE_GONE);
@@ -243,9 +230,7 @@ chat_session_set_gone(const char * const recipient)
 {
     ChatSession session = g_hash_table_lookup(sessions, recipient);
 
-    if (session == NULL) {
-        log_error("No chat session found for %s.", recipient);
-    } else {
+    if (session != NULL) {
         session->state = CHAT_STATE_GONE;
     }
 }
@@ -256,7 +241,6 @@ chat_session_get_recipient_supports(const char * const recipient)
     ChatSession session = g_hash_table_lookup(sessions, recipient);
 
     if (session == NULL) {
-        log_error("No chat session found for %s.", recipient);
         return FALSE;
     } else {
         return session->recipient_supports;
@@ -269,9 +253,7 @@ chat_session_set_recipient_supports(const char * const recipient,
 {
     ChatSession session = g_hash_table_lookup(sessions, recipient);
 
-    if (session == NULL) {
-        log_error("No chat session found for %s.", recipient);
-    } else {
+    if (session != NULL) {
         session->recipient_supports = recipient_supports;
     }
 }
diff --git a/src/command.c b/src/command.c
index 715ad0ee..facecd62 100644
--- a/src/command.c
+++ b/src/command.c
@@ -40,6 +40,7 @@
 #include "preferences.h"
 #include "prof_autocomplete.h"
 #include "profanity.h"
+#include "theme.h"
 #include "tinyurl.h"
 #include "ui.h"
 
@@ -108,6 +109,7 @@ static gboolean _cmd_set_beep(gchar **args, struct cmd_help_t help);
 static gboolean _cmd_set_notify(gchar **args, struct cmd_help_t help);
 static gboolean _cmd_set_log(gchar **args, struct cmd_help_t help);
 static gboolean _cmd_set_priority(gchar **args, struct cmd_help_t help);
+static gboolean _cmd_set_reconnect(gchar **args, struct cmd_help_t help);
 static gboolean _cmd_set_intype(gchar **args, struct cmd_help_t help);
 static gboolean _cmd_set_flash(gchar **args, struct cmd_help_t help);
 static gboolean _cmd_set_showsplash(gchar **args, struct cmd_help_t help);
@@ -124,6 +126,7 @@ static gboolean _cmd_xa(gchar **args, struct cmd_help_t help);
 static gboolean _cmd_info(gchar **args, struct cmd_help_t help);
 static gboolean _cmd_wins(gchar **args, struct cmd_help_t help);
 static gboolean _cmd_nick(gchar **args, struct cmd_help_t help);
+static gboolean _cmd_theme(gchar **args, struct cmd_help_t help);
 
 /*
  * The commands are broken down into three groups:
@@ -191,6 +194,18 @@ static struct cmd_t main_commands[] =
           "you will need to restart Profanity for config file edits to take effect.",
           NULL } } },
 
+    { "/theme",
+        _cmd_theme, parse_args, 1, 1,
+        { "/theme [theme-name]", "Change colour theme.",
+        { "/theme [theme-name]",
+          "--------------",
+          "Change the colour setting as defined in:",
+          "",
+          "    ~/.profanity/themes/theme-name",
+          "",
+          "Using \"default\" as the theme name will reset to the default colours.",
+          NULL } } },
+
     { "/msg",
         _cmd_msg, parse_args_with_freetext, 2, 2,
         { "/msg user@host mesg", "Send mesg to user.",
@@ -471,6 +486,18 @@ static struct cmd_t setting_commands[] =
           "Config file value :   maxsize=bytes",
           NULL } } },
 
+    { "/reconnect",
+        _cmd_set_reconnect, parse_args, 1, 1,
+        { "/reconnect seconds", "Set reconnect interval.",
+        { "/reconnect seconds",
+          "--------------------",
+          "Set the reconnect attempt interval in seconds for when the connection is lost.",
+          "A value of 0 will switch of reconnect attempts.",
+          "",
+          "Config file section : [jabber]",
+          "Config file value :   reconnect=seconds",
+          NULL } } },
+
     { "/priority",
         _cmd_set_priority, parse_args, 1, 1,
         { "/priority <value>", "Set priority for connection.",
@@ -1078,6 +1105,20 @@ _cmd_prefs(gchar **args, struct cmd_help_t help)
 }
 
 static gboolean
+_cmd_theme(gchar **args, struct cmd_help_t help)
+{
+    if (theme_change(args[0])) {
+        win_load_colours();
+        prefs_set_theme(args[0]);
+        cons_show("Loaded theme: %s", args[0]);
+    } else {
+        cons_show("Couldn't find theme: %s", args[0]);
+    }
+
+    return TRUE;
+}
+
+static gboolean
 _cmd_who(gchar **args, struct cmd_help_t help)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
@@ -1440,6 +1481,28 @@ _cmd_set_log(gchar **args, struct cmd_help_t help)
 }
 
 static gboolean
+_cmd_set_reconnect(gchar **args, struct cmd_help_t help)
+{
+    char *value = args[0];
+    int intval;
+
+    if (_strtoi(value, &intval, 0, INT_MAX) == 0) {
+        prefs_set_reconnect(intval);
+        if (intval == 0) {
+            cons_show("Reconnect disabled.", intval);
+        } else {
+            cons_show("Reconnect interval set to %d seconds.", intval);
+        }
+    } else {
+        cons_show("Usage: %s", help.usage);
+    }
+
+    /* TODO: make 'level' subcommand for debug level */
+
+    return TRUE;
+}
+
+static gboolean
 _cmd_set_priority(gchar **args, struct cmd_help_t help)
 {
     char *value = args[0];
diff --git a/src/input_win.c b/src/input_win.c
index fa61e1e3..55977706 100644
--- a/src/input_win.c
+++ b/src/input_win.c
@@ -58,6 +58,7 @@
 #include "log.h"
 #include "preferences.h"
 #include "profanity.h"
+#include "theme.h"
 #include "ui.h"
 
 static WINDOW *inp_win;
@@ -65,6 +66,7 @@ static int pad_start = 0;
 
 static int _handle_edit(const int ch, char *input, int *size);
 static int _printable(const int ch);
+static gboolean _special_key(const int ch);
 
 void
 create_input_window(void)
@@ -79,7 +81,7 @@ create_input_window(void)
     getmaxyx(stdscr, rows, cols);
 
     inp_win = newpad(1, INP_WIN_MAX);
-    wbkgd(inp_win, COLOR_PAIR(1));
+    wbkgd(inp_win, COLOUR_INPUT_TEXT);
     keypad(inp_win, TRUE);
     wmove(inp_win, 0, 0);
     prefresh(inp_win, 0, pad_start, rows-1, 0, rows-1, cols-1);
@@ -214,6 +216,12 @@ inp_put_back(void)
     prefresh(inp_win, 0, pad_start, rows-1, 0, rows-1, cols-1);
 }
 
+int
+inp_get_next_char(void)
+{
+    return wgetch(inp_win);
+}
+
 void
 inp_replace_input(char *input, const char * const new_input, int *size)
 {
@@ -240,6 +248,7 @@ _handle_edit(const int ch, char *input, int *size)
     char *next = NULL;
     int inp_y = 0;
     int inp_x = 0;
+    int next_ch;
 
     getmaxyx(stdscr, rows, cols);
     getyx(inp_win, inp_y, inp_x);
@@ -247,9 +256,50 @@ _handle_edit(const int ch, char *input, int *size)
     switch(ch) {
 
     case 27: // ESC
-        *size = 0;
-        inp_clear();
-        return 1;
+        // check for ALT-num
+        next_ch = inp_get_next_char();
+        if (next_ch != ERR) {
+            switch (next_ch)
+            {
+                case '1':
+                    win_switch_if_active(0);
+                    break;
+                case '2':
+                    win_switch_if_active(1);
+                    break;
+                case '3':
+                    win_switch_if_active(2);
+                    break;
+                case '4':
+                    win_switch_if_active(3);
+                    break;
+                case '5':
+                    win_switch_if_active(4);
+                    break;
+                case '6':
+                    win_switch_if_active(5);
+                    break;
+                case '7':
+                    win_switch_if_active(6);
+                    break;
+                case '8':
+                    win_switch_if_active(7);
+                    break;
+                case '9':
+                    win_switch_if_active(8);
+                    break;
+                case '0':
+                    win_switch_if_active(9);
+                    break;
+                default:
+                    break;
+            }
+            return 1;
+        } else {
+            *size = 0;
+            inp_clear();
+            return 1;
+        }
 
     case 127:
     case KEY_BACKSPACE:
@@ -372,6 +422,13 @@ _printable(const int ch)
             ch != KEY_F(4) && ch != KEY_F(5) && ch != KEY_F(6) &&
             ch != KEY_F(7) && ch != KEY_F(8) && ch != KEY_F(9) &&
             ch != KEY_F(10) && ch!= KEY_F(11) && ch != KEY_F(12) &&
-            ch != KEY_IC && ch != KEY_EIC && ch != KEY_RESIZE);
+            ch != KEY_IC && ch != KEY_EIC && ch != KEY_RESIZE &&
+            !_special_key(ch));
 }
 
+static gboolean
+_special_key(const int ch)
+{
+    char *str = unctrl(ch);
+    return ((strlen(str) > 1) && g_str_has_prefix(str, "^"));
+}
diff --git a/src/jabber.c b/src/jabber.c
index bb5f192f..84f5db40 100644
--- a/src/jabber.c
+++ b/src/jabber.c
@@ -48,6 +48,11 @@ static struct _jabber_conn_t {
     int priority;
 } jabber_conn;
 
+// for auto reconnect
+static char *saved_user;
+static char *saved_password;
+static GTimer *reconnect_timer;
+
 static log_level_t _get_log_level(xmpp_log_level_t xmpp_level);
 static xmpp_log_level_t _get_xmpp_log_level();
 static void _xmpp_file_logger(void * const userdata,
@@ -98,15 +103,22 @@ jabber_conn_status_t
 jabber_connect(const char * const user,
     const char * const passwd)
 {
-    log_info("Connecting as %s", user);
+    if (saved_user == NULL) {
+        saved_user = strdup(user);
+    }
+    if (saved_password == NULL) {
+        saved_password = strdup(passwd);
+    }
+
+    log_info("Connecting as %s", saved_user);
     xmpp_initialize();
 
     jabber_conn.log = _xmpp_get_file_logger();
     jabber_conn.ctx = xmpp_ctx_new(NULL, jabber_conn.log);
     jabber_conn.conn = xmpp_conn_new(jabber_conn.ctx);
 
-    xmpp_conn_set_jid(jabber_conn.conn, user);
-    xmpp_conn_set_pass(jabber_conn.conn, passwd);
+    xmpp_conn_set_jid(jabber_conn.conn, saved_user);
+    xmpp_conn_set_pass(jabber_conn.conn, saved_password);
 
     if (jabber_conn.tls_disabled)
         xmpp_conn_disable_tls(jabber_conn.conn);
@@ -141,10 +153,21 @@ jabber_disconnect(void)
 void
 jabber_process_events(void)
 {
+    // run xmpp event loop if connected, connecting or disconnecting
     if (jabber_conn.conn_status == JABBER_CONNECTED
             || jabber_conn.conn_status == JABBER_CONNECTING
-            || jabber_conn.conn_status == JABBER_DISCONNECTING)
+            || jabber_conn.conn_status == JABBER_DISCONNECTING) {
         xmpp_run_once(jabber_conn.ctx, 10);
+
+    // check timer and reconnect if disconnected and timer set
+    } else if ((jabber_conn.conn_status == JABBER_DISCONNECTED) &&
+            (reconnect_timer != NULL)) {
+        if (g_timer_elapsed(reconnect_timer, NULL) > (prefs_get_reconnect() * 1.0)) {
+            log_debug("Attempting reconncet as %s", saved_user);
+            jabber_connect(saved_user, saved_password);
+        }
+    }
+
 }
 
 void
@@ -400,6 +423,8 @@ jabber_get_status(void)
 void
 jabber_free_resources(void)
 {
+    saved_user = NULL;
+    saved_password = NULL;
     chat_sessions_clear();
     xmpp_conn_release(jabber_conn.conn);
     xmpp_ctx_free(jabber_conn.ctx);
@@ -624,6 +649,7 @@ _connection_handler(xmpp_conn_t * const conn,
 {
     xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
 
+    // login success
     if (status == XMPP_CONN_CONNECT) {
         const char *jid = xmpp_conn_get_jid(conn);
         prof_handle_login_success(jid);
@@ -637,26 +663,56 @@ _connection_handler(xmpp_conn_t * const conn,
         _jabber_roster_request();
         jabber_conn.conn_status = JABBER_CONNECTED;
         jabber_conn.presence = PRESENCE_ONLINE;
+
+        if (reconnect_timer != NULL) {
+            g_timer_destroy(reconnect_timer);
+            reconnect_timer = NULL;
+        }
+
     } else {
 
         // received close stream response from server after disconnect
         if (jabber_conn.conn_status == JABBER_DISCONNECTING) {
             jabber_conn.conn_status = JABBER_DISCONNECTED;
             jabber_conn.presence = PRESENCE_OFFLINE;
+            if (saved_user != NULL) {
+                free(saved_user);
+                saved_user = NULL;
+            }
+            if (saved_password != NULL) {
+                free(saved_password);
+                saved_password = NULL;
+            }
 
         // lost connection for unkown reason
         } else if (jabber_conn.conn_status == JABBER_CONNECTED) {
             prof_handle_lost_connection();
+            reconnect_timer = g_timer_new();
             xmpp_stop(ctx);
             jabber_conn.conn_status = JABBER_DISCONNECTED;
             jabber_conn.presence = PRESENCE_OFFLINE;
 
         // login attempt failed
         } else {
-            prof_handle_failed_login();
-            xmpp_stop(ctx);
-            jabber_conn.conn_status = JABBER_DISCONNECTED;
-            jabber_conn.presence = PRESENCE_OFFLINE;
+            if (reconnect_timer == NULL) {
+                prof_handle_failed_login();
+                if (saved_user != NULL) {
+                    free(saved_user);
+                    saved_user = NULL;
+                }
+                if (saved_password != NULL) {
+                    free(saved_password);
+                    saved_password = NULL;
+                }
+                xmpp_stop(ctx);
+                jabber_conn.conn_status = JABBER_DISCONNECTED;
+                jabber_conn.presence = PRESENCE_OFFLINE;
+            } else {
+                xmpp_stop(ctx);
+                g_timer_start(reconnect_timer);
+                jabber_conn.conn_status = JABBER_DISCONNECTED;
+                jabber_conn.presence = PRESENCE_OFFLINE;
+            }
         }
     }
 }
diff --git a/src/preferences.c b/src/preferences.c
index 9bfacd88..b2db3123 100644
--- a/src/preferences.c
+++ b/src/preferences.c
@@ -44,56 +44,6 @@ gint log_maxsize = 0;
 static PAutocomplete login_ac;
 static PAutocomplete boolean_choice_ac;
 
-struct colour_string_t {
-    char *str;
-    NCURSES_COLOR_T colour;
-};
-
-static int num_colours = 9;
-static struct colour_string_t colours[] = {
-    { "default", -1 },
-    { "white", COLOR_WHITE },
-    { "green", COLOR_GREEN },
-    { "red", COLOR_RED },
-    { "yellow", COLOR_YELLOW },
-    { "blue", COLOR_BLUE },
-    { "cyan", COLOR_CYAN },
-    { "black", COLOR_BLACK },
-    { "magenta", COLOR_MAGENTA },
-};
-
-// colour preferences
-static struct colours_t {
-        NCURSES_COLOR_T bkgnd;
-        NCURSES_COLOR_T titlebar;
-        NCURSES_COLOR_T statusbar;
-        NCURSES_COLOR_T titlebartext;
-        NCURSES_COLOR_T titlebarbrackets;
-        NCURSES_COLOR_T statusbartext;
-        NCURSES_COLOR_T statusbarbrackets;
-        NCURSES_COLOR_T statusbaractive;
-        NCURSES_COLOR_T statusbarnew;
-        NCURSES_COLOR_T maintext;
-        NCURSES_COLOR_T splashtext;
-        NCURSES_COLOR_T online;
-        NCURSES_COLOR_T away;
-        NCURSES_COLOR_T xa;
-        NCURSES_COLOR_T dnd;
-        NCURSES_COLOR_T chat;
-        NCURSES_COLOR_T offline;
-        NCURSES_COLOR_T typing;
-        NCURSES_COLOR_T gone;
-        NCURSES_COLOR_T error;
-        NCURSES_COLOR_T incoming;
-        NCURSES_COLOR_T roominfo;
-        NCURSES_COLOR_T me;
-        NCURSES_COLOR_T them;
-} colour_prefs;
-
-static NCURSES_COLOR_T _lookup_colour(const char * const colour);
-static void _set_colour(gchar *val, NCURSES_COLOR_T *pref,
-    NCURSES_COLOR_T def);
-static void _load_colours(void);
 static void _save_prefs(void);
 
 void
@@ -125,8 +75,6 @@ prefs_load(void)
     }
     free(jids);
 
-    _load_colours();
-
     err = NULL;
     log_maxsize = g_key_file_get_integer(prefs, "log", "maxsize", &err);
     if (err != NULL) {
@@ -147,111 +95,6 @@ prefs_close(void)
     g_key_file_free(prefs);
 }
 
-static NCURSES_COLOR_T
-_lookup_colour(const char * const colour)
-{
-    int i;
-    for (i = 0; i < num_colours; i++) {
-        if (strcmp(colours[i].str, colour) == 0) {
-            return colours[i].colour;
-        }
-    }
-
-    return -99;
-}
-
-static void
-_set_colour(gchar *val, NCURSES_COLOR_T *pref,
-    NCURSES_COLOR_T def)
-{
-    if(!val) {
-        *pref = def;
-    } else {
-        NCURSES_COLOR_T col = _lookup_colour(val);
-        if (col == -99) {
-            *pref = def;
-        } else {
-            *pref = col;
-        }
-    }
-}
-
-static void
-_load_colours(void)
-{
-    gchar *bkgnd_val = g_key_file_get_string(prefs, "colours", "bkgnd", NULL);
-    _set_colour(bkgnd_val, &colour_prefs.bkgnd, -1);
-
-    gchar *titlebar_val = g_key_file_get_string(prefs, "colours", "titlebar", NULL);
-    _set_colour(titlebar_val, &colour_prefs.titlebar, COLOR_BLUE);
-
-    gchar *statusbar_val = g_key_file_get_string(prefs, "colours", "statusbar", NULL);
-    _set_colour(statusbar_val, &colour_prefs.statusbar, COLOR_BLUE);
-
-    gchar *titlebartext_val = g_key_file_get_string(prefs, "colours", "titlebartext", NULL);
-    _set_colour(titlebartext_val, &colour_prefs.titlebartext, COLOR_WHITE);
-
-    gchar *titlebarbrackets_val = g_key_file_get_string(prefs, "colours", "titlebarbrackets", NULL);
-    _set_colour(titlebarbrackets_val, &colour_prefs.titlebarbrackets, COLOR_CYAN);
-
-    gchar *statusbartext_val = g_key_file_get_string(prefs, "colours", "statusbartext", NULL);
-    _set_colour(statusbartext_val, &colour_prefs.statusbartext, COLOR_WHITE);
-
-    gchar *statusbarbrackets_val = g_key_file_get_string(prefs, "colours", "statusbarbrackets", NULL);
-    _set_colour(statusbarbrackets_val, &colour_prefs.statusbarbrackets, COLOR_CYAN);
-
-    gchar *statusbaractive_val = g_key_file_get_string(prefs, "colours", "statusbaractive", NULL);
-    _set_colour(statusbaractive_val, &colour_prefs.statusbaractive, COLOR_CYAN);
-
-    gchar *statusbarnew_val = g_key_file_get_string(prefs, "colours", "statusbarnew", NULL);
-    _set_colour(statusbarnew_val, &colour_prefs.statusbarnew, COLOR_WHITE);
-
-    gchar *maintext_val = g_key_file_get_string(prefs, "colours", "maintext", NULL);
-    _set_colour(maintext_val, &colour_prefs.maintext, COLOR_WHITE);
-
-    gchar *splashtext_val = g_key_file_get_string(prefs, "colours", "splashtext", NULL);
-    _set_colour(splashtext_val, &colour_prefs.splashtext, COLOR_CYAN);
-
-    gchar *online_val = g_key_file_get_string(prefs, "colours", "online", NULL);
-    _set_colour(online_val, &colour_prefs.online, COLOR_GREEN);
-
-    gchar *away_val = g_key_file_get_string(prefs, "colours", "away", NULL);
-    _set_colour(away_val, &colour_prefs.away, COLOR_CYAN);
-
-    gchar *chat_val = g_key_file_get_string(prefs, "colours", "chat", NULL);
-    _set_colour(chat_val, &colour_prefs.chat, COLOR_GREEN);
-
-    gchar *dnd_val = g_key_file_get_string(prefs, "colours", "dnd", NULL);
-    _set_colour(dnd_val, &colour_prefs.dnd, COLOR_RED);
-
-    gchar *xa_val = g_key_file_get_string(prefs, "colours", "xa", NULL);
-    _set_colour(xa_val, &colour_prefs.xa, COLOR_CYAN);
-
-    gchar *offline_val = g_key_file_get_string(prefs, "colours", "offline", NULL);
-    _set_colour(offline_val, &colour_prefs.offline, COLOR_RED);
-
-    gchar *typing_val = g_key_file_get_string(prefs, "colours", "typing", NULL);
-    _set_colour(typing_val, &colour_prefs.typing, COLOR_YELLOW);
-
-    gchar *gone_val = g_key_file_get_string(prefs, "colours", "gone", NULL);
-    _set_colour(gone_val, &colour_prefs.gone, COLOR_RED);
-
-    gchar *error_val = g_key_file_get_string(prefs, "colours", "error", NULL);
-    _set_colour(error_val, &colour_prefs.error, COLOR_RED);
-
-    gchar *incoming_val = g_key_file_get_string(prefs, "colours", "incoming", NULL);
-    _set_colour(incoming_val, &colour_prefs.incoming, COLOR_YELLOW);
-
-    gchar *roominfo_val = g_key_file_get_string(prefs, "colours", "roominfo", NULL);
-    _set_colour(roominfo_val, &colour_prefs.roominfo, COLOR_YELLOW);
-
-    gchar *me_val = g_key_file_get_string(prefs, "colours", "me", NULL);
-    _set_colour(me_val, &colour_prefs.me, COLOR_YELLOW);
-
-    gchar *them_val = g_key_file_get_string(prefs, "colours", "them", NULL);
-    _set_colour(them_val, &colour_prefs.them, COLOR_GREEN);
-}
-
 char *
 prefs_find_login(char *prefix)
 {
@@ -289,6 +132,19 @@ prefs_set_beep(gboolean value)
     _save_prefs();
 }
 
+gchar *
+prefs_get_theme(void)
+{
+    return g_key_file_get_string(prefs, "ui", "theme", NULL);
+}
+
+void
+prefs_set_theme(gchar *value)
+{
+    g_key_file_set_string(prefs, "ui", "theme", value);
+    _save_prefs();
+}
+
 gboolean
 prefs_get_states(void)
 {
@@ -384,6 +240,19 @@ prefs_set_priority(gint value)
     _save_prefs();
 }
 
+gint
+prefs_get_reconnect(void)
+{
+    return g_key_file_get_integer(prefs, "jabber", "reconnect", NULL);
+}
+
+void
+prefs_set_reconnect(gint value)
+{
+    g_key_file_set_integer(prefs, "jabber", "reconnect", value);
+    _save_prefs();
+}
+
 gboolean
 prefs_get_vercheck(void)
 {
@@ -511,147 +380,3 @@ _save_prefs(void)
     char *g_prefs_data = g_key_file_to_data(prefs, &g_data_size, NULL);
     g_file_set_contents(prefs_loc->str, g_prefs_data, g_data_size, NULL);
 }
-
-NCURSES_COLOR_T
-prefs_get_bkgnd()
-{
-    return colour_prefs.bkgnd;
-}
-
-NCURSES_COLOR_T
-prefs_get_titlebar()
-{
-    return colour_prefs.titlebar;
-}
-
-NCURSES_COLOR_T
-prefs_get_statusbar()
-{
-    return colour_prefs.statusbar;
-}
-
-NCURSES_COLOR_T
-prefs_get_titlebartext()
-{
-    return colour_prefs.titlebartext;
-}
-
-NCURSES_COLOR_T
-prefs_get_titlebarbrackets()
-{
-    return colour_prefs.titlebarbrackets;
-}
-
-NCURSES_COLOR_T
-prefs_get_statusbartext()
-{
-    return colour_prefs.statusbartext;
-}
-
-NCURSES_COLOR_T
-prefs_get_statusbarbrackets()
-{
-    return colour_prefs.statusbarbrackets;
-}
-
-NCURSES_COLOR_T
-prefs_get_statusbaractive()
-{
-    return colour_prefs.statusbaractive;
-}
-
-NCURSES_COLOR_T
-prefs_get_statusbarnew()
-{
-    return colour_prefs.statusbarnew;
-}
-
-NCURSES_COLOR_T
-prefs_get_maintext()
-{
-    return colour_prefs.maintext;
-}
-
-NCURSES_COLOR_T
-prefs_get_splashtext()
-{
-    return colour_prefs.splashtext;
-}
-
-NCURSES_COLOR_T
-prefs_get_online()
-{
-    return colour_prefs.online;
-}
-
-NCURSES_COLOR_T
-prefs_get_away()
-{
-    return colour_prefs.away;
-}
-
-NCURSES_COLOR_T
-prefs_get_chat()
-{
-    return colour_prefs.chat;
-}
-
-NCURSES_COLOR_T
-prefs_get_dnd()
-{
-    return colour_prefs.dnd;
-}
-
-NCURSES_COLOR_T
-prefs_get_xa()
-{
-    return colour_prefs.xa;
-}
-
-NCURSES_COLOR_T
-prefs_get_offline()
-{
-    return colour_prefs.offline;
-}
-
-NCURSES_COLOR_T
-prefs_get_typing()
-{
-    return colour_prefs.typing;
-}
-
-NCURSES_COLOR_T
-prefs_get_gone()
-{
-    return colour_prefs.gone;
-}
-
-NCURSES_COLOR_T
-prefs_get_error()
-{
-    return colour_prefs.error;
-}
-
-NCURSES_COLOR_T
-prefs_get_incoming()
-{
-    return colour_prefs.incoming;
-}
-
-NCURSES_COLOR_T
-prefs_get_roominfo()
-{
-    return colour_prefs.roominfo;
-}
-
-NCURSES_COLOR_T
-prefs_get_me()
-{
-    return colour_prefs.me;
-}
-
-NCURSES_COLOR_T
-prefs_get_them()
-{
-    return colour_prefs.them;
-}
diff --git a/src/preferences.h b/src/preferences.h
index bb45ee07..bde18e59 100644
--- a/src/preferences.h
+++ b/src/preferences.h
@@ -63,6 +63,8 @@ gboolean prefs_get_states(void);
 void prefs_set_states(gboolean value);
 gboolean prefs_get_outtype(void);
 void prefs_set_outtype(gboolean value);
+gchar * prefs_get_theme(void);
+void prefs_set_theme(gchar *value);
 
 void prefs_set_notify_message(gboolean value);
 gboolean prefs_get_notify_message(void);
@@ -74,32 +76,9 @@ void prefs_set_max_log_size(gint value);
 gint prefs_get_max_log_size(void);
 void prefs_set_priority(gint value);
 gint prefs_get_priority(void);
+void prefs_set_reconnect(gint value);
+gint prefs_get_reconnect(void);
 
 void prefs_add_login(const char *jid);
 
-NCURSES_COLOR_T prefs_get_bkgnd();
-NCURSES_COLOR_T prefs_get_titlebar();
-NCURSES_COLOR_T prefs_get_statusbar();
-NCURSES_COLOR_T prefs_get_titlebartext();
-NCURSES_COLOR_T prefs_get_titlebarbrackets();
-NCURSES_COLOR_T prefs_get_statusbartext();
-NCURSES_COLOR_T prefs_get_statusbarbrackets();
-NCURSES_COLOR_T prefs_get_statusbaractive();
-NCURSES_COLOR_T prefs_get_statusbarnew();
-NCURSES_COLOR_T prefs_get_maintext();
-NCURSES_COLOR_T prefs_get_splashtext();
-NCURSES_COLOR_T prefs_get_online();
-NCURSES_COLOR_T prefs_get_away();
-NCURSES_COLOR_T prefs_get_chat();
-NCURSES_COLOR_T prefs_get_dnd();
-NCURSES_COLOR_T prefs_get_xa();
-NCURSES_COLOR_T prefs_get_offline();
-NCURSES_COLOR_T prefs_get_typing();
-NCURSES_COLOR_T prefs_get_gone();
-NCURSES_COLOR_T prefs_get_error();
-NCURSES_COLOR_T prefs_get_incoming();
-NCURSES_COLOR_T prefs_get_roominfo();
-NCURSES_COLOR_T prefs_get_me();
-NCURSES_COLOR_T prefs_get_them();
-
 #endif
diff --git a/src/profanity.c b/src/profanity.c
index 1b30296e..e98443eb 100644
--- a/src/profanity.c
+++ b/src/profanity.c
@@ -39,6 +39,7 @@
 #include "preferences.h"
 #include "profanity.h"
 #include "room_chat.h"
+#include "theme.h"
 #include "jabber.h"
 #include "ui.h"
 
@@ -446,6 +447,7 @@ _init(const int disable_tls, char *log_level)
     log_info("Starting Profanity (%s)...", PACKAGE_VERSION);
     chat_log_init();
     prefs_load();
+    theme_load(prefs_get_theme());
     gui_init();
     jabber_init(disable_tls);
     cmd_init();
@@ -462,6 +464,7 @@ _shutdown(void)
     gui_close();
     chat_log_close();
     prefs_close();
+    theme_close();
     cmd_close();
     log_close();
 }
diff --git a/src/status_bar.c b/src/status_bar.c
index 37a9bce4..52d90439 100644
--- a/src/status_bar.c
+++ b/src/status_bar.c
@@ -32,13 +32,14 @@
 #include <ncurses/ncurses.h>
 #endif
 
+#include "theme.h"
 #include "ui.h"
 
 static WINDOW *status_bar;
 static char *message = NULL;
-static char _active[29] = "[ ][ ][ ][ ][ ][ ][ ][ ][  ]";
-static int is_active[9];
-static int is_new[9];
+static char _active[31] = "[ ][ ][ ][ ][ ][ ][ ][ ][ ][ ]";
+static int is_active[10];
+static int is_new[10];
 static int dirty;
 static GDateTime *last_time;
 
@@ -50,7 +51,9 @@ create_status_bar(void)
     int rows, cols, i;
     getmaxyx(stdscr, rows, cols);
 
-    for (i = 0; i < 9; i++) {
+    is_active[0] = TRUE;
+    is_new[0] = FALSE;
+    for (i = 1; i < 10; i++) {
         is_active[i] = FALSE;
         is_new[i] = FALSE;
     }
@@ -58,7 +61,7 @@ create_status_bar(void)
     status_bar = newwin(1, cols, rows-2, 0);
     wbkgd(status_bar, COLOUR_STATUS_TEXT);
     wattron(status_bar, COLOUR_STATUS_BRACKET);
-    mvwprintw(status_bar, 0, cols - 29, _active);
+    mvwprintw(status_bar, 0, cols - 31, _active);
     wattroff(status_bar, COLOUR_STATUS_BRACKET);
 
     last_time = g_date_time_new_now_local();
@@ -98,18 +101,18 @@ status_bar_resize(void)
     wbkgd(status_bar, COLOUR_STATUS_TEXT);
     wclear(status_bar);
     wattron(status_bar, COLOUR_STATUS_BRACKET);
-    mvwprintw(status_bar, 0, cols - 29, _active);
+    mvwprintw(status_bar, 0, cols - 31, _active);
     wattroff(status_bar, COLOUR_STATUS_BRACKET);
 
-    for(i = 0; i < 9; i++) {
+    for(i = 0; i < 10; i++) {
         if (is_new[i])
-            status_bar_new(i+1);
+            status_bar_new(i);
         else if (is_active[i])
-            status_bar_active(i+1);
+            status_bar_active(i);
     }
 
     if (message != NULL)
-        mvwprintw(status_bar, 0, 9, message);
+        mvwprintw(status_bar, 0, 10, message);
 
     last_time = g_date_time_new_now_local();
     dirty = TRUE;
@@ -118,16 +121,14 @@ status_bar_resize(void)
 void
 status_bar_inactive(const int win)
 {
-    is_active[win-1] = FALSE;
-    is_new[win-1] = FALSE;
+    is_active[win] = FALSE;
+    is_new[win] = FALSE;
 
-    int active_pos = 1 + ((win -1) * 3);
+    int active_pos = 1 + (win * 3);
 
     int cols = getmaxx(stdscr);
 
-    mvwaddch(status_bar, 0, cols - 29 + active_pos, ' ');
-    if (win == 9)
-        mvwaddch(status_bar, 0, cols - 29 + active_pos + 1, ' ');
+    mvwaddch(status_bar, 0, cols - 31 + active_pos, ' ');
 
     dirty = TRUE;
 }
@@ -135,18 +136,18 @@ status_bar_inactive(const int win)
 void
 status_bar_active(const int win)
 {
-    is_active[win-1] = TRUE;
-    is_new[win-1] = FALSE;
+    is_active[win] = TRUE;
+    is_new[win] = FALSE;
 
-    int active_pos = 1 + ((win -1) * 3);
+    int active_pos = 1 + (win * 3);
 
     int cols = getmaxx(stdscr);
 
     wattron(status_bar, COLOUR_STATUS_ACTIVE);
-    if (win < 9)
-        mvwprintw(status_bar, 0, cols - 29 + active_pos, "%d", win+1);
+    if (win+1 < 10)
+        mvwprintw(status_bar, 0, cols - 31 + active_pos, "%d", win+1);
     else
-        mvwprintw(status_bar, 0, cols - 29 + active_pos, "10");
+        mvwprintw(status_bar, 0, cols - 31 + active_pos, "0");
     wattroff(status_bar, COLOUR_STATUS_ACTIVE);
 
     dirty = TRUE;
@@ -155,19 +156,19 @@ status_bar_active(const int win)
 void
 status_bar_new(const int win)
 {
-    is_active[win-1] = TRUE;
-    is_new[win-1] = TRUE;
+    is_active[win] = TRUE;
+    is_new[win] = TRUE;
 
-    int active_pos = 1 + ((win -1) * 3);
+    int active_pos = 1 + (win * 3);
 
     int cols = getmaxx(stdscr);
 
     wattron(status_bar, COLOUR_STATUS_NEW);
     wattron(status_bar, A_BLINK);
-    if (win < 9)
-        mvwprintw(status_bar, 0, cols - 29 + active_pos, "%d", win+1);
+    if (win+1 < 10)
+        mvwprintw(status_bar, 0, cols - 31 + active_pos, "%d", win+1);
     else
-        mvwprintw(status_bar, 0, cols - 29 + active_pos, "10");
+        mvwprintw(status_bar, 0, cols - 31 + active_pos, "0");
     wattroff(status_bar, COLOUR_STATUS_NEW);
     wattroff(status_bar, A_BLINK);
 
@@ -193,20 +194,20 @@ status_bar_print_message(const char * const msg)
 
     message = (char *) malloc((strlen(msg) + 1) * sizeof(char));
     strcpy(message, msg);
-    mvwprintw(status_bar, 0, 9, message);
+    mvwprintw(status_bar, 0, 10, message);
 
     int cols = getmaxx(stdscr);
 
     wattron(status_bar, COLOUR_STATUS_BRACKET);
-    mvwprintw(status_bar, 0, cols - 29, _active);
+    mvwprintw(status_bar, 0, cols - 31, _active);
     wattroff(status_bar, COLOUR_STATUS_BRACKET);
 
     int i;
-    for(i = 0; i < 9; i++) {
+    for(i = 0; i < 10; i++) {
         if (is_new[i])
-            status_bar_new(i+1);
+            status_bar_new(i);
         else if (is_active[i])
-            status_bar_active(i+1);
+            status_bar_active(i);
     }
 
     dirty = TRUE;
@@ -221,7 +222,9 @@ status_bar_clear(void)
     }
 
     int i;
-    for (i = 0; i < 9; i++) {
+    is_active[0] = TRUE;
+    is_new[0] = FALSE;
+    for (i = 1; i < 10; i++) {
         is_active[i] = FALSE;
         is_new[i] = FALSE;
     }
@@ -231,7 +234,7 @@ status_bar_clear(void)
     int cols = getmaxx(stdscr);
 
     wattron(status_bar, COLOUR_STATUS_BRACKET);
-    mvwprintw(status_bar, 0, cols - 29, _active);
+    mvwprintw(status_bar, 0, cols - 31, _active);
     wattroff(status_bar, COLOUR_STATUS_BRACKET);
 
     dirty = TRUE;
@@ -250,15 +253,15 @@ status_bar_clear_message(void)
     int cols = getmaxx(stdscr);
 
     wattron(status_bar, COLOUR_STATUS_BRACKET);
-    mvwprintw(status_bar, 0, cols - 29, _active);
+    mvwprintw(status_bar, 0, cols - 31, _active);
     wattroff(status_bar, COLOUR_STATUS_BRACKET);
 
     int i;
-    for(i = 0; i < 9; i++) {
+    for(i = 0; i < 10; i++) {
         if (is_new[i])
-            status_bar_new(i+1);
+            status_bar_new(i);
         else if (is_active[i])
-            status_bar_active(i+1);
+            status_bar_active(i);
     }
 
     dirty = TRUE;
diff --git a/src/theme.c b/src/theme.c
new file mode 100644
index 00000000..aafbd859
--- /dev/null
+++ b/src/theme.c
@@ -0,0 +1,303 @@
+/*
+ * theme.c
+ *
+ * Copyright (C) 2012 James Booth <boothj5@gmail.com>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Profanity.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#ifdef HAVE_NCURSES_H
+#include <ncurses.h>
+#endif
+#ifdef HAVE_NCURSES_NCURSES_H
+#include <ncurses/ncurses.h>
+#endif
+
+#include "log.h"
+#include "theme.h"
+
+static GString *theme_loc;
+static GKeyFile *theme;
+
+struct colour_string_t {
+    char *str;
+    NCURSES_COLOR_T colour;
+};
+
+static int num_colours = 9;
+static struct colour_string_t colours[] = {
+    { "default", -1 },
+    { "white", COLOR_WHITE },
+    { "green", COLOR_GREEN },
+    { "red", COLOR_RED },
+    { "yellow", COLOR_YELLOW },
+    { "blue", COLOR_BLUE },
+    { "cyan", COLOR_CYAN },
+    { "black", COLOR_BLACK },
+    { "magenta", COLOR_MAGENTA },
+};
+
+// colour preferences
+static struct colours_t {
+        NCURSES_COLOR_T bkgnd;
+        NCURSES_COLOR_T titlebar;
+        NCURSES_COLOR_T statusbar;
+        NCURSES_COLOR_T titlebartext;
+        NCURSES_COLOR_T titlebarbrackets;
+        NCURSES_COLOR_T statusbartext;
+        NCURSES_COLOR_T statusbarbrackets;
+        NCURSES_COLOR_T statusbaractive;
+        NCURSES_COLOR_T statusbarnew;
+        NCURSES_COLOR_T maintext;
+        NCURSES_COLOR_T inputtext;
+        NCURSES_COLOR_T timetext;
+        NCURSES_COLOR_T splashtext;
+        NCURSES_COLOR_T online;
+        NCURSES_COLOR_T away;
+        NCURSES_COLOR_T xa;
+        NCURSES_COLOR_T dnd;
+        NCURSES_COLOR_T chat;
+        NCURSES_COLOR_T offline;
+        NCURSES_COLOR_T typing;
+        NCURSES_COLOR_T gone;
+        NCURSES_COLOR_T error;
+        NCURSES_COLOR_T incoming;
+        NCURSES_COLOR_T roominfo;
+        NCURSES_COLOR_T me;
+        NCURSES_COLOR_T them;
+} colour_prefs;
+
+static NCURSES_COLOR_T _lookup_colour(const char * const colour);
+static void _set_colour(gchar *val, NCURSES_COLOR_T *pref,
+    NCURSES_COLOR_T def);
+static void _load_colours(void);
+
+void
+theme_load(const char * const theme_name)
+{
+    log_info("Loading theme");
+    theme = g_key_file_new();
+
+    if (theme_name != NULL) {
+        theme_loc = g_string_new(getenv("HOME"));
+        g_string_append(theme_loc, "/.profanity/themes/");
+        g_string_append(theme_loc, theme_name);
+        g_key_file_load_from_file(theme, theme_loc->str, G_KEY_FILE_KEEP_COMMENTS,
+            NULL);
+    }
+
+    _load_colours();
+}
+
+gboolean
+theme_change(const char * const theme_name)
+{
+    // use default theme
+    if (strcmp(theme_name, "default") == 0) {
+        g_key_file_free(theme);
+        theme = g_key_file_new();
+        _load_colours();
+        return TRUE;
+    } else {
+        GString *new_theme_file = g_string_new(getenv("HOME"));
+        g_string_append(new_theme_file, "/.profanity/themes/");
+        g_string_append(new_theme_file, theme_name);
+
+        // no theme file found
+        if (!g_file_test(new_theme_file->str, G_FILE_TEST_EXISTS)) {
+            log_info("Theme does not exist \"%s\"", theme_name);
+            g_string_free(new_theme_file, TRUE);
+            return FALSE;
+
+        // load from theme file
+        } else {
+            g_string_free(theme_loc, TRUE);
+            theme_loc = new_theme_file;
+            log_info("Changing theme to \"%s\"", theme_name);
+            g_key_file_free(theme);
+            theme = g_key_file_new();
+            g_key_file_load_from_file(theme, theme_loc->str, G_KEY_FILE_KEEP_COMMENTS,
+                NULL);
+            _load_colours();
+            return TRUE;
+        }
+    }
+}
+
+void
+theme_close(void)
+{
+    g_key_file_free(theme);
+}
+
+void
+theme_init_colours(void)
+{
+    // main text
+    init_pair(1, colour_prefs.maintext, colour_prefs.bkgnd);
+    init_pair(2, colour_prefs.splashtext, colour_prefs.bkgnd);
+    init_pair(3, colour_prefs.error, colour_prefs.bkgnd);
+    init_pair(4, colour_prefs.incoming, colour_prefs.bkgnd);
+    init_pair(5, colour_prefs.inputtext, colour_prefs.bkgnd);
+    init_pair(6, colour_prefs.timetext, colour_prefs.bkgnd);
+
+    // title bar
+    init_pair(10, colour_prefs.titlebartext, colour_prefs.titlebar);
+    init_pair(11, colour_prefs.titlebarbrackets, colour_prefs.titlebar);
+
+    // status bar
+    init_pair(20, colour_prefs.statusbartext, colour_prefs.statusbar);
+    init_pair(21, colour_prefs.statusbarbrackets, colour_prefs.statusbar);
+    init_pair(22, colour_prefs.statusbaractive, colour_prefs.statusbar);
+    init_pair(23, colour_prefs.statusbarnew, colour_prefs.statusbar);
+
+    // chat
+    init_pair(30, colour_prefs.me, colour_prefs.bkgnd);
+    init_pair(31, colour_prefs.them, colour_prefs.bkgnd);
+
+    // room chat
+    init_pair(40, colour_prefs.roominfo, colour_prefs.bkgnd);
+
+    // statuses
+    init_pair(50, colour_prefs.online, colour_prefs.bkgnd);
+    init_pair(51, colour_prefs.offline, colour_prefs.bkgnd);
+    init_pair(52, colour_prefs.away, colour_prefs.bkgnd);
+    init_pair(53, colour_prefs.chat, colour_prefs.bkgnd);
+    init_pair(54, colour_prefs.dnd, colour_prefs.bkgnd);
+    init_pair(55, colour_prefs.xa, colour_prefs.bkgnd);
+
+    // states
+    init_pair(60, colour_prefs.typing, colour_prefs.bkgnd);
+    init_pair(61, colour_prefs.gone, colour_prefs.bkgnd);
+}
+
+static NCURSES_COLOR_T
+_lookup_colour(const char * const colour)
+{
+    int i;
+    for (i = 0; i < num_colours; i++) {
+        if (strcmp(colours[i].str, colour) == 0) {
+            return colours[i].colour;
+        }
+    }
+
+    return -99;
+}
+
+static void
+_set_colour(gchar *val, NCURSES_COLOR_T *pref,
+    NCURSES_COLOR_T def)
+{
+    if(!val) {
+        *pref = def;
+    } else {
+        NCURSES_COLOR_T col = _lookup_colour(val);
+        if (col == -99) {
+            *pref = def;
+        } else {
+            *pref = col;
+        }
+    }
+}
+
+static void
+_load_colours(void)
+{
+    gchar *bkgnd_val = g_key_file_get_string(theme, "colours", "bkgnd", NULL);
+    _set_colour(bkgnd_val, &colour_prefs.bkgnd, -1);
+
+    gchar *titlebar_val = g_key_file_get_string(theme, "colours", "titlebar", NULL);
+    _set_colour(titlebar_val, &colour_prefs.titlebar, COLOR_BLUE);
+
+    gchar *statusbar_val = g_key_file_get_string(theme, "colours", "statusbar", NULL);
+    _set_colour(statusbar_val, &colour_prefs.statusbar, COLOR_BLUE);
+
+    gchar *titlebartext_val = g_key_file_get_string(theme, "colours", "titlebartext", NULL);
+    _set_colour(titlebartext_val, &colour_prefs.titlebartext, COLOR_WHITE);
+
+    gchar *titlebarbrackets_val = g_key_file_get_string(theme, "colours", "titlebarbrackets", NULL);
+    _set_colour(titlebarbrackets_val, &colour_prefs.titlebarbrackets, COLOR_CYAN);
+
+    gchar *statusbartext_val = g_key_file_get_string(theme, "colours", "statusbartext", NULL);
+    _set_colour(statusbartext_val, &colour_prefs.statusbartext, COLOR_WHITE);
+
+    gchar *statusbarbrackets_val = g_key_file_get_string(theme, "colours", "statusbarbrackets", NULL);
+    _set_colour(statusbarbrackets_val, &colour_prefs.statusbarbrackets, COLOR_CYAN);
+
+    gchar *statusbaractive_val = g_key_file_get_string(theme, "colours", "statusbaractive", NULL);
+    _set_colour(statusbaractive_val, &colour_prefs.statusbaractive, COLOR_CYAN);
+
+    gchar *statusbarnew_val = g_key_file_get_string(theme, "colours", "statusbarnew", NULL);
+    _set_colour(statusbarnew_val, &colour_prefs.statusbarnew, COLOR_WHITE);
+
+    gchar *maintext_val = g_key_file_get_string(theme, "colours", "maintext", NULL);
+    _set_colour(maintext_val, &colour_prefs.maintext, COLOR_WHITE);
+
+    gchar *splashtext_val = g_key_file_get_string(theme, "colours", "splashtext", NULL);
+    _set_colour(splashtext_val, &colour_prefs.splashtext, COLOR_CYAN);
+
+    gchar *inputtext_val = g_key_file_get_string(theme, "colours", "inputtext", NULL);
+    _set_colour(inputtext_val, &colour_prefs.inputtext, COLOR_WHITE);
+
+    gchar *timetext_val = g_key_file_get_string(theme, "colours", "timetext", NULL);
+    _set_colour(timetext_val, &colour_prefs.timetext, COLOR_WHITE);
+
+    gchar *online_val = g_key_file_get_string(theme, "colours", "online", NULL);
+    _set_colour(online_val, &colour_prefs.online, COLOR_GREEN);
+
+    gchar *away_val = g_key_file_get_string(theme, "colours", "away", NULL);
+    _set_colour(away_val, &colour_prefs.away, COLOR_CYAN);
+
+    gchar *chat_val = g_key_file_get_string(theme, "colours", "chat", NULL);
+    _set_colour(chat_val, &colour_prefs.chat, COLOR_GREEN);
+
+    gchar *dnd_val = g_key_file_get_string(theme, "colours", "dnd", NULL);
+    _set_colour(dnd_val, &colour_prefs.dnd, COLOR_RED);
+
+    gchar *xa_val = g_key_file_get_string(theme, "colours", "xa", NULL);
+    _set_colour(xa_val, &colour_prefs.xa, COLOR_CYAN);
+
+    gchar *offline_val = g_key_file_get_string(theme, "colours", "offline", NULL);
+    _set_colour(offline_val, &colour_prefs.offline, COLOR_RED);
+
+    gchar *typing_val = g_key_file_get_string(theme, "colours", "typing", NULL);
+    _set_colour(typing_val, &colour_prefs.typing, COLOR_YELLOW);
+
+    gchar *gone_val = g_key_file_get_string(theme, "colours", "gone", NULL);
+    _set_colour(gone_val, &colour_prefs.gone, COLOR_RED);
+
+    gchar *error_val = g_key_file_get_string(theme, "colours", "error", NULL);
+    _set_colour(error_val, &colour_prefs.error, COLOR_RED);
+
+    gchar *incoming_val = g_key_file_get_string(theme, "colours", "incoming", NULL);
+    _set_colour(incoming_val, &colour_prefs.incoming, COLOR_YELLOW);
+
+    gchar *roominfo_val = g_key_file_get_string(theme, "colours", "roominfo", NULL);
+    _set_colour(roominfo_val, &colour_prefs.roominfo, COLOR_YELLOW);
+
+    gchar *me_val = g_key_file_get_string(theme, "colours", "me", NULL);
+    _set_colour(me_val, &colour_prefs.me, COLOR_YELLOW);
+
+    gchar *them_val = g_key_file_get_string(theme, "colours", "them", NULL);
+    _set_colour(them_val, &colour_prefs.them, COLOR_GREEN);
+}
diff --git a/src/theme.h b/src/theme.h
new file mode 100644
index 00000000..ca1c2245
--- /dev/null
+++ b/src/theme.h
@@ -0,0 +1,66 @@
+/*
+ * theme.h
+ *
+ * Copyright (C) 2012 James Booth <boothj5@gmail.com>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Profanity.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef THEME_H
+#define THEME_H
+
+#include "config.h"
+
+#include <glib.h>
+
+#ifdef HAVE_NCURSES_H
+#include <ncurses.h>
+#endif
+#ifdef HAVE_NCURSES_NCURSES_H
+#include <ncurses/ncurses.h>
+#endif
+
+#define COLOUR_TEXT             COLOR_PAIR(1)
+#define COLOUR_SPLASH           COLOR_PAIR(2)
+#define COLOUR_ERROR            COLOR_PAIR(3)
+#define COLOUR_INCOMING         COLOR_PAIR(4)
+#define COLOUR_INPUT_TEXT       COLOR_PAIR(5)
+#define COLOUR_TIME             COLOR_PAIR(6)
+#define COLOUR_TITLE_TEXT       COLOR_PAIR(10)
+#define COLOUR_TITLE_BRACKET    COLOR_PAIR(11)
+#define COLOUR_STATUS_TEXT      COLOR_PAIR(20)
+#define COLOUR_STATUS_BRACKET   COLOR_PAIR(21)
+#define COLOUR_STATUS_ACTIVE    COLOR_PAIR(22)
+#define COLOUR_STATUS_NEW       COLOR_PAIR(23)
+#define COLOUR_ME               COLOR_PAIR(30)
+#define COLOUR_THEM             COLOR_PAIR(31)
+#define COLOUR_ROOMINFO         COLOR_PAIR(40)
+#define COLOUR_ONLINE           COLOR_PAIR(50)
+#define COLOUR_OFFLINE          COLOR_PAIR(51)
+#define COLOUR_AWAY             COLOR_PAIR(52)
+#define COLOUR_CHAT             COLOR_PAIR(53)
+#define COLOUR_DND              COLOR_PAIR(54)
+#define COLOUR_XA               COLOR_PAIR(55)
+#define COLOUR_TYPING           COLOR_PAIR(60)
+#define COLOUR_GONE             COLOR_PAIR(61)
+
+void theme_load(const char * const theme_name);
+void theme_init_colours(void);
+gboolean theme_change(const char * const theme_name);
+void theme_close(void);
+
+#endif
diff --git a/src/title_bar.c b/src/title_bar.c
index 1ebe4f09..08c8c6db 100644
--- a/src/title_bar.c
+++ b/src/title_bar.c
@@ -24,6 +24,7 @@
 #include <string.h>
 
 #include "common.h"
+#include "theme.h"
 #include "ui.h"
 
 static WINDOW *title_bar;
diff --git a/src/ui.h b/src/ui.h
index 3e30fd1a..bab4d47c 100644
--- a/src/ui.h
+++ b/src/ui.h
@@ -37,28 +37,7 @@
 #include "jabber.h"
 
 #define INP_WIN_MAX 1000
-
-#define COLOUR_TEXT             COLOR_PAIR(1)
-#define COLOUR_SPLASH           COLOR_PAIR(2)
-#define COLOUR_ERROR            COLOR_PAIR(3)
-#define COLOUR_INCOMING         COLOR_PAIR(4)
-#define COLOUR_TITLE_TEXT       COLOR_PAIR(10)
-#define COLOUR_TITLE_BRACKET    COLOR_PAIR(11)
-#define COLOUR_STATUS_TEXT      COLOR_PAIR(20)
-#define COLOUR_STATUS_BRACKET      COLOR_PAIR(21)
-#define COLOUR_STATUS_ACTIVE    COLOR_PAIR(22)
-#define COLOUR_STATUS_NEW       COLOR_PAIR(23)
-#define COLOUR_ME               COLOR_PAIR(30)
-#define COLOUR_THEM             COLOR_PAIR(31)
-#define COLOUR_ROOMINFO         COLOR_PAIR(40)
-#define COLOUR_ONLINE           COLOR_PAIR(50)
-#define COLOUR_OFFLINE          COLOR_PAIR(51)
-#define COLOUR_AWAY             COLOR_PAIR(52)
-#define COLOUR_CHAT             COLOR_PAIR(53)
-#define COLOUR_DND              COLOR_PAIR(54)
-#define COLOUR_XA               COLOR_PAIR(55)
-#define COLOUR_TYPING           COLOR_PAIR(60)
-#define COLOUR_GONE             COLOR_PAIR(61)
+#define PAD_SIZE 1000
 
 typedef enum {
     WIN_UNUSED,
@@ -124,6 +103,7 @@ void win_bad_show(const char * const msg);
 void win_remind(void);
 void win_activity(void);
 void win_no_activity(void);
+void win_switch_if_active(const int i);
 
 void win_join_chat(const char * const room, const char * const nick);
 void win_show_room_roster(const char * const room);
@@ -147,6 +127,7 @@ void win_show_room_member_nick_change(const char * const room,
 void win_show_room_nick_change(const char * const room, const char * const nick);
 void win_show_room_member_presence(const char * const room,
     const char * const nick, const char * const show, const char * const status);
+void win_load_colours(void);
 
 // console window actions
 void cons_about(void);
@@ -186,5 +167,6 @@ void inp_non_block(void);
 void inp_block(void);
 void inp_get_password(char *passwd);
 void inp_replace_input(char *input, const char * const new_input, int *size);
+int inp_get_next_char(void);
 
 #endif
diff --git a/src/window.c b/src/window.c
new file mode 100644
index 00000000..6f162eb4
--- /dev/null
+++ b/src/window.c
@@ -0,0 +1,67 @@
+/*
+ * window.c
+ *
+ * Copyright (C) 2012 James Booth <boothj5@gmail.com>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Profanity.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#ifdef HAVE_NCURSES_H
+#include <ncurses.h>
+#endif
+#ifdef HAVE_NCURSES_NCURSES_H
+#include <ncurses/ncurses.h>
+#endif
+
+#include "theme.h"
+#include "window.h"
+
+#define CONS_WIN_TITLE "_cons"
+
+ProfWin*
+window_create(const char * const title, int cols, win_type_t type)
+{
+    ProfWin *new_win = malloc(sizeof(struct prof_win_t));
+    new_win->from = strdup(title);
+    new_win->win = newpad(PAD_SIZE, cols);
+    wbkgd(new_win->win, COLOUR_TEXT);
+    new_win->y_pos = 0;
+    new_win->paged = 0;
+    new_win->unread = 0;
+    new_win->history_shown = 0;
+    new_win->type = type;
+    scrollok(new_win->win, TRUE);
+
+    return new_win;
+}
+
+void
+window_free(ProfWin* window)
+{
+    delwin(window->win);
+    free(window->from);
+    window->from = NULL;
+    window->win = NULL;
+    free(window);
+    window = NULL;
+}
diff --git a/src/window.h b/src/window.h
new file mode 100644
index 00000000..516b73a0
--- /dev/null
+++ b/src/window.h
@@ -0,0 +1,37 @@
+/*
+ * window.h
+ *
+ * Copyright (C) 2012 James Booth <boothj5@gmail.com>
+ *
+ * This file is part of Profanity.
+ *
+ * Profanity is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Profanity is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Profanity.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "ui.h"
+
+typedef struct prof_win_t {
+    char *from;
+    WINDOW *win;
+    win_type_t type;
+    int y_pos;
+    int paged;
+    int unread;
+    int history_shown;
+} ProfWin;
+
+
+ProfWin* window_create(const char * const title, int cols, win_type_t type);
+void window_free(ProfWin *window);
diff --git a/src/windows.c b/src/windows.c
index 16165487..df3f6b2c 100644
--- a/src/windows.c
+++ b/src/windows.c
@@ -45,20 +45,20 @@
 #include "preferences.h"
 #include "release.h"
 #include "room_chat.h"
+#include "theme.h"
 #include "ui.h"
+#include "window.h"
 
 #define CONS_WIN_TITLE "_cons"
-#define PAD_SIZE 1000
 #define NUM_WINS 10
 
 // holds console at index 0 and chat wins 1 through to 9
-static struct prof_win _wins[NUM_WINS];
+static ProfWin* windows[NUM_WINS];
 
 // the window currently being displayed
-static int _curr_prof_win = 0;
-
-// shortcut pointer to console window
-static WINDOW * _cons_win = NULL;
+static int current_index = 0;
+static ProfWin *current;
+static ProfWin *console;
 
 // current window state
 static int dirty;
@@ -66,6 +66,7 @@ static int dirty;
 // max columns for main windows, never resize below
 static int max_cols = 0;
 
+static void _set_current(int index);
 static void _create_windows(void);
 static void _cons_splash_logo(void);
 static void _cons_show_basic_help(void);
@@ -73,7 +74,6 @@ static void _cons_show_contact(PContact contact);
 static int _find_prof_win_index(const char * const contact);
 static int _new_prof_win(const char * const contact, win_type_t type);
 static void _current_window_refresh(void);
-static void _win_switch_if_active(const int i);
 static void _win_show_time(WINDOW *win);
 static void _win_show_user(WINDOW *win, const char * const user, const int colour);
 static void _win_show_message(WINDOW *win, const char * const message);
@@ -105,55 +105,18 @@ gui_init(void)
 {
     log_info("Initialising UI");
     initscr();
-    cbreak();
+    raw();
     keypad(stdscr, TRUE);
     mousemask(BUTTON2_PRESSED | BUTTON4_PRESSED, NULL);
     mouseinterval(5);
 
-    if (has_colors()) {
-        use_default_colors();
-        start_color();
-
-        // main text
-        init_pair(1, prefs_get_maintext(), prefs_get_bkgnd());
-        init_pair(2, prefs_get_splashtext(), prefs_get_bkgnd());
-        init_pair(3, prefs_get_error(), prefs_get_bkgnd());
-        init_pair(4, prefs_get_incoming(), prefs_get_bkgnd());
-
-        // title bar
-        init_pair(10, prefs_get_titlebartext(), prefs_get_titlebar());
-        init_pair(11, prefs_get_titlebarbrackets(), prefs_get_titlebar());
-
-        // status bar
-        init_pair(20, prefs_get_statusbartext(), prefs_get_statusbar());
-        init_pair(21, prefs_get_statusbarbrackets(), prefs_get_statusbar());
-        init_pair(22, prefs_get_statusbaractive(), prefs_get_statusbar());
-        init_pair(23, prefs_get_statusbarnew(), prefs_get_statusbar());
-
-        // chat
-        init_pair(30, prefs_get_me(), prefs_get_bkgnd());
-        init_pair(31, prefs_get_them(), prefs_get_bkgnd());
-
-        // room chat
-        init_pair(40, prefs_get_roominfo(), prefs_get_bkgnd());
-
-        // statuses
-        init_pair(50, prefs_get_online(), prefs_get_bkgnd());
-        init_pair(51, prefs_get_offline(), prefs_get_bkgnd());
-        init_pair(52, prefs_get_away(), prefs_get_bkgnd());
-        init_pair(53, prefs_get_chat(), prefs_get_bkgnd());
-        init_pair(54, prefs_get_dnd(), prefs_get_bkgnd());
-        init_pair(55, prefs_get_xa(), prefs_get_bkgnd());
-
-        // states
-        init_pair(60, prefs_get_typing(), prefs_get_bkgnd());
-        init_pair(61, prefs_get_gone(), prefs_get_bkgnd());
-    }
+    win_load_colours();
 
     refresh();
 
     create_title_bar();
     create_status_bar();
+    status_bar_active(0);
     create_input_window();
     _create_windows();
 
@@ -161,6 +124,16 @@ gui_init(void)
 }
 
 void
+win_load_colours(void)
+{
+    if (has_colors()) {
+        use_default_colors();
+        start_color();
+        theme_init_colours();
+    }
+}
+
+void
 gui_refresh(void)
 {
     title_bar_refresh();
@@ -199,17 +172,15 @@ gui_resize(const int ch, const char * const input, const int size)
 void
 win_close_win(void)
 {
-    // reset the chat win to unused
-    strcpy(_wins[_curr_prof_win].from, "");
-    wclear(_wins[_curr_prof_win].win);
-    _wins[_curr_prof_win].history_shown = 0;
-    _wins[_curr_prof_win].type = WIN_UNUSED;
+    window_free(current);
+    windows[current_index] = NULL;
 
     // set it as inactive in the status bar
-    status_bar_inactive(_curr_prof_win);
+    status_bar_inactive(current_index);
 
     // go back to console window
-    _curr_prof_win = 0;
+    _set_current(0);
+    status_bar_active(0);
     title_bar_title();
 
     dirty = TRUE;
@@ -218,19 +189,19 @@ win_close_win(void)
 int
 win_in_chat(void)
 {
-    return (_wins[_curr_prof_win].type == WIN_CHAT);
+    return (current->type == WIN_CHAT);
 }
 
 int
 win_in_groupchat(void)
 {
-    return (_wins[_curr_prof_win].type == WIN_MUC);
+    return (current->type == WIN_MUC);
 }
 
 int
 win_in_private_chat(void)
 {
-    return (_wins[_curr_prof_win].type == WIN_PRIVATE);
+    return (current->type == WIN_PRIVATE);
 }
 
 void
@@ -239,60 +210,56 @@ win_show_wins(void)
     int i = 0;
     int count = 0;
 
+    cons_show("");
+    cons_show("Active windows:");
+    _win_show_time(console->win);
+    wprintw(console->win, "1: Console\n");
+
     for (i = 1; i < NUM_WINS; i++) {
-        if (_wins[i].type != WIN_UNUSED) {
+        if (windows[i] != NULL) {
             count++;
         }
     }
 
-    cons_show("");
-
-    if (count == 0) {
-        cons_show("No active windows.");
-    } else if (count == 1) {
-        cons_show("1 active window:");
-    } else {
-        cons_show("%d active windows:", count);
-    }
-
     if (count != 0) {
         for (i = 1; i < NUM_WINS; i++) {
-            if (_wins[i].type != WIN_UNUSED) {
-                _win_show_time(_cons_win);
+            if (windows[i] != NULL) {
+                ProfWin *window = windows[i];
+                _win_show_time(console->win);
 
-                switch (_wins[i].type)
+                switch (window->type)
                 {
                     case WIN_CHAT:
-                        wprintw(_cons_win, "%d: chat %s", i + 1, _wins[i].from);
-                        PContact contact = contact_list_get_contact(_wins[i].from);
+                        wprintw(console->win, "%d: chat %s", i + 1, window->from);
+                        PContact contact = contact_list_get_contact(window->from);
 
                         if (contact != NULL) {
                             if (p_contact_name(contact) != NULL) {
-                                wprintw(_cons_win, " (%s)", p_contact_name(contact));
+                                wprintw(console->win, " (%s)", p_contact_name(contact));
                             }
-                            wprintw(_cons_win, " - %s", p_contact_presence(contact));
+                            wprintw(console->win, " - %s", p_contact_presence(contact));
                         }
 
-                        if (_wins[i].unread > 0) {
-                            wprintw(_cons_win, ", %d unread", _wins[i].unread);
+                        if (window->unread > 0) {
+                            wprintw(console->win, ", %d unread", window->unread);
                         }
 
                         break;
 
                     case WIN_PRIVATE:
-                        wprintw(_cons_win, "%d: private %s", i + 1, _wins[i].from);
+                        wprintw(console->win, "%d: private %s", i + 1, window->from);
 
-                        if (_wins[i].unread > 0) {
-                            wprintw(_cons_win, ", %d unread", _wins[i].unread);
+                        if (window->unread > 0) {
+                            wprintw(console->win, ", %d unread", window->unread);
                         }
 
                         break;
 
                     case WIN_MUC:
-                        wprintw(_cons_win, "%d: room %s", i + 1, _wins[i].from);
+                        wprintw(console->win, "%d: room %s", i + 1, window->from);
 
-                        if (_wins[i].unread > 0) {
-                            wprintw(_cons_win, ", %d unread", _wins[i].unread);
+                        if (window->unread > 0) {
+                            wprintw(console->win, ", %d unread", window->unread);
                         }
 
                         break;
@@ -301,7 +268,7 @@ win_show_wins(void)
                         break;
                 }
 
-                wprintw(_cons_win, "\n");
+                wprintw(console->win, "\n");
             }
         }
     }
@@ -310,9 +277,8 @@ win_show_wins(void)
 char *
 win_get_recipient(void)
 {
-    struct prof_win current = _wins[_curr_prof_win];
-    char *recipient = (char *) malloc(sizeof(char) * (strlen(current.from) + 1));
-    strcpy(recipient, current.from);
+    char *recipient = (char *) malloc(sizeof(char) * (strlen(current->from) + 1));
+    strcpy(recipient, current->from);
     return recipient;
 }
 
@@ -327,7 +293,7 @@ win_show_typing(const char * const from)
             _cons_show_typing(from);
 
         // have chat window but not currently in it
-        } else if (win_index != _curr_prof_win) {
+        } else if (win_index != current_index) {
             _cons_show_typing(from);
             dirty = TRUE;
 
@@ -378,8 +344,8 @@ win_no_activity(void)
 
     // loop through regular chat windows and update states
     for (i = 1; i < NUM_WINS; i++) {
-        if (_wins[i].type == WIN_CHAT) {
-            char *recipient = _wins[i].from;
+        if ((windows[i] != NULL) && (windows[i]->type == WIN_CHAT)) {
+            char *recipient = windows[i]->from;
             chat_session_no_activity(recipient);
 
             if (chat_session_is_gone(recipient) &&
@@ -413,66 +379,106 @@ win_show_incomming_msg(const char * const from, const char * const message,
     if (win_index == NUM_WINS)
         win_index = _new_prof_win(from, win_type);
 
-    WINDOW *win = _wins[win_index].win;
-
-    // currently viewing chat window with sender
-    if (win_index == _curr_prof_win) {
+    // no spare windows left
+    if (win_index == 0) {
         if (tv_stamp == NULL) {
-            _win_show_time(win);
+            _win_show_time(console->win);
         } else {
             GDateTime *time = g_date_time_new_from_timeval_utc(tv_stamp);
             gchar *date_fmt = g_date_time_format(time, "%H:%M:%S");
-            wprintw(win, "%s - ", date_fmt);
+            wattron(console->win, COLOUR_TIME);
+            wprintw(console->win, "%s - ", date_fmt);
+            wattroff(console->win, COLOUR_TIME);
             g_date_time_unref(time);
             g_free(date_fmt);
         }
 
         if (strncmp(message, "/me ", 4) == 0) {
-            wattron(win, COLOUR_THEM);
-            wprintw(win, "*%s ", from);
-            wprintw(win, message + 4);
-            wprintw(win, "\n");
-            wattroff(win, COLOUR_THEM);
+            wattron(console->win, COLOUR_THEM);
+            wprintw(console->win, "*%s ", from);
+            wprintw(console->win, message + 4);
+            wprintw(console->win, "\n");
+            wattroff(console->win, COLOUR_THEM);
         } else {
-            _win_show_user(win, from, 1);
-            _win_show_message(win, message);
+            _win_show_user(console->win, from, 1);
+            _win_show_message(console->win, message);
         }
-        title_bar_set_typing(FALSE);
-        title_bar_draw();
-        status_bar_active(win_index);
-        dirty = TRUE;
 
-    // not currently viewing chat window with sender
-    } else {
-        status_bar_new(win_index);
-        _cons_show_incoming_message(from, win_index);
-        if (prefs_get_flash())
-            flash();
+        cons_bad_show("Windows all used, close a window to respond.");
 
-        _wins[win_index].unread++;
-        if (prefs_get_chlog() && prefs_get_history()) {
-            _win_show_history(win, win_index, from);
-        }
-
-        if (tv_stamp == NULL) {
-            _win_show_time(win);
+        if (current_index == 0) {
+           dirty = TRUE;
         } else {
-            GDateTime *time = g_date_time_new_from_timeval_utc(tv_stamp);
-            gchar *date_fmt = g_date_time_format(time, "%H:%M:%S");
-            wprintw(win, "%s - ", date_fmt);
-            g_date_time_unref(time);
-            g_free(date_fmt);
+            status_bar_new(0);
         }
 
-        if (strncmp(message, "/me ", 4) == 0) {
-            wattron(win, COLOUR_THEM);
-            wprintw(win, "*%s ", from);
-            wprintw(win, message + 4);
-            wprintw(win, "\n");
-            wattroff(win, COLOUR_THEM);
+    // window found or created
+    } else {
+        WINDOW *win = windows[win_index]->win;
+
+        // currently viewing chat window with sender
+        if (win_index == current_index) {
+            if (tv_stamp == NULL) {
+                _win_show_time(win);
+            } else {
+                GDateTime *time = g_date_time_new_from_timeval_utc(tv_stamp);
+                gchar *date_fmt = g_date_time_format(time, "%H:%M:%S");
+                wattron(win, COLOUR_TIME);
+                wprintw(win, "%s - ", date_fmt);
+                wattroff(win, COLOUR_TIME);
+                g_date_time_unref(time);
+                g_free(date_fmt);
+            }
+
+            if (strncmp(message, "/me ", 4) == 0) {
+                wattron(win, COLOUR_THEM);
+                wprintw(win, "*%s ", from);
+                wprintw(win, message + 4);
+                wprintw(win, "\n");
+                wattroff(win, COLOUR_THEM);
+            } else {
+                _win_show_user(win, from, 1);
+                _win_show_message(win, message);
+            }
+            title_bar_set_typing(FALSE);
+            title_bar_draw();
+            status_bar_active(win_index);
+            dirty = TRUE;
+
+        // not currently viewing chat window with sender
         } else {
-            _win_show_user(win, from, 1);
-            _win_show_message(win, message);
+            status_bar_new(win_index);
+            _cons_show_incoming_message(from, win_index);
+            if (prefs_get_flash())
+                flash();
+
+            windows[win_index]->unread++;
+            if (prefs_get_chlog() && prefs_get_history()) {
+                _win_show_history(win, win_index, from);
+            }
+
+            if (tv_stamp == NULL) {
+                _win_show_time(win);
+            } else {
+                GDateTime *time = g_date_time_new_from_timeval_utc(tv_stamp);
+                gchar *date_fmt = g_date_time_format(time, "%H:%M:%S");
+                wattron(win, COLOUR_TIME);
+                wprintw(win, "%s - ", date_fmt);
+                wattroff(win, COLOUR_TIME);
+                g_date_time_unref(time);
+                g_free(date_fmt);
+            }
+
+            if (strncmp(message, "/me ", 4) == 0) {
+                wattron(win, COLOUR_THEM);
+                wprintw(win, "*%s ", from);
+                wprintw(win, message + 4);
+                wprintw(win, "\n");
+                wattroff(win, COLOUR_THEM);
+            } else {
+                _win_show_user(win, from, 1);
+                _win_show_message(win, message);
+            }
         }
     }
 
@@ -496,10 +502,10 @@ win_show_error_msg(const char * const from, const char *err_msg)
     win_index = _find_prof_win_index(from);
     // chat window exists
     if (win_index < NUM_WINS) {
-        win = _wins[win_index].win;
+        win = windows[win_index]->win;
         _win_show_time(win);
         _win_show_error_msg(win, err_msg);
-        if (win_index == _curr_prof_win) {
+        if (win_index == current_index) {
             dirty = TRUE;
         }
     }
@@ -517,14 +523,14 @@ win_show_gone(const char * const from)
     win_index = _find_prof_win_index(from);
     // chat window exists
     if (win_index < NUM_WINS) {
-        win = _wins[win_index].win;
+        win = windows[win_index]->win;
         _win_show_time(win);
         wattron(win, COLOUR_GONE);
         wprintw(win, "*%s ", from);
         wprintw(win, "has left the conversation.");
         wprintw(win, "\n");
         wattroff(win, COLOUR_GONE);
-        if (win_index == _curr_prof_win) {
+        if (win_index == current_index) {
             dirty = TRUE;
         }
     }
@@ -550,13 +556,13 @@ win_show_system_msg(const char * const from, const char *message)
         status_bar_active(win_index);
         dirty = TRUE;
     }
-    win = _wins[win_index].win;
+    win = windows[win_index]->win;
 
     _win_show_time(win);
     wprintw(win, "*%s %s\n", bare_jid, message);
 
     // this is the current window
-    if (win_index == _curr_prof_win) {
+    if (win_index == current_index) {
         dirty = TRUE;
     }
 }
@@ -642,7 +648,7 @@ win_show_outgoing_msg(const char * const from, const char * const to,
             win_index = _new_prof_win(to, WIN_CHAT);
         }
 
-        win = _wins[win_index].win;
+        win = windows[win_index]->win;
 
         if (prefs_get_chlog() && prefs_get_history()) {
             _win_show_history(win, win_index, to);
@@ -658,7 +664,7 @@ win_show_outgoing_msg(const char * const from, const char * const to,
 
     // use existing window
     } else {
-        win = _wins[win_index].win;
+        win = windows[win_index]->win;
     }
 
     _win_show_time(win);
@@ -672,7 +678,7 @@ win_show_outgoing_msg(const char * const from, const char * const to,
         _win_show_user(win, from, 0);
         _win_show_message(win, message);
     }
-    _win_switch_if_active(win_index);
+    win_switch_if_active(win_index);
 }
 
 void
@@ -685,14 +691,14 @@ win_join_chat(const char * const room, const char * const nick)
         win_index = _new_prof_win(room, WIN_MUC);
     }
 
-    _win_switch_if_active(win_index);
+    win_switch_if_active(win_index);
 }
 
 void
 win_show_room_roster(const char * const room)
 {
     int win_index = _find_prof_win_index(room);
-    WINDOW *win = _wins[win_index].win;
+    WINDOW *win = windows[win_index]->win;
 
     GList *roster = room_get_roster(room);
 
@@ -718,7 +724,7 @@ win_show_room_roster(const char * const room)
         wattroff(win, COLOUR_ONLINE);
     }
 
-    if (win_index == _curr_prof_win)
+    if (win_index == current_index)
         dirty = TRUE;
 }
 
@@ -726,14 +732,14 @@ void
 win_show_room_member_offline(const char * const room, const char * const nick)
 {
     int win_index = _find_prof_win_index(room);
-    WINDOW *win = _wins[win_index].win;
+    WINDOW *win = windows[win_index]->win;
 
     _win_show_time(win);
     wattron(win, COLOUR_OFFLINE);
     wprintw(win, "-- %s has left the room.\n", nick);
     wattroff(win, COLOUR_OFFLINE);
 
-    if (win_index == _curr_prof_win)
+    if (win_index == current_index)
         dirty = TRUE;
 }
 
@@ -742,14 +748,14 @@ win_show_room_member_online(const char * const room, const char * const nick,
     const char * const show, const char * const status)
 {
     int win_index = _find_prof_win_index(room);
-    WINDOW *win = _wins[win_index].win;
+    WINDOW *win = windows[win_index]->win;
 
     _win_show_time(win);
     wattron(win, COLOUR_ONLINE);
     wprintw(win, "++ %s has joined the room.\n", nick);
     wattroff(win, COLOUR_ONLINE);
 
-    if (win_index == _curr_prof_win)
+    if (win_index == current_index)
         dirty = TRUE;
 }
 
@@ -759,11 +765,11 @@ win_show_room_member_presence(const char * const room, const char * const nick,
 {
     int win_index = _find_prof_win_index(room);
     if (win_index != NUM_WINS) {
-        WINDOW *win = _wins[win_index].win;
+        WINDOW *win = windows[win_index]->win;
         _show_status_string(win, nick, show, status, "++", "online");
     }
 
-    if (win_index == _curr_prof_win)
+    if (win_index == current_index)
         dirty = TRUE;
 }
 
@@ -772,14 +778,14 @@ win_show_room_member_nick_change(const char * const room,
     const char * const old_nick, const char * const nick)
 {
     int win_index = _find_prof_win_index(room);
-    WINDOW *win = _wins[win_index].win;
+    WINDOW *win = windows[win_index]->win;
 
     _win_show_time(win);
     wattron(win, COLOUR_THEM);
     wprintw(win, "** %s is now known as %s\n", old_nick, nick);
     wattroff(win, COLOUR_THEM);
 
-    if (win_index == _curr_prof_win)
+    if (win_index == current_index)
         dirty = TRUE;
 }
 
@@ -787,14 +793,14 @@ void
 win_show_room_nick_change(const char * const room, const char * const nick)
 {
     int win_index = _find_prof_win_index(room);
-    WINDOW *win = _wins[win_index].win;
+    WINDOW *win = windows[win_index]->win;
 
     _win_show_time(win);
     wattron(win, COLOUR_ME);
     wprintw(win, "** You are now known as %s\n", nick);
     wattroff(win, COLOUR_ME);
 
-    if (win_index == _curr_prof_win)
+    if (win_index == current_index)
         dirty = TRUE;
 }
 
@@ -803,7 +809,7 @@ win_show_room_history(const char * const room_jid, const char * const nick,
     GTimeVal tv_stamp, const char * const message)
 {
     int win_index = _find_prof_win_index(room_jid);
-    WINDOW *win = _wins[win_index].win;
+    WINDOW *win = windows[win_index]->win;
 
     GDateTime *time = g_date_time_new_from_timeval_utc(&tv_stamp);
     gchar *date_fmt = g_date_time_format(time, "%H:%M:%S");
@@ -820,7 +826,7 @@ win_show_room_history(const char * const room_jid, const char * const nick,
         _win_show_message(win, message);
     }
 
-    if (win_index == _curr_prof_win)
+    if (win_index == current_index)
         dirty = TRUE;
 }
 
@@ -829,7 +835,7 @@ win_show_room_message(const char * const room_jid, const char * const nick,
     const char * const message)
 {
     int win_index = _find_prof_win_index(room_jid);
-    WINDOW *win = _wins[win_index].win;
+    WINDOW *win = windows[win_index]->win;
 
     _win_show_time(win);
     if (strcmp(nick, room_get_nick_for_room(room_jid)) != 0) {
@@ -858,7 +864,7 @@ win_show_room_message(const char * const room_jid, const char * const nick,
     }
 
     // currently in groupchat window
-    if (win_index == _curr_prof_win) {
+    if (win_index == current_index) {
         status_bar_active(win_index);
         dirty = TRUE;
 
@@ -866,7 +872,7 @@ win_show_room_message(const char * const room_jid, const char * const nick,
     } else {
         status_bar_new(win_index);
         _cons_show_incoming_message(nick, win_index);
-        if (_curr_prof_win == 0) {
+        if (current_index == 0) {
             dirty = TRUE;
         }
 
@@ -876,7 +882,7 @@ win_show_room_message(const char * const room_jid, const char * const nick,
             }
         }
 
-        _wins[win_index].unread++;
+        windows[win_index]->unread++;
     }
 
     if (strcmp(nick, room_get_nick_for_room(room_jid)) != 0) {
@@ -895,7 +901,7 @@ void
 win_show_room_subject(const char * const room_jid, const char * const subject)
 {
     int win_index = _find_prof_win_index(room_jid);
-    WINDOW *win = _wins[win_index].win;
+    WINDOW *win = windows[win_index]->win;
 
     wattron(win, COLOUR_ROOMINFO);
     wprintw(win, "Room subject: ");
@@ -903,7 +909,7 @@ win_show_room_subject(const char * const room_jid, const char * const subject)
     wprintw(win, "%s\n", subject);
 
     // currently in groupchat window
-    if (win_index == _curr_prof_win) {
+    if (win_index == current_index) {
         status_bar_active(win_index);
         dirty = TRUE;
 
@@ -917,7 +923,7 @@ void
 win_show_room_broadcast(const char * const room_jid, const char * const message)
 {
     int win_index = _find_prof_win_index(room_jid);
-    WINDOW *win = _wins[win_index].win;
+    WINDOW *win = windows[win_index]->win;
 
     wattron(win, COLOUR_ROOMINFO);
     wprintw(win, "Room message: ");
@@ -925,7 +931,7 @@ win_show_room_broadcast(const char * const room_jid, const char * const message)
     wprintw(win, "%s\n", message);
 
     // currently in groupchat window
-    if (win_index == _curr_prof_win) {
+    if (win_index == current_index) {
         status_bar_active(win_index);
         dirty = TRUE;
 
@@ -938,7 +944,7 @@ win_show_room_broadcast(const char * const room_jid, const char * const message)
 void
 win_show(const char * const msg)
 {
-    WINDOW *win = _wins[_curr_prof_win].win;
+    WINDOW *win = current->win;
     _win_show_time(win);
     wprintw(win, "%s\n", msg);
 
@@ -948,7 +954,7 @@ win_show(const char * const msg)
 void
 win_bad_show(const char * const msg)
 {
-    WINDOW *win = _wins[_curr_prof_win].win;
+    WINDOW *win = current->win;
     _win_show_time(win);
     wattron(win, COLOUR_ERROR);
     wprintw(win, "%s\n", msg);
@@ -961,15 +967,15 @@ void
 win_contact_online(const char * const from, const char * const show,
     const char * const status)
 {
-    _show_status_string(_cons_win, from, show, status, "++", "online");
+    _show_status_string(console->win, from, show, status, "++", "online");
 
     int win_index = _find_prof_win_index(from);
     if (win_index != NUM_WINS) {
-        WINDOW *win = _wins[win_index].win;
+        WINDOW *win = windows[win_index]->win;
         _show_status_string(win, from, show, status, "++", "online");
     }
 
-    if (win_index == _curr_prof_win)
+    if (win_index == current_index)
         dirty = TRUE;
 }
 
@@ -977,15 +983,15 @@ void
 win_contact_offline(const char * const from, const char * const show,
     const char * const status)
 {
-    _show_status_string(_cons_win, from, show, status, "--", "offline");
+    _show_status_string(console->win, from, show, status, "--", "offline");
 
     int win_index = _find_prof_win_index(from);
     if (win_index != NUM_WINS) {
-        WINDOW *win = _wins[win_index].win;
+        WINDOW *win = windows[win_index]->win;
         _show_status_string(win, from, show, status, "--", "offline");
     }
 
-    if (win_index == _curr_prof_win)
+    if (win_index == current_index)
         dirty = TRUE;
 }
 
@@ -1007,15 +1013,15 @@ win_disconnected(void)
     int i;
     // show message in all active chats
     for (i = 1; i < NUM_WINS; i++) {
-        if (strcmp(_wins[i].from, "") != 0) {
-            WINDOW *win = _wins[i].win;
+        if (windows[i] != NULL) {
+            WINDOW *win = windows[i]->win;
             _win_show_time(win);
             wattron(win, COLOUR_ERROR);
             wprintw(win, "%s\n", "Lost connection.");
             wattroff(win, COLOUR_ERROR);
 
             // if current win, set dirty
-            if (i == _curr_prof_win) {
+            if (i == current_index) {
                 dirty = TRUE;
             }
         }
@@ -1039,6 +1045,8 @@ cons_prefs(void)
     else
         cons_show("Terminal flash               : OFF");
 
+    cons_show("Theme                        : %s", prefs_get_theme());
+
     if (prefs_get_intype())
         cons_show("Show typing                  : ON");
     else
@@ -1097,10 +1105,22 @@ cons_prefs(void)
 
     cons_show("Priority                     : %d", prefs_get_priority());
 
+    gint reconnect_interval = prefs_get_reconnect();
+    if (reconnect_interval == 0) {
+        cons_show("Reconnect interval           : OFF");
+    } else if (remind_period == 1) {
+        cons_show("Reconnect interval           : 1 second");
+    } else {
+        cons_show("Reconnect interval           : %d seconds", reconnect_interval);
+    }
+
     cons_show("");
 
-    if (_curr_prof_win == 0)
+    if (current_index == 0) {
         dirty = TRUE;
+    } else {
+        status_bar_new(0);
+    }
 }
 
 static void
@@ -1132,8 +1152,11 @@ cons_help(void)
     cons_show("/help [command]  - Detailed help on a specific command.");
     cons_show("");
 
-    if (_curr_prof_win == 0)
+    if (current_index == 0) {
         dirty = TRUE;
+    } else {
+        status_bar_new(0);
+    }
 }
 
 void
@@ -1143,8 +1166,11 @@ cons_basic_help(void)
     cons_show("Basic Commands:");
     _cons_show_basic_help();
 
-    if (_curr_prof_win == 0)
+    if (current_index == 0) {
         dirty = TRUE;
+    } else {
+        status_bar_new(0);
+    }
 }
 
 void
@@ -1163,8 +1189,11 @@ cons_settings_help(void)
 
     cons_show("");
 
-    if (_curr_prof_win == 0)
+    if (current_index == 0) {
         dirty = TRUE;
+    } else {
+        status_bar_new(0);
+    }
 }
 
 void
@@ -1183,8 +1212,11 @@ cons_presence_help(void)
 
     cons_show("");
 
-    if (_curr_prof_win == 0)
+    if (current_index == 0) {
         dirty = TRUE;
+    } else {
+        status_bar_new(0);
+    }
 }
 
 void
@@ -1202,8 +1234,11 @@ cons_navigation_help(void)
     cons_show("PAGE UP, PAGE DOWN       : Page the main window.");
     cons_show("");
 
-    if (_curr_prof_win == 0)
+    if (current_index == 0) {
         dirty = TRUE;
+    } else {
+        status_bar_new(0);
+    }
 }
 
 void
@@ -1227,21 +1262,24 @@ cons_bad_show(const char * const msg, ...)
     va_start(arg, msg);
     GString *fmt_msg = g_string_new(NULL);
     g_string_vprintf(fmt_msg, msg, arg);
-    _win_show_time(_cons_win);
-    wattron(_cons_win, COLOUR_ERROR);
-    wprintw(_cons_win, "%s\n", fmt_msg->str);
-    wattroff(_cons_win, COLOUR_ERROR);
+    _win_show_time(console->win);
+    wattron(console->win, COLOUR_ERROR);
+    wprintw(console->win, "%s\n", fmt_msg->str);
+    wattroff(console->win, COLOUR_ERROR);
     g_string_free(fmt_msg, TRUE);
     va_end(arg);
 
-    if (_curr_prof_win == 0)
+    if (current_index == 0) {
         dirty = TRUE;
+    } else {
+        status_bar_new(0);
+    }
 }
 
 void
 cons_show_time(void)
 {
-    _win_show_time(_cons_win);
+    _win_show_time(console->win);
 }
 
 void
@@ -1251,32 +1289,41 @@ cons_show(const char * const msg, ...)
     va_start(arg, msg);
     GString *fmt_msg = g_string_new(NULL);
     g_string_vprintf(fmt_msg, msg, arg);
-    _win_show_time(_cons_win);
-    wprintw(_cons_win, "%s\n", fmt_msg->str);
+    _win_show_time(console->win);
+    wprintw(console->win, "%s\n", fmt_msg->str);
     g_string_free(fmt_msg, TRUE);
     va_end(arg);
 
-    if (_curr_prof_win == 0)
+    if (current_index == 0) {
         dirty = TRUE;
+    } else {
+        status_bar_new(0);
+    }
 }
 
 void
 cons_show_word(const char * const word)
 {
-    wprintw(_cons_win, "%s", word);
+    wprintw(console->win, "%s", word);
 
-    if (_curr_prof_win == 0)
+    if (current_index == 0) {
         dirty = TRUE;
+    } else {
+        status_bar_new(0);
+    }
 }
 
 void
 cons_bad_command(const char * const cmd)
 {
-    _win_show_time(_cons_win);
-    wprintw(_cons_win, "Unknown command: %s\n", cmd);
+    _win_show_time(console->win);
+    wprintw(console->win, "Unknown command: %s\n", cmd);
 
-    if (_curr_prof_win == 0)
+    if (current_index == 0) {
         dirty = TRUE;
+    } else {
+        status_bar_new(0);
+    }
 }
 
 void
@@ -1290,15 +1337,17 @@ void
 win_page_off(void)
 {
     int rows = getmaxy(stdscr);
-    _wins[_curr_prof_win].paged = 0;
+    ProfWin *window = windows[current_index];
 
-    int y = getcury(_wins[_curr_prof_win].win);
+    window->paged = 0;
+
+    int y = getcury(window->win);
 
     int size = rows - 3;
 
-    _wins[_curr_prof_win].y_pos = y - (size - 1);
-    if (_wins[_curr_prof_win].y_pos < 0)
-        _wins[_curr_prof_win].y_pos = 0;
+    window->y_pos = y - (size - 1);
+    if (window->y_pos < 0)
+        window->y_pos = 0;
 
     dirty = TRUE;
 }
@@ -1308,38 +1357,10 @@ _create_windows(void)
 {
     int cols = getmaxx(stdscr);
     max_cols = cols;
-
-    // create the console window in 0
-    struct prof_win cons;
-    strcpy(cons.from, CONS_WIN_TITLE);
-    cons.win = newpad(PAD_SIZE, cols);
-    wbkgd(cons.win, COLOUR_TEXT);
-    cons.y_pos = 0;
-    cons.paged = 0;
-    cons.unread = 0;
-    cons.history_shown = 0;
-    cons.type = WIN_CONSOLE;
-    scrollok(cons.win, TRUE);
-
-    _wins[0] = cons;
-
+    windows[0] = window_create(CONS_WIN_TITLE, cols, WIN_CONSOLE);
+    console = windows[0];
+    current = console;
     cons_about();
-
-    // create the chat windows
-    int i;
-    for (i = 1; i < NUM_WINS; i++) {
-        struct prof_win chat;
-        strcpy(chat.from, "");
-        chat.win = newpad(PAD_SIZE, cols);
-        wbkgd(chat.win, COLOUR_TEXT);
-        chat.y_pos = 0;
-        chat.paged = 0;
-        chat.unread = 0;
-        chat.history_shown = 0;
-        chat.type = WIN_UNUSED;
-        scrollok(chat.win, TRUE);
-        _wins[i] = chat;
-    }
 }
 
 void
@@ -1348,44 +1369,46 @@ cons_about(void)
     int rows, cols;
     getmaxyx(stdscr, rows, cols);
 
-    _cons_win = _wins[0].win;
-
     if (prefs_get_showsplash()) {
         _cons_splash_logo();
     } else {
-        _win_show_time(_cons_win);
+        _win_show_time(console->win);
 
         if (strcmp(PACKAGE_STATUS, "development") == 0) {
-            wprintw(_cons_win, "Welcome to Profanity, version %sdev\n", PACKAGE_VERSION);
+            wprintw(console->win, "Welcome to Profanity, version %sdev\n", PACKAGE_VERSION);
         } else {
-            wprintw(_cons_win, "Welcome to Profanity, version %s\n", PACKAGE_VERSION);
+            wprintw(console->win, "Welcome to Profanity, version %s\n", PACKAGE_VERSION);
         }
     }
 
-    _win_show_time(_cons_win);
-    wprintw(_cons_win, "Copyright (C) 2012 James Booth <%s>.\n", PACKAGE_BUGREPORT);
-    _win_show_time(_cons_win);
-    wprintw(_cons_win, "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n");
-    _win_show_time(_cons_win);
-    wprintw(_cons_win, "\n");
-    _win_show_time(_cons_win);
-    wprintw(_cons_win, "This is free software; you are free to change and redistribute it.\n");
-    _win_show_time(_cons_win);
-    wprintw(_cons_win, "There is NO WARRANTY, to the extent permitted by law.\n");
-    _win_show_time(_cons_win);
-    wprintw(_cons_win, "\n");
-    _win_show_time(_cons_win);
-    wprintw(_cons_win, "Type '/help' to show complete help.\n");
-    _win_show_time(_cons_win);
-    wprintw(_cons_win, "\n");
+    _win_show_time(console->win);
+    wprintw(console->win, "Copyright (C) 2012 James Booth <%s>.\n", PACKAGE_BUGREPORT);
+    _win_show_time(console->win);
+    wprintw(console->win, "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n");
+    _win_show_time(console->win);
+    wprintw(console->win, "\n");
+    _win_show_time(console->win);
+    wprintw(console->win, "This is free software; you are free to change and redistribute it.\n");
+    _win_show_time(console->win);
+    wprintw(console->win, "There is NO WARRANTY, to the extent permitted by law.\n");
+    _win_show_time(console->win);
+    wprintw(console->win, "\n");
+    _win_show_time(console->win);
+    wprintw(console->win, "Type '/help' to show complete help.\n");
+    _win_show_time(console->win);
+    wprintw(console->win, "\n");
 
     if (prefs_get_vercheck()) {
         cons_check_version(FALSE);
     }
 
-    prefresh(_cons_win, 0, 0, 1, 0, rows-3, cols-1);
+    prefresh(console->win, 0, 0, 1, 0, rows-3, cols-1);
 
-    dirty = TRUE;
+    if (current_index == 0) {
+        dirty = TRUE;
+    } else {
+        status_bar_new(0);
+    }
 }
 
 void
@@ -1398,19 +1421,25 @@ cons_check_version(gboolean not_available_msg)
 
         if (relase_valid) {
             if (_new_release(latest_release)) {
-                _win_show_time(_cons_win);
-                wprintw(_cons_win, "A new version of Profanity is available: %s", latest_release);
-                _win_show_time(_cons_win);
-                wprintw(_cons_win, "Check <http://www.profanity.im> for details.\n");
+                _win_show_time(console->win);
+                wprintw(console->win, "A new version of Profanity is available: %s", latest_release);
+                _win_show_time(console->win);
+                wprintw(console->win, "Check <http://www.profanity.im> for details.\n");
                 free(latest_release);
-                _win_show_time(_cons_win);
-                wprintw(_cons_win, "\n");
+                _win_show_time(console->win);
+                wprintw(console->win, "\n");
             } else {
                 if (not_available_msg) {
                     cons_show("No new version available.");
                     cons_show("");
                 }
             }
+
+            if (current_index == 0) {
+                dirty = TRUE;
+            } else {
+                status_bar_new(0);
+            }
         }
     }
 }
@@ -1444,62 +1473,63 @@ _new_release(char *found_version)
 static void
 _cons_splash_logo(void)
 {
-    _win_show_time(_cons_win);
-    wprintw(_cons_win, "Welcome to\n");
-
-    _win_show_time(_cons_win);
-    wattron(_cons_win, COLOUR_SPLASH);
-    wprintw(_cons_win, "                   ___            _           \n");
-    wattroff(_cons_win, COLOUR_SPLASH);
-
-    _win_show_time(_cons_win);
-    wattron(_cons_win, COLOUR_SPLASH);
-    wprintw(_cons_win, "                  / __)          (_)_         \n");
-    wattroff(_cons_win, COLOUR_SPLASH);
-
-    _win_show_time(_cons_win);
-    wattron(_cons_win, COLOUR_SPLASH);
-    wprintw(_cons_win, " ____   ____ ___ | |__ ____ ____  _| |_ _   _ \n");
-    wattroff(_cons_win, COLOUR_SPLASH);
-
-    _win_show_time(_cons_win);
-    wattron(_cons_win, COLOUR_SPLASH);
-    wprintw(_cons_win, "|  _ \\ / ___) _ \\|  __) _  |  _ \\| |  _) | | |\n");
-    wattroff(_cons_win, COLOUR_SPLASH);
-
-    _win_show_time(_cons_win);
-    wattron(_cons_win, COLOUR_SPLASH);
-    wprintw(_cons_win, "| | | | |  | |_| | | ( ( | | | | | | |_| |_| |\n");
-    wattroff(_cons_win, COLOUR_SPLASH);
-
-    _win_show_time(_cons_win);
-    wattron(_cons_win, COLOUR_SPLASH);
-    wprintw(_cons_win, "| ||_/|_|   \\___/|_|  \\_||_|_| |_|_|\\___)__  |\n");
-    wattroff(_cons_win, COLOUR_SPLASH);
-
-    _win_show_time(_cons_win);
-    wattron(_cons_win, COLOUR_SPLASH);
-    wprintw(_cons_win, "|_|                                    (____/ \n");
-    wattroff(_cons_win, COLOUR_SPLASH);
-
-    _win_show_time(_cons_win);
-    wprintw(_cons_win, "\n");
-    _win_show_time(_cons_win);
+    _win_show_time(console->win);
+    wprintw(console->win, "Welcome to\n");
+
+    _win_show_time(console->win);
+    wattron(console->win, COLOUR_SPLASH);
+    wprintw(console->win, "                   ___            _           \n");
+    wattroff(console->win, COLOUR_SPLASH);
+
+    _win_show_time(console->win);
+    wattron(console->win, COLOUR_SPLASH);
+    wprintw(console->win, "                  / __)          (_)_         \n");
+    wattroff(console->win, COLOUR_SPLASH);
+
+    _win_show_time(console->win);
+    wattron(console->win, COLOUR_SPLASH);
+    wprintw(console->win, " ____   ____ ___ | |__ ____ ____  _| |_ _   _ \n");
+    wattroff(console->win, COLOUR_SPLASH);
+
+    _win_show_time(console->win);
+    wattron(console->win, COLOUR_SPLASH);
+    wprintw(console->win, "|  _ \\ / ___) _ \\|  __) _  |  _ \\| |  _) | | |\n");
+    wattroff(console->win, COLOUR_SPLASH);
+
+    _win_show_time(console->win);
+    wattron(console->win, COLOUR_SPLASH);
+    wprintw(console->win, "| | | | |  | |_| | | ( ( | | | | | | |_| |_| |\n");
+    wattroff(console->win, COLOUR_SPLASH);
+
+    _win_show_time(console->win);
+    wattron(console->win, COLOUR_SPLASH);
+    wprintw(console->win, "| ||_/|_|   \\___/|_|  \\_||_|_| |_|_|\\___)__  |\n");
+    wattroff(console->win, COLOUR_SPLASH);
+
+    _win_show_time(console->win);
+    wattron(console->win, COLOUR_SPLASH);
+    wprintw(console->win, "|_|                                    (____/ \n");
+    wattroff(console->win, COLOUR_SPLASH);
+
+    _win_show_time(console->win);
+    wprintw(console->win, "\n");
+    _win_show_time(console->win);
     if (strcmp(PACKAGE_STATUS, "development") == 0) {
-        wprintw(_cons_win, "Version %sdev\n", PACKAGE_VERSION);
+        wprintw(console->win, "Version %sdev\n", PACKAGE_VERSION);
     } else {
-        wprintw(_cons_win, "Version %s\n", PACKAGE_VERSION);
+        wprintw(console->win, "Version %s\n", PACKAGE_VERSION);
     }
 }
 
 static int
 _find_prof_win_index(const char * const contact)
 {
-    // find the chat window for recipient
     int i;
-    for (i = 1; i < NUM_WINS; i++)
-        if (strcmp(_wins[i].from, contact) == 0)
+    for (i = 1; i < NUM_WINS; i++) {
+        if ((windows[i] != NULL) && (strcmp(windows[i]->from, contact) == 0)) {
             break;
+        }
+    }
 
     return i;
 }
@@ -1508,33 +1538,37 @@ static int
 _new_prof_win(const char * const contact, win_type_t type)
 {
     int i;
-    // find the first unused one
-    for (i = 1; i < NUM_WINS; i++)
-        if (strcmp(_wins[i].from, "") == 0)
+    for (i = 1; i < NUM_WINS; i++) {
+        if (windows[i] == NULL) {
             break;
+        }
+    }
 
-    // set it up
-    strcpy(_wins[i].from, contact);
-    wclear(_wins[i].win);
-    _wins[i].type = type;
-
-    return i;
+    if (i != NUM_WINS) {
+        int cols = getmaxx(stdscr);
+        windows[i] = window_create(contact, cols, type);
+        return i;
+    } else {
+        return 0;
+    }
 }
 
-static void
-_win_switch_if_active(const int i)
+void
+win_switch_if_active(const int i)
 {
     win_page_off();
-    if (strcmp(_wins[i].from, "") != 0) {
-        _curr_prof_win = i;
+    if (windows[i] != NULL) {
+        current_index = i;
+        current = windows[current_index];
         win_page_off();
 
-        _wins[i].unread = 0;
+        current->unread = 0;
 
         if (i == 0) {
             title_bar_title();
+            status_bar_active(0);
         } else {
-            title_bar_set_recipient(_wins[i].from);
+            title_bar_set_recipient(current->from);
             title_bar_draw();;
             status_bar_active(i);
         }
@@ -1548,7 +1582,9 @@ _win_show_time(WINDOW *win)
 {
     GDateTime *time = g_date_time_new_now_local();
     gchar *date_fmt = g_date_time_format(time, "%H:%M:%S");
+    wattron(win, COLOUR_TIME);
     wprintw(win, "%s - ", date_fmt);
+    wattroff(win, COLOUR_TIME);
     g_date_time_unref(time);
     g_free(date_fmt);
 }
@@ -1587,8 +1623,7 @@ _current_window_refresh(void)
     int rows, cols;
     getmaxyx(stdscr, rows, cols);
 
-    WINDOW *current = _wins[_curr_prof_win].win;
-    prefresh(current, _wins[_curr_prof_win].y_pos, 0, 1, 0, rows-3, cols-1);
+    prefresh(current->win, current->y_pos, 0, 1, 0, rows-3, cols-1);
 }
 
 void
@@ -1603,12 +1638,13 @@ _win_resize_all(void)
 
         int i;
         for (i = 0; i < NUM_WINS; i++) {
-            wresize(_wins[i].win, PAD_SIZE, cols);
+            if (windows[i] != NULL) {
+                wresize(windows[i]->win, PAD_SIZE, cols);
+            }
         }
     }
 
-    WINDOW *current = _wins[_curr_prof_win].win;
-    prefresh(current, _wins[_curr_prof_win].y_pos, 0, 1, 0, rows-3, cols-1);
+    prefresh(current->win, current->y_pos, 0, 1, 0, rows-3, cols-1);
 }
 
 static void
@@ -1674,19 +1710,19 @@ _show_status_string(WINDOW *win, const char * const from,
 static void
 _cons_show_typing(const char * const short_from)
 {
-    _win_show_time(_cons_win);
-    wattron(_cons_win, COLOUR_TYPING);
-    wprintw(_cons_win, "!! %s is typing a message...\n", short_from);
-    wattroff(_cons_win, COLOUR_TYPING);
+    _win_show_time(console->win);
+    wattron(console->win, COLOUR_TYPING);
+    wprintw(console->win, "!! %s is typing a message...\n", short_from);
+    wattroff(console->win, COLOUR_TYPING);
 }
 
 static void
 _cons_show_incoming_message(const char * const short_from, const int win_index)
 {
-    _win_show_time(_cons_win);
-    wattron(_cons_win, COLOUR_INCOMING);
-    wprintw(_cons_win, "<< incoming from %s (%d)\n", short_from, win_index + 1);
-    wattroff(_cons_win, COLOUR_INCOMING);
+    _win_show_time(console->win);
+    wattron(console->win, COLOUR_INCOMING);
+    wprintw(console->win, "<< incoming from %s (%d)\n", short_from, win_index + 1);
+    wattroff(console->win, COLOUR_INCOMING);
 }
 
 static void
@@ -1697,48 +1733,48 @@ _cons_show_contact(PContact contact)
     const char *presence = p_contact_presence(contact);
     const char *status = p_contact_status(contact);
 
-    _win_show_time(_cons_win);
+    _win_show_time(console->win);
 
     if (strcmp(presence, "online") == 0) {
-        wattron(_cons_win, COLOUR_ONLINE);
+        wattron(console->win, COLOUR_ONLINE);
     } else if (strcmp(presence, "away") == 0) {
-        wattron(_cons_win, COLOUR_AWAY);
+        wattron(console->win, COLOUR_AWAY);
     } else if (strcmp(presence, "chat") == 0) {
-        wattron(_cons_win, COLOUR_CHAT);
+        wattron(console->win, COLOUR_CHAT);
     } else if (strcmp(presence, "dnd") == 0) {
-        wattron(_cons_win, COLOUR_DND);
+        wattron(console->win, COLOUR_DND);
     } else if (strcmp(presence, "xa") == 0) {
-        wattron(_cons_win, COLOUR_XA);
+        wattron(console->win, COLOUR_XA);
     } else {
-        wattron(_cons_win, COLOUR_OFFLINE);
+        wattron(console->win, COLOUR_OFFLINE);
     }
 
-    wprintw(_cons_win, "%s", jid);
+    wprintw(console->win, "%s", jid);
 
     if (name != NULL) {
-        wprintw(_cons_win, " (%s)", name);
+        wprintw(console->win, " (%s)", name);
     }
 
-    wprintw(_cons_win, " is %s", presence);
+    wprintw(console->win, " is %s", presence);
 
     if (status != NULL) {
-        wprintw(_cons_win, ", \"%s\"", p_contact_status(contact));
+        wprintw(console->win, ", \"%s\"", p_contact_status(contact));
     }
 
-    wprintw(_cons_win, "\n");
+    wprintw(console->win, "\n");
 
     if (strcmp(presence, "online") == 0) {
-        wattroff(_cons_win, COLOUR_ONLINE);
+        wattroff(console->win, COLOUR_ONLINE);
     } else if (strcmp(presence, "away") == 0) {
-        wattroff(_cons_win, COLOUR_AWAY);
+        wattroff(console->win, COLOUR_AWAY);
     } else if (strcmp(presence, "chat") == 0) {
-        wattroff(_cons_win, COLOUR_CHAT);
+        wattroff(console->win, COLOUR_CHAT);
     } else if (strcmp(presence, "dnd") == 0) {
-        wattroff(_cons_win, COLOUR_DND);
+        wattroff(console->win, COLOUR_DND);
     } else if (strcmp(presence, "xa") == 0) {
-        wattroff(_cons_win, COLOUR_XA);
+        wattroff(console->win, COLOUR_XA);
     } else {
-        wattroff(_cons_win, COLOUR_OFFLINE);
+        wattroff(console->win, COLOUR_OFFLINE);
     }
 }
 
@@ -1746,25 +1782,25 @@ static void
 _win_handle_switch(const int * const ch)
 {
     if (*ch == KEY_F(1)) {
-        _win_switch_if_active(0);
+        win_switch_if_active(0);
     } else if (*ch == KEY_F(2)) {
-        _win_switch_if_active(1);
+        win_switch_if_active(1);
     } else if (*ch == KEY_F(3)) {
-        _win_switch_if_active(2);
+        win_switch_if_active(2);
     } else if (*ch == KEY_F(4)) {
-        _win_switch_if_active(3);
+        win_switch_if_active(3);
     } else if (*ch == KEY_F(5)) {
-        _win_switch_if_active(4);
+        win_switch_if_active(4);
     } else if (*ch == KEY_F(6)) {
-        _win_switch_if_active(5);
+        win_switch_if_active(5);
     } else if (*ch == KEY_F(7)) {
-        _win_switch_if_active(6);
+        win_switch_if_active(6);
     } else if (*ch == KEY_F(8)) {
-        _win_switch_if_active(7);
+        win_switch_if_active(7);
     } else if (*ch == KEY_F(9)) {
-        _win_switch_if_active(8);
+        win_switch_if_active(8);
     } else if (*ch == KEY_F(10)) {
-        _win_switch_if_active(9);
+        win_switch_if_active(9);
     }
 }
 
@@ -1772,10 +1808,10 @@ static void
 _win_handle_page(const int * const ch)
 {
     int rows = getmaxy(stdscr);
-    int y = getcury(_wins[_curr_prof_win].win);
+    int y = getcury(current->win);
 
     int page_space = rows - 4;
-    int *page_start = &_wins[_curr_prof_win].y_pos;
+    int *page_start = &(current->y_pos);
 
     MEVENT mouse_event;
 
@@ -1814,7 +1850,7 @@ _win_handle_page(const int * const ch)
         if (*page_start < 0)
             *page_start = 0;
 
-        _wins[_curr_prof_win].paged = 1;
+        current->paged = 1;
         dirty = TRUE;
 
     // page down
@@ -1829,7 +1865,7 @@ _win_handle_page(const int * const ch)
         else if (*page_start >= y)
             *page_start = y - page_space;
 
-        _wins[_curr_prof_win].paged = 1;
+        current->paged = 1;
         dirty = TRUE;
     }
 }
@@ -1840,7 +1876,9 @@ _win_get_unread(void)
     int i;
     gint result = 0;
     for (i = 0; i < NUM_WINS; i++) {
-        result += _wins[i].unread;
+        if (windows[i] != NULL) {
+            result += windows[i]->unread;
+        }
     }
     return result;
 }
@@ -1848,16 +1886,23 @@ _win_get_unread(void)
 static void
 _win_show_history(WINDOW *win, int win_index, const char * const contact)
 {
-    if (!_wins[win_index].history_shown) {
+    if (!windows[win_index]->history_shown) {
         GSList *history = NULL;
         history = chat_log_get_previous(jabber_get_jid(), contact, history);
         while (history != NULL) {
             wprintw(win, "%s\n", history->data);
             history = g_slist_next(history);
         }
-        _wins[win_index].history_shown = 1;
+        windows[win_index]->history_shown = 1;
 
         g_slist_free_full(history, free);
     }
 }
 
+void
+_set_current(int index)
+{
+    current_index = index;
+    current = windows[current_index];
+}
+
diff --git a/themes/aqua b/themes/aqua
new file mode 100644
index 00000000..a96ae9e8
--- /dev/null
+++ b/themes/aqua
@@ -0,0 +1,27 @@
+[colours]
+bkgnd=default
+titlebar=blue
+statusbar=blue
+titlebartext=white
+titlebarbrackets=white
+statusbartext=white
+statusbarbrackets=white
+statusbaractive=cyan
+statusbarnew=white
+maintext=blue
+inputtext=white
+timetext=cyan
+splashtext=white
+online=white
+away=cyan
+chat=white
+dnd=blue
+xa=cyan
+offline=blue
+typing=white
+gone=blue
+error=white
+incoming=white
+roominfo=white
+me=white
+them=cyan
diff --git a/themes/hacker b/themes/hacker
new file mode 100644
index 00000000..fc38c529
--- /dev/null
+++ b/themes/hacker
@@ -0,0 +1,27 @@
+[colours]
+bkgnd=default
+titlebar=green
+statusbar=green
+titlebartext=black
+titlebarbrackets=black
+statusbartext=black
+statusbarbrackets=black
+statusbaractive=black
+statusbarnew=black
+maintext=green
+inputtext=green
+timetext=green
+splashtext=green
+online=green
+away=green
+chat=green
+dnd=green
+xa=green
+offline=green
+typing=green
+gone=green
+error=red
+incoming=green
+roominfo=green
+me=green
+them=green
diff --git a/themes/headache b/themes/headache
new file mode 100644
index 00000000..350b589a
--- /dev/null
+++ b/themes/headache
@@ -0,0 +1,27 @@
+[colours]
+bkgnd=default
+titlebar=magenta
+statusbar=default
+titlebartext=white
+titlebarbrackets=white
+statusbartext=white
+statusbarbrackets=red
+statusbaractive=cyan
+statusbarnew=white
+maintext=blue
+inputtext=yellow
+timetext=green
+splashtext=red
+online=red
+away=cyan
+chat=green
+dnd=megenta
+xa=cyan
+offline=green
+typing=magenta
+gone=yellow
+error=red
+incoming=yellow
+roominfo=white
+me=white
+them=white
diff --git a/themes/original b/themes/original
new file mode 100644
index 00000000..7c401040
--- /dev/null
+++ b/themes/original
@@ -0,0 +1,27 @@
+[colours]
+bkgnd=default
+titlebar=blue
+statusbar=blue
+titlebartext=white
+titlebarbrackets=cyan
+statusbartext=white
+statusbarbrackets=cyan
+statusbaractive=cyan
+statusbarnew=white
+maintext=white
+inputtext=white
+timetext=white
+splashtext=cyan
+online=green
+away=cyan
+chat=green
+dnd=red
+xa=cyan
+offline=red
+typing=yellow
+gone=red
+error=red
+incoming=yellow
+roominfo=yellow
+me=yellow
+them=green
diff --git a/themes/redsplash b/themes/redsplash
new file mode 100644
index 00000000..6c6a5999
--- /dev/null
+++ b/themes/redsplash
@@ -0,0 +1,2 @@
+[colours]
+splashtext=red
diff --git a/themes/whiteness b/themes/whiteness
new file mode 100644
index 00000000..0795cdf6
--- /dev/null
+++ b/themes/whiteness
@@ -0,0 +1,27 @@
+[colours]
+bkgnd=white
+titlebar=blue
+statusbar=blue
+titlebartext=white
+titlebarbrackets=white
+statusbartext=white
+statusbarbrackets=white
+statusbaractive=megenta
+statusbarnew=red
+maintext=black
+inputtext=black
+timetext=black
+splashtext=black
+online=green
+away=cyan
+chat=green
+dnd=red
+xa=cyan
+offline=red
+typing=yellow
+gone=red
+error=red
+incoming=yellow
+roominfo=yellow
+me=yellow
+them=green
diff --git a/themes/yellowsplash b/themes/yellowsplash
new file mode 100644
index 00000000..46510a06
--- /dev/null
+++ b/themes/yellowsplash
@@ -0,0 +1,2 @@
+[colours]
+splashtext=yellow