about summary refs log tree commit diff stats
path: root/src/ui
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/buffer.c2
-rw-r--r--src/ui/buffer.h2
-rw-r--r--src/ui/chatwin.c16
-rw-r--r--src/ui/console.c80
-rw-r--r--src/ui/core.c49
-rw-r--r--src/ui/inputwin.c28
-rw-r--r--src/ui/inputwin.h2
-rw-r--r--src/ui/mucconfwin.c2
-rw-r--r--src/ui/mucwin.c21
-rw-r--r--src/ui/notifier.c2
-rw-r--r--src/ui/occupantswin.c2
-rw-r--r--src/ui/privwin.c6
-rw-r--r--src/ui/rosterwin.c11
-rw-r--r--src/ui/screen.c2
-rw-r--r--src/ui/screen.h2
-rw-r--r--src/ui/statusbar.c714
-rw-r--r--src/ui/statusbar.h15
-rw-r--r--src/ui/titlebar.c2
-rw-r--r--src/ui/titlebar.h2
-rw-r--r--src/ui/tray.c2
-rw-r--r--src/ui/tray.h2
-rw-r--r--src/ui/ui.h10
-rw-r--r--src/ui/win_types.h4
-rw-r--r--src/ui/window.c56
-rw-r--r--src/ui/window.h2
-rw-r--r--src/ui/window_list.c43
-rw-r--r--src/ui/window_list.h4
-rw-r--r--src/ui/xmlwin.c2
28 files changed, 650 insertions, 435 deletions
diff --git a/src/ui/buffer.c b/src/ui/buffer.c
index 102ba4b2..2d29ca65 100644
--- a/src/ui/buffer.c
+++ b/src/ui/buffer.c
@@ -1,7 +1,7 @@
 /*
  * buffer.c
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
diff --git a/src/ui/buffer.h b/src/ui/buffer.h
index 8a14dd51..b16bcb51 100644
--- a/src/ui/buffer.h
+++ b/src/ui/buffer.h
@@ -1,7 +1,7 @@
 /*
  * buffer.h
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
diff --git a/src/ui/chatwin.c b/src/ui/chatwin.c
index 3ca44dd3..31604db4 100644
--- a/src/ui/chatwin.c
+++ b/src/ui/chatwin.c
@@ -1,7 +1,7 @@
 /*
  * chatwin.c
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -105,7 +105,7 @@ chatwin_otr_secured(ProfChatWin *chatwin, gboolean trusted)
          title_bar_switch();
     } else {
         int num = wins_get_num(window);
-        status_bar_new(num);
+        status_bar_new(num, WIN_CHAT, chatwin->barejid);
 
         int ui_index = num;
         if (ui_index == 10) {
@@ -249,11 +249,11 @@ chatwin_incoming_msg(ProfChatWin *chatwin, const char *const resource, const cha
     if (wins_is_current(window)) {
         win_print_incoming(window, timestamp, display_name, plugin_message, enc_mode);
         title_bar_set_typing(FALSE);
-        status_bar_active(num);
+        status_bar_active(num, WIN_CHAT, chatwin->barejid);
 
     // not currently viewing chat window with sender
     } else {
-        status_bar_new(num);
+        status_bar_new(num, WIN_CHAT, chatwin->barejid);
         cons_show_incoming_message(display_name, num, chatwin->unread);
 
         if (prefs_get_boolean(PREF_FLASH)) {
@@ -324,9 +324,11 @@ chatwin_outgoing_carbon(ProfChatWin *chatwin, const char *const message, prof_en
         enc_char = prefs_get_pgp_char();
     }
 
-    win_print_outgoing((ProfWin*)chatwin, enc_char, "%s", message);
-    int num = wins_get_num((ProfWin*)chatwin);
-    status_bar_active(num);
+    ProfWin *window = (ProfWin*)chatwin;
+
+    win_print_outgoing(window, enc_char, "%s", message);
+    int num = wins_get_num(window);
+    status_bar_active(num, WIN_CHAT, chatwin->barejid);
 }
 
 void
diff --git a/src/ui/console.c b/src/ui/console.c
index cb5a10b0..9bead705 100644
--- a/src/ui/console.c
+++ b/src/ui/console.c
@@ -1,7 +1,7 @@
 /*
  * console.c
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -412,7 +412,7 @@ cons_about(void)
         }
     }
 
-    win_println(console, THEME_DEFAULT, '-', "Copyright (C) 2012 - 2017 James Booth <%s>.", PACKAGE_BUGREPORT);
+    win_println(console, THEME_DEFAULT, '-', "Copyright (C) 2012 - 2018 James Booth <%s>.", PACKAGE_BUGREPORT);
     win_println(console, THEME_DEFAULT, '-', "License GPLv3+: GNU GPL version 3 or later <https://www.gnu.org/licenses/gpl.html>");
     win_println(console, THEME_DEFAULT, '-', "");
     win_println(console, THEME_DEFAULT, '-', "This is free software; you are free to change and redistribute it.");
@@ -684,8 +684,9 @@ cons_show_bookmarks(const GList *list)
             Bookmark *item = list->data;
 
             theme_item_t presence_colour = THEME_TEXT;
+            ProfWin *roomwin = (ProfWin*)wins_get_muc(item->barejid);
 
-            if (muc_active(item->barejid)) {
+            if (muc_active(item->barejid) && roomwin) {
                 presence_colour = THEME_ONLINE;
             }
             win_print(console, presence_colour, '-', "  %s", item->barejid);
@@ -698,12 +699,9 @@ cons_show_bookmarks(const GList *list)
             if (item->password) {
                 win_append(console, presence_colour, " (private)");
             }
-            if (muc_active(item->barejid)) {
-                ProfWin *roomwin = (ProfWin*)wins_get_muc(item->barejid);
-                if (roomwin) {
-                    int num = wins_get_num(roomwin);
-                    win_append(console, presence_colour, " (win %d)", num);
-                }
+            if (muc_active(item->barejid) && roomwin) {
+                int num = wins_get_num(roomwin);
+                win_append(console, presence_colour, " (win %d)", num);
             }
             win_newline(console);
             list = g_list_next(list);
@@ -1126,15 +1124,6 @@ cons_wrap_setting(void)
 }
 
 void
-cons_winstidy_setting(void)
-{
-    if (prefs_get_boolean(PREF_WINS_AUTO_TIDY))
-        cons_show("Window Auto Tidy (/wins)            : ON");
-    else
-        cons_show("Window Auto Tidy (/wins)            : OFF");
-}
-
-void
 cons_encwarn_setting(void)
 {
     if (prefs_get_boolean(PREF_ENC_WARN)) {
@@ -1248,6 +1237,16 @@ cons_occupants_setting(void)
 }
 
 void
+cons_rooms_cache_setting(void)
+{
+    if (prefs_get_boolean(PREF_ROOM_LIST_CACHE)) {
+        cons_show("Room list cache (/rooms cache)  : ON");
+    } else {
+        cons_show("Room list cache (/rooms cache)  : OFF");
+    }
+}
+
+void
 cons_autoconnect_setting(void)
 {
     char *pref_connect_account = prefs_get_string(PREF_CONNECT_ACCOUNT);
@@ -1523,7 +1522,6 @@ cons_show_ui_prefs(void)
     cons_splash_setting();
     cons_winpos_setting();
     cons_wrap_setting();
-    cons_winstidy_setting();
     cons_time_setting();
     cons_resource_setting();
     cons_vercheck_setting();
@@ -1536,6 +1534,7 @@ cons_show_ui_prefs(void)
     cons_presence_setting();
     cons_inpblock_setting();
     cons_tlsshow_setting();
+    cons_statusbar_setting();
 
     cons_alert();
 }
@@ -1740,6 +1739,46 @@ cons_inpblock_setting(void)
 }
 
 void
+cons_statusbar_setting(void)
+{
+    if (prefs_get_boolean(PREF_STATUSBAR_SHOW_NAME)) {
+        cons_show("Show tab names (/statusbar)         : ON");
+    } else {
+        cons_show("Show tab names (/statusbar)         : OFF");
+    }
+    if (prefs_get_boolean(PREF_STATUSBAR_SHOW_NUMBER)) {
+        cons_show("Show tab numbers (/statusbar)       : ON");
+    } else {
+        cons_show("Show tab numbers (/statusbar)       : OFF");
+    }
+
+    cons_show("Max tabs (/statusbar)               : %d", prefs_get_statusbartabs());
+
+    gint pref_len = prefs_get_statusbartablen();
+    if (pref_len == 0) {
+        cons_show("Max tab length (/statusbar)         : OFF");
+    } else {
+        cons_show("Max tab length (/statusbar)         : %d", pref_len);
+    }
+
+    char *pref_self = prefs_get_string(PREF_STATUSBAR_SELF);
+    if (g_strcmp0(pref_self, "off") == 0) {
+        cons_show("Self statusbar display (/statusbar) : OFF");
+    } else {
+        cons_show("Self statusbar display (/statusbar) : %s", pref_self);
+    }
+    prefs_free_string(pref_self);
+
+    char *pref_chat = prefs_get_string(PREF_STATUSBAR_CHAT);
+    cons_show("Chat tab display (/statusbar)       : %s", pref_chat);
+    prefs_free_string(pref_chat);
+
+    char *pref_room = prefs_get_string(PREF_STATUSBAR_ROOM);
+    cons_show("Room tab display (/statusbar)       : %s", pref_room);
+    prefs_free_string(pref_room);
+}
+
+void
 cons_winpos_setting(void)
 {
     ProfWinPlacement *placement = prefs_get_win_placement();
@@ -1906,6 +1945,7 @@ cons_show_connection_prefs(void)
     cons_reconnect_setting();
     cons_autoping_setting();
     cons_autoconnect_setting();
+    cons_rooms_cache_setting();
 
     cons_alert();
 }
@@ -2152,7 +2192,7 @@ cons_alert(void)
 {
     ProfWin *current = wins_get_current();
     if (current->type != WIN_CONSOLE) {
-        status_bar_new(1);
+        status_bar_new(1, WIN_CONSOLE, "console");
     }
 }
 
diff --git a/src/ui/core.c b/src/ui/core.c
index 539d78fd..5246d06a 100644
--- a/src/ui/core.c
+++ b/src/ui/core.c
@@ -1,7 +1,7 @@
 /*
  * core.c
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -102,8 +102,8 @@ ui_init(void)
     ui_load_colours();
     refresh();
     create_title_bar();
-    create_status_bar();
-    status_bar_active(1);
+    status_bar_init();
+    status_bar_active(1, WIN_CONSOLE, "console");
     create_input_window();
     wins_init();
     notifier_initialise();
@@ -137,7 +137,7 @@ ui_update(void)
         _ui_draw_term_title();
     }
     title_bar_update_virtual();
-    status_bar_update_virtual();
+    status_bar_draw();
     inp_put_back();
     doupdate();
 
@@ -183,6 +183,7 @@ ui_close(void)
     notifier_uninit();
     wins_destroy();
     inp_close();
+    status_bar_close();
     endwin();
 }
 
@@ -286,7 +287,7 @@ ui_contact_typing(const char *const barejid, const char *const resource)
             title_bar_set_typing(TRUE);
 
             int num = wins_get_num(window);
-            status_bar_active(num);
+            status_bar_active(num, WIN_CHAT, chatwin->barejid);
        }
     }
 
@@ -387,8 +388,7 @@ ui_handle_login_account_success(ProfAccount *account, gboolean secured)
     title_bar_set_connected(TRUE);
     title_bar_set_tls(secured);
 
-    status_bar_print_message(connection_get_fulljid());
-    status_bar_update_virtual();
+    status_bar_set_fulljid(connection_get_fulljid());
 }
 
 void
@@ -481,8 +481,7 @@ ui_disconnected(void)
     title_bar_set_connected(FALSE);
     title_bar_set_tls(FALSE);
     title_bar_set_presence(CONTACT_OFFLINE);
-    status_bar_clear_message();
-    status_bar_update_virtual();
+    status_bar_clear_fulljid();
     ui_hide_roster();
 }
 
@@ -672,7 +671,10 @@ ui_focus_win(ProfWin *window)
         title_bar_switch();
     }
     status_bar_current(i);
-    status_bar_active(i);
+
+    char *identifier = win_get_tab_identifier(window);
+    status_bar_active(i, window->type, identifier);
+    free(identifier);
 }
 
 void
@@ -689,7 +691,7 @@ ui_close_win(int index)
     wins_close_by_num(index);
     title_bar_console();
     status_bar_current(1);
-    status_bar_active(1);
+    status_bar_active(1, WIN_CONSOLE, "console");
 }
 
 void
@@ -737,17 +739,19 @@ ui_print_system_msg_from_recipient(const char *const barejid, const char *messag
     if (barejid == NULL || message == NULL)
         return;
 
-    ProfWin *window = (ProfWin*)wins_get_chat(barejid);
+    ProfChatWin *chatwin = wins_get_chat(barejid);
+    ProfWin *window = (ProfWin*)chatwin;
     if (window == NULL) {
         int num = 0;
         window = wins_new_chat(barejid);
         if (window) {
+            chatwin = (ProfChatWin*)window;
             num = wins_get_num(window);
-            status_bar_active(num);
+            status_bar_active(num, WIN_CHAT, chatwin->barejid);
         } else {
             num = 0;
             window = wins_get_console();
-            status_bar_active(1);
+            status_bar_active(1, WIN_CONSOLE, "console");
         }
     }
 
@@ -757,10 +761,11 @@ ui_print_system_msg_from_recipient(const char *const barejid, const char *messag
 void
 ui_room_join(const char *const roomjid, gboolean focus)
 {
-    ProfWin *window = (ProfWin*)wins_get_muc(roomjid);
-    if (!window) {
-        window = wins_new_muc(roomjid);
+    ProfMucWin *mucwin = wins_get_muc(roomjid);
+    if (mucwin == NULL) {
+        mucwin = (ProfMucWin*)wins_new_muc(roomjid);
     }
+    ProfWin *window = (ProfWin*)mucwin;
 
     char *nick = muc_nick(roomjid);
     win_print(window, THEME_ROOMINFO, '!', "-> You have joined the room as %s", nick);
@@ -780,7 +785,7 @@ ui_room_join(const char *const roomjid, gboolean focus)
         ui_focus_win(window);
     } else {
         int num = wins_get_num(window);
-        status_bar_active(num);
+        status_bar_active(num, WIN_MUC, mucwin->roomjid);
         ProfWin *console = wins_get_console();
         char *nick = muc_nick(roomjid);
         win_println(console, THEME_TYPING, '!', "-> Autojoined %s as %s (%d).", roomjid, nick, num);
@@ -968,15 +973,14 @@ ui_win_unread(int index)
 char*
 ui_ask_password(void)
 {
-    status_bar_get_password();
-    status_bar_update_virtual();
+    status_bar_set_prompt("Enter password:");
     return inp_get_password();
 }
 
 char*
 ui_get_line(void)
 {
-    status_bar_update_virtual();
+    status_bar_draw();
     return inp_get_line();
 }
 
@@ -999,8 +1003,7 @@ ui_ask_pgp_passphrase(const char *hint, int prev_fail)
 
     ui_update();
 
-    status_bar_get_password();
-    status_bar_update_virtual();
+    status_bar_set_prompt("Enter password:");
     return inp_get_password();
 }
 
diff --git a/src/ui/inputwin.c b/src/ui/inputwin.c
index e4da800b..654a4602 100644
--- a/src/ui/inputwin.c
+++ b/src/ui/inputwin.c
@@ -1,7 +1,7 @@
 /*
  * inputwin.c
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -87,7 +87,7 @@ static char *inp_line = NULL;
 static gboolean get_password = FALSE;
 
 static void _inp_win_update_virtual(void);
-static int _inp_printable(const wint_t ch);
+static int _inp_edited(const wint_t ch);
 static void _inp_win_handle_scroll(void);
 static int _inp_offset_to_col(char *str, int offset);
 static void _inp_write(char *line, int offset);
@@ -251,7 +251,7 @@ inp_get_line(void)
         line = inp_readline();
         ui_update();
     }
-    status_bar_clear();
+    status_bar_clear_prompt();
     return line;
 }
 
@@ -269,7 +269,7 @@ inp_get_password(void)
         ui_update();
     }
     get_password = FALSE;
-    status_bar_clear();
+    status_bar_clear_prompt();
     return password;
 }
 
@@ -301,8 +301,24 @@ _inp_write(char *line, int offset)
 }
 
 static int
-_inp_printable(const wint_t ch)
+_inp_edited(const wint_t ch)
 {
+    // backspace
+    if (ch == 127) {
+        return 1;
+    }
+
+    // ctrl-w
+    if (ch == 23) {
+        return 1;
+    }
+
+    // enter
+    if (ch == 13) {
+        return 1;
+    }
+
+    // printable
     char bytes[MB_CUR_MAX+1];
     size_t utf_len = wcrtomb(bytes, ch, (mbstate_t*)NULL);
     bytes[utf_len] = '\0';
@@ -472,7 +488,7 @@ _inp_rl_getc(FILE *stream)
 
     shift_tab = FALSE;
 
-    if (_inp_printable(ch)) {
+    if (_inp_edited(ch)) {
         ProfWin *window = wins_get_current();
         cmd_ac_reset(window);
     }
diff --git a/src/ui/inputwin.h b/src/ui/inputwin.h
index 000c13c6..db36cf70 100644
--- a/src/ui/inputwin.h
+++ b/src/ui/inputwin.h
@@ -1,7 +1,7 @@
 /*
  * inputwin.c
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
diff --git a/src/ui/mucconfwin.c b/src/ui/mucconfwin.c
index d181c769..7a658a1e 100644
--- a/src/ui/mucconfwin.c
+++ b/src/ui/mucconfwin.c
@@ -1,7 +1,7 @@
 /*
  * mucconfwin.c
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
diff --git a/src/ui/mucwin.c b/src/ui/mucwin.c
index cb929b4a..f64a401e 100644
--- a/src/ui/mucwin.c
+++ b/src/ui/mucwin.c
@@ -1,7 +1,7 @@
 /*
  * mucwin.c
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -519,11 +519,11 @@ mucwin_requires_config(ProfMucWin *mucwin)
 
     // currently in groupchat window
     if (wins_is_current(window)) {
-        status_bar_active(num);
+        status_bar_active(num, WIN_MUC, mucwin->roomjid);
 
     // not currently on groupchat window
     } else {
-        status_bar_new(num);
+        status_bar_new(num, WIN_MUC, mucwin->roomjid);
     }
 }
 
@@ -533,8 +533,6 @@ mucwin_subject(ProfMucWin *mucwin, const char *const nick, const char *const sub
     assert(mucwin != NULL);
 
     ProfWin *window = (ProfWin*)mucwin;
-    int num = wins_get_num(window);
-
     if (subject) {
         if (nick) {
             win_print(window, THEME_ROOMINFO, '!', "*%s has set the room subject: ", nick);
@@ -550,15 +548,6 @@ mucwin_subject(ProfMucWin *mucwin, const char *const nick, const char *const sub
             win_println(window, THEME_ROOMINFO, '!', "Room subject cleared");
         }
     }
-
-    // currently in groupchat window
-    if (wins_is_current(window)) {
-        status_bar_active(num);
-
-    // not currently on groupchat window
-    } else {
-        status_bar_active(num);
-    }
 }
 
 void
@@ -583,11 +572,11 @@ mucwin_broadcast(ProfMucWin *mucwin, const char *const message)
 
     // currently in groupchat window
     if (wins_is_current(window)) {
-        status_bar_active(num);
+        status_bar_active(num, WIN_MUC, mucwin->roomjid);
 
     // not currently on groupchat window
     } else {
-        status_bar_new(num);
+        status_bar_new(num, WIN_MUC, mucwin->roomjid);
     }
 }
 
diff --git a/src/ui/notifier.c b/src/ui/notifier.c
index 7cf9ad02..452ae026 100644
--- a/src/ui/notifier.c
+++ b/src/ui/notifier.c
@@ -1,7 +1,7 @@
 /*
  * notifier.c
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
diff --git a/src/ui/occupantswin.c b/src/ui/occupantswin.c
index 213c1f85..f6bb2197 100644
--- a/src/ui/occupantswin.c
+++ b/src/ui/occupantswin.c
@@ -1,7 +1,7 @@
 /*
  * occupantswin.c
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
diff --git a/src/ui/privwin.c b/src/ui/privwin.c
index 20f4b1cb..8de59683 100644
--- a/src/ui/privwin.c
+++ b/src/ui/privwin.c
@@ -1,7 +1,7 @@
 /*
  * privwin.c
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -62,11 +62,11 @@ privwin_incoming_msg(ProfPrivateWin *privatewin, const char *const message, GDat
     if (wins_is_current(window)) {
         win_print_incoming(window, timestamp, jidp->resourcepart, message, PROF_MSG_PLAIN);
         title_bar_set_typing(FALSE);
-        status_bar_active(num);
+        status_bar_active(num, WIN_PRIVATE, privatewin->fulljid);
 
     // not currently viewing chat window with sender
     } else {
-        status_bar_new(num);
+        status_bar_new(num, WIN_PRIVATE, privatewin->fulljid);
         cons_show_incoming_private_message(jidp->resourcepart, jidp->barejid, num, privatewin->unread);
         win_print_incoming(window, timestamp, jidp->resourcepart, message, PROF_MSG_PLAIN);
 
diff --git a/src/ui/rosterwin.c b/src/ui/rosterwin.c
index bbeaba21..3a38109d 100644
--- a/src/ui/rosterwin.c
+++ b/src/ui/rosterwin.c
@@ -1,7 +1,7 @@
 /*
  * rosterwin.c
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -762,7 +762,14 @@ _rosterwin_room(ProfLayoutSplit *layout, ProfMucWin *mucwin)
         g_string_append(msg, jidp->localpart);
         jid_destroy(jidp);
     } else {
-        g_string_append(msg, mucwin->roomjid);
+        gboolean show_server = prefs_get_boolean(PREF_ROSTER_ROOMS_SERVER);
+        if (show_server) {
+            g_string_append(msg, mucwin->roomjid);
+        } else {
+            Jid *jidp = jid_create(mucwin->roomjid);
+            g_string_append(msg, jidp->localpart);
+            jid_destroy(jidp);
+        }
     }
     prefs_free_string(roombypref);
     if ((g_strcmp0(unreadpos, "after") == 0) && mucwin->unread > 0) {
diff --git a/src/ui/screen.c b/src/ui/screen.c
index 519f3b1e..22548a75 100644
--- a/src/ui/screen.c
+++ b/src/ui/screen.c
@@ -1,7 +1,7 @@
 /*
  * screen.c
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
diff --git a/src/ui/screen.h b/src/ui/screen.h
index 0ff94fd5..e382ab3e 100644
--- a/src/ui/screen.h
+++ b/src/ui/screen.h
@@ -1,7 +1,7 @@
 /*
  * screen.h
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
diff --git a/src/ui/statusbar.c b/src/ui/statusbar.c
index 7998d7b4..ac1d7498 100644
--- a/src/ui/statusbar.c
+++ b/src/ui/statusbar.c
@@ -1,7 +1,7 @@
 /*
  * statusbar.c
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -50,151 +50,111 @@
 #include "ui/statusbar.h"
 #include "ui/inputwin.h"
 #include "ui/screen.h"
+#include "xmpp/roster_list.h"
+#include "xmpp/contact.h"
+
+typedef struct _status_bar_tab_t {
+    win_type_t window_type;
+    char *identifier;
+    gboolean highlight;
+} StatusBarTab;
+
+typedef struct _status_bar_t {
+    gchar *time;
+    char *prompt;
+    char *fulljid;
+    GHashTable *tabs;
+    int current_tab;
+} StatusBar;
 
-#define TIME_CHECK 60000000
-
-static WINDOW *status_bar;
-static char *message = NULL;
-//                          1  2  3  4  5  6  7  8  9  0  >
-static char _active[34] = "[ ][ ][ ][ ][ ][ ][ ][ ][ ][ ][ ]";
-static char *bracket = "- -";
-static int is_active[12];
-static GHashTable *remaining_active;
-static int is_new[12];
-static GHashTable *remaining_new;
 static GTimeZone *tz;
-static GDateTime *last_time;
-static int current;
-
-static void _update_win_statuses(void);
-static void _mark_new(int num);
-static void _mark_active(int num);
-static void _mark_inactive(int num);
-static void _status_bar_draw(void);
+static StatusBar *statusbar;
+static WINDOW *statusbar_win;
+
+static int _status_bar_draw_time(int pos);
+static void _status_bar_draw_maintext(int pos);
+static int _status_bar_draw_bracket(gboolean current, int pos, char* ch);
+static int _status_bar_draw_extended_tabs(int pos);
+static int _status_bar_draw_tab(StatusBarTab *tab, int pos, int num);
+static void _destroy_tab(StatusBarTab *tab);
+static int _tabs_width(void);
+static char* _display_name(StatusBarTab *tab);
+static gboolean _extended_new(void);
 
 void
-create_status_bar(void)
+status_bar_init(void)
 {
-    int i;
-    int cols = getmaxx(stdscr);
-
-    is_active[1] = TRUE;
-    is_new[1] = FALSE;
-    for (i = 2; i < 12; i++) {
-        is_active[i] = FALSE;
-        is_new[i] = FALSE;
-    }
-    remaining_active = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL);
-    remaining_new = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL);
-    current = 1;
+    tz = g_time_zone_new_local();
 
-    int bracket_attrs = theme_attrs(THEME_STATUS_BRACKET);
+    statusbar = malloc(sizeof(StatusBar));
+    statusbar->time = NULL;
+    statusbar->prompt = NULL;
+    statusbar->fulljid = NULL;
+    statusbar->tabs = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)_destroy_tab);
+    StatusBarTab *console = malloc(sizeof(StatusBarTab));
+    console->window_type = WIN_CONSOLE;
+    console->identifier = strdup("console");
+    g_hash_table_insert(statusbar->tabs, GINT_TO_POINTER(1), console);
+    statusbar->current_tab = 1;
 
     int row = screen_statusbar_row();
-    status_bar = newwin(1, cols, row, 0);
-    wbkgd(status_bar, theme_attrs(THEME_STATUS_TEXT));
-    wattron(status_bar, bracket_attrs);
-    mvwprintw(status_bar, 0, cols - 34, _active);
-    mvwprintw(status_bar, 0, cols - 34 + ((current - 1) * 3), bracket);
-    wattroff(status_bar, bracket_attrs);
-
-    tz = g_time_zone_new_local();
-
-    if (last_time) {
-        g_date_time_unref(last_time);
-    }
-    last_time = g_date_time_new_now(tz);
+    int cols = getmaxx(stdscr);
+    statusbar_win = newwin(1, cols, row, 0);
 
-    _status_bar_draw();
+    status_bar_draw();
 }
 
 void
-status_bar_update_virtual(void)
+status_bar_close(void)
 {
-    _status_bar_draw();
+    if (tz) {
+        g_time_zone_unref(tz);
+    }
+    if (statusbar) {
+        if (statusbar->time) {
+            g_free(statusbar->time);
+        }
+        if (statusbar->prompt) {
+            free(statusbar->prompt);
+        }
+        if (statusbar->fulljid) {
+            free(statusbar->fulljid);
+        }
+        if (statusbar->tabs) {
+            g_hash_table_destroy(statusbar->tabs);
+        }
+        free(statusbar);
+    }
 }
 
 void
 status_bar_resize(void)
 {
     int cols = getmaxx(stdscr);
-
-    werase(status_bar);
-
-    int bracket_attrs = theme_attrs(THEME_STATUS_BRACKET);
-
+    werase(statusbar_win);
     int row = screen_statusbar_row();
-    mvwin(status_bar, row, 0);
-    wresize(status_bar, 1, cols);
-    wbkgd(status_bar, theme_attrs(THEME_STATUS_TEXT));
-    wattron(status_bar, bracket_attrs);
-    mvwprintw(status_bar, 0, cols - 34, _active);
-    mvwprintw(status_bar, 0, cols - 34 + ((current - 1) * 3), bracket);
-    wattroff(status_bar, bracket_attrs);
-
-    if (message) {
-        char *time_pref = prefs_get_string(PREF_TIME_STATUSBAR);
-
-        gchar *date_fmt = NULL;
-        if (g_strcmp0(time_pref, "off") == 0) {
-            date_fmt = g_strdup("");
-        } else {
-            date_fmt = g_date_time_format(last_time, time_pref);
-        }
-        assert(date_fmt != NULL);
-        size_t len = strlen(date_fmt);
-        g_free(date_fmt);
-        if (g_strcmp0(time_pref, "off") != 0) {
-            /* 01234567890123456
-             *  [HH:MM]  message */
-            mvwprintw(status_bar, 0, 5 + len, message);
-        } else {
-            mvwprintw(status_bar, 0, 1, message);
-        }
-        prefs_free_string(time_pref);
-    }
-    if (last_time) {
-        g_date_time_unref(last_time);
-    }
-    last_time = g_date_time_new_now_local();
+    mvwin(statusbar_win, row, 0);
+    wresize(statusbar_win, 1, cols);
 
