diff --git a/src/command/command.c b/src/command/command.c index d2d7f4ab..97d2dfc6 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -2266,6 +2266,7 @@ cmd_init(void) autocomplete_add(roster_show_ac, "empty"); autocomplete_add(roster_show_ac, "priority"); autocomplete_add(roster_show_ac, "contacts"); + autocomplete_add(roster_show_ac, "unsubscribed"); autocomplete_add(roster_show_ac, "rooms"); roster_by_ac = autocomplete_new(); diff --git a/src/command/commands.c b/src/command/commands.c index 6570f13d..6a9972b6 100644 --- a/src/command/commands.c +++ b/src/command/commands.c @@ -2245,6 +2245,13 @@ cmd_roster(ProfWin *window, const char *const command, gchar **args) rosterwin_roster(); } return TRUE; + } else if (g_strcmp0(args[1], "unsubscribed") == 0) { + cons_show("Roster unsubscribed enabled"); + prefs_set_boolean(PREF_ROSTER_UNSUBSCRIBED, TRUE); + if (conn_status == JABBER_CONNECTED) { + rosterwin_roster(); + } + return TRUE; } else { cons_bad_cmd_usage(command); return TRUE; @@ -2313,6 +2320,13 @@ cmd_roster(ProfWin *window, const char *const command, gchar **args) rosterwin_roster(); } return TRUE; + } else if (g_strcmp0(args[1], "unsubscribed") == 0) { + cons_show("Roster unsubscribed disabled"); + prefs_set_boolean(PREF_ROSTER_UNSUBSCRIBED, FALSE); + if (conn_status == JABBER_CONNECTED) { + rosterwin_roster(); + } + return TRUE; } else { cons_bad_cmd_usage(command); return TRUE; diff --git a/src/config/preferences.c b/src/config/preferences.c index 8642e20a..12870bb1 100644 --- a/src/config/preferences.c +++ b/src/config/preferences.c @@ -1143,6 +1143,7 @@ _get_group(preference_t pref) case PREF_ROSTER_WRAP: case PREF_ROSTER_RESOURCE_JOIN: case PREF_ROSTER_CONTACTS: + case PREF_ROSTER_UNSUBSCRIBED: case PREF_ROSTER_ROOMS: case PREF_ROSTER_ROOMS_POS: case PREF_ROSTER_ROOMS_BY: @@ -1351,6 +1352,8 @@ _get_key(preference_t pref) return "roster.resource.join"; case PREF_ROSTER_CONTACTS: return "roster.contacts"; + case PREF_ROSTER_UNSUBSCRIBED: + return "roster.unsubscribed"; case PREF_ROSTER_ROOMS: return "roster.rooms"; case PREF_ROSTER_ROOMS_POS: @@ -1425,6 +1428,7 @@ _get_default_boolean(preference_t pref) case PREF_ROSTER_PRIORITY: case PREF_ROSTER_RESOURCE_JOIN: case PREF_ROSTER_CONTACTS: + case PREF_ROSTER_UNSUBSCRIBED: case PREF_ROSTER_ROOMS: case PREF_TLS_SHOW: case PREF_LASTACTIVITY: diff --git a/src/config/preferences.h b/src/config/preferences.h index 8afb2707..a89ee65b 100644 --- a/src/config/preferences.h +++ b/src/config/preferences.h @@ -76,6 +76,7 @@ typedef enum { PREF_ROSTER_WRAP, PREF_ROSTER_RESOURCE_JOIN, PREF_ROSTER_CONTACTS, + PREF_ROSTER_UNSUBSCRIBED, PREF_ROSTER_ROOMS, PREF_ROSTER_ROOMS_POS, PREF_ROSTER_ROOMS_BY, diff --git a/src/config/theme.c b/src/config/theme.c index 1c4a3698..6a235f6d 100644 --- a/src/config/theme.c +++ b/src/config/theme.c @@ -391,6 +391,7 @@ _load_preferences(void) _set_boolean_preference("roster.count.zero", PREF_ROSTER_COUNT_ZERO); _set_boolean_preference("roster.priority", PREF_ROSTER_PRIORITY); _set_boolean_preference("roster.contacts", PREF_ROSTER_CONTACTS); + _set_boolean_preference("roster.unsubscribed", PREF_ROSTER_UNSUBSCRIBED); _set_boolean_preference("roster.rooms", PREF_ROSTER_ROOMS); _set_boolean_preference("privileges", PREF_MUC_PRIVILEGES); _set_boolean_preference("presence", PREF_PRESENCE); diff --git a/src/ui/console.c b/src/ui/console.c index be6246c8..55b1e25f 100644 --- a/src/ui/console.c +++ b/src/ui/console.c @@ -1410,6 +1410,11 @@ cons_roster_setting(void) else cons_show("Roster contacts (/roster) : hide"); + if (prefs_get_boolean(PREF_ROSTER_UNSUBSCRIBED)) + cons_show("Roster unsubscribed (/roster) : show"); + else + cons_show("Roster unsubscribed (/roster) : hide"); + char *count = prefs_get_string(PREF_ROSTER_COUNT); if (g_strcmp0(count, "off") == 0) { cons_show("Roster count (/roster) : OFF"); diff --git a/src/ui/rosterwin.c b/src/ui/rosterwin.c index 6d25b6ec..964b006c 100644 --- a/src/ui/rosterwin.c +++ b/src/ui/rosterwin.c @@ -53,9 +53,12 @@ static void _rosterwin_contacts_all(ProfLayoutSplit *layout, gboolean newline); static void _rosterwin_contacts_by_presence(ProfLayoutSplit *layout, const char *const presence, char *title, gboolean newline); static void _rosterwin_contacts_by_group(ProfLayoutSplit *layout, char *group, gboolean newline); +static void _rosteriwin_unsubscribed(ProfLayoutSplit *layout, gboolean newline); static void _rosterwin_contacts_header(ProfLayoutSplit *layout, const char *title, gboolean newline, GSList *contacts); +static void _rosterwin_unsubscribed_header(ProfLayoutSplit *layout, gboolean newline, GList *wins); static void _rosterwin_contact(ProfLayoutSplit *layout, PContact contact); +static void _rosterwin_unsubscribed_item(ProfLayoutSplit *layout, ProfChatWin *chatwin); static void _rosterwin_presence(ProfLayoutSplit *layout, const char *presence, const char *status, int current_indent); static void _rosterwin_resources(ProfLayoutSplit *layout, PContact contact, int current_indent, @@ -149,6 +152,10 @@ rosterwin_roster(void) } else { _rosterwin_contacts_all(layout, newline); } + + if (prefs_get_boolean(PREF_ROSTER_UNSUBSCRIBED)) { + _rosteriwin_unsubscribed(layout, newline); + } prefs_free_string(by); } @@ -215,6 +222,24 @@ _rosterwin_contacts_all(ProfLayoutSplit *layout, gboolean newline) g_slist_free(filtered_contacts); } +static void +_rosteriwin_unsubscribed(ProfLayoutSplit *layout, gboolean newline) +{ + GList *wins = wins_get_chat_unsubscribed(); + if (wins) { + _rosterwin_unsubscribed_header(layout, newline, wins); + } + + GList *curr = wins; + while (curr) { + ProfChatWin *chatwin = curr->data; + _rosterwin_unsubscribed_item(layout, chatwin); + curr = g_list_next(curr); + } + + g_list_free(wins); +} + static void _rosterwin_contacts_by_presence(ProfLayoutSplit *layout, const char *const presence, char *title, gboolean newline) { @@ -271,6 +296,58 @@ _rosterwin_contacts_by_group(ProfLayoutSplit *layout, char *group, gboolean newl g_slist_free(filtered_contacts); } +static void +_rosterwin_unsubscribed_item(ProfLayoutSplit *layout, ProfChatWin *chatwin) +{ + const char *const name = chatwin->barejid; + const char *const presence = "offline"; + int unread = 0; + + roster_contact_theme_t theme_type = ROSTER_CONTACT; + if (chatwin->unread > 0) { + theme_type = ROSTER_CONTACT_UNREAD; + unread = chatwin->unread; + } else { + theme_type = ROSTER_CONTACT_ACTIVE; + } + + theme_item_t presence_colour = _get_roster_theme(theme_type, presence); + + wattron(layout->subwin, theme_attrs(presence_colour)); + GString *msg = g_string_new(" "); + int indent = prefs_get_roster_contact_indent(); + int current_indent = 0; + if (indent > 0) { + current_indent += indent; + while (indent > 0) { + g_string_append(msg, " "); + indent--; + } + } + char ch = prefs_get_roster_contact_char(); + if (ch) { + g_string_append_printf(msg, "%c", ch); + } + + char *unreadpos = prefs_get_string(PREF_ROSTER_UNREAD); + if ((g_strcmp0(unreadpos, "before") == 0) && unread > 0) { + g_string_append_printf(msg, "(%d) ", unread); + unread = 0; + } + g_string_append(msg, name); + if ((g_strcmp0(unreadpos, "after") == 0) && unread > 0) { + g_string_append_printf(msg, " (%d)", unread); + unread = 0; + } + prefs_free_string(unreadpos); + + win_sub_newline_lazy(layout->subwin); + gboolean wrap = prefs_get_boolean(PREF_ROSTER_WRAP); + win_sub_print(layout->subwin, msg->str, FALSE, wrap, current_indent); + g_string_free(msg, TRUE); + wattroff(layout->subwin, theme_attrs(presence_colour)); +} + static void _rosterwin_contact(ProfLayoutSplit *layout, PContact contact) { @@ -888,6 +965,54 @@ _compare_rooms_unread(ProfMucWin *a, ProfMucWin *b) } } +static void +_rosterwin_unsubscribed_header(ProfLayoutSplit *layout, gboolean newline, GList *wins) +{ + if (newline) { + win_sub_newline_lazy(layout->subwin); + } + + GString *header = g_string_new(" "); + char ch = prefs_get_roster_header_char(); + if (ch) { + g_string_append_printf(header, "%c", ch); + } + + g_string_append(header, "Unsubscribed"); + + char *countpref = prefs_get_string(PREF_ROSTER_COUNT); + if (g_strcmp0(countpref, "items") == 0) { + int itemcount = g_list_length(wins); + if (itemcount == 0 && prefs_get_boolean(PREF_ROSTER_COUNT_ZERO)) { + g_string_append_printf(header, " (%d)", itemcount); + } else { + g_string_append_printf(header, " (%d)", itemcount); + } + } else if (g_strcmp0(countpref, "unread") == 0) { + int unreadcount = 0; + GList *curr = wins; + while (curr) { + ProfChatWin *chatwin = curr->data; + unreadcount += chatwin->unread; + curr = g_list_next(curr); + } + if (unreadcount == 0 && prefs_get_boolean(PREF_ROSTER_COUNT_ZERO)) { + g_string_append_printf(header, " (%d)", unreadcount); + } else if (unreadcount > 0) { + g_string_append_printf(header, " (%d)", unreadcount); + } + } + prefs_free_string(countpref); + + gboolean wrap = prefs_get_boolean(PREF_ROSTER_WRAP); + + wattron(layout->subwin, theme_attrs(THEME_ROSTER_HEADER)); + win_sub_print(layout->subwin, header->str, FALSE, wrap, 1); + wattroff(layout->subwin, theme_attrs(THEME_ROSTER_HEADER)); + + g_string_free(header, TRUE); +} + static void _rosterwin_contacts_header(ProfLayoutSplit *layout, const char *const title, gboolean newline, GSList *contacts) { diff --git a/src/window_list.c b/src/window_list.c index 97155294..b275fe63 100644 --- a/src/window_list.c +++ b/src/window_list.c @@ -106,6 +106,35 @@ wins_get_chat(const char *const barejid) return NULL; } +static gint +_cmp_unsubscribed_wins(ProfChatWin *a, ProfChatWin *b) +{ + return g_strcmp0(a->barejid, b->barejid); +} + +GList* +wins_get_chat_unsubscribed(void) +{ + GList *result = NULL; + GList *values = g_hash_table_get_values(windows); + GList *curr = values; + + while (curr) { + ProfWin *window = curr->data; + if (window->type == WIN_CHAT) { + ProfChatWin *chatwin = (ProfChatWin*)window; + PContact contact = roster_get_contact(chatwin->barejid); + if (contact == NULL) { + result = g_list_insert_sorted(result, chatwin, (GCompareFunc)_cmp_unsubscribed_wins); + } + } + curr = g_list_next(curr); + } + + g_list_free(values); + return result; +} + ProfMucConfWin* wins_get_muc_conf(const char *const roomjid) { diff --git a/src/window_list.h b/src/window_list.h index 46bac234..767dce1f 100644 --- a/src/window_list.h +++ b/src/window_list.h @@ -53,6 +53,7 @@ void wins_remove_nick(const char *const barejid, const char *const oldnick); ProfWin* wins_get_console(void); ProfChatWin* wins_get_chat(const char *const barejid); +GList* wins_get_chat_unsubscribed(void); ProfMucWin* wins_get_muc(const char *const roomjid); ProfMucConfWin* wins_get_muc_conf(const char *const roomjid); ProfPrivateWin* wins_get_private(const char *const fulljid); diff --git a/theme_template b/theme_template index bfcd498d..88c486fd 100644 --- a/theme_template +++ b/theme_template @@ -115,6 +115,7 @@ roster.presence= roster.presence.indent= roster.status= roster.contacts= +roster.unsubscribed= roster.rooms= roster.rooms.order= roster.rooms.unread= diff --git a/themes/boothj5 b/themes/boothj5 index 4d221a77..cf45e916 100644 --- a/themes/boothj5 +++ b/themes/boothj5 @@ -113,6 +113,7 @@ roster.presence=true roster.presence.indent=-1 roster.status=true roster.contacts=true +roster.unsubscribed=true roster.rooms=true roster.rooms.order=name roster.rooms.unread=after diff --git a/themes/boothj5_slack b/themes/boothj5_slack index 7a9fd5aa..2254b949 100644 --- a/themes/boothj5_slack +++ b/themes/boothj5_slack @@ -109,6 +109,7 @@ roster.resource=false roster.presence=false roster.status=true roster.contacts=true +roster.unsubscribed=true roster.rooms=true roster.rooms.order=name roster.rooms.unread=after diff --git a/themes/complex b/themes/complex index 6fc03af2..258a7776 100644 --- a/themes/complex +++ b/themes/complex @@ -38,6 +38,7 @@ roster.presence=true roster.presence.indent=1 roster.status=true roster.contacts=true +roster.unsubscribed=true roster.rooms=true roster.rooms.order=unread roster.rooms.unread=after diff --git a/themes/simple b/themes/simple index 7e3a0d8e..b63017ab 100644 --- a/themes/simple +++ b/themes/simple @@ -31,6 +31,7 @@ roster.resource=false roster.presence=false roster.status=false roster.contacts=true +roster.unsubscribed=false roster.rooms=false roster.rooms.unread=off roster.count=off