$ git clone http://tcclient.ion.nu/tc_client.git
commit dd21922e0c536622ab17d8de879ea758ac262d3a
Author: Alicia <...>
Date:   Fri Dec 9 16:41:36 2016 +0100

    tc_client-gtk: made the user list sorted.

diff --git a/ChangeLog b/ChangeLog
index a31f5b1..cd993f8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -23,6 +23,7 @@ tc_client-gtk: bugfix: hiding your own camera caused a segfault.
 tc_client-gtk: added an icon to mark moderators in the user list.
 tc_client-gtk: disable the input field and the broadcast menu when in "lurker" mode.
 tc_client-gtk: added an option to hide join/quit/nickname notifications.
+tc_client-gtk: made the user list sorted.
 dist/appimage.sh: fix audio in appimages by building ffmpeg with support for nellymoser and speex, and depending on the system's libao and libpulse instead of including it in the appimage.
 libcamera(escapi): handle failure to open camera more gracefully.
 irchack: pass along "<user> cammed up" notifications.
diff --git a/gtkgui.glade b/gtkgui.glade
index 91eb617..15d9031 100644
--- a/gtkgui.glade
+++ b/gtkgui.glade
@@ -1170,19 +1170,90 @@
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <child>
-                      <object class="GtkCheckButton" id="storecookiecheckbox">
+                      <object class="GtkBox" id="box19">
                         <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="receives_default">False</property>
-                        <property name="xalign">0</property>
-                        <property name="draw_indicator">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="orientation">vertical</property>
                         <child>
-                          <object class="GtkLabel" id="label26">
+                          <object class="GtkCheckButton" id="storecookiecheckbox">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="xalign">0</property>
+                            <property name="draw_indicator">True</property>
+                            <child>
+                              <object class="GtkLabel" id="label26">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="label" translatable="yes">Store HTTP cookies (prevents having to solve the captchas as often)</property>
+                                <property name="wrap">True</property>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="label39">
                             <property name="visible">True</property>
                             <property name="can_focus">False</property>
-                            <property name="label" translatable="yes">Store HTTP cookies (prevents having to solve the captchas as often)</property>
+                            <property name="label" translatable="yes">User list:</property>
                             <property name="wrap">True</property>
+                            <property name="wrap_mode">word-char</property>
+                            <property name="xalign">0</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="userlist_sort_mod">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="xalign">0</property>
+                            <property name="draw_indicator">True</property>
+                            <child>
+                              <object class="GtkLabel" id="label40">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="label" translatable="yes">Show moderators at the top</property>
+                                <property name="wrap">True</property>
+                              </object>
+                            </child>
                           </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="userlist_sort_self">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="xalign">0</property>
+                            <property name="draw_indicator">True</property>
+                            <child>
+                              <object class="GtkLabel" id="label41">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="label" translatable="yes">Show self at the top</property>
+                                <property name="wrap">True</property>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">3</property>
+                          </packing>
                         </child>
                       </object>
                     </child>
diff --git a/utilities/gtk/gui.c b/utilities/gtk/gui.c
index 8328a76..be8a157 100644
--- a/utilities/gtk/gui.c
+++ b/utilities/gtk/gui.c
@@ -100,6 +100,11 @@ void settings_reset(GtkBuilder* gui)
   // Misc/cookies
   option=GTK_WIDGET(gtk_builder_get_object(gui, "storecookiecheckbox"));
   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(option), config_get_bool("storecookies"));
+  // Misc/userlist
+  option=GTK_WIDGET(gtk_builder_get_object(gui, "userlist_sort_mod"));
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(option), config_get_bool("userlist_sort_mod"));
+  option=GTK_WIDGET(gtk_builder_get_object(gui, "userlist_sort_self"));
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(option), config_get_bool("userlist_sort_self"));
 }
 
 void showsettings(GtkMenuItem* item, GtkBuilder* gui)
@@ -173,6 +178,12 @@ void savesettings(GtkButton* button, GtkBuilder* gui)
   // Misc/cookies
   option=GTK_WIDGET(gtk_builder_get_object(gui, "storecookiecheckbox"));
   config_set("storecookies", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(option))?"True":"False");