-    _status_bar_draw();
+    status_bar_draw();
 }
 
 void
 status_bar_set_all_inactive(void)
 {
-    int i = 0;
-    for (i = 0; i < 12; i++) {
-        is_active[i] = FALSE;
-        is_new[i] = FALSE;
-        _mark_inactive(i);
-    }
-
-    g_hash_table_remove_all(remaining_active);
-    g_hash_table_remove_all(remaining_new);
-
-    _status_bar_draw();
+    g_hash_table_remove_all(statusbar->tabs);
 }
 
 void
 status_bar_current(int i)
 {
     if (i == 0) {
-        current = 10;
-    } else if (i > 10) {
-        current = 11;
+        statusbar->current_tab = 10;
     } else {
-        current = i;
+        statusbar->current_tab = i;
     }
-    int cols = getmaxx(stdscr);
-    int bracket_attrs = theme_attrs(THEME_STATUS_BRACKET);
-    wattron(status_bar, bracket_attrs);
-    mvwprintw(status_bar, 0, cols - 34, _active);
-    mvwprintw(status_bar, 0, cols - 34 + ((current - 1) * 3), bracket);
-    wattroff(status_bar, bracket_attrs);
 
-    _status_bar_draw();
+    status_bar_draw();
 }
 
 void
@@ -205,281 +165,433 @@ status_bar_inactive(const int win)
         true_win = 10;
     }
 
