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