/*
** wifimgr-gtk3.c
*/

/*
** Copyright (c) 2009, J.R. Oldroyd, Open Advisors Limited
** All rights reserved.
** 
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
**     * Redistributions of source code must retain the above copyright
**       notice, this list of conditions and the following disclaimer.
**     * Redistributions in binary form must reproduce the above copyright
**       notice, this list of conditions and the following disclaimer in the
**       documentation and/or other materials provided with the distribution.
**     * Neither the name of the author, the author's organization nor the
**       names of its contributors may be used to endorse or promote products
**       derived from this software without specific prior written permission.
** 
** THIS SOFTWARE IS PROVIDED BY OPEN ADVISORS LIMITED ''AS IS'' AND ANY
** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL OPEN ADVISORS LIMITED BE LIABLE FOR ANY
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/*
** $Id$
*/

#include "wifimgr.h"
#include "version.h"
#include <stdlib.h>
#include <gtk/gtk.h>
#include <string.h>

#ifndef WITHOUT_NLS
#include <libintl.h>
#else
#define gettext(x)	(x)
#endif

#define	ALIGN_LEFT	0
#define	ALIGN_CENTER	0.5
#define	ALIGN_RIGHT	1
#define	ALIGN_TOP	0
#define	ALIGN_MIDDLE	0.5
#define	ALIGN_BOTTOM	1

char			gui_response[1024];
int			gui_changes;
GtkWidget *		gui_up_down_icon;
struct wifi_net *	gui_new_net;
GtkWidget * 		gui_net_grid;
int			gui_show_all_networks = 0;
int			gui_networks_order = ORDER_BY_SSID;
char *			gui_any_bssid = "-- : -- : -- : -- : -- : --";
char *			gui_any_chan = " --";
int			wifi_if_status;

static GtkWidget *	gui_fill_network_grid(GtkWidget *, gpointer *);

/*
** initialize GUI
*/
int
gui_init(int * ac, char *** av) {

	return gtk_init_check(ac, av);
}

/*
** handle check_button input by user
*/
static void
gui_process_check_button(GtkWidget * w, gpointer * gp) {

	*((int *) gp) = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));
	gui_changes++;
}

/*
** handle check_button for disabled network input by user
*/
static void
gui_process_check_button_bssid_disabled(GtkWidget * w, gpointer * gp) {
	struct wifi_net *	net;
	struct wifi_net *	n;
	int			anyflag;
	int			disabled;

	net = (struct wifi_net *) gp;
	anyflag = net->wn_any_bssid;
	disabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));

	/* use inconsistent state as the [-] symbol is more intuitive than a check mark */
	if (disabled)
		gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(w), TRUE);

	if (!anyflag) {
		net->sup_disabled = disabled;
		gtk_widget_set_sensitive(GTK_WIDGET(net->w_sup_ssid), !disabled);
		gtk_widget_set_sensitive(GTK_WIDGET(net->w_sup_bssid), !disabled);
		gtk_widget_set_sensitive(GTK_WIDGET(net->w_wn_security), !disabled);
		gtk_widget_set_sensitive(GTK_WIDGET(net->w_wn_key), !disabled);
		gtk_widget_set_sensitive(GTK_WIDGET(net->w_sup_priority), !disabled);
		gtk_widget_set_sensitive(GTK_WIDGET(net->w_wn_chan), !disabled);
		gtk_widget_set_sensitive(GTK_WIDGET(net->w_wn_rate), !disabled);
		gtk_widget_set_sensitive(GTK_WIDGET(net->w_wn_bars), !disabled);
	}
	else {
		/*
		** find all matching SSIDs and set their flags
		*/
		for (n = nets; n; n = n->wn_next) {
			if (strcmp(n->sup_ssid, net->sup_ssid) == 0) {
				n->sup_disabled = disabled;
				gtk_widget_set_sensitive(GTK_WIDGET(n->w_sup_ssid), !disabled);
				gtk_widget_set_sensitive(GTK_WIDGET(n->w_sup_bssid), !disabled);
				gtk_widget_set_sensitive(GTK_WIDGET(n->w_wn_security), !disabled);
				gtk_widget_set_sensitive(GTK_WIDGET(n->w_wn_key), !disabled);
				gtk_widget_set_sensitive(GTK_WIDGET(n->w_sup_priority), !disabled);
				gtk_widget_set_sensitive(GTK_WIDGET(n->w_wn_chan), !disabled);
				gtk_widget_set_sensitive(GTK_WIDGET(n->w_wn_rate), !disabled);
				gtk_widget_set_sensitive(GTK_WIDGET(n->w_wn_bars), !disabled);
			}
		}
	}

	gui_changes++;
}

/*
** handle check_button any bssid input by user
*/
static void
gui_process_check_button_any_bssid(GtkWidget * w, gpointer * gp) {
	struct wifi_net *	net;
	struct wifi_net *	n;
	int			anyflag;

	net = (struct wifi_net *) gp;
	anyflag = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));

	/*
	** find all matching SSIDs and de-display their BSSIDs and set their checkboxes
	** set their flags to either all 0 or ascending 1,2,3,...; networks with flags <= 1 are displayed in gui_fill_network_grid()
	*/
	for (n = nets; n; n = n->wn_next) {
		if (strcmp(n->sup_ssid, net->sup_ssid) == 0) {
			n->wn_any_bssid = anyflag;
			if (anyflag) {
				n->sup_disabled = 0;
				if (n->w_sup_bssid)
					gtk_label_set_text(GTK_LABEL(n->w_sup_bssid), gui_any_bssid);
				if (n->w_wn_chan)
					gtk_label_set_text(GTK_LABEL(n->w_wn_chan), gui_any_chan);
				anyflag++;
			}
			else {
				char			buf[256];

				sprintf(buf, "%d", n->wn_chan);
				if (n->w_sup_bssid)
					gtk_label_set_text(GTK_LABEL(n->w_sup_bssid), n->sup_bssid);
				if (n->w_wn_chan)
					gtk_label_set_text(GTK_LABEL(n->w_wn_chan), buf);
			}
		}
	}

	/* redraw grid */
	gui_fill_network_grid(NULL, (gpointer *) &gui_net_grid);

	gui_changes++;
}