-    // extra windows
-    if (true_win > 10) {
-        g_hash_table_remove(remaining_active, GINT_TO_POINTER(true_win));
-        g_hash_table_remove(remaining_new, GINT_TO_POINTER(true_win));
+    g_hash_table_remove(statusbar->tabs, GINT_TO_POINTER(true_win));
 
-        // still have new windows
-        if (g_hash_table_size(remaining_new) != 0) {
-            is_active[11] = TRUE;
-            is_new[11] = TRUE;
-            _mark_new(11);
-
-        // still have active windows
-        } else if (g_hash_table_size(remaining_active) != 0) {
-            is_active[11] = TRUE;
-            is_new[11] = FALSE;
-            _mark_active(11);
-
-        // no active or new windows
-        } else {
-            is_active[11] = FALSE;
-            is_new[11] = FALSE;
-            _mark_inactive(11);
-        }
+    status_bar_draw();
+}
 
-    // visible window indicators
-    } else {
-        is_active[true_win] = FALSE;
-        is_new[true_win] = FALSE;
-        _mark_inactive(true_win);
+void
+status_bar_active(const int win, win_type_t wintype, char *identifier)
+{
+    int true_win = win;
+    if (true_win == 0) {
+        true_win = 10;
     }
 
-    _status_bar_draw();
+    StatusBarTab *tab = malloc(sizeof(StatusBarTab));
+    tab->identifier = strdup(identifier);
+    tab->highlight = FALSE;
+    tab->window_type = wintype;
+    g_hash_table_replace(statusbar->tabs, GINT_TO_POINTER(true_win), tab);
+
+    status_bar_draw();
 }
 
 void
-status_bar_active(const int win)
+status_bar_new(const int win, win_type_t wintype, char* identifier)
 {
     int true_win = win;
     if (true_win == 0) {
         true_win = 10;
     }
 
-    // extra windows
-    if (true_win > 10) {
-        g_hash_table_add(remaining_active, GINT_TO_POINTER(true_win));
-        g_hash_table_remove(remaining_new, GINT_TO_POINTER(true_win));
+    StatusBarTab *tab = malloc(sizeof(StatusBarTab));
+    tab->identifier = strdup(identifier);
+    tab->highlight = TRUE;
+    tab->window_type = wintype;
+    g_hash_table_replace(statusbar->tabs, GINT_TO_POINTER(true_win), tab);
 
-        // still have new windows
-        if (g_hash_table_size(remaining_new) != 0) {
-            is_active[11] = TRUE;
-            is_new[11] = TRUE;
-            _mark_new(11);
-
-        // only active windows
-        } else {
-            is_active[11] = TRUE;
-            is_new[11] = FALSE;
-            _mark_active(11);
-        }
+    status_bar_draw();
+}
 
-    // visible window indicators
-    } else {
-        is_active[true_win] = TRUE;
-        is_new[true_win] = FALSE;
-        _mark_active(true_win);
+void
+status_bar_set_prompt(const char *const prompt)
+{
+    if (statusbar->prompt) {
+        free(statusbar->prompt);
+        statusbar->prompt = NULL;
     }
+    statusbar->prompt = strdup(prompt);
 
-    _status_bar_draw();
+    status_bar_draw();
 }
 
 void
-status_bar_new(const int win)
+status_bar_clear_prompt(void)
 {
-    int true_win = win;
-    if (true_win == 0) {
-        true_win = 10;
+    if (statusbar->prompt) {
+        free(statusbar->prompt);
+        statusbar->prompt = NULL;
     }
 
-    if (true_win > 10) {
-        g_hash_table_add(remaining_active, GINT_TO_POINTER(true_win));
-        g_hash_table_add(remaining_new, GINT_TO_POINTER(true_win));
-
-        is_active[11] = TRUE;
-        is_new[11] = TRUE;
-        _mark_new(11);
+    status_bar_draw();
+}
 
-    } else {
-        is_active[true_win] = TRUE;
-        is_new[true_win] = TRUE;
-        _mark_new(true_win);
+void
+status_bar_set_fulljid(const char *const fulljid)
+{
+    if (statusbar->fulljid) {
+        free(statusbar->fulljid);
+        statusbar->fulljid = NULL;
     }
+    statusbar->fulljid = strdup(fulljid);
 
-    _status_bar_draw();
+    status_bar_draw();
 }
 
 void
-status_bar_get_password(void)
+status_bar_clear_fulljid(void)
 {
-    status_bar_print_message("Enter password:");
+    if (statusbar->fulljid) {
+        free(statusbar->fulljid);
+        statusbar->fulljid = NULL;
+    }
 
-    _status_bar_draw();
+    status_bar_draw();
 }
 
 void
-status_bar_print_message(const char *const msg)
+status_bar_draw(void)
 {
-    werase(status_bar);
+    werase(statusbar_win);
+    wbkgd(statusbar_win, theme_attrs(THEME_STATUS_TEXT));
 
-    if (message) {
-        free(message);
+    int pos = 1;
+
+    pos = _status_bar_draw_time(pos);
+
+    _status_bar_draw_maintext(pos);
+
+    pos = getmaxx(stdscr) - _tabs_width();
+    if (pos < 0) {
+        pos = 0;
+    }
+    gint max_tabs = prefs_get_statusbartabs();
+    int i = 1;
+    for (i = 1; i <= max_tabs; i++) {
+        StatusBarTab *tab = g_hash_table_lookup(statusbar->tabs, GINT_TO_POINTER(i));
+        if (tab) {
+            pos = _status_bar_draw_tab(tab, pos, i);
+        }
     }
-    message = strdup(msg);
 
-    char *time_pref = prefs_get_string(PREF_TIME_STATUSBAR);
-    gchar *date_fmt = NULL;
-    if (g_strcmp0(time_pref, "off") == 0) {
-        date_fmt = g_strdup("");
-    } else {
-        date_fmt = g_date_time_format(last_time, time_pref);
+    pos = _status_bar_draw_extended_tabs(pos);
+
+    wnoutrefresh(statusbar_win);
+    inp_put_back();
+}
+
+static gboolean
+_extended_new(void)
+{
+    gint max_tabs = prefs_get_statusbartabs();
+    int tabs_count = g_hash_table_size(statusbar->tabs);
+    if (tabs_count <= max_tabs) {
+        return FALSE;
     }
 
-    assert(date_fmt != NULL);
-    size_t len = strlen(date_fmt);
-    g_free(date_fmt);
-    if (g_strcmp0(time_pref, "off") != 0) {
-        mvwprintw(status_bar, 0, 5 + len, message);
-    } else {
-        mvwprintw(status_bar, 0, 1, message);
+    int i = 0;
+    for (i = max_tabs + 1; i <= tabs_count; i++) {
+        StatusBarTab *tab = g_hash_table_lookup(statusbar->tabs, GINT_TO_POINTER(i));
+        if (tab && tab->highlight) {
+            return TRUE;
+        }
     }
-    prefs_free_string(time_pref);
 
-    int cols = getmaxx(stdscr);
-    int bracket_attrs = theme_attrs(THEME_STATUS_BRACKET);
+    return FALSE;
+}
+
+static int
+_status_bar_draw_extended_tabs(int pos)
+{
+    gint max_tabs = prefs_get_statusbartabs();
+    if (max_tabs == 0) {
+        return pos;
+    }
 
-    wattron(status_bar, bracket_attrs);
-    mvwprintw(status_bar, 0, cols - 34, _active);
-    mvwprintw(status_bar, 0, cols - 34 + ((current - 1) * 3), bracket);
-    wattroff(status_bar, bracket_attrs);
+    if (g_hash_table_size(statusbar->tabs) > max_tabs) {
+        gboolean is_current = statusbar->current_tab > max_tabs;
 
-    _status_bar_draw();
+        pos = _status_bar_draw_bracket(is_current, pos, "[");
+
+        int status_attrs = theme_attrs(THEME_STATUS_ACTIVE);
+        if (_extended_new()) {
+            status_attrs = theme_attrs(THEME_STATUS_NEW);
+        }
+        wattron(statusbar_win, status_attrs);
+        mvwprintw(statusbar_win, 0, pos, ">");
+        wattroff(statusbar_win, status_attrs);
+        pos++;
+
+        pos = _status_bar_draw_bracket(is_current, pos, "]");
+    }
+
+    return pos;
 }
 
-void
-status_bar_clear(void)
+static int
+_status_bar_draw_tab(StatusBarTab *tab, int pos, int num)
 {
-    if (message) {
-        free(message);
-        message = NULL;
+    int display_num = num == 10 ? 0 : num;
+    gboolean is_current = num == statusbar->current_tab;
+
+    gboolean show_number = prefs_get_boolean(PREF_STATUSBAR_SHOW_NUMBER);
+    gboolean show_name = prefs_get_boolean(PREF_STATUSBAR_SHOW_NAME);
+
+    pos = _status_bar_draw_bracket(is_current, pos, "[");
+
+    int status_attrs = 0;
+    if (tab->highlight) {
+        status_attrs = theme_attrs(THEME_STATUS_NEW);
+    } else {
+        status_attrs = theme_attrs(THEME_STATUS_ACTIVE);
+    }
+    wattron(statusbar_win, status_attrs);
+    if (show_number) {
+        mvwprintw(statusbar_win, 0, pos, "%d", display_num);
+        pos++;
+    }
+    if (show_number && show_name) {
+        mvwprintw(statusbar_win, 0, pos, ":");
+        pos++;
     }
+    if (show_name) {
+        char *display_name = _display_name(tab);
+        mvwprintw(statusbar_win, 0, pos, display_name);
+        pos += utf8_display_len(display_name);
+        free(display_name);
+    }
+    wattroff(statusbar_win, status_attrs);
 
-    werase(status_bar);
+    pos = _status_bar_draw_bracket(is_current, pos, "]");
 
-    int cols = getmaxx(stdscr);
-    int bracket_attrs = theme_attrs(THEME_STATUS_BRACKET);
+    return pos;
+}
 
-    wattron(status_bar, bracket_attrs);
-    mvwprintw(status_bar, 0, cols - 34, _active);
-    mvwprintw(status_bar, 0, cols - 34 + ((current - 1) * 3), bracket);
-    wattroff(status_bar, bracket_attrs);
+static int
+_status_bar_draw_bracket(gboolean current, int pos, char* ch)
+{
+    int bracket_attrs = theme_attrs(THEME_STATUS_BRACKET);
+    wattron(statusbar_win, bracket_attrs);
+    if (current) {
+        mvwprintw(statusbar_win, 0, pos, "-");
+    } else {
+        mvwprintw(statusbar_win, 0, pos, ch);
+    }
+    wattroff(statusbar_win, bracket_attrs);
+    pos++;
 
-    _status_bar_draw();
+    return pos;
 }
 
-void
-status_bar_clear_message(void)
+static int
+_status_bar_draw_time(int pos)
 {
-    if (message) {
-        free(message);
-        message = NULL;
+    char *time_pref = prefs_get_string(PREF_TIME_STATUSBAR);
+    if (g_strcmp0(time_pref, "off") == 0) {
+        prefs_free_string(time_pref);
+        return pos;
     }
 
-    werase(status_bar);
+    if (statusbar->time) {
+        g_free(statusbar->time);
+        statusbar->time = NULL;
+    }
+
+    GDateTime *datetime = g_date_time_new_now(tz);
+    statusbar->time  = g_date_time_format(datetime, time_pref);
+    assert(statusbar->time != NULL);
+    g_date_time_unref(datetime);
 
-    int cols = getmaxx(stdscr);
     int bracket_attrs = theme_attrs(THEME_STATUS_BRACKET);
+    int time_attrs = theme_attrs(THEME_STATUS_TIME);
 
-    wattron(status_bar, bracket_attrs);
-    mvwprintw(status_bar, 0, cols - 34, _active);
-    mvwprintw(status_bar, 0, cols - 34 + ((current - 1) * 3), bracket);
-    wattroff(status_bar, bracket_attrs);
+    size_t len = strlen(statusbar->time);
+    wattron(statusbar_win, bracket_attrs);
+    mvwaddch(statusbar_win, 0, pos, '[');
+    pos++;
+    wattroff(statusbar_win, bracket_attrs);
+    wattron(statusbar_win, time_attrs);
+    mvwprintw(statusbar_win, 0, pos, statusbar->time);
+    pos += len;
+    wattroff(statusbar_win, time_attrs);
+    wattron(statusbar_win, bracket_attrs);
+    mvwaddch(statusbar_win, 0, pos, ']');
+    wattroff(statusbar_win, bracket_attrs);
+    pos += 2;
 
-    _status_bar_draw();
+    prefs_free_string(time_pref);
+
+    return pos;
 }
 
 static void
-_update_win_statuses(void)
+_status_bar_draw_maintext(int pos)
 {
-    int i;
-    for(i = 1; i < 12; i++) {
-        if (is_new[i]) {
-            _mark_new(i);
+    if (statusbar->prompt) {
+        mvwprintw(statusbar_win, 0, pos, statusbar->prompt);
+        return;
+    }
+
+    if (statusbar->fulljid) {
+        char *pref = prefs_get_string(PREF_STATUSBAR_SELF);
+        if (g_strcmp0(pref, "off") == 0) {
+            return;
         }
-        else if (is_active[i]) {
-            _mark_active(i);
+        if (g_strcmp0(pref, "user") == 0) {
+            Jid *jidp = jid_create(statusbar->fulljid);
+            mvwprintw(statusbar_win, 0, pos, jidp->localpart);
+            jid_destroy(jidp);
+            return;
         }
-        else {
-            _mark_inactive(i);
+        if (g_strcmp0(pref, "barejid") == 0) {
+            Jid *jidp = jid_create(statusbar->fulljid);
+            mvwprintw(statusbar_win, 0, pos, jidp->barejid);
+            jid_destroy(jidp);
+            return;
         }
+        mvwprintw(statusbar_win, 0, pos, statusbar->fulljid);
     }
 }
 
 static void
-_mark_new(int num)
+_destroy_tab(StatusBarTab *tab)
 {
-    int active_pos = 1 + ((num-1) * 3);
-    int cols = getmaxx(stdscr);
-    int status_attrs = theme_attrs(THEME_STATUS_NEW);
-    wattron(status_bar, status_attrs);
-    wattron(status_bar, A_BLINK);
-    if (num == 10) {
-        mvwprintw(status_bar, 0, cols - 34 + active_pos, "0");
-    } else if (num > 10) {
-        mvwprintw(status_bar, 0, cols - 34 + active_pos, ">");
-    } else {
-        mvwprintw(status_bar, 0, cols - 34 + active_pos, "%d", num);
+    if (tab) {
+        if (tab->identifier) {
+            free(tab->identifier);
+        }
+        free(tab);
     }
-    wattroff(status_bar, status_attrs);
-    wattroff(status_bar, A_BLINK);
 }
 
-static void
-_mark_active(int num)
+static int
+_tabs_width(void)
 {
-    int active_pos = 1 + ((num-1) * 3);
-    int cols = getmaxx(stdscr);
-    int status_attrs = theme_attrs(THEME_STATUS_ACTIVE);
-    wattron(status_bar, status_attrs);
-    if (num == 10) {
-        mvwprintw(status_bar, 0, cols - 34 + active_pos, "0");
-    } else if (num > 10) {
-        mvwprintw(status_bar, 0, cols - 34 + active_pos, ">");
-    } else {
-        mvwprintw(status_bar, 0, cols - 34 + active_pos, "%d", num);
+    gboolean show_number = prefs_get_boolean(PREF_STATUSBAR_SHOW_NUMBER);
+    gboolean show_name = prefs_get_boolean(PREF_STATUSBAR_SHOW_NAME);
+    gint max_tabs = prefs_get_statusbartabs();
+
+    if (show_name && show_number) {
+        int width = g_hash_table_size(statusbar->tabs) > max_tabs ? 4 : 1;
+        int i = 0;
+        for (i = 1; i <= max_tabs; i++) {
+            StatusBarTab *tab = g_hash_table_lookup(statusbar->tabs, GINT_TO_POINTER(i));
+            if (tab) {
+                char *display_name = _display_name(tab);
+                width += utf8_display_len(display_name);
+                width += 4;
+                free(display_name);
+            }
+        }
+        return width;
     }
-    wattroff(status_bar, status_attrs);
-}
 
-static void
-_mark_inactive(int num)
-{
-    int active_pos = 1 + ((num-1) * 3);
-    int cols = getmaxx(stdscr);
-    mvwaddch(status_bar, 0, cols - 34 + active_pos, ' ');
+    if (show_name && !show_number) {
+        int width = g_hash_table_size(statusbar->tabs) > max_tabs ? 4 : 1;
+        int i = 0;
+        for (i = 1; i <= max_tabs; i++) {
+            StatusBarTab *tab = g_hash_table_lookup(statusbar->tabs, GINT_TO_POINTER(i));
+            if (tab) {
+                char *display_name = _display_name(tab);
+                width += utf8_display_len(display_name);
+                width += 2;
+                free(display_name);
+            }
+        }
+        return width;
+    }
+
+    if (g_hash_table_size(statusbar->tabs) > max_tabs) {
+        return max_tabs * 3 + (g_hash_table_size(statusbar->tabs) > max_tabs ? 4 : 1);
+    }
+    return g_hash_table_size(statusbar->tabs) * 3 + (g_hash_table_size(statusbar->tabs) > max_tabs ? 4 : 1);
 }
 
-static void
-_status_bar_draw(void)
+static char*
+_display_name(StatusBarTab *tab)
 {
-    if (last_time) {
-        g_date_time_unref(last_time);
+    char *fullname = NULL;
+
+    if (tab->window_type == WIN_CONSOLE) {
+        fullname = strdup("console");
+    } else if (tab->window_type == WIN_XML) {
+        fullname = strdup("xmlconsole");
+    } else if (tab->window_type == WIN_PLUGIN) {
+        fullname = strdup(tab->identifier);
+    } else if (tab->window_type == WIN_CHAT) {
+        PContact contact = roster_get_contact(tab->identifier);
+        if (contact && p_contact_name(contact)) {
+            fullname = strdup(p_contact_name(contact));
+        } else {
+            char *pref = prefs_get_string(PREF_STATUSBAR_CHAT);
+            if (g_strcmp0("user", pref) == 0) {
+                Jid *jidp = jid_create(tab->identifier);
+                char *user = strdup(jidp->localpart);
+                jid_destroy(jidp);
+                fullname = user;
+            } else {
+                fullname = strdup(tab->identifier);
+            }
+        }
+    } else if (tab->window_type == WIN_MUC) {
+        char *pref = prefs_get_string(PREF_STATUSBAR_ROOM);
+        if (g_strcmp0("room", pref) == 0) {
+            Jid *jidp = jid_create(tab->identifier);
+            char *room = strdup(jidp->localpart);
+            jid_destroy(jidp);
+            fullname = room;
+        } else {
+            fullname = strdup(tab->identifier);
+        }
+    } else if (tab->window_type == WIN_MUC_CONFIG) {
+        char *pref = prefs_get_string(PREF_STATUSBAR_ROOM);
+        GString *display_str = g_string_new("");
+        if (g_strcmp0("room", pref) == 0) {
+            Jid *jidp = jid_create(tab->identifier);
+            g_string_append(display_str, jidp->localpart);
+            jid_destroy(jidp);
+        } else {
+            g_string_append(display_str, tab->identifier);
+        }
+        g_string_append(display_str, " conf");
+        char *result = strdup(display_str->str);
+        g_string_free(display_str, TRUE);
+        fullname = result;
+    } else if (tab->window_type == WIN_PRIVATE) {
+        char *pref = prefs_get_string(PREF_STATUSBAR_ROOM);
+        if (g_strcmp0("room", pref) == 0) {
+            GString *display_str = g_string_new("");
+            Jid *jidp = jid_create(tab->identifier);
+            g_string_append(display_str, jidp->localpart);
+            g_string_append(display_str, "/");
+            g_string_append(display_str, jidp->resourcepart);
+            jid_destroy(jidp);
+            char *result = strdup(display_str->str);
+            g_string_free(display_str, TRUE);
+            fullname = result;
+        } else {
+            fullname = strdup(tab->identifier);
+        }
+    } else {
+        fullname = strdup("window");
     }
-    last_time = g_date_time_new_now(tz);
 
-    int bracket_attrs = theme_attrs(THEME_STATUS_BRACKET);
-    int time_attrs = theme_attrs(THEME_STATUS_TIME);
+    gint tablen = prefs_get_statusbartablen();
+    if (tablen == 0) {
+        return fullname;
+    }
 
-    char *time_pref = prefs_get_string(PREF_TIME_STATUSBAR);
-    if (g_strcmp0(time_pref, "off") != 0) {
-        gchar *date_fmt = g_date_time_format(last_time, time_pref);
-        assert(date_fmt != NULL);
-        size_t len = strlen(date_fmt);
-        wattron(status_bar, bracket_attrs);
-        mvwaddch(status_bar, 0, 1, '[');
-        wattroff(status_bar, bracket_attrs);
-        wattron(status_bar, time_attrs);
-        mvwprintw(status_bar, 0, 2, date_fmt);
-        wattroff(status_bar, time_attrs);
-        wattron(status_bar, bracket_attrs);
-        mvwaddch(status_bar, 0, 2 + len, ']');
-        wattroff(status_bar, bracket_attrs);
-        g_free(date_fmt);
+    int namelen = utf8_display_len(fullname);
+    if (namelen < tablen) {
+        return fullname;
     }
-    prefs_free_string(time_pref);
 
-    _update_win_statuses();
-    wnoutrefresh(status_bar);
-    inp_put_back();
+    gchar *trimmed = g_utf8_substring(fullname, 0, tablen);
+    free(fullname);
+    char *trimmedname = strdup(trimmed);
+    g_free(trimmed);
+
+    return trimmedname;
+
 }
diff --git a/src/ui/statusbar.h b/src/ui/statusbar.h
index ce863b75..de8b51cc 100644
--- a/src/ui/statusbar.h
+++ b/src/ui/statusbar.h
@@ -1,7 +1,7 @@
 /*
  * statusbar.h
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -35,13 +35,14 @@
 #ifndef UI_STATUSBAR_H
 #define UI_STATUSBAR_H
 
-void create_status_bar(void);
-void status_bar_update_virtual(void);
+void status_bar_init(void);
+void status_bar_draw(void);
+void status_bar_close(void);
 void status_bar_resize(void);
-void status_bar_clear(void);
-void status_bar_clear_message(void);
-void status_bar_get_password(void);
-void status_bar_print_message(const char *const msg);
+void status_bar_set_prompt(const char *const prompt);
+void status_bar_clear_prompt(void);
+void status_bar_set_fulljid(const char *const fulljid);
+void status_bar_clear_fulljid(void);
 void status_bar_current(int i);
 
 #endif
diff --git a/src/ui/titlebar.c b/src/ui/titlebar.c
index 3b170558..4f5383c2 100644
--- a/src/ui/titlebar.c
+++ b/src/ui/titlebar.c
@@ -1,7 +1,7 @@
 /*
  * titlebar.c
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
diff --git a/src/ui/titlebar.h b/src/ui/titlebar.h
index dda517a2..0aed4b2b 100644
--- a/src/ui/titlebar.h
+++ b/src/ui/titlebar.h
@@ -1,7 +1,7 @@
 /*
  * titlebar.h
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
diff --git a/src/ui/tray.c b/src/ui/tray.c
index 36817004..ecb0843b 100644
--- a/src/ui/tray.c
+++ b/src/ui/tray.c
@@ -1,7 +1,7 @@
 /*
  * tray.c
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
diff --git a/src/ui/tray.h b/src/ui/tray.h
index 4efe988a..c1e6ba77 100644
--- a/src/ui/tray.h
+++ b/src/ui/tray.h
@@ -1,7 +1,7 @@
 /*
  * tray.h
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
diff --git a/src/ui/ui.h b/src/ui/ui.h
index c2c6a969..d344f855 100644
--- a/src/ui/ui.h
+++ b/src/ui/ui.h
@@ -1,7 +1,7 @@
 /*
  * ui.h
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -297,7 +297,6 @@ void cons_occupants_setting(void);
 void cons_roster_setting(void);
 void cons_presence_setting(void);
 void cons_wrap_setting(void);
-void cons_winstidy_setting(void);
 void cons_time_setting(void);
 void cons_wintitle_setting(void);
 void cons_notify_setting(void);
@@ -316,7 +315,9 @@ void cons_autoaway_setting(void);
 void cons_reconnect_setting(void);
 void cons_autoping_setting(void);
 void cons_autoconnect_setting(void);
+void cons_room_cache_setting(void);
 void cons_inpblock_setting(void);
+void cons_statusbar_setting(void);
 void cons_winpos_setting(void);
 void cons_show_contact_online(PContact contact, Resource *resource, GDateTime *last_activity);
 void cons_show_contact_offline(PContact contact, char *resource, char *status);
@@ -330,8 +331,8 @@ void title_bar_set_presence(contact_presence_t presence);
 
 // status bar
 void status_bar_inactive(const int win);
-void status_bar_active(const int win);
-void status_bar_new(const int win);
+void status_bar_active(const int win, win_type_t wintype, char *identifier);
+void status_bar_new(const int win, win_type_t wintype, char *identifier);
 void status_bar_set_all_inactive(void);
 
 // roster window
@@ -374,6 +375,7 @@ void win_show_occupant_info(ProfWin *window, const char *const room, Occupant *o
 void win_show_contact(ProfWin *window, PContact contact);
 void win_show_info(ProfWin *window, PContact contact);
 void win_clear(ProfWin *window);
+char* win_get_tab_identifier(ProfWin *window);
 char* win_to_string(ProfWin *window);
 
 // desktop notifications
diff --git a/src/ui/win_types.h b/src/ui/win_types.h
index 87ee0b40..7fa75b34 100644
--- a/src/ui/win_types.h
+++ b/src/ui/win_types.h
@@ -1,7 +1,7 @@
 /*
  * win_types.h
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -194,7 +194,7 @@ typedef struct prof_xml_win_t {
 } ProfXMLWin;
 
 typedef struct prof_plugin_win_t {
-    ProfWin super;
+    ProfWin window;
     char *tag;
     char *plugin_name;
     unsigned long memcheck;
diff --git a/src/ui/window.c b/src/ui/window.c
index aea4b60c..5543707d 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -1,7 +1,7 @@
 /*
  * window.c
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -162,7 +162,7 @@ win_create_muc(const char *const roomjid)
     int cols = getmaxx(stdscr);
 
     new_win->window.type = WIN_MUC;
-
+    new_win->window.layout = _win_create_simple_layout();
     ProfLayoutSplit *layout = malloc(sizeof(ProfLayoutSplit));
     layout->base.type = LAYOUT_SPLIT;
 
@@ -208,7 +208,6 @@ win_create_muc_config(const char *const roomjid, DataForm *form)
     ProfMucConfWin *new_win = malloc(sizeof(ProfMucConfWin));
     new_win->window.type = WIN_MUC_CONFIG;
     new_win->window.layout = _win_create_simple_layout();
-
     new_win->roomjid = strdup(roomjid);
     new_win->form = form;
 
@@ -223,7 +222,6 @@ win_create_private(const char *const fulljid)
     ProfPrivateWin *new_win = malloc(sizeof(ProfPrivateWin));
     new_win->window.type = WIN_PRIVATE;
     new_win->window.layout = _win_create_simple_layout();
-
     new_win->fulljid = strdup(fulljid);
     new_win->unread = 0;
     new_win->occupant_offline = FALSE;
@@ -250,15 +248,15 @@ ProfWin*
 win_create_plugin(const char *const plugin_name, const char *const tag)
 {
     ProfPluginWin *new_win = malloc(sizeof(ProfPluginWin));
-    new_win->super.type = WIN_PLUGIN;
-    new_win->super.layout = _win_create_simple_layout();
+    new_win->window.type = WIN_PLUGIN;
+    new_win->window.layout = _win_create_simple_layout();
 
     new_win->tag = strdup(tag);
     new_win->plugin_name = strdup(plugin_name);
 
     new_win->memcheck = PROFPLUGINWIN_MEMCHECK;
 
-    return &new_win->super;
+    return &new_win->window;
 }
 
 char*
@@ -321,6 +319,50 @@ win_get_title(ProfWin *window)
 }
 
 char*
+win_get_tab_identifier(ProfWin *window)
+{
+    assert(window != NULL);
+
+    switch (window->type) {
+        case WIN_CONSOLE:
+        {
+            return strdup("console");
+        }
+        case WIN_CHAT:
+        {
+            ProfChatWin *chatwin = (ProfChatWin*)window;
+            return strdup(chatwin->barejid);
+        }
+        case WIN_MUC:
+        {
+            ProfMucWin *mucwin = (ProfMucWin*)window;
+            return strdup(mucwin->roomjid);
+        }
+        case WIN_MUC_CONFIG:
+        {
+            ProfMucConfWin *mucconfwin = (ProfMucConfWin*)window;
+            return strdup(mucconfwin->roomjid);
+        }
+        case WIN_PRIVATE:
+        {
+            ProfPrivateWin *privwin = (ProfPrivateWin*)window;
+            return strdup(privwin->fulljid);
+        }
+        case WIN_PLUGIN:
+        {
+            ProfPluginWin *pluginwin = (ProfPluginWin*)window;
+            return strdup(pluginwin->tag);
+        }
+        case WIN_XML:
+        {
+            return strdup("xmlconsole");
+        }
+        default:
+            return strdup("UNKNOWN");
+    }
+}
+
+char*
 win_to_string(ProfWin *window)
 {
     assert(window != NULL);
diff --git a/src/ui/window.h b/src/ui/window.h
index 074e5855..62f737de 100644
--- a/src/ui/window.h
+++ b/src/ui/window.h
@@ -1,7 +1,7 @@
 /*
  * window.h
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
diff --git a/src/ui/window_list.c b/src/ui/window_list.c
index 2d19ab32..798f4e41 100644
--- a/src/ui/window_list.c
+++ b/src/ui/window_list.c
@@ -1,7 +1,7 @@
 /*
  * window_list.c
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -45,7 +45,6 @@
 #include "config/theme.h"
 #include "plugins/plugins.h"
 #include "ui/ui.h"
-#include "ui/statusbar.h"
 #include "ui/window_list.h"
 #include "xmpp/xmpp.h"
 #include "xmpp/roster_list.h"
@@ -239,9 +238,7 @@ wins_close_plugin(char *tag)
     int index = wins_get_num(toclose);
     ui_close_win(index);
 
-    if (prefs_get_boolean(PREF_WINS_AUTO_TIDY)) {
-        wins_tidy();
-    }
+    wins_tidy();
 }
 
 GList*
@@ -847,7 +844,7 @@ wins_lost_connection(void)
     g_list_free(values);
 }
 
-gboolean
+void
 wins_swap(int source_win, int target_win)
 {
     ProfWin *source = g_hash_table_lookup(windows, GINT_TO_POINTER(source_win));
@@ -857,20 +854,21 @@ wins_swap(int source_win, int target_win)
         ProfWin *target = g_hash_table_lookup(windows, GINT_TO_POINTER(target_win));
 
         // target window empty
-        if (!target) {
+        if (target == NULL) {
             g_hash_table_steal(windows, GINT_TO_POINTER(source_win));
             g_hash_table_insert(windows, GINT_TO_POINTER(target_win), source);
             status_bar_inactive(source_win);
+            char *identifier = win_get_tab_identifier(source);
             if (win_unread(source) > 0) {
-                status_bar_new(target_win);
+                status_bar_new(target_win, source->type, identifier);
             } else {
-                status_bar_active(target_win);
+                status_bar_active(target_win, source->type, identifier);
             }
+            free(identifier);
             if (wins_get_current_num() == source_win) {
                 wins_set_current_by_num(target_win);
                 ui_focus_win(console);
             }
-            return TRUE;
 
         // target window occupied
         } else {
@@ -878,23 +876,24 @@ wins_swap(int source_win, int target_win)
             g_hash_table_steal(windows, GINT_TO_POINTER(target_win));
             g_hash_table_insert(windows, GINT_TO_POINTER(source_win), target);
             g_hash_table_insert(windows, GINT_TO_POINTER(target_win), source);
+            char *source_identifier = win_get_tab_identifier(source);
+            char *target_identifier = win_get_tab_identifier(target);
             if (win_unread(source) > 0) {
-                status_bar_new(target_win);
+                status_bar_new(target_win, source->type, source_identifier);
             } else {
-                status_bar_active(target_win);
+                status_bar_active(target_win, source->type, source_identifier);
             }
             if (win_unread(target) > 0) {
-                status_bar_new(source_win);
+                status_bar_new(source_win, target->type, target_identifier);
             } else {
-                status_bar_active(source_win);
+                status_bar_active(source_win, target->type, target_identifier);
             }
+            free(source_identifier);
+            free(target_identifier);
             if ((wins_get_current_num() == source_win) || (wins_get_current_num() == target_win)) {
                 ui_focus_win(console);
             }
-            return TRUE;
         }
-    } else {
-        return FALSE;
     }
 }
 
@@ -999,22 +998,24 @@ wins_tidy(void)
         GList *curr = keys;
         while (curr) {
             ProfWin *window = g_hash_table_lookup(windows, curr->data);
+            char *identifier = win_get_tab_identifier(window);
             g_hash_table_steal(windows, curr->data);
             if (num == 10) {
                 g_hash_table_insert(new_windows, GINT_TO_POINTER(0), window);
                 if (win_unread(window) > 0) {
-                    status_bar_new(0);
+                    status_bar_new(0, window->type, identifier);
                 } else {
-                    status_bar_active(0);
+                    status_bar_active(0, window->type, identifier);
                 }
             } else {
                 g_hash_table_insert(new_windows, GINT_TO_POINTER(num), window);
                 if (win_unread(window) > 0) {
-                    status_bar_new(num);
+                    status_bar_new(num, window->type, identifier);
                 } else {
-                    status_bar_active(num);
+                    status_bar_active(num, window->type, identifier);
                 }
             }
+            free(identifier);
             num++;
             curr = g_list_next(curr);
         }
diff --git a/src/ui/window_list.h b/src/ui/window_list.h
index dc1f3be4..68e72739 100644
--- a/src/ui/window_list.h
+++ b/src/ui/window_list.h
@@ -1,7 +1,7 @@
 /*
  * window_list.h
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *
@@ -87,7 +87,7 @@ gboolean wins_tidy(void);
 GSList* wins_create_summary(gboolean unread);
 void wins_destroy(void);
 GList* wins_get_nums(void);
-gboolean wins_swap(int source_win, int target_win);
+void wins_swap(int source_win, int target_win);
 void wins_hide_subwin(ProfWin *window);
 void wins_show_subwin(ProfWin *window);
 
diff --git a/src/ui/xmlwin.c b/src/ui/xmlwin.c
index e845b2e0..c76e91cf 100644
--- a/src/ui/xmlwin.c
+++ b/src/ui/xmlwin.c
@@ -1,7 +1,7 @@
 /*
  * xmlwin.c
  *
- * Copyright (C) 2012 - 2017 James Booth <boothj5@gmail.com>
+ * Copyright (C) 2012 - 2018 James Booth <boothj5@gmail.com>
  *
  * This file is part of Profanity.
  *