changeset 8286:89d9d004e3f3

[gaim-migrate @ 9010] Improved the field request API, adding required fields and account fields, as well as some new utility API functions and bug fixes. These changes allowed me to migrate the New IM dialog over to the field request API, removing a lot of code and improving consistency. committer: Tailor Script <tailor@pidgin.im>
author Christian Hammond <chipx86@chipx86.com>
date Wed, 18 Feb 2004 07:22:53 +0000
parents d335cc7bca54
children ef881489396e
files ChangeLog src/dialogs.c src/gtkrequest.c src/request.c src/request.h
diffstat 5 files changed, 549 insertions(+), 133 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed Feb 18 02:39:47 2004 +0000
+++ b/ChangeLog	Wed Feb 18 07:22:53 2004 +0000
@@ -17,6 +17,8 @@
 	* Improved accessibility in conversation windows (Nathan Fredrickson)
 	* Keyboard access to context menus via Shift+F10 (Marc Mulcahy)
 	* Core/UI split event loop code. (Scott Lamb)
+	* Added improvements to the multi-field request code, including
+	  required fields and account fields.
 
 	Bug Fixes:
 	* Various buffer overflow fixes (Stefan Esser)
--- a/src/dialogs.c	Wed Feb 18 02:39:47 2004 +0000
+++ b/src/dialogs.c	Wed Feb 18 07:22:53 2004 +0000
@@ -468,34 +468,6 @@
 	return TRUE;
 }
 
-static void do_im(GtkWidget *dialog, int id, struct getuserinfo *info)
-{
-	const char *who;
-	GaimConversation *conv;
-	GaimAccount *account;
-
-	switch(id) {
-	case GTK_RESPONSE_OK:
-		who = gtk_entry_get_text(GTK_ENTRY(info->entry));
-
-		if (who && *who) {
-			account = (info->gc ? info->gc->account : NULL);
-
-			conv = gaim_find_conversation_with_account(who, account);
-
-			if (conv == NULL)
-				conv = gaim_conversation_new(GAIM_CONV_IM, account, who);
-			else
-				gaim_conv_window_raise(gaim_conversation_get_window(conv));
-		}
-
-		break;
-	}
-
-	gtk_widget_destroy(GTK_WIDGET(dialog));
-	g_free(info);
-}
-
 static void do_info(GtkWidget *dialog, int id, struct getuserinfo *info)
 {
 	char *who;
@@ -527,81 +499,56 @@
 	info->gc = gaim_account_get_connection(account);
 }
 
-void show_im_dialog()
+static void
+new_im_cb(gpointer data, GaimRequestFields *fields)
 {
-	GtkWidget *window, *hbox, *vbox;
-	GtkWidget *label;
-	GtkWidget *table;
-	GaimGtkBuddyList *gtkblist;
-	GtkWidget *img = gtk_image_new_from_stock(GAIM_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG);
-	struct getuserinfo *info = g_new0(struct getuserinfo, 1);
+	const char *username;
+	GaimAccount *account;
+	GaimConversation *conv;
 
-	gtkblist = GAIM_GTK_BLIST(gaim_get_blist());
+	username = gaim_request_fields_get_string(fields,  "screenname");
+	account  = gaim_request_fields_get_account(fields, "account");
 
-	info->gc = gaim_connections_get_all()->data;
+	conv = gaim_find_conversation_with_account(username, account);
 
-	window = gtk_dialog_new_with_buttons(_("New Instant Message"), gtkblist ? GTK_WINDOW(gtkblist->window) : NULL, 0,
-					       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
-	gtk_dialog_set_default_response (GTK_DIALOG(window), GTK_RESPONSE_OK);
-	gtk_container_set_border_width (GTK_CONTAINER(window), 6);
-	gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
-	gtk_dialog_set_has_separator(GTK_DIALOG(window), FALSE);
-	gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(window)->vbox), 12);
-	gtk_container_set_border_width (GTK_CONTAINER(GTK_DIALOG(window)->vbox), 6);
-	gtk_dialog_set_response_sensitive(GTK_DIALOG(window), GTK_RESPONSE_OK, FALSE);
+	if (conv == NULL)
+		conv = gaim_conversation_new(GAIM_CONV_IM, account, username);
+	else
+		gaim_conv_window_raise(gaim_conversation_get_window(conv));
+}
 