/*
** handle check_button for show all networks input by user
*/
static void
gui_process_check_button_show_all_networks(GtkWidget * w, gpointer * gp) {

	gui_show_all_networks = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));
	gui_fill_network_grid(NULL, (gpointer *) &gui_net_grid);
}

/*
** handle check_button for show password input by user
*/
static void
gui_process_check_button_showpass(GtkWidget * w, gpointer * gp) {
	struct wifi_net *	net;

	net = (struct wifi_net *) gp;
	net->wn_show_password = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));
	if (net->w_wn_key)
		gtk_entry_set_visibility(GTK_ENTRY(net->w_wn_key), net->wn_show_password);
	if (net->w_sup_password)
		gtk_entry_set_visibility(GTK_ENTRY(net->w_sup_password), net->wn_show_password);
	if (net->w_sup_private_key_passwd)
		gtk_entry_set_visibility(GTK_ENTRY(net->w_sup_private_key_passwd), net->wn_show_password);
	if (net->w_sup_private_key2_passwd)
		gtk_entry_set_visibility(GTK_ENTRY(net->w_sup_private_key2_passwd), net->wn_show_password);
}

/*
** order the networks depending on gui_networks_order value
*/
static void
gui_order_networks() {

	switch (gui_networks_order) {
	case ORDER_BY_SSID:
		nl_order_by_ssid(&nets);
		break;
	case ORDER_BY_BARS:
		nl_order_by_bars(&nets);
		break;
	case ORDER_BY_CHANNEL:
		nl_order_by_channel(&nets);
		break;
	}
}

/*
** handle order by...
*/
static void
gui_process_dropdown_order_by(GtkWidget * w, gpointer * gp) {

	gui_networks_order = gtk_combo_box_get_active(GTK_COMBO_BOX(w));
	gui_order_networks();
	gui_fill_network_grid(NULL, (gpointer *) &gui_net_grid);
}

/*
** handle spin button input by user
*/
static void
gui_process_spin_button(GtkWidget * w, gpointer * gp) {

	*((int *) gp) = (int) gtk_spin_button_get_value(GTK_SPIN_BUTTON(w));
	gui_changes++;
}

/*
** handle text input by user
*/
static void
gui_process_text_input(GtkWidget * w, gpointer * gp) {

	strcpy((char *) gp, (char *) gtk_entry_get_text(GTK_ENTRY(w)));
	gui_changes++;
}

/*
** handle filename selection by user
*/
static void
gui_process_file_selection(GtkWidget * w, gpointer * gp) {
	char *			filename;

	filename = (char *) gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(w));
	if (filename && strcmp((char *) gp, filename) != 0) {
		strcpy((char *) gp, filename);
		g_free(filename);
		gui_changes++;
	}
}

/*
** handle combo_box selection by user
*/
static void
gui_process_combo_box_selection(GtkWidget * w, gpointer * gp) {
	char *			text;

	text = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(w));
	if (text) {
		if (strcmp(text, gettext("(Unset)")) == 0)
			*((char *) gp) = '\0';
		else
			strcpy((char *) gp, text);
		g_free(text);
		gui_changes++;
	}
}

/*
** handle combo_box security type selection by user
*/
static void
gui_process_security_type_selection(GtkWidget * w, gpointer * gp) {
	char *			text;
	struct wifi_net *	net;

	net = (struct wifi_net *) gp;

	text = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(w));
	if (text) {
		if (strcmp(text, gettext("Open")) == 0) {
			net->wn_security = WN_SEC_NONE;
			net->wn_km = WN_KM_NONE;
		}
		if (strcmp(text, gettext("WEP")) == 0) {
			net->wn_security = WN_SEC_WEP;
			net->wn_km = WN_KM_NONE;
		}
		if (strcmp(text, gettext("WPA PSK")) == 0) {
			net->wn_security = WN_SEC_WPA;
			net->wn_km = WN_KM_PSK;
		}
		if (strcmp(text, gettext("RSN PSK")) == 0) {
			net->wn_security = WN_SEC_RSN;
			net->wn_km = WN_KM_PSK;
		}
		if (strcmp(text, gettext("WPA EAP")) == 0) {
			net->wn_security = WN_SEC_WPA;
			net->wn_km = WN_KM_EAP;
		}
		if (strcmp(text, gettext("RSN EAP")) == 0) {
			net->wn_security = WN_SEC_RSN;
			net->wn_km = WN_KM_EAP;
		}
		g_free(text);
		gui_changes++;
	}
}

