]> err.no Git - mapper/blob - src/gtkcompass.c
Add header and cast properly.
[mapper] / src / gtkcompass.c
1 /*
2  *
3  * GtkCompass: Display heading/compass information
4  * Also bearing to target and next waypoint.
5  *
6  * Copyright (C) 2007 Kaj-Michael Lang
7  * Originanl non-widget version Copyright Cezary Jackiewicz
8  *
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.
13  *
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.
18  *
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.
22  */
23
24 #include <math.h>
25 #include <stdlib.h>
26
27 #include <glib/gstdio.h>
28 #include <glib-object.h>
29 #include "gtkcompass.h"
30
31 #if 0
32 #define DEBUG
33 #endif
34
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);
44
45 G_DEFINE_TYPE(GtkCompass, gtk_compass, GTK_TYPE_WIDGET);
46
47 #define BOUND(x, a, b) { \
48         if((x) < (a)) \
49                 (x) = (a); \
50         else if((x) > (b)) \
51                 (x) = (b); \
52 }
53
54 typedef struct _GtkCompassPriv GtkCompassPriv;
55  
56 struct _GtkCompassPriv
57
58 PangoContext *context;
59 PangoLayout *layout;
60 PangoFontDescription *fontdesc;
61 };
62
63 #define GTK_COMPASS_GET_PRIVATE(o) \
64         (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_COMPASS, GtkCompassPriv))
65
66 static void
67 gtk_compass_class_init (GtkCompassClass *class)
68 {
69 GObjectClass *object_class;
70 GtkWidgetClass *widget_class;
71         
72 object_class = (GObjectClass*) class;
73 widget_class = (GtkWidgetClass*) class;
74         
75 object_class->finalize = gtk_compass_finalize;
76 object_class->set_property = gtk_compass_set_property;
77 object_class->get_property = gtk_compass_get_property;
78         
79 widget_class->size_request = gtk_compass_size_request;
80 widget_class->expose_event = gtk_compass_expose;
81 widget_class->realize = gtk_compass_realize;
82 widget_class->size_allocate = gtk_compass_size_allocate;
83
84 g_type_class_add_private (object_class, sizeof (GtkCompassPriv));
85 }
86
87 static void
88 gtk_compass_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
89 {
90 GtkCompass *compass;
91 g_return_if_fail(GTK_IS_COMPASS(object));
92 compass=GTK_COMPASS(object);
93 switch (prop_id) {
94         default:
95                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
96         break;
97 }
98 }
99
100 static void
101 gtk_compass_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec   *pspec)
102 {
103 GtkCompass *compass;
104 g_return_if_fail(GTK_IS_COMPASS(object));
105 compass=GTK_COMPASS(object);
106 switch (prop_id) {
107         default:
108                 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
109         break;
110 }
111 }
112
113 static void
114 gtk_compass_init(GtkCompass *compass)
115 {
116 compass->gc_h=NULL;
117 compass->gc_d=NULL;
118 compass->gc_w=NULL;
119 compass->dest_valid=FALSE;
120 compass->way_valid=FALSE;
121 compass->width=300;
122 compass->height=300;
123 compass->esid=0;
124 }
125
126 static gboolean 
127 gtk_compass_cb_button_press(GtkWidget *widget, GdkEventButton *event)
128 {
129 GtkCompass *compass;
130
131 compass=GTK_COMPASS(widget);
132
133 #ifdef DEBUG
134 compass->data->heading=0;
135 compass->heading=180;
136 #endif
137
138 return FALSE;
139 }
140
141 GtkWidget*
142 gtk_compass_new(GpsData *data)
143 {
144 GtkCompass *compass;
145 GtkWidget *widget;
146
147 compass=gtk_type_new(gtk_compass_get_type ());
148 widget=GTK_WIDGET(compass);
149 compass->data=data;
150 compass->heading=0;
151 g_signal_connect(G_OBJECT(widget), "button_press_event", G_CALLBACK(gtk_compass_cb_button_press), NULL);
152
153 compass->esid=g_timeout_add(1000,(GSourceFunc)gtk_compass_refresh_cb, compass);
154
155 return widget;
156 }
157
158 static void
159 gtk_compass_finalize(GObject *object)
160 {
161 GtkCompass *compass;
162         
163 g_return_if_fail(GTK_IS_COMPASS(object));
164 compass=GTK_COMPASS(object);
165 g_source_remove(compass->esid);
166
167 if (GTK_WIDGET(object)->parent && GTK_WIDGET_MAPPED(object)) {
168         gtk_widget_unmap(GTK_WIDGET(object));
169 }
170
171 G_OBJECT_CLASS(gtk_compass_parent_class)->finalize(object);
172 }
173
174 static void
175 gtk_compass_size_request(GtkWidget      *widget, GtkRequisition *requisition)
176 {
177 GtkCompass *compass;
178         
179 g_return_if_fail(GTK_IS_COMPASS(widget));
180 g_return_if_fail(requisition != NULL);
181         
182 compass=GTK_COMPASS(widget);
183         
184 requisition->width=400;
185 requisition->height=300;
186 compass->width=400;
187 compass->height=300;
188 }
189
190 static void
191 gtk_compass_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
192 {
193 GtkCompass *compass;
194
195 g_return_if_fail(GTK_IS_COMPASS(widget));
196 g_return_if_fail(allocation!=NULL);
197
198 compass=GTK_COMPASS(widget);
199
200 widget->allocation=*allocation;
201
202 if (GTK_WIDGET_REALIZED (widget)) {
203         gdk_window_move_resize (widget->window,
204                 allocation->x, allocation->y,
205                 allocation->width, allocation->height);
206 }
207
208 compass->width=allocation->width;
209 compass->height=allocation->height;
210
211 compass->size = MIN(widget->allocation.width, widget->allocation.height);
212 if (widget->allocation.width > widget->allocation.height) {
213         compass->xoffset = (widget->allocation.width - widget->allocation.height) / 2;
214         compass->yoffset = 0;
215 } else {
216         compass->xoffset = 0;
217         compass->yoffset = (widget->allocation.height - widget->allocation.width) / 2;
218 }
219
220 }
221
222 static void 
223 gtk_compass_realize (GtkWidget *widget)
224 {
225 GtkCompass *compass;
226 GdkColor color;
227 GdkWindowAttr attributes;
228 gint attributes_mask;
229
230 g_return_if_fail (GTK_IS_COMPASS(widget));
231 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
232 compass=GTK_COMPASS(widget);
233
234 attributes.x=widget->allocation.x;
235 attributes.y=widget->allocation.y;
236 attributes.width=widget->allocation.width;
237 attributes.height=widget->allocation.height;
238 attributes.wclass=GDK_INPUT_OUTPUT;
239 attributes.window_type=GDK_WINDOW_CHILD;
240 attributes.event_mask=gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
241 attributes.visual=gtk_widget_get_visual(widget);
242 attributes.colormap=gtk_widget_get_colormap(widget);
243
244 attributes_mask=GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
245
246 widget->window=gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributes_mask);
247 widget->style=gtk_style_attach(widget->style, widget->window);
248
249 gdk_window_set_user_data(widget->window, widget);
250 gtk_style_set_background(widget->style, widget->window, GTK_STATE_ACTIVE);
251
252 compass->context=gtk_widget_get_pango_context(widget);
253 compass->layout=pango_layout_new(compass->context);
254 compass->fontdesc=pango_font_description_new();
255 pango_font_description_set_family(compass->fontdesc, "Sans Serif");
256 pango_font_description_set_size(compass->fontdesc, 12*PANGO_SCALE);
257 pango_layout_set_font_description(compass->layout, compass->fontdesc);
258 pango_layout_set_alignment(compass->layout, PANGO_ALIGN_CENTER);
259
260 if (!compass->gc_h) {
261         color.red=0x0000;
262         color.green=0x0000;
263         color.blue=0x0000;
264         compass->gc_h=gdk_gc_new(widget->window);
265         gdk_gc_set_rgb_fg_color(compass->gc_h, &color);
266         gdk_gc_set_line_attributes(compass->gc_h, 2, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
267 }
268
269 if (!compass->gc_d) {
270         color.red=0xffff;
271         color.green=0x0000;
272         color.blue=0xffff;
273         compass->gc_d=gdk_gc_new(widget->window);
274         gdk_gc_set_rgb_fg_color(compass->gc_d, &color);
275         gdk_gc_set_line_attributes(compass->gc_d, 6, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
276 }
277
278 if (!compass->gc_w) {
279         color.red=0x0000;
280         color.green=0xffff;
281         color.blue=0x0000;
282         compass->gc_w=gdk_gc_new(widget->window);
283         gdk_gc_set_rgb_fg_color(compass->gc_w, &color);
284         gdk_gc_set_line_attributes(compass->gc_w, 6, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
285 }
286
287 }
288
289 static void
290 gtk_compass_draw_mark(GtkCompass *compass, GdkGC *gc, gfloat angle, gint size)
291 {
292 GtkWidget *widget;
293 gint hs;
294
295 widget=GTK_WIDGET(compass);
296
297 hs=compass->size/2;
298
299 gdk_draw_line(widget->window,gc,
300         compass->xoffset+hs+((hs-size)*sinf(angle)),
301         compass->yoffset+compass->size-((hs-size)*cosf(angle)),
302         compass->xoffset+hs+((hs+size)*sinf(angle)),
303         compass->yoffset+compass->size-((hs+size)*cosf(angle)));
304 }
305
306 static void
307 gtk_compass_paint(GtkCompass *compass)
308 {
309 GtkWidget *widget;
310 guint i, x, y, size, hsize, fs;
311 gint dir;
312 gfloat tmp;
313 gchar *text;
314 gchar htext[8];
315 gint angle[5] = { -90, -45, 0, 45, 90 };
316 gint fsize[5] = { 0, 4, 10, 4, 0 };
317
318 widget=GTK_WIDGET(compass);
319 size=compass->size;
320 #if 1
321 hsize=size/2;
322 #else
323 hsize=0;
324 #endif
325
326 fs=size/41;
327 BOUND(fs, 1, 16);
328
329 pango_context_set_matrix (compass->context, NULL);
330
331 g_snprintf(htext, 8, "%3.0f°", compass->data->heading);
332 pango_font_description_set_size(compass->fontdesc, (10+fs) * PANGO_SCALE);
333 pango_layout_set_font_description(compass->layout, compass->fontdesc);
334 pango_layout_set_text(compass->layout, htext, -1);
335 pango_layout_get_pixel_size(compass->layout, &x, &y);
336
337 gdk_draw_layout(widget->window,
338         compass->gc_h,
339         compass->xoffset+hsize-x/2,
340         compass->yoffset+size-y-2, compass->layout);
341
342 gdk_draw_arc(widget->window,
343         compass->gc_h,
344         FALSE,
345         compass->xoffset, compass->yoffset+hsize, size - fs, size - fs, 0, 64 * 360);
346
347 /* Simple arrow for heading */
348 gdk_draw_line(widget->window,
349         compass->gc_h,
350         compass->xoffset + hsize + 3,
351         compass->yoffset + size - y - 5,
352         compass->xoffset + hsize, compass->yoffset + hsize + 5);
353
354 gdk_draw_line(widget->window,
355         compass->gc_h,
356         compass->xoffset + hsize - 3,
357         compass->yoffset + size - y - 5,
358         compass->xoffset + hsize, compass->yoffset + hsize + 5);
359
360 gdk_draw_line(widget->window,
361         compass->gc_h,
362         compass->xoffset + hsize - 3,
363         compass->yoffset + size - y - 5,
364         compass->xoffset + hsize, compass->yoffset + size - y - 8);
365
366 gdk_draw_line(widget->window,
367         compass->gc_h,
368         compass->xoffset + hsize + 3,
369         compass->yoffset + size - y - 5,
370         compass->xoffset + hsize, compass->yoffset + size - y - 8);
371
372 for (i = 0; i < 5; i++) {
373         PangoMatrix matrix = PANGO_MATRIX_INIT;
374         dir = (gint) (compass->heading / 45) * 45 + angle[i];
375
376         switch (dir) {
377         case 0:
378         case 360:
379                 text = "N";
380                 break;
381         case 45:
382         case 405:
383                 text = "NE";
384                 break;
385         case 90:
386                 text = "E";
387                 break;
388         case 135:
389                 text = "SE";
390                 break;
391         case 180:
392                 text = "S";
393                 break;
394         case 225:
395                 text = "SW";
396                 break;
397         case 270:
398         case -90:
399                 text = "W";
400                 break;
401         case 315:
402         case -45:
403                 text = "NW";
404                 break;
405         default:
406                 text = "??";
407                 break;
408         }
409
410         tmp = ((dir - compass->heading) * (1.f / 180.f)) * G_PI;
411
412         gtk_compass_draw_mark(compass, compass->gc_h, tmp, 6);
413
414         x = fsize[i];
415         if (abs((guint) (compass->heading / 45) * 45 - compass->heading)
416             > abs((guint) (compass->heading / 45) * 45 + 45 - compass->heading) && (i > 0))
417                         x = fsize[i - 1];
418
419         pango_font_description_set_size(compass->fontdesc, (10 + x + fs) * PANGO_SCALE);
420         pango_layout_set_font_description(compass->layout, compass->fontdesc);
421         pango_layout_set_text(compass->layout, text, -1);
422         pango_matrix_rotate (&matrix, -(dir-compass->heading));
423         pango_context_set_matrix (compass->context, &matrix);
424         pango_layout_get_pixel_size(compass->layout, &x, &y);
425         x = compass->xoffset + hsize + ((hsize + 15 + fs) * sinf(tmp)) - x / 2,
426     y = compass->yoffset + size - ((hsize + 15 + fs) * cosf(tmp)) - y / 2,
427     gdk_draw_layout(widget->window, compass->gc_h, x, y, compass->layout);
428 }
429
430 if (compass->dest_valid) {
431         tmp=((compass->dest_heading-compass->heading) * (1.f / 180.f)) * G_PI;
432         gtk_compass_draw_mark(compass, compass->gc_d, tmp, 10);
433 }
434
435 if (compass->way_valid) {
436         tmp=((compass->way_heading-compass->heading) * (1.f / 180.f)) * G_PI;
437         gtk_compass_draw_mark(compass, compass->gc_w, tmp, 10);
438 }
439
440 return;
441 }
442
443 static gboolean
444 gtk_compass_expose(GtkWidget *widget, GdkEventExpose *event)
445 {
446 GtkCompass *compass;
447
448 g_return_val_if_fail(GTK_IS_COMPASS(widget), FALSE);
449 g_return_val_if_fail(event != NULL, FALSE);
450
451 compass=GTK_COMPASS(widget);
452 gtk_compass_paint(compass);
453 return TRUE;
454 }
455
456 static gboolean
457 gtk_compass_refresh_cb(GtkWidget *widget)
458 {
459 GtkCompass *compass;
460 gfloat tmp;
461
462 g_return_val_if_fail(GTK_IS_COMPASS(widget), FALSE);
463
464 compass=GTK_COMPASS(widget);
465
466 if ((GTK_WIDGET_MAPPED(widget)==FALSE) || (GTK_WIDGET_VISIBLE(widget)==FALSE)) {
467         compass->heading=compass->data->heading;
468         return TRUE;
469 }
470
471 if (compass->heading==compass->data->heading)
472         return TRUE;
473
474 tmp=fabsf(compass->heading-compass->data->heading);
475 if (tmp>5)
476         tmp=tmp/2.2;
477 else
478         compass->heading=compass->data->heading;
479
480 if (compass->heading<compass->data->heading)
481         compass->heading+=tmp;
482
483 if (compass->heading>compass->data->heading)
484         compass->heading-=tmp;
485
486 #ifdef DEBUG
487 g_printf("%.2f %.2f\n", compass->heading, compass->data->heading);
488 #endif
489
490 gtk_widget_queue_draw_area(widget, 0, 0, compass->width, compass->height);
491 return TRUE;
492 }
493
494 void
495 gtk_compass_refresh(GtkWidget *widget)
496 {
497 GtkCompass *compass;
498
499 g_return_if_fail(GTK_IS_COMPASS(widget));
500
501 compass=GTK_COMPASS(widget);
502 gtk_widget_queue_draw_area(widget, 0, 0, compass->width, compass->height);
503 }
504
505 void 
506 gtk_compass_set_way_heading(GtkWidget *widget, gboolean valid, gfloat heading)
507 {
508 GtkCompass *compass;
509 g_return_if_fail(GTK_IS_COMPASS(widget));
510
511 compass=GTK_COMPASS(widget);
512
513 compass->way_valid=valid;
514 compass->way_heading=heading;
515
516 gtk_widget_queue_draw_area(widget, 0, 0, compass->width, compass->height);
517 }
518
519 void 
520 gtk_compass_set_dest_heading(GtkWidget *widget, gboolean valid, gfloat heading)
521 {
522 GtkCompass *compass;
523 g_return_if_fail(GTK_IS_COMPASS(widget));
524
525 compass=GTK_COMPASS(widget);
526
527 compass->dest_valid=valid;
528 compass->dest_heading=heading;
529
530 compass=GTK_COMPASS(widget);
531 gtk_widget_queue_draw_area(widget, 0, 0, compass->width, compass->height);
532 }
533