From c40cfeb0fe12b6803d25a11664fd16d49a4a9b2a Mon Sep 17 00:00:00 2001
From: Simon Morlat <simon.morlat@linphone.org>
Date: Thu, 12 Jan 2012 17:16:06 +0100
Subject: [PATCH] gtk-ui: implement friend lookup and sorting

---
 NEWS             |   3 +
 gtk/friendlist.c | 170 ++++++++++++++++++++++++++++++++++-------------
 gtk/main.ui      |  19 +++---
 mediastreamer2   |   2 +-
 4 files changed, 136 insertions(+), 58 deletions(-)

diff --git a/NEWS b/NEWS
index 15cefbb713..a4b134ddfc 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,6 @@
+linphone-3.5.1 -- ??
+	* gtk - implement friend search by typing into the friendlist, and friend sorting
+
 linphone-3.5.0 -- December 22, 2011
 	* added VP-8 video codec
 	* added G722 audio codec
diff --git a/gtk/friendlist.c b/gtk/friendlist.c
index 9dacac7178..d5a056feff 100644
--- a/gtk/friendlist.c
+++ b/gtk/friendlist.c
@@ -239,6 +239,119 @@ static void linphone_gtk_init_bookmark_icon(void){
 	g_signal_connect(G_OBJECT(GTK_EDITABLE(entry)),"changed",(GCallback)check_contact,linphone_gtk_get_core());
 }
 
+static gboolean friend_search_func(GtkTreeModel *model, gint column,
+                                                         const gchar *key,
+                                                         GtkTreeIter *iter,
+                                                         gpointer search_data){
+	char *name=NULL;
+	gboolean ret=TRUE;
+	gtk_tree_model_get(model,iter,FRIEND_NAME,&name,-1);
+	if (name!=NULL){
+		ret=strstr(name,key)==NULL;
+		g_free(name);
+	}
+	return ret;
+}
+
+static gint friend_sort(GtkTreeModel *model, GtkTreeIter *a,GtkTreeIter *b,gpointer user_data){
+	char *n1=NULL,*n2=NULL;
+	int ret;
+	gtk_tree_model_get(model,a,FRIEND_NAME,&n1,-1);
+	gtk_tree_model_get(model,b,FRIEND_NAME,&n2,-1);
+	if (n1 && n2) {
+		ret=strcmp(n1,n2);
+		g_free(n1);
+		g_free(n2);
+	}else if (n1){
+		g_free(n1);
+		ret=-1;
+	}else if (n2){
+		g_free(n2);
+		ret=1;
+	}else ret=0;
+	return ret;
+}
+
+static void on_name_column_clicked(GtkTreeModel *model){
+	GtkSortType st;
+	gint column;
+	
+	gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(model),&column,&st);
+	if (column==FRIEND_NAME){
+		if (st==GTK_SORT_ASCENDING) st=GTK_SORT_DESCENDING;
+		else st=GTK_SORT_ASCENDING;
+	}else st=GTK_SORT_ASCENDING;
+	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model),FRIEND_NAME,st);
+}
+
+
+static int get_friend_weight(const LinphoneFriend *lf){
+	int w=0;
+	switch(linphone_friend_get_status(lf)){
+		case LinphoneStatusOnline:
+			w+=1000;
+		break;
+		case LinphoneStatusOffline:
+			if (linphone_friend_get_send_subscribe(lf))
+				w+=100;
+		break;
+		default:
+			w+=500;
+		break;
+	}
+	return w;
+}
+
+static int friend_compare_func(const LinphoneFriend *lf1, const LinphoneFriend *lf2){
+	int w1,w2;
+	w1=get_friend_weight(lf1);
+	w2=get_friend_weight(lf2);
+	if (w1==w2){
+		const char *u1,*u2;
+		const LinphoneAddress *addr1,*addr2;
+		addr1=linphone_friend_get_address(lf1);
+		addr2=linphone_friend_get_address(lf2);
+		u1=linphone_address_get_username(addr1);
+		u2=linphone_address_get_username(addr2);
+		if (u1 && u2) return strcasecmp(u1,u2);
+		if (u1) return 1;
+		else return -1;
+	}
+	return w2-w1;
+}
+
+static gint friend_sort_with_presence(GtkTreeModel *model, GtkTreeIter *a,GtkTreeIter *b,gpointer user_data){
+	LinphoneFriend *lf1=NULL,*lf2=NULL;
+	gtk_tree_model_get(model,a,FRIEND_ID,&lf1,-1);
+	gtk_tree_model_get(model,b,FRIEND_ID,&lf2,-1);
+	return friend_compare_func(lf1,lf2);
+}
+
+
+static MSList *sort_friend_list(const MSList *friends){
+	MSList *ret=NULL;
+	const MSList *elem;
+	LinphoneFriend *lf;
+
+	for(elem=friends;elem!=NULL;elem=elem->next){
+		lf=(LinphoneFriend*)elem->data;
+		ret=ms_list_insert_sorted(ret,lf,(MSCompareFunc)friend_compare_func);
+	}
+	return ret;
+}
+
+static void on_presence_column_clicked(GtkTreeModel *model){
+	GtkSortType st;
+	gint column;
+	
+	gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(model),&column,&st);
+	if (column==FRIEND_ID){
+		if (st==GTK_SORT_ASCENDING) st=GTK_SORT_DESCENDING;
+		else st=GTK_SORT_ASCENDING;
+	}else st=GTK_SORT_ASCENDING;
+	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model),FRIEND_ID,st);
+}
+
 static void linphone_gtk_friend_list_init(GtkWidget *friendlist)
 {
 	GtkListStore *store;
@@ -254,12 +367,21 @@ static void linphone_gtk_friend_list_init(GtkWidget *friendlist)
 	gtk_tree_view_set_model(GTK_TREE_VIEW(friendlist),GTK_TREE_MODEL(store));
 	g_object_unref(G_OBJECT(store));
 
+	gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(friendlist),friend_search_func,NULL,NULL);
+	gtk_tree_view_set_search_column(GTK_TREE_VIEW(friendlist),FRIEND_NAME);
+
+	gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store),FRIEND_NAME,friend_sort,NULL,NULL);
+	gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store),FRIEND_ID,friend_sort_with_presence,NULL,NULL);
+	
 	renderer = gtk_cell_renderer_pixbuf_new ();
 	column = gtk_tree_view_column_new_with_attributes (_("Name"),
                                                    renderer,
                                                    "pixbuf", FRIEND_ICON,
                                                    NULL);
 	g_object_set (G_OBJECT(column), "resizable", TRUE, NULL);