/*
** display GUI message dialog with OK button
*/
int
gui_message(const char * s, int msg_type) {
	GtkWidget *		dialog;
	GtkWidget *		vbox;
	GtkWidget *		hbox;
	GtkWidget *		icon;
	GtkWidget *		label;
	GtkWidget *		entry;
	GtkButtonsType		buttons;
	int			resp;
	char			iconname[64];

	switch(msg_type) {
	case MSG_ERROR:
		strcpy(iconname, "dialog-error");
		buttons = GTK_BUTTONS_CLOSE;
		break;
	case MSG_INFO:
		strcpy(iconname, "dialog-information");
		buttons = GTK_BUTTONS_OK;
		break;
	case MSG_INPUT:
		strcpy(iconname, "dialog-question");
		buttons = GTK_BUTTONS_OK;
		break;
	case MSG_QUESTION:
		strcpy(iconname, "dialog-question");
		buttons = GTK_BUTTONS_YES_NO;
		break;
	case MSG_WARNING:
		strcpy(iconname, "dialog-warning");
		buttons = GTK_BUTTONS_OK;
		break;
	}
	dialog = gtk_dialog_new();
	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
	icon = gtk_image_new_from_icon_name(iconname, GTK_ICON_SIZE_DIALOG);
	label = gtk_label_new(NULL);
	gtk_label_set_markup(GTK_LABEL(label), s);
	gtk_box_pack_start(GTK_BOX(hbox), icon, TRUE, TRUE, 0);
	gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
	gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
	if (msg_type == MSG_INPUT) {
		entry = gtk_entry_new();
		gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
		gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE); /* allow CR to end input */
		g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(gui_process_text_input),
		    (gpointer) gui_response);
		gtk_box_pack_start(GTK_BOX(vbox), entry, TRUE, TRUE, 0);
	}
	switch (buttons) {
	case GTK_BUTTONS_CLOSE:
		gtk_dialog_add_button(GTK_DIALOG(dialog), "Close", GTK_RESPONSE_CLOSE);
		gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE);
		break;
	case GTK_BUTTONS_OK:
		gtk_dialog_add_button(GTK_DIALOG(dialog), "Ok", GTK_RESPONSE_OK);
		gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
		break;
	case GTK_BUTTONS_YES_NO:
		gtk_dialog_add_button(GTK_DIALOG(dialog), "Yes", GTK_RESPONSE_YES);
		gtk_dialog_add_button(GTK_DIALOG(dialog), "No", GTK_RESPONSE_NO);
		gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_NO);
		break;
	case GTK_BUTTONS_NONE:
	default:
		break;
	}
	gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), vbox);
	gtk_widget_show_all(dialog);
	gtk_window_set_title(GTK_WINDOW(dialog), gettext("WiFi Networks Manager"));

	resp = gtk_dialog_run(GTK_DIALOG(dialog));
	gtk_widget_destroy(dialog);

	switch(resp) {
	case GTK_RESPONSE_OK:	return MSG_RESPONSE_OK;
	case GTK_RESPONSE_YES:	return MSG_RESPONSE_YES;
	case GTK_RESPONSE_NO:	return MSG_RESPONSE_NO;
	default:		return MSG_RESPONSE_FAIL;
	}
}

/*
** edit entry for new network
*/
static void
gui_edit_new_network_done(GtkWidget * w, gpointer * gp) {

	gtk_widget_destroy((GtkWidget *) gp);

	/* insert new network */
	if (*gui_new_net->sup_ssid && gui_new_net->wn_security != WN_SEC_UNKNOWN) {
		gui_new_net->wn_enabled = 1;

		/* use directed probe request for cloaked networks */
		gui_new_net->sup_scan_ssid = 1;

		/* mark as not associated for now */
		gui_new_net->wn_assoc_ifn = -1;

		if (!nl_insert(&nets, gui_new_net)) {
			char            buf[256];

			sprintf(buf, gettext("Error adding SSID <b>%s</b>"), gui_new_net->sup_ssid);
			gui_message(buf, MSG_ERROR);
			return;
		}

		/* order the freshly added entry */
		gui_order_networks();

		/* refresh main display */
		gui_fill_network_grid(NULL, (gpointer *) &gui_net_grid);
	}
}

static void
gui_edit_new_network(GtkWidget * x, gpointer * gp) {
	GtkWidget *		window;
	GtkWidget *		vbox;
	GtkWidget *		grid_scrolled_window;
	GtkWidget *		bottom_buttons;
	GtkWidget *		grid;
	GtkWidget *		w;
	int			row;
	int			col;
	char			buf[256];

	if ((gui_new_net = (struct wifi_net *) malloc(sizeof(struct wifi_net))) == NULL) {
		char		buf[256];
		sprintf(buf, gettext("Out of memory."));
		gui_message(buf, MSG_ERROR);
		return;
	}
	memset(gui_new_net, 0, sizeof(*gui_new_net));

	gui_new_net->wn_enabled = 0;
	gui_new_net->wn_next = NULL;

	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_window_set_destroy_with_parent(GTK_WINDOW(window), TRUE);
	gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
	gtk_window_set_default_size(GTK_WINDOW(window), 400, 142);
	gtk_window_set_title(GTK_WINDOW(window), gettext("WiFi Networks Manager"));
	gtk_container_set_border_width(GTK_CONTAINER(window), 5);
	g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(gtk_widget_destroy),
	    NULL);

	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);

	grid_scrolled_window = gtk_scrolled_window_new(NULL, NULL);
	gtk_container_set_border_width(GTK_CONTAINER(grid_scrolled_window), 5);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(grid_scrolled_window),
	    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

	grid = gtk_grid_new();
	gtk_grid_set_row_spacing(GTK_GRID(grid), 5);
	gtk_grid_set_column_spacing(GTK_GRID(grid), 15);

	row = 0; col = 0;

	sprintf(buf, "<span size=\"large\"><b>%s</b></span>", gettext("Add Cloaked Network"));
	w = gtk_label_new(buf);
	gtk_label_set_use_markup(GTK_LABEL(w), TRUE);
	gtk_box_pack_start(GTK_BOX(vbox), w, FALSE, FALSE, 0);

	w = gtk_label_new(gettext("SSID:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_entry_new();
	gtk_entry_set_text(GTK_ENTRY(w), gui_new_net->sup_ssid);
	g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(gui_process_text_input),
	    (gpointer) gui_new_net->sup_ssid);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("Security Type:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_combo_box_text_new();
	gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(w), NULL, gettext("Open"));
	gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(w), NULL, gettext("WEP"));
	gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(w), NULL, gettext("WPA PSK"));
	gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(w), NULL, gettext("RSN PSK"));
	gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(w), NULL, gettext("WPA EAP"));
	gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(w), NULL, gettext("RSN EAP"));
	gtk_combo_box_set_active(GTK_COMBO_BOX(w), 0);
	g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(gui_process_security_type_selection),
	    (gpointer) gui_new_net);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	gtk_container_add(GTK_CONTAINER(grid_scrolled_window), grid);
	gtk_box_pack_start(GTK_BOX(vbox), grid_scrolled_window, TRUE, TRUE, 0);

	bottom_buttons = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
	w = gtk_button_new_with_label(gettext("Done"));
	g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(gui_edit_new_network_done),
	    (gpointer) window);
	gtk_box_pack_end(GTK_BOX(bottom_buttons), w, FALSE, FALSE, 5);
	gtk_box_pack_start(GTK_BOX(vbox), bottom_buttons, FALSE, FALSE, 0);

	gtk_container_add(GTK_CONTAINER(window), vbox);

	gtk_widget_show_all(window);
}