-	hbox = gtk_hbox_new(FALSE, 12);
-	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(window)->vbox), hbox);
-	gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0);
-	gtk_misc_set_alignment(GTK_MISC(img), 0, 0);
-
-	vbox = gtk_vbox_new(FALSE, 0);
-	gtk_container_add(GTK_CONTAINER(hbox), vbox);
-
-	label = gtk_label_new(_("Please enter the screen name of the person you would like to IM.\n"));
-	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
-	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+void
+show_im_dialog(void)
+{
+	GaimRequestFields *fields;
+	GaimRequestFieldGroup *group;
+	GaimRequestField *field;
 
-	table = gtk_table_new(2, 2, FALSE);
-	gtk_table_set_row_spacings(GTK_TABLE(table), 6);
-	gtk_table_set_col_spacings(GTK_TABLE(table), 6);
-	gtk_container_set_border_width(GTK_CONTAINER(table), 12);
-	gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
+	fields = gaim_request_fields_new();
+
+	group = gaim_request_field_group_new(NULL);
+	gaim_request_fields_add_group(fields, group);
 
-	label = gtk_label_new(NULL);
-	gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("_Screen Name:"));
-	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
-	gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
-
-	info->entry = gtk_entry_new();
-	gtk_table_attach_defaults(GTK_TABLE(table), info->entry, 1, 2, 0, 1);
-	gtk_entry_set_activates_default (GTK_ENTRY(info->entry), TRUE);
-	gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_WIDGET(info->entry));
-	gaim_set_accessible_label (info->entry, label);
-
-	g_signal_connect(G_OBJECT(info->entry), "changed",
-			G_CALLBACK(gaim_gtk_set_sensitive_if_input), window);
+	field = gaim_request_field_string_new("screenname", _("_Screen name"),
+										  NULL, FALSE);
+	gaim_request_field_set_required(field, TRUE);
+	gaim_request_field_group_add_field(group, field);
 
-	if (gaim_connections_get_all()->next) {
-
-		label = gtk_label_new(NULL);
-		gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
-		gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("_Account:"));
-		gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
-
-		info->account = gaim_gtk_account_option_menu_new(NULL, FALSE,
-				G_CALLBACK(show_info_select_account), NULL, info);
+	field = gaim_request_field_account_new("account", _("_Account"), NULL);
+	gaim_request_field_set_visible(field,
+		(gaim_connections_get_all() != NULL &&
+		 gaim_connections_get_all()->next != NULL));
+	gaim_request_field_set_required(field, TRUE);
+	gaim_request_field_group_add_field(group, field);
 
-		gtk_table_attach_defaults(GTK_TABLE(table), info->account, 1, 2, 1, 2);
-		gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_WIDGET(info->account));
-		gaim_set_accessible_label (info->account, label);
-	}
-
-	g_signal_connect(G_OBJECT(window), "response", G_CALLBACK(do_im), info);
-
-	gtk_widget_show_all(window);
-	gtk_widget_grab_focus(GTK_WIDGET(info->entry));
+	gaim_request_fields(gaim_get_blist(), _("New Instant Message"),
+						NULL,
+						_("Please enter the screen name of the person you "
+						  "would like to IM."),
+						fields,
+						_("OK"), G_CALLBACK(new_im_cb),
+						_("Cancel"), NULL,
+						NULL);
 }
 
 void show_info_dialog()
--- a/src/gtkrequest.c	Wed Feb 18 02:39:47 2004 +0000
+++ b/src/gtkrequest.c	Wed Feb 18 07:22:53 2004 +0000
@@ -38,6 +38,8 @@
 	void *user_data;
 	GtkWidget *dialog;
 
+	GtkWidget *ok_button;
+
 	size_t cb_count;
 	GCallback *cbs;
 
@@ -108,7 +110,8 @@
 {
 	const char *value;
 
-	if (gaim_request_field_string_is_multiline(field)) {
+	if (gaim_request_field_string_is_multiline(field))
+	{
 		GtkTextBuffer *buffer;
 		GtkTextIter start_iter, end_iter;
 
@@ -161,6 +164,12 @@
 }
 
 static void
+field_account_cb(GObject *w, GaimAccount *account, GaimRequestField *field)
+{
+	gaim_request_field_account_set_value(field, account);
+}
+
+static void
 multifield_ok_cb(GtkWidget *button, GaimGtkRequestData *data)
 {
 	if (data->cbs[0] != NULL)
@@ -267,7 +276,7 @@
 								 "%s</span>%s%s" : "%s%s%s"),
 								 (primary ? primary : ""),
 								 ((primary && secondary) ? "\n\n" : ""),
- 								 (secondary ? secondary : ""));
+								 (secondary ? secondary : ""));
 
 	label = gtk_label_new(NULL);
 
