# HG changeset patch
# Node ID c656e0c2a223
# Parent  51461c48c5b3
diff --git a/libpurple/protocols/demo/purpledemoprotocolactions.c b/libpurple/protocols/demo/purpledemoprotocolactions.c
--- a/libpurple/protocols/demo/purpledemoprotocolactions.c
+++ b/libpurple/protocols/demo/purpledemoprotocolactions.c
@@ -20,9 +20,10 @@
 
 #include "purpledemoprotocol.h"
 #include "purpledemoprotocolactions.h"
+#include "purpledemoresource.h"
 
 /******************************************************************************
- * Action Implementations
+ * Connection failure action implementations
  *****************************************************************************/
 #define REAPER_BUDDY_NAME ("Gary")
 #define DEFAULT_REAP_TIME (5)  /* seconds */
@@ -33,37 +34,6 @@
 #define TEMPORARY_TICK_PLURAL_STR N_("Pruning connection in %d seconds...")
 #define TEMPORARY_DISCONNECT_STR N_("%s pruned the connection")
 
-static const gchar *contacts[] = {"Alice", "Bob", "Carlos", "Erin" };
-
-static void
-purple_demo_protocol_remote_add(G_GNUC_UNUSED GSimpleAction *action,
-                                GVariant *parameter,
-                                G_GNUC_UNUSED gpointer data)
-{
-	PurpleAccount *account = NULL;
-	PurpleAccountManager *account_manager = NULL;
-	PurpleAddContactRequest *request = NULL;
-	PurpleNotification *notification = NULL;
-	PurpleNotificationManager *notification_manager = NULL;
-	const gchar *account_id = NULL;
-	static guint counter = 0;
-
-	account_id = g_variant_get_string(parameter, NULL);
-	account_manager = purple_account_manager_get_default();
-	account = purple_account_manager_find_by_id(account_manager, account_id);
-
-	request = purple_add_contact_request_new(account, contacts[counter]);
-	notification = purple_notification_new_from_add_contact_request(request);
-
-	notification_manager = purple_notification_manager_get_default();
-	purple_notification_manager_add(notification_manager, notification);
-
-	counter++;
-	if(counter >= G_N_ELEMENTS(contacts)) {
-		counter = 0;
-	}
-}
-
 static gboolean
 purple_demo_protocol_failure_tick(gpointer data,
                                   PurpleConnectionError error_code,
@@ -184,6 +154,511 @@ purple_demo_protocol_fatal_failure_actio
 }
 
 /******************************************************************************
+ * Request API action implementations
+ *****************************************************************************/
+
+static void
+purple_demo_protocol_request_input_ok_cb(G_GNUC_UNUSED gpointer data,
+                                         const char *value)
+{
+	g_message("Successfully requested input from UI: %s", value);
+}
+
+static void
+purple_demo_protocol_request_input_cancel_cb(G_GNUC_UNUSED gpointer data,
+                                             G_GNUC_UNUSED const char *value)
+{
+	g_message("UI cancelled input request");
+}
+
+static void
+purple_demo_protocol_request_input_activate(G_GNUC_UNUSED GSimpleAction *action,
+                                            GVariant *parameter,
+                                            G_GNUC_UNUSED gpointer data)
+{
+	PurpleConnection *connection = NULL;
+	const gchar *account_id = NULL;
+	PurpleAccountManager *manager = NULL;
+	PurpleAccount *account = NULL;
+	static int form = 0;
+	gboolean multiline = FALSE, masked = FALSE;
+	char *secondary = NULL;
+
+	if(!g_variant_is_of_type(parameter, G_VARIANT_TYPE_STRING)) {
+		g_critical("Demo failure action parameter is of incorrect type %s",
+		           g_variant_get_type_string(parameter));
+	}
+
+	account_id = g_variant_get_string(parameter, NULL);
+	manager = purple_account_manager_get_default();
+	account = purple_account_manager_find_by_id(manager, account_id);
+	connection = purple_account_get_connection(account);
+
+	masked = form % 2 == 1;
+	multiline = (form / 2) % 2 == 1;
+	form++;
+	secondary = g_strdup_printf("The input will be %s %s.",
+	                            masked ? "masked" : "unmasked",
+	                            multiline ? "multiple lines" : "single line");
+
+	purple_request_input(connection, "Request Input Demo",
+	                     "Please input some text…", secondary, "default",
+	                     multiline, masked, NULL,
+	                     _("OK"),
+	                     G_CALLBACK(purple_demo_protocol_request_input_ok_cb),
+	                     _("Cancel"),
+	                     G_CALLBACK(purple_demo_protocol_request_input_cancel_cb),
+	                     purple_request_cpar_from_connection(connection), NULL);
+}
+
+static void
+purple_demo_protocol_request_choice_ok_cb(G_GNUC_UNUSED gpointer data,
+                                          gpointer value)
+{
+	const char *text = value;
+
+	g_message("Successfully requested a choice from UI: %s", text);
+}
+
+static void
+purple_demo_protocol_request_choice_cancel_cb(G_GNUC_UNUSED gpointer data,
+                                              G_GNUC_UNUSED gpointer value)
+{
+	g_message("UI cancelled choice request");
+}
+
+static void
+purple_demo_protocol_request_choice_activate(G_GNUC_UNUSED GSimpleAction *action,
+                                             GVariant *parameter,
+                                             G_GNUC_UNUSED gpointer data)
+{
+	PurpleConnection *connection = NULL;
+	const gchar *account_id = NULL;
+	PurpleAccountManager *manager = NULL;
+	PurpleAccount *account = NULL;
+
+	if(!g_variant_is_of_type(parameter, G_VARIANT_TYPE_STRING)) {
+		g_critical("Demo failure action parameter is of incorrect type %s",
+		           g_variant_get_type_string(parameter));
+	}
+
+	account_id = g_variant_get_string(parameter, NULL);
+	manager = purple_account_manager_get_default();
+	account = purple_account_manager_find_by_id(manager, account_id);
+	connection = purple_account_get_connection(account);
+
+	purple_request_choice(connection, "Request Choice Demo",
+	                      "Please pick an option…", NULL, "foo",
+	                      _("OK"),
+	                      G_CALLBACK(purple_demo_protocol_request_choice_ok_cb),
+	                      _("Cancel"),
+	                      G_CALLBACK(purple_demo_protocol_request_choice_cancel_cb),
+	                      purple_request_cpar_from_connection(connection),
+	                      NULL, "foo", "foo", "bar", "bar", "baz", "baz", NULL);
+}
+
+static void
+purple_demo_protocol_request_action_cb(G_GNUC_UNUSED gpointer data, int action)
+{
+	g_message("Successfully requested an action from the UI: %d", action);
+}
+
+static void
+purple_demo_protocol_request_action_activate(G_GNUC_UNUSED GSimpleAction *action,
+                                             GVariant *parameter,
+                                             G_GNUC_UNUSED gpointer data)
+{
+	PurpleConnection *connection = NULL;
+	const gchar *account_id = NULL;
+	PurpleAccountManager *manager = NULL;
+	PurpleAccount *account = NULL;
+
+	if(!g_variant_is_of_type(parameter, G_VARIANT_TYPE_STRING)) {
+		g_critical("Demo failure action parameter is of incorrect type %s",
+		           g_variant_get_type_string(parameter));
+	}
+
+	account_id = g_variant_get_string(parameter, NULL);
+	manager = purple_account_manager_get_default();
+	account = purple_account_manager_find_by_id(manager, account_id);
+	connection = purple_account_get_connection(account);
+
+	purple_request_action(connection, "Request Action Demo",
+	                      "Please choose an action…", NULL, 1,
+	                      purple_request_cpar_from_connection(connection),
+	                      NULL, 3,
+	                      "foo", purple_demo_protocol_request_action_cb,
+	                      "bar", purple_demo_protocol_request_action_cb,
+	                      "baz", purple_demo_protocol_request_action_cb);
+}
+
+typedef struct {
+	gint id;
+	gpointer ui_handle;
+} PurpleDemoProtocolWaitData;
+
+static gboolean
+purple_demo_protocol_request_wait_pulse_cb(gpointer data) {
+	PurpleDemoProtocolWaitData *wait = data;
+
+	purple_request_wait_pulse(wait->ui_handle);
+
+	return G_SOURCE_CONTINUE;
+}
+
+static void
+purple_demo_protocol_request_wait_cancel_cb(G_GNUC_UNUSED gpointer data) {
+	g_message("UI cancelled wait request");
+}
+
+static void
+purple_demo_protocol_request_wait_close_cb(gpointer data) {
+	PurpleDemoProtocolWaitData *wait = data;
+
+	g_source_remove(wait->id);
+	g_free(wait);
+}
+
+static void
+purple_demo_protocol_request_wait_activate(G_GNUC_UNUSED GSimpleAction *action,
+                                           GVariant *parameter,
+                                           G_GNUC_UNUSED gpointer data)
+{
+	PurpleConnection *connection = NULL;
+	const gchar *account_id = NULL;
+	PurpleAccountManager *manager = NULL;
+	PurpleAccount *account = NULL;
+	PurpleDemoProtocolWaitData *wait = NULL;
+
+	if(!g_variant_is_of_type(parameter, G_VARIANT_TYPE_STRING)) {
+		g_critical("Demo failure action parameter is of incorrect type %s",
+		           g_variant_get_type_string(parameter));
+	}
+
+	account_id = g_variant_get_string(parameter, NULL);
+	manager = purple_account_manager_get_default();
+	account = purple_account_manager_find_by_id(manager, account_id);
+	connection = purple_account_get_connection(account);
+
+	wait = g_new0(PurpleDemoProtocolWaitData, 1);
+
+	wait->ui_handle = purple_request_wait(connection, "Request Wait Demo",
+	                                      "Please wait…", NULL, TRUE,
+	                                      purple_demo_protocol_request_wait_cancel_cb,
+	                                      purple_request_cpar_from_connection(connection),
+	                                      wait);
+
+	wait->id = g_timeout_add(250, purple_demo_protocol_request_wait_pulse_cb,
+	                         wait);
+
+	purple_request_add_close_notify(wait->ui_handle,
+	                                purple_demo_protocol_request_wait_close_cb,
+	                                wait);
+}
+
+static void
+purple_demo_protocol_request_fields_ok_cb(G_GNUC_UNUSED gpointer data,
+                                          PurpleRequestFields *fields)
+{
+	PurpleAccount *account = NULL;
+	PurpleRequestField *field = NULL;
+	GList *list = NULL;
+	const char *tmp = NULL;
+	GString *info = NULL;
+
+	info = g_string_new("Basic group:\n");
+
+	g_string_append_printf(info, "\tString: %s\n",
+	                       purple_request_fields_get_string(fields, "string"));
+	g_string_append_printf(info, "\tMultiline string: %s\n",
+	                       purple_request_fields_get_string(fields, "multiline-string"));
+	g_string_append_printf(info, "\tMasked string: %s\n",
+	                       purple_request_fields_get_string(fields, "masked-string"));
+	g_string_append_printf(info, "\tAlphanumeric string: %s\n",
+	                       purple_request_fields_get_string(fields, "alphanumeric"));
+	g_string_append_printf(info, "\tEmail string: %s\n",
+	                       purple_request_fields_get_string(fields, "email"));
+	g_string_append_printf(info, "\tInteger: %d\n",
+	                       purple_request_fields_get_integer(fields, "int"));
+	g_string_append_printf(info, "\tBoolean: %s\n",
+	                       purple_request_fields_get_bool(fields, "bool") ?
+	                       "TRUE" : "FALSE");
+
+	g_string_append(info, "Multiple-choice group:\n");
+
+	tmp = (const char *)purple_request_fields_get_choice(fields, "choice");
+	g_string_append_printf(info, "\tChoice: %s\n", tmp);
+
+	field = purple_request_fields_get_field(fields, "list");
+	list = purple_request_field_list_get_selected(field);
+	if(list != NULL) {
+		tmp = (const char *)list->data;
+	} else {
+		tmp = "(unset)";
+	}
+	g_string_append_printf(info, "\tList: %s\n", tmp);
+
+	field = purple_request_fields_get_field(fields, "multilist");
+	list = purple_request_field_list_get_selected(field);
+	g_string_append(info, "\tMulti-list: [");
+	while(list != NULL) {
+		tmp = (const char *)list->data;
+		g_string_append_printf(info, "%s%s", tmp,
+		                       list->next != NULL ? ", " : "");
+		list = list->next;
+	}
+	g_string_append(info, "]\n");
+
+	g_string_append(info, "Special group:\n");
+
+	account = purple_request_fields_get_account(fields, "account");
+	if(PURPLE_IS_ACCOUNT(account)) {
+		tmp = purple_contact_info_get_name_for_display(PURPLE_CONTACT_INFO(account));
+	} else {
+		tmp = "(unset)";
+	}
+	g_string_append_printf(info, "\tAccount: %s\n", tmp);
+
+	g_message("Successfully requested fields:\n%s", info->str);
+
+	g_string_free(info, TRUE);
+}
+
+static void
+purple_demo_protocol_request_fields_cancel_cb(G_GNUC_UNUSED gpointer data,
+                                              G_GNUC_UNUSED PurpleRequestFields *fields)
+{
+	g_message("UI cancelled field request");
+}
+
+static void
+purple_demo_protocol_request_fields_activate(G_GNUC_UNUSED GSimpleAction *action,
+                                             GVariant *parameter,
+                                             G_GNUC_UNUSED gpointer data)
+{
+	PurpleConnection *connection = NULL;
+	const gchar *account_id = NULL;
+	PurpleAccountManager *manager = NULL;
+	PurpleAccount *account = NULL;
+	PurpleRequestFields *fields = NULL;
+	PurpleRequestFieldGroup *group = NULL;
+	PurpleRequestField *field = NULL;
+	GBytes *icon = NULL;
+	gconstpointer icon_data = NULL;
+	gsize icon_len = 0;
+
+	if(!g_variant_is_of_type(parameter, G_VARIANT_TYPE_STRING)) {
+		g_critical("Demo failure action parameter is of incorrect type %s",
+		           g_variant_get_type_string(parameter));
+	}
+
+	account_id = g_variant_get_string(parameter, NULL);
+	manager = purple_account_manager_get_default();
+	account = purple_account_manager_find_by_id(manager, account_id);
+	connection = purple_account_get_connection(account);
+
+	fields = purple_request_fields_new();
+
+	/* This group will contain basic fields. */
+	group = purple_request_field_group_new("Basic");
+	purple_request_fields_add_group(fields, group);
+
+	field = purple_request_field_label_new("basic-label",
+	                                       "This group contains basic fields");
+	purple_request_field_group_add_field(group, field);
+
+	field = purple_request_field_string_new("string", "A string", "default",
+	                                        FALSE);
+	purple_request_field_group_add_field(group, field);
+	field = purple_request_field_string_new("multiline-string",
+	                                        "A multiline string", "default",
+	                                        TRUE);
+	purple_request_field_group_add_field(group, field);
+	field = purple_request_field_string_new("masked-string", "A masked string",
+	                                        "default", FALSE);
+	purple_request_field_string_set_masked(field, TRUE);
+	purple_request_field_group_add_field(group, field);
+	field = purple_request_field_string_new("alphanumeric",
+	                                        "An alphanumeric string",
+	                                        "default", FALSE);
+	purple_request_field_set_validator(field,
+	                                   purple_request_field_alphanumeric_validator,
+	                                   NULL);
+	purple_request_field_group_add_field(group, field);
+	field = purple_request_field_string_new("email", "An email", "me@example.com",
+	                                        FALSE);
+	purple_request_field_set_validator(field,
+	                                   purple_request_field_email_validator,
+	                                   NULL);
+	purple_request_field_group_add_field(group, field);
+	field = purple_request_field_int_new("int", "An integer", 123, -42, 1337);
+	purple_request_field_group_add_field(group, field);
+	field = purple_request_field_bool_new("bool", "A boolean", FALSE);
+	purple_request_field_group_add_field(group, field);
+
+	/* This group will contain fields with multiple options. */
+	group = purple_request_field_group_new("Multiple");
+	purple_request_fields_add_group(fields, group);
+
+	field = purple_request_field_label_new("multiple-label",
+	                                       "This group contains fields with multiple options");
+	purple_request_field_group_add_field(group, field);
+
+	field = purple_request_field_choice_new("choice", "A choice", "foo");
+	purple_request_field_choice_add(field, "foo", "foo");
+	purple_request_field_choice_add(field, "bar", "bar");
+	purple_request_field_choice_add(field, "baz", "baz");
+	purple_request_field_choice_add(field, "quux", "quux");
+	purple_request_field_group_add_field(group, field);
+
+	field = purple_request_field_list_new("list", "A list");
+	purple_request_field_list_add_icon(field, "foo", NULL, "foo");
+	purple_request_field_list_add_icon(field, "bar", NULL, "bar");
+	purple_request_field_list_add_icon(field, "baz", NULL, "baz");
+	purple_request_field_list_add_icon(field, "quux", NULL, "quux");
+	purple_request_field_group_add_field(group, field);
+
+	field = purple_request_field_list_new("multilist", "A multi-select list");
+	purple_request_field_list_set_multi_select(field, TRUE);
+	purple_request_field_list_add_icon(field, "foo", NULL, "foo");
+	purple_request_field_list_add_icon(field, "bar", NULL, "bar");
+	purple_request_field_list_add_icon(field, "baz", NULL, "baz");
+	purple_request_field_list_add_icon(field, "quux", NULL, "quux");
+	purple_request_field_group_add_field(group, field);
+
+	/* This group will contain specialized fields. */
+	group = purple_request_field_group_new("Special");
+	purple_request_fields_add_group(fields, group);
+
+	field = purple_request_field_label_new("special-label",
+	                                       "This group contains specialized fields");
+	purple_request_field_group_add_field(group, field);
+
+	icon = g_resource_lookup_data(purple_demo_get_resource(),
+	                              "/im/pidgin/purple/demo/icons/scalable/apps/im-purple-demo.svg",
+	                              G_RESOURCE_LOOKUP_FLAGS_NONE, NULL);
+	icon_data = g_bytes_get_data(icon, &icon_len);
+	field = purple_request_field_image_new("image", "An image", icon_data, icon_len);
+	purple_request_field_group_add_field(group, field);
+	g_bytes_unref(icon);
+
+	field = purple_request_field_account_new("account", "An account", account);
+	purple_request_field_group_add_field(group, field);
+
+	purple_request_fields(connection, "Request Fields Demo",
+	                      "Please fill out these fields…", NULL, fields,
+	                      _("OK"),
+	                      G_CALLBACK(purple_demo_protocol_request_fields_ok_cb),
+	                      _("Cancel"),
+	                      G_CALLBACK(purple_demo_protocol_request_fields_cancel_cb),
+	                      purple_request_cpar_from_connection(connection),
+	                      NULL);
+}
+
+static void
+purple_demo_protocol_request_path_ok_cb(gpointer data, const char *filename) {
+	const char *type = data;
+
+	g_message("Successfully requested %s from UI: %s", type, filename);
+}
+
+static void
+purple_demo_protocol_request_path_cancel_cb(gpointer data) {
+	const char *type = data;
+
+	g_message("UI cancelled %s request", type);
+}
+
+static void
+purple_demo_protocol_request_file_activate(G_GNUC_UNUSED GSimpleAction *action,
+                                           GVariant *parameter,
+                                           G_GNUC_UNUSED gpointer data)
+{
+	PurpleConnection *connection = NULL;
+	const gchar *account_id = NULL;
+	PurpleAccountManager *manager = NULL;
+	PurpleAccount *account = NULL;
+
+	if(!g_variant_is_of_type(parameter, G_VARIANT_TYPE_STRING)) {
+		g_critical("Demo failure action parameter is of incorrect type %s",
+		           g_variant_get_type_string(parameter));
+	}
+
+	account_id = g_variant_get_string(parameter, NULL);
+	manager = purple_account_manager_get_default();
+	account = purple_account_manager_find_by_id(manager, account_id);
+	connection = purple_account_get_connection(account);
+
+	purple_request_file(connection, "Request File Demo",
+	                    "example.txt", FALSE,
+	                    G_CALLBACK(purple_demo_protocol_request_path_ok_cb),
+	                    G_CALLBACK(purple_demo_protocol_request_path_cancel_cb),
+	                    purple_request_cpar_from_connection(connection),
+	                    "file");
+}
+
+static void
+purple_demo_protocol_request_folder_activate(G_GNUC_UNUSED GSimpleAction *action,
+                                             GVariant *parameter,
+                                             G_GNUC_UNUSED gpointer data)
+{
+	PurpleConnection *connection = NULL;
+	const gchar *account_id = NULL;
+	PurpleAccountManager *manager = NULL;
+	PurpleAccount *account = NULL;
+
+	if(!g_variant_is_of_type(parameter, G_VARIANT_TYPE_STRING)) {
+		g_critical("Demo failure action parameter is of incorrect type %s",
+		           g_variant_get_type_string(parameter));
+	}
+
+	account_id = g_variant_get_string(parameter, NULL);
+	manager = purple_account_manager_get_default();
+	account = purple_account_manager_find_by_id(manager, account_id);
+	connection = purple_account_get_connection(account);
+
+	purple_request_folder(connection, "Request Folder Demo", NULL,
+	                      G_CALLBACK(purple_demo_protocol_request_path_ok_cb),
+	                      G_CALLBACK(purple_demo_protocol_request_path_cancel_cb),
+	                      purple_request_cpar_from_connection(connection),
+	                      "folder");
+}
+
+/******************************************************************************
+ * Contact action implementations
+ *****************************************************************************/
+static const gchar *contacts[] = {"Alice", "Bob", "Carlos", "Erin" };
+
+static void
+purple_demo_protocol_remote_add(G_GNUC_UNUSED GSimpleAction *action,
+                                GVariant *parameter,
+                                G_GNUC_UNUSED gpointer data)
+{
+	PurpleAccount *account = NULL;
+	PurpleAccountManager *account_manager = NULL;
+	PurpleAddContactRequest *request = NULL;
+	PurpleNotification *notification = NULL;
+	PurpleNotificationManager *notification_manager = NULL;
+	const gchar *account_id = NULL;
+	static guint counter = 0;
+
+	account_id = g_variant_get_string(parameter, NULL);
+	account_manager = purple_account_manager_get_default();
+	account = purple_account_manager_find_by_id(account_manager, account_id);
+
+	request = purple_add_contact_request_new(account, contacts[counter]);
+	notification = purple_notification_new_from_add_contact_request(request);
+
+	notification_manager = purple_notification_manager_get_default();
+	purple_notification_manager_add(notification_manager, notification);
+
+	counter++;
+	if(counter >= G_N_ELEMENTS(contacts)) {
+		counter = 0;
+	}
+}
+
+/******************************************************************************
  * PurpleProtocolActions Implementation
  *****************************************************************************/
 static const gchar *