/*
** extended EAP fields editor
*/
static void
gui_edit_eap_done(GtkWidget * w, gpointer * gp) {

	gtk_widget_destroy((GtkWidget *) gp);
}

static void
gui_edit_eap(GtkWidget * x, gpointer * gp) {
	GtkWidget *		window;
	GtkWidget *		vbox;
	GtkWidget *		show_pass_box;
	GtkWidget *		grid_scrolled_window;
	GtkWidget *		grid;
	GtkWidget *		bottom_buttons;
	GtkWidget *		w;
	int			row;
	int			col;
	char			buf[256];
	int			i;
	int			j;
	struct wifi_net *	net;

	net = (struct wifi_net *) gp;

	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_window_set_destroy_with_parent(GTK_WINDOW(window), TRUE);
	gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
	gtk_window_set_default_size(GTK_WINDOW(window), 400, 600);
	gtk_window_set_title(GTK_WINDOW(window), gettext("WiFi Networks Manager"));
	gtk_container_set_border_width(GTK_CONTAINER(window), 5);
	g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(gtk_widget_destroy),
	    NULL);

	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);

	sprintf(buf, "<span size=\"large\"><b>%s</b></span>", gettext("Edit EAP Parameters"));
	w = gtk_label_new(buf);
	gtk_label_set_use_markup(GTK_LABEL(w), TRUE);
	gtk_box_pack_start(GTK_BOX(vbox), w, FALSE, FALSE, 0);

	sprintf(buf, "<b>%s</b> %s (%s)", gettext("Network:"), net->sup_ssid, net->sup_bssid);
	w = gtk_label_new(buf);
	gtk_label_set_use_markup(GTK_LABEL(w), TRUE);
	gtk_box_pack_start(GTK_BOX(vbox), w, FALSE, FALSE, 0);

	show_pass_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);

	w = gtk_check_button_new();
	if (net->wn_show_password)
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
	g_signal_connect(G_OBJECT(w), "toggled", G_CALLBACK(gui_process_check_button_showpass),
	    (gpointer) net);
	gtk_box_pack_end(GTK_BOX(show_pass_box), w, FALSE, FALSE, 5);

	w = gtk_image_new_from_file(ICON_PATH "/eye.png");
	gtk_box_pack_end(GTK_BOX(show_pass_box), w, FALSE, FALSE, 5);

	gtk_box_pack_start(GTK_BOX(vbox), show_pass_box, FALSE, FALSE, 0);

	grid_scrolled_window = gtk_scrolled_window_new(NULL, NULL);
	gtk_container_set_border_width(GTK_CONTAINER(grid_scrolled_window), 5);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(grid_scrolled_window),
	    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

	grid = gtk_grid_new();
	gtk_grid_set_row_spacing(GTK_GRID(grid), 5);
	gtk_grid_set_column_spacing(GTK_GRID(grid), 15);

	row = 0; col = 0;

	w = gtk_label_new(gettext("<b>EAP Parameter</b>"));
	gtk_label_set_use_markup(GTK_LABEL(w), TRUE);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_label_new(gettext("<b>Value</b>"));
	gtk_label_set_use_markup(GTK_LABEL(w), TRUE);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	if (net->wn_km & WN_KM_PSK) {
		w = gtk_label_new(gettext("PSK:"));
		gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
		gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
		col++;

		w = gtk_entry_new();
		gtk_entry_set_text(GTK_ENTRY(w), net->wn_key);
		g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(gui_process_text_input),
		    (gpointer) net->wn_key);
		gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
		col++;

		row++; col = 0;
	}

	w = gtk_label_new(gettext("EAP:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_combo_box_text_new();
	gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(w), NULL, gettext("(Unset)"));
	j = 1;
	for (i = 0; conf_eap[i].cl_name; i++)
		if (conf_eap[i].cl_value > 0) {
			gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(w), NULL, conf_eap[i].cl_name);
			if (strstr(net->sup_eap, conf_eap[i].cl_name))
				gtk_combo_box_set_active(GTK_COMBO_BOX(w), j);
			j++;
		}
	g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(gui_process_combo_box_selection),
	    (gpointer) net->sup_eap);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("Pairwise:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_combo_box_text_new();
	gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(w), NULL, gettext("(Unset)"));
	j = 1;
	for (i = 0; conf_pairwise[i].cl_name; i++)
		if (conf_pairwise[i].cl_value > 0) {
			gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(w), NULL, conf_pairwise[i].cl_name);
			if (strstr(net->sup_pairwise, conf_pairwise[i].cl_name))
				gtk_combo_box_set_active(GTK_COMBO_BOX(w), j);
			j++;
		}
	g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(gui_process_combo_box_selection),
	    (gpointer) net->sup_pairwise);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("Group:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_combo_box_text_new();
	gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(w), NULL, gettext("(Unset)"));
	j = 1;
	for (i = 0; conf_group[i].cl_name; i++)
		if (conf_group[i].cl_value > 0) {
			gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(w), NULL, conf_group[i].cl_name);
			if (strstr(net->sup_group, conf_group[i].cl_name))
				gtk_combo_box_set_active(GTK_COMBO_BOX(w), j);
			j++;
		}
	g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(gui_process_combo_box_selection),
	    (gpointer) net->sup_group);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("Identity:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_entry_new();
	gtk_entry_set_text(GTK_ENTRY(w), net->sup_identity);
	g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(gui_process_text_input),
	    (gpointer) net->sup_identity);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("Anonymous Identity:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_entry_new();
	gtk_entry_set_text(GTK_ENTRY(w), net->sup_anonymous_identity);
	g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(gui_process_text_input),
	    (gpointer) net->sup_anonymous_identity);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("Mixed Cell:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_spin_button_new_with_range(0.0, 1.0, 1.0);
	gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), (gdouble) net->sup_mixed_cell);
	g_signal_connect(G_OBJECT(w), "value-changed", G_CALLBACK(gui_process_spin_button),
	    (gpointer) &net->sup_mixed_cell);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("Password:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_entry_new();
	gtk_entry_set_text(GTK_ENTRY(w), net->sup_password);
	g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(gui_process_text_input),
	    (gpointer) net->sup_password);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	gtk_entry_set_visibility(GTK_ENTRY(w), FALSE);
	net->w_sup_password = w;
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("CA Certificate:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_file_chooser_button_new(net->sup_ca_cert, GTK_FILE_CHOOSER_ACTION_OPEN);
	gtk_file_chooser_button_set_title(GTK_FILE_CHOOSER_BUTTON(w), gettext("CA Certificate:"));
	if (*net->sup_ca_cert)
		gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(w), net->sup_ca_cert);
	else
		gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(w), ETCDIR);
	g_signal_connect(G_OBJECT(w), "selection-changed", G_CALLBACK(gui_process_file_selection),
	    (gpointer) net->sup_ca_cert);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("Client Certificate:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_file_chooser_button_new(net->sup_client_cert, GTK_FILE_CHOOSER_ACTION_OPEN);
	gtk_file_chooser_button_set_title(GTK_FILE_CHOOSER_BUTTON(w), gettext("Client Certificate:"));
	if (*net->sup_client_cert)
		gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(w), net->sup_client_cert);
	else
		gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(w), ETCDIR);
	g_signal_connect(G_OBJECT(w), "selection-changed", G_CALLBACK(gui_process_file_selection),
	    (gpointer) net->sup_client_cert);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("Private Key:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_file_chooser_button_new(net->sup_private_key, GTK_FILE_CHOOSER_ACTION_OPEN);
	gtk_file_chooser_button_set_title(GTK_FILE_CHOOSER_BUTTON(w), gettext("Private Key:"));
	if (*net->sup_private_key)
		gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(w), net->sup_private_key);
	else
		gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(w), ETCDIR);
	g_signal_connect(G_OBJECT(w), "selection-changed", G_CALLBACK(gui_process_file_selection),
	    (gpointer) net->sup_private_key);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("Private Key Password:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_entry_new();
	gtk_entry_set_text(GTK_ENTRY(w), net->sup_private_key_passwd);
	g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(gui_process_text_input),
	    (gpointer) net->sup_private_key_passwd);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	gtk_entry_set_visibility(GTK_ENTRY(w), FALSE);
	net->w_sup_private_key_passwd = w;
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("DH/DSA Parameters File:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_file_chooser_button_new(net->sup_dh_file, GTK_FILE_CHOOSER_ACTION_OPEN);
	gtk_file_chooser_button_set_title(GTK_FILE_CHOOSER_BUTTON(w), gettext("DH/DSA Parameters File:"));
	if (*net->sup_dh_file)
		gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(w), net->sup_dh_file);
	else
		gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(w), ETCDIR);
	g_signal_connect(G_OBJECT(w), "selection-changed", G_CALLBACK(gui_process_file_selection),
	    (gpointer) net->sup_dh_file);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("Subject Match:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_entry_new();
	gtk_entry_set_text(GTK_ENTRY(w), net->sup_subject_match);
	g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(gui_process_text_input),
	    (gpointer) net->sup_subject_match);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("Phase1 Parameters:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_entry_new();
	gtk_entry_set_text(GTK_ENTRY(w), net->sup_phase1);
	g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(gui_process_text_input),
	    (gpointer) net->sup_phase1);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("CA Certificate 2:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_file_chooser_button_new(net->sup_ca_cert2, GTK_FILE_CHOOSER_ACTION_OPEN);
	gtk_file_chooser_button_set_title(GTK_FILE_CHOOSER_BUTTON(w), gettext("CA Certificate 2:"));
	if (*net->sup_ca_cert2)
		gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(w), net->sup_ca_cert2);
	else
		gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(w), ETCDIR);
	g_signal_connect(G_OBJECT(w), "selection-changed", G_CALLBACK(gui_process_file_selection),
	    (gpointer) net->sup_ca_cert2);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("Client Certificate 2:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_file_chooser_button_new(net->sup_client_cert2, GTK_FILE_CHOOSER_ACTION_OPEN);
	gtk_file_chooser_button_set_title(GTK_FILE_CHOOSER_BUTTON(w), gettext("Client Certificate 2:"));
	if (*net->sup_client_cert2)
		gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(w), net->sup_client_cert2);
	else
		gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(w), ETCDIR);
	g_signal_connect(G_OBJECT(w), "selection-changed", G_CALLBACK(gui_process_file_selection),
	    (gpointer) net->sup_client_cert2);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("Private Key 2:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_file_chooser_button_new(net->sup_private_key2, GTK_FILE_CHOOSER_ACTION_OPEN);
	gtk_file_chooser_button_set_title(GTK_FILE_CHOOSER_BUTTON(w), gettext("Private Key 2:"));
	if (*net->sup_private_key2)
		gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(w), net->sup_private_key2);
	else
		gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(w), ETCDIR);
	g_signal_connect(G_OBJECT(w), "selection-changed", G_CALLBACK(gui_process_file_selection),
	    (gpointer) net->sup_private_key2);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("Private Key 2 Password:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_entry_new();
	gtk_entry_set_text(GTK_ENTRY(w), net->sup_private_key2_passwd);
	g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(gui_process_text_input),
	    (gpointer) net->sup_private_key2_passwd);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	gtk_entry_set_visibility(GTK_ENTRY(w), FALSE);
	net->w_sup_private_key2_passwd = w;
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("DH/DSA Parameters File 2:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_file_chooser_button_new(net->sup_dh_file2, GTK_FILE_CHOOSER_ACTION_OPEN);
	gtk_file_chooser_button_set_title(GTK_FILE_CHOOSER_BUTTON(w), gettext("DH/DSA Parameters File 2:"));
	if (*net->sup_dh_file2)
		gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(w), net->sup_dh_file2);
	else
		gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(w), ETCDIR);
	g_signal_connect(G_OBJECT(w), "selection-changed", G_CALLBACK(gui_process_file_selection),
	    (gpointer) net->sup_dh_file2);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("Subject Match 2:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_entry_new();
	gtk_entry_set_text(GTK_ENTRY(w), net->sup_subject_match2);
	g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(gui_process_text_input),
	    (gpointer) net->sup_subject_match2);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("Phase2 Parameters:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_entry_new();
	gtk_entry_set_text(GTK_ENTRY(w), net->sup_phase2);
	g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(gui_process_text_input),
	    (gpointer) net->sup_phase2);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("PAC File:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_file_chooser_button_new(net->sup_pac_file, GTK_FILE_CHOOSER_ACTION_OPEN);
	gtk_file_chooser_button_set_title(GTK_FILE_CHOOSER_BUTTON(w), gettext("PAC File:"));
	if (*net->sup_pac_file)
		gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(w), net->sup_pac_file);
	else
		gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(w), ETCDIR);
	g_signal_connect(G_OBJECT(w), "selection-changed", G_CALLBACK(gui_process_file_selection),
	    (gpointer) net->sup_pac_file);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	w = gtk_label_new(gettext("EAP Workaround:"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_spin_button_new_with_range(0.0, 1.0, 1.0);
	gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), (gdouble) net->sup_eap_workaround);
	g_signal_connect(G_OBJECT(w), "value-changed", G_CALLBACK(gui_process_spin_button),
	    (gpointer) &net->sup_eap_workaround);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	row++; col = 0;

	gtk_container_add(GTK_CONTAINER(grid_scrolled_window), grid);
	gtk_box_pack_start(GTK_BOX(vbox), grid_scrolled_window, TRUE, TRUE, 0);

	bottom_buttons = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
	w = gtk_button_new_with_label(gettext("Done"));
	g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(gui_edit_eap_done),
	    (gpointer) window);
	gtk_box_pack_end(GTK_BOX(bottom_buttons), w, FALSE, FALSE, 5);
	gtk_box_pack_start(GTK_BOX(vbox), bottom_buttons, FALSE, FALSE, 0);

	gtk_container_add(GTK_CONTAINER(window), vbox);

	gtk_widget_show_all(window);
}