@@ -437,6 +446,32 @@
 	return data;
 }
 
+static void
+req_entry_field_changed_cb(GtkWidget *entry, GaimRequestField *field)
+{
+	GaimGtkRequestData *req_data;
+	const char *text = gtk_entry_get_text(GTK_ENTRY(entry));
+
+	gaim_request_field_string_set_value(field, (*text == '\0' ? NULL : text));
+
+	req_data = (GaimGtkRequestData *)field->group->fields_list->ui_data;
+
+	gtk_widget_set_sensitive(req_data->ok_button,
+		gaim_request_fields_all_required_filled(field->group->fields_list));
+}
+
+static void
+setup_entry_field(GtkWidget *entry, GaimRequestField *field)
+{
+	gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
+
+	if (gaim_request_field_is_required(field))
+	{
+		g_signal_connect(G_OBJECT(entry), "changed",
+						 G_CALLBACK(req_entry_field_changed_cb), field);
+	}
+}
+
 static GtkWidget *
 create_string_field(GaimRequestField *field)
 {
@@ -488,6 +523,8 @@
 	{
 		widget = gtk_entry_new();
 
+		setup_entry_field(widget, field);
+
 		if (value != NULL)
 			gtk_entry_set_text(GTK_ENTRY(widget), value);
 
@@ -512,6 +549,8 @@
 
 	widget = gtk_entry_new();
 
+	setup_entry_field(widget, field);
+
 	value = gaim_request_field_int_get_default_value(field);
 
 	if (value != 0)
@@ -612,6 +651,19 @@
 	return widget;
 }
 
+static GtkWidget *
+create_account_field(GaimRequestField *field)
+{
+	GtkWidget *widget;
+
+	widget = gaim_gtk_account_option_menu_new(
+		gaim_request_field_account_get_default_value(field),
+		gaim_request_field_account_get_show_all(field),
+		G_CALLBACK(field_account_cb), NULL, field);
+
+	return widget;
+}
+
 static void
 select_field_list_item(GtkTreeModel *model, GtkTreePath *path,
 					   GtkTreeIter *iter, gpointer data)
@@ -725,6 +777,8 @@
 	data->user_data = user_data;
 	data->u.multifield.fields = fields;
 
+	fields->ui_data = data;
+
 	data->cb_count = 2;
 	data->cbs = g_new0(GCallback, 2);
 
@@ -813,7 +867,8 @@
 		else
 			rows = field_count;
 
-		col_num=0;
+		col_num = 0;
+
 		for (fl = field_list; fl != NULL; fl = fl->next)
 		{
 			GaimRequestFieldType type;
@@ -822,19 +877,26 @@
 
 			type = gaim_request_field_get_type(field);
 
-			if (type == GAIM_REQUEST_FIELD_LABEL) {
-				if(col_num > 0)
+			if (type == GAIM_REQUEST_FIELD_LABEL)
+			{
+				if (col_num > 0)
 					rows++;
+
 				rows++;
-			} else if(type == GAIM_REQUEST_FIELD_STRING && gaim_request_field_string_is_multiline(field)){
-				if(col_num > 0)
+			}
+			else if (type == GAIM_REQUEST_FIELD_STRING &&
+					 gaim_request_field_string_is_multiline(field))
+			{
+				if (col_num > 0)
 					rows++;
+
 				rows += 2;
 			}
 
 			col_num++;
+
 			if(col_num >= cols)
-				col_num=0;
+				col_num = 0;
 		}
 
 		table = gtk_table_new(rows, 2 * cols, FALSE);
@@ -914,6 +976,8 @@
 					widget = create_choice_field(field);
 				else if (type == GAIM_REQUEST_FIELD_LIST)
 					widget = create_list_field(field);
+				else if (type == GAIM_REQUEST_FIELD_ACCOUNT)
+					widget = create_account_field(field);
 				else
 					continue;
 
@@ -970,14 +1034,24 @@
 	g_signal_connect(G_OBJECT(button), "clicked",
 					 G_CALLBACK(multifield_cancel_cb), data);
 
+	GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+
 	/* OK button */
 	button = gtk_button_new_from_stock(text_to_stock(ok_text));
 	gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
 	gtk_widget_show(button);
 
+	data->ok_button = button;
+
+	GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+	gtk_window_set_default(GTK_WINDOW(win), button);
+
 	g_signal_connect(G_OBJECT(button), "clicked",
 					 G_CALLBACK(multifield_ok_cb), data);
 
+	if (!gaim_request_fields_all_required_filled(fields))
+		gtk_widget_set_sensitive(button, FALSE);
+
 	gtk_widget_show(win);
 
 	return data;
@@ -991,11 +1065,11 @@
 	if (data->cbs != NULL)
 		g_free(data->cbs);
 
+	gtk_widget_destroy(data->dialog);
+
 	if (type == GAIM_REQUEST_FIELDS)
 		gaim_request_fields_destroy(data->u.multifield.fields);
 
-	gtk_widget_destroy(data->dialog);
-
 	g_free(data);
 }
 
--- a/src/request.c	Wed Feb 18 02:39:47 2004 +0000
+++ b/src/request.c	Wed Feb 18 07:22:53 2004 +0000
@@ -58,13 +58,18 @@
 
 	g_return_if_fail(fields != NULL);
 
-	for (l = fields->groups; l != NULL; l = l->next) {
+	for (l = fields->groups; l != NULL; l = l->next)
+	{
 		group = l->data;
 
 		gaim_request_field_group_destroy(group);
 	}
 
-	g_list_free(fields->groups);
+	if (fields->groups != NULL)
+		g_list_free(fields->groups);
+
+	if (fields->required_fields != NULL)
+		g_list_free(fields->required_fields);
 
 	g_hash_table_destroy(fields->fields);
 
@@ -104,6 +109,65 @@
 	return fields->groups;
 }
 
+gboolean
+gaim_request_fields_exists(const GaimRequestFields *fields, const char *id)
+{
+	g_return_val_if_fail(fields != NULL, FALSE);
+	g_return_val_if_fail(id     != NULL, FALSE);
+
+	return (g_hash_table_lookup(fields->fields, id) != NULL);
+}
+
+const GList *
+gaim_request_fields_get_required(const GaimRequestFields *fields)
+{
+	g_return_val_if_fail(fields != NULL, NULL);
+
+	return fields->required_fields;
+}
+
+gboolean
+gaim_request_fields_is_field_required(const GaimRequestFields *fields,
+									  const char *id)
+{
+	GaimRequestField *field;
+
+	g_return_val_if_fail(fields != NULL, FALSE);
+	g_return_val_if_fail(id     != NULL, FALSE);
+
+	if ((field = gaim_request_fields_get_field(fields, id)) == NULL)
+		return FALSE;
+
+	return gaim_request_field_is_required(field);
+}
+
+gboolean
+gaim_request_fields_all_required_filled(const GaimRequestFields *fields)
+{
+	GList *l;
+
+	g_return_val_if_fail(fields != NULL, FALSE);
+
+	for (l = fields->required_fields; l != NULL; l = l->next)
+	{
+		GaimRequestField *field = (GaimRequestField *)l->data;
+
+		switch (gaim_request_field_get_type(field))
+		{
+			case GAIM_REQUEST_FIELD_STRING:
+				if (gaim_request_field_string_get_value(field) == NULL)
+					return FALSE;
+
+				break;
+
+			default:
+				break;
+		}
+	}
+
+	return TRUE;
+}
+
 GaimRequestField *
 gaim_request_fields_get_field(const GaimRequestFields *fields, const char *id)
 {
@@ -176,6 +240,21 @@
 	return gaim_request_field_choice_get_value(field);
 }
 
+GaimAccount *
+gaim_request_fields_get_account(const GaimRequestFields *fields,
+								const char *id)
+{
+	GaimRequestField *field;
+
+	g_return_val_if_fail(fields != NULL, NULL);
+	g_return_val_if_fail(id     != NULL, NULL);
+
+	if ((field = gaim_request_fields_get_field(fields, id)) == NULL)
+		return NULL;
+
+	return gaim_request_field_account_get_value(field);
+}
+
 GaimRequestFieldGroup *
 gaim_request_field_group_new(const char *title)
 {
@@ -220,10 +299,19 @@
 
 	group->fields = g_list_append(group->fields, field);
 
-	if (group->fields_list != NULL) {
+	if (group->fields_list != NULL)
+	{
 		g_hash_table_insert(group->fields_list->fields,
 							g_strdup(gaim_request_field_get_id(field)), field);
 	}
+
+	field->group = group;
+
+	if (gaim_request_field_is_required(field))
+	{
+		group->fields_list->required_fields =
+			g_list_append(group->fields_list->required_fields, field);
+	}
 }
 
 const char *
@@ -344,6 +432,33 @@
 	field->type_hint = (type_hint == NULL ? NULL : g_strdup(type_hint));
 }
 
+void
+gaim_request_field_set_required(GaimRequestField *field, gboolean required)
+{
+	g_return_if_fail(field != NULL);
+
+	if (field->required == required)
+		return;
+
+	field->required = required;
+
+	if (field->group != NULL)
+	{
+		if (required)
+		{
+			field->group->fields_list->required_fields =
+				g_list_append(field->group->fields_list->required_fields,
+							  field);
+		}
+		else
+		{
+			field->group->fields_list->required_fields =
+				g_list_remove(field->group->fields_list->required_fields,
+							  field);
+		}
+	}
+}
+
 GaimRequestFieldType
 gaim_request_field_get_type(const GaimRequestField *field)
 {
@@ -384,6 +499,14 @@
 	return field->type_hint;
 }
 
+gboolean
+gaim_request_field_is_required(const GaimRequestField *field)
+{
+	g_return_val_if_fail(field != NULL, FALSE);
+
+	return field->required;
+}
+
 GaimRequestField *
 gaim_request_field_string_new(const char *id, const char *text,
 							  const char *default_value, gboolean multiline)
@@ -855,6 +978,105 @@
 	return field;
 }
 
