From d524c8e88f558b9f2aebff39b6fbe77eab51e081 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:43:08 +0200 Subject: Adding upstream version 43.0. Signed-off-by: Daniel Baumann --- chooser/gdm-host-chooser-widget.c | 879 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 879 insertions(+) create mode 100644 chooser/gdm-host-chooser-widget.c (limited to 'chooser/gdm-host-chooser-widget.c') diff --git a/chooser/gdm-host-chooser-widget.c b/chooser/gdm-host-chooser-widget.c new file mode 100644 index 0000000..0b67c58 --- /dev/null +++ b/chooser/gdm-host-chooser-widget.c @@ -0,0 +1,879 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 1998, 1999, 2000 Martin K, Petersen + * Copyright (C) 2007 William Jon McCann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_SOCKIO_H +#include +#endif + +#include +#include + +#include +#include +#include +#include + +#include "gdm-address.h" +#include "gdm-chooser-host.h" +#include "gdm-host-chooser-widget.h" + +struct _GdmHostChooserWidget +{ + GtkBox parent; + + GtkWidget *treeview; + + int kind_mask; + + char **hosts; + + XdmcpBuffer broadcast_buf; + XdmcpBuffer query_buf; + gboolean have_ipv6; + int socket_fd; + guint io_watch_id; + guint scan_time_id; + guint ping_try_id; + + int ping_tries; + + GSList *broadcast_addresses; + GSList *query_addresses; + GSList *chooser_hosts; + + GdmChooserHost *current_host; +}; + +enum { + PROP_0, + PROP_KIND_MASK, +}; + +enum { + HOST_ACTIVATED, + LAST_SIGNAL +}; + +static guint signals [LAST_SIGNAL] = { 0, }; + +static void gdm_host_chooser_widget_class_init (GdmHostChooserWidgetClass *klass); +static void gdm_host_chooser_widget_init (GdmHostChooserWidget *host_chooser_widget); + +G_DEFINE_TYPE (GdmHostChooserWidget, gdm_host_chooser_widget, GTK_TYPE_BOX) + +#define GDM_XDMCP_PROTOCOL_VERSION 1001 +#define SCAN_TIMEOUT 30 +#define PING_TIMEOUT 2 +#define PING_TRIES 3 + +enum { + CHOOSER_LIST_ICON_COLUMN = 0, + CHOOSER_LIST_LABEL_COLUMN, + CHOOSER_LIST_HOST_COLUMN +}; + +static void +chooser_host_add (GdmHostChooserWidget *widget, + GdmChooserHost *host) +{ + widget->chooser_hosts = g_slist_prepend (widget->chooser_hosts, host); +} + +#if 0 +static void +chooser_host_remove (GdmHostChooserWidget *widget, + GdmChooserHost *host) +{ + widget->chooser_hosts = g_slist_remove (widget->chooser_hosts, host); +} +#endif + +static gboolean +address_hostnames_equal (GdmAddress *address, + GdmAddress *other_address) +{ + char *hostname, *other_hostname; + gboolean are_equal; + + if (gdm_address_equal (address, other_address)) { + return TRUE; + } + + if (!gdm_address_get_hostname (address, &hostname)) { + gdm_address_get_numeric_info (address, &hostname, NULL); + } + + if (!gdm_address_get_hostname (other_address, &other_hostname)) { + gdm_address_get_numeric_info (other_address, &other_hostname, NULL); + } + + are_equal = g_strcmp0 (hostname, other_hostname) == 0; + + g_free (hostname); + g_free (other_hostname); + + return are_equal; +} + +static GdmChooserHost * +find_known_host (GdmHostChooserWidget *widget, + GdmAddress *address) +{ + GSList *li; + GdmChooserHost *host; + + for (li = widget->chooser_hosts; li != NULL; li = li->next) { + GdmAddress *other_address; + + host = li->data; + + other_address = gdm_chooser_host_get_address (host); + + if (address_hostnames_equal (address, other_address)) { + goto out; + } + } + + host = NULL; + out: + + return host; +} + +static void +browser_add_host (GdmHostChooserWidget *widget, + GdmChooserHost *host) +{ + char *hostname; + char *name; + char *desc; + char *label; + GtkTreeModel *model; + GtkTreeIter iter; + gboolean res; + + GtkTreeSelection *selection; + + g_assert (host != NULL); + + if (! gdm_chooser_host_get_willing (host)) { + gtk_widget_set_sensitive (GTK_WIDGET (widget), TRUE); + return; + } + + res = gdm_address_get_hostname (gdm_chooser_host_get_address (host), &hostname); + if (! res) { + gdm_address_get_numeric_info (gdm_chooser_host_get_address (host), &hostname, NULL); + } + + name = g_markup_escape_text (hostname, -1); + desc = g_markup_escape_text (gdm_chooser_host_get_description (host), -1); + label = g_strdup_printf ("%s\n%s", name, desc); + g_free (name); + g_free (desc); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget->treeview)); + + gtk_list_store_append (GTK_LIST_STORE (model), &iter); + gtk_list_store_set (GTK_LIST_STORE (model), + &iter, + CHOOSER_LIST_ICON_COLUMN, NULL, + CHOOSER_LIST_LABEL_COLUMN, label, + CHOOSER_LIST_HOST_COLUMN, host, + -1); + g_free (label); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget->treeview)); + if (!gtk_tree_selection_get_selected (selection, NULL, NULL)) { + gtk_tree_selection_select_iter (selection, &iter); + } + +} + +static gboolean +decode_packet (GIOChannel *source, + GIOCondition condition, + GdmHostChooserWidget *widget) +{ + struct sockaddr_storage clnt_ss; + GdmAddress *address; + int ss_len; + XdmcpHeader header; + int res; + static XdmcpBuffer buf; + ARRAY8 auth = {0}; + ARRAY8 host = {0}; + ARRAY8 stat = {0}; + char *status; + GdmChooserHost *chooser_host; + + status = NULL; + address = NULL; + + g_debug ("decode_packet: GIOCondition %d", (int)condition); + + if ( ! (condition & G_IO_IN)) { + return TRUE; + } + + ss_len = (int) sizeof (clnt_ss); + + res = XdmcpFill (widget->socket_fd, &buf, (XdmcpNetaddr)&clnt_ss, &ss_len); + if G_UNLIKELY (! res) { + g_debug (_("XDMCP: Could not create XDMCP buffer!")); + return TRUE; + } + + res = XdmcpReadHeader (&buf, &header); + if G_UNLIKELY (! res) { + g_warning (_("XDMCP: Could not read XDMCP header!")); + return TRUE; + } + + if G_UNLIKELY (header.version != XDM_PROTOCOL_VERSION && + header.version != GDM_XDMCP_PROTOCOL_VERSION) { + g_warning (_("XDMCP: Incorrect XDMCP version!")); + return TRUE; + } + + address = gdm_address_new_from_sockaddr ((struct sockaddr *) &clnt_ss, ss_len); + if (address == NULL) { + g_warning (_("XDMCP: Unable to parse address")); + return TRUE; + } + + gdm_address_debug (address); + + if (header.opcode == WILLING) { + if (! XdmcpReadARRAY8 (&buf, &auth)) { + goto done; + } + + if (! XdmcpReadARRAY8 (&buf, &host)) { + goto done; + } + + if (! XdmcpReadARRAY8 (&buf, &stat)) { + goto done; + } + + status = g_strndup ((char *) stat.data, MIN (stat.length, 256)); + } else if (header.opcode == UNWILLING) { + /* immaterial, will not be shown */ + status = NULL; + } else { + goto done; + } + + g_debug ("STATUS: %s", status); + + chooser_host = find_known_host (widget, address); + if (chooser_host == NULL) { + chooser_host = g_object_new (GDM_TYPE_CHOOSER_HOST, + "address", address, + "description", status, + "willing", (header.opcode == WILLING), + "kind", GDM_CHOOSER_HOST_KIND_XDMCP, + NULL); + chooser_host_add (widget, chooser_host); + browser_add_host (widget, chooser_host); + } else { + /* server changed it's mind */ + if (header.opcode == WILLING + && ! gdm_chooser_host_get_willing (chooser_host)) { + browser_add_host (widget, chooser_host); + g_object_set (chooser_host, "willing", TRUE, NULL); + } + /* FIXME: handle unwilling? */ + } + + done: + if (header.opcode == WILLING) { + XdmcpDisposeARRAY8 (&auth); + XdmcpDisposeARRAY8 (&host); + XdmcpDisposeARRAY8 (&stat); + } + + g_free (status); + gdm_address_free (address); + + return TRUE; +} + +static void +do_ping (GdmHostChooserWidget *widget, + gboolean full) +{ + GSList *l; + + g_debug ("do ping full:%d", full); + + for (l = widget->broadcast_addresses; l != NULL; l = l->next) { + GdmAddress *address; + int res; + + address = l->data; + + gdm_address_debug (address); + errno = 0; + g_debug ("fd:%d", widget->socket_fd); + res = XdmcpFlush (widget->socket_fd, + &widget->broadcast_buf, + (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address), + (int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address))); + + if (! res) { + g_warning ("Unable to flush the XDMCP broadcast packet: %s", g_strerror (errno)); + } + } + + if (full) { + for (l = widget->query_addresses; l != NULL; l = l->next) { + GdmAddress *address; + int res; + + address = l->data; + + gdm_address_debug (address); + res = XdmcpFlush (widget->socket_fd, + &widget->query_buf, + (XdmcpNetaddr)gdm_address_peek_sockaddr_storage (address), + (int)gdm_sockaddr_len (gdm_address_peek_sockaddr_storage (address))); + if (! res) { + g_warning ("Unable to flush the XDMCP query packet"); + } + } + } +} + +static gboolean +ping_try (GdmHostChooserWidget *widget) +{ + do_ping (widget, FALSE); + + widget->ping_tries --; + + if (widget->ping_tries <= 0) { + widget->ping_try_id = 0; + return FALSE; + } else { + return TRUE; + } +} + +static void +xdmcp_discover (GdmHostChooserWidget *widget) +{ +#if 0 + gtk_widget_set_sensitive (GTK_WIDGET (manage), FALSE); + gtk_widget_set_sensitive (GTK_WIDGET (rescan), FALSE); + gtk_list_store_clear (GTK_LIST_STORE (browser_model)); + gtk_widget_set_sensitive (GTK_WIDGET (browser), FALSE); + gtk_label_set_label (GTK_LABEL (status_label), + _(scanning_message)); + + while (hl) { + gdm_chooser_host_dispose ((GdmChooserHost *) hl->data); + hl = hl->next; + } + + g_list_free (chooser_hosts); + chooser_hosts = NULL; +#endif + + do_ping (widget, TRUE); + +#if 0 + if (widget->scan_time_id > 0) { + g_source_remove (widget->scan_time_id); + } + + widget->scan_time_id = g_timeout_add_seconds (SCAN_TIMEOUT, + chooser_scan_time_update, + widget); +#endif + /* Note we already used up one try */ + widget->ping_tries = PING_TRIES - 1; + if (widget->ping_try_id > 0) { + g_source_remove (widget->ping_try_id); + } + + widget->ping_try_id = g_timeout_add_seconds (PING_TIMEOUT, + (GSourceFunc)ping_try, + widget); +} + +/* Find broadcast address for all active, non pointopoint interfaces */ +static void +find_broadcast_addresses (GdmHostChooserWidget *widget) +{ + int i; + int num; + int sock; + struct ifconf ifc; + char *buf; + struct ifreq *ifr; + + g_debug ("Finding broadcast addresses"); + + sock = socket (AF_INET, SOCK_DGRAM, 0); +#ifdef SIOCGIFNUM + if (ioctl (sock, SIOCGIFNUM, &num) < 0) { + num = 64; + } +#else + num = 64; +#endif + + ifc.ifc_len = sizeof (struct ifreq) * num; + ifc.ifc_buf = buf = g_malloc0 (ifc.ifc_len); + if (ioctl (sock, SIOCGIFCONF, &ifc) < 0) { + g_warning ("Could not get local addresses!"); + goto out; + } + + ifr = ifc.ifc_req; + num = ifc.ifc_len / sizeof (struct ifreq); + for (i = 0 ; i < num ; i++) { + const char *name; + + name = ifr[i].ifr_name; + g_debug ("Checking if %s", name); + if (name != NULL && name[0] != '\0') { + struct ifreq ifreq; + GdmAddress *address; + struct sockaddr_in sin; + + memset (&ifreq, 0, sizeof (ifreq)); + + strncpy (ifreq.ifr_name, + ifr[i].ifr_name, + sizeof (ifreq.ifr_name)); + /* paranoia */ + ifreq.ifr_name[sizeof (ifreq.ifr_name) - 1] = '\0'; + + if ((ioctl (sock, SIOCGIFFLAGS, &ifreq) < 0) && (errno != ENXIO)) { + g_warning ("Could not get SIOCGIFFLAGS for %s", ifr[i].ifr_name); + } + + if ((ifreq.ifr_flags & IFF_UP) == 0 || + (ifreq.ifr_flags & IFF_BROADCAST) == 0 || + ioctl (sock, SIOCGIFBRDADDR, &ifreq) < 0) { + g_debug ("Skipping if %s", name); + continue; + } + + g_memmove (&sin, &ifreq.ifr_broadaddr, sizeof (struct sockaddr_in)); + sin.sin_port = htons (XDM_UDP_PORT); + address = gdm_address_new_from_sockaddr ((struct sockaddr *) &sin, sizeof (sin)); + if (address != NULL) { + g_debug ("Adding if %s", name); + gdm_address_debug (address); + + widget->broadcast_addresses = g_slist_append (widget->broadcast_addresses, address); + } + } + } + out: + g_free (buf); + close (sock); +} + +static void +add_hosts (GdmHostChooserWidget *widget) +{ + int i; + + for (i = 0; widget->hosts != NULL && widget->hosts[i] != NULL; i++) { + struct addrinfo hints; + struct addrinfo *result; + struct addrinfo *ai; + int gaierr; + const char *name; + char serv_buf [NI_MAXSERV]; + const char *serv; + + name = widget->hosts[i]; + + if (strcmp (name, "BROADCAST") == 0) { + find_broadcast_addresses (widget); + continue; + } + + if (strcmp (name, "MULTICAST") == 0) { + /*gdm_chooser_find_mcaddr ();*/ + continue; + } + + result = NULL; + memset (&hints, 0, sizeof (hints)); + hints.ai_socktype = SOCK_STREAM; + + snprintf (serv_buf, sizeof (serv_buf), "%u", XDM_UDP_PORT); + serv = serv_buf; + + gaierr = getaddrinfo (name, serv, &hints, &result); + if (gaierr != 0) { + g_warning ("Unable to get address info for name %s: %s", name, gai_strerror (gaierr)); + continue; + } + + for (ai = result; ai != NULL; ai = ai->ai_next) { + GdmAddress *address; + + address = gdm_address_new_from_sockaddr (ai->ai_addr, ai->ai_addrlen); + if (address != NULL) { + widget->query_addresses = g_slist_append (widget->query_addresses, address); + } + } + + freeaddrinfo (result); + } + + if (widget->broadcast_addresses == NULL && widget->query_addresses == NULL) { + find_broadcast_addresses (widget); + } +} + +static void +xdmcp_init (GdmHostChooserWidget *widget) +{ + XdmcpHeader header; + int sockopts; + int res; + GIOChannel *ioc; + ARRAYofARRAY8 aanames; + + sockopts = 1; + + widget->socket_fd = -1; + + /* Open socket for communication */ +#ifdef ENABLE_IPV6 + widget->socket_fd = socket (AF_INET6, SOCK_DGRAM, 0); + if (widget->socket_fd != -1) { + widget->have_ipv6 = TRUE; +#ifdef IPV6_V6ONLY + { + int zero = 0; + if (setsockopt(widget->socket_fd, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) < 0) + g_warning("setsockopt(IPV6_V6ONLY): %s", g_strerror(errno)); + } +#endif + } +#endif + if (! widget->have_ipv6) { + widget->socket_fd = socket (AF_INET, SOCK_DGRAM, 0); + if (widget->socket_fd == -1) { + g_critical ("Could not create socket!"); + } + } + + res = setsockopt (widget->socket_fd, + SOL_SOCKET, + SO_BROADCAST, + (char *) &sockopts, + sizeof (sockopts)); + if (res < 0) { + g_critical ("Could not set socket options!"); + } + + /* Assemble XDMCP BROADCAST_QUERY packet in static buffer */ + memset (&header, 0, sizeof (XdmcpHeader)); + header.opcode = (CARD16) BROADCAST_QUERY; + header.length = 1; + header.version = XDM_PROTOCOL_VERSION; + aanames.length = 0; + XdmcpWriteHeader (&widget->broadcast_buf, &header); + XdmcpWriteARRAYofARRAY8 (&widget->broadcast_buf, &aanames); + + /* Assemble XDMCP QUERY packet in static buffer */ + memset (&header, 0, sizeof (XdmcpHeader)); + header.opcode = (CARD16) QUERY; + header.length = 1; + header.version = XDM_PROTOCOL_VERSION; + memset (&widget->query_buf, 0, sizeof (XdmcpBuffer)); + XdmcpWriteHeader (&widget->query_buf, &header); + XdmcpWriteARRAYofARRAY8 (&widget->query_buf, &aanames); + + add_hosts (widget); + + ioc = g_io_channel_unix_new (widget->socket_fd); + g_io_channel_set_encoding (ioc, NULL, NULL); + g_io_channel_set_buffered (ioc, FALSE); + widget->io_watch_id = g_io_add_watch (ioc, + G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc)decode_packet, + widget); + g_io_channel_unref (ioc); +} + +void +gdm_host_chooser_widget_refresh (GdmHostChooserWidget *widget) +{ + g_return_if_fail (GDM_IS_HOST_CHOOSER_WIDGET (widget)); + + xdmcp_discover (widget); +} + +GdmChooserHost * +gdm_host_chooser_widget_get_host (GdmHostChooserWidget *widget) +{ + GdmChooserHost *host; + + g_return_val_if_fail (GDM_IS_HOST_CHOOSER_WIDGET (widget), NULL); + + host = NULL; + if (widget->current_host != NULL) { + host = g_object_ref (widget->current_host); + } + + return host; +} + +static void +_gdm_host_chooser_widget_set_kind_mask (GdmHostChooserWidget *widget, + int kind_mask) +{ + if (widget->kind_mask != kind_mask) { + widget->kind_mask = kind_mask; + } +} + +static void +gdm_host_chooser_widget_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdmHostChooserWidget *self; + + self = GDM_HOST_CHOOSER_WIDGET (object); + + switch (prop_id) { + case PROP_KIND_MASK: + _gdm_host_chooser_widget_set_kind_mask (self, g_value_get_int (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdm_host_chooser_widget_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GObject * +gdm_host_chooser_widget_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GdmHostChooserWidget *widget; + + widget = GDM_HOST_CHOOSER_WIDGET (G_OBJECT_CLASS (gdm_host_chooser_widget_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + + xdmcp_init (widget); + xdmcp_discover (widget); + + return G_OBJECT (widget); +} + +static void +gdm_host_chooser_widget_dispose (GObject *object) +{ + GdmHostChooserWidget *widget; + + widget = GDM_HOST_CHOOSER_WIDGET (object); + + g_debug ("Disposing host_chooser_widget"); + + if (widget->broadcast_addresses != NULL) { + g_slist_foreach (widget->broadcast_addresses, + (GFunc)gdm_address_free, + NULL); + g_slist_free (widget->broadcast_addresses); + widget->broadcast_addresses = NULL; + } + if (widget->query_addresses != NULL) { + g_slist_foreach (widget->query_addresses, + (GFunc)gdm_address_free, + NULL); + g_slist_free (widget->query_addresses); + widget->query_addresses = NULL; + } + if (widget->chooser_hosts != NULL) { + g_slist_foreach (widget->chooser_hosts, + (GFunc)g_object_unref, + NULL); + g_slist_free (widget->chooser_hosts); + widget->chooser_hosts = NULL; + } + + widget->current_host = NULL; + + G_OBJECT_CLASS (gdm_host_chooser_widget_parent_class)->dispose (object); +} + +static void +gdm_host_chooser_widget_class_init (GdmHostChooserWidgetClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = gdm_host_chooser_widget_get_property; + object_class->set_property = gdm_host_chooser_widget_set_property; + object_class->constructor = gdm_host_chooser_widget_constructor; + object_class->dispose = gdm_host_chooser_widget_dispose; + + g_object_class_install_property (object_class, + PROP_KIND_MASK, + g_param_spec_int ("kind-mask", + "kind mask", + "kind mask", + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + + signals [HOST_ACTIVATED] = g_signal_new ("host-activated", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); +} + +static void +on_host_selected (GtkTreeSelection *selection, + GdmHostChooserWidget *widget) +{ + GtkTreeModel *model = NULL; + GtkTreeIter iter = {0}; + GdmChooserHost *curhost; + + curhost = NULL; + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + gtk_tree_model_get (model, &iter, CHOOSER_LIST_HOST_COLUMN, &curhost, -1); + } + + widget->current_host = curhost; +} + +static void +on_row_activated (GtkTreeView *tree_view, + GtkTreePath *tree_path, + GtkTreeViewColumn *tree_column, + GdmHostChooserWidget *widget) +{ + g_signal_emit (widget, signals[HOST_ACTIVATED], 0); +} + +static void +gdm_host_chooser_widget_init (GdmHostChooserWidget *widget) +{ + GtkWidget *scrolled; + GtkTreeSelection *selection; + GtkTreeViewColumn *column; + GtkTreeModel *model; + + scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), + GTK_SHADOW_IN); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_box_pack_start (GTK_BOX (widget), scrolled, TRUE, TRUE, 0); + + widget->treeview = gtk_tree_view_new (); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (widget->treeview), FALSE); + g_signal_connect (widget->treeview, + "row-activated", + G_CALLBACK (on_row_activated), + widget); + gtk_container_add (GTK_CONTAINER (scrolled), widget->treeview); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget->treeview)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE); + g_signal_connect (selection, "changed", + G_CALLBACK (on_host_selected), + widget); + + model = (GtkTreeModel *)gtk_list_store_new (3, + GDK_TYPE_PIXBUF, + G_TYPE_STRING, + G_TYPE_POINTER); + gtk_tree_view_set_model (GTK_TREE_VIEW (widget->treeview), model); + + column = gtk_tree_view_column_new_with_attributes ("Icon", + gtk_cell_renderer_pixbuf_new (), + "pixbuf", CHOOSER_LIST_ICON_COLUMN, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (widget->treeview), column); + + column = gtk_tree_view_column_new_with_attributes ("Hostname", + gtk_cell_renderer_text_new (), + "markup", CHOOSER_LIST_LABEL_COLUMN, + NULL); + gtk_tree_view_append_column (GTK_TREE_VIEW (widget->treeview), column); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), + CHOOSER_LIST_LABEL_COLUMN, + GTK_SORT_ASCENDING); +} + +GtkWidget * +gdm_host_chooser_widget_new (int kind_mask) +{ + GObject *object; + + object = g_object_new (GDM_TYPE_HOST_CHOOSER_WIDGET, + "orientation", GTK_ORIENTATION_VERTICAL, + "kind-mask", kind_mask, + NULL); + + return GTK_WIDGET (object); +} -- cgit v1.2.3