/*
** delete existing grid data, if any
** fill grid with network data
*/
static GtkWidget *
gui_fill_network_grid(GtkWidget * x, gpointer * gp) {
	GtkWidget *		grid;
	GtkWidget *		grid_viewport = NULL;
	GtkWidget *		w;
	GtkWidget *		b;
	int			row;
	int			col;
	struct wifi_net *	net;
	char *			icon;
	char			buf[256];

	if (gp) {
		grid = *((GtkWidget **) gp);
		grid_viewport = gtk_widget_get_parent(grid);
		gtk_widget_destroy(grid);
	}

	grid = gtk_grid_new();
	gtk_grid_set_row_spacing(GTK_GRID(grid), 5);
	gtk_grid_set_column_spacing(GTK_GRID(grid), 15);

	row = 0;
	col = 0;

	w = gtk_label_new(gettext("<b>Enabled</b>"));
	gtk_label_set_use_markup(GTK_LABEL(w), TRUE);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_label_new(gettext("<b>SSID</b>"));
	gtk_label_set_use_markup(GTK_LABEL(w), TRUE);
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	/* encryption */
	col++;

	w = gtk_label_new(gettext("<b>Encryption Key</b>"));
	gtk_label_set_use_markup(GTK_LABEL(w), TRUE);
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_image_new_from_file(ICON_PATH "/eye.png");
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_label_new(gettext("<b>Comment</b>"));
	gtk_label_set_use_markup(GTK_LABEL(w), TRUE);
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_image_new_from_file(ICON_PATH "/disabled.png");
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_label_new(gettext("<b>Priority</b>"));
	gtk_label_set_use_markup(GTK_LABEL(w), TRUE);
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_label_new(gettext("<b>BSSID</b>"));
	gtk_label_set_use_markup(GTK_LABEL(w), TRUE);
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_CENTER);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_label_new(gettext("<b>Any\nBSSID</b>"));
	gtk_label_set_use_markup(GTK_LABEL(w), TRUE);
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_CENTER);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_label_new(gettext("<b>Chan</b>"));
	gtk_label_set_use_markup(GTK_LABEL(w), TRUE);
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_RIGHT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	w = gtk_label_new(gettext("<b>Mbps</b>"));
	gtk_label_set_use_markup(GTK_LABEL(w), TRUE);
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_RIGHT);
	gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
	col++;

	if (!nets) {
		nets = build_network_list();
		gui_order_networks();
	}

	ifconfig_associated_network();

	for (net = nets; net; net = net->wn_next) {
		if (!gui_show_all_networks && net->wn_bars <= 0)
			continue;

		if (net->wn_any_bssid > 1) {
			net->w_sup_bssid = NULL;
			net->w_wn_chan = NULL;
			continue;
		}

		row++; col = 0;

		b = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
		w = gtk_check_button_new();
		if (net->wn_enabled)
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
		g_signal_connect(G_OBJECT(w), "toggled", G_CALLBACK(gui_process_check_button),
		    (gpointer) &(net->wn_enabled));
		gtk_box_pack_start(GTK_BOX(b), w, FALSE, FALSE, 0);
		if (net->wn_assoc_ifn >= 0) {
			w = gtk_image_new_from_file(ICON_PATH "/wifimgr24green.png");
			gtk_widget_set_tooltip_text(w, get_intf_name(net->wn_assoc_ifn));
			gtk_box_pack_start(GTK_BOX(b), w, FALSE, FALSE, 5);
		}
		gtk_grid_attach(GTK_GRID(grid), b, col, row, 1, 1);
		col++;

		w = gtk_label_new(net->sup_ssid);
		gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
		gtk_label_set_max_width_chars(GTK_LABEL(w), 32);
		gtk_label_set_ellipsize(GTK_LABEL(w), PANGO_ELLIPSIZE_END);
		gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
		net->w_sup_ssid = w;
		col++;

		icon = NULL;
		if (net->wn_security & WN_SEC_RSN)
			icon = ICON_PATH "/padlock-rsn.png";
		else if (net->wn_security & WN_SEC_WPA)
			icon = ICON_PATH "/padlock-wpa.png";
		else if (net->wn_security & WN_SEC_WEP)
			icon = ICON_PATH "/padlock-wep.png";
		else if (net->wn_security & WN_SEC_NONE)
			icon = ICON_PATH "/padlock-open.png";
		else
			icon = ICON_PATH "/padlock-unk.png";
		if (icon) {
			w = gtk_image_new_from_file(icon);
			gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
			net->w_wn_security = w;
		}
		col++;

		/* if net has both EAP and PSK, gui_edit_eap() will allow editing of PSK field */
		if (net->wn_km & WN_KM_EAP) {
			w = gtk_button_new_with_label(gettext("Edit EAP Parameters"));
			g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(gui_edit_eap),
			    (gpointer) net);
			gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
			net->w_wn_key = w;
		}
		else if (net->wn_km & WN_KM_PSK || net->wn_security & WN_SEC_WEP) {
			w = gtk_entry_new();
			gtk_entry_set_text(GTK_ENTRY(w), net->wn_key);
			g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(gui_process_text_input),
			    (gpointer) net->wn_key);
			gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
			gtk_entry_set_visibility(GTK_ENTRY(w), FALSE);
			net->w_wn_key = w;
		}
		col++;

		if (net->wn_km & WN_KM_PSK || net->wn_security & WN_SEC_WEP) {
			w = gtk_check_button_new();
			if (net->wn_show_password)
				gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
			g_signal_connect(G_OBJECT(w), "toggled", G_CALLBACK(gui_process_check_button_showpass),
			    (gpointer) net);
			gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
		}
		col++;

		w = gtk_entry_new();
		gtk_entry_set_text(GTK_ENTRY(w), net->wn_comment);
		g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(gui_process_text_input),
		    (gpointer) net->wn_comment);
		gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
		col++;

		w = gtk_check_button_new();
		if (net->sup_disabled)
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
		g_signal_connect(G_OBJECT(w), "toggled", G_CALLBACK(gui_process_check_button_bssid_disabled),
		    (gpointer) net);
		gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
		col++;

		w = gtk_spin_button_new_with_range(0.0, 255.0, 1.0);
		gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), (gdouble) net->sup_priority);
		g_signal_connect(G_OBJECT(w), "value-changed", G_CALLBACK(gui_process_spin_button),
		    (gpointer) &net->sup_priority);
		gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
		net->w_sup_priority = w;
		col++;

		if (net->wn_any_bssid)
			w = gtk_label_new(gui_any_bssid);
		else
			w = gtk_label_new(net->sup_bssid);
		gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
		net->w_sup_bssid = w;
		col++;

		w = gtk_check_button_new();
		if (net->wn_any_bssid)
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
		g_signal_connect(G_OBJECT(w), "toggled", G_CALLBACK(gui_process_check_button_any_bssid),
		    (gpointer) net);
		gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
		col++;

		if (net->wn_any_bssid)
			w = gtk_label_new(gui_any_chan);
		else {
			sprintf(buf, "%d", net->wn_chan);
			w = gtk_label_new(buf);
		}
		gtk_label_set_xalign(GTK_LABEL(w), ALIGN_RIGHT);
		gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
		net->w_wn_chan = w;
		col++;

		sprintf(buf, "%d", net->wn_rate);
		w = gtk_label_new(buf);
		gtk_label_set_xalign(GTK_LABEL(w), ALIGN_RIGHT);
		gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
		net->w_wn_rate = w;
		col++;

		icon = NULL;
		if (net->wn_bars > 80)      icon = ICON_PATH "/signal_bars_5.png";
		else if (net->wn_bars > 60) icon = ICON_PATH "/signal_bars_4.png";
		else if (net->wn_bars > 40) icon = ICON_PATH "/signal_bars_3.png";
		else if (net->wn_bars > 20) icon = ICON_PATH "/signal_bars_2.png";
		else if (net->wn_bars > 0)  icon = ICON_PATH "/signal_bars_1.png";
		else			    icon = ICON_PATH "/signal_bars_0.png";
		w = gtk_image_new_from_file(icon);
		gtk_grid_attach(GTK_GRID(grid), w, col, row, 1, 1);
		net->w_wn_bars = w;
		col++;
	}

	if (gp) {
		gtk_container_add(GTK_CONTAINER(grid_viewport), grid);
		gtk_widget_show_all(grid);
		*gp = grid;
	}

	return grid;
}

