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