1 /**
2  * @file gntstatus.c GNT Status 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 <gntcombobox.h>
32 #include <gntentry.h>
33 #include <gntlabel.h>
34 #include <gntline.h>
35 #include <gnttree.h>
36 #include <gntutils.h>
37 
38 #include "finch.h"
39 
40 #include <notify.h>
41 #include <request.h>
42 
43 #include "gntstatus.h"
44 
45 static struct
46 {
47 	GntWidget *window;
48 	GntWidget *tree;
49 } statuses;
50 
51 typedef struct
52 {
53 	PurpleSavedStatus *saved;
54 	GntWidget *window;
55 	GntWidget *title;
56 	GntWidget *type;
57 	GntWidget *message;
58 	GntWidget *tree;
59 	GHashTable *hash;  /* list of windows for substatuses */
60 } EditStatus;
61 
62 typedef struct
63 {
64 	PurpleAccount *account;
65 	const PurpleStatusType *type;
66 	char *message;
67 } RowInfo;
68 
69 typedef struct
70 {
71 	GntWidget *window;
72 	GntWidget *type;
73 	GntWidget *message;
74 
75 	EditStatus *parent;
76 	RowInfo *key;
77 } EditSubStatus;
78 
79 static GList *edits;  /* List of opened edit-status dialogs */
80 
81 static void
reset_status_window(GntWidget * widget,gpointer null)82 reset_status_window(GntWidget *widget, gpointer null)
83 {
84 	statuses.window = NULL;
85 	statuses.tree = NULL;
86 }
87 
88 static void
populate_statuses(GntTree * tree)89 populate_statuses(GntTree *tree)
90 {
91 	GList *list;
92 
93 	for (list = purple_savedstatuses_get_all(); list; list = list->next)
94 	{
95 		PurpleSavedStatus *saved = list->data;
96 		const char *title, *type, *message;
97 
98 		if (purple_savedstatus_is_transient(saved))
99 			continue;
100 
101 		title = purple_savedstatus_get_title(saved);
102 		type = purple_primitive_get_name_from_type(purple_savedstatus_get_type(saved));
103 		message = purple_savedstatus_get_message(saved);  /* XXX: Strip possible markups */
104 
105 		gnt_tree_add_row_last(tree, saved,
106 				gnt_tree_create_row(tree, title, type, message), NULL);
107 	}
108 }
109 
110 static void
really_delete_status(PurpleSavedStatus * saved)111 really_delete_status(PurpleSavedStatus *saved)
112 {
113 	GList *iter;
114 
115 	for (iter = edits; iter; iter = iter->next)
116 	{
117 		EditStatus *edit = iter->data;
118 		if (edit->saved == saved)
119 		{
120 			gnt_widget_destroy(edit->window);
121 			break;
122 		}
123 	}
124 
125 	if (statuses.tree)
126 		gnt_tree_remove(GNT_TREE(statuses.tree), saved);
127 
128 	purple_savedstatus_delete(purple_savedstatus_get_title(saved));
129 }
130 
131 static void
ask_before_delete(GntWidget * button,gpointer null)132 ask_before_delete(GntWidget *button, gpointer null)
133 {
134 	char *ask;
135 	PurpleSavedStatus *saved;
136 
137 	g_return_if_fail(statuses.tree != NULL);
138 
139 	saved = gnt_tree_get_selection_data(GNT_TREE(statuses.tree));
140 	ask = g_strdup_printf(_("Are you sure you want to delete \"%s\""),
141 			purple_savedstatus_get_title(saved));
142 
143 	purple_request_action(saved, _("Delete Status"), ask, NULL, 0,
144 			NULL, NULL, NULL,
145 			saved, 2,
146 			_("Delete"), really_delete_status,
147 			_("Cancel"), NULL);
148 	g_free(ask);
149 }
150 
151 static void
use_savedstatus_cb(GntWidget * widget,gpointer null)152 use_savedstatus_cb(GntWidget *widget, gpointer null)
153 {
154 	g_return_if_fail(statuses.tree != NULL);
155 
156 	purple_savedstatus_activate(gnt_tree_get_selection_data(GNT_TREE(statuses.tree)));
157 }
158 
159 static void
edit_savedstatus_cb(GntWidget * widget,gpointer null)160 edit_savedstatus_cb(GntWidget *widget, gpointer null)
161 {
162 	g_return_if_fail(statuses.tree != NULL);
163 
164 	finch_savedstatus_edit(gnt_tree_get_selection_data(GNT_TREE(statuses.tree)));
165 }
166 
finch_savedstatus_show_all()167 void finch_savedstatus_show_all()
168 {
169 	GntWidget *window, *tree, *box, *button;
170 	int widths[] = {25, 12, 35};
171 	if (statuses.window) {
172 		gnt_window_present(statuses.window);
173 		return;
174 	}
175 
176 	statuses.window = window = gnt_vbox_new(FALSE);
177 	gnt_box_set_toplevel(GNT_BOX(window), TRUE);
178 	gnt_box_set_title(GNT_BOX(window), _("Saved Statuses"));
179 	gnt_box_set_fill(GNT_BOX(window), FALSE);
180 	gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
181 	gnt_box_set_pad(GNT_BOX(window), 0);
182 
183 	/* XXX: Add some sorting function to sort alphabetically, perhaps */
184 	statuses.tree = tree = gnt_tree_new_with_columns(3);
185 	gnt_tree_set_column_titles(GNT_TREE(tree), _("Title"), _("Type"), _("Message"));
186 	gnt_tree_set_show_title(GNT_TREE(tree), TRUE);
187 	gnt_tree_set_column_width_ratio(GNT_TREE(tree), widths);
188 	gnt_widget_set_size(tree, 72, 0);
189 	gnt_box_add_widget(GNT_BOX(window), tree);
190 
191 	populate_statuses(GNT_TREE(tree));
192 
193 	box = gnt_hbox_new(FALSE);
194 	gnt_box_add_widget(GNT_BOX(window), box);
195 
196 	button = gnt_button_new(_("Use"));
197 	gnt_box_add_widget(GNT_BOX(box), button);
198 	g_signal_connect(G_OBJECT(button), "activate",
199 			G_CALLBACK(use_savedstatus_cb), NULL);
200 
201 	button = gnt_button_new(_("Add"));
202 	gnt_box_add_widget(GNT_BOX(box), button);
203 	gnt_util_set_trigger_widget(tree, GNT_KEY_INS, button);
204 	g_signal_connect_swapped(G_OBJECT(button), "activate",
205 			G_CALLBACK(finch_savedstatus_edit), NULL);
206 
207 	button = gnt_button_new(_("Edit"));
208 	gnt_box_add_widget(GNT_BOX(box), button);
209 	g_signal_connect(G_OBJECT(button), "activate",
210 			G_CALLBACK(edit_savedstatus_cb), NULL);
211 
212 	button = gnt_button_new(_("Delete"));
213 	gnt_box_add_widget(GNT_BOX(box), button);
214 	gnt_util_set_trigger_widget(tree, GNT_KEY_DEL, button);
215 	g_signal_connect(G_OBJECT(button), "activate",
216 			G_CALLBACK(ask_before_delete), NULL);
217 
218 	button = gnt_button_new(_("Close"));
219 	gnt_box_add_widget(GNT_BOX(box), button);
220 	g_signal_connect_swapped(G_OBJECT(button), "activate",
221 			G_CALLBACK(gnt_widget_destroy), window);
222 
223 	g_signal_connect(G_OBJECT(window), "destroy",
224 			G_CALLBACK(reset_status_window), NULL);
225 	gnt_widget_show(window);
226 }
227 
228 static void
destroy_substatus_win(PurpleAccount * account,EditSubStatus * sub,gpointer null)229 destroy_substatus_win(PurpleAccount *account, EditSubStatus *sub, gpointer null)
230 {
231 	gnt_widget_destroy(sub->window);   /* the "destroy" callback will remove entry from the hashtable */
232 }
233 
234 static void
free_key(gpointer key,gpointer n)235 free_key(gpointer key, gpointer n)
236 {
237 	RowInfo *row = key;
238 	g_free(row->message);
239 	g_free(key);
240 }
241 
242 
243 static void
update_edit_list(GntWidget * widget,EditStatus * edit)244 update_edit_list(GntWidget *widget, EditStatus *edit)
245 {
246 	edits = g_list_remove(edits, edit);
247 	purple_notify_close_with_handle(edit);
248 	g_hash_table_foreach(edit->hash, (GHFunc)destroy_substatus_win, NULL);
249 	g_list_foreach((GList*)gnt_tree_get_rows(GNT_TREE(edit->tree)), free_key, NULL);
250 	g_free(edit);
251 }
252 
253 static void
set_substatuses(EditStatus * edit)254 set_substatuses(EditStatus *edit)
255 {
256 	GList *iter;
257 	for (iter = gnt_tree_get_rows(GNT_TREE(edit->tree)); iter; iter = iter->next) {
258 		RowInfo *key = iter->data;
259 		if (gnt_tree_get_choice(GNT_TREE(edit->tree), key)) {
260 			purple_savedstatus_set_substatus(edit->saved, key->account, key->type, key->message);
261 		}
262 	}
263 }
264 
265 
266 static void
use_trans_status_cb(GntWidget * button,EditStatus * edit)267 use_trans_status_cb(GntWidget *button, EditStatus *edit)
268 {
269 	const char *message;
270 	PurpleStatusPrimitive prim;
271 	PurpleSavedStatus *saved;
272 
273 	message = gnt_entry_get_text(GNT_ENTRY(edit->message));
274 	prim = GPOINTER_TO_INT(gnt_combo_box_get_selected_data(GNT_COMBO_BOX(edit->type)));
275 
276 	saved = purple_savedstatus_find_transient_by_type_and_message(prim, message);
277 	if (saved == NULL) {
278 		saved = purple_savedstatus_new(NULL, prim);
279 		edit->saved = saved;
280 		set_substatuses(edit);
281 	}
282 	purple_savedstatus_set_message(saved, message);
283 	purple_savedstatus_activate(saved);
284 	gnt_widget_destroy(edit->window);
285 }
286 
287 static void
save_savedstatus_cb(GntWidget * button,EditStatus * edit)288 save_savedstatus_cb(GntWidget *button, EditStatus *edit)
289 {
290 	const char *title, *message;
291 	PurpleStatusPrimitive prim;
292 	PurpleSavedStatus *find;
293 
294 	title = gnt_entry_get_text(GNT_ENTRY(edit->title));
295 	message = gnt_entry_get_text(GNT_ENTRY(edit->message));
296 	if (!message || !*message)
297 		message = NULL;
298 
299 	prim = GPOINTER_TO_INT(gnt_combo_box_get_selected_data(GNT_COMBO_BOX(edit->type)));
300 
301 	if (!title || !*title)
302 	{
303 		purple_notify_error(edit, _("Error"), _("Invalid title"),
304 				_("Please enter a non-empty title for the status."));
305 		gnt_box_give_focus_to_child(GNT_BOX(edit->window), edit->title);
306 		return;
307 	}
308 
309 	find = purple_savedstatus_find(title);
310 	if (find && find != edit->saved)
311 	{
312 		purple_notify_error(edit, _("Error"), _("Duplicate title"),
313 				_("Please enter a different title for the status."));
314 		gnt_box_give_focus_to_child(GNT_BOX(edit->window), edit->title);
315 		return;
316 	}
317 
318 	if (edit->saved == NULL)
319 	{
320 		edit->saved = purple_savedstatus_new(title, prim);
321 		purple_savedstatus_set_message(edit->saved, message);
322 		set_substatuses(edit);
323 		if (statuses.tree)
324 			gnt_tree_add_row_last(GNT_TREE(statuses.tree), edit->saved,
325 					gnt_tree_create_row(GNT_TREE(statuses.tree), title,
326 						purple_primitive_get_name_from_type(prim), message), NULL);
327 	}
328 	else
329 	{
330 		purple_savedstatus_set_title(edit->saved, title);
331 		purple_savedstatus_set_type(edit->saved, prim);
332 		purple_savedstatus_set_message(edit->saved, message);
333 		if (statuses.tree)
334 		{
335 			gnt_tree_change_text(GNT_TREE(statuses.tree), edit->saved, 0, title);
336 			gnt_tree_change_text(GNT_TREE(statuses.tree), edit->saved, 1,
337 						purple_primitive_get_name_from_type(prim));
338 			gnt_tree_change_text(GNT_TREE(statuses.tree), edit->saved, 2, message);
339 		}
340 	}
341 
342 	if (g_object_get_data(G_OBJECT(button), "use"))
343 		purple_savedstatus_activate(edit->saved);
344 
345 	gnt_widget_destroy(edit->window);
346 }
347 
348 static void
add_substatus(EditStatus * edit,PurpleAccount * account)349 add_substatus(EditStatus *edit, PurpleAccount *account)
350 {
351 	char *name;
352 	const char *type = NULL, *message = NULL;
353 	PurpleSavedStatusSub *sub = NULL;
354 	RowInfo *key;
355 
356 	if (!edit || !edit->tree)
357 		return;
358 
359 	if (edit->saved)
360 		sub = purple_savedstatus_get_substatus(edit->saved, account);
361 
362 	key = g_new0(RowInfo, 1);
363 	key->account = account;
364 
365 	if (sub)
366 	{
367 		key->type = purple_savedstatus_substatus_get_type(sub);
368 		type = purple_status_type_get_name(key->type);
369 		message = purple_savedstatus_substatus_get_message(sub);
370 		key->message = g_strdup(message);
371 	}
372 
373 	name = g_strdup_printf("%s (%s)", purple_account_get_username(account),
374 			purple_account_get_protocol_name(account));
375 	gnt_tree_add_choice(GNT_TREE(edit->tree), key,
376 			gnt_tree_create_row(GNT_TREE(edit->tree),
377 				name, type ? type : "", message ? message : ""), NULL, NULL);
378 
379 	if (sub)
380 		gnt_tree_set_choice(GNT_TREE(edit->tree), key, TRUE);
381 	g_free(name);
382 }
383 
384 static void
substatus_window_destroy_cb(GntWidget * window,EditSubStatus * sub)385 substatus_window_destroy_cb(GntWidget *window, EditSubStatus *sub)
386 {
387 	g_hash_table_remove(sub->parent->hash, sub->key->account);
388 	g_free(sub);
389 }
390 
391 static void
save_substatus_cb(GntWidget * widget,EditSubStatus * sub)392 save_substatus_cb(GntWidget *widget, EditSubStatus *sub)
393 {
394 	PurpleSavedStatus *saved = sub->parent->saved;
395 	RowInfo *row = sub->key;
396 	const char *message;
397 	PurpleStatusType *type;
398 
399 	type = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(sub->type));
400 	message = gnt_entry_get_text(GNT_ENTRY(sub->message));
401 
402 	row->type = type;
403 	row->message = g_strdup(message);
404 
405 	if (saved)    /* Save the substatus if the savedstatus actually exists. */
406 		purple_savedstatus_set_substatus(saved, row->account, type, message);
407 
408 	gnt_tree_set_choice(GNT_TREE(sub->parent->tree), row, TRUE);
409 	gnt_tree_change_text(GNT_TREE(sub->parent->tree), row, 1,
410 			purple_status_type_get_name(type));
411 	gnt_tree_change_text(GNT_TREE(sub->parent->tree), row, 2, message);
412 
413 	gnt_widget_destroy(sub->window);
414 }
415 
416 static gboolean
popup_substatus(GntTree * tree,const char * key,EditStatus * edit)417 popup_substatus(GntTree *tree, const char *key, EditStatus *edit)
418 {
419 	if (key[0] == ' ' && key[1] == 0)
420 	{
421 		EditSubStatus *sub;
422 		GntWidget *window, *combo, *entry, *box, *button, *l;
423 		PurpleSavedStatusSub *substatus = NULL;
424 		GList *iter;
425 		char *name;
426 		RowInfo *selected = gnt_tree_get_selection_data(tree);
427 		PurpleAccount *account = selected->account;
428 
429 		if (gnt_tree_get_choice(tree, selected))
430 		{
431 			/* There was a savedstatus for this account. Now remove it. */
432 			g_free(selected->message);
433 			selected->type = NULL;
434 			selected->message = NULL;
435 			/* XXX: should we really be saving it right now? */
436 			purple_savedstatus_unset_substatus(edit->saved, account);
437 			gnt_tree_change_text(tree, account, 1, NULL);
438 			gnt_tree_change_text(tree, account, 2, NULL);
439 			return FALSE;
440 		}
441 
442 		if (g_hash_table_lookup(edit->hash, account))
443 			return TRUE;
444 
445 		if (edit->saved)
446 			substatus = purple_savedstatus_get_substatus(edit->saved, account);
447 
448 		sub = g_new0(EditSubStatus, 1);
449 		sub->parent = edit;
450 		sub->key = selected;
451 
452 		sub->window = window = gnt_vbox_new(FALSE);
453 		gnt_box_set_toplevel(GNT_BOX(window), TRUE);
454 		gnt_box_set_title(GNT_BOX(window), _("Substatus"));  /* XXX: a better title */
455 		gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
456 
457 		box = gnt_hbox_new(FALSE);
458 		gnt_box_add_widget(GNT_BOX(box), gnt_label_new(_("Account:")));
459 		name = g_strdup_printf("%s (%s)", purple_account_get_username(account),
460 				purple_account_get_protocol_name(account));
461 		gnt_box_add_widget(GNT_BOX(box), gnt_label_new(name));
462 		g_free(name);
463 		gnt_box_add_widget(GNT_BOX(window), box);
464 
465 		box = gnt_hbox_new(FALSE);
466 		gnt_box_add_widget(GNT_BOX(box), (l = gnt_label_new(_("Status:"))));
467 		gnt_widget_set_size(l, 0, 1);   /* I don't like having to do this */
468 		sub->type = combo = gnt_combo_box_new();
469 		gnt_box_add_widget(GNT_BOX(box), combo);
470 		gnt_box_add_widget(GNT_BOX(window), box);
471 
472 		for (iter = purple_account_get_status_types(account); iter; iter = iter->next)
473 		{
474 			PurpleStatusType *type = iter->data;
475 			if (!purple_status_type_is_user_settable(type))
476 				continue;
477 			gnt_combo_box_add_data(GNT_COMBO_BOX(combo), type, purple_status_type_get_name(type));
478 		}
479 
480 		box = gnt_hbox_new(FALSE);
481 		gnt_box_add_widget(GNT_BOX(box), gnt_label_new(_("Message:")));
482 		sub->message = entry = gnt_entry_new(substatus ? purple_savedstatus_substatus_get_message(substatus) : NULL);
483 		gnt_box_add_widget(GNT_BOX(box), entry);
484 		gnt_box_add_widget(GNT_BOX(window), box);
485 
486 		box  = gnt_hbox_new(FALSE);
487 		button = gnt_button_new(_("Cancel"));
488 		g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gnt_widget_destroy), window);
489 		gnt_box_add_widget(GNT_BOX(box), button);
490 		button = gnt_button_new(_("Save"));
491 		g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(save_substatus_cb), sub);
492 		gnt_box_add_widget(GNT_BOX(box), button);
493 		gnt_box_add_widget(GNT_BOX(window), box);
494 
495 		gnt_widget_show(window);
496 
497 		g_hash_table_insert(edit->hash, account, sub);
498 
499 		g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(substatus_window_destroy_cb), sub);
500 
501 		return TRUE;
502 	}
503 	return FALSE;
504 }
505 
finch_savedstatus_edit(PurpleSavedStatus * saved)506 void finch_savedstatus_edit(PurpleSavedStatus *saved)
507 {
508 	EditStatus *edit;
509 	GntWidget *window, *box, *button, *entry, *combo, *label, *tree;
510 	PurpleStatusPrimitive prims[] = {PURPLE_STATUS_AVAILABLE, PURPLE_STATUS_AWAY,
511 		PURPLE_STATUS_INVISIBLE, PURPLE_STATUS_OFFLINE, PURPLE_STATUS_UNSET}, current;
512 	GList *iter;
513 	int i;
514 
515 	if (saved)
516 	{
517 		GList *iter;
518 		for (iter = edits; iter; iter = iter->next)
519 		{
520 			edit = iter->data;
521 			if (edit->saved == saved)
522 				return;
523 		}
524 	}
525 
526 	edit = g_new0(EditStatus, 1);
527 	edit->saved = saved;
528 	edit->window = window = gnt_vbox_new(FALSE);
529 	gnt_box_set_toplevel(GNT_BOX(window), TRUE);
530 	gnt_box_set_title(GNT_BOX(window), _("Edit Status"));
531 	gnt_box_set_fill(GNT_BOX(window), TRUE);
532 	gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
533 	gnt_box_set_pad(GNT_BOX(window), 0);
534 
535 	edits = g_list_append(edits, edit);
536 
537 	/* Title */
538 	box = gnt_hbox_new(FALSE);
539 	gnt_box_set_alignment(GNT_BOX(box), GNT_ALIGN_LEFT);
540 	gnt_box_add_widget(GNT_BOX(window), box);
541 	gnt_box_add_widget(GNT_BOX(box), gnt_label_new(_("Title")));
542 
543 	edit->title = entry = gnt_entry_new(saved ? purple_savedstatus_get_title(saved) : NULL);
544 	gnt_box_add_widget(GNT_BOX(box), entry);
545 
546 	/* Type */
547 	box = gnt_hbox_new(FALSE);
548 	gnt_box_add_widget(GNT_BOX(window), box);
549 	gnt_box_add_widget(GNT_BOX(box), label = gnt_label_new(_("Status")));
550 	gnt_widget_set_size(label, 0, 1);
551 
552 	edit->type = combo = gnt_combo_box_new();
553 	gnt_box_add_widget(GNT_BOX(box), combo);
554 	current = saved ? purple_savedstatus_get_type(saved) : PURPLE_STATUS_UNSET;
555 	for (i = 0; prims[i] != PURPLE_STATUS_UNSET; i++)
556 	{
557 		gnt_combo_box_add_data(GNT_COMBO_BOX(combo), GINT_TO_POINTER(prims[i]),
558 				purple_primitive_get_name_from_type(prims[i]));
559 		if (prims[i] == current)
560 			gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), GINT_TO_POINTER(current));
561 	}
562 
563 	/* Message */
564 	box = gnt_hbox_new(FALSE);
565 	gnt_box_add_widget(GNT_BOX(window), box);
566 	gnt_box_add_widget(GNT_BOX(box), gnt_label_new(_("Message")));
567 
568 	edit->message = entry = gnt_entry_new(saved ? purple_savedstatus_get_message(saved) : NULL);
569 	gnt_box_add_widget(GNT_BOX(window), entry);
570 
571 	gnt_box_add_widget(GNT_BOX(window), gnt_hline_new());
572 	gnt_box_add_widget(GNT_BOX(window), gnt_label_new(_("Use a different status for some accounts")));
573 
574 	edit->hash = g_hash_table_new(g_direct_hash, g_direct_equal);
575 	edit->tree = tree = gnt_tree_new_with_columns(3);
576 	gnt_box_add_widget(GNT_BOX(window), tree);
577 	gnt_tree_set_show_title(GNT_TREE(tree), TRUE);
578 	gnt_tree_set_column_titles(GNT_TREE(tree), _("Account"), _("Status"), _("Message"));
579 	gnt_tree_set_col_width(GNT_TREE(tree), 0, 30);
580 	gnt_tree_set_col_width(GNT_TREE(tree), 1, 10);
581 	gnt_tree_set_col_width(GNT_TREE(tree), 2, 30);
582 
583 	for (iter = purple_accounts_get_all(); iter; iter = iter->next)
584 	{
585 		add_substatus(edit, iter->data);
586 	}
587 
588 	g_signal_connect(G_OBJECT(tree), "key_pressed", G_CALLBACK(popup_substatus), edit);
589 
590 	/* The buttons */
591 	box = gnt_hbox_new(FALSE);
592 	gnt_box_add_widget(GNT_BOX(window), box);
593 
594 	/* Use */
595 	button = gnt_button_new(_("Use"));
596 	gnt_box_add_widget(GNT_BOX(box), button);
597 	g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(use_trans_status_cb), edit);
598 
599 	/* Save */
600 	button = gnt_button_new(_("Save"));
601 	gnt_box_add_widget(GNT_BOX(box), button);
602 	g_object_set_data(G_OBJECT(button), "use", NULL);
603 	g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(save_savedstatus_cb), edit);
604 
605 	/* Save and Use */
606 	button = gnt_button_new(_("Save and Use"));
607 	gnt_box_add_widget(GNT_BOX(box), button);
608 	g_object_set_data(G_OBJECT(button), "use", GINT_TO_POINTER(TRUE));
609 	g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(save_savedstatus_cb), edit);
610 
611 	/* Cancel */
612 	button = gnt_button_new(_("Cancel"));
613 	gnt_box_add_widget(GNT_BOX(box), button);
614 	g_signal_connect_swapped(G_OBJECT(button), "activate",
615 			G_CALLBACK(gnt_widget_destroy), window);
616 
617 	g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(update_edit_list), edit);
618 
619 	gnt_widget_show(window);
620 }
621 
622