3 * GtkCompass: Display heading/compass information
4 * Also bearing to target and next waypoint.
6 * Copyright (C) 2007 Kaj-Michael Lang
7 * Originanl non-widget version Copyright Cezary Jackiewicz
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the Free
21 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include <glib/gstdio.h>
28 #include <glib-object.h>
29 #include "gtkcompass.h"
35 static void gtk_compass_finalize (GObject *object);
36 static void gtk_compass_size_request (GtkWidget *widget, GtkRequisition *requisition);
37 static void gtk_compass_size_allocate (GtkWidget *widget, GtkAllocation *allocate);
38 static gboolean gtk_compass_expose (GtkWidget *widget, GdkEventExpose *event);
39 static void gtk_compass_realize (GtkWidget *widget);
40 static void gtk_compass_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
41 static void gtk_compass_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
42 static void gtk_compass_paint(GtkCompass *compass);
43 static gboolean gtk_compass_refresh_cb(GtkWidget *widget);
45 G_DEFINE_TYPE(GtkCompass, gtk_compass, GTK_TYPE_WIDGET);
47 #define BOUND(x, a, b) { \
55 gtk_compass_class_init (GtkCompassClass *class)
57 GObjectClass *object_class;
58 GtkWidgetClass *widget_class;
60 object_class = (GObjectClass*) class;
61 widget_class = (GtkWidgetClass*) class;
63 object_class->finalize = gtk_compass_finalize;
64 object_class->set_property = gtk_compass_set_property;
65 object_class->get_property = gtk_compass_get_property;
67 widget_class->size_request = gtk_compass_size_request;
68 widget_class->expose_event = gtk_compass_expose;
69 widget_class->realize = gtk_compass_realize;
70 widget_class->size_allocate = gtk_compass_size_allocate;
74 gtk_compass_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
77 g_return_if_fail(GTK_IS_COMPASS(object));
78 compass=GTK_COMPASS(object);
81 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
87 gtk_compass_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
90 g_return_if_fail(GTK_IS_COMPASS(object));
91 compass=GTK_COMPASS(object);
94 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
100 gtk_compass_init(GtkCompass *compass)
105 compass->dest_valid=FALSE;
106 compass->way_valid=FALSE;
113 gtk_compass_cb_button_press(GtkWidget *widget, GdkEventButton *event)
117 compass=GTK_COMPASS(widget);
120 compass->data->heading=0;
121 compass->heading=180;
128 gtk_compass_new(GpsData *data)
133 compass=gtk_type_new(gtk_compass_get_type ());
134 widget=GTK_WIDGET(compass);
137 g_signal_connect(G_OBJECT(widget), "button_press_event", G_CALLBACK(gtk_compass_cb_button_press), NULL);
139 compass->esid=g_timeout_add(1000,(GSourceFunc)gtk_compass_refresh_cb, compass);
145 gtk_compass_finalize(GObject *object)
149 g_return_if_fail(GTK_IS_COMPASS(object));
150 compass=GTK_COMPASS(object);
151 g_source_remove(compass->esid);
153 if (GTK_WIDGET(object)->parent && GTK_WIDGET_MAPPED(object)) {
154 gtk_widget_unmap(GTK_WIDGET(object));
157 G_OBJECT_CLASS(gtk_compass_parent_class)->finalize(object);
161 gtk_compass_size_request(GtkWidget *widget, GtkRequisition *requisition)
165 g_return_if_fail(GTK_IS_COMPASS(widget));
166 g_return_if_fail(requisition != NULL);
168 compass=GTK_COMPASS(widget);
170 requisition->width=400;
171 requisition->height=300;
177 gtk_compass_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
181 g_return_if_fail(GTK_IS_COMPASS(widget));
182 g_return_if_fail(allocation!=NULL);
184 compass=GTK_COMPASS(widget);
186 widget->allocation=*allocation;
188 if (GTK_WIDGET_REALIZED (widget)) {
189 gdk_window_move_resize (widget->window,
190 allocation->x, allocation->y,
191 allocation->width, allocation->height);
194 compass->width=allocation->width;
195 compass->height=allocation->height;
197 compass->size = MIN(widget->allocation.width, widget->allocation.height);
198 if (widget->allocation.width > widget->allocation.height) {
199 compass->xoffset = (widget->allocation.width - widget->allocation.height) / 2;
200 compass->yoffset = 0;
202 compass->xoffset = 0;
203 compass->yoffset = (widget->allocation.height - widget->allocation.width) / 2;
209 gtk_compass_realize (GtkWidget *widget)
213 GdkWindowAttr attributes;
214 gint attributes_mask;
216 g_return_if_fail (GTK_IS_COMPASS(widget));
217 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
218 compass=GTK_COMPASS(widget);
220 attributes.x=widget->allocation.x;
221 attributes.y=widget->allocation.y;
222 attributes.width=widget->allocation.width;
223 attributes.height=widget->allocation.height;
224 attributes.wclass=GDK_INPUT_OUTPUT;
225 attributes.window_type=GDK_WINDOW_CHILD;
226 attributes.event_mask=gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
227 attributes.visual=gtk_widget_get_visual(widget);
228 attributes.colormap=gtk_widget_get_colormap(widget);
230 attributes_mask=GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
232 widget->window=gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributes_mask);
233 widget->style=gtk_style_attach(widget->style, widget->window);
235 gdk_window_set_user_data(widget->window, widget);
236 gtk_style_set_background(widget->style, widget->window, GTK_STATE_ACTIVE);
238 compass->context=gtk_widget_get_pango_context(widget);
239 compass->layout=pango_layout_new(compass->context);
240 compass->fontdesc=pango_font_description_new();
241 pango_font_description_set_family(compass->fontdesc, "Sans Serif");
242 pango_font_description_set_size(compass->fontdesc, 12*PANGO_SCALE);
243 pango_layout_set_font_description(compass->layout, compass->fontdesc);
244 pango_layout_set_alignment(compass->layout, PANGO_ALIGN_CENTER);
246 if (!compass->gc_h) {
250 compass->gc_h=gdk_gc_new(widget->window);
251 gdk_gc_set_rgb_fg_color(compass->gc_h, &color);
252 gdk_gc_set_line_attributes(compass->gc_h, 2, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
255 if (!compass->gc_d) {
259 compass->gc_d=gdk_gc_new(widget->window);
260 gdk_gc_set_rgb_fg_color(compass->gc_d, &color);
261 gdk_gc_set_line_attributes(compass->gc_d, 6, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
264 if (!compass->gc_w) {
268 compass->gc_w=gdk_gc_new(widget->window);
269 gdk_gc_set_rgb_fg_color(compass->gc_w, &color);
270 gdk_gc_set_line_attributes(compass->gc_w, 6, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
276 gtk_compass_draw_mark(GtkCompass *compass, GdkGC *gc, gfloat angle, gint size)
281 widget=GTK_WIDGET(compass);
285 gdk_draw_line(widget->window,gc,
286 compass->xoffset+hs+((hs-size)*sinf(angle)),
287 compass->yoffset+compass->size-((hs-size)*cosf(angle)),
288 compass->xoffset+hs+((hs+size)*sinf(angle)),
289 compass->yoffset+compass->size-((hs+size)*cosf(angle)));
293 gtk_compass_paint(GtkCompass *compass)
296 guint i, x, y, size, hsize, fs;
301 gint angle[5] = { -90, -45, 0, 45, 90 };
302 gint fsize[5] = { 0, 4, 10, 4, 0 };
304 widget=GTK_WIDGET(compass);
315 pango_context_set_matrix (compass->context, NULL);
317 g_snprintf(htext, 8, "%3.0f°", compass->heading);
318 pango_font_description_set_size(compass->fontdesc, (10+fs) * PANGO_SCALE);
319 pango_layout_set_font_description(compass->layout, compass->fontdesc);
320 pango_layout_set_text(compass->layout, htext, -1);
321 pango_layout_get_pixel_size(compass->layout, &x, &y);
323 gdk_draw_layout(widget->window,
325 compass->xoffset+hsize-x/2,
326 compass->yoffset+size-y-2, compass->layout);
328 gdk_draw_arc(widget->window,
331 compass->xoffset, compass->yoffset+hsize, size - fs, size - fs, 0, 64 * 360);
333 /* Simple arrow for heading */
334 gdk_draw_line(widget->window,
336 compass->xoffset + hsize + 3,
337 compass->yoffset + size - y - 5,
338 compass->xoffset + hsize, compass->yoffset + hsize + 5);
340 gdk_draw_line(widget->window,
342 compass->xoffset + hsize - 3,
343 compass->yoffset + size - y - 5,
344 compass->xoffset + hsize, compass->yoffset + hsize + 5);
346 gdk_draw_line(widget->window,
348 compass->xoffset + hsize - 3,
349 compass->yoffset + size - y - 5,
350 compass->xoffset + hsize, compass->yoffset + size - y - 8);
352 gdk_draw_line(widget->window,
354 compass->xoffset + hsize + 3,
355 compass->yoffset + size - y - 5,
356 compass->xoffset + hsize, compass->yoffset + size - y - 8);
358 for (i = 0; i < 5; i++) {
359 PangoMatrix matrix = PANGO_MATRIX_INIT;
360 dir = (gint) (compass->heading / 45) * 45 + angle[i];
396 tmp = ((dir - compass->heading) * (1.f / 180.f)) * G_PI;
398 gtk_compass_draw_mark(compass, compass->gc_h, tmp, 6);
401 if (abs((guint) (compass->heading / 45) * 45 - compass->heading)
402 > abs((guint) (compass->heading / 45) * 45 + 45 - compass->heading) && (i > 0))
405 pango_font_description_set_size(compass->fontdesc, (10 + x + fs) * PANGO_SCALE);
406 pango_layout_set_font_description(compass->layout, compass->fontdesc);
407 pango_layout_set_text(compass->layout, text, -1);
408 pango_matrix_rotate (&matrix, -(dir-compass->heading));
409 pango_context_set_matrix (compass->context, &matrix);
410 pango_layout_get_pixel_size(compass->layout, &x, &y);
411 x = compass->xoffset + hsize + ((hsize + 15 + fs) * sinf(tmp)) - x / 2,
412 y = compass->yoffset + size - ((hsize + 15 + fs) * cosf(tmp)) - y / 2,
413 gdk_draw_layout(widget->window, compass->gc_h, x, y, compass->layout);
416 if (compass->dest_valid) {
417 tmp=((compass->dest_heading-compass->heading) * (1.f / 180.f)) * G_PI;
418 gtk_compass_draw_mark(compass, compass->gc_d, tmp, 10);
421 if (compass->way_valid) {
422 tmp=((compass->way_heading-compass->heading) * (1.f / 180.f)) * G_PI;
423 gtk_compass_draw_mark(compass, compass->gc_w, tmp, 10);
430 gtk_compass_expose(GtkWidget *widget, GdkEventExpose *event)
434 g_return_val_if_fail(GTK_IS_COMPASS(widget), FALSE);
435 g_return_val_if_fail(event != NULL, FALSE);
437 compass=GTK_COMPASS(widget);
438 gtk_compass_paint(compass);
443 gtk_compass_refresh_cb(GtkWidget *widget)
448 g_return_val_if_fail(GTK_IS_COMPASS(widget), FALSE);
450 compass=GTK_COMPASS(widget);
452 if ((GTK_WIDGET_MAPPED(widget)==FALSE) || (GTK_WIDGET_VISIBLE(widget)==FALSE)) {
453 compass->heading=compass->data->heading;
457 if (compass->heading==compass->data->heading)
460 tmp=fabsf(compass->heading-compass->data->heading);
464 compass->heading=compass->data->heading;
466 if (compass->heading<compass->data->heading)
467 compass->heading+=tmp;
469 if (compass->heading>compass->data->heading)
470 compass->heading-=tmp;
473 g_printf("%.2f %.2f\n", compass->heading, compass->data->heading);
476 gtk_widget_queue_draw_area(widget, 0, 0, compass->width, compass->height);
481 gtk_compass_refresh(GtkWidget *widget)
485 g_return_if_fail(GTK_IS_COMPASS(widget));
487 compass=GTK_COMPASS(widget);
488 gtk_widget_queue_draw_area(widget, 0, 0, compass->width, compass->height);
492 gtk_compass_set_way_heading(GtkWidget *widget, gboolean valid, gfloat heading)
495 g_return_if_fail(GTK_IS_COMPASS(widget));
497 compass=GTK_COMPASS(widget);
499 compass->way_valid=valid;
500 compass->way_heading=heading;
502 gtk_widget_queue_draw_area(widget, 0, 0, compass->width, compass->height);
506 gtk_compass_set_dest_heading(GtkWidget *widget, gboolean valid, gfloat heading)
509 g_return_if_fail(GTK_IS_COMPASS(widget));
511 compass=GTK_COMPASS(widget);
513 compass->dest_valid=valid;
514 compass->dest_heading=heading;
516 compass=GTK_COMPASS(widget);
517 gtk_widget_queue_draw_area(widget, 0, 0, compass->width, compass->height);