]> err.no Git - mapper/blob - src/gtkgps.c
Add signal display mode.
[mapper] / src / gtkgps.c
1 /*
2  *
3  * GtkGps: Display GPS information
4  * Copyright (C) 2007 Kaj-Michael Lang
5  * Originanl non-widget version Copyright Cezary Jackiewicz
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the Free
19  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21
22 #include <math.h>
23
24 #include <glib/gstdio.h>
25 #include <glib-object.h>
26 #include "gtkgps.h"
27
28 static void gtk_gps_finalize (GObject *object);
29 static void gtk_gps_size_request (GtkWidget *widget, GtkRequisition *requisition);
30 static void gtk_gps_size_allocate (GtkWidget *widget, GtkAllocation *allocate);
31 static gint gtk_gps_expose (GtkWidget *widget, GdkEventExpose *event);
32 static void gtk_gps_realize (GtkWidget *widget);
33 static void gtk_gps_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
34 static void gtk_gps_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
35 static void gtk_gps_paint_by_mode(GtkGps *gps);
36
37 G_DEFINE_TYPE(GtkGps, gtk_gps, GTK_TYPE_WIDGET);
38
39 #define PADDING (15)
40
41 #define BOUND(x, a, b) { \
42     if((x) < (a)) \
43         (x) = (a); \
44     else if((x) > (b)) \
45         (x) = (b); \
46 }
47
48 static void
49 gtk_gps_class_init (GtkGpsClass *class)
50 {
51 GObjectClass *object_class;
52 GtkWidgetClass *widget_class;
53         
54 object_class = (GObjectClass*) class;
55 widget_class = (GtkWidgetClass*) class;
56         
57 object_class->finalize = gtk_gps_finalize;
58 object_class->set_property = gtk_gps_set_property;
59 object_class->get_property = gtk_gps_get_property;
60         
61 widget_class->size_request = gtk_gps_size_request;
62 widget_class->expose_event = gtk_gps_expose;
63 widget_class->realize = gtk_gps_realize;
64 widget_class->size_allocate = gtk_gps_size_allocate;
65 }
66
67 static void
68 gtk_gps_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
69 {
70 GtkGps *gps;
71 g_return_if_fail(GTK_IS_GPS(object));
72 gps=GTK_GPS(object);
73 switch (prop_id) {
74         default:
75                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
76         break;
77 }
78 }
79
80 static void
81 gtk_gps_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec   *pspec)
82 {
83 GtkGps *gps;
84 g_return_if_fail(GTK_IS_GPS(object));
85 gps=GTK_GPS(object);
86 switch (prop_id) {
87         default:
88                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
89         break;
90 }
91 }
92
93 static void
94 gtk_gps_init (GtkGps *gps)
95 {
96 g_printf("%s()\n", __PRETTY_FUNCTION__);
97
98 gps->satinview=0;
99 gps->satinuse=0;
100 gps->gc1=NULL;
101 gps->gc2=NULL;
102 gps->gc3=NULL;
103 gps->width=400;
104 gps->height=300;
105 }
106
107 static gboolean 
108 gtk_gps_cb_button_press(GtkWidget * widget, GdkEventButton * event)
109 {
110 GtkGps *gps;
111
112 printf("%s()\n", __PRETTY_FUNCTION__);
113 gps=GTK_GPS(widget);
114
115 if (event->button==1) {
116         switch (gps->display_mode) {
117         case GTK_GPS_MODE_SKY:
118                 gps->display_mode=GTK_GPS_MODE_SIGNAL;
119         break;
120         case GTK_GPS_MODE_SIGNAL:
121         default:
122                 gps->display_mode=GTK_GPS_MODE_SKY;
123         break;
124         }
125 }
126 gtk_gps_paint_by_mode(gps);
127 return FALSE;
128 }
129
130 GtkWidget*
131 gtk_gps_new(GtkGpsMode display_mode)
132 {
133 GtkGps *gps;
134 GtkWidget *widget;
135
136 gps=gtk_type_new(gtk_gps_get_type ());
137 gps->display_mode=display_mode;
138 widget=GTK_WIDGET(gps);
139 g_signal_connect(G_OBJECT(widget), "button_press_event", G_CALLBACK(gtk_gps_cb_button_press), NULL);
140 return GTK_WIDGET(gps);
141 }
142
143 static void
144 gtk_gps_finalize(GObject *object)
145 {
146 GtkGps *gps;
147
148 g_printf("%s()\n", __PRETTY_FUNCTION__);
149         
150 g_return_if_fail(GTK_IS_GPS(object));
151 gps=GTK_GPS(object);
152
153 if (GTK_WIDGET(object)->parent && GTK_WIDGET_MAPPED(object)) {
154         gtk_widget_unmap(GTK_WIDGET(object));
155 }
156
157 G_OBJECT_CLASS(gtk_gps_parent_class)->finalize(object);
158 }
159
160 static void
161 gtk_gps_size_request(GtkWidget  *widget, GtkRequisition *requisition)
162 {
163 GtkGps *gps;
164
165 g_printf("%s()\n", __PRETTY_FUNCTION__);
166         
167 g_return_if_fail(GTK_IS_GPS(widget));
168 g_return_if_fail(requisition != NULL);
169         
170 gps = GTK_GPS (widget);
171         
172 requisition->width=400;
173 requisition->height=300;
174 gps->width=400 - 10;
175 gps->height=300 - 10;
176 }
177
178 static void
179 gtk_gps_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
180 {
181 GtkGps *gps;
182
183 g_printf("%s()\n", __PRETTY_FUNCTION__);
184
185 g_return_if_fail(GTK_IS_GPS(widget));
186 g_return_if_fail(allocation!=NULL);
187
188 gps=GTK_GPS (widget);
189
190 widget->allocation = *allocation;
191
192 if (GTK_WIDGET_REALIZED (widget)) {
193         gdk_window_move_resize (widget->window,
194                 allocation->x, allocation->y,
195                 allocation->width, allocation->height);
196 }
197
198 gps->width=allocation->width-PADDING;
199 gps->height=allocation->height-PADDING;
200 }
201
202 static void 
203 gtk_gps_realize (GtkWidget *widget)
204 {
205 GtkGps *gps;
206 GdkColor color;
207 GdkWindowAttr attributes;
208 gint attributes_mask;
209
210 g_printf("%s()\n", __PRETTY_FUNCTION__);
211
212 g_return_if_fail (GTK_IS_GPS(widget));
213 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
214 gps=GTK_GPS(widget);
215
216 attributes.x=widget->allocation.x;
217 attributes.y=widget->allocation.y;
218 attributes.width=widget->allocation.width;
219 attributes.height=widget->allocation.height;
220 attributes.wclass=GDK_INPUT_OUTPUT;
221 attributes.window_type=GDK_WINDOW_CHILD;
222 attributes.event_mask=gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
223 attributes.visual=gtk_widget_get_visual(widget);
224 attributes.colormap=gtk_widget_get_colormap(widget);
225
226 attributes_mask=GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
227
228 widget->window=gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributes_mask);
229 widget->style=gtk_style_attach(widget->style, widget->window);
230
231 gdk_window_set_user_data(widget->window, widget);
232 gtk_style_set_background(widget->style, widget->window, GTK_STATE_ACTIVE);
233
234 gps->sat_details_context=gtk_widget_get_pango_context(widget);
235 gps->sat_details_layout=pango_layout_new(gps->sat_details_context);
236 gps->sat_details_fontdesc=pango_font_description_new();
237 pango_font_description_set_family(gps->sat_details_fontdesc, "Sans Serif");
238 pango_font_description_set_size(gps->sat_details_fontdesc, 12*PANGO_SCALE);
239 pango_layout_set_font_description(gps->sat_details_layout, gps->sat_details_fontdesc);
240 pango_layout_set_alignment(gps->sat_details_layout, PANGO_ALIGN_CENTER);
241
242 if (!gps->gc1) {
243         color.red=0;
244         color.green=0;
245         color.blue=0;
246         gps->gc1=gdk_gc_new(widget->window);
247         gdk_gc_set_rgb_fg_color(gps->gc1, &color);
248 }
249
250 if (!gps->gc2) {
251         color.red=0;
252         color.green=0;
253         color.blue=0xffff;
254         gps->gc2=gdk_gc_new(widget->window);
255         gdk_gc_set_rgb_fg_color(gps->gc2, &color);
256 }
257
258 if (!gps->gc3) {
259         color.red=0xffff;
260         color.green=0xffff;
261         color.blue=0xffff;
262         gps->gc3=gdk_gc_new(widget->window);
263         gdk_gc_set_rgb_fg_color(gps->gc3, &color);
264 }
265 }
266
267 static void
268 gtk_gps_paint_sky(GtkGps *gps)
269 {
270 GdkGC *gc;
271 GtkWidget *widget;
272 guint i, j, x, y, size, halfsize, xoffset, yoffset;
273 guint x1, y1, x0, y0;
274 gfloat tmp;
275 gchar buffer[16];
276 guint line[12]={0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330};
277
278 g_printf("%s()\n", __PRETTY_FUNCTION__);
279
280 widget=GTK_WIDGET(gps);
281
282 if (!GTK_WIDGET_MAPPED(widget))
283         return;
284
285 x0=0;
286 y0=0;
287
288 size=MIN(gps->width, gps->height);
289 halfsize=size/2;
290 if (gps->width>gps->height) {
291         xoffset=x0+(gps->width-gps->height-PADDING)/2;
292         yoffset=y0+5;
293 } else {
294         xoffset=x0+5;
295         yoffset=y0+(gps->height-gps->width-PADDING)/2;
296 }
297
298 /* 90 */
299 gdk_draw_arc(widget->window,
300              widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
301              FALSE,
302              xoffset + 2, yoffset + 2, size - 4, size - 4, 0, 64 * 360);
303
304 /* 60 */
305 gdk_draw_arc(widget->window,
306              widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
307              FALSE,
308              xoffset + size / 6, yoffset + size / 6,
309              size / 6 * 4, size / 6 * 4, 0, 64 * 360);
310
311 /* 30 */
312 gdk_draw_arc(widget->window,
313              widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
314              FALSE,
315              xoffset + size / 6 * 2, yoffset + size / 6 * 2,
316              size / 6 * 2, size / 6 * 2, 0, 64 * 360);
317
318 for (i = 0; i < 6; i++) {
319         /* line */
320         tmp = (line[i] * (1.f / 180.f)) * G_PI;
321         gdk_draw_line(widget->window,
322                       widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
323                       xoffset + halfsize + (halfsize - 2) * sinf(tmp),
324                       yoffset + halfsize - (halfsize - 2) * cosf(tmp),
325                       xoffset + halfsize - (halfsize - 2) * sinf(tmp),
326                       yoffset + halfsize + (halfsize - 2) * cosf(tmp));
327 }
328
329 for (i = 0; i < 12; i++) {
330         tmp = (line[i] * (1.f / 180.f)) * G_PI;
331         /* azimuth */
332         if (line[i] == 0)
333                 g_snprintf(buffer, 16, "N");
334         else
335                 g_snprintf(buffer, 16, "%d°", line[i]);
336         pango_layout_set_text(gps->sat_details_layout, buffer, -1);
337         pango_layout_get_pixel_size(gps->sat_details_layout, &x, &y);
338         gdk_draw_layout(widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
339                         (xoffset + halfsize + (halfsize - size / 12) * sinf(tmp)) - x / 2,
340                         (yoffset + halfsize - (halfsize - size / 12) * cosf(tmp)) - y / 2,
341                         gps->sat_details_layout);
342 }
343
344 /* elevation 30 */
345 tmp = (30 * (1.f / 180.f)) * G_PI;
346 g_snprintf(buffer, 16, "30°");
347 pango_layout_set_text(gps->sat_details_layout, buffer, -1);
348 pango_layout_get_pixel_size(gps->sat_details_layout, &x, &y);
349 gdk_draw_layout(widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
350                 (xoffset + halfsize + size / 6 * 2 * sinf(tmp)) - x / 2,
351                 (yoffset + halfsize - size / 6 * 2 * cosf(tmp)) - y / 2,
352                 gps->sat_details_layout);
353
354 /* elevation 60 */
355 tmp = (30 * (1.f / 180.f)) * G_PI;
356 g_snprintf(buffer, 16, "60°");
357 pango_layout_set_text(gps->sat_details_layout, buffer, -1);
358 pango_layout_get_pixel_size(gps->sat_details_layout, &x, &y);
359 gdk_draw_layout(widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
360                 (xoffset + halfsize + size / 6 * sinf(tmp)) - x / 2,
361                 (yoffset + halfsize - size / 6 * cosf(tmp)) - y / 2, 
362                 gps->sat_details_layout);
363
364 for (i=0;i<gps->satinview;i++) {
365         /* Sat used or not */
366         gc=gps->gc1;
367         for (j=0;j<gps->satinuse;j++) {
368                 if (gps->satforfix[j]==gps->gps_sat[i].prn) {
369                         gc=gps->gc2;
370                         break;
371                 }
372         }
373
374         tmp = (gps->gps_sat[i].azimuth * (1.f / 180.f)) * G_PI;
375         x = xoffset + halfsize + (90 - gps->gps_sat[i].elevation) * halfsize / 90 * sinf(tmp);
376         y = yoffset + halfsize - (90 - gps->gps_sat[i].elevation) * halfsize / 90 * cosf(tmp);
377
378         gdk_draw_arc(widget->window, gc, TRUE, x - 10, y - 10, 20, 20, 0, 64 * 360);
379
380         g_snprintf(buffer, 6, "%02d", gps->gps_sat[i].prn);
381         pango_layout_set_text(gps->sat_details_layout, buffer, -1);
382         pango_layout_get_pixel_size(gps->sat_details_layout, &x1, &y1);
383         gdk_draw_layout(widget->window, gps->gc3, x - x1 / 2, y - y1 / 2, gps->sat_details_layout);
384 }
385
386 g_printf("%s(): return\n", __PRETTY_FUNCTION__);
387 return;
388 }
389
390 static void
391 gtk_gps_paint_signals(GtkGps *gps)
392 {
393 GdkGC *gc;
394 GtkWidget *widget;
395 guint step, i, j, snr_height, bymargin, xoffset, yoffset;
396 guint x, y, x1, y1;
397 gchar tmp[32];
398
399 g_printf("%s()\n", __PRETTY_FUNCTION__);
400
401 widget=GTK_WIDGET(gps);
402
403 if (!GTK_WIDGET_MAPPED(widget))
404         return;
405
406 xoffset = 0;
407 yoffset = 0;
408
409 /* Bootom margin - 12% */
410 bymargin = gps->height * 0.88f;
411
412 /* Bottom line */
413 gdk_draw_line(widget->window,
414               widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
415               xoffset + 5, yoffset + bymargin,
416               xoffset + gps->width - 10 - 2, yoffset + bymargin);
417 gdk_draw_line(widget->window,
418               widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
419               xoffset + 5, yoffset + bymargin - 1,
420               xoffset + gps->width - 10 - 2, yoffset + bymargin - 1);
421
422 if (gps->satinview > 0) {
423         /* Left margin - 5pix, Right margin - 5pix */
424         step = (gps->width - 10) / gps->satinview;
425
426         for (i = 0; i < gps->satinview; i++) {
427                 /* Sat used or not */
428                 gc = gps->gc1;
429                 for (j = 0; j < gps->satinuse; j++) {
430                         if (gps->satforfix[j] == gps->gps_sat[i].prn) {
431                                 gc = gps->gc2;
432                                 break;
433                         }
434                 }
435
436                 x = 5 + i * step;
437                 snr_height = gps->gps_sat[i].snr * gps->height * 0.78f / 100;
438                 y = gps->height * 0.1f + (gps->height * 0.78f - snr_height);
439
440                 /* draw sat rectangle... */
441                 gdk_draw_rectangle(widget->window,
442                                    gc, TRUE,
443                                    xoffset + x,
444                                    yoffset + y, step - 2, snr_height);
445
446                 if (gps->gps_sat[i].snr > 0) {
447                         /* ...snr.. */
448                         g_snprintf(tmp, 32, "%02d", gps->gps_sat[i].snr);
449                         pango_layout_set_text(gps->sat_details_layout, tmp, 2);
450                         pango_layout_get_pixel_size(gps->sat_details_layout, &x1, &y1);
451                         gdk_draw_layout(widget->window,
452                                         widget->style->fg_gc[GTK_STATE_NORMAL],
453                                         xoffset + x + ((step - 2) - x1) / 2,
454                                                 yoffset + y - 15,
455                                                 gps->sat_details_layout);
456                 }
457
458                 /* ...and sat number */
459                 g_snprintf(tmp, 32, "%02d", gps->gps_sat[i].prn);
460                 pango_layout_set_text(gps->sat_details_layout, tmp, 2);
461                 pango_layout_get_pixel_size(gps->sat_details_layout, &x1, &y1);
462                 gdk_draw_layout(widget->window,
463                                 widget->style->fg_gc[GTK_STATE_NORMAL],
464                                 xoffset + x + ((step - 2) - x1) / 2,
465                                 yoffset + bymargin + 1,
466                                 gps->sat_details_layout);
467         }
468 }
469
470 return;
471 }
472
473 static void
474 gtk_gps_paint_by_mode(GtkGps *gps)
475 {
476 switch (gps->display_mode) {
477         case GTK_GPS_MODE_SIGNAL:
478                 gtk_gps_paint_signals(gps);
479         break;
480         case GTK_GPS_MODE_SKY:
481         default:
482                 gtk_gps_paint_sky(gps);
483         break;
484 }
485 }
486
487 static gint
488 gtk_gps_expose(GtkWidget *widget, GdkEventExpose *event)
489 {
490 GtkGps *gps;
491 g_return_val_if_fail(GTK_IS_GPS(widget), FALSE);
492 g_return_val_if_fail(event != NULL, FALSE);
493
494 gps=GTK_GPS(widget);
495 gtk_gps_paint_by_mode(gps);
496 return TRUE;
497 }
498
499 void 
500 gtk_gps_set_satellite_data(GtkWidget *widget, guint sat, gboolean fix, guint prn, guint elevation, guint azimuth, guint snr)
501 {
502 GtkGps *gps;
503
504 g_printf("%s()\n", __PRETTY_FUNCTION__);
505 g_return_if_fail(GTK_IS_GPS (widget));
506
507 gps=GTK_GPS(widget);
508
509 if (sat>12)
510         sat=12;
511
512 BOUND(elevation, 0, 90);
513 BOUND(azimuth, 0, 360);
514 BOUND(snr, 0, 99);
515 gps->gps_sat[sat].prn=prn;
516 gps->gps_sat[sat].elevation=elevation;
517 gps->gps_sat[sat].azimuth=azimuth;
518 gps->gps_sat[sat].snr=snr;
519 if (fix)
520         gps->satforfix[sat]=prn;
521 else
522         gps->satforfix[sat]=-1;
523 g_printf("%s(): return\n", __PRETTY_FUNCTION__);
524 }
525
526 void
527 gtk_gps_set_satellite_in_view(GtkWidget *widget, guint sat)
528 {
529 GtkGps *gps;
530
531 g_printf("%s()\n", __PRETTY_FUNCTION__);
532 g_return_if_fail(GTK_IS_GPS(widget));
533
534 gps=GTK_GPS(widget);
535
536 if (sat>12)
537         sat=12;
538
539 gps->satinview=sat;
540 gtk_gps_paint_by_mode(gps);
541
542 g_printf("%s(): return\n", __PRETTY_FUNCTION__);
543 }