about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/command/command.c15
-rw-r--r--src/command/commands.c20
-rw-r--r--src/config/preferences.c5
-rw-r--r--src/config/preferences.h1
-rw-r--r--src/roster_list.c116
-rw-r--r--src/roster_list.h3
-rw-r--r--src/ui/console.c4
-rw-r--r--src/ui/rosterwin.c30
8 files changed, 184 insertions, 10 deletions
diff --git a/src/command/command.c b/src/command/command.c
index 4241acb4..2a98ba75 100644
--- a/src/command/command.c
+++ b/src/command/command.c
@@ -277,6 +277,7 @@ static struct cmd_t command_defs[] =
             "/roster show [offline|resource|presence|status|empty]",
             "/roster hide [offline|resource|presence|status|empty]",
             "/roster by group|presence|none",
+            "/roster order name|presence"
             "/roster size <percent>",
             "/roster add <jid> [<nick>]",
             "/roster remove <jid>",
@@ -303,6 +304,8 @@ static struct cmd_t command_defs[] =
             { "by group",            "Group contacts in the roster panel by roster group." },
             { "by presence",         "Group contacts in the roster panel by presence." },
             { "by none",             "No grouping in the roster panel." },
+            { "order name",          "Order roster items by name only." },
+            { "order presence",      "Order roster items by presence, and then by name." },
             { "size <precent>",      "Percentage of the screen taken up by the roster (1-99)." },
             { "add <jid> [<nick>]",  "Add a new item to the roster." },
             { "remove <jid>",        "Removes an item from the roster." },
@@ -1771,6 +1774,7 @@ static Autocomplete wins_ac;
 static Autocomplete roster_ac;
 static Autocomplete roster_option_ac;
 static Autocomplete roster_by_ac;
+static Autocomplete roster_order_ac;
 static Autocomplete roster_remove_all_ac;
 static Autocomplete group_ac;
 static Autocomplete bookmark_ac;
@@ -2014,6 +2018,7 @@ cmd_init(void)
     autocomplete_add(roster_ac, "show");
     autocomplete_add(roster_ac, "hide");
     autocomplete_add(roster_ac, "by");
+    autocomplete_add(roster_ac, "order");
     autocomplete_add(roster_ac, "size");
 
     roster_option_ac = autocomplete_new();
@@ -2028,6 +2033,10 @@ cmd_init(void)
     autocomplete_add(roster_by_ac, "presence");
     autocomplete_add(roster_by_ac, "none");
 
+    roster_order_ac = autocomplete_new();
+    autocomplete_add(roster_order_ac, "name");
+    autocomplete_add(roster_order_ac, "presence");
+
     roster_remove_all_ac = autocomplete_new();
     autocomplete_add(roster_remove_all_ac, "contacts");
 
@@ -2282,6 +2291,7 @@ cmd_uninit(void)
     autocomplete_free(roster_ac);
     autocomplete_free(roster_option_ac);
     autocomplete_free(roster_by_ac);
+    autocomplete_free(roster_order_ac);
     autocomplete_free(roster_remove_all_ac);
     autocomplete_free(group_ac);
     autocomplete_free(bookmark_ac);
@@ -2468,6 +2478,7 @@ cmd_reset_autocomplete(ProfWin *window)
     autocomplete_reset(roster_ac);
     autocomplete_reset(roster_option_ac);
     autocomplete_reset(roster_by_ac);
+    autocomplete_reset(roster_order_ac);
     autocomplete_reset(roster_remove_all_ac);
     autocomplete_reset(group_ac);
     autocomplete_reset(titlebar_ac);
@@ -2881,6 +2892,10 @@ _roster_autocomplete(ProfWin *window, const char *const input)
     if (result) {
         return result;
     }
+    result = autocomplete_param_with_ac(input, "/roster order", roster_order_ac, TRUE);
+    if (result) {
+        return result;
+    }
     result = autocomplete_param_with_ac(input, "/roster", roster_ac, TRUE);
     if (result) {
         return result;
diff --git a/src/command/commands.c b/src/command/commands.c
index 40a041c9..67f8ae98 100644
--- a/src/command/commands.c
+++ b/src/command/commands.c
@@ -1872,6 +1872,26 @@ cmd_roster(ProfWin *window, const char *const command, gchar **args)
             cons_bad_cmd_usage(command);
             return TRUE;
         }