@@ -201,8 +676,7 @@ purple_demo_protocol_get_action_group(G_
 			.name = "temporary-failure",
 			.activate = purple_demo_protocol_temporary_failure_action_activate,
 			.parameter_type = "s",
-		},
-		{
+		}, {
 			.name = "fatal-failure",
 			.activate = purple_demo_protocol_fatal_failure_action_activate,
 			.parameter_type = "s",
@@ -210,6 +684,34 @@ purple_demo_protocol_get_action_group(G_
 			.name = "remote-add",
 			.activate = purple_demo_protocol_remote_add,
 			.parameter_type = "s",
+		}, {
+			.name = "request-input",
+			.activate = purple_demo_protocol_request_input_activate,
+			.parameter_type = "s",
+		}, {
+			.name = "request-choice",
+			.activate = purple_demo_protocol_request_choice_activate,
+			.parameter_type = "s",
+		}, {
+			.name = "request-action",
+			.activate = purple_demo_protocol_request_action_activate,
+			.parameter_type = "s",
+		}, {
+			.name = "request-wait",
+			.activate = purple_demo_protocol_request_wait_activate,
+			.parameter_type = "s",
+		}, {
+			.name = "request-fields",
+			.activate = purple_demo_protocol_request_fields_activate,
+			.parameter_type = "s",
+		}, {
+			.name = "request-file",
+			.activate = purple_demo_protocol_request_file_activate,
+			.parameter_type = "s",
+		}, {
+			.name = "request-folder",
+			.activate = purple_demo_protocol_request_folder_activate,
+			.parameter_type = "s",
 		}
 	};
 	gsize nentries = G_N_ELEMENTS(entries);
