Mercurial > pidgin.yaz
comparison pidgin/gtkdisco.c @ 26331:a5010188c01e
Add the files I forgot to `mtn add` earlier (also, alphabetized disco.h
enum a bit)
author | Paul Aurich <paul@darkrain42.org> |
---|---|
date | Sun, 29 Mar 2009 19:43:32 +0000 |
parents | |
children | 31e05bafd7a3 |
comparison
equal
deleted
inserted
replaced
26330:f19c214201db | 26331:a5010188c01e |
---|---|
1 /** | |
2 * @file gtkdisco.c GTK+ Service Discovery UI | |
3 * @ingroup pidgin | |
4 */ | |
5 | |
6 /* pidgin | |
7 * | |
8 * Pidgin 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 | |
27 #include "internal.h" | |
28 #include "pidgin.h" | |
29 #include "gtkutils.h" | |
30 #include "debug.h" | |
31 #include "disco.h" | |
32 | |
33 #include "gtkdisco.h" | |
34 | |
35 typedef struct _PidginDiscoDialog { | |
36 GtkWidget *window; | |
37 GtkWidget *account_widget; | |
38 | |
39 GtkWidget *sw; | |
40 GtkWidget *progress; | |
41 GtkTreeStore *model; | |
42 GtkWidget *tree; | |
43 GHashTable *cats; /** Meow. */ | |
44 | |
45 GtkWidget *stop_button; | |
46 GtkWidget *list_button; | |
47 GtkWidget *register_button; | |
48 GtkWidget *add_button; | |
49 GtkWidget *close_button; | |
50 | |
51 PurpleAccount *account; | |
52 PurpleDiscoList *discolist; | |
53 } PidginDiscoDialog; | |
54 | |
55 struct _menu_cb_info { | |
56 PurpleDiscoList *list; | |
57 PurpleDiscoService *service; | |
58 }; | |
59 | |
60 enum { | |
61 PIXBUF_COLUMN = 0, | |
62 NAME_COLUMN, | |
63 DESCRIPTION_COLUMN, | |
64 SERVICE_COLUMN, | |
65 NUM_OF_COLUMNS | |
66 }; | |
67 | |
68 static void dialog_select_account_cb(GObject *w, PurpleAccount *account, | |
69 PidginDiscoDialog *dialog) | |
70 { | |
71 dialog->account = account; | |
72 } | |
73 | |
74 static void register_button_cb(GtkButton *button, PidginDiscoDialog *dialog) | |
75 { | |
76 struct _menu_cb_info *info = g_object_get_data(G_OBJECT(button), "disco-info"); | |
77 PurpleConnection *gc = purple_account_get_connection(info->list->account); | |
78 | |
79 purple_disco_service_register(gc, info->service); | |
80 } | |
81 | |
82 static void list_button_cb(GtkButton *button, PidginDiscoDialog *dialog) | |
83 { | |
84 PurpleConnection *gc; | |
85 | |
86 gc = purple_account_get_connection(dialog->account); | |
87 if (!gc) | |
88 return; | |
89 | |
90 if (dialog->discolist != NULL) | |
91 purple_disco_list_unref(dialog->discolist); | |
92 | |
93 dialog->discolist = purple_disco_list_new(dialog->account, (void*) dialog); | |
94 | |
95 purple_disco_get_list(gc, dialog->discolist); | |
96 } | |
97 | |
98 static void add_room_to_blist_cb(GtkButton *button, PidginDiscoDialog *dialog) | |
99 { | |
100 struct _menu_cb_info *info = g_object_get_data(G_OBJECT(button), "disco-info"); | |
101 | |
102 if (info) { | |
103 if (info->service->category == PURPLE_DISCO_SERVICE_CAT_MUC) | |
104 purple_blist_request_add_chat(info->list->account, NULL, NULL, info->service->name); | |
105 else | |
106 purple_blist_request_add_buddy(info->list->account, info->service->name, NULL, NULL); | |
107 } | |
108 } | |
109 | |
110 static void | |
111 selection_changed_cb(GtkTreeSelection *selection, PidginDiscoDialog *dialog) | |
112 { | |
113 PurpleDiscoService *service; | |
114 GtkTreeIter iter; | |
115 GValue val; | |
116 static struct _menu_cb_info *info; | |
117 | |
118 if (gtk_tree_selection_get_selected(selection, NULL, &iter)) { | |
119 val.g_type = 0; | |
120 gtk_tree_model_get_value(GTK_TREE_MODEL(dialog-> model), &iter, SERVICE_COLUMN, &val); | |
121 service = g_value_get_pointer(&val); | |
122 if (!service) { | |
123 gtk_widget_set_sensitive(dialog->add_button, FALSE); | |
124 gtk_widget_set_sensitive(dialog->register_button, FALSE); | |
125 return; | |
126 } | |
127 | |
128 info = g_new0(struct _menu_cb_info, 1); | |
129 info->list = dialog->discolist; | |
130 info->service = service; | |
131 | |
132 g_object_set_data(G_OBJECT(dialog->add_button), "disco-info", info); | |
133 g_object_set_data(G_OBJECT(dialog->register_button), "disco-info", info); | |
134 | |
135 gtk_widget_set_sensitive(dialog->add_button, service->flags & PURPLE_DISCO_FLAG_ADD); | |
136 gtk_widget_set_sensitive(dialog->register_button, service->flags & PURPLE_DISCO_FLAG_REGISTER); | |
137 } else { | |
138 gtk_widget_set_sensitive(dialog->add_button, FALSE); | |
139 gtk_widget_set_sensitive(dialog->register_button, FALSE); | |
140 } | |
141 } | |
142 | |
143 static gint | |
144 delete_win_cb(GtkWidget *w, GdkEventAny *e, gpointer d) | |
145 { | |
146 PidginDiscoDialog *dialog = d; | |
147 | |
148 if (dialog->discolist) | |
149 purple_disco_list_unref(dialog->discolist); | |
150 | |
151 g_free(dialog); | |
152 | |
153 return FALSE; | |
154 } | |
155 | |
156 static void stop_button_cb(GtkButton *button, PidginDiscoDialog *dialog) | |
157 { | |
158 purple_disco_cancel_get_list(dialog->discolist); | |
159 | |
160 if (dialog->account_widget) | |
161 gtk_widget_set_sensitive(dialog->account_widget, TRUE); | |
162 | |
163 gtk_widget_set_sensitive(dialog->stop_button, FALSE); | |
164 gtk_widget_set_sensitive(dialog->list_button, TRUE); | |
165 gtk_widget_set_sensitive(dialog->add_button, FALSE); | |
166 } | |
167 | |
168 static void close_button_cb(GtkButton *button, PidginDiscoDialog *dialog) | |
169 { | |
170 GtkWidget *window = dialog->window; | |
171 | |
172 delete_win_cb(NULL, NULL, dialog); | |
173 gtk_widget_destroy(window); | |
174 } | |
175 | |
176 static gboolean account_filter_func(PurpleAccount *account) | |
177 { | |
178 PurpleConnection *conn = purple_account_get_connection(account); | |
179 PurplePluginProtocolInfo *prpl_info = NULL; | |
180 | |
181 if (conn) | |
182 prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(conn->prpl); | |
183 | |
184 return (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, disco_get_list)); | |
185 } | |
186 | |
187 gboolean | |
188 pidgin_disco_is_showable() | |
189 { | |
190 GList *c; | |
191 PurpleConnection *gc; | |
192 | |
193 for (c = purple_connections_get_all(); c != NULL; c = c->next) { | |
194 gc = c->data; | |
195 | |
196 if (account_filter_func(purple_connection_get_account(gc))) | |
197 return TRUE; | |
198 } | |
199 | |
200 return FALSE; | |
201 } | |
202 | |
203 static void pidgin_disco_create_tree(PidginDiscoDialog *dialog) | |
204 { | |
205 GtkCellRenderer *text_renderer, *pixbuf_renderer; | |
206 GtkTreeViewColumn *column; | |
207 GtkTreeSelection *selection; | |
208 | |
209 dialog->model = gtk_tree_store_new(NUM_OF_COLUMNS, | |
210 GDK_TYPE_PIXBUF, /* PIXBUF_COLUMN */ | |
211 G_TYPE_STRING, /* NAME_COLUMN */ | |
212 G_TYPE_STRING, /* DESCRIPTION_COLUMN */ | |
213 G_TYPE_POINTER /* SERVICE_COLUMN */ | |
214 ); | |
215 | |
216 dialog->tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model)); | |
217 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(dialog->tree), TRUE); | |
218 | |
219 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->tree)); | |
220 g_signal_connect(G_OBJECT(selection), "changed", | |
221 G_CALLBACK(selection_changed_cb), dialog); | |
222 | |
223 g_object_unref(dialog->model); | |
224 | |
225 gtk_container_add(GTK_CONTAINER(dialog->sw), dialog->tree); | |
226 gtk_widget_show(dialog->tree); | |
227 | |
228 text_renderer = gtk_cell_renderer_text_new(); | |
229 pixbuf_renderer = gtk_cell_renderer_pixbuf_new(); | |
230 | |
231 column = gtk_tree_view_column_new(); | |
232 gtk_tree_view_column_set_title(column, _("Name")); | |
233 | |
234 gtk_tree_view_column_pack_start(column, pixbuf_renderer, FALSE); | |
235 gtk_tree_view_column_set_attributes(column, pixbuf_renderer, | |
236 "pixbuf", PIXBUF_COLUMN, NULL); | |
237 | |
238 gtk_tree_view_column_pack_start(column, text_renderer, TRUE); | |
239 gtk_tree_view_column_set_attributes(column, text_renderer, | |
240 "text", NAME_COLUMN, NULL); | |
241 | |
242 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column), | |
243 GTK_TREE_VIEW_COLUMN_GROW_ONLY); | |
244 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE); | |
245 gtk_tree_view_column_set_sort_column_id(GTK_TREE_VIEW_COLUMN(column), NAME_COLUMN); | |
246 gtk_tree_view_column_set_reorderable(GTK_TREE_VIEW_COLUMN(column), TRUE); | |
247 gtk_tree_view_append_column(GTK_TREE_VIEW(dialog->tree), column); | |
248 | |
249 column = gtk_tree_view_column_new_with_attributes(_("Description"), text_renderer, | |
250 "text", DESCRIPTION_COLUMN, NULL); | |
251 gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column), | |
252 GTK_TREE_VIEW_COLUMN_GROW_ONLY); | |
253 gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE); | |
254 gtk_tree_view_column_set_sort_column_id(GTK_TREE_VIEW_COLUMN(column), DESCRIPTION_COLUMN); | |
255 gtk_tree_view_column_set_reorderable(GTK_TREE_VIEW_COLUMN(column), TRUE); | |
256 gtk_tree_view_append_column(GTK_TREE_VIEW(dialog->tree), column); | |
257 } | |
258 | |
259 static PidginDiscoDialog* | |
260 pidgin_disco_dialog_new_with_account(PurpleAccount *account) | |
261 { | |
262 PidginDiscoDialog *dialog; | |
263 GtkWidget *window, *vbox, *vbox2, *bbox; | |
264 | |
265 dialog = g_new0(PidginDiscoDialog, 1); | |
266 dialog->account = account; | |
267 | |
268 /* Create the window. */ | |
269 dialog->window = window = pidgin_create_dialog(_("Service Discovery"), PIDGIN_HIG_BORDER, "service discovery", TRUE); | |
270 | |
271 g_signal_connect(G_OBJECT(window), "delete_event", | |
272 G_CALLBACK(delete_win_cb), dialog); | |
273 | |
274 /* Create the parent vbox for everything. */ | |
275 vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(window), FALSE, PIDGIN_HIG_BORDER); | |
276 | |
277 vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER); | |
278 gtk_container_add(GTK_CONTAINER(vbox), vbox2); | |
279 gtk_widget_show(vbox2); | |
280 | |
281 /* accounts dropdown list */ | |
282 dialog->account_widget = pidgin_account_option_menu_new(dialog->account, FALSE, | |
283 G_CALLBACK(dialog_select_account_cb), account_filter_func, dialog); | |
284 if (!dialog->account) /* this is normally null, and we normally don't care what the first selected item is */ | |
285 dialog->account = pidgin_account_option_menu_get_selected(dialog->account_widget); | |
286 pidgin_add_widget_to_vbox(GTK_BOX(vbox2), _("_Account:"), NULL, dialog->account_widget, TRUE, NULL); | |
287 | |
288 /* scrolled window */ | |
289 dialog->sw = gtk_scrolled_window_new(NULL, NULL); | |
290 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(dialog->sw), | |
291 GTK_SHADOW_IN); | |
292 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dialog->sw), | |
293 GTK_POLICY_AUTOMATIC, | |
294 GTK_POLICY_AUTOMATIC); | |
295 gtk_box_pack_start(GTK_BOX(vbox2), dialog->sw, TRUE, TRUE, 0); | |
296 gtk_widget_set_size_request(dialog->sw, -1, 250); | |
297 gtk_widget_show(dialog->sw); | |
298 | |
299 /* progress bar */ | |
300 dialog->progress = gtk_progress_bar_new(); | |
301 gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(dialog->progress), 0.1); | |
302 gtk_box_pack_start(GTK_BOX(vbox2), dialog->progress, FALSE, FALSE, 0); | |
303 gtk_widget_show(dialog->progress); | |
304 | |
305 | |
306 /* button box */ | |
307 bbox = pidgin_dialog_get_action_area(GTK_DIALOG(window)); | |
308 gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE); | |
309 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); | |
310 | |
311 /* stop button */ | |
312 dialog->stop_button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_STOP, | |
313 G_CALLBACK(stop_button_cb), dialog); | |
314 gtk_widget_set_sensitive(dialog->stop_button, FALSE); | |
315 | |
316 /* list button */ | |
317 dialog->list_button = pidgin_pixbuf_button_from_stock(_("_Get List"), GTK_STOCK_REFRESH, | |
318 PIDGIN_BUTTON_HORIZONTAL); | |
319 gtk_box_pack_start(GTK_BOX(bbox), dialog->list_button, FALSE, FALSE, 0); | |
320 g_signal_connect(G_OBJECT(dialog->list_button), "clicked", | |
321 G_CALLBACK(list_button_cb), dialog); | |
322 gtk_widget_show(dialog->list_button); | |
323 | |
324 /* register button */ | |
325 dialog->register_button = pidgin_dialog_add_button(GTK_DIALOG(dialog->window), _("Register"), | |
326 G_CALLBACK(register_button_cb), dialog); | |
327 gtk_widget_set_sensitive(dialog->register_button, FALSE); | |
328 | |
329 /* add button */ | |
330 dialog->add_button = pidgin_pixbuf_button_from_stock(_("_Add"), GTK_STOCK_ADD, | |
331 PIDGIN_BUTTON_HORIZONTAL); | |
332 gtk_box_pack_start(GTK_BOX(bbox), dialog->add_button, FALSE, FALSE, 0); | |
333 g_signal_connect(G_OBJECT(dialog->add_button), "clicked", | |
334 G_CALLBACK(add_room_to_blist_cb), dialog); | |
335 gtk_widget_set_sensitive(dialog->add_button, FALSE); | |
336 gtk_widget_show(dialog->add_button); | |
337 | |
338 /* close button */ | |
339 dialog->close_button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_CLOSE, | |
340 G_CALLBACK(close_button_cb), dialog); | |
341 | |
342 pidgin_disco_create_tree(dialog); | |
343 | |
344 /* show the dialog window and return the dialog */ | |
345 gtk_widget_show(dialog->window); | |
346 | |
347 return dialog; | |
348 } | |
349 | |
350 void | |
351 pidgin_disco_dialog_show(void) | |
352 { | |
353 pidgin_disco_dialog_new_with_account(NULL); | |
354 } | |
355 | |
356 void | |
357 pidgin_disco_dialog_show_with_account(PurpleAccount* account) | |
358 { | |
359 PidginDiscoDialog *dialog = pidgin_disco_dialog_new_with_account(account); | |
360 | |
361 if (!dialog) | |
362 return; | |
363 | |
364 list_button_cb(GTK_BUTTON(dialog->list_button), dialog); | |
365 } | |
366 | |
367 static void | |
368 pidgin_disco_create(PurpleDiscoList *list) | |
369 { | |
370 PidginDiscoDialog *dialog = (PidginDiscoDialog *) list->ui_data; | |
371 | |
372 dialog->cats = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)gtk_tree_row_reference_free); | |
373 } | |
374 | |
375 | |
376 static void | |
377 pidgin_disco_destroy(PurpleDiscoList *list) | |
378 { | |
379 PidginDiscoDialog *dialog = (PidginDiscoDialog *) list->ui_data; | |
380 | |
381 g_hash_table_destroy(dialog->cats); | |
382 } | |
383 | |
384 static void pidgin_disco_in_progress(PurpleDiscoList *list, gboolean in_progress) | |
385 { | |
386 PidginDiscoDialog *dialog = (PidginDiscoDialog *) list->ui_data; | |
387 | |
388 if (!dialog) | |
389 return; | |
390 | |
391 if (in_progress) { | |
392 gtk_tree_store_clear(dialog->model); | |
393 if (dialog->account_widget) | |
394 gtk_widget_set_sensitive(dialog->account_widget, FALSE); | |
395 gtk_widget_set_sensitive(dialog->stop_button, TRUE); | |
396 gtk_widget_set_sensitive(dialog->list_button, FALSE); | |
397 } else { | |
398 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(dialog->progress), 0.0); | |
399 if (dialog->account_widget) | |
400 gtk_widget_set_sensitive(dialog->account_widget, TRUE); | |
401 gtk_widget_set_sensitive(dialog->stop_button, FALSE); | |
402 gtk_widget_set_sensitive(dialog->list_button, TRUE); | |
403 } | |
404 } | |
405 | |
406 static void pidgin_disco_add_service(PurpleDiscoList *list, PurpleDiscoService *service, PurpleDiscoService *parent) | |
407 { | |
408 PidginDiscoDialog *dialog = (PidginDiscoDialog *) list->ui_data; | |
409 GtkTreeIter iter, parent_iter; | |
410 GtkTreeRowReference *rr; | |
411 GtkTreePath *path; | |
412 char *filename = NULL; | |
413 GdkPixbuf *pixbuf = NULL; | |
414 | |
415 purple_debug_info("disco", "Add_service \"%s\"\n", service->name); | |
416 | |
417 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(dialog->progress)); | |
418 | |
419 if (parent) { | |
420 rr = g_hash_table_lookup(dialog->cats, parent); | |
421 path = gtk_tree_row_reference_get_path(rr); | |
422 if (path) { | |
423 gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &parent_iter, path); | |
424 gtk_tree_path_free(path); | |
425 } | |
426 } | |
427 | |
428 gtk_tree_store_append(dialog->model, &iter, (parent ? &parent_iter : NULL)); | |
429 | |
430 if (service->type == PURPLE_DISCO_SERVICE_TYPE_XMPP) | |
431 filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", "jabber.png", NULL); | |
432 else if (service->type == PURPLE_DISCO_SERVICE_TYPE_ICQ) | |
433 filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", "icq.png", NULL); | |
434 else if (service->type == PURPLE_DISCO_SERVICE_TYPE_YAHOO) | |
435 filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", "yahoo.png", NULL); | |
436 else if (service->type == PURPLE_DISCO_SERVICE_TYPE_GTALK) | |
437 filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", "google-talk.png", NULL); | |
438 else if (service->type == PURPLE_DISCO_SERVICE_TYPE_IRC) | |
439 filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", "irc.png", NULL); | |
440 else if (service->type == PURPLE_DISCO_SERVICE_TYPE_GG) | |
441 filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", "gadu-gadu.png", NULL); | |
442 else if (service->type == PURPLE_DISCO_SERVICE_TYPE_AIM) | |
443 filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", "aim.png", NULL); | |
444 else if (service->type == PURPLE_DISCO_SERVICE_TYPE_QQ) | |
445 filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", "qq.png", NULL); | |
446 else if (service->type == PURPLE_DISCO_SERVICE_TYPE_MSN) | |
447 filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", "msn.png", NULL); | |
448 else if (service->type == PURPLE_DISCO_SERVICE_TYPE_USER) | |
449 filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "status", "22", "person.png", NULL); | |
450 else if (service->category == PURPLE_DISCO_SERVICE_CAT_MUC) | |
451 filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "status", "22", "chat.png", NULL); | |
452 | |
453 if (filename) { | |
454 pixbuf = gdk_pixbuf_new_from_file(filename, NULL); | |
455 g_free(filename); | |
456 } | |
457 | |
458 gtk_tree_store_set(dialog->model, &iter, | |
459 PIXBUF_COLUMN, pixbuf, | |
460 NAME_COLUMN, service->name, | |
461 DESCRIPTION_COLUMN, service->description, | |
462 SERVICE_COLUMN, service, | |
463 -1); | |
464 | |
465 path = gtk_tree_model_get_path(GTK_TREE_MODEL(dialog->model), &iter); | |
466 | |
467 rr = gtk_tree_row_reference_new(GTK_TREE_MODEL(dialog->model), path); | |
468 g_hash_table_insert(dialog->cats, service, rr); | |
469 | |
470 gtk_tree_path_free(path); | |
471 | |
472 if (pixbuf) | |
473 g_object_unref(pixbuf); | |
474 } | |
475 | |
476 static PurpleDiscoUiOps ops = { | |
477 pidgin_disco_dialog_show_with_account, | |
478 pidgin_disco_create, | |
479 pidgin_disco_destroy, | |
480 pidgin_disco_add_service, | |
481 pidgin_disco_in_progress | |
482 }; | |
483 | |
484 void pidgin_disco_init() { | |
485 purple_disco_set_ui_ops(&ops); | |
486 } |