1 /**
2  * @file gntrequest.c GNT Request API
3  * @ingroup finch
4  */
5 
6 /* finch
7  *
8  * Finch is the legal property of its developers, whose names are too numerous
9  * to list here.  Please refer to the COPYRIGHT file distributed with this
10  * source distribution.
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
25  */
26 #include <internal.h>
27 
28 #include <gnt.h>
29 #include <gntbox.h>
30 #include <gntbutton.h>
31 #include <gntcheckbox.h>
32 #include <gntcombobox.h>
33 #include <gntentry.h>
34 #include <gntfilesel.h>
35 #include <gntlabel.h>
36 #include <gntline.h>
37 #include <gnttree.h>
38 
39 #include "finch.h"
40 #include "gntrequest.h"
41 #include "debug.h"
42 #include "util.h"
43 
44 /* XXX: Until gobjectification ... */
45 #undef FINCH_GET_DATA
46 #undef FINCH_SET_DATA
47 #define FINCH_GET_DATA(obj)  purple_request_field_get_ui_data(obj)
48 #define FINCH_SET_DATA(obj, data)  purple_request_field_set_ui_data(obj, data)
49 
50 typedef struct
51 {
52 	void *user_data;
53 	GntWidget *dialog;
54 	GCallback *cbs;
55 	gboolean save;
56 } FinchFileRequest;
57 
58 static GntWidget *
setup_request_window(const char * title,const char * primary,const char * secondary,PurpleRequestType type)59 setup_request_window(const char *title, const char *primary,
60 		const char *secondary, PurpleRequestType type)
61 {
62 	GntWidget *window;
63 
64 	window = gnt_vbox_new(FALSE);
65 	gnt_box_set_toplevel(GNT_BOX(window), TRUE);
66 	gnt_box_set_title(GNT_BOX(window), title);
67 	gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
68 
69 	if (primary)
70 		gnt_box_add_widget(GNT_BOX(window),
71 				gnt_label_new_with_format(primary, GNT_TEXT_FLAG_BOLD));
72 	if (secondary)
73 		gnt_box_add_widget(GNT_BOX(window), gnt_label_new(secondary));
74 
75 	g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(purple_request_close),
76 			GINT_TO_POINTER(type));
77 
78 	return window;
79 }
80 
81 /**
82  * If the window is closed by the wm (ie, without triggering any of
83  * the buttons, then do some default callback.
84  */
85 static void
setup_default_callback(GntWidget * window,gpointer default_cb,gpointer data)86 setup_default_callback(GntWidget *window, gpointer default_cb, gpointer data)
87 {
88 	if (default_cb == NULL)
89 		return;
90 	g_object_set_data(G_OBJECT(window), "default-callback", default_cb);
91 	g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(default_cb), data);
92 }
93 
94 static void
action_performed(GntWidget * button,gpointer data)95 action_performed(GntWidget *button, gpointer data)
96 {
97 	g_signal_handlers_disconnect_matched(data, G_SIGNAL_MATCH_FUNC,
98 			0, 0, NULL,
99 			g_object_get_data(data, "default-callback"),
100 			 NULL);
101 }
102 
103 /**
104  * window: this is the window
105  * userdata: the userdata to pass to the primary callbacks
106  * cb: the callback
107  * data: data for the callback
108  * (text, primary-callback) pairs, ended by a NULL
109  *
110  * The cancellation callback should be the last callback sent.
111  */
112 static GntWidget *
setup_button_box(GntWidget * win,gpointer userdata,gpointer cb,gpointer data,...)113 setup_button_box(GntWidget *win, gpointer userdata, gpointer cb, gpointer data, ...)
114 {
115 	GntWidget *box;
116 	GntWidget *button = NULL;
117 	va_list list;
118 	const char *text;
119 	gpointer callback;
120 
121 	box = gnt_hbox_new(FALSE);
122 
123 	va_start(list, data);
124 
125 	while ((text = va_arg(list, const char *)))
126 	{
127 		callback = va_arg(list, gpointer);
128 		button = gnt_button_new(text);
129 		gnt_box_add_widget(GNT_BOX(box), button);
130 		g_object_set_data(G_OBJECT(button), "activate-callback", callback);
131 		g_object_set_data(G_OBJECT(button), "activate-userdata", userdata);
132 		g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(action_performed), win);
133 		g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(cb), data);
134 	}
135 
136 	if (button)
137 		g_object_set_data(G_OBJECT(button), "cancellation-function", GINT_TO_POINTER(TRUE));
138 
139 	va_end(list);
140 	return box;
141 }
142 
143 static void
notify_input_cb(GntWidget * button,GntWidget * entry)144 notify_input_cb(GntWidget *button, GntWidget *entry)
145 {
146 	PurpleRequestInputCb callback = g_object_get_data(G_OBJECT(button), "activate-callback");
147 	gpointer data = g_object_get_data(G_OBJECT(button), "activate-userdata");
148 	const char *text = gnt_entry_get_text(GNT_ENTRY(entry));
149 	GntWidget *window;
150 
151 	if (callback)
152 		callback(data, text);
153 
154 	window = gnt_widget_get_toplevel(button);
155 	purple_request_close(PURPLE_REQUEST_INPUT, window);
156 }
157 
158 static void *
finch_request_input(const char * title,const char * primary,const char * secondary,const char * default_value,gboolean multiline,gboolean masked,gchar * hint,const char * ok_text,GCallback ok_cb,const char * cancel_text,GCallback cancel_cb,PurpleAccount * account,const char * who,PurpleConversation * conv,void * user_data)159 finch_request_input(const char *title, const char *primary,
160 		const char *secondary, const char *default_value,
161 		gboolean multiline, gboolean masked, gchar *hint,
162 		const char *ok_text, GCallback ok_cb,
163 		const char *cancel_text, GCallback cancel_cb,
164 		PurpleAccount *account, const char *who, PurpleConversation *conv,
165 		void *user_data)
166 {
167 	GntWidget *window, *box, *entry;
168 
169 	window = setup_request_window(title, primary, secondary, PURPLE_REQUEST_INPUT);
170 
171 	entry = gnt_entry_new(default_value);
172 	if (masked)
173 		gnt_entry_set_masked(GNT_ENTRY(entry), TRUE);
174 	gnt_box_add_widget(GNT_BOX(window), entry);
175 
176 	box = setup_button_box(window, user_data, notify_input_cb, entry,
177 			ok_text, ok_cb, cancel_text, cancel_cb, NULL);
178 	gnt_box_add_widget(GNT_BOX(window), box);
179 
180 	setup_default_callback(window, cancel_cb, user_data);
181 	gnt_widget_show(window);
182 
183 	return window;
184 }
185 
186 static void
finch_close_request(PurpleRequestType type,gpointer ui_handle)187 finch_close_request(PurpleRequestType type, gpointer ui_handle)
188 {
189 	GntWidget *widget = GNT_WIDGET(ui_handle);
190 	if (type == PURPLE_REQUEST_FIELDS) {
191 		PurpleRequestFields *fields = g_object_get_data(G_OBJECT(widget), "fields");
192 		purple_request_fields_destroy(fields);
193 	}
194 
195 	widget = gnt_widget_get_toplevel(widget);
196 	gnt_widget_destroy(widget);
197 }
198 
199 static void
request_choice_cb(GntWidget * button,GntComboBox * combo)200 request_choice_cb(GntWidget *button, GntComboBox *combo)
201 {
202 	PurpleRequestChoiceCb callback = g_object_get_data(G_OBJECT(button), "activate-callback");
203 	gpointer data = g_object_get_data(G_OBJECT(button), "activate-userdata");
204 	int choice = GPOINTER_TO_INT(gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo))) - 1;
205 	GntWidget *window;
206 
207 	if (callback)
208 		callback(data, choice);
209 
210 	window = gnt_widget_get_toplevel(button);
211 	purple_request_close(PURPLE_REQUEST_INPUT, window);
212 }
213 
214 static void *
finch_request_choice(const char * title,const char * primary,const char * secondary,int default_value,const char * ok_text,GCallback ok_cb,const char * cancel_text,GCallback cancel_cb,PurpleAccount * account,const char * who,PurpleConversation * conv,void * user_data,va_list choices)215 finch_request_choice(const char *title, const char *primary,
216 		const char *secondary, int default_value,
217 		const char *ok_text, GCallback ok_cb,
218 		const char *cancel_text, GCallback cancel_cb,
219 		PurpleAccount *account, const char *who, PurpleConversation *conv,
220 		void *user_data, va_list choices)
221 {
222 	GntWidget *window, *combo, *box;
223 	const char *text;
224 	int val;
225 
226 	window = setup_request_window(title, primary, secondary, PURPLE_REQUEST_CHOICE);
227 
228 	combo = gnt_combo_box_new();
229 	gnt_box_add_widget(GNT_BOX(window), combo);
230 	while ((text = va_arg(choices, const char *)))
231 	{
232 		val = va_arg(choices, int);
233 		gnt_combo_box_add_data(GNT_COMBO_BOX(combo), GINT_TO_POINTER(val + 1), text);
234 	}
235 	gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), GINT_TO_POINTER(default_value + 1));
236 
237 	box = setup_button_box(window, user_data, request_choice_cb, combo,
238 			ok_text, ok_cb, cancel_text, cancel_cb, NULL);
239 	gnt_box_add_widget(GNT_BOX(window), box);
240 
241 	setup_default_callback(window, cancel_cb, user_data);
242 	gnt_widget_show(window);
243 
244 	return window;
245 }
246 
247 static void
request_action_cb(GntWidget * button,GntWidget * window)248 request_action_cb(GntWidget *button, GntWidget *window)
249 {
250 	PurpleRequestActionCb callback = g_object_get_data(G_OBJECT(button), "activate-callback");
251 	gpointer data = g_object_get_data(G_OBJECT(button), "activate-userdata");
252 	int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), "activate-id"));
253 
254 	if (callback)
255 		callback(data, id);
256 
257 	purple_request_close(PURPLE_REQUEST_ACTION, window);
258 }
259 
260 static void*
finch_request_action(const char * title,const char * primary,const char * secondary,int default_value,PurpleAccount * account,const char * who,PurpleConversation * conv,void * user_data,size_t actioncount,va_list actions)261 finch_request_action(const char *title, const char *primary,
262 		const char *secondary, int default_value,
263 		PurpleAccount *account, const char *who, PurpleConversation *conv,
264 		void *user_data, size_t actioncount,
265 		va_list actions)
266 {
267 	GntWidget *window, *box, *button, *focus = NULL;
268 	gsize i;
269 
270 	window = setup_request_window(title, primary, secondary, PURPLE_REQUEST_ACTION);
271 
272 	box = gnt_hbox_new(FALSE);
273 	gnt_box_add_widget(GNT_BOX(window), box);
274 	for (i = 0; i < actioncount; i++)
275 	{
276 		const char *text = va_arg(actions, const char *);
277 		PurpleRequestActionCb callback = va_arg(actions, PurpleRequestActionCb);
278 
279 		button = gnt_button_new(text);
280 		gnt_box_add_widget(GNT_BOX(box), button);
281 
282 		g_object_set_data(G_OBJECT(button), "activate-callback", callback);
283 		g_object_set_data(G_OBJECT(button), "activate-userdata", user_data);
284 		g_object_set_data(G_OBJECT(button), "activate-id", GINT_TO_POINTER(i));
285 		g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(request_action_cb), window);
286 
287 		if (default_value >= 0 && i == (gsize)default_value)
288 			focus = button;
289 	}
290 
291 	gnt_widget_show(window);
292 	if (focus)
293 		gnt_box_give_focus_to_child(GNT_BOX(window), focus);
294 
295 	return window;
296 }
297 
298 static void
request_fields_cb(GntWidget * button,PurpleRequestFields * fields)299 request_fields_cb(GntWidget *button, PurpleRequestFields *fields)
300 {
301 	PurpleRequestFieldsCb callback = g_object_get_data(G_OBJECT(button), "activate-callback");
302 	gpointer data = g_object_get_data(G_OBJECT(button), "activate-userdata");
303 	GList *list;
304 	GntWidget *window;
305 
306 	/* Update the data of the fields. Pidgin does this differently. Instead of
307 	 * updating the fields at the end like here, it updates the appropriate field
308 	 * instantly whenever a change is made. That allows it to make sure the
309 	 * 'required' fields are entered before the user can hit OK. It's not the case
310 	 * here, althought it can be done. */
311 	for (list = purple_request_fields_get_groups(fields); list; list = list->next)
312 	{
313 		PurpleRequestFieldGroup *group = list->data;
314 		GList *fields = purple_request_field_group_get_fields(group);
315 
316 		for (; fields ; fields = fields->next)
317 		{
318 			PurpleRequestField *field = fields->data;
319 			PurpleRequestFieldType type = purple_request_field_get_type(field);
320 			if (!purple_request_field_is_visible(field))
321 				continue;
322 			if (type == PURPLE_REQUEST_FIELD_BOOLEAN)
323 			{
324 				GntWidget *check = FINCH_GET_DATA(field);
325 				gboolean value = gnt_check_box_get_checked(GNT_CHECK_BOX(check));
326 				purple_request_field_bool_set_value(field, value);
327 			}
328 			else if (type == PURPLE_REQUEST_FIELD_STRING)
329 			{
330 				GntWidget *entry = FINCH_GET_DATA(field);
331 				const char *text = gnt_entry_get_text(GNT_ENTRY(entry));
332 				purple_request_field_string_set_value(field, (text && *text) ? text : NULL);
333 			}
334 			else if (type == PURPLE_REQUEST_FIELD_INTEGER)
335 			{
336 				GntWidget *entry = FINCH_GET_DATA(field);
337 				const char *text = gnt_entry_get_text(GNT_ENTRY(entry));
338 				int value = (text && *text) ? atoi(text) : 0;
339 				purple_request_field_int_set_value(field, value);
340 			}
341 			else if (type == PURPLE_REQUEST_FIELD_CHOICE)
342 			{
343 				GntWidget *combo = FINCH_GET_DATA(field);
344 				int id;
345 				id = GPOINTER_TO_INT(gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo)));
346 				purple_request_field_choice_set_value(field, id);
347 			}
348 			else if (type == PURPLE_REQUEST_FIELD_LIST)
349 			{
350 				GList *list = NULL, *iter;
351 				if (purple_request_field_list_get_multi_select(field))
352 				{
353 					GntWidget *tree = FINCH_GET_DATA(field);
354 
355 					iter = purple_request_field_list_get_items(field);
356 					for (; iter; iter = iter->next)
357 					{
358 						const char *text = iter->data;
359 						gpointer key = purple_request_field_list_get_data(field, text);
360 						if (gnt_tree_get_choice(GNT_TREE(tree), key))
361 							list = g_list_prepend(list, (gpointer)text);
362 					}
363 				}
364 				else
365 				{
366 					GntWidget *combo = FINCH_GET_DATA(field);
367 					gpointer data = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo));
368 
369 					iter = purple_request_field_list_get_items(field);
370 					for (; iter; iter = iter->next) {
371 						const char *text = iter->data;
372 						gpointer key = purple_request_field_list_get_data(field, text);
373 						if (key == data) {
374 							list = g_list_prepend(list, (gpointer)text);
375 							break;
376 						}
377 					}
378 				}
379 
380 				purple_request_field_list_set_selected(field, list);
381 				g_list_free(list);
382 			}
383 			else if (type == PURPLE_REQUEST_FIELD_ACCOUNT)
384 			{
385 				GntWidget *combo = FINCH_GET_DATA(field);
386 				PurpleAccount *acc = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo));
387 				purple_request_field_account_set_value(field, acc);
388 			}
389 		}
390 	}
391 
392 	purple_notify_close_with_handle(button);
393 
394 	if (!g_object_get_data(G_OBJECT(button), "cancellation-function") &&
395 			!purple_request_fields_all_required_filled(fields)) {
396 		purple_notify_error(button, _("Error"),
397 				_("You must fill all the required fields."),
398 				_("The required fields are underlined."));
399 		return;
400 	}
401 
402 	if (callback)
403 		callback(data, fields);
404 
405 	window = gnt_widget_get_toplevel(button);
406 	purple_request_close(PURPLE_REQUEST_FIELDS, window);
407 }
408 
409 static void
update_selected_account(GntEntry * username,const char * start,const char * end,GntComboBox * accountlist)410 update_selected_account(GntEntry *username, const char *start, const char *end,
411 		GntComboBox *accountlist)
412 {
413 	GList *accounts =
414 		gnt_tree_get_rows(GNT_TREE(gnt_combo_box_get_dropdown(accountlist)));
415 	const char *name = gnt_entry_get_text(username);
416 	while (accounts) {
417 		if (purple_find_buddy(accounts->data, name)) {
418 			gnt_combo_box_set_selected(accountlist, accounts->data);
419 			gnt_widget_draw(GNT_WIDGET(accountlist));
420 			break;
421 		}
422 		accounts = accounts->next;
423 	}
424 }
425 
426 static GntWidget*
create_boolean_field(PurpleRequestField * field)427 create_boolean_field(PurpleRequestField *field)
428 {
429 	const char *label = purple_request_field_get_label(field);
430 	GntWidget *check = gnt_check_box_new(label);
431 	gnt_check_box_set_checked(GNT_CHECK_BOX(check),
432 			purple_request_field_bool_get_default_value(field));
433 	return check;
434 }
435 
436 static GntWidget*
create_string_field(PurpleRequestField * field,GntWidget ** username)437 create_string_field(PurpleRequestField *field, GntWidget **username)
438 {
439 	const char *hint = purple_request_field_get_type_hint(field);
440 	GntWidget *entry = gnt_entry_new(
441 			purple_request_field_string_get_default_value(field));
442 	gnt_entry_set_masked(GNT_ENTRY(entry),
443 			purple_request_field_string_is_masked(field));
444 	if (hint && purple_str_has_prefix(hint, "screenname")) {
445 		PurpleBlistNode *node = purple_blist_get_root();
446 		gboolean offline = purple_str_has_suffix(hint, "all");
447 		for (; node; node = purple_blist_node_next(node, offline)) {
448 			if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
449 				continue;
450 			gnt_entry_add_suggest(GNT_ENTRY(entry), purple_buddy_get_name((PurpleBuddy*)node));
451 		}
452 		gnt_entry_set_always_suggest(GNT_ENTRY(entry), TRUE);
453 		if (username)
454 			*username = entry;
455 	} else if (purple_strequal(hint, "group")) {
456 		PurpleBlistNode *node;
457 		for (node = purple_blist_get_root(); node;
458 				node = purple_blist_node_get_sibling_next(node)) {
459 			if (PURPLE_BLIST_NODE_IS_GROUP(node))
460 				gnt_entry_add_suggest(GNT_ENTRY(entry), purple_group_get_name((PurpleGroup *)node));
461 		}
462 	}
463 	return entry;
464 }
465 
466 static GntWidget*
create_integer_field(PurpleRequestField * field)467 create_integer_field(PurpleRequestField *field)
468 {
469 	char str[256];
470 	int val = purple_request_field_int_get_default_value(field);
471 	GntWidget *entry;
472 
473 	snprintf(str, sizeof(str), "%d", val);
474 	entry = gnt_entry_new(str);
475 	gnt_entry_set_flag(GNT_ENTRY(entry), GNT_ENTRY_FLAG_INT);
476 	return entry;
477 }
478 
479 static GntWidget*
create_choice_field(PurpleRequestField * field)480 create_choice_field(PurpleRequestField *field)
481 {
482 	int id;
483 	GList *list;
484 	GntWidget *combo = gnt_combo_box_new();
485 
486 	list = purple_request_field_choice_get_labels(field);
487 	for (id = 1; list; list = list->next, id++)
488 	{
489 		gnt_combo_box_add_data(GNT_COMBO_BOX(combo),
490 				GINT_TO_POINTER(id), list->data);
491 	}
492 	gnt_combo_box_set_selected(GNT_COMBO_BOX(combo),
493 			GINT_TO_POINTER(purple_request_field_choice_get_default_value(field)));
494 	return combo;
495 }
496 
497 static GntWidget*
create_list_field(PurpleRequestField * field)498 create_list_field(PurpleRequestField *field)
499 {
500 	GntWidget *ret = NULL;
501 	GList *list;
502 	gboolean multi = purple_request_field_list_get_multi_select(field);
503 	if (multi)
504 	{
505 		GntWidget *tree = gnt_tree_new();
506 
507 		list = purple_request_field_list_get_items(field);
508 		for (; list; list = list->next)
509 		{
510 			const char *text = list->data;
511 			gpointer key = purple_request_field_list_get_data(field, text);
512 			gnt_tree_add_choice(GNT_TREE(tree), key,
513 					gnt_tree_create_row(GNT_TREE(tree), text), NULL, NULL);
514 			if (purple_request_field_list_is_selected(field, text))
515 				gnt_tree_set_choice(GNT_TREE(tree), key, TRUE);
516 		}
517 		ret = tree;
518 	}
519 	else
520 	{
521 		GntWidget *combo = gnt_combo_box_new();
522 
523 		list = purple_request_field_list_get_items(field);
524 		for (; list; list = list->next)
525 		{
526 			const char *text = list->data;
527 			gpointer key = purple_request_field_list_get_data(field, text);
528 			gnt_combo_box_add_data(GNT_COMBO_BOX(combo), key, text);
529 			if (purple_request_field_list_is_selected(field, text))
530 				gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), key);
531 		}
532 		ret = combo;
533 	}
534 	return ret;
535 }
536 
537 static GntWidget*
create_account_field(PurpleRequestField * field)538 create_account_field(PurpleRequestField *field)
539 {
540 	gboolean all;
541 	PurpleAccount *def;
542 	GList *list;
543 	GntWidget *combo = gnt_combo_box_new();
544 
545 	all = purple_request_field_account_get_show_all(field);
546 	def = purple_request_field_account_get_value(field);
547 	if (!def)
548 		def = purple_request_field_account_get_default_value(field);
549 
550 	if (all)
551 		list = purple_accounts_get_all();
552 	else
553 		list = purple_connections_get_all();
554 
555 	for (; list; list = list->next)
556 	{
557 		PurpleAccount *account;
558 		char *text;
559 
560 		if (all)
561 			account = list->data;
562 		else
563 			account = purple_connection_get_account(list->data);
564 
565 		text = g_strdup_printf("%s (%s)",
566 				purple_account_get_username(account),
567 				purple_account_get_protocol_name(account));
568 		gnt_combo_box_add_data(GNT_COMBO_BOX(combo), account, text);
569 		g_free(text);
570 		if (account == def)
571 			gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), account);
572 	}
573 	gnt_widget_set_size(combo, 20, 3); /* ew */
574 	return combo;
575 }
576 
577 static void *
finch_request_fields(const char * title,const char * primary,const char * secondary,PurpleRequestFields * allfields,const char * ok,GCallback ok_cb,const char * cancel,GCallback cancel_cb,PurpleAccount * account,const char * who,PurpleConversation * conv,void * userdata)578 finch_request_fields(const char *title, const char *primary,
579 		const char *secondary, PurpleRequestFields *allfields,
580 		const char *ok, GCallback ok_cb,
581 		const char *cancel, GCallback cancel_cb,
582 		PurpleAccount *account, const char *who, PurpleConversation *conv,
583 		void *userdata)
584 {
585 	GntWidget *window, *box;
586 	GList *grlist;
587 	GntWidget *username = NULL, *accountlist = NULL;
588 
589 	window = setup_request_window(title, primary, secondary, PURPLE_REQUEST_FIELDS);
590 
591 	/* This is how it's going to work: the request-groups are going to be
592 	 * stacked vertically one after the other. A GntLine will be separating
593 	 * the groups. */
594 	box = gnt_vbox_new(FALSE);
595 	gnt_box_set_pad(GNT_BOX(box), 0);
596 	gnt_box_set_fill(GNT_BOX(box), TRUE);
597 	for (grlist = purple_request_fields_get_groups(allfields); grlist; grlist = grlist->next)
598 	{
599 		PurpleRequestFieldGroup *group = grlist->data;
600 		GList *fields = purple_request_field_group_get_fields(group);
601 		GntWidget *hbox;
602 		const char *title = purple_request_field_group_get_title(group);
603 
604 		if (title)
605 			gnt_box_add_widget(GNT_BOX(box),
606 					gnt_label_new_with_format(title, GNT_TEXT_FLAG_BOLD));
607 
608 		for (; fields ; fields = fields->next)
609 		{
610 			PurpleRequestField *field = fields->data;
611 			PurpleRequestFieldType type = purple_request_field_get_type(field);
612 			const char *label = purple_request_field_get_label(field);
613 
614 			if (!purple_request_field_is_visible(field))
615 				continue;
616 
617 			hbox = gnt_hbox_new(TRUE);   /* hrm */
618 			gnt_box_add_widget(GNT_BOX(box), hbox);
619 
620 			if (type != PURPLE_REQUEST_FIELD_BOOLEAN && label)
621 			{
622 				GntWidget *l;
623 				if (purple_request_field_is_required(field))
624 					l = gnt_label_new_with_format(label, GNT_TEXT_FLAG_UNDERLINE);
625 				else
626 					l = gnt_label_new(label);
627 				gnt_widget_set_size(l, 0, 1);
628 				gnt_box_add_widget(GNT_BOX(hbox), l);
629 			}
630 
631 			if (type == PURPLE_REQUEST_FIELD_BOOLEAN)
632 			{
633 				FINCH_SET_DATA(field, create_boolean_field(field));
634 			}
635 			else if (type == PURPLE_REQUEST_FIELD_STRING)
636 			{
637 				FINCH_SET_DATA(field, create_string_field(field, &username));
638 			}
639 			else if (type == PURPLE_REQUEST_FIELD_INTEGER)
640 			{
641 				FINCH_SET_DATA(field, create_integer_field(field));
642 			}
643 			else if (type == PURPLE_REQUEST_FIELD_CHOICE)
644 			{
645 				FINCH_SET_DATA(field, create_choice_field(field));
646 			}
647 			else if (type == PURPLE_REQUEST_FIELD_LIST)
648 			{
649 				FINCH_SET_DATA(field, create_list_field(field));
650 			}
651 			else if (type == PURPLE_REQUEST_FIELD_ACCOUNT)
652 			{
653 				accountlist = create_account_field(field);
654 				FINCH_SET_DATA(field, accountlist);
655 			}
656 			else
657 			{
658 				FINCH_SET_DATA(field, gnt_label_new_with_format(_("Not implemented yet."),
659 						GNT_TEXT_FLAG_BOLD));
660 			}
661 			gnt_box_set_alignment(GNT_BOX(hbox), GNT_ALIGN_MID);
662 			gnt_box_add_widget(GNT_BOX(hbox), GNT_WIDGET(FINCH_GET_DATA(field)));
663 		}
664 		if (grlist->next)
665 			gnt_box_add_widget(GNT_BOX(box), gnt_hline_new());
666 	}
667 	gnt_box_add_widget(GNT_BOX(window), box);
668 
669 	box = setup_button_box(window, userdata, request_fields_cb, allfields,
670 			ok, ok_cb, cancel, cancel_cb, NULL);
671 	gnt_box_add_widget(GNT_BOX(window), box);
672 
673 	setup_default_callback(window, cancel_cb, userdata);
674 	gnt_widget_show(window);
675 
676 	if (username && accountlist) {
677 		g_signal_connect(username, "completion", G_CALLBACK(update_selected_account), accountlist);
678 	}
679 
680 	g_object_set_data(G_OBJECT(window), "fields", allfields);
681 
682 	return window;
683 }
684 
685 static void
file_cancel_cb(GntWidget * wid,gpointer fq)686 file_cancel_cb(GntWidget *wid, gpointer fq)
687 {
688 	FinchFileRequest *data = fq;
689 	if (data->dialog == NULL) {
690 		/* We've already handled this request, and are in the destroy handler. */
691 		return;
692 	}
693 
694 	if (data->cbs[1] != NULL)
695 		((PurpleRequestFileCb)data->cbs[1])(data->user_data, NULL);
696 
697 	purple_request_close(PURPLE_REQUEST_FILE, data->dialog);
698 	data->dialog = NULL;
699 }
700 
701 static void
file_ok_cb(GntWidget * widget,const char * path,const char * file,gpointer fq)702 file_ok_cb(GntWidget *widget, const char *path, const char *file, gpointer fq)
703 {
704 	FinchFileRequest *data = fq;
705 	char *dir = g_path_get_dirname(path);
706 	if (data->cbs[0] != NULL)
707 		((PurpleRequestFileCb)data->cbs[0])(data->user_data, file);
708 	purple_prefs_set_path(data->save ? "/finch/filelocations/last_save_folder" :
709 			"/finch/filelocations/last_open_folder", dir);
710 	g_free(dir);
711 
712 	purple_request_close(PURPLE_REQUEST_FILE, data->dialog);
713 	data->dialog = NULL;
714 }
715 
716 static void
file_request_destroy(FinchFileRequest * data)717 file_request_destroy(FinchFileRequest *data)
718 {
719 	g_free(data->cbs);
720 	g_free(data);
721 }
722 
723 static FinchFileRequest *
finch_file_request_window(const char * title,const char * path,GCallback ok_cb,GCallback cancel_cb,void * user_data)724 finch_file_request_window(const char *title, const char *path,
725 				GCallback ok_cb, GCallback cancel_cb,
726 				void *user_data)
727 {
728 	GntWidget *window = gnt_file_sel_new();
729 	GntFileSel *sel = GNT_FILE_SEL(window);
730 	FinchFileRequest *data = g_new0(FinchFileRequest, 1);
731 
732 	data->user_data = user_data;
733 	data->cbs = g_new0(GCallback, 2);
734 	data->cbs[0] = ok_cb;
735 	data->cbs[1] = cancel_cb;
736 	data->dialog = window;
737 	gnt_box_set_title(GNT_BOX(window), title);
738 
739 	gnt_file_sel_set_current_location(sel, (path && *path) ? path : purple_home_dir());
740 
741 	g_signal_connect(G_OBJECT(sel), "destroy",
742 			G_CALLBACK(file_cancel_cb), data);
743 	g_signal_connect(G_OBJECT(sel), "cancelled",
744 			G_CALLBACK(file_cancel_cb), data);
745 	g_signal_connect(G_OBJECT(sel), "file_selected",
746 			G_CALLBACK(file_ok_cb), data);
747 
748 	g_object_set_data_full(G_OBJECT(window), "filerequestdata", data,
749 			(GDestroyNotify)file_request_destroy);
750 
751 	return data;
752 }
753 
754 static void *
finch_request_file(const char * title,const char * filename,gboolean savedialog,GCallback ok_cb,GCallback cancel_cb,PurpleAccount * account,const char * who,PurpleConversation * conv,void * user_data)755 finch_request_file(const char *title, const char *filename,
756 				gboolean savedialog,
757 				GCallback ok_cb, GCallback cancel_cb,
758 				PurpleAccount *account, const char *who, PurpleConversation *conv,
759 				void *user_data)
760 {
761 	FinchFileRequest *data;
762 	const char *path;
763 
764 	path = purple_prefs_get_path(savedialog ? "/finch/filelocations/last_save_folder" : "/finch/filelocations/last_open_folder");
765 	data = finch_file_request_window(title ? title : (savedialog ? _("Save File...") : _("Open File...")), path,
766 			ok_cb, cancel_cb, user_data);
767 	data->save = savedialog;
768 	if (savedialog)
769 		gnt_file_sel_set_suggested_filename(GNT_FILE_SEL(data->dialog), filename);
770 
771 	gnt_widget_show(data->dialog);
772 	return data->dialog;
773 }
774 
775 static void *
finch_request_folder(const char * title,const char * dirname,GCallback ok_cb,GCallback cancel_cb,PurpleAccount * account,const char * who,PurpleConversation * conv,void * user_data)776 finch_request_folder(const char *title, const char *dirname, GCallback ok_cb,
777 		GCallback cancel_cb, PurpleAccount *account, const char *who, PurpleConversation *conv,
778 		void *user_data)
779 {
780 	FinchFileRequest *data;
781 
782 	data = finch_file_request_window(title ? title : _("Choose Location..."), dirname,
783 			ok_cb, cancel_cb, user_data);
784 	data->save = TRUE;
785 	gnt_file_sel_set_dirs_only(GNT_FILE_SEL(data->dialog), TRUE);
786 
787 	gnt_widget_show(data->dialog);
788 	return data->dialog;
789 }
790 
791 static PurpleRequestUiOps uiops =
792 {
793 	finch_request_input,
794 	finch_request_choice,
795 	finch_request_action,
796 	finch_request_fields,
797 	finch_request_file,
798 	finch_close_request,
799 	finch_request_folder,
800 	NULL,
801 	NULL,
802 	NULL,
803 	NULL
804 };
805 
finch_request_get_ui_ops()806 PurpleRequestUiOps *finch_request_get_ui_ops()
807 {
808 	return &uiops;
809 }
810 
finch_request_init()811 void finch_request_init()
812 {
813 }
814 
finch_request_uninit()815 void finch_request_uninit()
816 {
817 }
818 
finch_request_save_in_prefs(gpointer null,PurpleRequestFields * allfields)819 void finch_request_save_in_prefs(gpointer null, PurpleRequestFields *allfields)
820 {
821 	GList *list;
822 	for (list = purple_request_fields_get_groups(allfields); list; list = list->next) {
823 		PurpleRequestFieldGroup *group = list->data;
824 		GList *fields = purple_request_field_group_get_fields(group);
825 
826 		for (; fields ; fields = fields->next) {
827 			PurpleRequestField *field = fields->data;
828 			PurpleRequestFieldType type = purple_request_field_get_type(field);
829 			PurplePrefType pt;
830 			gpointer val = NULL;
831 			const char *id = purple_request_field_get_id(field);
832 
833 			switch (type) {
834 				case PURPLE_REQUEST_FIELD_LIST:
835 					val = purple_request_field_list_get_selected(field)->data;
836 					val = purple_request_field_list_get_data(field, val);
837 					break;
838 				case PURPLE_REQUEST_FIELD_BOOLEAN:
839 					val = GINT_TO_POINTER(purple_request_field_bool_get_value(field));
840 					break;
841 				case PURPLE_REQUEST_FIELD_INTEGER:
842 					val = GINT_TO_POINTER(purple_request_field_int_get_value(field));
843 					break;
844 				case PURPLE_REQUEST_FIELD_STRING:
845 					val = (gpointer)purple_request_field_string_get_value(field);
846 					break;
847 				default:
848 					break;
849 			}
850 
851 			pt = purple_prefs_get_type(id);
852 			switch (pt) {
853 				case PURPLE_PREF_INT:
854 				{
855 					long int tmp = GPOINTER_TO_INT(val);
856 					if (type == PURPLE_REQUEST_FIELD_LIST) {
857 						/* Lists always return string */
858 						if (sscanf(val, "%ld", &tmp) != 1)
859 							tmp = 0;
860 					}
861 					purple_prefs_set_int(id, (gint)tmp);
862 					break;
863 				}
864 				case PURPLE_PREF_BOOLEAN:
865 					purple_prefs_set_bool(id, GPOINTER_TO_INT(val));
866 					break;
867 				case PURPLE_PREF_STRING:
868 					purple_prefs_set_string(id, val);
869 					break;
870 				default:
871 					break;
872 			}
873 		}
874 	}
875 }
876 
finch_request_field_get_widget(PurpleRequestField * field)877 GntWidget *finch_request_field_get_widget(PurpleRequestField *field)
878 {
879 	GntWidget *ret = NULL;
880 	switch (purple_request_field_get_type(field)) {
881 		case PURPLE_REQUEST_FIELD_BOOLEAN:
882 			ret = create_boolean_field(field);
883 			break;
884 		case PURPLE_REQUEST_FIELD_STRING:
885 			ret = create_string_field(field, NULL);
886 			break;
887 		case PURPLE_REQUEST_FIELD_INTEGER:
888 			ret = create_integer_field(field);
889 			break;
890 		case PURPLE_REQUEST_FIELD_CHOICE:
891 			ret = create_choice_field(field);
892 			break;
893 		case PURPLE_REQUEST_FIELD_LIST:
894 			ret = create_list_field(field);
895 			break;
896 		case PURPLE_REQUEST_FIELD_ACCOUNT:
897 			ret = create_account_field(field);
898 			break;
899 		default:
900 			purple_debug_error("GntRequest", "Unimplemented request-field %d\n", purple_request_field_get_type(field));
901 			break;
902 	}
903 	return ret;
904 }
905 
906