/*
** turn WiFi interface on or off
*/
static GtkWidget *
gui_interface_up_down(GtkWidget * x1, gpointer * x2) {
	char *			icon;

	if (gui_changes && gui_message(gettext("Discard changes?"), MSG_QUESTION) != MSG_RESPONSE_YES)
		return NULL;

	gui_changes = 0;

	wifi_if_status = toggle_intf_up_down();

	/* change the main icon */
	icon = (wifi_if_status == WIFI_IF_UP) ? ICON_PATH "/wifimgr.png" : ICON_PATH "/wifimgr-grey.png";
	gtk_image_set_from_file(GTK_IMAGE(gui_up_down_icon), icon);

	/* zero existing network list */
	nl_delete_list(&nets);

	/* redraw grid */
	gui_fill_network_grid(NULL, (gpointer *) &gui_net_grid);

	return gui_net_grid;
}

/*
** restart interface and rescan
*/
static GtkWidget *
gui_rescan(GtkWidget * x1, gpointer * x2) {
	char *			icon;

	if (gui_changes && gui_message(gettext("Discard changes?"), MSG_QUESTION) != MSG_RESPONSE_YES)
		return NULL;

	gui_changes = 0;

	/* restart interface */
	if (! restart_intf())
		return NULL;

	/* change the main icon */
	if (wifi_if_status == WIFI_IF_DOWN) {
		wifi_if_status = WIFI_IF_UP;
		icon = ICON_PATH "/wifimgr.png";
		gtk_image_set_from_file(GTK_IMAGE(gui_up_down_icon), icon);
	}

	/* zero existing network list */
	nl_delete_list(&nets);

	/* redraw grid */
	gui_fill_network_grid(NULL, (gpointer *) &gui_net_grid);

	return gui_net_grid;
}

