diff options
Diffstat (limited to 'netspeed/src')
-rw-r--r-- | netspeed/src/Makefile.am | 18 | ||||
-rw-r--r-- | netspeed/src/backend.c | 330 | ||||
-rw-r--r-- | netspeed/src/backend.h | 95 | ||||
-rw-r--r-- | netspeed/src/netspeed.c | 1812 |
4 files changed, 2255 insertions, 0 deletions
diff --git a/netspeed/src/Makefile.am b/netspeed/src/Makefile.am new file mode 100644 index 00000000..02e439cb --- /dev/null +++ b/netspeed/src/Makefile.am @@ -0,0 +1,18 @@ +AM_CPPFLAGS = -I$(top_srcdir) -I$(includedir) \ + $(GIO_CFLAGS) $(GTOP_APPLETS_CFLAGS) \ + $(MATE_APPLETS4_CFLAGS) $(MATEDESKTOP_CFLAGS) \ + -DMATELOCALEDIR=\""$(datadir)/locale"\" \ + -DDATADIR=\""$(datadir)/"\" \ + -DPREFIX=\""$(prefix)/"\" \ + -DLIBDIR=\""$(libdir)/"\" \ + -DSYSCONFDIR=\""$(sysconfdir)/"\" \ + -DNETSPEED_MENU_UI_DIR=\""$(datadir)/mate/ui"\" + +libexec_PROGRAMS = mate-netspeed-applet + +mate_netspeed_applet_SOURCES = backend.h backend.c netspeed.c + +mate_netspeed_applet_LDADD = $(GIO_LIBS) $(GTOP_APPLETS_LIBS) \ + $(MATE_APPLETS4_LIBS) $(MATEDESKTOP_LIBS) \ + $(IWLIB) $(INTLLIBS) -lm + diff --git a/netspeed/src/backend.c b/netspeed/src/backend.c new file mode 100644 index 00000000..17bbef24 --- /dev/null +++ b/netspeed/src/backend.c @@ -0,0 +1,330 @@ +/* backend.c + * + * 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 Library 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 St, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Netspeed Applet was writen by Jörgen Scheibengruber <[email protected]> + * + * Mate Netspeed Applet migrated by Stefano Karapetsas <[email protected]> + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined(sun) && defined(__SVR4) +#include <sys/sockio.h> +#endif + +#include <glibtop/netlist.h> +#include <glibtop/netload.h> + +#ifdef HAVE_IW + #include <iwlib.h> +#endif /* HAVE_IW */ + +#include "backend.h" + +gboolean +is_dummy_device(const char* device) +{ + glibtop_netload netload; + glibtop_get_netload(&netload, device); + + if (netload.if_flags & (1 << GLIBTOP_IF_FLAGS_LOOPBACK)) + return TRUE; + + /* Skip interfaces without any IPv4/IPv6 address (or + those with only a LINK ipv6 addr) However we need to + be able to exclude these while still keeping the + value so when they get online (with NetworkManager + for example) we don't get a suddent peak. Once we're + able to get this, ignoring down interfaces will be + possible too. */ + if (!(netload.flags & (1 << GLIBTOP_NETLOAD_ADDRESS6) + && netload.scope6 != GLIBTOP_IF_IN6_SCOPE_LINK) + && !(netload.flags & (1 << GLIBTOP_NETLOAD_ADDRESS))) + return TRUE; + + return FALSE; +} + + +/* Check for all available devices. This really should be + * portable for at least all plattforms using the gnu c lib + * TODO: drop it, use glibtop_get_netlist directly / gchar** + */ +GList* +get_available_devices(void) +{ + glibtop_netlist buf; + char **devices, **dev; + GList *device_glist = NULL; + + devices = glibtop_get_netlist(&buf); + + for(dev = devices; *dev; ++dev) { + device_glist = g_list_prepend(device_glist, g_strdup(*dev)); + } + + g_strfreev(devices); + + return device_glist; +} + +const gchar* +get_default_route(void) +{ + FILE *fp; + static char device[50]; + + fp = fopen("/proc/net/route", "r"); + + if (fp == NULL) return NULL; + + while (!feof(fp)) { + char buffer[1024]; + unsigned int ip, gw, flags, ref, use, metric, mask, mtu, window, irtt; + int retval; + char *rv; + + rv = fgets(buffer, 1024, fp); + if (!rv) { + break; + } + + retval = sscanf(buffer, "%49s %x %x %x %u %u %u %x %u %u %u", + device, &ip, &gw, &flags, &ref, &use, &metric, &mask, &mtu, &window, &irtt); + + if (retval != 11) continue; + + if (gw == 0 && !is_dummy_device(device)) { + fclose(fp); + return device; + } + } + fclose(fp); + return NULL; +} + + +void +free_devices_list(GList *list) +{ + g_list_foreach(list, (GFunc)g_free, NULL); + g_list_free(list); +} + + +/* Frees a DevInfo struct and all the stuff it contains + */ +void +free_device_info(DevInfo *devinfo) +{ + g_free(devinfo->name); + g_free(devinfo->ip); + g_free(devinfo->netmask); + g_free(devinfo->ptpip); + g_free(devinfo->hwaddr); + g_free(devinfo->ipv6); + g_free(devinfo->essid); + g_free(devinfo->tx_rate); + g_free(devinfo->rx_rate); + g_free(devinfo->sum_rate); +} + + + + +static char* +format_ipv4(guint32 ip) +{ + char *str = g_malloc(INET_ADDRSTRLEN); + inet_ntop(AF_INET, &ip, str, INET_ADDRSTRLEN); + return str; +} + + +static char* +format_ipv6(const guint8 ip[16]) +{ + char *str = g_malloc(INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, ip, str, INET6_ADDRSTRLEN); + return str; +} + + +/* TODO: + these stuff are not portable because of ioctl +*/ +static void +get_ptp_info(DevInfo *devinfo) +{ + int fd = -1; + struct ifreq request = {}; + + g_strlcpy(request.ifr_name, devinfo->name, sizeof request.ifr_name); + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + return; + + if (ioctl(fd, SIOCGIFDSTADDR, &request) >= 0) { + struct sockaddr_in* addr; + addr = (struct sockaddr_in*)&request.ifr_dstaddr; + devinfo->ptpip = format_ipv4(addr->sin_addr.s_addr); + } + + close(fd); +} + + + + +void +get_device_info(const char *device, DevInfo *devinfo) +{ + glibtop_netload netload; + guint8 *hw; + + g_assert(device); + + memset(devinfo, 0, sizeof *devinfo); + + devinfo->name = g_strdup(device); + devinfo->type = DEV_UNKNOWN; + + glibtop_get_netload(&netload, device); + devinfo->tx = netload.bytes_out; + devinfo->rx = netload.bytes_in; + + devinfo->up = (netload.if_flags & (1L << GLIBTOP_IF_FLAGS_UP) ? TRUE : FALSE); + devinfo->running = (netload.if_flags & (1L << GLIBTOP_IF_FLAGS_RUNNING) ? TRUE : FALSE); + + devinfo->ip = format_ipv4(netload.address); + devinfo->netmask = format_ipv4(netload.subnet); + devinfo->ipv6 = format_ipv6(netload.address6); + devinfo->qual = 0; + devinfo->essid = NULL; + + hw = netload.hwaddress; + if (hw[6] || hw[7]) { + devinfo->hwaddr = g_strdup_printf( + "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", + hw[0], hw[1], hw[2], hw[3], + hw[4], hw[5], hw[6], hw[7]); + } else { + devinfo->hwaddr = g_strdup_printf( + "%02X:%02X:%02X:%02X:%02X:%02X", + hw[0], hw[1], hw[2], + hw[3], hw[4], hw[5]); + } + /* stolen from gnome-applets/multiload/linux-proc.c */ + + if(netload.if_flags & (1L << GLIBTOP_IF_FLAGS_LOOPBACK)) { + devinfo->type = DEV_LO; + } + +#ifdef HAVE_IW + + else if (netload.if_flags & (1L << GLIBTOP_IF_FLAGS_WIRELESS)) { + devinfo->type = DEV_WIRELESS; + get_wireless_info (devinfo); + } + +#endif /* HAVE_IW */ + + else if(netload.if_flags & (1L << GLIBTOP_IF_FLAGS_POINTOPOINT)) { + if (g_str_has_prefix(device, "plip")) { + devinfo->type = DEV_PLIP; + } + else if (g_str_has_prefix(device, "sl")) { + devinfo->type = DEV_SLIP; + } + else { + devinfo->type = DEV_PPP; + } + + get_ptp_info(devinfo); + } + else { + devinfo->type = DEV_ETHERNET; + } +} + +gboolean +compare_device_info(const DevInfo *a, const DevInfo *b) +{ + g_assert(a && b); + g_assert(a->name && b->name); + + if (!g_str_equal(a->name, b->name)) return TRUE; + if (a->ip && b->ip) { + if (!g_str_equal(a->ip, b->ip)) return TRUE; + } else { + if (a->ip || b->ip) return TRUE; + } + /* Ignore hwaddr, ptpip and netmask... I think this is ok */ + if (a->up != b->up) return TRUE; + if (a->running != b->running) return TRUE; + + return FALSE; +} +#ifdef HAVE_IW +void +get_wireless_info (DevInfo *devinfo) +{ + int fd; + int newqual; + wireless_info info = {0}; + + fd = iw_sockets_open (); + + if (fd < 0) + return; + + if (iw_get_basic_config (fd, devinfo->name, &info.b) < 0) + goto out; + + if (info.b.has_essid) { + if ((!devinfo->essid) || (strcmp (devinfo->essid, info.b.essid) != 0)) { + devinfo->essid = g_strdup (info.b.essid); + } + } else { + devinfo->essid = NULL; + } + + if (iw_get_stats (fd, devinfo->name, &info.stats, &info.range, info.has_range) >= 0) + info.has_stats = 1; + + if (info.has_stats) { + if ((iw_get_range_info(fd, devinfo->name, &info.range) >= 0) && (info.range.max_qual.qual > 0)) { + newqual = 0.5f + (100.0f * info.stats.qual.qual) / (1.0f * info.range.max_qual.qual); + } else { + newqual = info.stats.qual.qual; + } + + newqual = CLAMP(newqual, 0, 100); + if (devinfo->qual != newqual) + devinfo->qual = newqual; + + } else { + devinfo->qual = 0; + } + + goto out; +out: + if (fd != -1) + close (fd); +} +#endif /* HAVE_IW */ diff --git a/netspeed/src/backend.h b/netspeed/src/backend.h new file mode 100644 index 00000000..7abccdf2 --- /dev/null +++ b/netspeed/src/backend.h @@ -0,0 +1,95 @@ +/* backend.h + * + * 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 Library 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 St, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Netspeed Applet was writen by Jörgen Scheibengruber <[email protected]> + */ + +#ifndef _BACKEND_H +#define _BACKEND_H + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <sys/ioctl.h> +#include <glib.h> +#include <glibtop/netload.h> + +/* copied from <linux/wireless.h> */ +#define SIOCGIWNAME 0x8B01 /* get name == wireless protocol */ +#define SIOCGIWENCODE 0x8B2B /* get encoding token & mode */ + +/* Different types of interfaces */ +typedef enum +{ + DEV_LO, + DEV_ETHERNET, + DEV_WIRELESS, + DEV_PPP, + DEV_PLIP, + DEV_SLIP, + DEV_UNKNOWN // this has to be the last one +} DevType; + +/* Some information about the selected network device + */ +typedef struct +{ + DevType type; + char *name; + char *ip; + char *netmask; + char *hwaddr; + char *ptpip; + char *ipv6; + char *essid; + gboolean up, running; + guint64 tx, rx; + int qual; + char *tx_rate; + char *rx_rate; + char *sum_rate; +} DevInfo; + +GList* +get_available_devices(void); + +const gchar* +get_default_route(void); + +gboolean +is_dummy_device(const char* device); + +void +free_devices_list(GList *list); + +void +free_device_info(DevInfo *devinfo); + +void +get_device_info(const char *device, DevInfo *info); + +gboolean +compare_device_info(const DevInfo *a, const DevInfo *b); + +void +get_wireless_info (DevInfo *devinfo); +#endif /* _BACKEND_H */ diff --git a/netspeed/src/netspeed.c b/netspeed/src/netspeed.c new file mode 100644 index 00000000..98d486d6 --- /dev/null +++ b/netspeed/src/netspeed.c @@ -0,0 +1,1812 @@ + /* netspeed.c + * + * 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 Library 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 St, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Netspeed Applet was writen by Jörgen Scheibengruber <[email protected]> + * + * Mate Netspeed Applet migrated by Stefano Karapetsas <[email protected]> + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <math.h> +#include <gtk/gtk.h> +#include <mate-panel-applet.h> +#include <mate-panel-applet-gsettings.h> +#include <glib.h> +#include <glib/gi18n.h> +#include <gio/gio.h> +#include <libmate-desktop/mate-aboutdialog.h> +#include <libmate-desktop/mate-colorbutton.h> + +#include "backend.h" + +#if GTK_CHECK_VERSION (3, 0, 0) +#define gtk_vbox_new(X,Y) gtk_box_new(GTK_ORIENTATION_VERTICAL,Y) +#define gtk_hbox_new(X,Y) gtk_box_new(GTK_ORIENTATION_HORIZONTAL,Y) +#endif + + /* Icons for the interfaces */ +static const char* const dev_type_icon[DEV_UNKNOWN + 1] = { + "mate-netspeed-loopback", /* DEV_LO */ + "network-wired", /* DEV_ETHERNET */ + "network-wireless", /* DEV_WIRELESS */ + "mate-netspeed-ppp", /* DEV_PPP */ + "mate-netspeed-plip", /* DEV_PLIP */ + "mate-netspeed-plip", /* DEV_SLIP */ + "network-workgroup", /* DEV_UNKNOWN */ +}; + +static const char* wireless_quality_icon[] = { + "mate-netspeed-wireless-25", + "mate-netspeed-wireless-50", + "mate-netspeed-wireless-75", + "mate-netspeed-wireless-100" +}; + +static const char IN_ICON[] = "go-down"; +static const char OUT_ICON[] = "go-up"; +static const char ERROR_ICON[] = "gtk-dialog-error"; +static const char LOGO_ICON[] = "mate-netspeed-applet"; + +/* How many old in out values do we store? + * The value actually shown in the applet is the average + * of these values -> prevents the value from + * "jumping around like crazy" + */ +#define OLD_VALUES 5 +#define GRAPH_VALUES 180 +#define GRAPH_LINES 4 + +/* A struct containing all the "global" data of the + * applet + */ +typedef struct +{ + MatePanelApplet *applet; + GtkWidget *box, *pix_box, + *in_box, *in_label, *in_pix, + *out_box, *out_label, *out_pix, + *sum_box, *sum_label, *dev_pix, *qual_pix; + GdkPixbuf *qual_pixbufs[4]; + + GtkWidget *signalbar; + + gboolean labels_dont_shrink; + + DevInfo devinfo; + gboolean device_has_changed; + + guint timeout_id; + int refresh_time; + char *up_cmd, *down_cmd; + gboolean show_sum, show_bits; + gboolean change_icon, auto_change_device; + gboolean show_icon, short_unit; + gboolean show_quality_icon; + GdkColor in_color, out_color; + int width; + + GtkWidget *inbytes_text, *outbytes_text; + GtkDialog *details, *settings; + GtkDrawingArea *drawingarea; + GtkWidget *network_device_combo; + + guint index_old; + guint64 in_old[OLD_VALUES], out_old[OLD_VALUES]; + double max_graph, in_graph[GRAPH_VALUES], out_graph[GRAPH_VALUES]; + int index_graph; + + GtkWidget *connect_dialog; + + gboolean show_tooltip; + + GSettings *gsettings; +} MateNetspeedApplet; + +static void +update_tooltip(MateNetspeedApplet* applet); + +static void +device_change_cb(GtkComboBox *combo, MateNetspeedApplet *applet); + +/* Adds a Pango markup "size" to a bytestring + */ +static void +add_markup_size(char **string, int size) +{ + char *tmp = *string; + *string = g_strdup_printf("<span size=\"%d\">%s</span>", size * 1000, tmp); + g_free(tmp); +} + +/* Adds a Pango markup "foreground" to a bytestring + */ +static void +add_markup_fgcolor(char **string, const char *color) +{ + char *tmp = *string; + *string = g_strdup_printf("<span foreground=\"%s\">%s</span>", color, tmp); + g_free(tmp); +} + +/* Here some rearangement of the icons and the labels occurs + * according to the panelsize and wether we show in and out + * or just the sum + */ +static void +applet_change_size_or_orient(MatePanelApplet *applet_widget, int arg1, MateNetspeedApplet *applet) +{ + int size; + MatePanelAppletOrient orient; + + g_assert(applet); + + size = mate_panel_applet_get_size(applet_widget); + orient = mate_panel_applet_get_orient(applet_widget); + + g_object_ref(applet->pix_box); + g_object_ref(applet->in_pix); + g_object_ref(applet->in_label); + g_object_ref(applet->out_pix); + g_object_ref(applet->out_label); + g_object_ref(applet->sum_label); + + if (applet->in_box) { + gtk_container_remove(GTK_CONTAINER(applet->in_box), applet->in_label); + gtk_container_remove(GTK_CONTAINER(applet->in_box), applet->in_pix); + gtk_widget_destroy(applet->in_box); + } + if (applet->out_box) { + gtk_container_remove(GTK_CONTAINER(applet->out_box), applet->out_label); + gtk_container_remove(GTK_CONTAINER(applet->out_box), applet->out_pix); + gtk_widget_destroy(applet->out_box); + } + if (applet->sum_box) { + gtk_container_remove(GTK_CONTAINER(applet->sum_box), applet->sum_label); + gtk_widget_destroy(applet->sum_box); + } + if (applet->box) { + gtk_container_remove(GTK_CONTAINER(applet->box), applet->pix_box); + gtk_widget_destroy(applet->box); + } + + if (orient == MATE_PANEL_APPLET_ORIENT_LEFT || orient == MATE_PANEL_APPLET_ORIENT_RIGHT) { + applet->box = gtk_vbox_new(FALSE, 0); + if (size > 64) { + applet->sum_box = gtk_hbox_new(FALSE, 2); + applet->in_box = gtk_hbox_new(FALSE, 1); + applet->out_box = gtk_hbox_new(FALSE, 1); + } else { + applet->sum_box = gtk_vbox_new(FALSE, 0); + applet->in_box = gtk_vbox_new(FALSE, 0); + applet->out_box = gtk_vbox_new(FALSE, 0); + } + applet->labels_dont_shrink = FALSE; + } else { + applet->in_box = gtk_hbox_new(FALSE, 1); + applet->out_box = gtk_hbox_new(FALSE, 1); + if (size < 48) { + applet->sum_box = gtk_hbox_new(FALSE, 2); + applet->box = gtk_hbox_new(FALSE, 1); + applet->labels_dont_shrink = TRUE; + } else { + applet->sum_box = gtk_vbox_new(FALSE, 0); + applet->box = gtk_vbox_new(FALSE, 0); + applet->labels_dont_shrink = !applet->show_sum; + } + } + + gtk_box_pack_start(GTK_BOX(applet->in_box), applet->in_pix, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(applet->in_box), applet->in_label, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(applet->out_box), applet->out_pix, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(applet->out_box), applet->out_label, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(applet->sum_box), applet->sum_label, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(applet->box), applet->pix_box, FALSE, FALSE, 0); + + g_object_unref(applet->pix_box); + g_object_unref(applet->in_pix); + g_object_unref(applet->in_label); + g_object_unref(applet->out_pix); + g_object_unref(applet->out_label); + g_object_unref(applet->sum_label); + + if (applet->show_sum) { + gtk_box_pack_start(GTK_BOX(applet->box), applet->sum_box, TRUE, TRUE, 0); + } else { + gtk_box_pack_start(GTK_BOX(applet->box), applet->in_box, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(applet->box), applet->out_box, TRUE, TRUE, 0); + } + + gtk_widget_show_all(applet->box); + if (!applet->show_icon) { + gtk_widget_hide(applet->dev_pix); + } + gtk_container_add(GTK_CONTAINER(applet->applet), applet->box); +} + +/* Change the icons according to the selected device + */ +static void +change_icons(MateNetspeedApplet *applet) +{ + GdkPixbuf *dev, *down; + GdkPixbuf *in_arrow, *out_arrow; + GtkIconTheme *icon_theme; + + icon_theme = gtk_icon_theme_get_default(); + + /* If the user wants a different icon than current, we load it */ + if (applet->show_icon && applet->change_icon) { + dev = gtk_icon_theme_load_icon(icon_theme, + dev_type_icon[applet->devinfo.type], + 16, 0, NULL); + } else { + dev = gtk_icon_theme_load_icon(icon_theme, + dev_type_icon[DEV_UNKNOWN], + 16, 0, NULL); + } + + /* We need a fallback */ + if (dev == NULL) + dev = gtk_icon_theme_load_icon(icon_theme, + dev_type_icon[DEV_UNKNOWN], + 16, 0, NULL); + + in_arrow = gtk_icon_theme_load_icon(icon_theme, IN_ICON, 16, 0, NULL); + out_arrow = gtk_icon_theme_load_icon(icon_theme, OUT_ICON, 16, 0, NULL); + + /* Set the windowmanager icon for the applet */ + gtk_window_set_default_icon_name(LOGO_ICON); + + gtk_image_set_from_pixbuf(GTK_IMAGE(applet->out_pix), out_arrow); + gtk_image_set_from_pixbuf(GTK_IMAGE(applet->in_pix), in_arrow); + g_object_unref(in_arrow); + g_object_unref(out_arrow); + + if (applet->devinfo.running) { + gtk_widget_show(applet->in_box); + gtk_widget_show(applet->out_box); + } else { + GdkPixbuf *copy; + gtk_widget_hide(applet->in_box); + gtk_widget_hide(applet->out_box); + + /* We're not allowed to modify "dev" */ + copy = gdk_pixbuf_copy(dev); + + down = gtk_icon_theme_load_icon(icon_theme, ERROR_ICON, 16, 0, NULL); + gdk_pixbuf_composite(down, copy, 8, 8, 8, 8, 8, 8, 0.5, 0.5, GDK_INTERP_BILINEAR, 0xFF); + g_object_unref(down); + g_object_unref(dev); + dev = copy; + } + + if (applet->show_icon) { + gtk_widget_show(applet->dev_pix); + gtk_image_set_from_pixbuf(GTK_IMAGE(applet->dev_pix), dev); + } else { + gtk_widget_hide(applet->dev_pix); + } + g_object_unref(dev); +} + +/* Change visibility of signal quality icon for wireless devices + */ +static void +change_quality_icon(MateNetspeedApplet *applet) +{ + if (applet->devinfo.type == DEV_WIRELESS && + applet->devinfo.up && applet->show_quality_icon) { + gtk_widget_show(applet->qual_pix); + } else { + gtk_widget_hide(applet->qual_pix); + } +} + +static void +update_quality_icon(MateNetspeedApplet *applet) +{ + if (!applet->show_quality_icon) { + return; + } + + unsigned int q; + + q = (applet->devinfo.qual); + q /= 25; + q = CLAMP(q, 0, 3); /* q out of range would crash when accessing qual_pixbufs[q] */ + gtk_image_set_from_pixbuf (GTK_IMAGE(applet->qual_pix), applet->qual_pixbufs[q]); +} + +static void +init_quality_pixbufs(MateNetspeedApplet *applet) +{ + GtkIconTheme *icon_theme; + int i; + GdkPixbuf *pixbuf; + + icon_theme = gtk_icon_theme_get_default(); + + for (i = 0; i < 4; i++) { + if (applet->qual_pixbufs[i]) + g_object_unref(applet->qual_pixbufs[i]); + pixbuf = gtk_icon_theme_load_icon(icon_theme, + wireless_quality_icon[i], 24, 0, NULL); + if (pixbuf) { + applet->qual_pixbufs[i] = gdk_pixbuf_copy(pixbuf); + g_object_unref(pixbuf); + } + else { + applet->qual_pixbufs[i] = NULL; + } + } +} + + +static void +icon_theme_changed_cb(GtkIconTheme *icon_theme, gpointer user_data) +{ + MateNetspeedApplet *applet = (MateNetspeedApplet*)user_data; + + init_quality_pixbufs(user_data); + if (applet->devinfo.type == DEV_WIRELESS && applet->devinfo.up) + update_quality_icon(user_data); + change_icons(user_data); +} + +/* Converts a number of bytes into a human + * readable string - in [M/k]bytes[/s] + * The string has to be freed + */ +static char* +bytes_to_string(double bytes, gboolean per_sec, gboolean bits, gboolean shortened) +{ + const char *format; + const char *unit; + guint kilo; /* no really a kilo : a kilo or kibi */ + + if (bits) { + bytes *= 8; + kilo = 1000; + } else + kilo = 1024; + + if (bytes < kilo) { + + format = "%.0f %s"; + + if (per_sec) + if (shortened) { + unit = bits ? /* translators: bits (short) */ N_("b"): /* translators: Bytes (short) */ N_("B"); + } else { + unit = bits ? N_("b/s") : N_("B/s"); + } + else + unit = bits ? N_("bits") : N_("bytes"); + + } else if (bytes < (kilo * kilo)) { + format = (bytes < (100 * kilo)) ? "%.1f %s" : "%.0f %s"; + bytes /= kilo; + + if (per_sec) + if (shortened) { + unit = bits ? /* translators: kilobits (short) */ N_("k") : /* translators: Kilobytes (short) */ N_("K"); + } else { + unit = bits ? N_("kb/s") : N_("KiB/s"); + } + else + unit = bits ? N_("kb") : N_("KiB"); + + } else { + + format = "%.1f %s"; + + bytes /= kilo * kilo; + + if (per_sec) + if (shortened) { + unit = bits ? /* translators: megabits (short) */ N_("m") : /* translators: Megabytes (short) */ N_("M"); + } else { + unit = bits ? N_("Mb/s") : N_("MiB/s"); + } + else + unit = bits ? N_("Mb") : N_("MiB"); + } + + return g_strdup_printf(format, bytes, gettext(unit)); +} + + +/* Redraws the graph drawingarea + * Some really black magic is going on in here ;-) + */ +static void +redraw_graph(MateNetspeedApplet *applet, cairo_t *cr) +{ + GtkWidget *da = GTK_WIDGET(applet->drawingarea); +#if GTK_CHECK_VERSION (3, 0, 0) + GtkStyleContext *stylecontext = gtk_widget_get_style_context (da); +#else + GtkStyle *style = gtk_widget_get_style (da); +#endif + GdkWindow *real_window = gtk_widget_get_window (da); + GdkRectangle ra; + GtkStateType state; + GdkPoint in_points[GRAPH_VALUES], out_points[GRAPH_VALUES]; + PangoLayout *layout; + PangoRectangle logical_rect; + char *text; + int i, offset, w, h; + double max_val; + double dash[2] = { 1.0, 2.0 }; + + w = gdk_window_get_width (real_window); + h = gdk_window_get_height (real_window); + + /* the graph hight should be: hight/2 <= applet->max_graph < hight */ + for (max_val = 1; max_val < applet->max_graph; max_val *= 2) ; + + /* calculate the polygons (GdkPoint[]) for the graphs */ + offset = 0; + for (i = (applet->index_graph + 1) % GRAPH_VALUES; applet->in_graph[i] < 0; i = (i + 1) % GRAPH_VALUES) + offset++; + for (i = offset + 1; i < GRAPH_VALUES; i++) + { + int index = (applet->index_graph + i) % GRAPH_VALUES; + out_points[i].x = in_points[i].x = ((w - 6) * i) / GRAPH_VALUES + 4; + in_points[i].y = h - 6 - (int)((h - 8) * applet->in_graph[index] / max_val); + out_points[i].y = h - 6 - (int)((h - 8) * applet->out_graph[index] / max_val); + } + in_points[offset].x = out_points[offset].x = ((w - 6) * offset) / GRAPH_VALUES + 4; + in_points[offset].y = in_points[(offset + 1) % GRAPH_VALUES].y; + out_points[offset].y = out_points[(offset + 1) % GRAPH_VALUES].y; + + /* draw the background */ + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); + cairo_rectangle (cr, 02, 2, w - 6, h - 6); + cairo_fill (cr); + + cairo_set_line_width(cr, 1.0); + cairo_set_dash (cr, dash, 2, 0); + + cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); + cairo_rectangle (cr, 2, 2, w - 6, h - 6); + cairo_stroke (cr); + + for (i = 0; i < GRAPH_LINES; i++) { + int y = 2 + ((h - 6) * i) / GRAPH_LINES; + cairo_move_to (cr, 2, y); + cairo_line_to (cr, w - 4, y); + } + cairo_stroke (cr); + + /* draw the polygons */ + cairo_set_dash (cr, dash, 0, 1); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); + cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND); + + gdk_cairo_set_source_color (cr, &applet->in_color); + for (i = offset; i < GRAPH_VALUES; i++) { + cairo_line_to (cr, in_points[i].x, in_points[i].y); + } + cairo_stroke (cr); + + gdk_cairo_set_source_color (cr, &applet->out_color); + for (i = offset; i < GRAPH_VALUES; i++) { + cairo_line_to (cr, out_points[i].x, out_points[i].y); + } + cairo_stroke (cr); + + /* draw the 2 labels */ + state = GTK_STATE_NORMAL; + ra.x = 0; ra.y = 0; + ra.width = w; ra.height = h; + + text = bytes_to_string(max_val, TRUE, applet->show_bits, applet->short_unit); + add_markup_fgcolor(&text, "black"); + layout = gtk_widget_create_pango_layout (da, NULL); + pango_layout_set_markup(layout, text, -1); + g_free (text); +#if GTK_CHECK_VERSION (3, 0, 0) + gtk_render_layout(stylecontext, cr, 3, 2, layout); +#else + gtk_paint_layout(style, real_window, state, FALSE, &ra, da, "max_graph", 3, 2, layout); +#endif + g_object_unref(G_OBJECT(layout)); + + text = bytes_to_string(0.0, TRUE, applet->show_bits, applet->short_unit); + add_markup_fgcolor(&text, "black"); + layout = gtk_widget_create_pango_layout (da, NULL); + pango_layout_set_markup(layout, text, -1); + pango_layout_get_pixel_extents (layout, NULL, &logical_rect); + g_free (text); +#if GTK_CHECK_VERSION (3, 0, 0) + gtk_render_layout(stylecontext, cr, 3, h - 4 - logical_rect.height, layout); +#else + gtk_paint_layout(style, real_window, state, FALSE, &ra, da, "max_graph", 3, h - 4 - logical_rect.height, layout); +#endif + g_object_unref(G_OBJECT(layout)); +} + +static gboolean +set_applet_devinfo(MateNetspeedApplet* applet, const char* iface) +{ + DevInfo info; + + get_device_info(iface, &info); + + if (info.running) { + free_device_info(&applet->devinfo); + applet->devinfo = info; + applet->device_has_changed = TRUE; + return TRUE; + } + + free_device_info(&info); + return FALSE; +} + +/* Find the first available device, that is running and != lo */ +static void +search_for_up_if(MateNetspeedApplet *applet) +{ + const gchar *default_route; + GList *devices, *tmp; + DevInfo info; + + default_route = get_default_route(); + + if (default_route != NULL) { + if (set_applet_devinfo(applet, default_route)) + return; + } + + devices = get_available_devices(); + for (tmp = devices; tmp; tmp = g_list_next(tmp)) { + if (is_dummy_device(tmp->data)) + continue; + if (set_applet_devinfo(applet, tmp->data)) + break; + } + free_devices_list(devices); +} + +/* Here happens the really interesting stuff */ +static void +update_applet(MateNetspeedApplet *applet) +{ + guint64 indiff, outdiff; + double inrate, outrate; + char *inbytes, *outbytes; + int i; + DevInfo oldinfo; + + if (!applet) return; + + /* First we try to figure out if the device has changed */ + oldinfo = applet->devinfo; + get_device_info(oldinfo.name, &applet->devinfo); + if (compare_device_info(&applet->devinfo, &oldinfo)) + applet->device_has_changed = TRUE; + free_device_info(&oldinfo); + + /* If the device has changed, reintialize stuff */ + if (applet->device_has_changed) { + change_icons(applet); + change_quality_icon(applet); + for (i = 0; i < OLD_VALUES; i++) + { + applet->in_old[i] = applet->devinfo.rx; + applet->out_old[i] = applet->devinfo.tx; + } + for (i = 0; i < GRAPH_VALUES; i++) + { + applet->in_graph[i] = -1; + applet->out_graph[i] = -1; + } + applet->max_graph = 0; + applet->index_graph = 0; + applet->device_has_changed = FALSE; + } + + /* create the strings for the labels and tooltips */ + if (applet->devinfo.running) + { + if (applet->devinfo.rx < applet->in_old[applet->index_old]) indiff = 0; + else indiff = applet->devinfo.rx - applet->in_old[applet->index_old]; + if (applet->devinfo.tx < applet->out_old[applet->index_old]) outdiff = 0; + else outdiff = applet->devinfo.tx - applet->out_old[applet->index_old]; + + inrate = indiff * 1000.0; + inrate /= (double)(applet->refresh_time * OLD_VALUES); + outrate = outdiff * 1000.0; + outrate /= (double)(applet->refresh_time * OLD_VALUES); + + applet->in_graph[applet->index_graph] = inrate; + applet->out_graph[applet->index_graph] = outrate; + applet->max_graph = MAX(inrate, applet->max_graph); + applet->max_graph = MAX(outrate, applet->max_graph); + + applet->devinfo.rx_rate = bytes_to_string(inrate, TRUE, applet->show_bits, applet->short_unit); + applet->devinfo.tx_rate = bytes_to_string(outrate, TRUE, applet->show_bits, applet->short_unit); + applet->devinfo.sum_rate = bytes_to_string(inrate + outrate, TRUE, applet->show_bits, applet->short_unit); + } else { + applet->devinfo.rx_rate = g_strdup(""); + applet->devinfo.tx_rate = g_strdup(""); + applet->devinfo.sum_rate = g_strdup(""); + applet->in_graph[applet->index_graph] = 0; + applet->out_graph[applet->index_graph] = 0; + } + + if (applet->devinfo.type == DEV_WIRELESS) { + if (applet->devinfo.up) + update_quality_icon(applet); + + if (applet->signalbar) { + float quality; + char *text; + + quality = applet->devinfo.qual / 100.0f; + if (quality > 1.0) + quality = 1.0; + + text = g_strdup_printf ("%d %%", applet->devinfo.qual); + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (applet->signalbar), quality); + gtk_progress_bar_set_text (GTK_PROGRESS_BAR (applet->signalbar), text); + g_free(text); + } + } + + update_tooltip(applet); + + /* Refresh the text of the labels and tooltip */ + if (applet->show_sum) { + gtk_label_set_markup(GTK_LABEL(applet->sum_label), applet->devinfo.sum_rate); + } else { + gtk_label_set_markup(GTK_LABEL(applet->in_label), applet->devinfo.rx_rate); + gtk_label_set_markup(GTK_LABEL(applet->out_label), applet->devinfo.tx_rate); + } + + /* Refresh the values of the Infodialog */ + if (applet->inbytes_text) { + inbytes = bytes_to_string((double)applet->devinfo.rx, FALSE, FALSE, FALSE); + gtk_label_set_text(GTK_LABEL(applet->inbytes_text), inbytes); + g_free(inbytes); + } + if (applet->outbytes_text) { + outbytes = bytes_to_string((double)applet->devinfo.tx, FALSE, FALSE, FALSE); + gtk_label_set_text(GTK_LABEL(applet->outbytes_text), outbytes); + g_free(outbytes); + } + /* Redraw the graph of the Infodialog */ + if (applet->drawingarea) + gtk_widget_queue_draw (GTK_WIDGET (applet->drawingarea)); + + /* Save old values... */ + applet->in_old[applet->index_old] = applet->devinfo.rx; + applet->out_old[applet->index_old] = applet->devinfo.tx; + applet->index_old = (applet->index_old + 1) % OLD_VALUES; + + /* Move the graphindex. Check if we can scale down again */ + applet->index_graph = (applet->index_graph + 1) % GRAPH_VALUES; + if (applet->index_graph % 20 == 0) + { + double max = 0; + for (i = 0; i < GRAPH_VALUES; i++) + { + max = MAX(max, applet->in_graph[i]); + max = MAX(max, applet->out_graph[i]); + } + applet->max_graph = max; + } + + /* Always follow the default route */ + if (applet->auto_change_device) { + gboolean change_device_now = !applet->devinfo.running; + if (!change_device_now) { + const gchar *default_route; + default_route = get_default_route(); + change_device_now = (default_route != NULL + && strcmp(default_route, + applet->devinfo.name)); + } + if (change_device_now) { + search_for_up_if(applet); + } + } +} + +static gboolean +timeout_function(MateNetspeedApplet *applet) +{ + if (!applet) + return FALSE; + if (!applet->timeout_id) + return FALSE; + + update_applet(applet); + return TRUE; +} + +/* Display a section of netspeed help + */ +static void +display_help (GtkWidget *dialog, const gchar *section) +{ + GdkScreen *screen; + GError *error = NULL; + gboolean ret; + char *uri; + + if (section) + uri = g_strdup_printf ("help:mate-netspeed-applet/%s", section); + else + uri = g_strdup ("help:mate-netspeed-applet"); + + screen = gtk_widget_get_screen (dialog); + ret = gtk_show_uri (screen, uri, gtk_get_current_event_time (), &error); + g_free (uri); + + if (ret == FALSE) { + GtkWidget *error_dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("There was an error displaying help:\n%s"), + error->message); + g_signal_connect (error_dialog, "response", + G_CALLBACK (gtk_widget_destroy), NULL); + + gtk_window_set_resizable (GTK_WINDOW (error_dialog), FALSE); + gtk_window_set_screen (GTK_WINDOW (error_dialog), gtk_widget_get_screen (dialog)); + gtk_widget_show (error_dialog); + g_error_free (error); + } +} + +/* Opens gnome help application + */ +static void +help_cb (GtkAction *action, MateNetspeedApplet *ap) +{ + display_help (GTK_WIDGET (ap->applet), NULL); +} + +/* Just the about window... If it's already open, just fokus it + */ +static void +about_cb(GtkAction *action, gpointer data) +{ + const char *authors[] = + { + "Jörgen Scheibengruber <[email protected]>", + "Dennis Cranston <[email protected]>", + "Pedro Villavicencio Garrido <[email protected]>", + "Benoît Dejean <[email protected]>", + "Stefano Karapetsas <[email protected]>", + "Perberos <[email protected]>", + NULL + }; + + mate_show_about_dialog (NULL, + "version", VERSION, + "copyright", "Copyright 2002 - 2003 Jörgen Scheibengruber\nCopyright 2011-2014 Stefano Karapetsas", + "comments", _("A little applet that displays some information on the traffic on the specified network device"), + "authors", authors, + "documenters", NULL, + "translator-credits", _("translator-credits"), + "website", "http://www.mate-desktop.org/", + "logo-icon-name", LOGO_ICON, + NULL); + +} + +/* this basically just retrieves the new devicestring + * and then calls applet_device_change() and change_icons() + */ +static void +device_change_cb(GtkComboBox *combo, MateNetspeedApplet *applet) +{ + GList *devices; + int i, active; + + g_assert(combo); + devices = g_object_get_data(G_OBJECT(combo), "devices"); + active = gtk_combo_box_get_active(combo); + g_assert(active > -1); + + if (0 == active) { + if (applet->auto_change_device) + return; + applet->auto_change_device = TRUE; + } else { + applet->auto_change_device = FALSE; + for (i = 1; i < active; i++) { + devices = g_list_next(devices); + } + if (g_str_equal(devices->data, applet->devinfo.name)) + return; + free_device_info(&applet->devinfo); + get_device_info(devices->data, &applet->devinfo); + } + + applet->device_has_changed = TRUE; + update_applet(applet); +} + + +/* Handle preference dialog response event + */ +static void +pref_response_cb (GtkDialog *dialog, gint id, gpointer data) +{ + MateNetspeedApplet *applet = data; + + if(id == GTK_RESPONSE_HELP){ + display_help (GTK_WIDGET (dialog), "netspeed_applet-settings"); + return; + } + g_settings_delay (applet->gsettings); + g_settings_set_string (applet->gsettings, "device", applet->devinfo.name); + g_settings_set_boolean (applet->gsettings, "show-sum", applet->show_sum); + g_settings_set_boolean (applet->gsettings, "show-bits", applet->show_bits); + g_settings_set_boolean (applet->gsettings, "short-unit", applet->short_unit); + g_settings_set_boolean (applet->gsettings, "show-icon", applet->show_icon); + g_settings_set_boolean (applet->gsettings, "show-quality-icon", applet->show_quality_icon); + g_settings_set_boolean (applet->gsettings, "change-icon", applet->change_icon); + g_settings_set_boolean (applet->gsettings, "auto-change-device", applet->auto_change_device); + g_settings_apply (applet->gsettings); + + gtk_widget_destroy(GTK_WIDGET(applet->settings)); + applet->settings = NULL; +} + +/* Called when the showsum checkbutton is toggled... + */ +static void +showsum_change_cb(GtkToggleButton *togglebutton, MateNetspeedApplet *applet) +{ + applet->show_sum = gtk_toggle_button_get_active(togglebutton); + applet_change_size_or_orient(applet->applet, -1, (gpointer)applet); + change_icons(applet); +} + +/* Called when the showbits checkbutton is toggled... + */ +static void +showbits_change_cb(GtkToggleButton *togglebutton, MateNetspeedApplet *applet) +{ + applet->show_bits = gtk_toggle_button_get_active(togglebutton); +} + +/* Called when the shortunit checkbutton is toggled... + */ +static void +shortunit_change_cb(GtkToggleButton *togglebutton, MateNetspeedApplet *applet) +{ + applet->short_unit = gtk_toggle_button_get_active(togglebutton); +} + +/* Called when the showicon checkbutton is toggled... + */ +static void +showicon_change_cb(GtkToggleButton *togglebutton, MateNetspeedApplet *applet) +{ + applet->show_icon = gtk_toggle_button_get_active(togglebutton); + change_icons(applet); +} + +/* Called when the showqualityicon checkbutton is toggled... + */ +static void +showqualityicon_change_cb(GtkToggleButton *togglebutton, MateNetspeedApplet *applet) +{ + applet->show_quality_icon = gtk_toggle_button_get_active(togglebutton); + change_quality_icon(applet); +} + +/* Called when the changeicon checkbutton is toggled... + */ +static void +changeicon_change_cb(GtkToggleButton *togglebutton, MateNetspeedApplet *applet) +{ + applet->change_icon = gtk_toggle_button_get_active(togglebutton); + change_icons(applet); +} + +/* Creates the settings dialog + * After its been closed, take the new values and store + * them in the gsettings database + */ +static void +settings_cb(GtkAction *action, gpointer data) +{ + MateNetspeedApplet *applet = (MateNetspeedApplet*)data; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *categories_vbox; + GtkWidget *category_vbox; + GtkWidget *controls_vbox; + GtkWidget *category_header_label; + GtkWidget *network_device_hbox; + GtkWidget *network_device_label; + GtkWidget *indent_label; + GtkWidget *show_sum_checkbutton; + GtkWidget *show_bits_checkbutton; + GtkWidget *short_unit_checkbutton; + GtkWidget *show_icon_checkbutton; + GtkWidget *show_quality_icon_checkbutton; + GtkWidget *change_icon_checkbutton; + GtkSizeGroup *category_label_size_group; + gchar *header_str; + GList *ptr, *devices; + int i, active = -1; + + g_assert(applet); + + if (applet->settings) + { + gtk_window_present(GTK_WINDOW(applet->settings)); + return; + } + + category_label_size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + + applet->settings = GTK_DIALOG(gtk_dialog_new_with_buttons(_("Mate Netspeed Preferences"), + NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_HELP, GTK_RESPONSE_HELP, + GTK_STOCK_CLOSE, GTK_RESPONSE_ACCEPT, + NULL)); + + gtk_window_set_resizable(GTK_WINDOW(applet->settings), FALSE); + gtk_window_set_screen(GTK_WINDOW(applet->settings), + gtk_widget_get_screen(GTK_WIDGET(applet->settings))); + + gtk_dialog_set_default_response(GTK_DIALOG(applet->settings), GTK_RESPONSE_CLOSE); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 12); + + categories_vbox = gtk_vbox_new(FALSE, 18); + gtk_box_pack_start(GTK_BOX (vbox), categories_vbox, TRUE, TRUE, 0); + + category_vbox = gtk_vbox_new(FALSE, 6); + gtk_box_pack_start(GTK_BOX (categories_vbox), category_vbox, TRUE, TRUE, 0); + + header_str = g_strconcat("<span weight=\"bold\">", _("General Settings"), "</span>", NULL); + category_header_label = gtk_label_new(header_str); + gtk_label_set_use_markup(GTK_LABEL(category_header_label), TRUE); + gtk_label_set_justify(GTK_LABEL(category_header_label), GTK_JUSTIFY_LEFT); +#if GTK_CHECK_VERSION (3, 0, 0) + gtk_widget_set_halign (category_header_label, GTK_ALIGN_START); + gtk_widget_set_valign (category_header_label, GTK_ALIGN_CENTER); +#else + gtk_misc_set_alignment(GTK_MISC (category_header_label), 0, 0.5); +#endif + gtk_box_pack_start(GTK_BOX (category_vbox), category_header_label, FALSE, FALSE, 0); + g_free(header_str); + + hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX (category_vbox), hbox, TRUE, TRUE, 0); + + indent_label = gtk_label_new(" "); + gtk_label_set_justify(GTK_LABEL (indent_label), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX (hbox), indent_label, FALSE, FALSE, 0); + + controls_vbox = gtk_vbox_new(FALSE, 10); + gtk_box_pack_start(GTK_BOX(hbox), controls_vbox, TRUE, TRUE, 0); + + network_device_hbox = gtk_hbox_new(FALSE, 6); + gtk_box_pack_start(GTK_BOX(controls_vbox), network_device_hbox, TRUE, TRUE, 0); + + network_device_label = gtk_label_new_with_mnemonic(_("Network _device:")); + gtk_label_set_justify(GTK_LABEL(network_device_label), GTK_JUSTIFY_LEFT); +#if GTK_CHECK_VERSION (3, 0, 0) + gtk_widget_set_halign (network_device_label, GTK_ALIGN_START); + gtk_widget_set_valign (network_device_label, GTK_ALIGN_CENTER); +#else + gtk_misc_set_alignment(GTK_MISC(network_device_label), 0.0f, 0.5f); +#endif + gtk_size_group_add_widget(category_label_size_group, network_device_label); + gtk_box_pack_start(GTK_BOX (network_device_hbox), network_device_label, FALSE, FALSE, 0); + + applet->network_device_combo = gtk_combo_box_text_new(); + gtk_label_set_mnemonic_widget(GTK_LABEL(network_device_label), applet->network_device_combo); + gtk_box_pack_start (GTK_BOX (network_device_hbox), applet->network_device_combo, TRUE, TRUE, 0); + + /* Default means device with default route set */ + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(applet->network_device_combo), _("Default")); + ptr = devices = get_available_devices(); + for (i = 0; ptr; ptr = g_list_next(ptr)) { + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(applet->network_device_combo), ptr->data); + if (g_str_equal(ptr->data, applet->devinfo.name)) active = (i + 1); + ++i; + } + if (active < 0 || applet->auto_change_device) { + active = 0; + } + gtk_combo_box_set_active(GTK_COMBO_BOX(applet->network_device_combo), active); + g_object_set_data_full(G_OBJECT(applet->network_device_combo), "devices", devices, (GDestroyNotify)free_devices_list); + + show_sum_checkbutton = gtk_check_button_new_with_mnemonic(_("Show _sum instead of in & out")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(show_sum_checkbutton), applet->show_sum); + gtk_box_pack_start(GTK_BOX(controls_vbox), show_sum_checkbutton, FALSE, FALSE, 0); + + show_bits_checkbutton = gtk_check_button_new_with_mnemonic(_("Show _bits instead of bytes")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(show_bits_checkbutton), applet->show_bits); + gtk_box_pack_start(GTK_BOX(controls_vbox), show_bits_checkbutton, FALSE, FALSE, 0); + + short_unit_checkbutton = gtk_check_button_new_with_mnemonic(_("Shorten _unit legend")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(short_unit_checkbutton), applet->short_unit); + gtk_box_pack_start(GTK_BOX(controls_vbox), short_unit_checkbutton, FALSE, FALSE, 0); + + change_icon_checkbutton = gtk_check_button_new_with_mnemonic(_("_Change icon according to the selected device")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(change_icon_checkbutton), applet->change_icon); + gtk_box_pack_start(GTK_BOX(controls_vbox), change_icon_checkbutton, FALSE, FALSE, 0); + + show_icon_checkbutton = gtk_check_button_new_with_mnemonic(_("Show _icon")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(show_icon_checkbutton), applet->show_icon); + gtk_box_pack_start(GTK_BOX(controls_vbox), show_icon_checkbutton, FALSE, FALSE, 0); + + show_quality_icon_checkbutton = gtk_check_button_new_with_mnemonic(_("Show signal _quality icon for wireless devices")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(show_quality_icon_checkbutton), applet->show_quality_icon); + gtk_box_pack_start(GTK_BOX(controls_vbox), show_quality_icon_checkbutton, FALSE, FALSE, 0); + + g_signal_connect(G_OBJECT (applet->network_device_combo), "changed", + G_CALLBACK(device_change_cb), (gpointer)applet); + + g_signal_connect(G_OBJECT (show_sum_checkbutton), "toggled", + G_CALLBACK(showsum_change_cb), (gpointer)applet); + + g_signal_connect(G_OBJECT (show_bits_checkbutton), "toggled", + G_CALLBACK(showbits_change_cb), (gpointer)applet); + + g_signal_connect(G_OBJECT (short_unit_checkbutton), "toggled", + G_CALLBACK(shortunit_change_cb), (gpointer)applet); + + g_signal_connect(G_OBJECT (show_icon_checkbutton), "toggled", + G_CALLBACK(showicon_change_cb), (gpointer)applet); + + g_signal_connect(G_OBJECT (show_quality_icon_checkbutton), "toggled", + G_CALLBACK(showqualityicon_change_cb), (gpointer)applet); + + g_signal_connect(G_OBJECT (change_icon_checkbutton), "toggled", + G_CALLBACK(changeicon_change_cb), (gpointer)applet); + + g_signal_connect(G_OBJECT (applet->settings), "response", + G_CALLBACK(pref_response_cb), (gpointer)applet); + + gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area (applet->settings)), vbox); + + gtk_widget_show_all(GTK_WIDGET(applet->settings)); +} + +static gboolean +#if GTK_CHECK_VERSION (3, 0, 0) +da_draw(GtkWidget *widget, cairo_t *cr, gpointer data) +#else +da_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data) +#endif +{ + MateNetspeedApplet *applet = (MateNetspeedApplet*)data; + +#if !GTK_CHECK_VERSION (3, 0, 0) + cairo_t *cr; + cr = gdk_cairo_create (event->window); + gdk_cairo_region (cr, event->region); + cairo_clip (cr); +#endif + redraw_graph(applet, cr); +#if !GTK_CHECK_VERSION (3, 0, 0) + cairo_destroy (cr); +#endif + + return FALSE; +} + +static void +incolor_changed_cb (MateColorButton *cb, gpointer data) +{ + MateNetspeedApplet *applet = (MateNetspeedApplet*)data; + gchar *color; + GdkColor clr; + + mate_color_button_get_color (cb, &clr); + applet->in_color = clr; + + color = g_strdup_printf ("#%04x%04x%04x", clr.red, clr.green, clr.blue); + g_settings_set_string (applet->gsettings, "in-color", color); + g_free (color); +} + +static void +outcolor_changed_cb (MateColorButton *cb, gpointer data) +{ + MateNetspeedApplet *applet = (MateNetspeedApplet*)data; + gchar *color; + GdkColor clr; + + mate_color_button_get_color (cb, &clr); + applet->out_color = clr; + + color = g_strdup_printf ("#%04x%04x%04x", clr.red, clr.green, clr.blue); + g_settings_set_string (applet->gsettings, "out-color", color); + g_free (color); +} + +/* Handle info dialog response event + */ +static void +info_response_cb (GtkDialog *dialog, gint id, MateNetspeedApplet *applet) +{ + + if(id == GTK_RESPONSE_HELP){ + display_help (GTK_WIDGET (dialog), "netspeed_applet-details"); + return; + } + + gtk_widget_destroy(GTK_WIDGET(applet->details)); + + applet->details = NULL; + applet->inbytes_text = NULL; + applet->outbytes_text = NULL; + applet->drawingarea = NULL; + applet->signalbar = NULL; +} + +/* Creates the details dialog + */ +static void +showinfo_cb(GtkAction *action, gpointer data) +{ + MateNetspeedApplet *applet = (MateNetspeedApplet*)data; + GtkWidget *box, *hbox; + GtkWidget *table, *da_frame; + GtkWidget *ip_label, *netmask_label; + GtkWidget *hwaddr_label, *ptpip_label; + GtkWidget *ip_text, *netmask_text; + GtkWidget *hwaddr_text, *ptpip_text; + GtkWidget *inbytes_label, *outbytes_label; + GtkWidget *incolor_sel, *incolor_label; + GtkWidget *outcolor_sel, *outcolor_label; + char *title; + + g_assert(applet); + + if (applet->details) + { + gtk_window_present(GTK_WINDOW(applet->details)); + return; + } + + title = g_strdup_printf(_("Device Details for %s"), applet->devinfo.name); + applet->details = GTK_DIALOG(gtk_dialog_new_with_buttons(title, + NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CLOSE, GTK_RESPONSE_ACCEPT, + GTK_STOCK_HELP, GTK_RESPONSE_HELP, + NULL)); + g_free(title); + + gtk_dialog_set_default_response(GTK_DIALOG(applet->details), GTK_RESPONSE_CLOSE); + + box = gtk_vbox_new(FALSE, 10); + gtk_container_set_border_width(GTK_CONTAINER(box), 12); + + table = gtk_table_new(4, 4, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 10); + gtk_table_set_col_spacings(GTK_TABLE(table), 15); + + da_frame = gtk_frame_new(NULL); + gtk_frame_set_shadow_type(GTK_FRAME(da_frame), GTK_SHADOW_NONE); + applet->drawingarea = GTK_DRAWING_AREA(gtk_drawing_area_new()); + gtk_widget_set_size_request(GTK_WIDGET(applet->drawingarea), -1, 180); + gtk_container_add(GTK_CONTAINER(da_frame), GTK_WIDGET(applet->drawingarea)); + + hbox = gtk_hbox_new(FALSE, 5); + incolor_label = gtk_label_new_with_mnemonic(_("_In graph color")); + outcolor_label = gtk_label_new_with_mnemonic(_("_Out graph color")); + + incolor_sel = mate_color_button_new (); + outcolor_sel = mate_color_button_new (); + + mate_color_button_set_color (MATE_COLOR_BUTTON (incolor_sel), &applet->in_color); + mate_color_button_set_color (MATE_COLOR_BUTTON (outcolor_sel), &applet->out_color); + + gtk_label_set_mnemonic_widget(GTK_LABEL(incolor_label), incolor_sel); + gtk_label_set_mnemonic_widget(GTK_LABEL(outcolor_label), outcolor_sel); + + gtk_box_pack_start(GTK_BOX(hbox), incolor_sel, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), incolor_label, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), outcolor_sel, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), outcolor_label, FALSE, FALSE, 0); + + ip_label = gtk_label_new(_("Internet Address:")); + netmask_label = gtk_label_new(_("Netmask:")); + hwaddr_label = gtk_label_new(_("Hardware Address:")); + ptpip_label = gtk_label_new(_("P-t-P Address:")); + inbytes_label = gtk_label_new(_("Bytes in:")); + outbytes_label = gtk_label_new(_("Bytes out:")); + + ip_text = gtk_label_new(applet->devinfo.ip ? applet->devinfo.ip : _("none")); + netmask_text = gtk_label_new(applet->devinfo.netmask ? applet->devinfo.netmask : _("none")); + hwaddr_text = gtk_label_new(applet->devinfo.hwaddr ? applet->devinfo.hwaddr : _("none")); + ptpip_text = gtk_label_new(applet->devinfo.ptpip ? applet->devinfo.ptpip : _("none")); + applet->inbytes_text = gtk_label_new("0 byte"); + applet->outbytes_text = gtk_label_new("0 byte"); + + gtk_label_set_selectable(GTK_LABEL(ip_text), TRUE); + gtk_label_set_selectable(GTK_LABEL(netmask_text), TRUE); + gtk_label_set_selectable(GTK_LABEL(hwaddr_text), TRUE); + gtk_label_set_selectable(GTK_LABEL(ptpip_text), TRUE); + +#if GTK_CHECK_VERSION (3, 0, 0) + gtk_widget_set_halign (ip_label, GTK_ALIGN_START); + gtk_widget_set_valign (ip_label, GTK_ALIGN_CENTER); + gtk_widget_set_halign (ip_text, GTK_ALIGN_START); + gtk_widget_set_valign (ip_text, GTK_ALIGN_CENTER); + gtk_widget_set_halign (netmask_label, GTK_ALIGN_START); + gtk_widget_set_valign (netmask_label, GTK_ALIGN_CENTER); + gtk_widget_set_halign (netmask_text, GTK_ALIGN_START); + gtk_widget_set_valign (netmask_text, GTK_ALIGN_CENTER); + gtk_widget_set_halign (hwaddr_label, GTK_ALIGN_START); + gtk_widget_set_valign (hwaddr_label, GTK_ALIGN_CENTER); + gtk_widget_set_halign (hwaddr_text, GTK_ALIGN_START); + gtk_widget_set_valign (hwaddr_text, GTK_ALIGN_CENTER); + gtk_widget_set_halign (ptpip_label, GTK_ALIGN_START); + gtk_widget_set_valign (ptpip_label, GTK_ALIGN_CENTER); + gtk_widget_set_halign (ptpip_text, GTK_ALIGN_START); + gtk_widget_set_valign (ptpip_text, GTK_ALIGN_CENTER); + gtk_widget_set_halign (inbytes_label, GTK_ALIGN_START); + gtk_widget_set_valign (inbytes_label, GTK_ALIGN_CENTER); + gtk_widget_set_halign (applet->inbytes_text, GTK_ALIGN_START); + gtk_widget_set_valign (applet->inbytes_text, GTK_ALIGN_CENTER); + gtk_widget_set_halign (inbytes_label, GTK_ALIGN_START); + gtk_widget_set_valign (inbytes_label, GTK_ALIGN_CENTER); + gtk_widget_set_halign (applet->outbytes_text, GTK_ALIGN_START); + gtk_widget_set_valign (applet->outbytes_text, GTK_ALIGN_CENTER); + gtk_widget_set_halign (outbytes_label, GTK_ALIGN_START); + gtk_widget_set_valign (outbytes_label, GTK_ALIGN_CENTER); +#else + gtk_misc_set_alignment(GTK_MISC(ip_label), 0.0f, 0.5f); + gtk_misc_set_alignment(GTK_MISC(ip_text), 0.0f, 0.5f); + gtk_misc_set_alignment(GTK_MISC(netmask_label), 0.0f, 0.5f); + gtk_misc_set_alignment(GTK_MISC(netmask_text), 0.0f, 0.5f); + gtk_misc_set_alignment(GTK_MISC(hwaddr_label), 0.0f, 0.5f); + gtk_misc_set_alignment(GTK_MISC(hwaddr_text), 0.0f, 0.5f); + gtk_misc_set_alignment(GTK_MISC(ptpip_label), 0.0f, 0.5f); + gtk_misc_set_alignment(GTK_MISC(ptpip_text), 0.0f, 0.5f); + gtk_misc_set_alignment(GTK_MISC(inbytes_label), 0.0f, 0.5f); + gtk_misc_set_alignment(GTK_MISC(applet->inbytes_text), 0.0f, 0.5f); + gtk_misc_set_alignment(GTK_MISC(outbytes_label), 0.0f, 0.5f); + gtk_misc_set_alignment(GTK_MISC(applet->outbytes_text), 0.0f, 0.5f); +#endif + + gtk_table_attach_defaults(GTK_TABLE(table), ip_label, 0, 1, 0, 1); + gtk_table_attach_defaults(GTK_TABLE(table), ip_text, 1, 2, 0, 1); + gtk_table_attach_defaults(GTK_TABLE(table), netmask_label, 2, 3, 0, 1); + gtk_table_attach_defaults(GTK_TABLE(table), netmask_text, 3, 4, 0, 1); + gtk_table_attach_defaults(GTK_TABLE(table), hwaddr_label, 0, 1, 1, 2); + gtk_table_attach_defaults(GTK_TABLE(table), hwaddr_text, 1, 2, 1, 2); + gtk_table_attach_defaults(GTK_TABLE(table), ptpip_label, 2, 3, 1, 2); + gtk_table_attach_defaults(GTK_TABLE(table), ptpip_text, 3, 4, 1, 2); + gtk_table_attach_defaults(GTK_TABLE(table), inbytes_label, 0, 1, 2, 3); + gtk_table_attach_defaults(GTK_TABLE(table), applet->inbytes_text, 1, 2, 2, 3); + gtk_table_attach_defaults(GTK_TABLE(table), outbytes_label, 2, 3, 2, 3); + gtk_table_attach_defaults(GTK_TABLE(table), applet->outbytes_text, 3, 4, 2, 3); + + /* check if we got an ipv6 address */ + if (applet->devinfo.ipv6 && (strlen (applet->devinfo.ipv6) > 2)) { + GtkWidget *ipv6_label, *ipv6_text; + + ipv6_label = gtk_label_new (_("IPV6 Address:")); + ipv6_text = gtk_label_new (applet->devinfo.ipv6); + + gtk_label_set_selectable (GTK_LABEL (ipv6_text), TRUE); + +#if GTK_CHECK_VERSION (3, 0, 0) + gtk_widget_set_halign (ipv6_label, GTK_ALIGN_START); + gtk_widget_set_valign (ipv6_label, GTK_ALIGN_CENTER); + gtk_widget_set_halign (ipv6_text, GTK_ALIGN_START); + gtk_widget_set_valign (ipv6_text, GTK_ALIGN_CENTER); +#else + gtk_misc_set_alignment (GTK_MISC (ipv6_label), 0.0f, 0.5f); + gtk_misc_set_alignment (GTK_MISC (ipv6_text), 0.0f, 0.5f); +#endif + + gtk_table_attach_defaults (GTK_TABLE (table), ipv6_label, 0, 1, 3, 4); + gtk_table_attach_defaults (GTK_TABLE (table), ipv6_text, 1, 2, 3, 4); + } + + if (applet->devinfo.type == DEV_WIRELESS) { + GtkWidget *signal_label; + GtkWidget *essid_label; + GtkWidget *essid_text; + float quality; + char *text; + + /* _maybe_ we can add the encrypted icon between the essid and the signal bar. */ + + applet->signalbar = gtk_progress_bar_new (); + + quality = applet->devinfo.qual / 100.0f; + if (quality > 1.0) + quality = 1.0; + + text = g_strdup_printf ("%d %%", applet->devinfo.qual); + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (applet->signalbar), quality); + gtk_progress_bar_set_text (GTK_PROGRESS_BAR (applet->signalbar), text); + g_free(text); + + signal_label = gtk_label_new (_("Signal Strength:")); + essid_label = gtk_label_new (_("ESSID:")); + essid_text = gtk_label_new (applet->devinfo.essid); + +#if GTK_CHECK_VERSION (3, 0, 0) + gtk_widget_set_halign (signal_label, GTK_ALIGN_START); + gtk_widget_set_valign (signal_label, GTK_ALIGN_CENTER); + gtk_widget_set_halign (essid_label, GTK_ALIGN_START); + gtk_widget_set_valign (essid_label, GTK_ALIGN_CENTER); + gtk_widget_set_halign (essid_text, GTK_ALIGN_START); + gtk_widget_set_valign (essid_text, GTK_ALIGN_CENTER); +#else + gtk_misc_set_alignment (GTK_MISC (signal_label), 0.0f, 0.5f); + gtk_misc_set_alignment (GTK_MISC (essid_label), 0.0f, 0.5f); + gtk_misc_set_alignment (GTK_MISC (essid_text), 0.0f, 0.5f); +#endif + + gtk_label_set_selectable (GTK_LABEL (essid_text), TRUE); + + gtk_table_attach_defaults (GTK_TABLE (table), signal_label, 2, 3, 4, 5); + gtk_table_attach_defaults (GTK_TABLE (table), GTK_WIDGET (applet->signalbar), 3, 4, 4, 5); + gtk_table_attach_defaults (GTK_TABLE (table), essid_label, 0, 3, 4, 5); + gtk_table_attach_defaults (GTK_TABLE (table), essid_text, 1, 4, 4, 5); + } + +#if GTK_CHECK_VERSION (3, 0, 0) + g_signal_connect(G_OBJECT(applet->drawingarea), "draw", + G_CALLBACK(da_draw), + (gpointer)applet); +#else + g_signal_connect(G_OBJECT(applet->drawingarea), "expose_event", + G_CALLBACK(da_expose_event), + (gpointer)applet); +#endif + + g_signal_connect(G_OBJECT(incolor_sel), "color_set", + G_CALLBACK(incolor_changed_cb), + (gpointer)applet); + + g_signal_connect(G_OBJECT(outcolor_sel), "color_set", + G_CALLBACK(outcolor_changed_cb), + (gpointer)applet); + + g_signal_connect(G_OBJECT(applet->details), "response", + G_CALLBACK(info_response_cb), (gpointer)applet); + + gtk_box_pack_start(GTK_BOX(box), da_frame, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(box), table, FALSE, FALSE, 0); + + gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area (applet->details)), box); + gtk_widget_show_all(GTK_WIDGET(applet->details)); +} + +static const GtkActionEntry mate_netspeed_applet_menu_actions [] = { + { "MateNetspeedAppletDetails", GTK_STOCK_INFO, N_("Device _Details"), + NULL, NULL, G_CALLBACK (showinfo_cb) }, + { "MateNetspeedAppletProperties", GTK_STOCK_PROPERTIES, N_("Preferences..."), + NULL, NULL, G_CALLBACK (settings_cb) }, + { "MateNetspeedAppletHelp", GTK_STOCK_HELP, N_("Help"), + NULL, NULL, G_CALLBACK (help_cb) }, + { "MateNetspeedAppletAbout", GTK_STOCK_ABOUT, N_("About..."), + NULL, NULL, G_CALLBACK (about_cb) } +}; + +/* Block the size_request signal emit by the label if the + * text changes. Only if the label wants to grow, we give + * permission. This will eventually result in the maximal + * size of the applet and prevents the icons and labels from + * "jumping around" in the mate_panel which looks uggly + */ +static void +label_size_request_cb(GtkWidget *widget, GtkRequisition *requisition, MateNetspeedApplet *applet) +{ + if (applet->labels_dont_shrink) { + if (requisition->width <= applet->width) + requisition->width = applet->width; + else + applet->width = requisition->width; + } +} + +static gboolean +applet_button_press(GtkWidget *widget, GdkEventButton *event, MateNetspeedApplet *applet) +{ + if (event->button == 1) + { + GError *error = NULL; + + if (applet->connect_dialog) + { + gtk_window_present(GTK_WINDOW(applet->connect_dialog)); + return FALSE; + } + + if (applet->up_cmd && applet->down_cmd) + { + const char *question; + int response; + + if (applet->devinfo.up) + { + question = _("Do you want to disconnect %s now?"); + } + else + { + question = _("Do you want to connect %s now?"); + } + + applet->connect_dialog = gtk_message_dialog_new(NULL, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, + question, + applet->devinfo.name); + response = gtk_dialog_run(GTK_DIALOG(applet->connect_dialog)); + gtk_widget_destroy (applet->connect_dialog); + applet->connect_dialog = NULL; + + if (response == GTK_RESPONSE_YES) + { + GtkWidget *dialog; + char *command; + + command = g_strdup_printf("%s %s", + applet->devinfo.up ? applet->down_cmd : applet->up_cmd, + applet->devinfo.name); + + if (!g_spawn_command_line_async(command, &error)) + { + + dialog = gtk_message_dialog_new_with_markup(NULL, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, + _("<b>Running command %s failed</b>\n%s"), + command, + error->message); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + g_error_free (error); + } + g_free(command); + } + } + } + + return FALSE; +} + +/* Frees the applet and all the data it contains + * Removes the timeout_cb + */ +static void +applet_destroy(MatePanelApplet *applet_widget, MateNetspeedApplet *applet) +{ + GtkIconTheme *icon_theme; + + g_assert(applet); + + icon_theme = gtk_icon_theme_get_default(); + g_object_disconnect(G_OBJECT(icon_theme), "changed", + G_CALLBACK(icon_theme_changed_cb), (gpointer)applet, + NULL); + + g_source_remove(applet->timeout_id); + applet->timeout_id = 0; + + if (applet->up_cmd) + g_free(applet->up_cmd); + if (applet->down_cmd) + g_free(applet->down_cmd); + if (applet->gsettings) + g_object_unref (applet->gsettings); + + /* Should never be NULL */ + free_device_info(&applet->devinfo); + g_free(applet); + return; +} + + + +static void +update_tooltip(MateNetspeedApplet* applet) +{ + GString* tooltip; + + if (!applet->show_tooltip) + return; + + tooltip = g_string_new(""); + + if (!applet->devinfo.running) + g_string_printf(tooltip, _("%s is down"), applet->devinfo.name); + else { + if (applet->show_sum) { + g_string_printf( + tooltip, + _("%s: %s\nin: %s out: %s"), + applet->devinfo.name, + applet->devinfo.ip ? applet->devinfo.ip : _("has no ip"), + applet->devinfo.rx_rate, + applet->devinfo.tx_rate + ); + } else { + g_string_printf( + tooltip, + _("%s: %s\nsum: %s"), + applet->devinfo.name, + applet->devinfo.ip ? applet->devinfo.ip : _("has no ip"), + applet->devinfo.sum_rate + ); + } + if (applet->devinfo.type == DEV_WIRELESS) + g_string_append_printf( + tooltip, + _("\nESSID: %s\nStrength: %d %%"), + applet->devinfo.essid ? applet->devinfo.essid : _("unknown"), + applet->devinfo.qual + ); + + } + + gtk_widget_set_tooltip_text(GTK_WIDGET(applet->applet), tooltip->str); + gtk_widget_trigger_tooltip_query(GTK_WIDGET(applet->applet)); + g_string_free(tooltip, TRUE); +} + + +static gboolean +mate_netspeed_enter_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer data) +{ + MateNetspeedApplet *applet = data; + + applet->show_tooltip = TRUE; + update_tooltip(applet); + + return TRUE; +} + +static gboolean +mate_netspeed_leave_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer data) +{ + MateNetspeedApplet *applet = data; + + applet->show_tooltip = FALSE; + return TRUE; +} + +/* The "main" function of the applet + */ +static gboolean +mate_netspeed_applet_factory(MatePanelApplet *applet_widget, const gchar *iid, gpointer data) +{ + MateNetspeedApplet *applet; + int i; + char* menu_string; + GtkIconTheme *icon_theme; + GtkWidget *spacer, *spacer_box; + + /* Have our background automatically painted. */ + mate_panel_applet_set_background_widget(MATE_PANEL_APPLET(applet_widget), + GTK_WIDGET(applet_widget)); + + if (strcmp (iid, "NetspeedApplet")) + return FALSE; + + glibtop_init(); + g_set_application_name (_("Mate Netspeed")); + + icon_theme = gtk_icon_theme_get_default(); + + /* Alloc the applet. The "NULL-setting" is really redudant + * but aren't we paranoid? + */ + applet = g_malloc0(sizeof(MateNetspeedApplet)); + applet->applet = applet_widget; + memset(&applet->devinfo, 0, sizeof(DevInfo)); + applet->refresh_time = 1000.0; + applet->show_sum = FALSE; + applet->show_bits = FALSE; + applet->short_unit = FALSE; + applet->show_icon = TRUE; + applet->show_quality_icon = TRUE; + applet->change_icon = TRUE; + applet->auto_change_device = TRUE; + + /* Set the default colors of the graph + */ + applet->in_color.red = 0xdf00; + applet->in_color.green = 0x2800; + applet->in_color.blue = 0x4700; + applet->out_color.red = 0x3700; + applet->out_color.green = 0x2800; + applet->out_color.blue = 0xdf00; + + for (i = 0; i < GRAPH_VALUES; i++) + { + applet->in_graph[i] = -1; + applet->out_graph[i] = -1; + } + + applet->gsettings = mate_panel_applet_settings_new (applet_widget, "org.mate.panel.applet.netspeed"); + + /* Get stored settings from gsettings + */ + char *tmp = NULL; + + applet->show_sum = g_settings_get_boolean (applet->gsettings, "show-sum"); + applet->show_bits = g_settings_get_boolean (applet->gsettings, "show-bits"); + applet->short_unit = g_settings_get_boolean (applet->gsettings, "short-unit"); + applet->show_icon = g_settings_get_boolean (applet->gsettings, "show-icon"); + applet->show_quality_icon = g_settings_get_boolean (applet->gsettings, "show-quality-icon"); + applet->change_icon = g_settings_get_boolean (applet->gsettings, "change-icon"); + applet->auto_change_device = g_settings_get_boolean (applet->gsettings, "auto-change-device"); + + tmp = g_settings_get_string (applet->gsettings, "device"); + if (tmp && strcmp(tmp, "")) + { + get_device_info(tmp, &applet->devinfo); + g_free(tmp); + } + tmp = g_settings_get_string (applet->gsettings, "up-command"); + if (tmp && strcmp(tmp, "")) + { + applet->up_cmd = g_strdup(tmp); + g_free(tmp); + } + tmp = g_settings_get_string (applet->gsettings, "down-command"); + if (tmp && strcmp(tmp, "")) + { + applet->down_cmd = g_strdup(tmp); + g_free(tmp); + } + + tmp = g_settings_get_string (applet->gsettings, "in-color"); + if (tmp) + { + gdk_color_parse(tmp, &applet->in_color); + g_free(tmp); + } + tmp = g_settings_get_string (applet->gsettings, "out-color"); + if (tmp) + { + gdk_color_parse(tmp, &applet->out_color); + g_free(tmp); + } + + if (!applet->devinfo.name) { + GList *ptr, *devices = get_available_devices(); + ptr = devices; + while (ptr) { + if (!g_str_equal(ptr->data, "lo")) { + get_device_info(ptr->data, &applet->devinfo); + break; + } + ptr = g_list_next(ptr); + } + free_devices_list(devices); + } + if (!applet->devinfo.name) + get_device_info("lo", &applet->devinfo); + applet->device_has_changed = TRUE; + + applet->in_label = gtk_label_new(""); + applet->out_label = gtk_label_new(""); + applet->sum_label = gtk_label_new(""); + + applet->in_pix = gtk_image_new(); + applet->out_pix = gtk_image_new(); + applet->dev_pix = gtk_image_new(); + applet->qual_pix = gtk_image_new(); + + applet->pix_box = gtk_hbox_new(FALSE, 0); + spacer = gtk_label_new(""); + gtk_box_pack_start(GTK_BOX(applet->pix_box), spacer, TRUE, TRUE, 0); + spacer = gtk_label_new(""); + gtk_box_pack_end(GTK_BOX(applet->pix_box), spacer, TRUE, TRUE, 0); + + spacer_box = gtk_hbox_new(FALSE, 2); + gtk_box_pack_start(GTK_BOX(applet->pix_box), spacer_box, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(spacer_box), applet->qual_pix, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(spacer_box), applet->dev_pix, FALSE, FALSE, 0); + + init_quality_pixbufs(applet); + + applet_change_size_or_orient(applet_widget, -1, (gpointer)applet); + gtk_widget_show_all(GTK_WIDGET(applet_widget)); + update_applet(applet); + + mate_panel_applet_set_flags(applet_widget, MATE_PANEL_APPLET_EXPAND_MINOR); + + applet->timeout_id = g_timeout_add(applet->refresh_time, + (GSourceFunc)timeout_function, + (gpointer)applet); + + g_signal_connect(G_OBJECT(applet_widget), "change_size", + G_CALLBACK(applet_change_size_or_orient), + (gpointer)applet); + + g_signal_connect(G_OBJECT(icon_theme), "changed", + G_CALLBACK(icon_theme_changed_cb), + (gpointer)applet); + + g_signal_connect(G_OBJECT(applet_widget), "change_orient", + G_CALLBACK(applet_change_size_or_orient), + (gpointer)applet); + + g_signal_connect(G_OBJECT(applet->in_label), "size_request", + G_CALLBACK(label_size_request_cb), + (gpointer)applet); + + g_signal_connect(G_OBJECT(applet->out_label), "size_request", + G_CALLBACK(label_size_request_cb), + (gpointer)applet); + + g_signal_connect(G_OBJECT(applet->sum_label), "size_request", + G_CALLBACK(label_size_request_cb), + (gpointer)applet); + + g_signal_connect(G_OBJECT(applet_widget), "destroy", + G_CALLBACK(applet_destroy), + (gpointer)applet); + + g_signal_connect(G_OBJECT(applet_widget), "button-press-event", + G_CALLBACK(applet_button_press), + (gpointer)applet); + + g_signal_connect(G_OBJECT(applet_widget), "leave_notify_event", + G_CALLBACK(mate_netspeed_leave_cb), + (gpointer)applet); + + g_signal_connect(G_OBJECT(applet_widget), "enter_notify_event", + G_CALLBACK(mate_netspeed_enter_cb), + (gpointer)applet); + + GtkActionGroup *action_group; + gchar *ui_path; + action_group = gtk_action_group_new ("Netspeed Applet Actions"); + gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE); + gtk_action_group_add_actions (action_group, + mate_netspeed_applet_menu_actions, + G_N_ELEMENTS (mate_netspeed_applet_menu_actions), + applet); + ui_path = g_build_filename (NETSPEED_MENU_UI_DIR, "netspeed-menu.xml", NULL); + mate_panel_applet_setup_menu_from_file (MATE_PANEL_APPLET (applet->applet), ui_path, action_group); + g_free (ui_path); + g_object_unref (action_group); + + return TRUE; +} + +MATE_PANEL_APPLET_OUT_PROCESS_FACTORY("NetspeedAppletFactory", + PANEL_TYPE_APPLET, + "NetspeedApplet", + mate_netspeed_applet_factory, + NULL) |