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