+
+GaimRequestField *
+gaim_request_field_account_new(const char *id, const char *text,
+							   GaimAccount *account)
+{
+	GaimRequestField *field;
+
+	g_return_val_if_fail(id   != NULL, NULL);
+	g_return_val_if_fail(text != NULL, NULL);
+
+	field = gaim_request_field_new(id, text, GAIM_REQUEST_FIELD_ACCOUNT);
+
+	if (account == NULL && gaim_connections_get_all() != NULL)
+	{
+		account = gaim_connection_get_account(
+			(GaimConnection *)gaim_connections_get_all()->data);
+	}
+
+	gaim_request_field_account_set_default_value(field, account);
+	gaim_request_field_account_set_value(field, account);
+
+	return field;
+}
+
+void
+gaim_request_field_account_set_default_value(GaimRequestField *field,
+											 GaimAccount *default_value)
+{
+	g_return_if_fail(field != NULL);
+	g_return_if_fail(field->type == GAIM_REQUEST_FIELD_ACCOUNT);
+
+	field->u.account.default_account = default_value;
+}
+
+void
+gaim_request_field_account_set_value(GaimRequestField *field,
+									 GaimAccount *value)
+{
+	g_return_if_fail(field != NULL);
+	g_return_if_fail(field->type == GAIM_REQUEST_FIELD_ACCOUNT);
+
+	field->u.account.account = value;
+}
+
+void
+gaim_request_field_account_set_show_all(GaimRequestField *field,
+										gboolean show_all)
+{
+	g_return_if_fail(field != NULL);
+	g_return_if_fail(field->type == GAIM_REQUEST_FIELD_ACCOUNT);
+
+	if (field->u.account.show_all == show_all)
+		return;
+
+	field->u.account.show_all = show_all;
+
+	if (!show_all)
+	{
+		if (gaim_account_is_connected(field->u.account.default_account))
+		{
+			gaim_request_field_account_set_default_value(field,
+				(GaimAccount *)gaim_connections_get_all()->data);
+		}
+
+		if (gaim_account_is_connected(field->u.account.account))
+		{
+			gaim_request_field_account_set_value(field,
+				(GaimAccount *)gaim_connections_get_all()->data);
+		}
+	}
+}
+
+GaimAccount *
+gaim_request_field_account_get_default_value(const GaimRequestField *field)
+{
+	g_return_val_if_fail(field != NULL, NULL);
+	g_return_val_if_fail(field->type == GAIM_REQUEST_FIELD_ACCOUNT, NULL);
+
+	return field->u.account.default_account;
+}
+
+GaimAccount *
+gaim_request_field_account_get_value(const GaimRequestField *field)
+{
+	g_return_val_if_fail(field != NULL, NULL);
+	g_return_val_if_fail(field->type == GAIM_REQUEST_FIELD_ACCOUNT, NULL);
+
+	return field->u.account.account;
+}
+
+gboolean
+gaim_request_field_account_get_show_all(const GaimRequestField *field)
+{
+	g_return_val_if_fail(field != NULL, FALSE);
+	g_return_val_if_fail(field->type == GAIM_REQUEST_FIELD_ACCOUNT, FALSE);
+
+	return field->u.account.show_all;
+}
+
 /* -- */
 
 void *