+    // roster item order
+    } else if (g_strcmp0(args[0], "order") == 0) {
+        if (g_strcmp0(args[1], "name") == 0) {
+            cons_show("Ordering roster by name");
+            prefs_set_string(PREF_ROSTER_ORDER, "name");
+            if (conn_status == JABBER_CONNECTED) {
+                rosterwin_roster();
+            }
+            return TRUE;
+        } else if (g_strcmp0(args[1], "presence") == 0) {
+            cons_show("Ordering roster by presence");
+            prefs_set_string(PREF_ROSTER_ORDER, "presence");
+            if (conn_status == JABBER_CONNECTED) {
+                rosterwin_roster();
+            }
+            return TRUE;
+        } else {
+            cons_bad_cmd_usage(command);
+            return TRUE;
+        }
     // add contact
     } else if (strcmp(args[0], "add") == 0) {
         if (conn_status != JABBER_CONNECTED) {
diff --git a/src/config/preferences.c b/src/config/preferences.c
index b8f6d607..8348edb7 100644
--- a/src/config/preferences.c
+++ b/src/config/preferences.c
@@ -601,6 +601,7 @@ _get_group(preference_t pref)
         case PREF_ROSTER_STATUS:
         case PREF_ROSTER_EMPTY:
         case PREF_ROSTER_BY:
+        case PREF_ROSTER_ORDER:
         case PREF_RESOURCE_TITLE:
         case PREF_RESOURCE_MESSAGE:
         case PREF_ENC_WARN:
@@ -778,6 +779,8 @@ _get_key(preference_t pref)
             return "roster.empty";
         case PREF_ROSTER_BY:
             return "roster.by";
+        case PREF_ROSTER_ORDER:
+            return "roster.order";
         case PREF_RESOURCE_TITLE:
             return "resource.title";
         case PREF_RESOURCE_MESSAGE:
@@ -861,6 +864,8 @@ _get_default_string(preference_t pref)
             return "all";
         case PREF_ROSTER_BY:
             return "presence";
+        case PREF_ROSTER_ORDER:
+            return "name";
         case PREF_TIME_CONSOLE:
             return "%H:%M:%S";
         case PREF_TIME_CHAT:
diff --git a/src/config/preferences.h b/src/config/preferences.h
index ff830cce..55b46b07 100644
--- a/src/config/preferences.h
+++ b/src/config/preferences.h
@@ -68,6 +68,7 @@ typedef enum {
     PREF_ROSTER_STATUS,
     PREF_ROSTER_EMPTY,
     PREF_ROSTER_BY,
+    PREF_ROSTER_ORDER,
     PREF_MUC_PRIVILEGES,
     PREF_PRESENCE,
     PREF_WRAP,
diff --git a/src/roster_list.c b/src/roster_list.c
index 0ba20107..fe3063dd 100644
--- a/src/roster_list.c
+++ b/src/roster_list.c
@@ -67,7 +67,8 @@ static gboolean _key_equals(void *key1, void *key2);
 static gboolean _datetimes_equal(GDateTime *dt1, GDateTime *dt2);
 static void _replace_name(const char *const current_name, const char *const new_name, const char *const barejid);
 static void _add_name_and_barejid(const char *const name, const char *const barejid);
-static gint _compare_contacts(PContact a, PContact b);
+static gint _compare_name(PContact a, PContact b);
+static gint _compare_presence(PContact a, PContact b);
 
 void
 roster_clear(void)
@@ -373,7 +374,7 @@ roster_get_contacts_by_presence(const char *const presence)
     while (g_hash_table_iter_next(&iter, &key, &value)) {
         PContact contact = (PContact)value;
         if (g_strcmp0(p_contact_presence(contact), presence) == 0) {
-            result = g_slist_insert_sorted(result, value, (GCompareFunc)_compare_contacts);
+            result = g_slist_insert_sorted(result, value, (GCompareFunc)_compare_name);
         }
     }
 
@@ -382,6 +383,23 @@ roster_get_contacts_by_presence(const char *const presence)
 }
 
 GSList*
+roster_get_contacts_ord_presence(void)
+{
+    GSList *result = NULL;
+    GHashTableIter iter;
+    gpointer key;
+    gpointer value;
+
+    g_hash_table_iter_init(&iter, contacts);
+    while (g_hash_table_iter_next(&iter, &key, &value)) {
+        result = g_slist_insert_sorted(result, value, (GCompareFunc)_compare_presence);
+    }
+
+    // return all contact structs
+    return result;
+}
+
+GSList*
 roster_get_contacts(void)
 {
     GSList *result = NULL;
@@ -391,7 +409,7 @@ roster_get_contacts(void)
 
     g_hash_table_iter_init(&iter, contacts);
     while (g_hash_table_iter_next(&iter, &key, &value)) {
-        result = g_slist_insert_sorted(result, value, (GCompareFunc)_compare_contacts);
+        result = g_slist_insert_sorted(result, value, (GCompareFunc)_compare_name);
     }
 
     // return all contact structs
@@ -409,7 +427,7 @@ roster_get_contacts_online(void)
     g_hash_table_iter_init(&iter, contacts);
     while (g_hash_table_iter_next(&iter, &key, &value)) {
         if(strcmp(p_contact_presence(value), "offline"))
-            result = g_slist_insert_sorted(result, value, (GCompareFunc)_compare_contacts);
+            result = g_slist_insert_sorted(result, value, (GCompareFunc)_compare_name);
     }
 
     // return all contact structs
@@ -447,6 +465,26 @@ roster_fulljid_autocomplete(const char *const search_str)
 }
 
 GSList*
+roster_get_nogroup_ord_presence(void)
+{
+    GSList *result = NULL;
+    GHashTableIter iter;
+    gpointer key;
+    gpointer value;
+
+    g_hash_table_iter_init(&iter, contacts);
+    while (g_hash_table_iter_next(&iter, &key, &value)) {
+        GSList *groups = p_contact_groups(value);
+        if (groups == NULL) {
+            result = g_slist_insert_sorted(result, value, (GCompareFunc)_compare_presence);
+        }
+    }
+
+    // return all contact structs
+    return result;
+}
+
+GSList*
 roster_get_nogroup(void)
 {
     GSList *result = NULL;
@@ -458,7 +496,31 @@ roster_get_nogroup(void)
     while (g_hash_table_iter_next(&iter, &key, &value)) {
         GSList *groups = p_contact_groups(value);
         if (groups == NULL) {
-            result = g_slist_insert_sorted(result, value, (GCompareFunc)_compare_contacts);
+            result = g_slist_insert_sorted(result, value, (GCompareFunc)_compare_name);
+        }
+    }
+
+    // return all contact structs
+    return result;
+}
+
+GSList*
+roster_get_group_ord_presence(const char *const group)
+{
+    GSList *result = NULL;
+    GHashTableIter iter;
+    gpointer key;
+    gpointer value;
+
+    g_hash_table_iter_init(&iter, contacts);
+    while (g_hash_table_iter_next(&iter, &key, &value)) {
+        GSList *groups = p_contact_groups(value);
+        while (groups) {
+            if (strcmp(groups->data, group) == 0) {
+                result = g_slist_insert_sorted(result, value, (GCompareFunc)_compare_presence);
+                break;
+            }
+            groups = g_slist_next(groups);
         }
     }
 
@@ -479,7 +541,7 @@ roster_get_group(const char *const group)
         GSList *groups = p_contact_groups(value);
         while (groups) {
             if (strcmp(groups->data, group) == 0) {
-                result = g_slist_insert_sorted(result, value, (GCompareFunc)_compare_contacts);
+                result = g_slist_insert_sorted(result, value, (GCompareFunc)_compare_name);
                 break;
             }
             groups = g_slist_next(groups);
@@ -560,7 +622,7 @@ _add_name_and_barejid(const char *const name, const char *const barejid)
 }
 
 static gint
-_compare_contacts(PContact a, PContact b)
+_compare_name(PContact a, PContact b)
 {
     const char * utf8_str_a = NULL;
     const char * utf8_str_b = NULL;
@@ -580,3 +642,43 @@ _compare_contacts(PContact a, PContact b)
 
     return result;
 }
+
+static gint
+_get_presence_weight(const char *presence)
+{
+    if (g_strcmp0(presence, "chat") == 0) {
+        return 0;
+    } else if (g_strcmp0(presence, "online") == 0) {
+        return 1;
+    } else if (g_strcmp0(presence, "away") == 0) {
+        return 2;
+    } else if (g_strcmp0(presence, "xa") == 0) {
+        return 3;
+    } else if (g_strcmp0(presence, "dnd") == 0) {
+        return 4;
+    } else { // offline
+        return 5;
+    }
+}
+
+static gint
+_compare_presence(PContact a, PContact b)
+{
+    const char *presence_a = p_contact_presence(a);
+    const char *presence_b = p_contact_presence(b);
+
+    // if presence different, order by presence
+    if (g_strcmp0(presence_a, presence_b) != 0) {
+        int weight_a = _get_presence_weight(presence_a);
+        int weight_b = _get_presence_weight(presence_b);
+        if (weight_a < weight_b) {
+            return -1;
+        } else {
+            return 1;
+        }
+
+    // otherwise order by name
+    } else {
+        return _compare_name(a, b);
+    }
+}
diff --git a/src/roster_list.h b/src/roster_list.h
index 01def408..da4bbbcd 100644
--- a/src/roster_list.h
+++ b/src/roster_list.h
@@ -55,16 +55,19 @@ gboolean roster_add(const char *const barejid, const char *const name, GSList *g
     gboolean pending_out);
 char* roster_barejid_from_name(const char *const name);
 GSList* roster_get_contacts(void);
+GSList* roster_get_contacts_ord_presence(void);
 GSList* roster_get_contacts_online(void);
 gboolean roster_has_pending_subscriptions(void);
 char* roster_contact_autocomplete(const char *const search_str);
 char* roster_fulljid_autocomplete(const char *const search_str);
 GSList* roster_get_group(const char *const group);
+GSList* roster_get_group_ord_presence(const char *const group);
 GSList* roster_get_groups(void);
 char* roster_group_autocomplete(const char *const search_str);
 char* roster_barejid_autocomplete(const char *const search_str);
 GSList* roster_get_contacts_by_presence(const char *const presence);
 GSList* roster_get_nogroup(void);
+GSList* roster_get_nogroup_ord_presence(void);
 char* roster_get_msg_display_name(const char *const barejid, const char *const resource);
 
 #endif
diff --git a/src/ui/console.c b/src/ui/console.c
index 8825cf69..882a9b54 100644
--- a/src/ui/console.c
+++ b/src/ui/console.c
@@ -1226,6 +1226,10 @@ cons_roster_setting(void)
     cons_show("Roster by (/roster)           : %s", by);
     prefs_free_string(by);
 
+    char *order = prefs_get_string(PREF_ROSTER_ORDER);
+    cons_show("Roster order (/roster)        : %s", order);
+    prefs_free_string(order);
+
     int size = prefs_get_roster_size();
     cons_show("Roster size (/roster)         : %d", size);
 }
diff --git a/src/ui/rosterwin.c b/src/ui/rosterwin.c
index b721dbfe..95d374bd 100644
--- a/src/ui/rosterwin.c
+++ b/src/ui/rosterwin.c
@@ -167,7 +167,15 @@ _rosterwin_contacts_by_group(ProfLayoutSplit *layout, char *group)
     g_string_free(title, TRUE);
     wattroff(layout->subwin, theme_attrs(THEME_ROSTER_HEADER));
 
-    GSList *contacts = roster_get_group(group);
+    GSList *contacts = NULL;
+
+    char *order = prefs_get_string(PREF_ROSTER_ORDER);
+    if (g_strcmp0(order, "presence") == 0) {
+        contacts = roster_get_group_ord_presence(group);
+    } else {
+        contacts = roster_get_group(group);
+    }
+
     if (contacts) {
         GSList *curr_contact = contacts;
         while (curr_contact) {
@@ -182,7 +190,15 @@ _rosterwin_contacts_by_group(ProfLayoutSplit *layout, char *group)
 static void
 _rosterwin_contacts_by_no_group(ProfLayoutSplit *layout)
 {
-    GSList *contacts = roster_get_nogroup();
+    GSList *contacts = NULL;
+
+    char *order = prefs_get_string(PREF_ROSTER_ORDER);
+    if (g_strcmp0(order, "presence") == 0) {
+        contacts = roster_get_nogroup_ord_presence();
+    } else {
+        contacts = roster_get_nogroup();
+    }
+
     if (contacts) {
         wattron(layout->subwin, theme_attrs(THEME_ROSTER_HEADER));
         win_printline_nowrap(layout->subwin, " -no group");
@@ -228,7 +244,15 @@ rosterwin_roster(void)
             g_slist_free_full(groups, free);
             _rosterwin_contacts_by_no_group(layout);
         } else {
-            GSList *contacts = roster_get_contacts();
+            GSList *contacts = NULL;
+
+            char *order = prefs_get_string(PREF_ROSTER_ORDER);
+            if (g_strcmp0(order, "presence") == 0) {
+                contacts = roster_get_contacts_ord_presence();
+            } else {
+                contacts = roster_get_contacts();
+            }
+
             if (contacts) {
                 werase(layout->subwin);