@@ -226,6 +728,7 @@ purple_demo_protocol_get_menu(G_GNUC_UNU
                               G_GNUC_UNUSED PurpleConnection *connection)
 {
 	GMenu *menu = NULL;
+	GMenu *submenu = NULL;
 	GMenuItem *item = NULL;
 
 	menu = g_menu_new();
@@ -251,6 +754,53 @@ purple_demo_protocol_get_menu(G_GNUC_UNU
 	g_menu_append_item(menu, item);
 	g_object_unref(item);
 
+	submenu = g_menu_new();
+
+	item = g_menu_item_new(_("Input"), "prpl-demo.request-input");
+	g_menu_item_set_attribute(item, PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET, "s",
+	                          "account");
+	g_menu_append_item(submenu, item);
+	g_object_unref(item);
+
+	item = g_menu_item_new(_("Choice"), "prpl-demo.request-choice");
+	g_menu_item_set_attribute(item, PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET, "s",
+	                          "account");
+	g_menu_append_item(submenu, item);
+	g_object_unref(item);
+
+	item = g_menu_item_new(_("Action"), "prpl-demo.request-action");
+	g_menu_item_set_attribute(item, PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET, "s",
+	                          "account");
+	g_menu_append_item(submenu, item);
+	g_object_unref(item);
+
+	item = g_menu_item_new(_("Wait"), "prpl-demo.request-wait");
+	g_menu_item_set_attribute(item, PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET, "s",
+	                          "account");
+	g_menu_append_item(submenu, item);
+	g_object_unref(item);
+
+	item = g_menu_item_new(_("Fields"), "prpl-demo.request-fields");
+	g_menu_item_set_attribute(item, PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET, "s",
+	                          "account");
+	g_menu_append_item(submenu, item);
+	g_object_unref(item);
+
+	item = g_menu_item_new(_("File"), "prpl-demo.request-file");
+	g_menu_item_set_attribute(item, PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET, "s",
+	                          "account");
+	g_menu_append_item(submenu, item);
+	g_object_unref(item);
+
+	item = g_menu_item_new(_("Folder"), "prpl-demo.request-folder");
+	g_menu_item_set_attribute(item, PURPLE_MENU_ATTRIBUTE_DYNAMIC_TARGET, "s",
+	                          "account");
+	g_menu_append_item(submenu, item);
+	g_object_unref(item);
+
+	g_menu_append_submenu(menu, _("Trigger requests"), G_MENU_MODEL(submenu));
+	g_object_unref(submenu);
+
 	return menu;
 }
 
