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/console.c2
-rw-r--r--src/ui/core.c253
-rw-r--r--src/ui/titlebar.c11
-rw-r--r--src/ui/window.c412
-rw-r--r--src/ui/window.h97
-rw-r--r--src/ui/windows.c44
6 files changed, 443 insertions, 376 deletions
diff --git a/src/ui/console.c b/src/ui/console.c
index ae0dec97..fab5a4ac 100644
--- a/src/ui/console.c
+++ b/src/ui/console.c
@@ -189,7 +189,7 @@ _cons_about(void)
         cons_check_version(FALSE);
     }
 
-    pnoutrefresh(console->win, 0, 0, 1, 0, rows-3, cols-1);
+    pnoutrefresh(console->layout->win, 0, 0, 1, 0, rows-3, cols-1);
 
     cons_alert();
 }
diff --git a/src/ui/core.c b/src/ui/core.c
index c6616c91..a70e76e9 100644
--- a/src/ui/core.c
+++ b/src/ui/core.c
@@ -116,7 +116,7 @@ static void
 _ui_update(void)
 {
     ProfWin *current = wins_get_current();
-    if (current->paged == 0) {
+    if (current->layout->paged == 0) {
         win_move_to_end(current);
     }
 
@@ -336,7 +336,8 @@ _ui_incoming_msg(const char * const barejid, const char * const message, GTimeVa
         window = wins_new_chat(barejid);
 #ifdef HAVE_LIBOTR
         if (otr_is_secure(barejid)) {
-            window->wins.chat.is_otr = TRUE;
+            ProfChatWin *chatwin = (ProfChatWin*)window;
+            chatwin->is_otr = TRUE;
         }
 #endif
         win_created = TRUE;
@@ -749,7 +750,7 @@ _ui_redraw_all_room_rosters(void)
     while (curr != NULL) {
         int num = GPOINTER_TO_INT(curr->data);
         ProfWin *window = wins_get_by_num(num);
-        if (window->type == WIN_MUC && window->wins.muc.subwin) {
+        if (window->type == WIN_MUC && win_has_active_subwin(window)) {
             char *room = window->from;
             ui_muc_roster(room);
         }
@@ -769,7 +770,7 @@ _ui_hide_all_room_rosters(void)
     while (curr != NULL) {
         int num = GPOINTER_TO_INT(curr->data);
         ProfWin *window = wins_get_by_num(num);
-        if (window->type == WIN_MUC && window->wins.muc.subwin) {
+        if (window->type == WIN_MUC && win_has_active_subwin(window)) {
             char *room = window->from;
             ui_room_hide_occupants(room);
         }
@@ -789,7 +790,7 @@ _ui_show_all_room_rosters(void)
     while (curr != NULL) {
         int num = GPOINTER_TO_INT(curr->data);
         ProfWin *window = wins_get_by_num(num);
-        if (window->type == WIN_MUC && window->wins.muc.subwin == NULL) {
+        if (window->type == WIN_MUC && !win_has_active_subwin(window)) {
             char *room = window->from;
             ui_room_show_occupants(room);
         }
@@ -808,11 +809,8 @@ _ui_win_has_unsaved_form(int num)
     if (window->type != WIN_MUC_CONFIG) {
         return FALSE;
     }
-    if (window->wins.conf.form == NULL) {
-        return FALSE;
-    }
 
-    return window->wins.conf.form->modified;
+    return win_has_modified_form(window);
 }
 
 GString *
@@ -839,12 +837,14 @@ _ui_switch_win(const int i)
     if (ui_win_exists(i)) {
         ProfWin *old_current = wins_get_current();
         if (old_current->type == WIN_MUC_CONFIG) {
-            cmd_autocomplete_remove_form_fields(old_current->wins.conf.form);
+            ProfMucConfWin *confwin = (ProfMucConfWin*)old_current;
+            cmd_autocomplete_remove_form_fields(confwin->form);
         }
 
         ProfWin *new_current = wins_get_by_num(i);
         if (new_current->type == WIN_MUC_CONFIG) {
-            cmd_autocomplete_add_form_fields(new_current->wins.conf.form);
+            ProfMucConfWin *confwin = (ProfMucConfWin*)new_current;
+            cmd_autocomplete_add_form_fields(confwin->form);
         }
 
         wins_set_current_by_num(i);
@@ -873,12 +873,14 @@ _ui_previous_win(void)
 {
     ProfWin *old_current = wins_get_current();
     if (old_current->type == WIN_MUC_CONFIG) {
-        cmd_autocomplete_remove_form_fields(old_current->wins.conf.form);
+        ProfMucConfWin *confwin = (ProfMucConfWin*)old_current;
+        cmd_autocomplete_remove_form_fields(confwin->form);
     }
 
     ProfWin *new_current = wins_get_previous();
     if (new_current->type == WIN_MUC_CONFIG) {
-        cmd_autocomplete_add_form_fields(new_current->wins.conf.form);
+        ProfMucConfWin *confwin = (ProfMucConfWin*)new_current;
+        cmd_autocomplete_add_form_fields(confwin->form);
     }
 
     int i = wins_get_num(new_current);
@@ -904,12 +906,14 @@ _ui_next_win(void)
 {
     ProfWin *old_current = wins_get_current();
     if (old_current->type == WIN_MUC_CONFIG) {
-        cmd_autocomplete_remove_form_fields(old_current->wins.conf.form);
+        ProfMucConfWin *confwin = (ProfMucConfWin*)old_current;
+        cmd_autocomplete_remove_form_fields(confwin->form);
     }
 
     ProfWin *new_current = wins_get_next();
     if (new_current->type == WIN_MUC_CONFIG) {
-        cmd_autocomplete_add_form_fields(new_current->wins.conf.form);
+        ProfMucConfWin *confwin = (ProfMucConfWin*)new_current;
+        cmd_autocomplete_add_form_fields(confwin->form);
     }
 
     int i = wins_get_num(new_current);
@@ -942,10 +946,11 @@ _ui_gone_secure(const char * const barejid, gboolean trusted)
         return;
     }
 
-    FREE_SET_NULL(window->wins.chat.resource);
+    ProfChatWin *chatwin = (ProfChatWin*)window;
+    FREE_SET_NULL(chatwin->resource);
 
-    window->wins.chat.is_otr = TRUE;
-    window->wins.chat.is_trusted = trusted;
+    chatwin->is_otr = TRUE;
+    chatwin->is_trusted = trusted;
     if (trusted) {
         win_save_print(window, '!', NULL, 0, THEME_OTR_STARTED_TRUSTED, "", "OTR session started (trusted).");
     } else {
@@ -1067,8 +1072,9 @@ _ui_gone_insecure(const char * const recipient)
         if (window->type != WIN_CHAT) {
             return;
         }
-        window->wins.chat.is_otr = FALSE;
-        window->wins.chat.is_trusted = FALSE;
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        chatwin->is_otr = FALSE;
+        chatwin->is_trusted = FALSE;
         win_save_print(window, '!', NULL, 0, THEME_OTR_ENDED, "", "OTR session ended.");
 
         if (wins_is_current(window)) {
@@ -1087,8 +1093,9 @@ _ui_trust(const char * const recipient)
         if (window->type != WIN_CHAT) {
             return;
         }
-        window->wins.chat.is_otr = TRUE;
-        window->wins.chat.is_trusted = TRUE;
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        chatwin->is_otr = TRUE;
+        chatwin->is_trusted = TRUE;
         win_save_print(window, '!', NULL, 0, THEME_OTR_TRUSTED, "", "OTR session trusted.");
 
         if (wins_is_current(window)) {
@@ -1107,8 +1114,9 @@ _ui_untrust(const char * const recipient)
         if (window->type != WIN_CHAT) {
             return;
         }
-        window->wins.chat.is_otr = TRUE;
-        window->wins.chat.is_trusted = FALSE;
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        chatwin->is_otr = TRUE;
+        chatwin->is_trusted = FALSE;
         win_save_print(window, '!', NULL, 0, THEME_OTR_UNTRUSTED, "", "OTR session untrusted.");
 
         if (wins_is_current(window)) {
@@ -1140,9 +1148,10 @@ static void
 _ui_close_win(int index)
 {
     ProfWin *window = wins_get_by_num(index);
-    if (window) {
-        if (window->type == WIN_MUC_CONFIG && window->wins.conf.form) {
-            cmd_autocomplete_remove_form_fields(window->wins.conf.form);
+    if (window && window->type == WIN_MUC_CONFIG) {
+        ProfMucConfWin *confwin = (ProfMucConfWin*)window;
+        if (confwin->form) {
+            cmd_autocomplete_remove_form_fields(confwin->form);
         }
     }
 
@@ -1234,7 +1243,8 @@ _ui_current_set_otr(gboolean value)
 {
     ProfWin *current = wins_get_current();
     if (current->type == WIN_CHAT) {
-        current->wins.chat.is_otr = value;
+        ProfChatWin *chatwin = (ProfChatWin*)current;
+        chatwin->is_otr = value;
     }
 }
 
@@ -1442,7 +1452,8 @@ _ui_outgoing_msg(const char * const from, const char * const to,
             window = wins_new_chat(to);
 #ifdef HAVE_LIBOTR
             if (otr_is_secure(to)) {
-                window->wins.chat.is_otr = TRUE;
+                ProfChatWin *chatwin = (ProfChatWin*)window;
+                chatwin->is_otr = TRUE;
             }
 #endif
         }
@@ -2939,14 +2950,15 @@ _ui_roster_contact(PContact contact)
                 (prefs_get_boolean(PREF_ROSTER_OFFLINE)))) {
             theme_item_t presence_colour = theme_main_presence_attrs(presence);
 
-            wattron(window->wins.cons.subwin, theme_attrs(presence_colour));
+            ProfLayoutSplit *layout = (ProfLayoutSplit*)window->layout;
+            wattron(layout->subwin, theme_attrs(presence_colour));
 
             GString *msg = g_string_new("   ");
             g_string_append(msg, name);
-            win_printline_nowrap(window->wins.cons.subwin, msg->str);
+            win_printline_nowrap(layout->subwin, msg->str);
             g_string_free(msg, TRUE);
 
-            wattroff(window->wins.cons.subwin, theme_attrs(presence_colour));
+            wattroff(layout->subwin, theme_attrs(presence_colour));
 
             if (prefs_get_boolean(PREF_ROSTER_RESOURCE)) {
                 GList *resources = p_contact_get_available_resources(contact);
@@ -2955,14 +2967,14 @@ _ui_roster_contact(PContact contact)
                     Resource *resource = curr_resource->data;
                     const char *resource_presence = string_from_resource_presence(resource->presence);
                     theme_item_t resource_presence_colour = theme_main_presence_attrs(resource_presence);
-                    wattron(window->wins.cons.subwin, theme_attrs(resource_presence_colour));
+                    wattron(layout->subwin, theme_attrs(resource_presence_colour));
 
                     GString *msg = g_string_new("     ");
                     g_string_append(msg, resource->name);
-                    win_printline_nowrap(window->wins.cons.subwin, msg->str);
+                    win_printline_nowrap(layout->subwin, msg->str);
                     g_string_free(msg, TRUE);
 
-                    wattroff(window->wins.cons.subwin, theme_attrs(resource_presence_colour));
+                    wattroff(layout->subwin, theme_attrs(resource_presence_colour));
 
                     curr_resource = g_list_next(curr_resource);
                 }
@@ -2976,9 +2988,11 @@ static void
 _ui_roster_contacts_by_presence(const char * const presence, char *title)
 {
     ProfWin *window = wins_get_console();
-    wattron(window->wins.cons.subwin, theme_attrs(THEME_ROSTER_HEADER));
-    win_printline_nowrap(window->wins.cons.subwin, title);
-    wattroff(window->wins.cons.subwin, theme_attrs(THEME_ROSTER_HEADER));
+    ProfLayoutSplit *layout = (ProfLayoutSplit*)window->layout;
+
+    wattron(layout->subwin, theme_attrs(THEME_ROSTER_HEADER));
+    win_printline_nowrap(layout->subwin, title);
+    wattroff(layout->subwin, theme_attrs(THEME_ROSTER_HEADER));
     GSList *contacts = roster_get_contacts_by_presence(presence);
     if (contacts) {
         GSList *curr_contact = contacts;
@@ -2995,12 +3009,14 @@ static void
 _ui_roster_contacts_by_group(char *group)
 {
     ProfWin *window = wins_get_console();
-    wattron(window->wins.cons.subwin, theme_attrs(THEME_ROSTER_HEADER));
+    ProfLayoutSplit *layout = (ProfLayoutSplit*)window->layout;
+
+    wattron(layout->subwin, theme_attrs(THEME_ROSTER_HEADER));
     GString *title = g_string_new(" -");
     g_string_append(title, group);
-    win_printline_nowrap(window->wins.cons.subwin, title->str);
+    win_printline_nowrap(layout->subwin, title->str);
     g_string_free(title, TRUE);
-    wattroff(window->wins.cons.subwin, theme_attrs(THEME_ROSTER_HEADER));
+    wattroff(layout->subwin, theme_attrs(THEME_ROSTER_HEADER));
     GSList *contacts = roster_get_group(group);
     if (contacts) {
         GSList *curr_contact = contacts;
@@ -3017,11 +3033,13 @@ static void
 _ui_roster_contacts_by_no_group(void)
 {
     ProfWin *window = wins_get_console();
+    ProfLayoutSplit *layout = (ProfLayoutSplit*)window->layout;
+
     GSList *contacts = roster_get_nogroup();
     if (contacts) {
-        wattron(window->wins.cons.subwin, theme_attrs(THEME_ROSTER_HEADER));
-        win_printline_nowrap(window->wins.cons.subwin, " -no group");
-        wattroff(window->wins.cons.subwin, theme_attrs(THEME_ROSTER_HEADER));
+        wattron(layout->subwin, theme_attrs(THEME_ROSTER_HEADER));
+        win_printline_nowrap(layout->subwin, " -no group");
+        wattroff(layout->subwin, theme_attrs(THEME_ROSTER_HEADER));
         GSList *curr_contact = contacts;
         while (curr_contact) {
             PContact contact = curr_contact->data;
@@ -3037,9 +3055,10 @@ _ui_roster(void)
 {
     ProfWin *window = wins_get_console();
     if (window) {
+        ProfLayoutSplit *layout = (ProfLayoutSplit*)window->layout;
         char *by = prefs_get_string(PREF_ROSTER_BY);
         if (g_strcmp0(by, "presence") == 0) {
-            werase(window->wins.cons.subwin);
+            werase(layout->subwin);
             _ui_roster_contacts_by_presence("chat", " -Available for chat");
             _ui_roster_contacts_by_presence("online", " -Online");
             _ui_roster_contacts_by_presence("away", " -Away");
@@ -3049,7 +3068,7 @@ _ui_roster(void)
                 _ui_roster_contacts_by_presence("offline", " -Offline");
             }
         } else if (g_strcmp0(by, "group") == 0) {
-            werase(window->wins.cons.subwin);
+            werase(layout->subwin);
             GSList *groups = roster_get_groups();
             GSList *curr_group = groups;
             while (curr_group) {
@@ -3061,10 +3080,10 @@ _ui_roster(void)
         } else {
             GSList *contacts = roster_get_contacts();
             if (contacts) {
-                werase(window->wins.cons.subwin);
-                wattron(window->wins.cons.subwin, theme_attrs(THEME_ROSTER_HEADER));
-                win_printline_nowrap(window->wins.cons.subwin, " -Roster");
-                wattroff(window->wins.cons.subwin, theme_attrs(THEME_ROSTER_HEADER));
+                werase(layout->subwin);
+                wattron(layout->subwin, theme_attrs(THEME_ROSTER_HEADER));
+                win_printline_nowrap(layout->subwin, " -Roster");
+                wattroff(layout->subwin, theme_attrs(THEME_ROSTER_HEADER));
                 GSList *curr_contact = contacts;
                 while (curr_contact) {
                     PContact contact = curr_contact->data;
@@ -3085,88 +3104,89 @@ _ui_muc_roster(const char * const room)
     if (window) {
         GList *occupants = muc_roster(room);
         if (occupants) {
-            werase(window->wins.muc.subwin);
+            ProfLayoutSplit *layout = (ProfLayoutSplit*)window->layout;
+            werase(layout->subwin);
 
             if (prefs_get_boolean(PREF_MUC_PRIVILEGES)) {
-                wattron(window->wins.muc.subwin, theme_attrs(THEME_OCCUPANTS_HEADER));
-                win_printline_nowrap(window->wins.muc.subwin, " -Moderators");
-                wattroff(window->wins.muc.subwin, theme_attrs(THEME_OCCUPANTS_HEADER));
+                wattron(layout->subwin, theme_attrs(THEME_OCCUPANTS_HEADER));
+                win_printline_nowrap(layout->subwin, " -Moderators");
+                wattroff(layout->subwin, theme_attrs(THEME_OCCUPANTS_HEADER));
                 GList *roster_curr = occupants;
                 while (roster_curr) {
                     Occupant *occupant = roster_curr->data;
                     if (occupant->role == MUC_ROLE_MODERATOR) {
                         const char *presence_str = string_from_resource_presence(occupant->presence);
                         theme_item_t presence_colour = theme_main_presence_attrs(presence_str);
-                        wattron(window->wins.muc.subwin, theme_attrs(presence_colour));
+                        wattron(layout->subwin, theme_attrs(presence_colour));
 
                         GString *msg = g_string_new("   ");
                         g_string_append(msg, occupant->nick);
-                        win_printline_nowrap(window->wins.muc.subwin, msg->str);
+                        win_printline_nowrap(layout->subwin, msg->str);
                         g_string_free(msg, TRUE);
 
-                        wattroff(window->wins.muc.subwin, theme_attrs(presence_colour));
+                        wattroff(layout->subwin, theme_attrs(presence_colour));
                     }
                     roster_curr = g_list_next(roster_curr);
                 }
 
-                wattron(window->wins.muc.subwin, theme_attrs(THEME_OCCUPANTS_HEADER));
-                win_printline_nowrap(window->wins.muc.subwin, " -Participants");
-                wattroff(window->wins.muc.subwin, theme_attrs(THEME_OCCUPANTS_HEADER));
+                wattron(layout->subwin, theme_attrs(THEME_OCCUPANTS_HEADER));
+                win_printline_nowrap(layout->subwin, " -Participants");
+                wattroff(layout->subwin, theme_attrs(THEME_OCCUPANTS_HEADER));
                 roster_curr = occupants;
                 while (roster_curr) {
                     Occupant *occupant = roster_curr->data;
                     if (occupant->role == MUC_ROLE_PARTICIPANT) {
                         const char *presence_str = string_from_resource_presence(occupant->presence);
                         theme_item_t presence_colour = theme_main_presence_attrs(presence_str);
-                        wattron(window->wins.muc.subwin, theme_attrs(presence_colour));
+                        wattron(layout->subwin, theme_attrs(presence_colour));
 
                         GString *msg = g_string_new("   ");
                         g_string_append(msg, occupant->nick);
-                        win_printline_nowrap(window->wins.muc.subwin, msg->str);
+                        win_printline_nowrap(layout->subwin, msg->str);
                         g_string_free(msg, TRUE);
 
-                        wattroff(window->wins.muc.subwin, theme_attrs(presence_colour));
+                        wattroff(layout->subwin, theme_attrs(presence_colour));
                     }
                     roster_curr = g_list_next(roster_curr);
                 }
 
-                wattron(window->wins.muc.subwin, theme_attrs(THEME_OCCUPANTS_HEADER));
-                win_printline_nowrap(window->wins.muc.subwin, " -Visitors");
-                wattroff(window->wins.muc.subwin, theme_attrs(THEME_OCCUPANTS_HEADER));
+                wattron(layout->subwin, theme_attrs(THEME_OCCUPANTS_HEADER));
+                win_printline_nowrap(layout->subwin, " -Visitors");
+                wattroff(layout->subwin, theme_attrs(THEME_OCCUPANTS_HEADER));
                 roster_curr = occupants;
                 while (roster_curr) {
                     Occupant *occupant = roster_curr->data;
                     if (occupant->role == MUC_ROLE_VISITOR) {
                         const char *presence_str = string_from_resource_presence(occupant->presence);
                         theme_item_t presence_colour = theme_main_presence_attrs(presence_str);
-                        wattron(window->wins.muc.subwin, theme_attrs(presence_colour));
+                        wattron(layout->subwin, theme_attrs(presence_colour));
 
                         GString *msg = g_string_new("   ");
                         g_string_append(msg, occupant->nick);
-                        win_printline_nowrap(window->wins.muc.subwin, msg->str);
+                        win_printline_nowrap(layout->subwin, msg->str);
                         g_string_free(msg, TRUE);
 
-                        wattroff(window->wins.muc.subwin, theme_attrs(presence_colour));
+                        wattroff(layout->subwin, theme_attrs(presence_colour));
                     }
                     roster_curr = g_list_next(roster_curr);
                 }
             } else {
-                wattron(window->wins.muc.subwin, theme_attrs(THEME_OCCUPANTS_HEADER));
-                win_printline_nowrap(window->wins.muc.subwin, " -Occupants\n");
-                wattroff(window->wins.muc.subwin, theme_attrs(THEME_OCCUPANTS_HEADER));
+                wattron(layout->subwin, theme_attrs(THEME_OCCUPANTS_HEADER));
+                win_printline_nowrap(layout->subwin, " -Occupants\n");
+                wattroff(layout->subwin, theme_attrs(THEME_OCCUPANTS_HEADER));
                 GList *roster_curr = occupants;
                 while (roster_curr) {
                     Occupant *occupant = roster_curr->data;
                     const char *presence_str = string_from_resource_presence(occupant->presence);
                     theme_item_t presence_colour = theme_main_presence_attrs(presence_str);
-                    wattron(window->wins.muc.subwin, theme_attrs(presence_colour));
+                    wattron(layout->subwin, theme_attrs(presence_colour));
 
                     GString *msg = g_string_new("   ");
                     g_string_append(msg, occupant->nick);
-                    win_printline_nowrap(window->wins.muc.subwin, msg->str);
+                    win_printline_nowrap(layout->subwin, msg->str);
                     g_string_free(msg, TRUE);
 
-                    wattroff(window->wins.muc.subwin, theme_attrs(presence_colour));
+                    wattroff(layout->subwin, theme_attrs(presence_colour));
                     roster_curr = g_list_next(roster_curr);
                 }
             }
@@ -3180,7 +3200,7 @@ static void
 _ui_room_show_occupants(const char * const room)
 {
     ProfWin *window = wins_get_by_recipient(room);
-    if (window && !window->wins.muc.subwin) {
+    if (window && !win_has_active_subwin(window)) {
         wins_show_subwin(window);
         ui_muc_roster(room);
     }
@@ -3190,7 +3210,7 @@ static void
 _ui_room_hide_occupants(const char * const room)
 {
     ProfWin *window = wins_get_by_recipient(room);
-    if (window && window->wins.muc.subwin) {
+    if (window && win_has_active_subwin(window)) {
         wins_hide_subwin(window);
     }
 }
@@ -3199,7 +3219,7 @@ static void
 _ui_show_roster(void)
 {
     ProfWin *window = wins_get_console();
-    if (window && !window->wins.cons.subwin) {
+    if (window && !win_has_active_subwin(window)) {
         wins_show_subwin(window);
         ui_roster();
     }
@@ -3209,7 +3229,7 @@ static void
 _ui_hide_roster(void)
 {
     ProfWin *window = wins_get_console();
-    if (window && window->wins.cons.subwin) {
+    if (window && win_has_active_subwin(window)) {
         wins_hide_subwin(window);
     }
 }
@@ -3245,10 +3265,10 @@ _win_handle_page(const wint_t * const ch, const int result)
 {
     ProfWin *current = wins_get_current();
     int rows = getmaxy(stdscr);
-    int y = getcury(current->win);
+    int y = getcury(current->layout->win);
 
     int page_space = rows - 4;
-    int *page_start = &(current->y_pos);
+    int *page_start = &(current->layout->y_pos);
 
     if (prefs_get_boolean(PREF_MOUSE)) {
         MEVENT mouse_event;
@@ -3271,7 +3291,7 @@ _win_handle_page(const wint_t * const ch, const int result)
                     else if (*page_start >= y)
                         *page_start = y - page_space;
 
-                    current->paged = 1;
+                    current->layout->paged = 1;
                     win_update_virtual(current);
                 } else if (mouse_event.bstate & BUTTON4_PRESSED) { // mouse wheel up
                     *page_start -= 4;
@@ -3280,7 +3300,7 @@ _win_handle_page(const wint_t * const ch, const int result)
                     if (*page_start < 0)
                         *page_start = 0;
 
-                    current->paged = 1;
+                    current->layout->paged = 1;
                     win_update_virtual(current);
                 }
             }
@@ -3295,7 +3315,7 @@ _win_handle_page(const wint_t * const ch, const int result)
         if (*page_start < 0)
             *page_start = 0;
 
-        current->paged = 1;
+        current->layout->paged = 1;
         win_update_virtual(current);
 
     // page down
@@ -3310,25 +3330,23 @@ _win_handle_page(const wint_t * const ch, const int result)
         else if (*page_start >= y)
             *page_start = y - page_space - 1;
 
-        current->paged = 1;
+        current->layout->paged = 1;
         win_update_virtual(current);
     }
 
     // switch off page if last line and space line visible
     if ((y) - *page_start == page_space) {
-        current->paged = 0;
+        current->layout->paged = 0;
     }
 
     if ((current->type == WIN_MUC) || (current->type == WIN_CONSOLE)) {
         int sub_y = 0;
         int *sub_y_pos = NULL;
 
-        if (current->type == WIN_MUC) {
-            sub_y = getcury(current->wins.muc.subwin);
-            sub_y_pos = &(current->wins.muc.sub_y_pos);
-        } else if (current->type == WIN_CONSOLE) {
-            sub_y = getcury(current->wins.cons.subwin);
-            sub_y_pos = &(current->wins.cons.sub_y_pos);
+        if (current->type == WIN_MUC || current->type == WIN_CONSOLE) {
+            ProfLayoutSplit *split_layout = (ProfLayoutSplit*)current->layout;
+            sub_y = getcury(split_layout->subwin);
+            sub_y_pos = &(split_layout->sub_y_pos);
         }
 
         // alt up arrow
@@ -3362,32 +3380,35 @@ static void
 _win_show_history(int win_index, const char * const contact)
 {
     ProfWin *window = wins_get_by_num(win_index);
-    if (window->type == WIN_CHAT && !window->wins.chat.history_shown) {
-        Jid *jid = jid_create(jabber_get_fulljid());
-        GSList *history = chat_log_get_previous(jid->barejid, contact);
-        jid_destroy(jid);
-        GSList *curr = history;
-        while (curr != NULL) {
-            char *line = curr->data;
-            // entry
-            if (line[2] == ':') {
-                char hh[3]; memcpy(hh, &line[0], 2); hh[2] = '\0'; int ihh = atoi(hh);
-                char mm[3]; memcpy(mm, &line[3], 2); mm[2] = '\0'; int imm = atoi(mm);
-                char ss[3]; memcpy(ss, &line[6], 2); ss[2] = '\0'; int iss = atoi(ss);
-                GDateTime *time = g_date_time_new_local(2000, 1, 1, ihh, imm, iss);
-                GTimeVal tv;
-                g_date_time_to_timeval(time, &tv);
-                win_save_print(window, '-', &tv, NO_COLOUR_DATE, 0, "", curr->data+11);
-                g_date_time_unref(time);
-            // header
-            } else {
-                win_save_print(window, '-', NULL, 0, 0, "", curr->data);
+    if (window->type == WIN_CHAT) {
+        if (win_chat_history_shown(window)) {
+            Jid *jid = jid_create(jabber_get_fulljid());
+            GSList *history = chat_log_get_previous(jid->barejid, contact);
+            jid_destroy(jid);
+            GSList *curr = history;
+            while (curr != NULL) {
+                char *line = curr->data;
+                // entry
+                if (line[2] == ':') {
+                    char hh[3]; memcpy(hh, &line[0], 2); hh[2] = '\0'; int ihh = atoi(hh);
+                    char mm[3]; memcpy(mm, &line[3], 2); mm[2] = '\0'; int imm = atoi(mm);
+                    char ss[3]; memcpy(ss, &line[6], 2); ss[2] = '\0'; int iss = atoi(ss);
+                    GDateTime *time = g_date_time_new_local(2000, 1, 1, ihh, imm, iss);
+                    GTimeVal tv;
+                    g_date_time_to_timeval(time, &tv);
+                    win_save_print(window, '-', &tv, NO_COLOUR_DATE, 0, "", curr->data+11);
+                    g_date_time_unref(time);
+                // header
+                } else {
+                    win_save_print(window, '-', NULL, 0, 0, "", curr->data);
+                }
+                curr = g_slist_next(curr);
             }
-            curr = g_slist_next(curr);
-        }
-        window->wins.chat.history_shown = TRUE;
+            ProfChatWin *chatwin = (ProfChatWin*)window;
+            chatwin->history_shown = TRUE;
 
-        g_slist_free_full(history, free);
+            g_slist_free_full(history, free);
+        }
     }
 }
 
diff --git a/src/ui/titlebar.c b/src/ui/titlebar.c
index 8a022916..cd254aba 100644
--- a/src/ui/titlebar.c
+++ b/src/ui/titlebar.c
@@ -189,7 +189,7 @@ _title_bar_draw(void)
             wprintw(win, " (typing...)");
         }
     } else if (current && current->type == WIN_MUC_CONFIG) {
-        if (current->wins.conf.form && current->wins.conf.form->modified) {
+        if (win_has_modified_form(current)) {
             wprintw(win, " *");
         }
     }
@@ -324,21 +324,22 @@ _show_contact_presence(void)
     int bracket_attrs = theme_attrs(THEME_TITLE_BRACKET);
 
     ProfWin *current = wins_get_current();
-    if (current && current->wins.chat.resource) {
+    ProfChatWin *chatwin = (ProfChatWin*)current;
+    if (current && win_has_chat_resource(current)) {
         wprintw(win, "/");
-        wprintw(win, current->wins.chat.resource);
+        wprintw(win, chatwin->resource);
     }
 
     if (prefs_get_boolean(PREF_PRESENCE)) {
         theme_item_t presence_colour = THEME_TITLE_OFFLINE;
         const char *presence = "offline";
 
-        if (current && current->wins.chat.resource) {
+        if (current && win_has_chat_resource(current)) {
             char *barejid = roster_barejid_from_name(current_recipient);
             if (barejid) {
                 PContact contact = roster_get_contact(barejid);
                 if (contact) {
-                    Resource *resource = p_contact_get_resource(contact, current->wins.chat.resource);
+                    Resource *resource = p_contact_get_resource(contact, chatwin->resource);
                     if (resource) {
                         presence = string_from_resource_presence(resource->presence);
                     }
diff --git a/src/ui/window.c b/src/ui/window.c
index 90c819e5..0c486e12 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -79,180 +79,191 @@ win_occpuants_cols(void)
 ProfWin*
 win_create_console(void)
 {
-    ProfWin *new_win = malloc(sizeof(ProfWin));
+    ProfConsoleWin *new_win = malloc(sizeof(ProfConsoleWin));
     int cols = getmaxx(stdscr);
 
-    new_win->type = WIN_CONSOLE;
-
-    new_win->win = newpad(PAD_SIZE, (cols));
-    wbkgd(new_win->win, theme_attrs(THEME_TEXT));
-    new_win->wins.cons.subwin = NULL;
-    new_win->wins.cons.sub_y_pos = 0;
-
-    new_win->from = strdup(CONS_WIN_TITLE);
-    new_win->buffer = buffer_create();
-    new_win->y_pos = 0;
-    new_win->paged = 0;
-    new_win->unread = 0;
-
-    scrollok(new_win->win, TRUE);
-
-    return new_win;
+    new_win->super.type = WIN_CONSOLE;
+
+    ProfLayoutSplit *layout = malloc(sizeof(ProfLayoutSplit));
+    layout->super.type = LAYOUT_SPLIT;
+    layout->super.win = newpad(PAD_SIZE, (cols));
+    wbkgd(layout->super.win, theme_attrs(THEME_TEXT));
+    layout->super.buffer = buffer_create();
+    layout->super.y_pos = 0;
+    layout->super.paged = 0;
+    scrollok(layout->super.win, TRUE);
+    layout->subwin = NULL;
+    layout->sub_y_pos = 0;
+    new_win->super.layout = (ProfLayout*)layout;
+
+    ProfWin *profwin = (ProfWin*)new_win;
+    profwin->from = strdup(CONS_WIN_TITLE);
+    profwin->unread = 0;
+
+    return (ProfWin*)new_win;
 }
 
 ProfWin*
 win_create_chat(const char * const barejid)
 {
-    ProfWin *new_win = malloc(sizeof(ProfWin));
+    ProfChatWin *new_win = malloc(sizeof(ProfChatWin));
     int cols = getmaxx(stdscr);
 
-    new_win->type = WIN_CHAT;
+    new_win->super.type = WIN_CHAT;
 
-    new_win->win = newpad(PAD_SIZE, (cols));
-    wbkgd(new_win->win, theme_attrs(THEME_TEXT));
+    ProfLayoutSingle *layout = malloc(sizeof(ProfLayoutSingle));
+    layout->super.type = LAYOUT_SINGLE;
+    layout->super.win = newpad(PAD_SIZE, (cols));
+    wbkgd(layout->super.win, theme_attrs(THEME_TEXT));
+    layout->super.buffer = buffer_create();
+    layout->super.y_pos = 0;
+    layout->super.paged = 0;
+    scrollok(layout->super.win, TRUE);
+    new_win->super.layout = (ProfLayout*)layout;
 
-    new_win->from = strdup(barejid);
-    new_win->buffer = buffer_create();
-    new_win->y_pos = 0;
-    new_win->paged = 0;
-    new_win->unread = 0;
+    ProfWin *profwin = (ProfWin*)new_win;
+    profwin->from = strdup(barejid);
+    profwin->unread = 0;
 
-    new_win->wins.chat.resource = NULL;
-    new_win->wins.chat.is_otr = FALSE;
-    new_win->wins.chat.is_trusted = FALSE;
-    new_win->wins.chat.history_shown = FALSE;
+    new_win->resource = NULL;
+    new_win->is_otr = FALSE;
+    new_win->is_trusted = FALSE;
+    new_win->history_shown = FALSE;
 
-    scrollok(new_win->win, TRUE);
-
-    return new_win;
+    return (ProfWin*)new_win;
 }
 
 ProfWin*
 win_create_muc(const char * const roomjid)
 {
-    ProfWin *new_win = malloc(sizeof(ProfWin));
+    ProfMucWin *new_win = malloc(sizeof(ProfMucWin));
     int cols = getmaxx(stdscr);
 
-    new_win->type = WIN_MUC;
+    new_win->super.type = WIN_MUC;
+
+    ProfLayoutSplit *layout = malloc(sizeof(ProfLayoutSplit));
+    layout->super.type = LAYOUT_SPLIT;
 
     if (prefs_get_boolean(PREF_OCCUPANTS)) {
         int subwin_cols = win_occpuants_cols();
-        new_win->win = newpad(PAD_SIZE, cols - subwin_cols);
-        wbkgd(new_win->win, theme_attrs(THEME_TEXT));
-        new_win->wins.muc.subwin = newpad(PAD_SIZE, subwin_cols);;
-        wbkgd(new_win->wins.muc.subwin, theme_attrs(THEME_TEXT));
+        layout->super.win = newpad(PAD_SIZE, cols - subwin_cols);
+        wbkgd(layout->super.win, theme_attrs(THEME_TEXT));
+        layout->subwin = newpad(PAD_SIZE, subwin_cols);;
+        wbkgd(layout->subwin, theme_attrs(THEME_TEXT));
     } else {
-        new_win->win = newpad(PAD_SIZE, (cols));
-        wbkgd(new_win->win, theme_attrs(THEME_TEXT));
-        new_win->wins.muc.subwin = NULL;
-    }
-    new_win->wins.muc.sub_y_pos = 0;
-
-    new_win->from = strdup(roomjid);
-    new_win->buffer = buffer_create();
-    new_win->y_pos = 0;
-    new_win->paged = 0;
-    new_win->unread = 0;
-
-    scrollok(new_win->win, TRUE);
-
-    return new_win;
+        layout->super.win = newpad(PAD_SIZE, (cols));
+        wbkgd(layout->super.win, theme_attrs(THEME_TEXT));
+        layout->subwin = NULL;
+    }
+    layout->sub_y_pos = 0;
+    layout->super.buffer = buffer_create();
+    layout->super.y_pos = 0;
+    layout->super.paged = 0;
+    scrollok(layout->super.win, TRUE);
+    new_win->super.layout = (ProfLayout*)layout;
+
+    ProfWin *profwin = (ProfWin*)new_win;
+    profwin->from = strdup(roomjid);
+    profwin->unread = 0;
+
+    return (ProfWin*)new_win;
 }
 
 ProfWin*
 win_create_muc_config(const char * const title, DataForm *form)
 {
-    ProfWin *new_win = malloc(sizeof(ProfWin));
+    ProfMucConfWin *new_win = malloc(sizeof(ProfMucConfWin));
     int cols = getmaxx(stdscr);
 
-    new_win->type = WIN_MUC_CONFIG;
-
-    new_win->win = newpad(PAD_SIZE, (cols));
-    wbkgd(new_win->win, theme_attrs(THEME_TEXT));
+    new_win->super.type = WIN_MUC_CONFIG;
 
-    new_win->wins.conf.form = form;
+    ProfLayoutSingle *layout = malloc(sizeof(ProfLayoutSingle));
+    layout->super.type = LAYOUT_SINGLE;
+    layout->super.win = newpad(PAD_SIZE, (cols));
+    wbkgd(layout->super.win, theme_attrs(THEME_TEXT));
+    layout->super.buffer = buffer_create();
+    layout->super.y_pos = 0;
+    layout->super.paged = 0;
+    scrollok(layout->super.win, TRUE);
+    new_win->super.layout = (ProfLayout*)layout;
 
-    new_win->from = strdup(title);
-    new_win->buffer = buffer_create();
-    new_win->y_pos = 0;
-    new_win->paged = 0;
-    new_win->unread = 0;
+    new_win->form = form;
 
-    scrollok(new_win->win, TRUE);
+    ProfWin *profwin = (ProfWin*)new_win;
+    profwin->from = strdup(title);
+    profwin->unread = 0;
 
-    return new_win;
+    return (ProfWin*)new_win;
 }
 
 ProfWin*
 win_create_private(const char * const fulljid)
 {
-    ProfWin *new_win = malloc(sizeof(ProfWin));
+    ProfPrivateWin *new_win = malloc(sizeof(ProfPrivateWin));
     int cols = getmaxx(stdscr);
 
-    new_win->type = WIN_PRIVATE;
+    new_win->super.type = WIN_PRIVATE;
 
-    new_win->win = newpad(PAD_SIZE, (cols));
-    wbkgd(new_win->win, theme_attrs(THEME_TEXT));
+    ProfLayoutSingle *layout = malloc(sizeof(ProfLayoutSingle));
+    layout->super.type = LAYOUT_SINGLE;
+    layout->super.win = newpad(PAD_SIZE, (cols));
+    wbkgd(layout->super.win, theme_attrs(THEME_TEXT));
+    layout->super.buffer = buffer_create();
+    layout->super.y_pos = 0;
+    layout->super.paged = 0;
+    scrollok(layout->super.win, TRUE);
+    new_win->super.layout = (ProfLayout*)layout;
 
-    new_win->from = strdup(fulljid);
-    new_win->buffer = buffer_create();
-    new_win->y_pos = 0;
-    new_win->paged = 0;
-    new_win->unread = 0;
-
-    scrollok(new_win->win, TRUE);
-
-    return new_win;
+    ProfWin *profwin = (ProfWin*)new_win;
+    profwin->from = strdup(fulljid);
+    profwin->unread = 0;
 
+    return (ProfWin*)new_win;
 }
 
 ProfWin*
 win_create_xmlconsole(void)
 {
-    ProfWin *new_win = malloc(sizeof(ProfWin));
+    ProfXMLWin *new_win = malloc(sizeof(ProfXMLWin));
     int cols = getmaxx(stdscr);
 
-    new_win->type = WIN_XML;
-
-    new_win->win = newpad(PAD_SIZE, (cols));
-    wbkgd(new_win->win, theme_attrs(THEME_TEXT));
+    new_win->super.type = WIN_XML;
 
-    new_win->from = strdup(XML_WIN_TITLE);
-    new_win->buffer = buffer_create();
-    new_win->y_pos = 0;
-    new_win->paged = 0;
-    new_win->unread = 0;
+    ProfLayoutSingle *layout = malloc(sizeof(ProfLayoutSingle));
+    layout->super.type = LAYOUT_SINGLE;
+    layout->super.win = newpad(PAD_SIZE, (cols));
+    wbkgd(layout->super.win, theme_attrs(THEME_TEXT));
+    layout->super.buffer = buffer_create();
+    layout->super.y_pos = 0;
+    layout->super.paged = 0;
+    scrollok(layout->super.win, TRUE);
+    new_win->super.layout = (ProfLayout*)layout;
 
-    scrollok(new_win->win, TRUE);
+    ProfWin *profwin = (ProfWin*)new_win;
+    profwin->from = strdup(XML_WIN_TITLE);
+    profwin->unread = 0;
 
-    return new_win;
+    return (ProfWin*)new_win;
 }
 
 void
 win_hide_subwin(ProfWin *window)
 {
-    switch (window->type) {
-    case WIN_CONSOLE:
-        if (window->wins.cons.subwin) {
-            delwin(window->wins.cons.subwin);
+    if (window->layout->type == LAYOUT_SPLIT) {
+        ProfLayoutSplit *layout = (ProfLayoutSplit*)window->layout;
+        if (layout->subwin) {
+            delwin(layout->subwin);
         }
-        window->wins.cons.subwin = NULL;
-        window->wins.cons.sub_y_pos = 0;
-        break;
-    case WIN_MUC:
-        if (window->wins.muc.subwin) {
-            delwin(window->wins.muc.subwin);
-        }
-        window->wins.muc.subwin = NULL;
-        window->wins.muc.sub_y_pos = 0;
-        break;
-    default:
-        break;
+        layout->subwin = NULL;
+        layout->sub_y_pos = 0;
+        int cols = getmaxx(stdscr);
+        wresize(layout->super.win, PAD_SIZE, cols);
+        win_redraw(window);
+    } else {
+        int cols = getmaxx(stdscr);
+        wresize(window->layout->win, PAD_SIZE, cols);
+        win_redraw(window);
     }
-
-    int cols = getmaxx(stdscr);
-    wresize(window->win, PAD_SIZE, cols);
-    win_redraw(window);
 }
 
 void
@@ -261,30 +272,21 @@ win_show_subwin(ProfWin *window)
     int cols = getmaxx(stdscr);
     int subwin_cols = 0;
 
-    switch (window->type) {
-    case WIN_CONSOLE:
+    if (window->layout->type == LAYOUT_SPLIT) {
+        ProfLayoutSplit *layout = (ProfLayoutSplit*)window->layout;
         subwin_cols = win_roster_cols();
-        window->wins.cons.subwin = newpad(PAD_SIZE, subwin_cols);
-        wbkgd(window->wins.cons.subwin, theme_attrs(THEME_TEXT));
-        wresize(window->win, PAD_SIZE, cols - subwin_cols);
-        win_redraw(window);
-        break;
-    case WIN_MUC:
-        subwin_cols = win_occpuants_cols();
-        window->wins.muc.subwin = newpad(PAD_SIZE, subwin_cols);
-        wbkgd(window->wins.muc.subwin, theme_attrs(THEME_TEXT));
-        wresize(window->win, PAD_SIZE, cols - subwin_cols);
+        layout->subwin = newpad(PAD_SIZE, subwin_cols);
+        wbkgd(layout->subwin, theme_attrs(THEME_TEXT));
+        wresize(layout->super.win, PAD_SIZE, cols - subwin_cols);
         win_redraw(window);
-        break;
-    default:
-        break;
     }
 }
 
 gboolean win_is_otr(ProfWin *window)
 {
     if (window->type == WIN_CHAT) {
-        return window->wins.chat.is_otr;
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        return chatwin->is_otr;
     } else {
         return FALSE;
     }
@@ -293,7 +295,8 @@ gboolean win_is_otr(ProfWin *window)
 gboolean win_is_trusted(ProfWin *window)
 {
     if (window->type == WIN_CHAT) {
-        return window->wins.chat.is_trusted;
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        return chatwin->is_trusted;
     } else {
         return FALSE;
     }
@@ -302,32 +305,28 @@ gboolean win_is_trusted(ProfWin *window)
 void
 win_free(ProfWin* window)
 {
-    buffer_free(window->buffer);
-    delwin(window->win);
-
-    switch (window->type) {
-    case WIN_CONSOLE:
-        if (window->wins.cons.subwin) {
-            delwin(window->wins.cons.subwin);
-        }
-        break;
-    case WIN_MUC:
-        if (window->wins.muc.subwin) {
-            delwin(window->wins.muc.subwin);
+    if (window->layout->type == LAYOUT_SPLIT) {
+        ProfLayoutSplit *layout = (ProfLayoutSplit*)window->layout;
+        if (layout->subwin) {
+            delwin(layout->subwin);
         }
-        break;
-    default:
-        break;
+        buffer_free(layout->super.buffer);
+        delwin(layout->super.win);
+    } else {
+        buffer_free(window->layout->buffer);
+        delwin(window->layout->win);
     }
 
     if (window->type == WIN_CHAT) {
-        free(window->wins.chat.resource);
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        free(chatwin->resource);
     }
 
     free(window->from);
 
     if (window->type == WIN_MUC_CONFIG) {
-        form_destroy(window->wins.conf.form);
+        ProfMucConfWin *mucconf = (ProfMucConfWin*)window;
+        form_destroy(mucconf->form);
     }
 
     free(window);
@@ -340,43 +339,36 @@ win_update_virtual(ProfWin *window)
     getmaxyx(stdscr, rows, cols);
     int subwin_cols = 0;
 
-    switch (window->type) {
-    case WIN_CONSOLE:
-        if (window->wins.cons.subwin) {
-            subwin_cols = win_roster_cols();
-            pnoutrefresh(window->win, window->y_pos, 0, 1, 0, rows-3, (cols-subwin_cols)-1);
-            pnoutrefresh(window->wins.cons.subwin, window->wins.cons.sub_y_pos, 0, 1, (cols-subwin_cols), rows-3, cols-1);
-        } else {
-            pnoutrefresh(window->win, window->y_pos, 0, 1, 0, rows-3, cols-1);
-        }
-        break;
-    case WIN_MUC:
-        if (window->wins.muc.subwin) {
-            subwin_cols = win_occpuants_cols();
-            pnoutrefresh(window->win, window->y_pos, 0, 1, 0, rows-3, (cols-subwin_cols)-1);
-            pnoutrefresh(window->wins.muc.subwin, window->wins.muc.sub_y_pos, 0, 1, (cols-subwin_cols), rows-3, cols-1);
+    if (window->layout->type == LAYOUT_SPLIT) {
+        ProfLayoutSplit *layout = (ProfLayoutSplit*)window->layout;
+        if (layout->subwin) {
+            if (window->type == WIN_MUC) {
+                subwin_cols = win_occpuants_cols();
+            } else {
+                subwin_cols = win_roster_cols();
+            }
+            pnoutrefresh(layout->super.win, layout->super.y_pos, 0, 1, 0, rows-3, (cols-subwin_cols)-1);
+            pnoutrefresh(layout->subwin, layout->sub_y_pos, 0, 1, (cols-subwin_cols), rows-3, cols-1);
         } else {
-            pnoutrefresh(window->win, window->y_pos, 0, 1, 0, rows-3, cols-1);
+            pnoutrefresh(layout->super.win, layout->super.y_pos, 0, 1, 0, rows-3, cols-1);
         }
-        break;
-    default:
-        pnoutrefresh(window->win, window->y_pos, 0, 1, 0, rows-3, cols-1);
-        break;
+    } else {
+        pnoutrefresh(window->layout->win, window->layout->y_pos, 0, 1, 0, rows-3, cols-1);
     }
 }
 
 void
 win_move_to_end(ProfWin *window)
 {
-    window->paged = 0;
+    window->layout->paged = 0;
 
     int rows = getmaxy(stdscr);
-    int y = getcury(window->win);
+    int y = getcury(window->layout->win);
     int size = rows - 3;
 
-    window->y_pos = y - (size - 1);
-    if (window->y_pos < 0) {
-        window->y_pos = 0;
+    window->layout->y_pos = y - (size - 1);
+    if (window->layout->y_pos < 0) {
+        window->layout->y_pos = 0;
     }
 }
 
@@ -721,7 +713,7 @@ win_save_print(ProfWin *window, const char show_char, GTimeVal *tstamp,
         time = g_date_time_new_from_timeval_utc(tstamp);
     }
 
-    buffer_push(window->buffer, show_char, time, flags, theme_item, from, message);
+    buffer_push(window->layout->buffer, show_char, time, flags, theme_item, from, message);
     _win_print(window, show_char, time, flags, theme_item, from, message);
 }
 
@@ -762,11 +754,11 @@ _win_print(ProfWin *window, const char show_char, GDateTime *time,
 
         if (date_fmt) {
             if ((flags & NO_COLOUR_DATE) == 0) {
-                wattron(window->win, theme_attrs(THEME_TIME));
+                wattron(window->layout->win, theme_attrs(THEME_TIME));
             }
-            wprintw(window->win, "%s %c ", date_fmt, show_char);
+            wprintw(window->layout->win, "%s %c ", date_fmt, show_char);
             if ((flags & NO_COLOUR_DATE) == 0) {
-                wattroff(window->win, theme_attrs(THEME_TIME));
+                wattroff(window->layout->win, theme_attrs(THEME_TIME));
             }
         }
         g_free(date_fmt);
@@ -781,35 +773,35 @@ _win_print(ProfWin *window, const char show_char, GDateTime *time,
             colour = 0;
         }
 
-        wattron(window->win, colour);
+        wattron(window->layout->win, colour);
         if (strncmp(message, "/me ", 4) == 0) {
-            wprintw(window->win, "*%s ", from);
+            wprintw(window->layout->win, "*%s ", from);
             offset = 4;
             me_message = TRUE;
         } else {
-            wprintw(window->win, "%s: ", from);
-            wattroff(window->win, colour);
+            wprintw(window->layout->win, "%s: ", from);
+            wattroff(window->layout->win, colour);
         }
     }
 
     if (!me_message) {
-        wattron(window->win, theme_attrs(theme_item));
+        wattron(window->layout->win, theme_attrs(theme_item));
     }
 
     if (prefs_get_boolean(PREF_WRAP)) {
-        _win_print_wrapped(window->win, message+offset);
+        _win_print_wrapped(window->layout->win, message+offset);
     } else {
-        wprintw(window->win, "%s", message+offset);
+        wprintw(window->layout->win, "%s", message+offset);
     }
 
     if ((flags & NO_EOL) == 0) {
-        wprintw(window->win, "\n");
+        wprintw(window->layout->win, "\n");
     }
 
     if (me_message) {
-        wattroff(window->win, colour);
+        wattroff(window->layout->win, colour);
     } else {
-        wattroff(window->win, theme_attrs(theme_item));
+        wattroff(window->layout->win, theme_attrs(theme_item));
     }
 }
 
@@ -886,15 +878,59 @@ void
 win_redraw(ProfWin *window)
 {
     int i, size;
-    werase(window->win);
-    size = buffer_size(window->buffer);
+    werase(window->layout->win);
+    size = buffer_size(window->layout->buffer);
 
     for (i = 0; i < size; i++) {
-        ProfBuffEntry *e = buffer_yield_entry(window->buffer, i);
+        ProfBuffEntry *e = buffer_yield_entry(window->layout->buffer, i);
         _win_print(window, e->show_char, e->time, e->flags, e->theme_item, e->from, e->message);
     }
 }
 
+gboolean
+win_has_active_subwin(ProfWin *window)
+{
+    if (window->layout->type == LAYOUT_SPLIT) {
+        ProfLayoutSplit *layout = (ProfLayoutSplit*)window->layout;
+        return (layout->subwin != NULL);
+    } else {
+        return FALSE;
+    }
+}
+
+gboolean
+win_chat_history_shown(ProfWin *window)
+{
+    if (window->type == WIN_CHAT) {
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        return chatwin->history_shown;
+    } else {
+        return FALSE;
+    }
+}
+
+gboolean
+win_has_chat_resource(ProfWin *window)
+{
+    if (window->type == WIN_CHAT) {
+        ProfChatWin *chatwin = (ProfChatWin*)window;
+        return (chatwin->resource != NULL);
+    } else {
+        return FALSE;
+    }
+}
+
+gboolean
+win_has_modified_form(ProfWin *window)
+{
+    if (window->type == WIN_MUC_CONFIG) {
+        ProfMucConfWin *confwin = (ProfMucConfWin*)window;
+        return confwin->form->modified;
+    } else {
+        return FALSE;
+    }
+}
+
 void
 win_printline_nowrap(WINDOW *win, char *msg)
 {
diff --git a/src/ui/window.h b/src/ui/window.h
index 65dcb4f7..52e1e80f 100644
--- a/src/ui/window.h
+++ b/src/ui/window.h
@@ -57,6 +57,29 @@
 #define PAD_SIZE 1000
 
 typedef enum {
+    LAYOUT_SINGLE,
+    LAYOUT_SPLIT
+} layout_type_t;
+
+typedef struct prof_layout_t {
+    layout_type_t type;
+    WINDOW *win;
+    ProfBuff buffer;
+    int y_pos;
+    int paged;
+} ProfLayout;
+
+typedef struct prof_layout_single_t {
+    ProfLayout super;
+} ProfLayoutSingle;
+
+typedef struct prof_layout_split_t {
+    ProfLayout super;
+    WINDOW *subwin;
+    int sub_y_pos;
+} ProfLayoutSplit;
+
+typedef enum {
     WIN_CONSOLE,
     WIN_CHAT,
     WIN_MUC,
@@ -67,49 +90,40 @@ typedef enum {
 
 typedef struct prof_win_t {
     win_type_t type;
-
-    WINDOW *win;
-    ProfBuff buffer;
+    ProfLayout *layout;
     char *from;
-    int y_pos;
-    int paged;
     int unread;
-    union {
-        // WIN_CONSOLE
-        struct {
-            WINDOW *subwin;
-            int sub_y_pos;
-        } cons;
-
-        // WIN_CHAT
-        struct {
-            gboolean is_otr;
-            gboolean is_trusted;
-            char *resource;
-            gboolean history_shown;
-        } chat;
-
-        // WIN_MUC
-        struct {
-            WINDOW *subwin;
-            int sub_y_pos;
-        } muc;
-
-        // WIN_MUC_CONFIG
-        struct {
-            DataForm *form;
-        } conf;
-
-        // WIN_PRIVATE
-        struct {
-        } priv;
-
-        // WIN_XML
-        struct {
-        } xml;
-    } wins;
 } ProfWin;
 
+typedef struct prof_console_win_t {
+    ProfWin super;
+} ProfConsoleWin;
+
+typedef struct prof_chat_win_t {
+    ProfWin super;
+    gboolean is_otr;
+    gboolean is_trusted;
+    char *resource;
+    gboolean history_shown;
+} ProfChatWin;
+
+typedef struct prof_muc_win_t {
+    ProfWin super;
+} ProfMucWin;
+
+typedef struct prof_mucconf_win_t {
+    ProfWin super;
+    DataForm *form;
+} ProfMucConfWin;
+
+typedef struct prof_private_win_t {
+    ProfWin super;
+} ProfPrivateWin;
+
+typedef struct prof_xml_win_t {
+    ProfWin super;
+} ProfXMLWin;
+
 ProfWin* win_create_console(void);
 ProfWin* win_create_chat(const char * const barejid);
 ProfWin* win_create_muc(const char * const roomjid);
@@ -143,4 +157,9 @@ void win_printline_nowrap(WINDOW *win, char *msg);
 gboolean win_is_otr(ProfWin *window);
 gboolean win_is_trusted(ProfWin *window);
 
+gboolean win_has_active_subwin(ProfWin *window);
+gboolean win_has_modified_form(ProfWin *window);
+gboolean win_chat_history_shown(ProfWin *window);
+gboolean win_has_chat_resource(ProfWin *window);
+
 #endif
diff --git a/src/ui/windows.c b/src/ui/windows.c
index 6d7e1f50..99c17252 100644
--- a/src/ui/windows.c
+++ b/src/ui/windows.c
@@ -237,7 +237,7 @@ void
 wins_clear_current(void)
 {
     ProfWin *window = wins_get_current();
-    werase(window->win);
+    werase(window->layout->win);
     win_update_virtual(window);
 }
 
@@ -335,30 +335,18 @@ wins_resize_all(void)
         ProfWin *window = curr->data;
         int subwin_cols = 0;
 
-        switch (window->type) {
-        case WIN_CONSOLE:
-            if (window->wins.cons.subwin) {
+        if (window->layout->type == LAYOUT_SPLIT) {
+            ProfLayoutSplit *layout = (ProfLayoutSplit*)window;
+            if (layout->subwin) {
                 subwin_cols = win_roster_cols();
-                wresize(window->win, PAD_SIZE, cols - subwin_cols);
-                wresize(window->wins.cons.subwin, PAD_SIZE, subwin_cols);
+                wresize(layout->super.win, PAD_SIZE, cols - subwin_cols);
+                wresize(layout->subwin, PAD_SIZE, subwin_cols);
                 ui_roster();
             } else {
-                wresize(window->win, PAD_SIZE, cols);
+                wresize(layout->super.win, PAD_SIZE, cols);
             }
-            break;
-        case WIN_MUC:
-            if (window->wins.muc.subwin) {
-                subwin_cols = win_occpuants_cols();
-                wresize(window->win, PAD_SIZE, cols - subwin_cols);
-                wresize(window->wins.muc.subwin, PAD_SIZE, subwin_cols);
-                ui_muc_roster(window->from);
-            } else {
-                wresize(window->win, PAD_SIZE, cols);
-            }
-            break;
-        default:
-            wresize(window->win, PAD_SIZE, cols);
-            break;
+        } else {
+            wresize(window->layout->win, PAD_SIZE, cols);
         }
 
         win_redraw(window);
@@ -380,7 +368,7 @@ wins_hide_subwin(ProfWin *window)
 
     ProfWin *current_win = wins_get_current();
     if ((current_win->type == WIN_MUC) || (current_win->type == WIN_CONSOLE)) {
-        pnoutrefresh(current_win->win, current_win->y_pos, 0, 1, 0, rows-3, cols-1);
+        pnoutrefresh(current_win->layout->win, current_win->layout->y_pos, 0, 1, 0, rows-3, cols-1);
     }
 }
 
@@ -395,13 +383,15 @@ wins_show_subwin(ProfWin *window)
 
     ProfWin *current_win = wins_get_current();
     if (current_win->type == WIN_MUC) {
+        ProfLayoutSplit *layout = (ProfLayoutSplit*)current_win->layout;
         subwin_cols = win_occpuants_cols();
-        pnoutrefresh(current_win->win, current_win->y_pos, 0, 1, 0, rows-3, (cols-subwin_cols)-1);
-        pnoutrefresh(current_win->wins.muc.subwin, current_win->wins.muc.sub_y_pos, 0, 1, (cols-subwin_cols), rows-3, cols-1);
+        pnoutrefresh(layout->super.win, layout->super.y_pos, 0, 1, 0, rows-3, (cols-subwin_cols)-1);
+        pnoutrefresh(layout->subwin, layout->sub_y_pos, 0, 1, (cols-subwin_cols), rows-3, cols-1);
     } else if (current_win->type == WIN_CONSOLE) {
+        ProfLayoutSplit *layout = (ProfLayoutSplit*)current_win->layout;
         subwin_cols = win_roster_cols();
-        pnoutrefresh(current_win->win, current_win->y_pos, 0, 1, 0, rows-3, (cols-subwin_cols)-1);
-        pnoutrefresh(current_win->wins.cons.subwin, current_win->wins.cons.sub_y_pos, 0, 1, (cols-subwin_cols), rows-3, cols-1);
+        pnoutrefresh(layout->super.win, layout->super.y_pos, 0, 1, 0, rows-3, (cols-subwin_cols)-1);
+        pnoutrefresh(layout->subwin, layout->sub_y_pos, 0, 1, (cols-subwin_cols), rows-3, cols-1);
     }
 }
 
@@ -698,7 +688,7 @@ wins_create_summary(void)
             case WIN_MUC_CONFIG:
                 muc_config_string = g_string_new("");
                 g_string_printf(muc_config_string, "%d: %s", ui_index, window->from);
-                if ((window->wins.conf.form) && (window->wins.conf.form->modified)) {
+                if (win_has_modified_form(window)) {
                     g_string_append(muc_config_string, " *");
                 }
                 result = g_slist_append(result, strdup(muc_config_string->str));