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