--- a/src/request.h	Wed Feb 18 02:39:47 2004 +0000
+++ b/src/request.h	Wed Feb 18 07:22:53 2004 +0000
@@ -29,6 +29,8 @@
 #include <glib-object.h>
 #include <glib.h>
 
+#include "account.h"
+
 /**
  * Request types.
  */
@@ -52,22 +54,53 @@
 	GAIM_REQUEST_FIELD_BOOLEAN,
 	GAIM_REQUEST_FIELD_CHOICE,
 	GAIM_REQUEST_FIELD_LIST,
-	GAIM_REQUEST_FIELD_LABEL
+	GAIM_REQUEST_FIELD_LABEL,
+	GAIM_REQUEST_FIELD_ACCOUNT
 
 } GaimRequestFieldType;
 
 /**
+ * Multiple fields request data.
+ */
+typedef struct
+{
+	GList *groups;
+
+	GHashTable *fields;
+
+	GList *required_fields;
+
+	void *ui_data;
+
+} GaimRequestFields;
+
+/**
+ * A group of fields with a title.
+ */
+typedef struct
+{
+	GaimRequestFields *fields_list;
+
+	char *title;
+
+	GList *fields;
+
+} GaimRequestFieldGroup;
+
+/**
  * A request field.
  */
 typedef struct
 {
 	GaimRequestFieldType type;
+	GaimRequestFieldGroup *group;
 
 	char *id;
 	char *label;
 	char *type_hint;
 
 	gboolean visible;
+	gboolean required;
 
 	union
 	{
@@ -115,6 +148,14 @@
 
 		} list;
 
+		struct
+		{
+			GaimAccount *default_account;
+			GaimAccount *account;
+			gboolean show_all;
+
+		} account;
+
 	} u;
 
 	void *ui_data;
