Panel
/* $Id$ */
							/* Copyright (c) 2013-2018 Pierre Pronchery <khorben@defora.org> */
							/* This file is part of DeforaOS Desktop Panel */
							/* 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, version 3 of the License.
							 *
							 * 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, see <http://www.gnu.org/licenses/>. */
							/* TODO:
							 * - list every network known
							 * - show only connect or disconnect in the popup menu
							 * - also allow removing networks */
							#include <stdio.h>
							#include <string.h>
							#include <locale.h>
							#include <libintl.h>
							#include <gtk/gtk.h>
							#include "../src/applets/wpa_supplicant.c"
							#include "../config.h"
							#define _(string) gettext(string)
							/* constants */
							#ifndef PROGNAME_WIFIBROWSER
							# define PROGNAME_WIFIBROWSER	"wifibrowser"
							#endif
							#ifndef PREFIX
							# define PREFIX			"/usr/local"
							#endif
							#ifndef DATADIR
							# define DATADIR		PREFIX "/share"
							#endif
							#ifndef LOCALEDIR
							# define LOCALEDIR		DATADIR "/locale"
							#endif
							/* WifiBrowser */
							/* private */
							/* types */
							typedef enum _WifiBrowserResponse
							{
								WBR_REASSOCIATE = 0,
								WBR_RESCAN,
								WBR_SAVE_CONFIGURATION
							} WifiBrowserResponse;
							struct _Panel
							{
								Config * config;
							};
							/* prototypes */
							static int _wifibrowser(char const * configfile, char const * interface);
							static int _error(Panel * panel, char const * message, int ret);
							static int _usage(void);
							/* helpers */
							static char const * _helper_config_get(Panel * panel, char const * section,
									char const * variable);
							/* callbacks */
							static gboolean _wifibrowser_on_closex(gpointer data);
							static void _wifibrowser_on_enabled_toggled(GtkCellRenderer * renderer,
									gchar * path, gpointer data);
							static void _wifibrowser_on_response(GtkWidget * widget, gint arg1,
									gpointer data);
							static gboolean _wifibrowser_on_view_button_press(GtkWidget * widget,
									GdkEventButton * event, gpointer data);
							static gboolean _wifibrowser_on_view_popup_menu(GtkWidget * widget,
									gpointer data);
							/* functions */
							/* wifibrowser */
							static int _wifibrowser(char const * configfile, char const * interface)
							{
								Panel panel;
								PanelAppletHelper helper;
								WPA * wpa;
								GtkWidget * window;
								GtkWidget * vbox;
								GtkWidget * view;
								GtkWidget * widget;
								GtkCellRenderer * renderer;
								GtkTreeViewColumn * column;
								/* XXX report errors to the user instead */
								if((panel.config = config_new()) != NULL)
								{
									if(configfile != NULL
											&& config_load(panel.config, configfile) != 0)
										error_print(PROGNAME_WIFIBROWSER);
									if(interface != NULL
											&& config_set(panel.config, "wpa_supplicant",
												"interface", interface) != 0)
										error_print(PROGNAME_WIFIBROWSER);
								}
								else
									error_print(PROGNAME_WIFIBROWSER);
								/* FIXME load the configuration */
								memset(&helper, 0, sizeof(helper));
								helper.panel = &panel;
								helper.error = _error;
								helper.config_get = _helper_config_get;
								if((wpa = _wpa_init(&helper, &widget)) == NULL)
									return 2;
								window = gtk_dialog_new_with_buttons(_("Wireless browser"), NULL, 0,
										_("Reassociate"), WBR_REASSOCIATE,
										_("Rescan"), WBR_RESCAN,
										GTK_STOCK_SAVE, WBR_SAVE_CONFIGURATION,
										GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
								gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
							#if GTK_CHECK_VERSION(2, 6, 0)
								gtk_window_set_icon_name(GTK_WINDOW(window), "network-wireless");
							#endif
								g_signal_connect_swapped(window, "delete-event", G_CALLBACK(
											_wifibrowser_on_closex), wpa);
								g_signal_connect(window, "response", G_CALLBACK(
											_wifibrowser_on_response), wpa);
							#if GTK_CHECK_VERSION(2, 14, 0)
								vbox = gtk_dialog_get_content_area(GTK_DIALOG(window));
							#else
								vbox = GTK_DIALOG(window)->vbox;
							#endif
								widget = gtk_scrolled_window_new(NULL, NULL);
								gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget),
										GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
								view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(wpa->store));
								gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), TRUE);
							#if GTK_CHECK_VERSION(2, 12, 0)
								gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(view), WSR_TOOLTIP);
							#endif
								/* auto-connect */
								renderer = gtk_cell_renderer_toggle_new();
								column = gtk_tree_view_column_new_with_attributes("", renderer,
										"visible", WSR_CAN_ENABLE, "active", WSR_ENABLED,
										"activatable", WSR_CAN_ENABLE, NULL);
								gtk_tree_view_column_set_sort_column_id(column, WSR_ENABLED);
								gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
								g_signal_connect(renderer, "toggled", G_CALLBACK(
											_wifibrowser_on_enabled_toggled), wpa);
								/* signal level */
								renderer = gtk_cell_renderer_pixbuf_new();
								column = gtk_tree_view_column_new_with_attributes("", renderer,
										"pixbuf", WSR_ICON, NULL);
								gtk_tree_view_column_set_sort_column_id(column, WSR_LEVEL);
								gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
								/* SSID */
								renderer = gtk_cell_renderer_text_new();
								column = gtk_tree_view_column_new_with_attributes(_("SSID"), renderer,
										"text", WSR_SSID_DISPLAY, NULL);
								gtk_tree_view_column_set_resizable(column, TRUE);
								gtk_tree_view_column_set_sort_column_id(column, WSR_SSID);
								gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
								/* BSSID */
								renderer = gtk_cell_renderer_text_new();
								column = gtk_tree_view_column_new_with_attributes(_("BSSID"), renderer,
										"text", WSR_BSSID, NULL);
								gtk_tree_view_column_set_resizable(column, TRUE);
								gtk_tree_view_column_set_sort_column_id(column, WSR_BSSID);
								gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
								g_signal_connect(view, "button-press-event", G_CALLBACK(
											_wifibrowser_on_view_button_press), wpa);
								g_signal_connect(view, "popup-menu", G_CALLBACK(
											_wifibrowser_on_view_popup_menu), wpa);
								gtk_container_add(GTK_CONTAINER(widget), view);
								gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 0);
								gtk_widget_show_all(window);
								gtk_main();
								_wpa_destroy(wpa);
								if(panel.config != NULL)
									config_delete(panel.config);
								return 0;
							}
							/* error */
							static int _error(Panel * panel, char const * message, int ret)
							{
								(void) panel;
								fputs(PROGNAME_WIFIBROWSER ": ", stderr);
								perror(message);
								return ret;
							}
							/* usage */
							static int _usage(void)
							{
								fprintf(stderr, _("Usage: %s [-c filename][-i interface]\n"
							"  -c	Path to a configuration file\n"
							"  -i	Network interface to connect to\n"), PROGNAME_WIFIBROWSER);
								return 1;
							}
							/* helpers */
							/* helper_config_get */
							static char const * _helper_config_get(Panel * panel, char const * section,
									char const * variable)
							{
								if(panel->config == NULL)
									return NULL;
								return config_get(panel->config, section, variable);
							}
							/* callbacks */
							/* wifibrowser_on_closex */
							static gboolean _wifibrowser_on_closex(gpointer data)
							{
								WPA * wpa = data;
								gtk_widget_hide(wpa->widget);
								gtk_main_quit();
								return FALSE;
							}
							/* wifibrowser_on_enabled_toggled */
							static void _wifibrowser_on_enabled_toggled(GtkCellRenderer * renderer,
									gchar * path, gpointer data)
							{
								WPA * wpa = data;
								GtkTreeModel * model = GTK_TREE_MODEL(wpa->store);
								GtkTreePath * p;
								GtkTreeIter iter;
								gboolean enabled;
								(void) renderer;
								if((p = gtk_tree_path_new_from_string(path)) == NULL)
									return;
								if(gtk_tree_model_get_iter(model, &iter, p) == TRUE)
								{
									gtk_tree_model_get(model, &iter, WSR_ENABLED, &enabled, -1);
									/* FIXME really implement (add/enable/disable the network) */
									gtk_tree_store_set(wpa->store, &iter, WSR_ENABLED,
											(enabled != FALSE) ? FALSE : TRUE, -1);
								}
								gtk_tree_path_free(p);
							}
							/* wifibrowser_on_response */
							static void _wifibrowser_on_response(GtkWidget * widget, gint arg1,
									gpointer data)
							{
								WPA * wpa = data;
								WPAChannel * channel = &wpa->channel[0];
								switch(arg1)
								{
									case GTK_RESPONSE_CLOSE:
										gtk_widget_hide(widget);
										gtk_main_quit();
										break;
									case WBR_REASSOCIATE:
										_wpa_queue(wpa, channel, WC_REASSOCIATE);
										break;
									case WBR_RESCAN:
										_wpa_queue(wpa, channel, WC_SCAN);
										break;
									case WBR_SAVE_CONFIGURATION:
										_wpa_queue(wpa, channel, WC_SAVE_CONFIGURATION);
										break;
								}
							}
							/* wifibrowser_on_view_button_press */
							static gboolean _wifibrowser_on_view_button_press(GtkWidget * widget,
									GdkEventButton * event, gpointer data)
							{
								WPA * wpa = data;
								GtkTreeSelection * treesel;
								GtkTreeModel * model;
								GtkTreeIter iter;
								gchar * ssid;
								GtkWidget * menu;
								if(event->type != GDK_BUTTON_PRESS
										|| (event->button != 3 && event->button != 0))
									return FALSE;
								treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
								if(gtk_tree_selection_get_selected(treesel, &model, &iter) != TRUE)
									return FALSE;
								gtk_tree_model_get(model, &iter, WSR_SSID, &ssid, -1);
								menu = gtk_menu_new();
							#if GTK_CHECK_VERSION(3, 10, 0)
								widget = gtk_image_menu_item_new_with_label(_("Connect"));
								gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget),
										gtk_image_new_from_icon_name(GTK_STOCK_CONNECT,
											GTK_ICON_SIZE_MENU));
							#else
								widget = gtk_image_menu_item_new_from_stock(GTK_STOCK_CONNECT, NULL);
							#endif
								if(ssid != NULL)
									g_object_set_data(G_OBJECT(widget), "ssid", ssid);
								g_signal_connect(widget, "activate", G_CALLBACK(
											_clicked_on_network_activated), wpa);
								gtk_menu_shell_append(GTK_MENU_SHELL(menu), widget);
							#if GTK_CHECK_VERSION(3, 10, 0)
								widget = gtk_image_menu_item_new_with_label(_("Disconnect"));
								gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget),
										gtk_image_new_from_icon_name(GTK_STOCK_DISCONNECT,
											GTK_ICON_SIZE_MENU));
							#else
								widget = gtk_image_menu_item_new_from_stock(GTK_STOCK_DISCONNECT, NULL);
							#endif
								g_signal_connect_swapped(widget, "activate", G_CALLBACK(
											_clicked_on_disconnect), wpa);
								gtk_menu_shell_append(GTK_MENU_SHELL(menu), widget);
								gtk_widget_show_all(menu);
								gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button,
										event->time);
							#if 0 /* XXX memory leak (for g_object_set_data() above) */
								g_free(ssid);
							#endif
								return TRUE;
							}
							/* wifibrowser_on_view_popup_menu */
							static gboolean _wifibrowser_on_view_popup_menu(GtkWidget * widget,
									gpointer data)
							{
								WPA * wpa = data;
								GdkEventButton event;
								memset(&event, 0, sizeof(event));
								event.type = GDK_BUTTON_PRESS;
								event.button = 0;
								event.time = gtk_get_current_event_time();
								return _wifibrowser_on_view_button_press(widget, &event, wpa);
							}
							/* public */
							/* functions */
							/* main */
							int main(int argc, char * argv[])
							{
								char const * configfile = NULL;
								char const * interface = NULL;
								int o;
								if(setlocale(LC_ALL, "") == NULL)
									_error(NULL, "setlocale", 1);
								bindtextdomain(PACKAGE, LOCALEDIR);
								textdomain(PACKAGE);
								gtk_init(&argc, &argv);
								while((o = getopt(argc, argv, "c:i:")) != -1)
									switch(o)
									{
										case 'c':
											configfile = optarg;
											break;
										case 'i':
											interface = optarg;
											break;
										default:
											return _usage();
									}
								if(optind != argc)
									return _usage();
								return (_wifibrowser(configfile, interface) == 0) ? 0 : 2;
							}
							