]> err.no Git - mapper/commitdiff
Make a widget of the heading/compass display
authorKaj-Michael Lang <milang@onion.tal.org>
Thu, 27 Sep 2007 15:29:23 +0000 (18:29 +0300)
committerKaj-Michael Lang <milang@onion.tal.org>
Thu, 27 Sep 2007 15:29:23 +0000 (18:29 +0300)
src/gtkcompass.c [new file with mode: 0644]
src/gtkcompass.h [new file with mode: 0644]

diff --git a/src/gtkcompass.c b/src/gtkcompass.c
new file mode 100644 (file)
index 0000000..c24bc02
--- /dev/null
@@ -0,0 +1,442 @@
+/*
+ *
+ * GtkCompass: Display heading/compass information
+ * Also bearing to target and next waypoint.
+ *
+ * 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 <stdlib.h>
+
+#include <glib/gstdio.h>
+#include <glib-object.h>
+#include "gtkcompass.h"
+
+static void gtk_compass_finalize (GObject *object);
+static void gtk_compass_size_request (GtkWidget *widget, GtkRequisition *requisition);
+static void gtk_compass_size_allocate (GtkWidget *widget, GtkAllocation *allocate);
+static gint gtk_compass_expose (GtkWidget *widget, GdkEventExpose *event);
+static void gtk_compass_realize (GtkWidget *widget);
+static void gtk_compass_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
+static void gtk_compass_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
+static void gtk_compass_paint(GtkCompass *compass);
+
+G_DEFINE_TYPE(GtkCompass, gtk_compass, 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_compass_class_init (GtkCompassClass *class)
+{
+GObjectClass *object_class;
+GtkWidgetClass *widget_class;
+       
+object_class = (GObjectClass*) class;
+widget_class = (GtkWidgetClass*) class;
+       
+object_class->finalize = gtk_compass_finalize;
+object_class->set_property = gtk_compass_set_property;
+object_class->get_property = gtk_compass_get_property;
+       
+widget_class->size_request = gtk_compass_size_request;
+widget_class->expose_event = gtk_compass_expose;
+widget_class->realize = gtk_compass_realize;
+widget_class->size_allocate = gtk_compass_size_allocate;
+}
+
+static void
+gtk_compass_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+GtkCompass *compass;
+g_return_if_fail(GTK_IS_COMPASS(object));
+compass=GTK_COMPASS(object);
+switch (prop_id) {
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       break;
+}
+}
+
+static void
+gtk_compass_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec   *pspec)
+{
+GtkCompass *compass;
+g_return_if_fail(GTK_IS_COMPASS(object));
+compass=GTK_COMPASS(object);
+switch (prop_id) {
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       break;
+}
+}
+
+static void
+gtk_compass_init (GtkCompass *compass)
+{
+g_printf("%s()\n", __PRETTY_FUNCTION__);
+
+compass->heading=0;
+compass->gc_h=NULL;
+compass->gc_d=NULL;
+compass->gc_w=NULL;
+compass->dest_valid=FALSE;
+compass->way_valid=FALSE;
+compass->width=400;
+compass->height=300;
+}
+
+static gboolean 
+gtk_compass_cb_button_press(GtkWidget * widget, GdkEventButton * event)
+{
+GtkCompass *compass;
+
+compass=GTK_COMPASS(widget);
+
+return FALSE;
+}
+
+GtkWidget*
+gtk_compass_new(void)
+{
+GtkCompass *compass;
+GtkWidget *widget;
+
+compass=gtk_type_new(gtk_compass_get_type ());
+widget=GTK_WIDGET(compass);
+g_signal_connect(G_OBJECT(widget), "button_press_event", G_CALLBACK(gtk_compass_cb_button_press), NULL);
+return widget;
+}
+
+static void
+gtk_compass_finalize(GObject *object)
+{
+GtkCompass *compass;
+
+g_printf("%s()\n", __PRETTY_FUNCTION__);
+       
+g_return_if_fail(GTK_IS_COMPASS(object));
+compass=GTK_COMPASS(object);
+
+if (GTK_WIDGET(object)->parent && GTK_WIDGET_MAPPED(object)) {
+       gtk_widget_unmap(GTK_WIDGET(object));
+}
+
+G_OBJECT_CLASS(gtk_compass_parent_class)->finalize(object);
+}
+
+static void
+gtk_compass_size_request(GtkWidget     *widget, GtkRequisition *requisition)
+{
+GtkCompass *compass;
+
+g_printf("%s()\n", __PRETTY_FUNCTION__);
+       
+g_return_if_fail(GTK_IS_COMPASS(widget));
+g_return_if_fail(requisition != NULL);
+       
+compass = GTK_COMPASS (widget);
+       
+requisition->width=400;
+requisition->height=300;
+compass->width=400 - 10;
+compass->height=300 - 10;
+}
+
+static void
+gtk_compass_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
+{
+GtkCompass *compass;
+
+g_printf("%s()\n", __PRETTY_FUNCTION__);
+
+g_return_if_fail(GTK_IS_COMPASS(widget));
+g_return_if_fail(allocation!=NULL);
+
+compass=GTK_COMPASS(widget);
+
+widget->allocation=*allocation;
+
+if (GTK_WIDGET_REALIZED (widget)) {
+       gdk_window_move_resize (widget->window,
+               allocation->x, allocation->y,
+               allocation->width, allocation->height);
+}
+
+compass->width=allocation->width-PADDING;
+compass->height=allocation->height-PADDING;
+
+compass->size = MIN(widget->allocation.width, widget->allocation.height);
+if (widget->allocation.width > widget->allocation.height) {
+       compass->xoffset = (widget->allocation.width - widget->allocation.height) / 2;
+       compass->yoffset = 0;
+} else {
+       compass->xoffset = 0;
+       compass->yoffset = (widget->allocation.height - widget->allocation.width) / 2;
+}
+
+}
+
+static void 
+gtk_compass_realize (GtkWidget *widget)
+{
+GtkCompass *compass;
+GdkColor color;
+GdkWindowAttr attributes;
+gint attributes_mask;
+
+g_printf("%s()\n", __PRETTY_FUNCTION__);
+
+g_return_if_fail (GTK_IS_COMPASS(widget));
+GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+compass=GTK_COMPASS(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 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_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);
+
+compass->context=gtk_widget_get_pango_context(widget);
+compass->layout=pango_layout_new(compass->context);
+compass->fontdesc=pango_font_description_new();
+pango_font_description_set_family(compass->fontdesc, "Sans Serif");
+pango_font_description_set_size(compass->fontdesc, 12*PANGO_SCALE);
+pango_layout_set_font_description(compass->layout, compass->fontdesc);
+pango_layout_set_alignment(compass->layout, PANGO_ALIGN_CENTER);
+
+if (!compass->gc_h) {
+       color.red=0x0000;
+       color.green=0x0000;
+       color.blue=0x0000;
+       compass->gc_h=gdk_gc_new(widget->window);
+       gdk_gc_set_rgb_fg_color(compass->gc_h, &color);
+       gdk_gc_set_line_attributes(compass->gc_h, 6, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
+}
+
+if (!compass->gc_d) {
+       color.red=0xffff;
+       color.green=0x0000;
+       color.blue=0xffff;
+       compass->gc_d=gdk_gc_new(widget->window);
+       gdk_gc_set_rgb_fg_color(compass->gc_d, &color);
+       gdk_gc_set_line_attributes(compass->gc_d, 6, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
+}
+
+if (!compass->gc_w) {
+       color.red=0x0000;
+       color.green=0xffff;
+       color.blue=0x0000;
+       compass->gc_w=gdk_gc_new(widget->window);
+       gdk_gc_set_rgb_fg_color(compass->gc_w, &color);
+       gdk_gc_set_line_attributes(compass->gc_w, 6, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
+}
+
+}
+
+static void
+gtk_compass_draw_mark(GtkCompass *compass, GdkGC *gc, gdouble t)
+{
+GtkWidget *widget;
+gint hs;
+
+widget=GTK_WIDGET(compass);
+
+hs=compass->size/2;
+
+gdk_draw_line(widget->window,gc,
+       compass->xoffset+hs+((hs-5)*sin(t)),
+       compass->yoffset+compass->size-((hs-5)*cos(t)),
+       compass->xoffset+hs+((hs+5)*sin(t)),
+       compass->yoffset+compass->size-((hs+5)*cos(t)));
+}
+
+static void
+gtk_compass_paint(GtkCompass *compass)
+{
+GtkWidget *widget;
+guint i, x, y, size, hsize;
+gint dir;
+gfloat tmp;
+gchar *text;
+gchar htext[8];
+gint angle[5] = { -90, -45, 0, 45, 90 };
+gint fsize[5] = { 0, 4, 10, 4, 0 };
+
+widget=GTK_WIDGET(compass);
+size=compass->size;
+hsize=size/2;
+
+g_snprintf(htext, 8, "%3.0f°", compass->heading);
+pango_layout_set_text(compass->layout, htext, -1);
+pango_layout_get_pixel_size(compass->layout, &x, &y);
+
+gdk_draw_layout(widget->window,
+               widget->style->fg_gc[GTK_STATE_NORMAL],
+               compass->xoffset+size/2-x/2,
+               compass->yoffset+size-y-2, compass->layout);
+
+gdk_draw_arc(widget->window,
+            widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+            FALSE,
+            compass->xoffset, compass->yoffset+size/2, size, size, 0, 64 * 180);
+
+/* Simple arrow for heading */
+gdk_draw_line(widget->window,
+             widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+             compass->xoffset + size / 2 + 3,
+             compass->yoffset + size - y - 5,
+             compass->xoffset + size / 2, compass->yoffset + size / 2 + 5);
+
+gdk_draw_line(widget->window,
+             widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+             compass->xoffset + size / 2 - 3,
+             compass->yoffset + size - y - 5,
+             compass->xoffset + size / 2, compass->yoffset + size / 2 + 5);
+
+gdk_draw_line(widget->window,
+             widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+             compass->xoffset + size / 2 - 3,
+             compass->yoffset + size - y - 5,
+             compass->xoffset + size / 2, compass->yoffset + size - y - 8);
+
+gdk_draw_line(widget->window,
+             widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+             compass->xoffset + size / 2 + 3,
+             compass->yoffset + size - y - 5,
+             compass->xoffset + size / 2, compass->yoffset + size - y - 8);
+
+for (i = 0; i < 5; i++) {
+       dir = (gint) (compass->heading / 45) * 45 + angle[i];
+
+       switch (dir) {
+       case 0:
+       case 360:
+               text = "N";
+               break;
+       case 45:
+       case 405:
+               text = "NE";
+               break;
+       case 90:
+               text = "E";
+               break;
+       case 135:
+               text = "SE";
+               break;
+       case 180:
+               text = "S";
+               break;
+       case 225:
+               text = "SW";
+               break;
+       case 270:
+       case -90:
+               text = "W";
+               break;
+       case 315:
+       case -45:
+               text = "NW";
+               break;
+       default:
+               text = "??";
+               break;
+       }
+
+       tmp = ((dir - compass->heading) * (1.f / 180.f)) * G_PI;
+       gdk_draw_line(widget->window,
+             widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+             compass->xoffset + size / 2 + ((size / 2 - 5) * sinf(tmp)),
+             compass->yoffset + size - ((size / 2 - 5) * cosf(tmp)),
+             compass->xoffset + size / 2 + ((size / 2 + 5) * sinf(tmp)),
+             compass->yoffset + size - ((size / 2 + 5) * cosf(tmp)));
+
+       x = fsize[i];
+       if (abs((guint) (compass->heading / 45) * 45 - compass->heading)
+           > abs((guint) (compass->heading / 45) * 45 + 45 - compass->heading) && (i > 0))
+                       x = fsize[i - 1];
+
+       pango_font_description_set_size(compass->fontdesc, (10 + x) * PANGO_SCALE);
+       pango_layout_set_font_description(compass->layout, compass->fontdesc);
+       pango_layout_set_text(compass->layout, text, -1);
+       pango_layout_get_pixel_size(compass->layout, &x, &y);
+       x = compass->xoffset + size / 2 + ((size / 2 + 15) * sinf(tmp)) - x / 2,
+    y = compass->yoffset + size - ((size / 2 + 15) * cosf(tmp)) - y / 2,
+    gdk_draw_layout(widget->window,
+           widget->style->fg_gc[GTK_STATE_NORMAL],
+           x, y, compass->layout);
+
+       if (compass->dest_valid) {
+               gtk_compass_draw_mark(compass, compass->gc_d, compass->dest_heading);
+       }
+
+       if (compass->way_valid) {
+               gtk_compass_draw_mark(compass, compass->gc_w, compass->way_heading);
+       }
+}
+
+return TRUE;
+}
+
+
+static gint
+gtk_compass_expose(GtkWidget *widget, GdkEventExpose *event)
+{
+GtkCompass *compass;
+g_return_val_if_fail(GTK_IS_COMPASS(widget), FALSE);
+g_return_val_if_fail(event != NULL, FALSE);
+
+compass=GTK_COMPASS(widget);
+gtk_compass_paint(compass);
+return TRUE;
+}
+
+void
+gtk_compass_set_heading(GtkWidget *widget, gfloat heading)
+{
+GtkCompass *compass;
+
+g_printf("%s()\n", __PRETTY_FUNCTION__);
+g_return_if_fail(GTK_IS_COMPASS(widget));
+
+compass=GTK_COMPASS(widget);
+
+compass->heading=heading;
+gtk_compass_paint(compass);
+
+g_printf("%s(): return\n", __PRETTY_FUNCTION__);
+}
diff --git a/src/gtkcompass.h b/src/gtkcompass.h
new file mode 100644 (file)
index 0000000..e33ada0
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * GtkCompass: Display a compass with heading and target
+ * Copyright (C) 2007 Kaj-Michael Lang
+ *
+ * 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.
+ */
+#ifndef __GTK_COMPASS_H__
+#define __GTK_COMPASS_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTK_COMPASS_TYPE                       (gtk_compass_get_type ())
+#define GTK_COMPASS(obj)                       (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_COMPASS_TYPE, GtkCompass))
+#define GTK_COMPASS_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_COMPASS_TYPE, GtkCompassClass))
+#define GTK_IS_COMPASS(obj)                    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_COMPASS_TYPE))
+
+#define GTK_COMPASS_SAT_MAX                    (12)
+
+typedef struct _GtkCompass                     GtkCompass;
+typedef struct _GtkCompassClass                GtkCompassClass;
+
+struct _GtkCompass {
+       GtkWidget parent;
+       GtkWidget widget;
+       GdkGC *gc_h;
+       GdkGC *gc_w;
+       GdkGC *gc_d;
+       guint width, height;
+       guint size;
+       guint xoffset, yoffset;
+
+       PangoContext *context;
+       PangoLayout *layout;
+       PangoFontDescription *fontdesc;
+
+       gfloat heading;
+       gfloat dest_heading;
+       gfloat way_heading;
+       gboolean dest_valid;
+       gboolean way_valid;
+};
+
+struct _GtkCompassClass {
+       GtkWidgetClass parent_class;
+};
+
+GType gtk_compass_get_type(void);
+GtkWidget* gtk_compass_new(void);
+void gtk_compass_set_heading(GtkWidget *widget, gfloat heading);
+
+G_END_DECLS
+
+#endif /* __GTK_COMPASS_H__ */