@@ -122,30 +163,6 @@
 } GaimRequestField;
 
 /**
- * Multiple fields request data.
- */
-typedef struct
-{
-	GList *groups;
-
-	GHashTable *fields;
-
-} GaimRequestFields;
-
-/**
- * A group of fields with a title.
- */
-typedef struct
-{
-	GaimRequestFields *fields_list;
-
-	char *title;
-
-	GList *fields;
-
-} GaimRequestFieldGroup;
-
-/**
  * Request UI operations.
  */
 typedef struct
@@ -222,6 +239,47 @@
 GList *gaim_request_fields_get_groups(const GaimRequestFields *fields);
 
 /**
+ * Returns whether or not the field with the specified ID exists.
+ *
+ * @param fields The fields list.
+ * @param id     The ID of the field.
+ *
+ * @return TRUE if the field exists, or FALSE.
+ */
+gboolean gaim_request_fields_exists(const GaimRequestFields *fields,
+									const char *id);
+
+/**
+ * Returns a list of all required fields.
+ *
+ * @param fields The fields list.
+ *
+ * @return The list of required fields.
+ */
+const GList *gaim_request_fields_get_required(const GaimRequestFields *fields);
+
+/**
+ * Returns whether or not a field with the specified ID is required.
+ *
+ * @param fields The fields list.
+ * @param id     The field ID.
+ *
+ * @return TRUE if the specified field is required, or FALSE.
+ */
+gboolean gaim_request_fields_is_field_required(const GaimRequestFields *fields,
+											   const char *id);
+
+/**
+ * Returns whether or not all required fields have values.
+ *
+ * @param fields The fields list.
+ *
+ * @return TRUE if all required fields have values, or FALSE.
+ */
+gboolean gaim_request_fields_all_required_filled(
+	const GaimRequestFields *fields);
+
+/**
  * Return the field with the specified ID.
  *
  * @param fields The fields list.
@@ -276,6 +334,17 @@
 int gaim_request_fields_get_choice(const GaimRequestFields *fields,
 								   const char *id);
 
+/**
+ * Returns the account of a field with the specified ID.
+ *
+ * @param fields The fields list.
+ * @param id     The ID of the field.
+ *
+ * @return The account value, if found, or NULL otherwise.
+ */
+GaimAccount *gaim_request_fields_get_account(const GaimRequestFields *fields,
+											 const char *id);
+
 /*@}*/
 
 /**************************************************************************/