+  // Misc/userlist
+  option=GTK_WIDGET(gtk_builder_get_object(gui, "userlist_sort_mod"));
+  config_set("userlist_sort_mod", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(option))?"True":"False");
+  option=GTK_WIDGET(gtk_builder_get_object(gui, "userlist_sort_self"));
+  config_set("userlist_sort_self", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(option))?"True":"False");
+  userlist_sort();
 
   config_save();
   GtkWidget* settings=GTK_WIDGET(gtk_builder_get_object(gui, "settings"));
@@ -1024,8 +1035,6 @@ void gui_init(char frombuild)
   option=GTK_WIDGET(gtk_builder_get_object(gui, "youtuberadio_embed"));
   gtk_widget_destroy(option);
   #endif
-  // Misc
-  option=GTK_WIDGET(gtk_builder_get_object(gui, "camdownonjoin"));
 
   GtkWidget* window=GTK_WIDGET(gtk_builder_get_object(gui, "main"));
   g_signal_connect(window, "configure-event", G_CALLBACK(handleresize), 0);
diff --git a/utilities/gtk/userlist.c b/utilities/gtk/userlist.c
index e296143..3873c74 100644
--- a/utilities/gtk/userlist.c
+++ b/utilities/gtk/userlist.c
@@ -19,6 +19,8 @@
 #include <gtk/gtk.h>
 #include "gui.h"
 #include "compat.h"
+#include "main.h"
+#include "configfile.h"
 #include "userlist.h"
 
 struct user* userlist=0;
@@ -64,6 +66,7 @@ struct user* adduser(const char* nick)
   userlist[usercount-1].ismod=0;
   gtk_box_pack_start(GTK_BOX(userlistwidget), userlist[usercount-1].label, 0, 0, 0);
   gtk_widget_show(userlist[usercount-1].label);
+  userlist_sort();
   return &userlist[usercount-1];
 }
 
@@ -83,6 +86,7 @@ void renameuser(const char* old, const char* newnick)
       gtk_label_set_text(GTK_LABEL(user->pm_tablabel), newnick);
     }
   }
+  userlist_sort();
 }
 
 void removeuser(const char* nick)
@@ -121,4 +125,34 @@ void usersetmod(const char* nick, char mod)
     user->item=user->label;
   }
   g_object_unref(user->label);
+  userlist_sort();
+}
+
+signed char usercmp(const struct user* a, const struct user* b)
+{
+  if(a==b){return 0;}
+  signed char r=strcasecmp(a->nick, b->nick); // Primarily sort case-insensitively
+  if(!r){r=strcmp(a->nick, b->nick);} // But fall back to case-sensitive if they match
+  if(config_get_bool("userlist_sort_mod") && a->ismod^b->ismod){r=b->ismod-a->ismod;}
+  if(config_get_bool("userlist_sort_self"))
+  {
+    if(!strcmp(a->nick, nickname)){r=-1;}
+    else if(!strcmp(b->nick, nickname)){r=1;}
+  }
+  return r;
+}
+
+void userlist_sort(void)
+{
+  unsigned int i;
+  for(i=0; i<usercount; ++i)
+  {
+    unsigned int position=0;
+    unsigned int i2;
+    for(i2=0; i2<usercount; ++i2)
+    {
+      if(usercmp(&userlist[i], &userlist[i2])>0){++position;}
+    }
+    gtk_box_reorder_child(GTK_BOX(userlistwidget), userlist[i].item, position);
+  }
 }
diff --git a/utilities/gtk/userlist.h b/utilities/gtk/userlist.h
index 2c03fd2..ebcf7b8 100644
--- a/utilities/gtk/userlist.h
+++ b/utilities/gtk/userlist.h
@@ -36,3 +36,4 @@ extern struct user* adduser(const char* nick);
 extern void renameuser(const char* old, const char* newnick);
 extern void removeuser(const char* nick);
 extern void usersetmod(const char* nick, char mod);
+extern void userlist_sort(void);