+	g_signal_connect_swapped(G_OBJECT(column),"clicked",(GCallback)on_name_column_clicked,GTK_TREE_MODEL(store));
+	gtk_tree_view_column_set_clickable(column,TRUE);
+	
 	renderer = gtk_cell_renderer_text_new ();
 	gtk_tree_view_column_pack_start(column,renderer,FALSE);
 	gtk_tree_view_column_add_attribute  (column,renderer,
@@ -273,6 +395,8 @@ static void linphone_gtk_friend_list_init(GtkWidget *friendlist)
                                                    "text", FRIEND_PRESENCE_STATUS,
                                                    NULL);
 	g_object_set (G_OBJECT(column), "resizable", TRUE, NULL);
+	g_signal_connect_swapped(G_OBJECT(column),"clicked",(GCallback)on_presence_column_clicked,GTK_TREE_MODEL(store));
+	gtk_tree_view_column_set_clickable(column,TRUE);
 	gtk_tree_view_column_set_visible(column,linphone_gtk_get_ui_config_int("friendlist_status",1));
 	
 	renderer = gtk_cell_renderer_pixbuf_new();
@@ -344,52 +468,6 @@ void linphone_gtk_directory_search_button_clicked(GtkWidget *button){
 		linphone_gtk_get_widget(gtk_widget_get_toplevel(button),"directory_search_entry"));
 }
 
