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: props, 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;
101 SERVICE_COLUMN_PRETTY_IFACE,
102 SERVICE_COLUMN_PRETTY_TYPE,
112 static void aui_service_dialog_finalize(GObject *object);
114 G_DEFINE_TYPE(AuiServiceDialog, aui_service_dialog, GTK_TYPE_DIALOG)
116 static void aui_service_dialog_class_init(AuiServiceDialogClass *klass) {
117 GObjectClass *object_class;
118 /* GtkWidgetClass *widget_class; */
119 /* GtkDialogClass *dialog_class; */
121 object_class = (GObjectClass*) klass;
122 /* widget_class = (GtkWidgetClass*) klass; */
123 /* dialog_class = (GtkDialogClass*) klass; */
125 object_class->finalize = aui_service_dialog_finalize;
129 GtkWidget *aui_service_dialog_new(const gchar *title) {
130 return GTK_WIDGET(g_object_new(
131 AUI_TYPE_SERVICE_DIALOG,
132 "has-separator", FALSE,
137 static gboolean service_pulse_callback(gpointer data) {
138 AuiServiceDialog *d = AUI_SERVICE_DIALOG(data);
140 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(d->service_progress_bar));
144 static gboolean domain_pulse_callback(gpointer data) {
145 AuiServiceDialog *d = AUI_SERVICE_DIALOG(data);
147 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(d->domain_progress_bar));
151 static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
152 AuiServiceDialog *d = AUI_SERVICE_DIALOG(userdata);
154 if (state == AVAHI_CLIENT_FAILURE) {
155 GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
156 GTK_DIALOG_DESTROY_WITH_PARENT,
159 "Avahi client failure: %s",
160 avahi_strerror(avahi_client_errno(c)));
161 gtk_dialog_run(GTK_DIALOG(m));
162 gtk_widget_destroy(m);
164 gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
168 static void resolve_callback(
169 AvahiServiceResolver *r,
170 AvahiIfIndex interface,
171 AvahiProtocol protocol,
172 AvahiResolverEvent event,
176 const char *host_name,
177 const AvahiAddress *a,
179 AvahiStringList *txt,
180 AvahiLookupResultFlags flags,
183 AuiServiceDialog *d = AUI_SERVICE_DIALOG(userdata);
186 case AVAHI_RESOLVER_FOUND:
188 d->resolve_service_done = 1;
190 g_free(d->service_name);
191 d->service_name = g_strdup(name);
193 g_free(d->service_type);
194 d->service_type = g_strdup(type);
197 d->domain = g_strdup(domain);
199 g_free(d->host_name);
200 d->host_name = g_strdup(host_name);
204 avahi_string_list_free(d->txt_data);
205 d->txt_data = avahi_string_list_copy(txt);
208 d->resolve_host_name_done = 1;
212 gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_OK);
216 case AVAHI_RESOLVER_FAILURE: {
217 GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
218 GTK_DIALOG_DESTROY_WITH_PARENT,
221 "Avahi resolver failure: %s",
222 avahi_strerror(avahi_client_errno(d->client)));
223 gtk_dialog_run(GTK_DIALOG(m));
224 gtk_widget_destroy(m);
226 gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
233 static void browse_callback(
234 AvahiServiceBrowser *b,
235 AvahiIfIndex interface,
236 AvahiProtocol protocol,
237 AvahiBrowserEvent event,
241 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
244 AuiServiceDialog *d = AUI_SERVICE_DIALOG(userdata);
248 case AVAHI_BROWSER_NEW: {
250 const gchar *pretty_type;
251 char ifname[IFNAMSIZ];
253 GtkTreeSelection *selection;
255 if (!(if_indextoname(interface, ifname)))
256 g_snprintf(ifname, sizeof(ifname), "%i", interface);
258 ifs = g_strdup_printf("%s %s", ifname, protocol == AVAHI_PROTO_INET ? "IPv4" : "IPv6");
260 #if defined(HAVE_GDBM) || defined(HAVE_DBM)
261 pretty_type = stdb_lookup(type);
266 gtk_list_store_append(d->service_list_store, &iter);
268 gtk_list_store_set(d->service_list_store, &iter,
269 SERVICE_COLUMN_IFACE, interface,
270 SERVICE_COLUMN_PROTO, protocol,
271 SERVICE_COLUMN_NAME, name,
272 SERVICE_COLUMN_TYPE, type,
273 SERVICE_COLUMN_PRETTY_IFACE, ifs,
274 SERVICE_COLUMN_PRETTY_TYPE, pretty_type,
279 if (d->common_protocol == AVAHI_PROTO_UNSPEC)
280 d->common_protocol = protocol;
282 if (d->common_interface == AVAHI_IF_UNSPEC)
283 d->common_interface = interface;
285 if (d->common_interface != interface || d->common_protocol != protocol) {
286 gtk_tree_view_column_set_visible(gtk_tree_view_get_column(GTK_TREE_VIEW(d->service_tree_view), 0), TRUE);
287 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(d->service_tree_view), TRUE);
290 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(d->service_tree_view));
291 if (!gtk_tree_selection_get_selected(selection, NULL, NULL)) {
293 if (d->service_type && d->service_name &&
294 avahi_domain_equal(d->service_type, type) && strcasecmp(d->service_name, name) == 0) {
297 gtk_tree_selection_select_iter(selection, &iter);
299 path = gtk_tree_model_get_path(GTK_TREE_MODEL(d->service_list_store), &iter);
300 gtk_tree_view_set_cursor(GTK_TREE_VIEW(d->service_tree_view), path, NULL, FALSE);
301 gtk_tree_path_free(path);
309 case AVAHI_BROWSER_REMOVE: {
313 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(d->service_list_store), &iter);
315 gint _interface, _protocol;
316 gchar *_name, *_type;
319 gtk_tree_model_get(GTK_TREE_MODEL(d->service_list_store), &iter,
320 SERVICE_COLUMN_IFACE, &_interface,
321 SERVICE_COLUMN_PROTO, &_protocol,
322 SERVICE_COLUMN_NAME, &_name,
323 SERVICE_COLUMN_TYPE, &_type,
326 found = _interface == interface && _protocol == protocol && strcasecmp(_name, name) == 0 && avahi_domain_equal(_type, type);
330 gtk_list_store_remove(d->service_list_store, &iter);
334 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(d->service_list_store), &iter);
340 case AVAHI_BROWSER_FAILURE: {
341 GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
342 GTK_DIALOG_DESTROY_WITH_PARENT,
345 "Browsing for service type %s in domain %s failed: %s",
347 avahi_strerror(avahi_client_errno(d->client)));
348 gtk_dialog_run(GTK_DIALOG(m));
349 gtk_widget_destroy(m);
354 case AVAHI_BROWSER_ALL_FOR_NOW:
355 if (d->service_pulse_timeout > 0) {
356 g_source_remove(d->service_pulse_timeout);
357 d->service_pulse_timeout = 0;
358 gtk_widget_hide(d->service_progress_bar);
362 case AVAHI_BROWSER_CACHE_EXHAUSTED:
367 static void domain_make_default_selection(AuiServiceDialog *d, const gchar *name, GtkTreeIter *iter) {
368 GtkTreeSelection *selection;
370 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(d->domain_tree_view));
371 if (!gtk_tree_selection_get_selected(selection, NULL, NULL)) {
373 if (avahi_domain_equal(gtk_entry_get_text(GTK_ENTRY(d->domain_entry)), name)) {
376 gtk_tree_selection_select_iter(selection, iter);
378 path = gtk_tree_model_get_path(GTK_TREE_MODEL(d->domain_list_store), iter);
379 gtk_tree_view_set_cursor(GTK_TREE_VIEW(d->domain_tree_view), path, NULL, FALSE);
380 gtk_tree_path_free(path);
386 static void domain_browse_callback(
387 AvahiDomainBrowser *b,
388 AvahiIfIndex interface,
389 AvahiProtocol protocol,
390 AvahiBrowserEvent event,
392 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
395 AuiServiceDialog *d = AUI_SERVICE_DIALOG(userdata);
399 case AVAHI_BROWSER_NEW: {
401 gboolean found = FALSE, valid;
404 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(d->domain_list_store), &iter);
408 gtk_tree_model_get(GTK_TREE_MODEL(d->domain_list_store), &iter,
409 DOMAIN_COLUMN_NAME, &_name,
410 DOMAIN_COLUMN_REF, &ref,
413 found = avahi_domain_equal(_name, name);
419 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(d->domain_list_store), &iter);
423 gtk_list_store_set(d->domain_list_store, &iter, DOMAIN_COLUMN_REF, ref + 1, -1);
425 gtk_list_store_append(d->domain_list_store, &iter);
427 gtk_list_store_set(d->domain_list_store, &iter,
428 DOMAIN_COLUMN_NAME, name,
429 DOMAIN_COLUMN_REF, 1,
433 domain_make_default_selection(d, name, &iter);
438 case AVAHI_BROWSER_REMOVE: {
442 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(d->domain_list_store), &iter);
448 gtk_tree_model_get(GTK_TREE_MODEL(d->domain_list_store), &iter,
449 DOMAIN_COLUMN_NAME, &_name,
450 DOMAIN_COLUMN_REF, &ref,
453 found = avahi_domain_equal(_name, name);
458 gtk_list_store_remove(d->service_list_store, &iter);
460 gtk_list_store_set(d->domain_list_store, &iter, DOMAIN_COLUMN_REF, ref - 1, -1);
464 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(d->domain_list_store), &iter);
471 case AVAHI_BROWSER_FAILURE: {
472 GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
473 GTK_DIALOG_DESTROY_WITH_PARENT,
476 "Avahi domain browser failure: %s",
477 avahi_strerror(avahi_client_errno(d->client)));
478 gtk_dialog_run(GTK_DIALOG(m));
479 gtk_widget_destroy(m);
484 case AVAHI_BROWSER_ALL_FOR_NOW:
485 if (d->domain_pulse_timeout > 0) {
486 g_source_remove(d->domain_pulse_timeout);
487 d->domain_pulse_timeout = 0;
488 gtk_widget_hide(d->domain_progress_bar);
492 case AVAHI_BROWSER_CACHE_EXHAUSTED:
497 static const gchar *get_domain_name(AuiServiceDialog *d) {
500 g_return_val_if_fail(d, NULL);
505 if (!(domain = avahi_client_get_domain_name(d->client))) {
506 GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
507 GTK_DIALOG_DESTROY_WITH_PARENT,
510 "Failed to read Avahi domain : %s",
511 avahi_strerror(avahi_client_errno(d->client)));
512 gtk_dialog_run(GTK_DIALOG(m));
513 gtk_widget_destroy(m);
521 static gboolean start_callback(gpointer data) {
523 AuiServiceDialog *d = AUI_SERVICE_DIALOG(data);
525 AvahiServiceBrowser **sb;
531 if (!d->browse_service_types || !*d->browse_service_types) {
532 g_warning("Browse service type list is empty!");
537 if (!(d->client = avahi_client_new(avahi_glib_poll_get(d->glib_poll), 0, client_callback, d, &error))) {
539 GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
540 GTK_DIALOG_DESTROY_WITH_PARENT,
543 "Failed to connect to Avahi server: %s",
544 avahi_strerror(error));
545 gtk_dialog_run(GTK_DIALOG(m));
546 gtk_widget_destroy(m);
548 gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
553 if (!(domain = get_domain_name(d))) {
554 gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
560 if (avahi_domain_equal(domain, "local."))
561 gtk_label_set_markup(GTK_LABEL(d->domain_label), "Browsing for services on <b>local network</b>:");
563 gchar *t = g_strdup_printf("Browsing for services in domain <b>%s</b>:", domain);
564 gtk_label_set_markup(GTK_LABEL(d->domain_label), t);
569 for (sb = d->browsers; *sb; sb++)
570 avahi_service_browser_free(*sb);
576 gtk_list_store_clear(GTK_LIST_STORE(d->service_list_store));
577 d->common_interface = AVAHI_IF_UNSPEC;
578 d->common_protocol = AVAHI_PROTO_UNSPEC;
580 gtk_tree_view_column_set_visible(gtk_tree_view_get_column(GTK_TREE_VIEW(d->service_tree_view), 0), FALSE);
581 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)));
582 gtk_widget_show(d->service_progress_bar);
584 if (d->service_pulse_timeout <= 0)
585 d->service_pulse_timeout = g_timeout_add(100, service_pulse_callback, d);
587 for (i = 0; d->browse_service_types[i]; i++)
591 d->browsers = g_new0(AvahiServiceBrowser*, i+1);
592 for (st = d->browse_service_types, sb = d->browsers; *st; st++, sb++) {
594 if (!(*sb = avahi_service_browser_new(d->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, *st, d->domain, 0, browse_callback, d))) {
595 GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
596 GTK_DIALOG_DESTROY_WITH_PARENT,
599 "Failed to create browser for %s: %s",
601 avahi_strerror(avahi_client_errno(d->client)));
602 gtk_dialog_run(GTK_DIALOG(m));
603 gtk_widget_destroy(m);
605 gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
614 static void aui_service_dialog_finalize(GObject *object) {
615 AuiServiceDialog *d = AUI_SERVICE_DIALOG(object);
617 if (d->domain_pulse_timeout > 0)
618 g_source_remove(d->domain_pulse_timeout);
620 if (d->service_pulse_timeout > 0)
621 g_source_remove(d->service_pulse_timeout);
623 if (d->start_idle > 0)
624 g_source_remove(d->start_idle);
626 g_free(d->host_name);
628 g_free(d->service_name);
630 avahi_string_list_free(d->txt_data);
632 g_strfreev(d->browse_service_types);
634 if (d->domain_browser)
635 avahi_domain_browser_free(d->domain_browser);
638 avahi_service_resolver_free(d->resolver);
641 AvahiServiceBrowser **sb;
643 for (sb = d->browsers; *sb; sb++)
644 avahi_service_browser_free(*sb);
650 avahi_client_free(d->client);
653 avahi_glib_poll_free(d->glib_poll);
655 G_OBJECT_CLASS(aui_service_dialog_parent_class)->finalize(object);
658 static void service_row_activated_callback(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data) {
659 AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
661 gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_OK);
664 static void service_selection_changed_callback(GtkTreeSelection *selection, gpointer user_data) {
665 AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
667 gtk_widget_set_sensitive(d->service_ok_button, gtk_tree_selection_get_selected(selection, NULL, NULL));
670 static void response_callback(GtkDialog *dialog, gint response, gpointer user_data) {
671 AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
673 if (response == GTK_RESPONSE_OK &&
674 ((d->resolve_service && !d->resolve_service_done) ||
675 (d->resolve_host_name && !d->resolve_host_name_done))) {
678 gint interface, protocol;
682 g_signal_stop_emission(dialog, g_signal_lookup("response", gtk_dialog_get_type()), 0);
687 g_return_if_fail(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(d->service_tree_view)), NULL, &iter));
689 gtk_tree_model_get(GTK_TREE_MODEL(d->service_list_store), &iter,
690 SERVICE_COLUMN_IFACE, &interface,
691 SERVICE_COLUMN_PROTO, &protocol,
692 SERVICE_COLUMN_NAME, &name,
693 SERVICE_COLUMN_TYPE, &type, -1);
695 g_return_if_fail(d->client);
697 gtk_widget_set_sensitive(GTK_WIDGET(dialog), FALSE);
698 cursor = gdk_cursor_new(GDK_WATCH);
699 gdk_window_set_cursor(GTK_WIDGET(dialog)->window, cursor);
700 gdk_cursor_unref(cursor);
702 if (!(d->resolver = avahi_service_resolver_new(
703 d->client, interface, protocol, name, type, d->domain,
704 d->address_family, !d->resolve_host_name ? AVAHI_LOOKUP_NO_ADDRESS : 0, resolve_callback, d))) {
706 GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
707 GTK_DIALOG_DESTROY_WITH_PARENT,
710 "Failed to create resolver for %s of type %s in domain %s: %s",
711 name, type, d->domain,
712 avahi_strerror(avahi_client_errno(d->client)));
713 gtk_dialog_run(GTK_DIALOG(m));
714 gtk_widget_destroy(m);
716 gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
722 static gboolean is_valid_domain_suffix(const gchar *n) {
723 gchar label[AVAHI_LABEL_MAX];
725 if (!avahi_is_valid_domain_name(n))
728 if (!avahi_unescape_label(&n, label, sizeof(label)))
731 /* At least one label */
736 static void domain_row_activated_callback(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data) {
737 AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
739 if (is_valid_domain_suffix(gtk_entry_get_text(GTK_ENTRY(d->domain_entry))))
740 gtk_dialog_response(GTK_DIALOG(d->domain_dialog), GTK_RESPONSE_OK);
743 static void domain_selection_changed_callback(GtkTreeSelection *selection, gpointer user_data) {
745 AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
748 g_return_if_fail(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(d->domain_tree_view)), NULL, &iter));
750 gtk_tree_model_get(GTK_TREE_MODEL(d->domain_list_store), &iter,
751 DOMAIN_COLUMN_NAME, &name, -1);
753 gtk_entry_set_text(GTK_ENTRY(d->domain_entry), name);
756 static void domain_entry_changed_callback(GtkEditable *editable, gpointer user_data) {
757 AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
759 gtk_widget_set_sensitive(d->domain_ok_button, is_valid_domain_suffix(gtk_entry_get_text(GTK_ENTRY(d->domain_entry))));
762 static void domain_button_clicked(GtkButton *button, gpointer user_data) {
763 GtkWidget *vbox, *vbox2, *scrolled_window;
764 GtkTreeSelection *selection;
765 GtkCellRenderer *renderer;
766 GtkTreeViewColumn *column;
767 AuiServiceDialog *d = AUI_SERVICE_DIALOG(user_data);
771 g_return_if_fail(!d->domain_dialog);
772 g_return_if_fail(!d->domain_browser);
774 if (!(domain = get_domain_name(d))) {
775 gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
779 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))) {
780 GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(d),
781 GTK_DIALOG_DESTROY_WITH_PARENT,
784 "Failed to create domain browser: %s",
785 avahi_strerror(avahi_client_errno(d->client)));
786 gtk_dialog_run(GTK_DIALOG(m));
787 gtk_widget_destroy(m);
789 gtk_dialog_response(GTK_DIALOG(d), GTK_RESPONSE_CANCEL);
793 d->domain_dialog = gtk_dialog_new();
794 gtk_container_set_border_width(GTK_CONTAINER(d->domain_dialog), 5);
795 gtk_window_set_title(GTK_WINDOW(d->domain_dialog), "Change domain");
796 gtk_dialog_set_has_separator(GTK_DIALOG(d->domain_dialog), FALSE);
798 vbox = gtk_vbox_new(FALSE, 8);
799 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
800 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(d->domain_dialog)->vbox), vbox, TRUE, TRUE, 0);
802 d->domain_entry = gtk_entry_new_with_max_length(AVAHI_DOMAIN_NAME_MAX);
803 gtk_entry_set_text(GTK_ENTRY(d->domain_entry), domain);
804 gtk_entry_set_activates_default(GTK_ENTRY(d->domain_entry), TRUE);
805 g_signal_connect(d->domain_entry, "changed", G_CALLBACK(domain_entry_changed_callback), d);
806 gtk_box_pack_start(GTK_BOX(vbox), d->domain_entry, FALSE, FALSE, 0);
808 vbox2 = gtk_vbox_new(FALSE, 8);
809 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
811 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
812 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
813 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_ETCHED_IN);
814 gtk_box_pack_start(GTK_BOX(vbox2), scrolled_window, TRUE, TRUE, 0);
816 d->domain_list_store = gtk_list_store_new(N_DOMAIN_COLUMNS, G_TYPE_STRING, G_TYPE_INT);
818 d->domain_tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(d->domain_list_store));
819 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(d->domain_tree_view), FALSE);
820 g_signal_connect(d->domain_tree_view, "row-activated", G_CALLBACK(domain_row_activated_callback), d);
821 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(d->domain_tree_view));
822 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
823 g_signal_connect(selection, "changed", G_CALLBACK(domain_selection_changed_callback), d);
825 renderer = gtk_cell_renderer_text_new();
826 column = gtk_tree_view_column_new_with_attributes("Name", renderer, "text", DOMAIN_COLUMN_NAME, NULL);
827 gtk_tree_view_column_set_expand(column, TRUE);
828 gtk_tree_view_append_column(GTK_TREE_VIEW(d->domain_tree_view), column);
830 gtk_tree_view_set_search_column(GTK_TREE_VIEW(d->domain_tree_view), DOMAIN_COLUMN_NAME);
831 gtk_container_add(GTK_CONTAINER(scrolled_window), d->domain_tree_view);
833 d->domain_progress_bar = gtk_progress_bar_new();
834 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(d->domain_progress_bar), "Browsing ...");
835 gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(d->domain_progress_bar), 0.1);
836 gtk_box_pack_end(GTK_BOX(vbox2), d->domain_progress_bar, FALSE, FALSE, 0);
838 gtk_dialog_add_button(GTK_DIALOG(d->domain_dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
839 d->domain_ok_button = GTK_WIDGET(gtk_dialog_add_button(GTK_DIALOG(d->domain_dialog), GTK_STOCK_OK, GTK_RESPONSE_OK));
840 gtk_dialog_set_default_response(GTK_DIALOG(d->domain_dialog), GTK_RESPONSE_OK);
841 gtk_widget_set_sensitive(d->domain_ok_button, is_valid_domain_suffix(gtk_entry_get_text(GTK_ENTRY(d->domain_entry))));
843 gtk_widget_grab_default(d->domain_ok_button);
844 gtk_widget_grab_focus(d->domain_entry);
846 gtk_window_set_default_size(GTK_WINDOW(d->domain_dialog), 300, 300);
848 gtk_widget_show_all(vbox);
850 gtk_list_store_append(d->domain_list_store, &iter);
851 gtk_list_store_set(d->domain_list_store, &iter, DOMAIN_COLUMN_NAME, "local", DOMAIN_COLUMN_REF, 1, -1);
852 domain_make_default_selection(d, "local", &iter);
854 d->domain_pulse_timeout = g_timeout_add(100, domain_pulse_callback, d);
856 if (gtk_dialog_run(GTK_DIALOG(d->domain_dialog)) == GTK_RESPONSE_OK)
857 aui_service_dialog_set_domain(d, gtk_entry_get_text(GTK_ENTRY(d->domain_entry)));
859 gtk_widget_destroy(d->domain_dialog);
860 d->domain_dialog = NULL;
862 if (d->domain_pulse_timeout > 0) {
863 g_source_remove(d->domain_pulse_timeout);
864 d->domain_pulse_timeout = 0;
867 avahi_domain_browser_free(d->domain_browser);
868 d->domain_browser = NULL;
872 static void aui_service_dialog_init(AuiServiceDialog *d) {
873 GtkWidget *vbox, *vbox2, *hbox, *scrolled_window;
874 GtkCellRenderer *renderer;
875 GtkTreeViewColumn *column;
876 GtkTreeSelection *selection;
880 d->service_name = NULL;
882 d->browse_service_types = NULL;
883 memset(&d->address, 0, sizeof(d->address));
885 d->resolve_host_name = d->resolve_service = TRUE;
886 d->resolve_host_name_done = d->resolve_service_done = FALSE;
887 d->address_family = AVAHI_PROTO_UNSPEC;
893 d->domain_browser = NULL;
895 d->service_pulse_timeout = 0;
896 d->domain_pulse_timeout = 0;
898 d->common_interface = AVAHI_IF_UNSPEC;
899 d->common_protocol = AVAHI_PROTO_UNSPEC;
901 d->domain_dialog = NULL;
902 d->domain_entry = NULL;
903 d->domain_tree_view = NULL;
904 d->domain_progress_bar = NULL;
905 d->domain_ok_button = NULL;
907 d->service_list_store = d->domain_list_store = NULL;
909 gtk_widget_push_composite_child();
911 gtk_container_set_border_width(GTK_CONTAINER(d), 5);
913 vbox = gtk_vbox_new(FALSE, 8);
914 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
915 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(d)->vbox), vbox, TRUE, TRUE, 0);
917 hbox = gtk_hbox_new(FALSE, 8);
918 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
920 d->domain_label = gtk_label_new("Initializing...");
921 gtk_label_set_ellipsize(GTK_LABEL(d->domain_label), TRUE);
922 gtk_misc_set_alignment(GTK_MISC(d->domain_label), 0, 0.5);
923 gtk_box_pack_start(GTK_BOX(hbox), d->domain_label, TRUE, TRUE, 0);
925 d->domain_button = gtk_button_new_with_mnemonic("Change _domain...");
926 g_signal_connect(d->domain_button, "clicked", G_CALLBACK(domain_button_clicked), d);
927 gtk_box_pack_end(GTK_BOX(hbox), d->domain_button, FALSE, TRUE, 0);
929 vbox2 = gtk_vbox_new(FALSE, 8);
930 gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
932 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
933 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
934 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_ETCHED_IN);
935 gtk_box_pack_start(GTK_BOX(vbox2), scrolled_window, TRUE, TRUE, 0);
937 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);
939 d->service_tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(d->service_list_store));
940 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(d->service_tree_view), FALSE);
941 g_signal_connect(d->service_tree_view, "row-activated", G_CALLBACK(service_row_activated_callback), d);
942 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(d->service_tree_view));
943 gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
944 g_signal_connect(selection, "changed", G_CALLBACK(service_selection_changed_callback), d);
946 renderer = gtk_cell_renderer_text_new();
947 column = gtk_tree_view_column_new_with_attributes("Location", renderer, "text", SERVICE_COLUMN_PRETTY_IFACE, NULL);
948 gtk_tree_view_column_set_visible(column, FALSE);
949 gtk_tree_view_append_column(GTK_TREE_VIEW(d->service_tree_view), column);
951 renderer = gtk_cell_renderer_text_new();
952 column = gtk_tree_view_column_new_with_attributes("Name", renderer, "text", SERVICE_COLUMN_NAME, NULL);
953 gtk_tree_view_column_set_expand(column, TRUE);
954 gtk_tree_view_append_column(GTK_TREE_VIEW(d->service_tree_view), column);
956 renderer = gtk_cell_renderer_text_new();
957 column = gtk_tree_view_column_new_with_attributes("Type", renderer, "text", SERVICE_COLUMN_PRETTY_TYPE, NULL);
958 gtk_tree_view_column_set_visible(column, FALSE);
959 gtk_tree_view_append_column(GTK_TREE_VIEW(d->service_tree_view), column);
961 gtk_tree_view_set_search_column(GTK_TREE_VIEW(d->service_tree_view), SERVICE_COLUMN_NAME);
962 gtk_container_add(GTK_CONTAINER(scrolled_window), d->service_tree_view);
964 d->service_progress_bar = gtk_progress_bar_new();
965 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(d->service_progress_bar), "Browsing ...");
966 gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(d->service_progress_bar), 0.1);
967 gtk_box_pack_end(GTK_BOX(vbox2), d->service_progress_bar, FALSE, FALSE, 0);
969 gtk_dialog_add_button(GTK_DIALOG(d), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
970 d->service_ok_button = GTK_WIDGET(gtk_dialog_add_button(GTK_DIALOG(d), GTK_STOCK_OK, GTK_RESPONSE_OK));
971 gtk_dialog_set_default_response(GTK_DIALOG(d), GTK_RESPONSE_OK);
972 gtk_widget_set_sensitive(d->service_ok_button, FALSE);
974 gtk_widget_grab_default(d->service_ok_button);
975 gtk_widget_grab_focus(d->service_tree_view);
977 gtk_window_set_default_size(GTK_WINDOW(d), 400, 300);
979 gtk_widget_show_all(vbox);
981 gtk_widget_pop_composite_child();
983 d->glib_poll = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT);
985 d->service_pulse_timeout = g_timeout_add(100, service_pulse_callback, d);
986 d->start_idle = g_idle_add(start_callback, d);
988 g_signal_connect(d, "response", G_CALLBACK(response_callback), d);
991 static void restart_browsing(AuiServiceDialog *d) {
992 g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
994 if (d->start_idle <= 0)
995 d->start_idle = g_idle_add(start_callback, d);
998 void aui_service_dialog_set_browse_service_types(AuiServiceDialog *d, const char *type, ...) {
1003 g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1004 g_return_if_fail(type);
1006 g_strfreev(d->browse_service_types);
1008 va_copy(apcopy, ap);
1011 for (u = 1; va_arg(ap, const char *); u++)
1015 d->browse_service_types = g_new0(gchar*, u+1);
1016 d->browse_service_types[0] = g_strdup(type);
1018 va_start(apcopy, type);
1019 for (u = 1; (t = va_arg(apcopy, const char*)); u++)
1020 d->browse_service_types[u] = g_strdup(t);
1023 if (d->browse_service_types[0] && d->browse_service_types[1]) {
1024 /* Multiple service types, enable headers */
1026 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(d->service_tree_view), TRUE);
1027 gtk_tree_view_column_set_visible(gtk_tree_view_get_column(GTK_TREE_VIEW(d->service_tree_view), 2), TRUE);
1030 restart_browsing(d);
1033 void aui_service_dialog_set_browse_service_typesv(AuiServiceDialog *d, const char *const*types) {
1035 g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1036 g_return_if_fail(types);
1037 g_return_if_fail(*types);
1039 g_strfreev(d->browse_service_types);
1040 d->browse_service_types = g_strdupv((char**) types);
1042 if (d->browse_service_types[0] && d->browse_service_types[1]) {
1043 /* Multiple service types, enable headers */
1045 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(d->service_tree_view), TRUE);
1046 gtk_tree_view_column_set_visible(gtk_tree_view_get_column(GTK_TREE_VIEW(d->service_tree_view), 2), TRUE);
1049 restart_browsing(d);
1052 const gchar*const* aui_service_dialog_get_browse_service_types(AuiServiceDialog *d) {
1053 g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1055 return (const char* const*) d->browse_service_types;
1058 void aui_service_dialog_set_domain(AuiServiceDialog *d, const char *domain) {
1059 g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1060 g_return_if_fail(is_valid_domain_suffix(domain));
1063 d->domain = avahi_normalize_name_strdup(domain);
1065 restart_browsing(d);
1068 const char* aui_service_dialog_get_domain(AuiServiceDialog *d) {
1069 g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1074 void aui_service_dialog_set_service_name(AuiServiceDialog *d, const char *name) {
1075 g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1077 g_free(d->service_name);
1078 d->service_name = g_strdup(name);
1081 const char* aui_service_dialog_get_service_name(AuiServiceDialog *d) {
1082 g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1084 return d->service_name;
1087 void aui_service_dialog_set_service_type(AuiServiceDialog *d, const char*stype) {
1088 g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1090 g_free(d->service_type);
1091 d->service_type = g_strdup(stype);
1094 const char* aui_service_dialog_get_service_type(AuiServiceDialog *d) {
1095 g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1097 return d->service_type;
1100 const AvahiAddress* aui_service_dialog_get_address(AuiServiceDialog *d) {
1101 g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1102 g_return_val_if_fail(d->resolve_service_done && d->resolve_host_name_done, NULL);
1107 guint16 aui_service_dialog_get_port(AuiServiceDialog *d) {
1108 g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), 0);
1109 g_return_val_if_fail(d->resolve_service_done, 0);
1114 const char* aui_service_dialog_get_host_name(AuiServiceDialog *d) {
1115 g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1116 g_return_val_if_fail(d->resolve_service_done, NULL);
1118 return d->host_name;
1121 const AvahiStringList *aui_service_dialog_get_txt_data(AuiServiceDialog *d) {
1122 g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), NULL);
1123 g_return_val_if_fail(d->resolve_service_done, NULL);
1128 void aui_service_dialog_set_resolve_service(AuiServiceDialog *d, gboolean resolve) {
1129 g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1131 d->resolve_service = resolve;
1134 gboolean aui_service_dialog_get_resolve_service(AuiServiceDialog *d) {
1135 g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), FALSE);
1137 return d->resolve_service;
1140 void aui_service_dialog_set_resolve_host_name(AuiServiceDialog *d, gboolean resolve) {
1141 g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1143 d->resolve_host_name = resolve;
1146 gboolean aui_service_dialog_get_resolve_host_name(AuiServiceDialog *d) {
1147 g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), FALSE);
1149 return d->resolve_host_name;
1152 void aui_service_dialog_set_address_family(AuiServiceDialog *d, AvahiProtocol proto) {
1153 g_return_if_fail(AUI_IS_SERVICE_DIALOG(d));
1154 g_return_if_fail(proto == AVAHI_PROTO_UNSPEC || proto == AVAHI_PROTO_INET || proto == AVAHI_PROTO_INET6);
1156 d->address_family = proto;
1159 AvahiProtocol aui_service_dialog_get_address_family(AuiServiceDialog *d) {
1160 g_return_val_if_fail(AUI_IS_SERVICE_DIALOG(d), AVAHI_PROTO_UNSPEC);
1162 return d->address_family;