--- /dev/null
+/*
+ *
+ * GtkGps: Display GPS information
+ * Copyright (C) 2007 Kaj-Michael Lang
+ * Originanl non-widget version Copyright Cezary Jackiewicz
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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 Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <math.h>
+
+#include <glib/gstdio.h>
+#include <glib-object.h>
+#include "gtkgps.h"
+
+static void gtk_gps_finalize (GObject *object);
+static void gtk_gps_size_request (GtkWidget *widget, GtkRequisition *requisition);
+static void gtk_gps_size_allocate (GtkWidget *widget, GtkAllocation *allocate);
+static gint gtk_gps_expose (GtkWidget *widget, GdkEventExpose *event);
+static void gtk_gps_realize (GtkWidget *widget);
+static void gtk_gps_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
+static void gtk_gps_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
+static gint gtk_gps_paint(GtkGps *gps);
+
+G_DEFINE_TYPE(GtkGps, gtk_gps, GTK_TYPE_WIDGET);
+
+#define PADDING (15)
+
+#define BOUND(x, a, b) { \
+ if((x) < (a)) \
+ (x) = (a); \
+ else if((x) > (b)) \
+ (x) = (b); \
+}
+
+static void
+gtk_gps_class_init (GtkGpsClass *class)
+{
+GObjectClass *object_class;
+GtkWidgetClass *widget_class;
+
+object_class = (GObjectClass*) class;
+widget_class = (GtkWidgetClass*) class;
+
+object_class->finalize = gtk_gps_finalize;
+object_class->set_property = gtk_gps_set_property;
+object_class->get_property = gtk_gps_get_property;
+
+widget_class->size_request = gtk_gps_size_request;
+widget_class->expose_event = gtk_gps_expose;
+widget_class->realize = gtk_gps_realize;
+widget_class->size_allocate = gtk_gps_size_allocate;
+}
+
+static void
+gtk_gps_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+GtkGps *gps;
+g_return_if_fail(GTK_IS_GPS(object));
+gps=GTK_GPS(object);
+switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+}
+}
+
+static void
+gtk_gps_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+GtkGps *gps;
+g_return_if_fail(GTK_IS_GPS(object));
+gps=GTK_GPS(object);
+switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+}
+}
+
+static void
+gtk_gps_init (GtkGps *gps)
+{
+g_printf("%s()\n", __PRETTY_FUNCTION__);
+
+gps->satinview=0;
+gps->satinuse=0;
+gps->gc1=NULL;
+gps->gc2=NULL;
+gps->gc3=NULL;
+gps->width=400;
+gps->height=300;
+}
+
+GtkWidget*
+gtk_gps_new(void)
+{
+GtkGps *gps;
+gps = gtk_type_new(gtk_gps_get_type ());
+return GTK_WIDGET(gps);
+}
+
+static void
+gtk_gps_finalize(GObject *object)
+{
+GtkGps *gps;
+
+g_printf("%s()\n", __PRETTY_FUNCTION__);
+
+g_return_if_fail(GTK_IS_GPS(object));
+gps=GTK_GPS(object);
+
+if (GTK_WIDGET(object)->parent && GTK_WIDGET_MAPPED(object)) {
+ gtk_widget_unmap(GTK_WIDGET(object));
+}
+
+G_OBJECT_CLASS(gtk_gps_parent_class)->finalize(object);
+}
+
+static void
+gtk_gps_size_request(GtkWidget *widget, GtkRequisition *requisition)
+{
+GtkGps *gps;
+
+g_printf("%s()\n", __PRETTY_FUNCTION__);
+
+g_return_if_fail(GTK_IS_GPS(widget));
+g_return_if_fail(requisition != NULL);
+
+gps = GTK_GPS (widget);
+
+requisition->width=400;
+requisition->height=300;
+gps->width=400 - 10;
+gps->height=300 - 10;
+}
+
+static void
+gtk_gps_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+GtkGps *gps;
+
+g_printf("%s()\n", __PRETTY_FUNCTION__);
+
+g_return_if_fail(GTK_IS_GPS(widget));
+g_return_if_fail(allocation!=NULL);
+
+gps=GTK_GPS (widget);
+
+widget->allocation = *allocation;
+
+if (GTK_WIDGET_REALIZED (widget)) {
+ gdk_window_move_resize (widget->window,
+ allocation->x, allocation->y,
+ allocation->width, allocation->height);
+}
+
+gps->width=allocation->width-PADDING;
+gps->height=allocation->height-PADDING;
+}
+
+static void
+gtk_gps_realize (GtkWidget *widget)
+{
+GtkGps *gps;
+GdkColor color;
+GdkWindowAttr attributes;
+gint attributes_mask;
+
+g_printf("%s()\n", __PRETTY_FUNCTION__);
+
+g_return_if_fail (GTK_IS_GPS(widget));
+GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+gps=GTK_GPS(widget);
+
+attributes.x=widget->allocation.x;
+attributes.y=widget->allocation.y;
+attributes.width=widget->allocation.width;
+attributes.height=widget->allocation.height;
+attributes.wclass=GDK_INPUT_OUTPUT;
+attributes.window_type=GDK_WINDOW_CHILD;
+attributes.event_mask=gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK ;
+attributes.visual=gtk_widget_get_visual(widget);
+attributes.colormap=gtk_widget_get_colormap(widget);
+
+attributes_mask=GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+widget->window=gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributes_mask);
+widget->style=gtk_style_attach(widget->style, widget->window);
+
+gdk_window_set_user_data(widget->window, widget);
+gtk_style_set_background(widget->style, widget->window, GTK_STATE_ACTIVE);
+
+gps->sat_details_context=gtk_widget_get_pango_context(widget);
+gps->sat_details_layout=pango_layout_new(gps->sat_details_context);
+gps->sat_details_fontdesc=pango_font_description_new();
+pango_font_description_set_family(gps->sat_details_fontdesc, "Sans Serif");
+pango_font_description_set_size(gps->sat_details_fontdesc, 12*PANGO_SCALE);
+pango_layout_set_font_description(gps->sat_details_layout, gps->sat_details_fontdesc);
+pango_layout_set_alignment(gps->sat_details_layout, PANGO_ALIGN_CENTER);
+
+if (!gps->gc1) {
+ color.red=0;
+ color.green=0;
+ color.blue=0;
+ gps->gc1=gdk_gc_new(widget->window);
+ gdk_gc_set_rgb_fg_color(gps->gc1, &color);
+}
+
+if (!gps->gc2) {
+ color.red=0;
+ color.green=0;
+ color.blue=0xffff;
+ gps->gc2=gdk_gc_new(widget->window);
+ gdk_gc_set_rgb_fg_color(gps->gc2, &color);
+}
+
+if (!gps->gc3) {
+ color.red=0xffff;
+ color.green=0xffff;
+ color.blue=0xffff;
+ gps->gc3=gdk_gc_new(widget->window);
+ gdk_gc_set_rgb_fg_color(gps->gc3, &color);
+}
+}
+
+static gint
+gtk_gps_paint(GtkGps *gps)
+{
+GdkGC *gc;
+GtkWidget *widget;
+guint i, j, x, y, size, halfsize, xoffset, yoffset;
+guint x1, y1, x0, y0;
+gfloat tmp;
+gchar buffer[16];
+guint line[12]={0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330};
+
+g_printf("%s()\n", __PRETTY_FUNCTION__);
+
+widget=GTK_WIDGET(gps);
+
+if (!GTK_WIDGET_MAPPED(widget))
+ return TRUE;
+
+x0=0;
+y0=0;
+
+size=MIN(gps->width, gps->height);
+halfsize=size/2;
+if (gps->width>gps->height) {
+ xoffset=x0+(gps->width-gps->height-PADDING)/2;
+ yoffset=y0+5;
+} else {
+ xoffset=x0+5;
+ yoffset=y0+(gps->height-gps->width-PADDING)/2;
+}
+
+/* 90 */
+gdk_draw_arc(widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+ FALSE,
+ xoffset + 2, yoffset + 2, size - 4, size - 4, 0, 64 * 360);
+
+/* 60 */
+gdk_draw_arc(widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+ FALSE,
+ xoffset + size / 6, yoffset + size / 6,
+ size / 6 * 4, size / 6 * 4, 0, 64 * 360);
+
+/* 30 */
+gdk_draw_arc(widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+ FALSE,
+ xoffset + size / 6 * 2, yoffset + size / 6 * 2,
+ size / 6 * 2, size / 6 * 2, 0, 64 * 360);
+
+for (i = 0; i < 6; i++) {
+ /* line */
+ tmp = (line[i] * (1.f / 180.f)) * G_PI;
+ gdk_draw_line(widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+ xoffset + halfsize + (halfsize - 2) * sinf(tmp),
+ yoffset + halfsize - (halfsize - 2) * cosf(tmp),
+ xoffset + halfsize - (halfsize - 2) * sinf(tmp),
+ yoffset + halfsize + (halfsize - 2) * cosf(tmp));
+}
+
+for (i = 0; i < 12; i++) {
+ tmp = (line[i] * (1.f / 180.f)) * G_PI;
+ /* azimuth */
+ if (line[i] == 0)
+ g_snprintf(buffer, 16, "N");
+ else
+ g_snprintf(buffer, 16, "%d°", line[i]);
+ pango_layout_set_text(gps->sat_details_layout, buffer, -1);
+ pango_layout_get_pixel_size(gps->sat_details_layout, &x, &y);
+ gdk_draw_layout(widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
+ (xoffset + halfsize + (halfsize - size / 12) * sinf(tmp)) - x / 2,
+ (yoffset + halfsize - (halfsize - size / 12) * cosf(tmp)) - y / 2,
+ gps->sat_details_layout);
+}
+
+/* elevation 30 */
+tmp = (30 * (1.f / 180.f)) * G_PI;
+g_snprintf(buffer, 16, "30°");
+pango_layout_set_text(gps->sat_details_layout, buffer, -1);
+pango_layout_get_pixel_size(gps->sat_details_layout, &x, &y);
+gdk_draw_layout(widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
+ (xoffset + halfsize + size / 6 * 2 * sinf(tmp)) - x / 2,
+ (yoffset + halfsize - size / 6 * 2 * cosf(tmp)) - y / 2,
+ gps->sat_details_layout);
+
+/* elevation 60 */
+tmp = (30 * (1.f / 180.f)) * G_PI;
+g_snprintf(buffer, 16, "60°");
+pango_layout_set_text(gps->sat_details_layout, buffer, -1);
+pango_layout_get_pixel_size(gps->sat_details_layout, &x, &y);
+gdk_draw_layout(widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
+ (xoffset + halfsize + size / 6 * sinf(tmp)) - x / 2,
+ (yoffset + halfsize - size / 6 * cosf(tmp)) - y / 2,
+ gps->sat_details_layout);
+
+for (i=0;i<gps->satinview;i++) {
+ /* Sat used or not */
+ gc=gps->gc1;
+ for (j=0;j<gps->satinuse;j++) {
+ if (gps->satforfix[j]==gps->gps_sat[i].prn) {
+ gc=gps->gc2;
+ break;
+ }
+ }
+
+ tmp = (gps->gps_sat[i].azimuth * (1.f / 180.f)) * G_PI;
+ x = xoffset + halfsize + (90 - gps->gps_sat[i].elevation) * halfsize / 90 * sinf(tmp);
+ y = yoffset + halfsize - (90 - gps->gps_sat[i].elevation) * halfsize / 90 * cosf(tmp);
+
+ gdk_draw_arc(widget->window, gc, TRUE, x - 10, y - 10, 20, 20, 0, 64 * 360);
+
+ g_snprintf(buffer, 6, "%02d", gps->gps_sat[i].prn);
+ pango_layout_set_text(gps->sat_details_layout, buffer, -1);
+ pango_layout_get_pixel_size(gps->sat_details_layout, &x1, &y1);
+ gdk_draw_layout(widget->window, gps->gc3, x - x1 / 2, y - y1 / 2, gps->sat_details_layout);
+}
+
+g_printf("%s(): return\n", __PRETTY_FUNCTION__);
+return TRUE;
+}
+
+static gint
+gtk_gps_expose(GtkWidget *widget, GdkEventExpose *event)
+{
+g_return_val_if_fail(GTK_IS_GPS(widget), FALSE);
+g_return_val_if_fail(event != NULL, FALSE);
+
+gtk_gps_paint(GTK_GPS(widget));
+return TRUE;
+}
+
+void
+gtk_gps_set_satellite_data(GtkWidget *widget, guint sat, gboolean fix, guint prn, guint elevation, guint azimuth, guint snr)
+{
+GtkGps *gps;
+
+g_printf("%s()\n", __PRETTY_FUNCTION__);
+g_return_if_fail(GTK_IS_GPS (widget));
+
+gps=GTK_GPS(widget);
+
+if (sat>12)
+ sat=12;
+
+BOUND(elevation, 0, 90);
+BOUND(azimuth, 0, 360);
+BOUND(snr, 0, 99);
+gps->gps_sat[sat].prn=prn;
+gps->gps_sat[sat].elevation=elevation;
+gps->gps_sat[sat].azimuth=azimuth;
+gps->gps_sat[sat].snr=snr;
+if (fix)
+ gps->satforfix[sat]=prn;
+else
+ gps->satforfix[sat]=-1;
+g_printf("%s(): return\n", __PRETTY_FUNCTION__);
+}
+
+void
+gtk_gps_set_satellite_in_view(GtkWidget *widget, guint sat)
+{
+GtkGps *gps;
+
+g_printf("%s()\n", __PRETTY_FUNCTION__);
+g_return_if_fail(GTK_IS_GPS(widget));
+
+gps=GTK_GPS(widget);
+
+if (sat>12)
+ sat=12;
+
+gps->satinview=sat;
+gtk_gps_paint(gps);
+
+g_printf("%s(): return\n", __PRETTY_FUNCTION__);
+}