@@ -383,6 +452,15 @@
 									  const char *type_hint);
 
 /**
+ * Sets whether or not a field is required.
+ *
+ * @param field    The field.
+ * @param required TRUE if required, or FALSE.
+ */
+void gaim_request_field_set_required(GaimRequestField *field,
+									 gboolean required);
+
+/**
  * Returns the type of a field.
  *
  * @param field The field.
@@ -427,6 +505,15 @@
  */
 const char *gaim_request_field_get_type_hint(const GaimRequestField *field);
 
+/**
+ * Returns whether or not a field is required.
+ *
+ * @param field The field.
+ *
+ * @return TRUE if the fiels is required, or FALSE.
+ */
+gboolean gaim_request_field_is_required(const GaimRequestField *field);
+
 /*@}*/
 
 /**************************************************************************/
@@ -848,7 +935,7 @@
  * Creates a label field.
  *
  * @param id   The field ID.
- * @param text The optional label of the field.
+ * @param text The label of the field.
  *
  * @return The new field.
  */
@@ -858,6 +945,90 @@
 /*@}*/
 
 /**************************************************************************/
+/** @name Account Field API                                               */
+/**************************************************************************/
+/*@{*/
+
+/**
+ * Creates an account field.
+ *
+ * By default, this field will not show offline accounts.
+ *
+ * @param id      The field ID.
+ * @param text    The text label of the field.
+ * @param account The optional default account.
+ *
+ * @return The new field.
+ */
+GaimRequestField *gaim_request_field_account_new(const char *id,
+												 const char *text,
+												 GaimAccount *account);
+
+/**
+ * Sets the default account on an account field.
+ *
+ * @param field         The account field.
+ * @param default_value The default account.
+ */
+void gaim_request_field_account_set_default_value(GaimRequestField *field,
+												  GaimAccount *default_value);
+
+/**
+ * Sets the account in an account field.
+ *
+ * @param field The account field.
+ * @param value The account.
+ */
+void gaim_request_field_account_set_value(GaimRequestField *field,
+										  GaimAccount *value);
+
+/**
+ * Sets whether or not to show all accounts in an account field.
+ *
+ * If TRUE, all accounts, online or offline, will be shown. If FALSE,
+ * only online accounts will be shown.
+ *
+ * @param field    The account field.
+ * @param show_all Whether or not to show all accounts.
+ */
+void gaim_request_field_account_set_show_all(GaimRequestField *field,
+											 gboolean show_all);
+
+/**
+ * Returns the default account in an account field.
+ *
+ * @param field The field.
+ *
+ * @return The default account.
+ */
+GaimAccount *gaim_request_field_account_get_default_value(
+		const GaimRequestField *field);
+
+/**
+ * Returns the user-entered account in an account field.
+ *
+ * @param field The field.
+ *
+ * @return The user-entered account.
+ */
+GaimAccount *gaim_request_field_account_get_value(
+		const GaimRequestField *field);
+
+/**
+ * Returns whether or not to show all accounts in an account field.
+ *
+ * If TRUE, all accounts, online or offline, will be shown. If FALSE,
+ * only online accounts will be shown.
+ *
+ * @param field    The account field.
+ * @param show_all Whether or not to show all accounts.
+ */
+gboolean gaim_request_field_account_get_show_all(
+		const GaimRequestField *field);
+
+/*@}*/
+
+/**************************************************************************/
 /** @name Request API                                                     */
 /**************************************************************************/
 /*@{*/