]> git.meshlink.io Git - catta/blob - avahi-ui/avahi-ui.c
Reduce warning storm in libavahi-ui, contributed by Matthias Hasselmann (Re #180)
[catta] / avahi-ui / avahi-ui.c
1 /* $Id$ */
2
3 /***
4   This file is part of avahi.
5  
6   avahi is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as
8   published by the Free Software Foundation; either version 2.1 of the
9   License, or (at your option) any later version.
10  
11   avahi is distributed in the hope that it will be useful, but WITHOUT
12   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14   Public License for more details.
15  
16   You should have received a copy of the GNU Lesser General Public
17   License along with avahi; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <net/if.h>
31
32 #include <gtk/gtk.h>
33
34 #include <avahi-glib/glib-watch.h>
35 #include <avahi-client/client.h>
36 #include <avahi-client/lookup.h>
37 #include <avahi-common/error.h>
38 #include <avahi-common/address.h>
39 #include <avahi-common/domain.h>
40
41 #include "avahi-ui.h"
42
43 #if defined(HAVE_GDBM) || defined(HAVE_DBM)
44 #include "../avahi-utils/stdb.h"
45 #endif
46
47 /* todo: i18n, HIGify */
48
49 struct _AuiServiceDialogPrivate {
50     AvahiGLibPoll *glib_poll;
51     AvahiClient *client;
52     AvahiServiceBrowser **browsers;
53     AvahiServiceResolver *resolver;
54     AvahiDomainBrowser *domain_browser;
55     
56     gchar **browse_service_types;
57     gchar *service_type;
58     gchar *domain;
59     gchar *service_name;
60     AvahiProtocol address_family;
61         
62     AvahiAddress address;
63     gchar *host_name;
64     AvahiStringList *txt_data;
65     guint16 port;
66
67     gboolean resolve_service, resolve_service_done;
68     gboolean resolve_host_name, resolve_host_name_done;
69     
70     GtkWidget *domain_label;
71     GtkWidget *domain_button;
72     GtkWidget *service_tree_view;
73     GtkWidget *service_progress_bar;
74
75     GtkListStore *service_list_store, *domain_list_store;
76
77     guint service_pulse_timeout;
78     guint domain_pulse_timeout;
79     guint start_idle;
80
81     AvahiIfIndex common_interface;
82     AvahiProtocol common_protocol;
83
84     GtkWidget *domain_dialog;
85     GtkWidget *domain_entry;
86     GtkWidget *domain_tree_view;
87     GtkWidget *domain_progress_bar;
88     GtkWidget *domain_ok_button;
89
90     gint forward_response_id;
91 };
92
93 enum {
94     PROP_0,
95     PROP_BROWSE_SERVICE_TYPES,
96     PROP_DOMAIN,
97     PROP_SERVICE_TYPE,
98     PROP_SERVICE_NAME,
99     PROP_ADDRESS,
100     PROP_PORT,
101     PROP_HOST_NAME,
102     PROP_TXT_DATA,
103     PROP_RESOLVE_SERVICE,
104     PROP_RESOLVE_HOST_NAME,
105     PROP_ADDRESS_FAMILY
106 };
107
108 enum {
109     SERVICE_COLUMN_IFACE,
110     SERVICE_COLUMN_PROTO,
111     SERVICE_COLUMN_TYPE,
112     SERVICE_COLUMN_NAME,
113     SERVICE_COLUMN_PRETTY_IFACE,
114     SERVICE_COLUMN_PRETTY_TYPE,
115     N_SERVICE_COLUMNS
116 };
117
118 enum {
119     DOMAIN_COLUMN_NAME,
120     DOMAIN_COLUMN_REF,
121     N_DOMAIN_COLUMNS
122 };
123
124 static void aui_service_dialog_finalize(GObject *object);
125 static void aui_service_dialog_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
126 static void aui_service_dialog_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
127
128 static int get_default_response(GtkDialog *dlg) {
129     gint ret = GTK_RESPONSE_NONE;
130
131     if (GTK_WINDOW(dlg)->default_widget)
132         /* Use the response of the default widget, if possible */
133         ret = gtk_dialog_get_response_for_widget(dlg, GTK_WINDOW(dlg)->default_widget);
134
135     if (ret == GTK_RESPONSE_NONE) {
136         /* Fall back to finding the first positive response */
137         GList *children, *t;
138         gint bad = GTK_RESPONSE_NONE;
139         
140         t = children = gtk_container_get_children(GTK_CONTAINER(dlg->action_area));
141         
142         while (t) {
143             GtkWidget *child = t->data;
144             
145             ret = gtk_dialog_get_response_for_widget(dlg, child);
146             
147             if (ret == GTK_RESPONSE_ACCEPT ||
148                 ret == GTK_RESPONSE_OK ||
149                 ret == GTK_RESPONSE_YES ||
150                 ret == GTK_RESPONSE_APPLY)
151                 break;
152             
153             if (ret != GTK_RESPONSE_NONE && bad == GTK_RESPONSE_NONE)
154                 bad = ret;
155             
156             t = t->next;
157         }
158         
159         g_list_free (children);
160
161         /* Fall back to finding the first negative response */
162         if (ret == GTK_RESPONSE_NONE)
163             ret = bad;
164     }
165
166     return ret;
167 }
168
169 G_DEFINE_TYPE(AuiServiceDialog, aui_service_dialog, GTK_TYPE_DIALOG)
170
171 static void aui_service_dialog_class_init(AuiServiceDialogClass *klass) {
172     GObjectClass *object_class;
173     
174     object_class = (GObjectClass*) klass;
175
176     object_class->finalize = aui_service_dialog_finalize;
177     object_class->set_property = aui_service_dialog_set_property;
178     object_class->get_property = aui_service_dialog_get_property;
179
180     g_object_class_install_property(
181             object_class,
182             PROP_BROWSE_SERVICE_TYPES,
183             g_param_spec_pointer("browse_service_types", "Browse Service Types", "A NULL terminated list of service types to browse for",
184                                 G_PARAM_READABLE | G_PARAM_WRITABLE));
185     g_object_class_install_property(
186             object_class,
187             PROP_DOMAIN,
188             g_param_spec_string("domain", "Domain", "The domain to browse in, or NULL for the default domain",
189                                 NULL,
190                                 G_PARAM_READABLE | G_PARAM_WRITABLE));
191     g_object_class_install_property(
192             object_class,
193             PROP_SERVICE_TYPE,
194             g_param_spec_string("service_type", "Service Type", "The service type of the selected service",
195                                 NULL,
196                                 G_PARAM_READABLE | G_PARAM_WRITABLE));
197     g_object_class_install_property(
198             object_class,
199             PROP_SERVICE_NAME,
200             g_param_spec_string("service_name", "Service Name", "The service name of the selected service",
201                                 NULL,
202                                 G_PARAM_READABLE | G_PARAM_WRITABLE));
203     g_object_class_install_property(
204             object_class,
205             PROP_ADDRESS,
206             g_param_spec_pointer("address", "Address", "The address of the resolved service",
207                                 G_PARAM_READABLE));    
208     g_object_class_install_property(
209             object_class,
210             PROP_PORT,
211             g_param_spec_uint("port", "Port", "The IP port number of the resolved service",
212                              0, 0xFFFF, 0,
213                              G_PARAM_READABLE));
214     g_object_class_install_property(
215             object_class,
216             PROP_HOST_NAME,
217             g_param_spec_string("host_name", "Host Name", "The host name of the resolved service",
218                                 NULL,
219                                 G_PARAM_READABLE));
220     g_object_class_install_property(
221             object_class,
222             PROP_TXT_DATA,
223             g_param_spec_pointer("txt_data", "TXT Data", "The TXT data of the resolved service",
224                                 G_PARAM_READABLE));
225     g_object_class_install_property(
226             object_class,
227             PROP_RESOLVE_SERVICE,
228             g_param_spec_boolean("resolve_service", "Resolve service", "Resolve service",
229                                  TRUE,
230                                  G_PARAM_READABLE | G_PARAM_WRITABLE));
231     g_object_class_install_property(
232             object_class,
233             PROP_RESOLVE_HOST_NAME,
234             g_param_spec_boolean("resolve_host_name", "Resolve service host name", "Resolve service host name",
235                                  TRUE,
236                                  G_PARAM_READABLE | G_PARAM_WRITABLE));
237     g_object_class_install_property(
238             object_class,
239             PROP_ADDRESS_FAMILY,
240             g_param_spec_int("address_family", "Address family", "The address family for host name resolution",
241                              AVAHI_PROTO_UNSPEC, AVAHI_PROTO_INET6, AVAHI_PROTO_UNSPEC,
242                              G_PARAM_READABLE | G_PARAM_WRITABLE));
243 }
244
245
246 GtkWidget *aui_service_dialog_new_valist(
247         const gchar *title,
248         GtkWindow *parent,
249         const gchar *first_button_text,
250         va_list varargs) {
251     
252     const gchar *button_text;
253     gint dr;
254     
255     GtkWidget *w = GTK_WIDGET(g_object_new(
256                                       AUI_TYPE_SERVICE_DIALOG,
257                                       "has-separator", FALSE,
258                                       "title", title,
259                                       NULL));
260
261     if (parent)
262         gtk_window_set_transient_for(GTK_WINDOW(w), parent);
263
264     button_text = first_button_text;
265     while (button_text) {
266         gint response_id;
267         
268         response_id = va_arg(varargs, gint);
269         gtk_dialog_add_button(GTK_DIALOG(w), button_text, response_id);
270         button_text = va_arg(varargs, const gchar *);
271     }
272     
273     gtk_dialog_set_response_sensitive(GTK_DIALOG(w), GTK_RESPONSE_ACCEPT, FALSE);
274     gtk_dialog_set_response_sensitive(GTK_DIALOG(w), GTK_RESPONSE_OK, FALSE);
275     gtk_dialog_set_response_sensitive(GTK_DIALOG(w), GTK_RESPONSE_YES, FALSE);
276     gtk_dialog_set_response_sensitive(GTK_DIALOG(w), GTK_RESPONSE_APPLY, FALSE);
277
278     if ((dr = get_default_response(GTK_DIALOG(w))) != GTK_RESPONSE_NONE)
279         gtk_dialog_set_default_response(GTK_DIALOG(w), dr);
280
281     return w;
282 }
283
284 GtkWidget* aui_service_dialog_new(
285         const gchar *title,
286         GtkWindow *parent,
287         const gchar *first_button_text,
288         ...) {
289
290     GtkWidget *w;
291     
292     va_list varargs;
293     va_start(varargs, first_button_text);
294     w = aui_service_dialog_new_valist(title, parent, first_button_text, varargs);
295     va_end(varargs);
296     
297     return w;
298 }
299
300 static gboolean service_pulse_callback(gpointer data) {
301     AuiServiceDialog *d = AUI_SERVICE_DIALOG(data);
302
303     gtk_progress_bar_pulse(GTK_PROGRESS_BAR(d->priv->service_progress_bar));
304     return TRUE;
305 }
306
307 static gboolean domain_pulse_callback(gpointer data) {
308     AuiServiceDialog *d = AUI_SERVICE_DIALOG(data);
309
310     gtk_progress_bar_pulse(GTK_PROGRESS_BAR(d->priv->domain_progress_bar));
311     return TRUE;
312 }
313
314 static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
315     AuiServiceDialog *d = AUI_SERVICE_DIALOG(userdata);
316
317     if (state == AVAHI_CLIENT_FAILURE) {
318         GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
319                                               GTK_DIALOG_DESTROY_WITH_PARENT,
320                                               GTK_MESSAGE_ERROR,
321                                               GTK_BUTTONS_CLOSE,
322                                               "Avahi client failure: %s",
323                                               avahi_strerror(avahi_client_errno(c)));
324         gtk_dialog_run(GTK_DIALOG(m));
325         gtk_widget_destroy(m);
326         
327         gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
328     }
329 }
330
331 static void resolve_callback(
332         AvahiServiceResolver *r G_GNUC_UNUSED,
333         AvahiIfIndex interface G_GNUC_UNUSED,
334         AvahiProtocol protocol G_GNUC_UNUSED,
335         AvahiResolverEvent event,
336         const char *name,
337         const char *type,
338         const char *domain,
339         const char *host_name,
340         const AvahiAddress *a,
341         uint16_t port,
342         AvahiStringList *txt,
343         AvahiLookupResultFlags flags G_GNUC_UNUSED,
344         void *userdata) {
345
346     AuiServiceDialog *d = AUI_SERVICE_DIALOG(userdata);
347
348     switch (event) {
349         case AVAHI_RESOLVER_FOUND:
350
351             d->priv->resolve_service_done = 1;
352
353             g_free(d->priv->service_name);
354             d->priv->service_name = g_strdup(name);
355
356             g_free(d->priv->service_type);
357             d->priv->service_type = g_strdup(type);
358
359             g_free(d->priv->domain);
360             d->priv->domain = g_strdup(domain);
361
362             g_free(d->priv->host_name);
363             d->priv->host_name = g_strdup(host_name);
364
365             d->priv->port = port;
366
367             avahi_string_list_free(d->priv->txt_data);
368             d->priv->txt_data = avahi_string_list_copy(txt);
369             
370             if (a) {
371                 d->priv->resolve_host_name_done = 1;
372                 d->priv->address = *a;
373             }
374
375             gtk_dialog_response(GTK_DIALOG(d), d->priv->forward_response_id);
376
377             break;
378
379         case AVAHI_RESOLVER_FAILURE: {
380             GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
381                                                   GTK_DIALOG_DESTROY_WITH_PARENT,
382                                                   GTK_MESSAGE_ERROR,
383                                                   GTK_BUTTONS_CLOSE,
384                                                   "Avahi resolver failure: %s",
385                                                   avahi_strerror(avahi_client_errno(d->priv->client)));
386             gtk_dialog_run(GTK_DIALOG(m));
387             gtk_widget_destroy(m);
388             
389             gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
390             break;
391         }
392     }
393 }
394
395
396 static void browse_callback(
397         AvahiServiceBrowser *b G_GNUC_UNUSED,
398         AvahiIfIndex interface,
399         AvahiProtocol protocol,
400         AvahiBrowserEvent event,
401         const char *name,
402         const char *type,
403         const char *domain,
404         AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
405         void* userdata) {
406
407     AuiServiceDialog *d = AUI_SERVICE_DIALOG(userdata);
408     
409     switch (event) {
410
411         case AVAHI_BROWSER_NEW: {
412             gchar *ifs;
413             const gchar *pretty_type;
414             char ifname[IFNAMSIZ];
415             GtkTreeIter iter;
416             GtkTreeSelection *selection;
417
418             if (!(if_indextoname(interface, ifname)))
419                 g_snprintf(ifname, sizeof(ifname), "%i", interface);
420
421             ifs = g_strdup_printf("%s %s", ifname, protocol == AVAHI_PROTO_INET ? "IPv4" : "IPv6");
422
423 #if defined(HAVE_GDBM) || defined(HAVE_DBM)
424             pretty_type = stdb_lookup(type);
425 #else
426             pretty_type = type;
427 #endif            
428             
429             gtk_list_store_append(d->priv->service_list_store, &iter);
430
431             gtk_list_store_set(d->priv->service_list_store, &iter,
432                                SERVICE_COLUMN_IFACE, interface,
433                                SERVICE_COLUMN_PROTO, protocol,
434                                SERVICE_COLUMN_NAME, name,
435                                SERVICE_COLUMN_TYPE, type,
436                                SERVICE_COLUMN_PRETTY_IFACE, ifs,
437                                SERVICE_COLUMN_PRETTY_TYPE, pretty_type,
438                                -1);
439
440             g_free(ifs);
441                     
442             if (d->priv->common_protocol == AVAHI_PROTO_UNSPEC)
443                 d->priv->common_protocol = protocol;
444
445             if (d->priv->common_interface == AVAHI_IF_UNSPEC)
446                 d->priv->common_interface = interface;
447
448             if (d->priv->common_interface != interface || d->priv->common_protocol != protocol) {
449                 gtk_tree_view_column_set_visible(gtk_tree_view_get_column(GTK_TREE_VIEW(d->priv->service_tree_view), 0), TRUE);
450                 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(d->priv->service_tree_view), TRUE);
451             }
452
453             selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(d->priv->service_tree_view));
454             if (!gtk_tree_selection_get_selected(selection, NULL, NULL)) {
455
456                 if (!d->priv->service_type ||
457                     !d->priv->service_name ||
458                     (avahi_domain_equal(d->priv->service_type, type) && strcasecmp(d->priv->service_name, name) == 0)) {
459                     GtkTreePath *path;
460                     
461                     gtk_tree_selection_select_iter(selection, &iter);
462
463                     path = gtk_tree_model_get_path(GTK_TREE_MODEL(d->priv->service_list_store), &iter);
464                     gtk_tree_view_set_cursor(GTK_TREE_VIEW(d->priv->service_tree_view), path, NULL, FALSE);
465                     gtk_tree_path_free(path);
466                 }
467                 
468             }
469             
470             break;
471         }
472             
473         case AVAHI_BROWSER_REMOVE: {
474             GtkTreeIter iter;
475             gboolean valid;
476
477             valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(d->priv->service_list_store), &iter);
478             while (valid) {
479                 gint _interface, _protocol;
480                 gchar *_name, *_type;
481                 gboolean found;
482                 
483                 gtk_tree_model_get(GTK_TREE_MODEL(d->priv->service_list_store), &iter,
484                                    SERVICE_COLUMN_IFACE, &_interface,
485                                    SERVICE_COLUMN_PROTO, &_protocol,
486                                    SERVICE_COLUMN_NAME, &_name,
487                                    SERVICE_COLUMN_TYPE, &_type,
488                                    -1);
489
490                 found = _interface == interface && _protocol == protocol && strcasecmp(_name, name) == 0 && avahi_domain_equal(_type, type);
491                 g_free(_name);
492
493                 if (found) {
494                     gtk_list_store_remove(d->priv->service_list_store, &iter);
495                     break;
496                 }
497                 
498                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(d->priv->service_list_store), &iter);
499             }
500             
501             break;
502         }
503
504         case AVAHI_BROWSER_FAILURE: {
505             GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
506                                                   GTK_DIALOG_DESTROY_WITH_PARENT,
507                                                   GTK_MESSAGE_ERROR,
508                                                   GTK_BUTTONS_CLOSE,
509                                                   "Browsing for service type %s in domain %s failed: %s",
510                                                   type, domain ? domain : "n/a",
511                                                   avahi_strerror(avahi_client_errno(d->priv->client)));
512             gtk_dialog_run(GTK_DIALOG(m));
513             gtk_widget_destroy(m);
514
515             /* Fall through */
516         }
517
518         case AVAHI_BROWSER_ALL_FOR_NOW:
519             if (d->priv->service_pulse_timeout > 0) {
520                 g_source_remove(d->priv->service_pulse_timeout);
521                 d->priv->service_pulse_timeout = 0;
522                 gtk_widget_hide(d->priv->service_progress_bar);
523             }
524             break;
525
526         case AVAHI_BROWSER_CACHE_EXHAUSTED:
527             ;
528     }
529 }
530
531 static void domain_make_default_selection(AuiServiceDialog *d, const gchar *name, GtkTreeIter *iter) {
532     GtkTreeSelection *selection;
533     
534     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(d->priv->domain_tree_view));
535     if (!gtk_tree_selection_get_selected(selection, NULL, NULL)) {
536         
537         if (avahi_domain_equal(gtk_entry_get_text(GTK_ENTRY(d->priv->domain_entry)), name)) {
538             GtkTreePath *path;
539             
540             gtk_tree_selection_select_iter(selection, iter);
541             
542             path = gtk_tree_model_get_path(GTK_TREE_MODEL(d->priv->domain_list_store), iter);
543             gtk_tree_view_set_cursor(GTK_TREE_VIEW(d->priv->domain_tree_view), path, NULL, FALSE);
544             gtk_tree_path_free(path);
545         }
546         
547     }
548 }
549
550 static void domain_browse_callback(
551         AvahiDomainBrowser *b G_GNUC_UNUSED,
552         AvahiIfIndex interface G_GNUC_UNUSED,
553         AvahiProtocol protocol G_GNUC_UNUSED,
554         AvahiBrowserEvent event,
555         const char *name,
556         AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
557         void* userdata) {
558
559     AuiServiceDialog *d = AUI_SERVICE_DIALOG(userdata);
560     
561     switch (event) {
562
563         case AVAHI_BROWSER_NEW: {
564             GtkTreeIter iter;
565             gboolean found = FALSE, valid;
566             gint ref;
567
568             valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(d->priv->domain_list_store), &iter);
569             while (valid) {
570                 gchar *_name;
571                 
572                 gtk_tree_model_get(GTK_TREE_MODEL(d->priv->domain_list_store), &iter,
573                                    DOMAIN_COLUMN_NAME, &_name,
574                                    DOMAIN_COLUMN_REF, &ref,
575                                    -1);
576
577                 found = avahi_domain_equal(_name, name);
578                 g_free(_name);
579
580                 if (found)
581                     break;
582                 
583                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(d->priv->domain_list_store), &iter);
584             }
585
586             if (found) 
587                 gtk_list_store_set(d->priv->domain_list_store, &iter, DOMAIN_COLUMN_REF, ref + 1, -1);
588             else {
589                 gtk_list_store_append(d->priv->domain_list_store, &iter);
590                 
591                 gtk_list_store_set(d->priv->domain_list_store, &iter,
592                                    DOMAIN_COLUMN_NAME, name,
593                                    DOMAIN_COLUMN_REF, 1,
594                                    -1);
595             }
596
597             domain_make_default_selection(d, name, &iter);
598             
599             break;
600         }
601
602         case AVAHI_BROWSER_REMOVE: {
603             gboolean valid;
604             GtkTreeIter iter;
605             
606             valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(d->priv->domain_list_store), &iter);
607             while (valid) {
608                 gint ref;
609                 gchar *_name;
610                 gboolean found;
611                 
612                 gtk_tree_model_get(GTK_TREE_MODEL(d->priv->domain_list_store), &iter,
613                                    DOMAIN_COLUMN_NAME, &_name,
614                                    DOMAIN_COLUMN_REF, &ref,
615                                    -1);
616
617                 found = avahi_domain_equal(_name, name);
618                 g_free(_name);
619
620                 if (found) {
621                     if (ref <= 1)
622                         gtk_list_store_remove(d->priv->service_list_store, &iter);
623                     else
624                         gtk_list_store_set(d->priv->domain_list_store, &iter, DOMAIN_COLUMN_REF, ref - 1, -1);
625                     break;
626                 }
627                 
628                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(d->priv->domain_list_store), &iter);
629             }
630             
631             break;
632         }
633             
634
635         case AVAHI_BROWSER_FAILURE: {
636             GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
637                                                   GTK_DIALOG_DESTROY_WITH_PARENT,
638                                                   GTK_MESSAGE_ERROR,
639                                                   GTK_BUTTONS_CLOSE,
640                                                   "Avahi domain browser failure: %s",
641                                                   avahi_strerror(avahi_client_errno(d->priv->client)));
642             gtk_dialog_run(GTK_DIALOG(m));
643             gtk_widget_destroy(m);
644
645             /* Fall through */
646         }
647
648         case AVAHI_BROWSER_ALL_FOR_NOW:
649             if (d->priv->domain_pulse_timeout > 0) {
650                 g_source_remove(d->priv->domain_pulse_timeout);
651                 d->priv->domain_pulse_timeout = 0;
652                 gtk_widget_hide(d->priv->domain_progress_bar);
653             }
654             break;
655
656         case AVAHI_BROWSER_CACHE_EXHAUSTED:
657             ;
658     }
659 }
660
661 static const gchar *get_domain_name(AuiServiceDialog *d) {
662     const gchar *domain;
663     
664     g_return_val_if_fail(d, NULL);
665     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
666     
667     if (d->priv->domain)
668         return d->priv->domain;
669
670     if (!(domain = avahi_client_get_domain_name(d->priv->client))) {
671         GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
672                                               GTK_DIALOG_DESTROY_WITH_PARENT,
673                                               GTK_MESSAGE_ERROR,
674                                               GTK_BUTTONS_CLOSE,
675                                               "Failed to read Avahi domain : %s",
676                                               avahi_strerror(avahi_client_errno(d->priv->client)));
677         gtk_dialog_run(GTK_DIALOG(m));
678         gtk_widget_destroy(m);
679
680         return NULL;
681     }
682
683     return domain;
684 }
685
686 static gboolean start_callback(gpointer data) {
687     int error;
688     AuiServiceDialog *d = AUI_SERVICE_DIALOG(data);
689     gchar **st;
690     AvahiServiceBrowser **sb;
691     unsigned i;
692     const char *domain;
693
694     d->priv->start_idle = 0;
695     
696     if (!d->priv->browse_service_types || !*d->priv->browse_service_types) {
697         g_warning("Browse service type list is empty!");
698         return FALSE;
699     }
700
701     if (!d->priv->client) {
702         if (!(d->priv->client = avahi_client_new(avahi_glib_poll_get(d->priv->glib_poll), 0, client_callback, d, &error))) {
703             
704             GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
705                                                   GTK_DIALOG_DESTROY_WITH_PARENT,
706                                                   GTK_MESSAGE_ERROR,
707                                                   GTK_BUTTONS_CLOSE,
708                                                   "Failed to connect to Avahi server: %s",
709                                                   avahi_strerror(error));
710             gtk_dialog_run(GTK_DIALOG(m));
711             gtk_widget_destroy(m);
712             
713             gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
714             return FALSE;
715         }
716     }
717
718     if (!(domain = get_domain_name(d))) {
719         gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
720         return FALSE;
721     }
722
723     g_assert(domain);
724
725     if (avahi_domain_equal(domain, "local."))
726         gtk_label_set_markup(GTK_LABEL(d->priv->domain_label), "Browsing for services on <b>local network</b>:");
727     else {
728         gchar *t = g_strdup_printf("Browsing for services in domain <b>%s</b>:", domain);
729         gtk_label_set_markup(GTK_LABEL(d->priv->domain_label), t);
730         g_free(t);
731     }
732     
733     if (d->priv->browsers) {
734         for (sb = d->priv->browsers; *sb; sb++)
735             avahi_service_browser_free(*sb);
736
737         g_free(d->priv->browsers);
738         d->priv->browsers = NULL;
739     }
740
741     gtk_list_store_clear(GTK_LIST_STORE(d->priv->service_list_store));
742     d->priv->common_interface = AVAHI_IF_UNSPEC;
743     d->priv->common_protocol = AVAHI_PROTO_UNSPEC;
744
745     gtk_tree_view_column_set_visible(gtk_tree_view_get_column(GTK_TREE_VIEW(d->priv->service_tree_view), 0), FALSE);
746     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(d->priv->service_tree_view), gtk_tree_view_column_get_visible(gtk_tree_view_get_column(GTK_TREE_VIEW(d->priv->service_tree_view), 2)));
747     gtk_widget_show(d->priv->service_progress_bar);
748
749     if (d->priv->service_pulse_timeout <= 0)
750         d->priv->service_pulse_timeout = g_timeout_add(100, service_pulse_callback, d);
751
752     for (i = 0; d->priv->browse_service_types[i]; i++)
753         ;
754     g_assert(i > 0);
755
756     d->priv->browsers = g_new0(AvahiServiceBrowser*, i+1);
757     for (st = d->priv->browse_service_types, sb = d->priv->browsers; *st; st++, sb++) {
758
759         if (!(*sb = avahi_service_browser_new(d->priv->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, *st, d->priv->domain, 0, browse_callback, d))) {
760             GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
761                                                   GTK_DIALOG_DESTROY_WITH_PARENT,
762                                                   GTK_MESSAGE_ERROR,
763                                                   GTK_BUTTONS_CLOSE,
764                                                   "Failed to create browser for %s: %s",
765                                                   *st,
766                                                   avahi_strerror(avahi_client_errno(d->priv->client)));
767             gtk_dialog_run(GTK_DIALOG(m));
768             gtk_widget_destroy(m);
769             
770             gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
771             return FALSE;
772
773         }
774     }
775
776     return FALSE;
777 }
778
779 static void aui_service_dialog_finalize(GObject *object) {
780     AuiServiceDialog *d = AUI_SERVICE_DIALOG(object);
781
782     if (d->priv->domain_pulse_timeout > 0)
783         g_source_remove(d->priv->domain_pulse_timeout);
784
785     if (d->priv->service_pulse_timeout > 0)
786         g_source_remove(d->priv->service_pulse_timeout);
787
788     if (d->priv->start_idle > 0)
789         g_source_remove(d->priv->start_idle);
790     
791     g_free(d->priv->host_name);
792     g_free(d->priv->domain);
793     g_free(d->priv->service_name);
794
795     avahi_string_list_free(d->priv->txt_data);
796     
797     g_strfreev(d->priv->browse_service_types);
798
799     if (d->priv->domain_browser)
800         avahi_domain_browser_free(d->priv->domain_browser);
801     
802     if (d->priv->resolver)
803         avahi_service_resolver_free(d->priv->resolver);
804     
805     if (d->priv->browsers) {
806         AvahiServiceBrowser **sb;
807
808         for (sb = d->priv->browsers; *sb; sb++)
809             avahi_service_browser_free(*sb);
810
811         g_free(d->priv->browsers);
812     }
813
814     if (d->priv->client)
815         avahi_client_free(d->priv->client);
816
817     if (d->priv->glib_poll)
818         avahi_glib_poll_free(d->priv->glib_poll);
819
820     g_free(d->priv);
821     d->priv = NULL;
822
823     G_OBJECT_CLASS(aui_service_dialog_parent_class)->finalize(object);
824 }
825
826 static void service_row_activated_callback(GtkTreeView *tree_view G_GNUC_UNUSED, GtkTreePath *path G_GNUC_UNUSED, GtkTreeViewColumn *column G_GNUC_UNUSED, gpointer user_data) {
827     AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
828
829     gtk_dialog_response(GTK_DIALOG(d), get_default_response(GTK_DIALOG(d)));
830 }
831
832 static void service_selection_changed_callback(GtkTreeSelection *selection, gpointer user_data) {
833     AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
834     gboolean b;
835
836     b = gtk_tree_selection_get_selected(selection, NULL, NULL);
837     gtk_dialog_set_response_sensitive(GTK_DIALOG(d), GTK_RESPONSE_ACCEPT, b);
838     gtk_dialog_set_response_sensitive(GTK_DIALOG(d), GTK_RESPONSE_OK, b);
839     gtk_dialog_set_response_sensitive(GTK_DIALOG(d), GTK_RESPONSE_YES, b);
840     gtk_dialog_set_response_sensitive(GTK_DIALOG(d), GTK_RESPONSE_APPLY, b);
841 }
842
843 static void response_callback(GtkDialog *dialog, gint response, gpointer user_data) {
844     AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
845
846     if ((response == GTK_RESPONSE_ACCEPT ||
847         response == GTK_RESPONSE_OK ||
848         response == GTK_RESPONSE_YES ||
849         response == GTK_RESPONSE_APPLY) &&
850         ((d->priv->resolve_service && !d->priv->resolve_service_done) ||
851          (d->priv->resolve_host_name && !d->priv->resolve_host_name_done))) {
852         
853         GtkTreeIter iter;
854         gint interface, protocol;
855         gchar *name, *type;
856         GdkCursor *cursor;
857
858         g_signal_stop_emission(dialog, g_signal_lookup("response", gtk_dialog_get_type()), 0);
859         d->priv->forward_response_id = response;
860
861         if (d->priv->resolver)
862             return;
863
864         g_return_if_fail(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(d->priv->service_tree_view)), NULL, &iter));
865
866         gtk_tree_model_get(GTK_TREE_MODEL(d->priv->service_list_store), &iter,
867                            SERVICE_COLUMN_IFACE, &interface,
868                            SERVICE_COLUMN_PROTO, &protocol,
869                            SERVICE_COLUMN_NAME, &name,
870                            SERVICE_COLUMN_TYPE, &type, -1);
871
872         g_return_if_fail(d->priv->client);
873
874         gtk_widget_set_sensitive(GTK_WIDGET(dialog), FALSE);
875         cursor = gdk_cursor_new(GDK_WATCH);
876         gdk_window_set_cursor(GTK_WIDGET(dialog)->window, cursor);
877         gdk_cursor_unref(cursor);
878
879         if (!(d->priv->resolver = avahi_service_resolver_new(
880                       d->priv->client, interface, protocol, name, type, d->priv->domain,
881                       d->priv->address_family, !d->priv->resolve_host_name ? AVAHI_LOOKUP_NO_ADDRESS : 0, resolve_callback, d))) {
882
883             GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
884                                                   GTK_DIALOG_DESTROY_WITH_PARENT,
885                                                   GTK_MESSAGE_ERROR,
886                                                   GTK_BUTTONS_CLOSE,
887                                                   "Failed to create resolver for %s of type %s in domain %s: %s",
888                                                   name, type, d->priv->domain,
889                                                   avahi_strerror(avahi_client_errno(d->priv->client)));
890             gtk_dialog_run(GTK_DIALOG(m));
891             gtk_widget_destroy(m);
892             
893             gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
894             return;
895         }
896     }
897 }
898
899 static gboolean is_valid_domain_suffix(const gchar *n) {
900     gchar label[AVAHI_LABEL_MAX];
901     
902     if (!avahi_is_valid_domain_name(n))
903         return FALSE;
904
905     if (!avahi_unescape_label(&n, label, sizeof(label)))
906         return FALSE;
907
908     /* At least one label */
909     
910     return !!label[0];
911 }
912
913 static void domain_row_activated_callback(GtkTreeView *tree_view G_GNUC_UNUSED, GtkTreePath *path G_GNUC_UNUSED, GtkTreeViewColumn *column G_GNUC_UNUSED, gpointer user_data) {
914     AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
915
916     if (is_valid_domain_suffix(gtk_entry_get_text(GTK_ENTRY(d->priv->domain_entry))))
917         gtk_dialog_response(GTK_DIALOG(d->priv->domain_dialog), GTK_RESPONSE_ACCEPT);
918 }
919
920 static void domain_selection_changed_callback(GtkTreeSelection *selection G_GNUC_UNUSED, gpointer user_data) {
921     GtkTreeIter iter;
922     AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
923     gchar *name;
924
925     g_return_if_fail(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(d->priv->domain_tree_view)), NULL, &iter));
926
927     gtk_tree_model_get(GTK_TREE_MODEL(d->priv->domain_list_store), &iter,
928                        DOMAIN_COLUMN_NAME, &name, -1);
929
930     gtk_entry_set_text(GTK_ENTRY(d->priv->domain_entry), name);
931 }
932
933 static void domain_entry_changed_callback(GtkEditable *editable G_GNUC_UNUSED, gpointer user_data) {
934     AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
935
936     gtk_widget_set_sensitive(d->priv->domain_ok_button, is_valid_domain_suffix(gtk_entry_get_text(GTK_ENTRY(d->priv->domain_entry))));
937 }
938
939 static void domain_button_clicked(GtkButton *button G_GNUC_UNUSED, gpointer user_data) {
940     GtkWidget *vbox, *vbox2, *scrolled_window;
941     GtkTreeSelection *selection;
942     GtkCellRenderer *renderer;
943     GtkTreeViewColumn *column;
944     AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
945     AuiServiceDialogPrivate *p = d->priv;
946     const gchar *domain;
947     GtkTreeIter iter;
948
949     g_return_if_fail(!p->domain_dialog);
950     g_return_if_fail(!p->domain_browser);
951
952     if (!(domain = get_domain_name(d))) {
953         gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
954         return;
955     }
956
957     if (!(p->domain_browser = avahi_domain_browser_new(p->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, AVAHI_DOMAIN_BROWSER_BROWSE, 0, domain_browse_callback, d))) {
958         GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
959                                               GTK_DIALOG_DESTROY_WITH_PARENT,
960                                               GTK_MESSAGE_ERROR,
961                                               GTK_BUTTONS_CLOSE,
962                                               "Failed to create domain browser: %s",
963                                               avahi_strerror(avahi_client_errno(p->client)));
964         gtk_dialog_run(GTK_DIALOG(m));
965         gtk_widget_destroy(m);
966         
967         gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
968         return;
969     }
970     
971     p->domain_dialog = gtk_dialog_new();
972     gtk_container_set_border_width(GTK_CONTAINER(p->domain_dialog), 5);
973     gtk_window_set_title(GTK_WINDOW(p->domain_dialog), "Change domain");
974     gtk_dialog_set_has_separator(GTK_DIALOG(p->domain_dialog), FALSE);
975     
976     vbox = gtk_vbox_new(FALSE, 8);
977     gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
978     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(p->domain_dialog)->vbox), vbox, TRUE, TRUE, 0);
979
980     p->domain_entry = gtk_entry_new();
981     gtk_entry_set_max_length(GTK_ENTRY(p->domain_entry), AVAHI_DOMAIN_NAME_MAX);
982     gtk_entry_set_text(GTK_ENTRY(p->domain_entry), domain);
983     gtk_entry_set_activates_default(GTK_ENTRY(p->domain_entry), TRUE);
984     g_signal_connect(p->domain_entry, "changed", G_CALLBACK(domain_entry_changed_callback), d);
985     gtk_box_pack_start(GTK_BOX(vbox), p->domain_entry, FALSE, FALSE, 0);
986     
987     vbox2 = gtk_vbox_new(FALSE, 8);
988     gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
989
990     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
991     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
992     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_ETCHED_IN);
993     gtk_box_pack_start(GTK_BOX(vbox2), scrolled_window, TRUE, TRUE, 0);
994
995     p->domain_list_store = gtk_list_store_new(N_DOMAIN_COLUMNS, G_TYPE_STRING, G_TYPE_INT);
996
997     p->domain_tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(p->domain_list_store));
998     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(p->domain_tree_view), FALSE);
999     g_signal_connect(p->domain_tree_view, "row-activated", G_CALLBACK(domain_row_activated_callback), d);
1000     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(p->domain_tree_view));
1001     gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
1002     g_signal_connect(selection, "changed", G_CALLBACK(domain_selection_changed_callback), d);
1003
1004     renderer = gtk_cell_renderer_text_new();
1005     column = gtk_tree_view_column_new_with_attributes("Service Name", renderer, "text", DOMAIN_COLUMN_NAME, NULL);
1006     gtk_tree_view_column_set_expand(column, TRUE);
1007     gtk_tree_view_append_column(GTK_TREE_VIEW(p->domain_tree_view), column);
1008
1009     gtk_tree_view_set_search_column(GTK_TREE_VIEW(p->domain_tree_view), DOMAIN_COLUMN_NAME);
1010     gtk_container_add(GTK_CONTAINER(scrolled_window), p->domain_tree_view);
1011
1012     p->domain_progress_bar = gtk_progress_bar_new();
1013     gtk_progress_bar_set_text(GTK_PROGRESS_BAR(p->domain_progress_bar), "Browsing ...");
1014     gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(p->domain_progress_bar), 0.1);
1015     gtk_box_pack_end(GTK_BOX(vbox2), p->domain_progress_bar, FALSE, FALSE, 0);
1016     
1017     gtk_dialog_add_button(GTK_DIALOG(p->domain_dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1018     p->domain_ok_button = GTK_WIDGET(gtk_dialog_add_button(GTK_DIALOG(p->domain_dialog), GTK_STOCK_OK, GTK_RESPONSE_ACCEPT));
1019     gtk_dialog_set_default_response(GTK_DIALOG(p->domain_dialog), GTK_RESPONSE_ACCEPT);
1020     gtk_widget_set_sensitive(p->domain_ok_button, is_valid_domain_suffix(gtk_entry_get_text(GTK_ENTRY(p->domain_entry))));
1021
1022     gtk_widget_grab_default(p->domain_ok_button);
1023     gtk_widget_grab_focus(p->domain_entry);
1024
1025     gtk_window_set_default_size(GTK_WINDOW(p->domain_dialog), 300, 300);
1026     
1027     gtk_widget_show_all(vbox);
1028
1029     gtk_list_store_append(p->domain_list_store, &iter);
1030     gtk_list_store_set(p->domain_list_store, &iter, DOMAIN_COLUMN_NAME, "local", DOMAIN_COLUMN_REF, 1, -1);
1031     domain_make_default_selection(d, "local", &iter);
1032
1033     p->domain_pulse_timeout = g_timeout_add(100, domain_pulse_callback, d);
1034     
1035     if (gtk_dialog_run(GTK_DIALOG(p->domain_dialog)) == GTK_RESPONSE_ACCEPT)
1036         aui_service_dialog_set_domain(d, gtk_entry_get_text(GTK_ENTRY(p->domain_entry)));
1037
1038     gtk_widget_destroy(p->domain_dialog);
1039     p->domain_dialog = NULL;
1040
1041     if (p->domain_pulse_timeout > 0) {
1042         g_source_remove(p->domain_pulse_timeout);
1043         p->domain_pulse_timeout = 0;
1044     }
1045
1046     avahi_domain_browser_free(p->domain_browser);
1047     p->domain_browser = NULL;
1048 }
1049
1050 static void aui_service_dialog_init(AuiServiceDialog *d) {
1051     GtkWidget *vbox, *vbox2, *scrolled_window;
1052     GtkCellRenderer *renderer;
1053     GtkTreeViewColumn *column;
1054     GtkTreeSelection *selection;
1055     AuiServiceDialogPrivate *p;
1056
1057     p = d->priv = g_new(AuiServiceDialogPrivate, 1);
1058     
1059     p->host_name = NULL;
1060     p->domain = NULL;
1061     p->service_name = NULL;
1062     p->service_type = NULL;
1063     p->txt_data = NULL;
1064     p->browse_service_types = NULL;
1065     memset(&p->address, 0, sizeof(p->address));
1066     p->port = 0;
1067     p->resolve_host_name = p->resolve_service = TRUE;
1068     p->resolve_host_name_done = p->resolve_service_done = FALSE;
1069     p->address_family = AVAHI_PROTO_UNSPEC;
1070
1071     p->glib_poll = NULL;
1072     p->client = NULL;
1073     p->browsers = NULL;
1074     p->resolver = NULL;
1075     p->domain_browser = NULL;
1076
1077     p->service_pulse_timeout = 0;
1078     p->domain_pulse_timeout = 0;
1079     p->start_idle = 0;
1080     p->common_interface = AVAHI_IF_UNSPEC;
1081     p->common_protocol = AVAHI_PROTO_UNSPEC;
1082
1083     p->domain_dialog = NULL;
1084     p->domain_entry = NULL;
1085     p->domain_tree_view = NULL;
1086     p->domain_progress_bar = NULL;
1087     p->domain_ok_button = NULL;
1088
1089     p->forward_response_id = GTK_RESPONSE_NONE;
1090
1091     p->service_list_store = p->domain_list_store = NULL;
1092     
1093     gtk_widget_push_composite_child();
1094
1095     gtk_container_set_border_width(GTK_CONTAINER(d), 5);
1096
1097     vbox = gtk_vbox_new(FALSE, 8);
1098     gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
1099     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(d)->vbox), vbox, TRUE, TRUE, 0);
1100
1101     p->domain_label = gtk_label_new("Initializing...");
1102     gtk_label_set_ellipsize(GTK_LABEL(p->domain_label), TRUE);
1103     gtk_misc_set_alignment(GTK_MISC(p->domain_label), 0, 0.5);
1104     gtk_box_pack_start(GTK_BOX(vbox), p->domain_label, FALSE, FALSE, 0);
1105     
1106
1107     vbox2 = gtk_vbox_new(FALSE, 8);
1108     gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
1109
1110     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1111     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1112     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_ETCHED_IN);
1113     gtk_box_pack_start(GTK_BOX(vbox2), scrolled_window, TRUE, TRUE, 0);
1114
1115     p->service_list_store = gtk_list_store_new(N_SERVICE_COLUMNS, G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
1116
1117     p->service_tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(p->service_list_store));
1118     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(p->service_tree_view), FALSE);
1119     g_signal_connect(p->service_tree_view, "row-activated", G_CALLBACK(service_row_activated_callback), d);
1120     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(p->service_tree_view));
1121     gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
1122     g_signal_connect(selection, "changed", G_CALLBACK(service_selection_changed_callback), d);
1123     
1124     renderer = gtk_cell_renderer_text_new();
1125     column = gtk_tree_view_column_new_with_attributes("Location", renderer, "text", SERVICE_COLUMN_PRETTY_IFACE, NULL);
1126     gtk_tree_view_column_set_visible(column, FALSE);
1127     gtk_tree_view_append_column(GTK_TREE_VIEW(p->service_tree_view), column);
1128     
1129     renderer = gtk_cell_renderer_text_new();
1130     column = gtk_tree_view_column_new_with_attributes("Name", renderer, "text", SERVICE_COLUMN_NAME, NULL);
1131     gtk_tree_view_column_set_expand(column, TRUE);
1132     gtk_tree_view_append_column(GTK_TREE_VIEW(p->service_tree_view), column);
1133
1134     renderer = gtk_cell_renderer_text_new();
1135     column = gtk_tree_view_column_new_with_attributes("Type", renderer, "text", SERVICE_COLUMN_PRETTY_TYPE, NULL);
1136     gtk_tree_view_column_set_visible(column, FALSE);
1137     gtk_tree_view_append_column(GTK_TREE_VIEW(p->service_tree_view), column);
1138
1139     gtk_tree_view_set_search_column(GTK_TREE_VIEW(p->service_tree_view), SERVICE_COLUMN_NAME);
1140     gtk_container_add(GTK_CONTAINER(scrolled_window), p->service_tree_view);
1141
1142     p->service_progress_bar = gtk_progress_bar_new();
1143     gtk_progress_bar_set_text(GTK_PROGRESS_BAR(p->service_progress_bar), "Browsing ...");
1144     gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(p->service_progress_bar), 0.1);
1145     gtk_box_pack_end(GTK_BOX(vbox2), p->service_progress_bar, FALSE, FALSE, 0);
1146
1147     p->domain_button = gtk_button_new_with_mnemonic("_Domain..."); 
1148     gtk_button_set_image(GTK_BUTTON(p->domain_button), gtk_image_new_from_stock(GTK_STOCK_NETWORK, GTK_ICON_SIZE_BUTTON)); 
1149     g_signal_connect(p->domain_button, "clicked", G_CALLBACK(domain_button_clicked), d);
1150     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(d)->action_area), p->domain_button, FALSE, TRUE, 0);
1151     gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(GTK_DIALOG(d)->action_area), p->domain_button, TRUE);
1152     gtk_widget_show(p->domain_button); 
1153     
1154     gtk_dialog_set_default_response(GTK_DIALOG(d), GTK_RESPONSE_ACCEPT);
1155
1156     gtk_widget_grab_focus(p->service_tree_view);
1157
1158     gtk_window_set_default_size(GTK_WINDOW(d), 400, 300);
1159     
1160     gtk_widget_show_all(vbox);
1161
1162     gtk_widget_pop_composite_child();
1163
1164     p->glib_poll = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT);
1165
1166     p->service_pulse_timeout = g_timeout_add(100, service_pulse_callback, d);
1167     p->start_idle = g_idle_add(start_callback, d);
1168
1169     g_signal_connect(d, "response", G_CALLBACK(response_callback), d);
1170 }
1171
1172 static void restart_browsing(AuiServiceDialog *d) {
1173     g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1174
1175     if (d->priv->start_idle <= 0)
1176         d->priv->start_idle = g_idle_add(start_callback, d);
1177 }
1178
1179 void aui_service_dialog_set_browse_service_types(AuiServiceDialog *d, const char *type, ...) {
1180     va_list ap;
1181     const char *t;
1182     unsigned u;
1183     
1184     g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1185     g_return_if_fail(type);
1186
1187     g_strfreev(d->priv->browse_service_types);
1188     
1189     va_start(ap, type);
1190     for (u = 1; va_arg(ap, const char *); u++)
1191         ;
1192     va_end(ap);
1193
1194     d->priv->browse_service_types = g_new0(gchar*, u+1);
1195     d->priv->browse_service_types[0] = g_strdup(type);
1196     
1197     va_start(ap, type);
1198     for (u = 1; (t = va_arg(apcopy, const char*)); u++)
1199         d->priv->browse_service_types[u] = g_strdup(t);
1200     va_end(ap);
1201
1202     if (d->priv->browse_service_types[0] && d->priv->browse_service_types[1]) {
1203         /* Multiple service types, enable headers */
1204     
1205         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(d->priv->service_tree_view), TRUE);
1206         gtk_tree_view_column_set_visible(gtk_tree_view_get_column(GTK_TREE_VIEW(d->priv->service_tree_view), 2), TRUE);
1207     }
1208
1209     restart_browsing(d);
1210 }
1211
1212 void aui_service_dialog_set_browse_service_typesv(AuiServiceDialog *d, const char *const*types) {
1213     
1214     g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1215     g_return_if_fail(types);
1216     g_return_if_fail(*types);
1217
1218     g_strfreev(d->priv->browse_service_types);
1219     d->priv->browse_service_types = g_strdupv((char**) types);
1220
1221     if (d->priv->browse_service_types[0] && d->priv->browse_service_types[1]) {
1222         /* Multiple service types, enable headers */
1223     
1224         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(d->priv->service_tree_view), TRUE);
1225         gtk_tree_view_column_set_visible(gtk_tree_view_get_column(GTK_TREE_VIEW(d->priv->service_tree_view), 2), TRUE);
1226     }
1227
1228     restart_browsing(d);
1229 }
1230
1231 const gchar*const* aui_service_dialog_get_browse_service_types(AuiServiceDialog *d) {
1232     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1233
1234     return (const char* const*) d->priv->browse_service_types;
1235 }
1236
1237 void aui_service_dialog_set_domain(AuiServiceDialog *d, const char *domain) {
1238     g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1239     g_return_if_fail(!domain || is_valid_domain_suffix(domain));
1240
1241     g_free(d->priv->domain);
1242     d->priv->domain = domain ? avahi_normalize_name_strdup(domain) : NULL;
1243     
1244     restart_browsing(d);
1245 }
1246
1247 const char* aui_service_dialog_get_domain(AuiServiceDialog *d) {
1248     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1249     
1250     return d->priv->domain;
1251 }
1252
1253 void aui_service_dialog_set_service_name(AuiServiceDialog *d, const char *name) {
1254     g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1255
1256     g_free(d->priv->service_name);
1257     d->priv->service_name = g_strdup(name);
1258 }
1259
1260 const char* aui_service_dialog_get_service_name(AuiServiceDialog *d) {
1261     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1262
1263     return d->priv->service_name;
1264 }
1265
1266 void aui_service_dialog_set_service_type(AuiServiceDialog *d, const char*stype) {
1267     g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1268
1269     g_free(d->priv->service_type);
1270     d->priv->service_type = g_strdup(stype);
1271 }
1272
1273 const char* aui_service_dialog_get_service_type(AuiServiceDialog *d) {
1274     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1275
1276     return d->priv->service_type;
1277 }
1278
1279 const AvahiAddress* aui_service_dialog_get_address(AuiServiceDialog *d) {
1280     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1281     g_return_val_if_fail(d->priv->resolve_service_done && d->priv->resolve_host_name_done, NULL);
1282
1283     return &d->priv->address;
1284 }
1285
1286 guint16 aui_service_dialog_get_port(AuiServiceDialog *d) {
1287     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), 0);
1288     g_return_val_if_fail(d->priv->resolve_service_done, 0);
1289
1290     return d->priv->port;
1291 }
1292
1293 const char* aui_service_dialog_get_host_name(AuiServiceDialog *d) {
1294     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1295     g_return_val_if_fail(d->priv->resolve_service_done, NULL);
1296
1297     return d->priv->host_name;
1298 }
1299
1300 const AvahiStringList *aui_service_dialog_get_txt_data(AuiServiceDialog *d) {
1301     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1302     g_return_val_if_fail(d->priv->resolve_service_done, NULL);
1303
1304     return d->priv->txt_data;
1305 }
1306
1307 void aui_service_dialog_set_resolve_service(AuiServiceDialog *d, gboolean resolve) {
1308     g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1309     
1310     d->priv->resolve_service = resolve;
1311 }
1312
1313 gboolean aui_service_dialog_get_resolve_service(AuiServiceDialog *d) {
1314     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), FALSE);
1315
1316     return d->priv->resolve_service;
1317 }
1318
1319 void aui_service_dialog_set_resolve_host_name(AuiServiceDialog *d, gboolean resolve) {
1320     g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1321     
1322     d->priv->resolve_host_name = resolve;
1323 }
1324
1325 gboolean aui_service_dialog_get_resolve_host_name(AuiServiceDialog *d) {
1326     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), FALSE);
1327
1328     return d->priv->resolve_host_name;
1329 }
1330
1331 void aui_service_dialog_set_address_family(AuiServiceDialog *d, AvahiProtocol proto) {
1332     g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1333     g_return_if_fail(proto == AVAHI_PROTO_UNSPEC || proto == AVAHI_PROTO_INET || proto == AVAHI_PROTO_INET6);
1334
1335     d->priv->address_family = proto;
1336 }
1337
1338 AvahiProtocol aui_service_dialog_get_address_family(AuiServiceDialog *d) {
1339     g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), AVAHI_PROTO_UNSPEC);
1340
1341     return d->priv->address_family;
1342 }
1343
1344 static void aui_service_dialog_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) {
1345     AuiServiceDialog *d = AUI_SERVICE_DIALOG(object);
1346     
1347     switch (prop_id) {
1348         case PROP_BROWSE_SERVICE_TYPES:
1349             aui_service_dialog_set_browse_service_typesv(d, g_value_get_pointer(value));
1350             break;
1351
1352         case PROP_DOMAIN:
1353             aui_service_dialog_set_domain(d, g_value_get_string(value));
1354             break;
1355
1356         case PROP_SERVICE_TYPE:
1357             aui_service_dialog_set_service_type(d, g_value_get_string(value));
1358             break;
1359
1360         case PROP_SERVICE_NAME:
1361             aui_service_dialog_set_service_name(d, g_value_get_string(value));
1362             break;
1363             
1364         case PROP_RESOLVE_SERVICE:
1365             aui_service_dialog_set_resolve_service(d, g_value_get_boolean(value));
1366             break;
1367
1368         case PROP_RESOLVE_HOST_NAME:
1369             aui_service_dialog_set_resolve_host_name(d, g_value_get_boolean(value));
1370             break;
1371
1372         case PROP_ADDRESS_FAMILY:
1373             aui_service_dialog_set_address_family(d, g_value_get_int(value));
1374             break;
1375             
1376         default:
1377             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1378             break;
1379     }
1380 }
1381
1382 static void aui_service_dialog_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) {
1383     AuiServiceDialog *d = AUI_SERVICE_DIALOG(object);
1384     
1385     switch (prop_id) {
1386         case PROP_BROWSE_SERVICE_TYPES:
1387             g_value_set_pointer(value, (gpointer) aui_service_dialog_get_browse_service_types(d));
1388             break;
1389
1390         case PROP_DOMAIN:
1391             g_value_set_string(value, aui_service_dialog_get_domain(d));
1392             break;
1393
1394         case PROP_SERVICE_TYPE:
1395             g_value_set_string(value, aui_service_dialog_get_service_type(d));
1396             break;
1397
1398         case PROP_SERVICE_NAME:
1399             g_value_set_string(value, aui_service_dialog_get_service_name(d));
1400             break;
1401
1402         case PROP_ADDRESS:
1403             g_value_set_pointer(value, (gpointer) aui_service_dialog_get_address(d));
1404             break;
1405
1406         case PROP_PORT:
1407             g_value_set_uint(value, aui_service_dialog_get_port(d));
1408             break;
1409
1410         case PROP_HOST_NAME:
1411             g_value_set_string(value, aui_service_dialog_get_host_name(d));
1412             break;
1413                                
1414         case PROP_TXT_DATA:
1415             g_value_set_pointer(value, (gpointer) aui_service_dialog_get_txt_data(d));
1416             break;
1417             
1418         case PROP_RESOLVE_SERVICE:
1419             g_value_set_boolean(value, aui_service_dialog_get_resolve_service(d));
1420             break;
1421
1422         case PROP_RESOLVE_HOST_NAME:
1423             g_value_set_boolean(value, aui_service_dialog_get_resolve_host_name(d));
1424             break;
1425
1426         case PROP_ADDRESS_FAMILY:
1427             g_value_set_int(value, aui_service_dialog_get_address_family(d));
1428             break;
1429             
1430         default:
1431             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1432             break;
1433     }
1434 }
1435