4 This file is part of avahi.
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.
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.
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
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>
41 #if defined(HAVE_GDBM) || defined(HAVE_DBM)
42 #include "../avahi-utils/stdb.h"
45 /* todo: i18n, HIGify */
47 struct _AuiServiceDialog {
48 GtkDialog parent_instance;
50 AvahiGLibPoll *glib_poll;
52 AvahiServiceBrowser **browsers;
53 AvahiServiceResolver *resolver;
54 AvahiDomainBrowser *domain_browser;
56 gchar **browse_service_types;
60 AvahiProtocol address_family;
64 AvahiStringList *txt_data;
67 gboolean resolve_service, resolve_service_done;
68 gboolean resolve_host_name, resolve_host_name_done;
70 GtkWidget *domain_label;
71 GtkWidget *domain_button;
72 GtkWidget *service_tree_view;
73 GtkWidget *service_progress_bar;
74 GtkWidget *service_ok_button;
76 GtkListStore *service_list_store, *domain_list_store;
78 guint service_pulse_timeout;
79 guint domain_pulse_timeout;
82 AvahiIfIndex common_interface;
83 AvahiProtocol common_protocol;
85 GtkWidget *domain_dialog;
86 GtkWidget *domain_entry;
87 GtkWidget *domain_tree_view;
88 GtkWidget *domain_progress_bar;
89 GtkWidget *domain_ok_button;
94 PROP_BROWSE_SERVICE_TYPES,
102 PROP_RESOLVE_SERVICE,
103 PROP_RESOLVE_HOST_NAME,
108 SERVICE_COLUMN_IFACE,
109 SERVICE_COLUMN_PROTO,
112 SERVICE_COLUMN_PRETTY_IFACE,
113 SERVICE_COLUMN_PRETTY_TYPE,
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);
127 G_DEFINE_TYPE(AuiServiceDialog, aui_service_dialog, GTK_TYPE_DIALOG)
129 static void aui_service_dialog_class_init(AuiServiceDialogClass *klass) {
130 GObjectClass *object_class;
132 object_class = (GObjectClass*) klass;
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;
138 g_object_class_install_property(
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(
146 g_param_spec_string("domain", "Domain", "The domain to browse in, or NULL for the default domain",
148 G_PARAM_READABLE | G_PARAM_WRITABLE));
149 g_object_class_install_property(
152 g_param_spec_string("service_type", "Service Type", "The service type of the selected service",
154 G_PARAM_READABLE | G_PARAM_WRITABLE));
155 g_object_class_install_property(
158 g_param_spec_string("service_name", "Service Name", "The service name of the selected service",
160 G_PARAM_READABLE | G_PARAM_WRITABLE));
161 g_object_class_install_property(
164 g_param_spec_pointer("address", "Address", "The address of the resolved service",
166 g_object_class_install_property(
169 g_param_spec_uint("port", "Port", "The IP port number of the resolved service",
172 g_object_class_install_property(
175 g_param_spec_string("host_name", "Host Name", "The host name of the resolved service",
178 g_object_class_install_property(
181 g_param_spec_pointer("txt_data", "TXT Data", "The TXT data of the resolved service",
183 g_object_class_install_property(
185 PROP_RESOLVE_SERVICE,
186 g_param_spec_boolean("resolve_service", "Resolve service", "Resolve service",
188 G_PARAM_READABLE | G_PARAM_WRITABLE));
189 g_object_class_install_property(
191 PROP_RESOLVE_HOST_NAME,
192 g_param_spec_boolean("resolve_host_name", "Resolve service host name", "Resolve service host name",
194 G_PARAM_READABLE | G_PARAM_WRITABLE));
195 g_object_class_install_property(
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));
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,
212 static gboolean service_pulse_callback(gpointer data) {
213 AuiServiceDialog *d = AUI_SERVICE_DIALOG(data);
215 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(d->service_progress_bar));
219 static gboolean domain_pulse_callback(gpointer data) {
220 AuiServiceDialog *d = AUI_SERVICE_DIALOG(data);
222 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(d->domain_progress_bar));
226 static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
227 AuiServiceDialog *d = AUI_SERVICE_DIALOG(userdata);
229 if (state == AVAHI_CLIENT_FAILURE) {
230 GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
231 GTK_DIALOG_DESTROY_WITH_PARENT,
234 "Avahi client failure: %s",
235 avahi_strerror(avahi_client_errno(c)));
236 gtk_dialog_run(GTK_DIALOG(m));
237 gtk_widget_destroy(m);
239 gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
243 static void resolve_callback(
244 AvahiServiceResolver *r,
245 AvahiIfIndex interface,
246 AvahiProtocol protocol,
247 AvahiResolverEvent event,
251 const char *host_name,
252 const AvahiAddress *a,
254 AvahiStringList *txt,
255 AvahiLookupResultFlags flags,
258 AuiServiceDialog *d = AUI_SERVICE_DIALOG(userdata);
261 case AVAHI_RESOLVER_FOUND:
263 d->resolve_service_done = 1;
265 g_free(d->service_name);
266 d->service_name = g_strdup(name);
268 g_free(d->service_type);
269 d->service_type = g_strdup(type);
272 d->domain = g_strdup(domain);
274 g_free(d->host_name);
275 d->host_name = g_strdup(host_name);
279 avahi_string_list_free(d->txt_data);
280 d->txt_data = avahi_string_list_copy(txt);
283 d->resolve_host_name_done = 1;
287 gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_OK);
291 case AVAHI_RESOLVER_FAILURE: {
292 GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
293 GTK_DIALOG_DESTROY_WITH_PARENT,
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);
301 gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
308 static void browse_callback(
309 AvahiServiceBrowser *b,
310 AvahiIfIndex interface,
311 AvahiProtocol protocol,
312 AvahiBrowserEvent event,
316 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
319 AuiServiceDialog *d = AUI_SERVICE_DIALOG(userdata);
323 case AVAHI_BROWSER_NEW: {
325 const gchar *pretty_type;
326 char ifname[IFNAMSIZ];
328 GtkTreeSelection *selection;
330 if (!(if_indextoname(interface, ifname)))
331 g_snprintf(ifname, sizeof(ifname), "%i", interface);
333 ifs = g_strdup_printf("%s %s", ifname, protocol == AVAHI_PROTO_INET ? "IPv4" : "IPv6");
335 #if defined(HAVE_GDBM) || defined(HAVE_DBM)
336 pretty_type = stdb_lookup(type);
341 gtk_list_store_append(d->service_list_store, &iter);
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,
354 if (d->common_protocol == AVAHI_PROTO_UNSPEC)
355 d->common_protocol = protocol;
357 if (d->common_interface == AVAHI_IF_UNSPEC)
358 d->common_interface = interface;
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);
365 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(d->service_tree_view));
366 if (!gtk_tree_selection_get_selected(selection, NULL, NULL)) {
368 if (d->service_type && d->service_name &&
369 avahi_domain_equal(d->service_type, type) && strcasecmp(d->service_name, name) == 0) {
372 gtk_tree_selection_select_iter(selection, &iter);
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);
384 case AVAHI_BROWSER_REMOVE: {
388 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(d->service_list_store), &iter);
390 gint _interface, _protocol;
391 gchar *_name, *_type;
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,
401 found = _interface == interface && _protocol == protocol && strcasecmp(_name, name) == 0 && avahi_domain_equal(_type, type);
405 gtk_list_store_remove(d->service_list_store, &iter);
409 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(d->service_list_store), &iter);
415 case AVAHI_BROWSER_FAILURE: {
416 GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
417 GTK_DIALOG_DESTROY_WITH_PARENT,
420 "Browsing for service type %s in domain %s failed: %s",
422 avahi_strerror(avahi_client_errno(d->client)));
423 gtk_dialog_run(GTK_DIALOG(m));
424 gtk_widget_destroy(m);
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);
437 case AVAHI_BROWSER_CACHE_EXHAUSTED:
442 static void domain_make_default_selection(AuiServiceDialog *d, const gchar *name, GtkTreeIter *iter) {
443 GtkTreeSelection *selection;
445 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(d->domain_tree_view));
446 if (!gtk_tree_selection_get_selected(selection, NULL, NULL)) {
448 if (avahi_domain_equal(gtk_entry_get_text(GTK_ENTRY(d->domain_entry)), name)) {
451 gtk_tree_selection_select_iter(selection, iter);
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);
461 static void domain_browse_callback(
462 AvahiDomainBrowser *b,
463 AvahiIfIndex interface,
464 AvahiProtocol protocol,
465 AvahiBrowserEvent event,
467 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
470 AuiServiceDialog *d = AUI_SERVICE_DIALOG(userdata);
474 case AVAHI_BROWSER_NEW: {
476 gboolean found = FALSE, valid;
479 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(d->domain_list_store), &iter);
483 gtk_tree_model_get(GTK_TREE_MODEL(d->domain_list_store), &iter,
484 DOMAIN_COLUMN_NAME, &_name,
485 DOMAIN_COLUMN_REF, &ref,
488 found = avahi_domain_equal(_name, name);
494 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(d->domain_list_store), &iter);
498 gtk_list_store_set(d->domain_list_store, &iter, DOMAIN_COLUMN_REF, ref + 1, -1);
500 gtk_list_store_append(d->domain_list_store, &iter);
502 gtk_list_store_set(d->domain_list_store, &iter,
503 DOMAIN_COLUMN_NAME, name,
504 DOMAIN_COLUMN_REF, 1,
508 domain_make_default_selection(d, name, &iter);
513 case AVAHI_BROWSER_REMOVE: {
517 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(d->domain_list_store), &iter);
523 gtk_tree_model_get(GTK_TREE_MODEL(d->domain_list_store), &iter,
524 DOMAIN_COLUMN_NAME, &_name,
525 DOMAIN_COLUMN_REF, &ref,
528 found = avahi_domain_equal(_name, name);
533 gtk_list_store_remove(d->service_list_store, &iter);
535 gtk_list_store_set(d->domain_list_store, &iter, DOMAIN_COLUMN_REF, ref - 1, -1);
539 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(d->domain_list_store), &iter);
546 case AVAHI_BROWSER_FAILURE: {
547 GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
548 GTK_DIALOG_DESTROY_WITH_PARENT,
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);
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);
567 case AVAHI_BROWSER_CACHE_EXHAUSTED:
572 static const gchar *get_domain_name(AuiServiceDialog *d) {
575 g_return_val_if_fail(d, NULL);
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,
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);
596 static gboolean start_callback(gpointer data) {
598 AuiServiceDialog *d = AUI_SERVICE_DIALOG(data);
600 AvahiServiceBrowser **sb;
606 if (!d->browse_service_types || !*d->browse_service_types) {
607 g_warning("Browse service type list is empty!");
612 if (!(d->client = avahi_client_new(avahi_glib_poll_get(d->glib_poll), 0, client_callback, d, &error))) {
614 GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
615 GTK_DIALOG_DESTROY_WITH_PARENT,
618 "Failed to connect to Avahi server: %s",
619 avahi_strerror(error));
620 gtk_dialog_run(GTK_DIALOG(m));
621 gtk_widget_destroy(m);
623 gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
628 if (!(domain = get_domain_name(d))) {
629 gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
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>:");
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);
644 for (sb = d->browsers; *sb; sb++)
645 avahi_service_browser_free(*sb);
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;
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);
659 if (d->service_pulse_timeout <= 0)
660 d->service_pulse_timeout = g_timeout_add(100, service_pulse_callback, d);
662 for (i = 0; d->browse_service_types[i]; i++)
666 d->browsers = g_new0(AvahiServiceBrowser*, i+1);
667 for (st = d->browse_service_types, sb = d->browsers; *st; st++, sb++) {
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,
674 "Failed to create browser for %s: %s",
676 avahi_strerror(avahi_client_errno(d->client)));
677 gtk_dialog_run(GTK_DIALOG(m));
678 gtk_widget_destroy(m);
680 gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
689 static void aui_service_dialog_finalize(GObject *object) {
690 AuiServiceDialog *d = AUI_SERVICE_DIALOG(object);
692 if (d->domain_pulse_timeout > 0)
693 g_source_remove(d->domain_pulse_timeout);
695 if (d->service_pulse_timeout > 0)
696 g_source_remove(d->service_pulse_timeout);
698 if (d->start_idle > 0)
699 g_source_remove(d->start_idle);
701 g_free(d->host_name);
703 g_free(d->service_name);
705 avahi_string_list_free(d->txt_data);
707 g_strfreev(d->browse_service_types);
709 if (d->domain_browser)
710 avahi_domain_browser_free(d->domain_browser);
713 avahi_service_resolver_free(d->resolver);
716 AvahiServiceBrowser **sb;
718 for (sb = d->browsers; *sb; sb++)
719 avahi_service_browser_free(*sb);
725 avahi_client_free(d->client);
728 avahi_glib_poll_free(d->glib_poll);
730 G_OBJECT_CLASS(aui_service_dialog_parent_class)->finalize(object);
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);
736 gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_OK);
739 static void service_selection_changed_callback(GtkTreeSelection *selection, gpointer user_data) {
740 AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
742 gtk_widget_set_sensitive(d->service_ok_button, gtk_tree_selection_get_selected(selection, NULL, NULL));
745 static void response_callback(GtkDialog *dialog, gint response, gpointer user_data) {
746 AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
748 if (response == GTK_RESPONSE_OK &&
749 ((d->resolve_service && !d->resolve_service_done) ||
750 (d->resolve_host_name && !d->resolve_host_name_done))) {
753 gint interface, protocol;
757 g_signal_stop_emission(dialog, g_signal_lookup("response", gtk_dialog_get_type()), 0);
762 g_return_if_fail(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(d->service_tree_view)), NULL, &iter));
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);
770 g_return_if_fail(d->client);
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);
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))) {
781 GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
782 GTK_DIALOG_DESTROY_WITH_PARENT,
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);
791 gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
797 static gboolean is_valid_domain_suffix(const gchar *n) {
798 gchar label[AVAHI_LABEL_MAX];
800 if (!avahi_is_valid_domain_name(n))
803 if (!avahi_unescape_label(&n, label, sizeof(label)))
806 /* At least one label */
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);
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);
818 static void domain_selection_changed_callback(GtkTreeSelection *selection, gpointer user_data) {
820 AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
823 g_return_if_fail(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(d->domain_tree_view)), NULL, &iter));
825 gtk_tree_model_get(GTK_TREE_MODEL(d->domain_list_store), &iter,
826 DOMAIN_COLUMN_NAME, &name, -1);
828 gtk_entry_set_text(GTK_ENTRY(d->domain_entry), name);
831 static void domain_entry_changed_callback(GtkEditable *editable, gpointer user_data) {
832 AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
834 gtk_widget_set_sensitive(d->domain_ok_button, is_valid_domain_suffix(gtk_entry_get_text(GTK_ENTRY(d->domain_entry))));
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);
846 g_return_if_fail(!d->domain_dialog);
847 g_return_if_fail(!d->domain_browser);
849 if (!(domain = get_domain_name(d))) {
850 gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
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,
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);
864 gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
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);
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);
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);
883 vbox2 = gtk_vbox_new(FALSE, 8);
884 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
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);
891 d->domain_list_store = gtk_list_store_new(N_DOMAIN_COLUMNS, G_TYPE_STRING, G_TYPE_INT);
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);
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);
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);
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);
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))));
918 gtk_widget_grab_default(d->domain_ok_button);
919 gtk_widget_grab_focus(d->domain_entry);
921 gtk_window_set_default_size(GTK_WINDOW(d->domain_dialog), 300, 300);
923 gtk_widget_show_all(vbox);
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);
929 d->domain_pulse_timeout = g_timeout_add(100, domain_pulse_callback, d);
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)));
934 gtk_widget_destroy(d->domain_dialog);
935 d->domain_dialog = NULL;
937 if (d->domain_pulse_timeout > 0) {
938 g_source_remove(d->domain_pulse_timeout);
939 d->domain_pulse_timeout = 0;
942 avahi_domain_browser_free(d->domain_browser);
943 d->domain_browser = NULL;
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;
955 d->service_name = NULL;
957 d->browse_service_types = NULL;
958 memset(&d->address, 0, sizeof(d->address));
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;
968 d->domain_browser = NULL;
970 d->service_pulse_timeout = 0;
971 d->domain_pulse_timeout = 0;
973 d->common_interface = AVAHI_IF_UNSPEC;
974 d->common_protocol = AVAHI_PROTO_UNSPEC;
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;
982 d->service_list_store = d->domain_list_store = NULL;
984 gtk_widget_push_composite_child();
986 gtk_container_set_border_width(GTK_CONTAINER(d), 5);
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);
992 hbox = gtk_hbox_new(FALSE, 8);
993 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
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);
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);
1004 vbox2 = gtk_vbox_new(FALSE, 8);
1005 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
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);
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);
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);
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);
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);
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);
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);
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);
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);
1049 gtk_widget_grab_default(d->service_ok_button);
1050 gtk_widget_grab_focus(d->service_tree_view);
1052 gtk_window_set_default_size(GTK_WINDOW(d), 400, 300);
1054 gtk_widget_show_all(vbox);
1056 gtk_widget_pop_composite_child();
1058 d->glib_poll = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT);
1060 d->service_pulse_timeout = g_timeout_add(100, service_pulse_callback, d);
1061 d->start_idle = g_idle_add(start_callback, d);
1063 g_signal_connect(d, "response", G_CALLBACK(response_callback), d);
1066 static void restart_browsing(AuiServiceDialog *d) {
1067 g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1069 if (d->start_idle <= 0)
1070 d->start_idle = g_idle_add(start_callback, d);
1073 void aui_service_dialog_set_browse_service_types(AuiServiceDialog *d, const char *type, ...) {
1078 g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1079 g_return_if_fail(type);
1081 g_strfreev(d->browse_service_types);
1083 va_copy(apcopy, ap);
1086 for (u = 1; va_arg(ap, const char *); u++)
1090 d->browse_service_types = g_new0(gchar*, u+1);
1091 d->browse_service_types[0] = g_strdup(type);
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);
1098 if (d->browse_service_types[0] && d->browse_service_types[1]) {
1099 /* Multiple service types, enable headers */
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);
1105 restart_browsing(d);
1108 void aui_service_dialog_set_browse_service_typesv(AuiServiceDialog *d, const char *const*types) {
1110 g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1111 g_return_if_fail(types);
1112 g_return_if_fail(*types);
1114 g_strfreev(d->browse_service_types);
1115 d->browse_service_types = g_strdupv((char**) types);
1117 if (d->browse_service_types[0] && d->browse_service_types[1]) {
1118 /* Multiple service types, enable headers */
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);
1124 restart_browsing(d);
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);
1130 return (const char* const*) d->browse_service_types;
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));
1138 d->domain = domain ? avahi_normalize_name_strdup(domain) : NULL;
1140 restart_browsing(d);
1143 const char* aui_service_dialog_get_domain(AuiServiceDialog *d) {
1144 g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1149 void aui_service_dialog_set_service_name(AuiServiceDialog *d, const char *name) {
1150 g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1152 g_free(d->service_name);
1153 d->service_name = g_strdup(name);
1156 const char* aui_service_dialog_get_service_name(AuiServiceDialog *d) {
1157 g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1159 return d->service_name;
1162 void aui_service_dialog_set_service_type(AuiServiceDialog *d, const char*stype) {
1163 g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1165 g_free(d->service_type);
1166 d->service_type = g_strdup(stype);
1169 const char* aui_service_dialog_get_service_type(AuiServiceDialog *d) {
1170 g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1172 return d->service_type;
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);
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);
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);
1193 return d->host_name;
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);
1203 void aui_service_dialog_set_resolve_service(AuiServiceDialog *d, gboolean resolve) {
1204 g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1206 d->resolve_service = resolve;
1209 gboolean aui_service_dialog_get_resolve_service(AuiServiceDialog *d) {
1210 g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), FALSE);
1212 return d->resolve_service;
1215 void aui_service_dialog_set_resolve_host_name(AuiServiceDialog *d, gboolean resolve) {
1216 g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1218 d->resolve_host_name = resolve;
1221 gboolean aui_service_dialog_get_resolve_host_name(AuiServiceDialog *d) {
1222 g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), FALSE);
1224 return d->resolve_host_name;
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);
1231 d->address_family = proto;
1234 AvahiProtocol aui_service_dialog_get_address_family(AuiServiceDialog *d) {
1235 g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), AVAHI_PROTO_UNSPEC);
1237 return d->address_family;
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);
1244 case PROP_BROWSE_SERVICE_TYPES:
1245 aui_service_dialog_set_browse_service_typesv(d, g_value_get_pointer(value));
1249 aui_service_dialog_set_domain(d, g_value_get_string(value));
1252 case PROP_SERVICE_TYPE:
1253 aui_service_dialog_set_service_type(d, g_value_get_string(value));
1256 case PROP_SERVICE_NAME:
1257 aui_service_dialog_set_service_name(d, g_value_get_string(value));
1260 case PROP_RESOLVE_SERVICE:
1261 aui_service_dialog_set_resolve_service(d, g_value_get_boolean(value));
1264 case PROP_RESOLVE_HOST_NAME:
1265 aui_service_dialog_set_resolve_host_name(d, g_value_get_boolean(value));
1268 case PROP_ADDRESS_FAMILY:
1269 aui_service_dialog_set_address_family(d, g_value_get_int(value));
1273 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
1278 static void aui_service_dialog_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) {
1279 AuiServiceDialog *d = AUI_SERVICE_DIALOG(object);
1282 case PROP_BROWSE_SERVICE_TYPES:
1283 g_value_set_pointer(value, (gpointer) aui_service_dialog_get_browse_service_types(d));
1287 g_value_set_string(value, aui_service_dialog_get_domain(d));
1290 case PROP_SERVICE_TYPE:
1291 g_value_set_string(value, aui_service_dialog_get_service_type(d));
1294 case PROP_SERVICE_NAME:
1295 g_value_set_string(value, aui_service_dialog_get_service_name(d));
1299 g_value_set_pointer(value, (gpointer) aui_service_dialog_get_address(d));
1303 g_value_set_uint(value, aui_service_dialog_get_port(d));
1306 case PROP_HOST_NAME:
1307 g_value_set_string(value, aui_service_dialog_get_host_name(d));
1311 g_value_set_pointer(value, aui_service_dialog_get_txt_data(d));
1314 case PROP_RESOLVE_SERVICE:
1315 g_value_set_boolean(value, aui_service_dialog_get_resolve_service(d));
1318 case PROP_RESOLVE_HOST_NAME:
1319 g_value_set_boolean(value, aui_service_dialog_get_resolve_host_name(d));
1322 case PROP_ADDRESS_FAMILY:
1323 g_value_set_int(value, aui_service_dialog_get_address_family(d));
1327 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);