/*
** save networks file, restart interface and rescan
*/
static GtkWidget *
gui_save_and_rescan(GtkWidget * x1, gpointer * x2) {
	char *			icon;

	/* save updates */
	if (! save_networks_file(NETWORKS_FILE))
		return NULL;

	gui_changes = 0;

	/* restart interface */
	if (! restart_intf())
		return NULL;

	/* change the main icon */
	if (wifi_if_status == WIFI_IF_DOWN) {
		wifi_if_status = WIFI_IF_UP;
		icon = ICON_PATH "/wifimgr.png";
		gtk_image_set_from_file(GTK_IMAGE(gui_up_down_icon), icon);
	}

	/* zero existing network list */
	nl_delete_list(&nets);

	/* redraw grid */
	gui_fill_network_grid(NULL, (gpointer *) &gui_net_grid);

	return gui_net_grid;
}

/*
** user has clicked exit button
*/
static void
gui_exit(GtkWidget * w, gpointer * gp) {
	if (gui_changes && gui_message(gettext("Discard changes?"), MSG_QUESTION) != MSG_RESPONSE_YES)
		return;

	gtk_main_quit();
}

/*
** main GUI loop
*/
void
gui_loop() {
	GtkWidget *		window;
	GtkWidget *		main_vbox;
	GtkWidget *		top_graphic;
	GtkWidget *		grid_scrolled_window;
	GtkWidget *		bottom_buttons;
	GtkWidget *		w;
	char *			icon;
	char			buf[256];

	wifi_if_status = network_intf_status();

	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
	gtk_window_set_default_size(GTK_WINDOW(window), 1200, 600);
	gtk_window_set_title(GTK_WINDOW(window), gettext("WiFi Networks Manager"));
	gtk_container_set_border_width(GTK_CONTAINER(window), 5);
	g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(gtk_main_quit), NULL);

	main_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);

	top_graphic = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
	w = gtk_image_new_from_file(ICON_PATH "/freebsd-surf.png");
	gtk_box_pack_start(GTK_BOX(top_graphic), w, FALSE, FALSE, 0);
	icon = (wifi_if_status == WIFI_IF_UP) ? ICON_PATH "/wifimgr.png" : ICON_PATH "/wifimgr-grey.png";
	gui_up_down_icon = gtk_image_new_from_file(icon);
	gtk_box_pack_end(GTK_BOX(top_graphic), gui_up_down_icon, FALSE, FALSE, 0);
	sprintf(buf, "<span size=\"x-large\"><b>%s</b></span>\n%s", gettext("WiFi Networks Manager"), VERSION);
	w = gtk_label_new(buf);
	gtk_label_set_use_markup(GTK_LABEL(w), TRUE);
	gtk_box_pack_end(GTK_BOX(top_graphic), w, FALSE, FALSE, 10);
	gtk_box_pack_start(GTK_BOX(main_vbox), top_graphic, FALSE, FALSE, 0);

	grid_scrolled_window = gtk_scrolled_window_new(NULL, NULL);
	gtk_container_set_border_width(GTK_CONTAINER(grid_scrolled_window), 5);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(grid_scrolled_window),
	    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

	gui_net_grid = gui_fill_network_grid(NULL, NULL);

	gtk_container_add(GTK_CONTAINER(grid_scrolled_window), GTK_WIDGET(gui_net_grid));
	gtk_box_pack_start(GTK_BOX(main_vbox), grid_scrolled_window, TRUE, TRUE, 0);

	bottom_buttons = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
	w = gtk_button_new_with_label(gettext("WiFi Up/Down"));
	gtk_button_set_image(GTK_BUTTON(w), gtk_image_new_from_file(ICON_PATH "/on-off.png"));
	g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(gui_interface_up_down), NULL);
	gtk_box_pack_start(GTK_BOX(bottom_buttons), w, FALSE, FALSE, 5);
	w = gtk_button_new_with_label(gettext("Add Cloaked Network"));
	g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(gui_edit_new_network), NULL);
	gtk_box_pack_start(GTK_BOX(bottom_buttons), w, FALSE, FALSE, 5);
	w = gtk_button_new_with_label(gettext("Rescan networks"));
	g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(gui_rescan), NULL);
	w = gtk_label_new(gettext("Sort by"));
	gtk_label_set_xalign(GTK_LABEL(w), ALIGN_LEFT);
	gtk_box_pack_start(GTK_BOX(bottom_buttons), w, FALSE, FALSE, 5);
	w = gtk_combo_box_text_new();
	gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(w), NULL, gettext("SSID"));
	gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(w), NULL, gettext("signal strength"));
	gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(w), NULL, gettext("channel"));
	gtk_combo_box_set_active(GTK_COMBO_BOX(w), gui_networks_order);
	g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(gui_process_dropdown_order_by), NULL);
	gtk_box_pack_start(GTK_BOX(bottom_buttons), w, FALSE, FALSE, 5);
	w = gtk_check_button_new_with_label(gettext("Show all networks"));
	if (gui_show_all_networks)
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
	g_signal_connect(G_OBJECT(w), "toggled", G_CALLBACK(gui_process_check_button_show_all_networks), NULL);
	gtk_box_pack_start(GTK_BOX(bottom_buttons), w, FALSE, FALSE, 5);
	w = gtk_button_new_with_label(gettext("Close"));
	g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(gui_exit), NULL);
	gtk_box_pack_end(GTK_BOX(bottom_buttons), w, FALSE, FALSE, 5);
	w = gtk_button_new_with_label(gettext("Save and Reconnect"));
	g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(gui_save_and_rescan), NULL);
	gtk_box_pack_end(GTK_BOX(bottom_buttons), w, FALSE, FALSE, 5);
	gtk_box_pack_start(GTK_BOX(main_vbox), bottom_buttons, FALSE, FALSE, 0);

	gtk_container_add(GTK_CONTAINER(window), main_vbox);

	gtk_widget_show_all(window);

	gtk_main();
}