-static int get_friend_weight(const LinphoneFriend *lf){
-	int w=0;
-	switch(linphone_friend_get_status(lf)){
-		case LinphoneStatusOnline:
-			w+=1000;
-		break;
-		case LinphoneStatusOffline:
-			if (linphone_friend_get_send_subscribe(lf))
-				w+=100;
-		break;
-		default:
-			w+=500;
-		break;
-	}
-	return w;
-}
-
-static int friend_compare_func(const LinphoneFriend *lf1, const LinphoneFriend *lf2){
-	int w1,w2;
-	w1=get_friend_weight(lf1);
-	w2=get_friend_weight(lf2);
-	if (w1==w2){
-		const char *u1,*u2;
-		const LinphoneAddress *addr1,*addr2;
-		addr1=linphone_friend_get_address(lf1);
-		addr2=linphone_friend_get_address(lf2);
-		u1=linphone_address_get_username(addr1);
-		u2=linphone_address_get_username(addr2);
-		if (u1 && u2) return strcasecmp(u1,u2);
-		if (u1) return 1;
-		else return -1;
-	}
-	return w2-w1;
-}
-
-static MSList *sort_friend_list(const MSList *friends){
-	MSList *ret=NULL;
-	const MSList *elem;
-	LinphoneFriend *lf;
-
-	for(elem=friends;elem!=NULL;elem=elem->next){
-		lf=(LinphoneFriend*)elem->data;
-		ret=ms_list_insert_sorted(ret,lf,(MSCompareFunc)friend_compare_func);
-	}
-	return ret;
-}
 
 void linphone_gtk_show_friends(void){
 	GtkWidget *mw=linphone_gtk_get_main_window();
diff --git a/gtk/main.ui b/gtk/main.ui
index 7ebe953635..fde3a794a6 100644
--- a/gtk/main.ui
+++ b/gtk/main.ui
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <requires lib="gtk+" version="2.18"/>
-  <!-- interface-naming-policy toplevel-contextual -->
   <object class="GtkWindow" id="dummy_conf_window">
     <property name="can_focus">False</property>
     <child>
@@ -652,8 +651,6 @@
                                     <property name="invisible_char">●</property>
                                     <property name="primary_icon_activatable">False</property>
                                     <property name="secondary_icon_activatable">False</property>
-                                    <property name="primary_icon_sensitive">True</property>
-                                    <property name="secondary_icon_sensitive">True</property>
                                     <signal name="activate" handler="linphone_gtk_uri_bar_activate" swapped="no"/>
                                   </object>
                                   <packing>
@@ -745,8 +742,6 @@
                                     <property name="invisible_char_set">True</property>
                                     <property name="primary_icon_activatable">False</property>
                                     <property name="secondary_icon_activatable">False</property>
-                                    <property name="primary_icon_sensitive">True</property>
-                                    <property name="secondary_icon_sensitive">True</property>
                                     <signal name="changed" handler="linphone_gtk_show_friends" swapped="no"/>
                                   </object>
                                   <packing>
@@ -808,10 +803,15 @@
                                     <property name="visible">True</property>
                                     <property name="can_focus">True</property>
                                     <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                                    <property name="reorderable">True</property>
+                                    <property name="search_column">0</property>
                                     <signal name="button-press-event" handler="linphone_gtk_contact_list_button_pressed" swapped="no"/>
                                     <signal name="cursor-changed" handler="linphone_gtk_contact_clicked" swapped="no"/>
                                     <signal name="row-activated" handler="linphone_gtk_contact_activated" swapped="no"/>
                                     <signal name="popup-menu" handler="linphone_gtk_popup_contact_menu" swapped="no"/>
+                                    <child internal-child="selection">
+                                      <object class="GtkTreeSelection" id="treeview-selection1"/>
+                                    </child>
                                   </object>
                                 </child>
                               </object>
@@ -837,8 +837,6 @@
                                         <property name="invisible_char_set">True</property>
                                         <property name="primary_icon_activatable">False</property>
                                         <property name="secondary_icon_activatable">False</property>
-                                        <property name="primary_icon_sensitive">True</property>
-                                        <property name="secondary_icon_sensitive">True</property>
                                         <signal name="activate" handler="linphone_gtk_directory_search_activate" swapped="no"/>
                                         <signal name="icon-press" handler="linphone_gtk_directory_search_activate" swapped="no"/>
                                         <signal name="focus-in-event" handler="linphone_gtk_directory_search_focus_in" swapped="no"/>
@@ -1037,6 +1035,9 @@
                                     <property name="headers_visible">False</property>
                                     <signal name="cursor-changed" handler="linphone_gtk_history_row_selected" swapped="no"/>
                                     <signal name="row-activated" handler="linphone_gtk_history_row_activated" swapped="no"/>
+                                    <child internal-child="selection">
+                                      <object class="GtkTreeSelection" id="treeview-selection2"/>
+                                    </child>
                                   </object>
                                 </child>
                               </object>
@@ -1540,8 +1541,6 @@
                                         <property name="invisible_char">●</property>
                                         <property name="primary_icon_activatable">False</property>
                                         <property name="secondary_icon_activatable">False</property>
-                                        <property name="primary_icon_sensitive">True</property>
-                                        <property name="secondary_icon_sensitive">True</property>
                                       </object>
                                       <packing>
                                         <property name="left_attach">1</property>
@@ -1556,8 +1555,6 @@
                                         <property name="invisible_char">●</property>
                                         <property name="primary_icon_activatable">False</property>
                                         <property name="secondary_icon_activatable">False</property>
-                                        <property name="primary_icon_sensitive">True</property>
-                                        <property name="secondary_icon_sensitive">True</property>
                                       </object>
                                       <packing>
                                         <property name="left_attach">1</property>
diff --git a/mediastreamer2 b/mediastreamer2
index 668bdcc2c0..c8106487fa 160000
--- a/mediastreamer2
+++ b/mediastreamer2
@@ -1 +1 @@
-Subproject commit 668bdcc2c04a487fb94f31b7790ef301908c47b5
+Subproject commit c8106487faa8cee02f4dd94b03e4f9185b236b4e
-- 
GitLab