# HG changeset patch
# Node ID 6e77d687804c
# Parent  4784cd60a1f8
diff --git a/pidgin/prefs/pidginvvprefs.c b/pidgin/prefs/pidginvvprefs.c
--- a/pidgin/prefs/pidginvvprefs.c
+++ b/pidgin/prefs/pidginvvprefs.c
@@ -40,8 +40,8 @@ struct _PidginVVPrefs {
 	AdwPreferencesPage parent;
 
 	struct {
-		PidginPrefCombo input;
-		PidginPrefCombo output;
+		AdwComboRow *input;
+		AdwComboRow *output;
 		GtkWidget *threshold_row;
 		GtkWidget *threshold;
 		GtkWidget *volume;
@@ -52,8 +52,8 @@ struct _PidginVVPrefs {
 	} voice;
 
 	struct {
-		PidginPrefCombo input;
-		PidginPrefCombo output;
+		AdwComboRow *input;
+		AdwComboRow *output;
 		GtkWidget *frame;
 		GtkWidget *test;
 		GstElement *pipeline;
@@ -70,31 +70,37 @@ G_DEFINE_TYPE(PidginVVPrefs, pidgin_vv_p
  * Helpers
  *****************************************************************************/
 static void
-populate_vv_device_menuitems(PurpleMediaElementType type, GtkListStore *store)
+populate_vv_device_menuitems(AdwComboRow *row, PurpleMediaElementType type,
+                             const char *active)
 {
 	PurpleMediaManager *manager = NULL;
+	GListStore *store = NULL;
 	GList *devices;
+	guint selected = 0; /* Default to auto{audio,video}{src,sink} elements. */
 
-	gtk_list_store_clear(store);
+	store = G_LIST_STORE(adw_combo_row_get_model(row));
+
+	g_list_store_remove_all(store);
 
 	manager = purple_media_manager_get();
 	devices = purple_media_manager_enumerate_elements(manager, type);
 	for (; devices; devices = g_list_delete_link(devices, devices)) {
 		PurpleMediaElementInfo *info = devices->data;
-		GtkTreeIter iter;
-		char *name, *id;
-
-		name = purple_media_element_info_get_name(info);
-		id = purple_media_element_info_get_id(info);
+		char *id;
 
-		gtk_list_store_append(store, &iter);
-		gtk_list_store_set(store, &iter, PIDGIN_PREF_COMBO_TEXT, name,
-		                   PIDGIN_PREF_COMBO_VALUE, id, -1);
+		id = purple_media_element_info_get_id(info);
+		if(purple_strequal(id, active)) {
+			/* The index will be for the *next* appended item. */
+			selected = g_list_model_get_n_items(G_LIST_MODEL(store));
+		}
+		g_free(id);
 
-		g_free(name);
-		g_free(id);
+		g_list_store_append(store, info);
+
 		g_object_unref(info);
 	}
+
+	adw_combo_row_set_selected(row, selected);
 }
 
 static GstElement *
@@ -435,66 +441,80 @@ purple_media_type_to_preference_key(Purp
 }
 
 static void
-bind_vv_dropdown(PidginPrefCombo *combo, PurpleMediaElementType element_type)
+vv_combo_row_set(GObject *obj, G_GNUC_UNUSED GParamSpec *pspec, gpointer data)
 {
-	const gchar *preference_key;
-	GtkTreeModel *model;
+	const char *key = data;
+	PurpleMediaElementInfo *item = NULL;
+	char *id = NULL;
 
-	preference_key = purple_media_type_to_preference_key(element_type);
-	model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo->combo));
-	populate_vv_device_menuitems(element_type, GTK_LIST_STORE(model));
-
-	combo->type = PURPLE_PREF_STRING;
-	combo->key = preference_key;
-	pidgin_prefs_bind_dropdown(combo);
+	item = adw_combo_row_get_selected_item(ADW_COMBO_ROW(obj));
+	id = purple_media_element_info_get_id(item);
+	purple_prefs_set_string(key, id);
+	g_free(id);
 }
 
 static void
-bind_vv_frame(PidginVVPrefs *prefs, PidginPrefCombo *combo,
+bind_vv_combo(PidginVVPrefs *prefs, AdwComboRow *row,
               PurpleMediaElementType type)
 {
-	bind_vv_dropdown(combo, type);
+	const char *pref_key = NULL;
+	const char *pref_value = NULL;
+
+	pref_key = purple_media_type_to_preference_key(type);
+	pref_value = purple_prefs_get_string(pref_key);
+
+	populate_vv_device_menuitems(row, type, pref_value);
 
-	purple_prefs_connect_callback(combo->combo,
-	                              purple_media_type_to_preference_key(type),
-	                              vv_device_changed_cb, prefs);
-	g_signal_connect_swapped(combo->combo, "destroy",
+	g_signal_connect(row, "notify::selected", G_CALLBACK(vv_combo_row_set),
+	                 (gpointer)pref_key);
+
+	purple_prefs_connect_callback(row, pref_key, vv_device_changed_cb, prefs);
+	g_signal_connect_swapped(row, "destroy",
 	                         G_CALLBACK(purple_prefs_disconnect_by_handle),
-	                         combo->combo);
+	                         row);
 
-	g_object_set_data(G_OBJECT(combo->combo), "vv_media_type",
-	                  (gpointer)type);
-	g_object_set_data(G_OBJECT(combo->combo), "vv_combo", combo);
+	g_object_set_data(G_OBJECT(row), "vv_media_type", GINT_TO_POINTER(type));
 }
 
 static void
 device_list_changed_cb(G_GNUC_UNUSED PurpleMediaManager *manager,
-                       GtkWidget *widget)
+                       gpointer data)
 {
-	PidginPrefCombo *combo;
+	AdwComboRow *row = data;
+	PurpleMediaElementInfo *original = NULL;
+	PurpleMediaElementInfo *selected = NULL;
 	PurpleMediaElementType media_type;
-	const gchar *preference_key;
-	guint signal_id;
-	GtkTreeModel *model;
+	const char *pref_key = NULL;
+	const char *pref_value = NULL;
+
+	original = g_object_ref(adw_combo_row_get_selected_item(row));
 
-	combo = g_object_get_data(G_OBJECT(widget), "vv_combo");
-	media_type = (PurpleMediaElementType)GPOINTER_TO_INT(g_object_get_data(
-			G_OBJECT(widget),
-			"vv_media_type"));
-	preference_key = purple_media_type_to_preference_key(media_type);
+	media_type = (PurpleMediaElementType)GPOINTER_TO_INT(
+	    g_object_get_data(
+	        G_OBJECT(row),
+	        "vv_media_type"));
+	pref_key = purple_media_type_to_preference_key(media_type);
+	pref_value = purple_prefs_get_string(pref_key);
 
 	/* Block signals so pref doesn't get re-saved while changing UI. */
-	signal_id = g_signal_lookup("changed", GTK_TYPE_COMBO_BOX);
-	g_signal_handlers_block_matched(combo->combo, G_SIGNAL_MATCH_ID, signal_id,
-	                                0, NULL, NULL, NULL);
+	g_signal_handlers_block_by_func(row, vv_combo_row_set, (gpointer)pref_key);
+
+	populate_vv_device_menuitems(row, media_type, pref_value);
+
+	g_signal_handlers_unblock_by_func(row, vv_combo_row_set,
+	                                  (gpointer)pref_key);
 
-	model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo->combo));
-	populate_vv_device_menuitems(media_type, GTK_LIST_STORE(model));
-	gtk_combo_box_set_active_id(GTK_COMBO_BOX(combo->combo),
-	                            purple_prefs_get_string(preference_key));
+	selected = adw_combo_row_get_selected_item(row);
+	if(original != selected) {
+		/* We blocked signals to prevent accidentally changing the selected
+		 * element while the combo row repopulates, but now the original
+		 * element is gone, so we need to push this change back to the pref. */
+		char *id = purple_media_element_info_get_id(selected);
+		purple_prefs_set_string(pref_key, id);
+		g_free(id);
+	}
 
-	g_signal_handlers_unblock_matched(combo->combo, G_SIGNAL_MATCH_ID,
-	                                  signal_id, 0, NULL, NULL, NULL);
+	g_clear_object(&original);
 }
 
 /******************************************************************************
@@ -511,9 +531,9 @@ pidgin_vv_prefs_class_init(PidginVVPrefs
 	);
 
 	gtk_widget_class_bind_template_child(widget_class, PidginVVPrefs,
-	                                     voice.input.combo);
+	                                     voice.input);
 	gtk_widget_class_bind_template_child(widget_class, PidginVVPrefs,
-	                                     voice.output.combo);
+	                                     voice.output);
 	gtk_widget_class_bind_template_child(widget_class, PidginVVPrefs,
 	                                     voice.volume);
 	gtk_widget_class_bind_template_child(widget_class, PidginVVPrefs,
@@ -533,9 +553,9 @@ pidgin_vv_prefs_class_init(PidginVVPrefs
 	                                        toggle_voice_test_cb);
 
 	gtk_widget_class_bind_template_child(widget_class, PidginVVPrefs,
-	                                     video.input.combo);
+	                                     video.input);
 	gtk_widget_class_bind_template_child(widget_class, PidginVVPrefs,
-	                                     video.output.combo);
+	                                     video.output);
 	gtk_widget_class_bind_template_child(widget_class, PidginVVPrefs,
 	                                     video.frame);
 	gtk_widget_class_bind_template_child(widget_class, PidginVVPrefs,
@@ -553,31 +573,31 @@ pidgin_vv_prefs_init(PidginVVPrefs *pref
 
 	manager = purple_media_manager_get();
 
-	bind_vv_frame(prefs, &prefs->voice.input,
+	bind_vv_combo(prefs, prefs->voice.input,
 	              PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SRC);
 	g_signal_connect_object(manager, "elements-changed::audiosrc",
 	                        G_CALLBACK(device_list_changed_cb),
-	                        prefs->voice.input.combo, 0);
+	                        prefs->voice.input, 0);
 
-	bind_vv_frame(prefs, &prefs->voice.output,
+	bind_vv_combo(prefs, prefs->voice.output,
 	              PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SINK);
 	g_signal_connect_object(manager, "elements-changed::audiosink",
 	                        G_CALLBACK(device_list_changed_cb),
-	                        prefs->voice.output.combo, 0);
+	                        prefs->voice.output, 0);
 
 	bind_voice_test(prefs);
 
-	bind_vv_frame(prefs, &prefs->video.input,
+	bind_vv_combo(prefs, prefs->video.input,
 	              PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SRC);
 	g_signal_connect_object(manager, "elements-changed::videosrc",
 	                        G_CALLBACK(device_list_changed_cb),
-	                        prefs->video.input.combo, 0);
+	                        prefs->video.input, 0);
 
-	bind_vv_frame(prefs, &prefs->video.output,
+	bind_vv_combo(prefs, prefs->video.output,
 	              PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SINK);
 	g_signal_connect_object(manager, "elements-changed::videosink",
 	                        G_CALLBACK(device_list_changed_cb),
-	                        prefs->video.output.combo, 0);
+	                        prefs->video.output, 0);
 }
 
 /******************************************************************************
diff --git a/pidgin/resources/Prefs/vv.ui b/pidgin/resources/Prefs/vv.ui
--- a/pidgin/resources/Prefs/vv.ui
+++ b/pidgin/resources/Prefs/vv.ui
@@ -33,78 +33,51 @@ along with this program; if not, see <ht
     <property name="step-increment">1</property>
     <property name="page-increment">10</property>
   </object>
-  <object class="GtkListStore" id="video.input.store">
-    <columns>
-      <!-- column-name text -->
-      <column type="gchararray"/>
-      <!-- column-name value -->
-      <column type="gchararray"/>
-    </columns>
-  </object>
-  <object class="GtkListStore" id="video.output.store">
-    <columns>
-      <!-- column-name text -->
-      <column type="gchararray"/>
-      <!-- column-name value -->
-      <column type="gchararray"/>
-    </columns>
-  </object>
-  <object class="GtkListStore" id="voice.input.store">
-    <columns>
-      <!-- column-name text -->
-      <column type="gchararray"/>
-      <!-- column-name value -->
-      <column type="gchararray"/>
-    </columns>
-  </object>
-  <object class="GtkListStore" id="voice.output.store">
-    <columns>
-      <!-- column-name text -->
-      <column type="gchararray"/>
-      <!-- column-name value -->
-      <column type="gchararray"/>
-    </columns>
+  <object class="GtkBuilderListItemFactory" id="element-info-factory">
+    <property name="bytes">
+<![CDATA[
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="GtkListItem">
+    <property name="child">
+      <object class="GtkLabel">
+        <property name="xalign">0</property>
+        <binding name="label">
+          <lookup name="name" type="PurpleMediaElementInfo">
+            <lookup name="item">GtkListItem</lookup>
+          </lookup>
+        </binding>
+      </object>
+    </property>
+  </template>
+</interface>
+]]>
+    </property>
   </object>
   <template class="PidginVVPrefs" parent="AdwPreferencesPage">
     <child>
       <object class="AdwPreferencesGroup">
         <property name="title" translatable="1">Audio</property>
         <child>
-          <object class="AdwActionRow">
-            <property name="activatable-widget">voice.input.combo</property>
+          <object class="AdwComboRow" id="voice.input">
             <property name="title" translatable="1" context="Input for Audio">Input Device</property>
-            <child>
-              <object class="GtkComboBox" id="voice.input.combo">
-                <property name="id-column">1</property>
-                <property name="model">voice.input.store</property>
-                <property name="valign">center</property>
-                <child>
-                  <object class="GtkCellRendererText"/>
-                  <attributes>
-                    <attribute name="text">0</attribute>
-                  </attributes>
-                </child>
+            <property name="factory">element-info-factory</property>
+            <property name="model">
+              <object class="GListStore">
+                <property name="item-type">PurpleMediaElementInfo</property>
               </object>
-            </child>
+            </property>
           </object>
         </child>
         <child>
-          <object class="AdwActionRow">
-            <property name="activatable-widget">voice.output.combo</property>
+          <object class="AdwComboRow" id="voice.output">
             <property name="title" translatable="1" context="Device for Audio Output">Output Device</property>
-            <child>
-              <object class="GtkComboBox" id="voice.output.combo">
-                <property name="id-column">1</property>
-                <property name="model">voice.output.store</property>
-                <property name="valign">center</property>
-                <child>
-                  <object class="GtkCellRendererText"/>
-                  <attributes>
-                    <attribute name="text">0</attribute>
-                  </attributes>
-                </child>
+            <property name="factory">element-info-factory</property>
+            <property name="model">
+              <object class="GListStore">
+                <property name="item-type">PurpleMediaElementInfo</property>
               </object>
-            </child>
+            </property>
           </object>
         </child>
         <child>
@@ -188,43 +161,25 @@ along with this program; if not, see <ht
       <object class="AdwPreferencesGroup">
         <property name="title" translatable="1">Video</property>
         <child>
-          <object class="AdwActionRow">
-            <property name="activatable-widget">video.input.combo</property>
+          <object class="AdwComboRow" id="video.input">
             <property name="title" translatable="1" context="Device for Video Input">Input Device</property>
-            <child>
-              <object class="GtkComboBox" id="video.input.combo">
-                <property name="id-column">1</property>
-                <property name="hexpand">1</property>
-                <property name="model">video.input.store</property>
-                <property name="valign">center</property>
-                <child>
-                  <object class="GtkCellRendererText"/>
-                  <attributes>
-                    <attribute name="text">0</attribute>
-                  </attributes>
-                </child>
+            <property name="factory">element-info-factory</property>
+            <property name="model">
+              <object class="GListStore">
+                <property name="item-type">PurpleMediaElementInfo</property>
               </object>
-            </child>
+            </property>
           </object>
         </child>
         <child>
-          <object class="AdwActionRow">
-            <property name="activatable-widget">video.output.combo</property>
+          <object class="AdwComboRow" id="video.output">
             <property name="title" translatable="1" context="Device for Video Output">Output Device</property>
-            <child>
-              <object class="GtkComboBox" id="video.output.combo">
-                <property name="hexpand">1</property>
-                <property name="id-column">1</property>
-                <property name="valign">center</property>
-                <property name="model">video.output.store</property>
-                <child>
-                  <object class="GtkCellRendererText"/>
-                  <attributes>
-                    <attribute name="text">0</attribute>
-                  </attributes>
-                </child>
+            <property name="factory">element-info-factory</property>
+            <property name="model">
+              <object class="GListStore">
+                <property name="item-type">PurpleMediaElementInfo</property>
               </object>
-            </child>
+            </property>
           </object>
         </child>
         <child>
