]> err.no Git - mapper/blob - src/gtkgps.c
Some more work on the gps widgets.
[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
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
72 g_return_if_fail(GTK_IS_GPS(object));
73
74 gps=GTK_GPS(object);
75 switch (prop_id) {
76         default:
77                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
78         break;
79 }
80 }
81
82 static void
83 gtk_gps_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec   *pspec)
84 {
85 GtkGps *gps;
86
87 g_return_if_fail(GTK_IS_GPS(object));
88
89 gps=GTK_GPS(object);
90 switch (prop_id) {
91         default:
92                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
93         break;
94 }
95 }
96
97 static void
98 gtk_gps_init(GtkGps *gps)
99 {
100 g_printf("%s()\n", __PRETTY_FUNCTION__);
101
102 gps->gc1=NULL;
103 gps->gc2=NULL;
104 gps->gc3=NULL;
105 gps->width=300;
106 gps->height=300;
107 gps->size=300;
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_gps_paint_by_mode(gps);
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 g_signal_connect(G_OBJECT(widget), "button_press_event", G_CALLBACK(gtk_gps_cb_button_press), NULL);
143
144 gps->context=gtk_widget_get_pango_context(widget);
145 gps->layout=pango_layout_new(gps->context);
146 gps->fontdesc=pango_font_description_new();
147 pango_font_description_set_family(gps->fontdesc, "Sans Serif");
148 pango_font_description_set_size(gps->fontdesc, 10*PANGO_SCALE);
149 pango_layout_set_font_description(gps->layout, gps->fontdesc);
150 pango_layout_set_alignment(gps->layout, PANGO_ALIGN_CENTER);
151
152 return GTK_WIDGET(gps);
153 }
154
155 static void
156 gtk_gps_finalize(GObject *object)
157 {
158 GtkGps *gps;
159
160 g_return_if_fail(GTK_IS_GPS(object));
161 gps=GTK_GPS(object);
162
163 if (GTK_WIDGET(object)->parent && GTK_WIDGET_MAPPED(object)) {
164         gtk_widget_unmap(GTK_WIDGET(object));
165 }
166
167 G_OBJECT_CLASS(gtk_gps_parent_class)->finalize(object);
168 }
169
170 static void
171 gtk_gps_size_request(GtkWidget  *widget, GtkRequisition *requisition)
172 {
173 GtkGps *gps;
174
175 g_printf("%s()\n", __PRETTY_FUNCTION__);
176         
177 g_return_if_fail(GTK_IS_GPS(widget));
178 g_return_if_fail(requisition!=NULL);
179         
180 gps = GTK_GPS (widget);
181         
182 requisition->width=300;
183 requisition->height=300;
184 gps->width=300;
185 gps->height=300;
186 gps->size=300;
187 }
188
189 static void
190 gtk_gps_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
191 {
192 GtkGps *gps;
193 gint fs;
194
195 g_return_if_fail(GTK_IS_GPS(widget));
196 g_return_if_fail(allocation!=NULL);
197
198 gps=GTK_GPS (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 gps->width=allocation->width;
209 gps->height=allocation->height;
210 gps->size=MIN(gps->width, gps->height);
211
212 gps->fs=gps->size/20;
213 BOUND(gps->fs,8,20);
214
215 pango_font_description_set_size(gps->fontdesc, gps->fs*PANGO_SCALE);
216 pango_layout_set_font_description(gps->layout, gps->fontdesc);
217 }
218
219 static void 
220 gtk_gps_realize (GtkWidget *widget)
221 {
222 GtkGps *gps;
223 GdkColor color;
224 GdkWindowAttr attributes;
225 gint attributes_mask;
226
227 g_return_if_fail (GTK_IS_GPS(widget));
228 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
229 gps=GTK_GPS(widget);
230
231 attributes.x=widget->allocation.x;
232 attributes.y=widget->allocation.y;
233 attributes.width=widget->allocation.width;
234 attributes.height=widget->allocation.height;
235 attributes.wclass=GDK_INPUT_OUTPUT;
236 attributes.window_type=GDK_WINDOW_CHILD;
237 attributes.event_mask=gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
238 attributes.visual=gtk_widget_get_visual(widget);
239 attributes.colormap=gtk_widget_get_colormap(widget);
240
241 attributes_mask=GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
242
243 widget->window=gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributes_mask);
244 widget->style=gtk_style_attach(widget->style, widget->window);
245
246 gdk_window_set_user_data(widget->window, widget);
247 gtk_style_set_background(widget->style, widget->window, GTK_STATE_ACTIVE);
248
249 if (!gps->gc1) {
250         color.red=0;
251         color.green=0;
252         color.blue=0;
253         gps->gc1=gdk_gc_new(widget->window);
254         gdk_gc_set_rgb_fg_color(gps->gc1, &color);
255 }
256
257 if (!gps->gc2) {
258         color.red=0;
259         color.green=0;
260         color.blue=0xffff;
261         gps->gc2=gdk_gc_new(widget->window);
262         gdk_gc_set_rgb_fg_color(gps->gc2, &color);
263 }
264
265 if (!gps->gc3) {
266         color.red=0xffff;
267         color.green=0xffff;
268         color.blue=0xffff;
269         gps->gc3=gdk_gc_new(widget->window);
270         gdk_gc_set_rgb_fg_color(gps->gc3, &color);
271 }
272 }
273
274 static void
275 gtk_gps_paint_sky(GtkGps *gps)
276 {
277 GdkGC *gc;
278 GtkWidget *widget;
279 guint i, x, y, size, halfsize, xoffset, yoffset;
280 guint x1, y1, x0, y0, ds;
281 gfloat tmp;
282 gchar buffer[16];
283 guint line[12]={0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330};
284
285 g_printf("%s()\n", __PRETTY_FUNCTION__);
286
287 widget=GTK_WIDGET(gps);
288
289 g_return_if_fail(gps->data);
290
291 if (!GTK_WIDGET_MAPPED(widget))
292         return;
293
294 x0=0;
295 y0=0;
296
297 size=gps->size;
298 halfsize=size/2;
299 if (gps->width>gps->height) {
300         xoffset=x0+(gps->width-gps->height-PADDING)/2;
301         yoffset=y0+5;
302 } else {
303         xoffset=x0+5;
304         yoffset=y0+(gps->height-gps->width-PADDING)/2;
305 }
306
307 gtk_paint_flat_box (widget->style,
308         widget->window,
309         GTK_STATE_NORMAL, GTK_SHADOW_NONE,
310         NULL, widget, "trough", 0, 0, 
311         gps->width, gps->height);
312
313 /* 90 */
314 gdk_draw_arc(widget->window,
315              widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
316              FALSE,
317              xoffset + 2, yoffset + 2, size - 4, size - 4, 0, 64 * 360);
318
319 /* 60 */
320 gdk_draw_arc(widget->window,
321              widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
322              FALSE,
323              xoffset + size / 6, yoffset + size / 6,
324              size / 6 * 4, size / 6 * 4, 0, 64 * 360);
325
326 /* 30 */
327 gdk_draw_arc(widget->window,
328              widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
329              FALSE,
330              xoffset + size / 6 * 2, yoffset + size / 6 * 2,
331              size / 6 * 2, size / 6 * 2, 0, 64 * 360);
332
333 for (i = 0; i < 6; i++) {
334         /* line */
335         tmp = (line[i] * (1.f / 180.f)) * G_PI;
336         gdk_draw_line(widget->window,
337                       widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
338                       xoffset + halfsize + (halfsize - 2) * sinf(tmp),
339                       yoffset + halfsize - (halfsize - 2) * cosf(tmp),
340                       xoffset + halfsize - (halfsize - 2) * sinf(tmp),
341                       yoffset + halfsize + (halfsize - 2) * cosf(tmp));
342 }
343
344 if (size > 140) {
345 for (i = 0; i < 12; i++) {
346         tmp = (line[i] * (1.f / 180.f)) * G_PI;
347         /* azimuth */
348         if (line[i] == 0)
349                 g_snprintf(buffer, 16, "N");
350         else
351                 g_snprintf(buffer, 16, "%d°", line[i]);
352         pango_layout_set_text(gps->layout, buffer, -1);
353         pango_layout_get_pixel_size(gps->layout, &x, &y);
354         gdk_draw_layout(widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
355                         (xoffset + halfsize + (halfsize - size / 12) * sinf(tmp)) - x / 2,
356                         (yoffset + halfsize - (halfsize - size / 12) * cosf(tmp)) - y / 2,
357                         gps->layout);
358 }
359
360 /* elevation 30 */
361 tmp = (30 * (1.f / 180.f)) * G_PI;
362 g_snprintf(buffer, 16, "30°");
363 pango_layout_set_text(gps->layout, buffer, -1);
364 pango_layout_get_pixel_size(gps->layout, &x, &y);
365 gdk_draw_layout(widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
366                 (xoffset + halfsize + size / 6 * 2 * sinf(tmp)) - x / 2,
367                 (yoffset + halfsize - size / 6 * 2 * cosf(tmp)) - y / 2,
368                 gps->layout);
369
370 /* elevation 60 */
371 tmp = (30 * (1.f / 180.f)) * G_PI;
372 g_snprintf(buffer, 16, "60°");
373 pango_layout_set_text(gps->layout, buffer, -1);
374 pango_layout_get_pixel_size(gps->layout, &x, &y);
375 gdk_draw_layout(widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
376                 (xoffset + halfsize + size / 6 * sinf(tmp)) - x / 2,
377                 (yoffset + halfsize - size / 6 * cosf(tmp)) - y / 2, 
378                 gps->layout);
379 }
380
381 for (i=0;i<gps->data->satinview;i++) {
382         /* Sat used or not */
383         gc=(gps->data->sat[i].fix) ? gps->gc2 : gps->gc1;
384
385         tmp = (gps->data->sat[i].azimuth * (1.f / 180.f)) * G_PI;
386         x = xoffset + halfsize + (90 - gps->data->sat[i].elevation) * halfsize / 90 * sinf(tmp);
387         y = yoffset + halfsize - (90 - gps->data->sat[i].elevation) * halfsize / 90 * cosf(tmp);
388
389         gdk_draw_arc(widget->window, gc, TRUE, x-gps->fs, y-gps->fs, gps->fs+3, gps->fs+3, 0, 64 * 360);
390
391         g_snprintf(buffer, 6, "%02d", gps->data->sat[i].prn);
392         pango_layout_set_text(gps->layout, buffer, -1);
393         pango_layout_get_pixel_size(gps->layout, &x1, &y1);
394         gdk_draw_layout(widget->window, gps->gc3, x - x1 / 2, y - y1 / 2, gps->layout);
395 }
396
397 return;
398 }
399
400 static void
401 gtk_gps_paint_signals(GtkGps *gps)
402 {
403 GdkGC *gc;
404 GtkWidget *widget;
405 guint step, i, snr_height, bymargin, xoffset, yoffset;
406 guint x, y, x1, y1;
407 gchar tmp[32];
408
409 widget=GTK_WIDGET(gps);
410
411 if (!GTK_WIDGET_MAPPED(widget))
412         return;
413
414 xoffset = 0;
415 yoffset = 0;
416
417 /* Bootom margin - 12% */
418 bymargin = gps->height * 0.88f;
419
420 /* Bottom line */
421 gdk_draw_line(widget->window,
422               widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
423               xoffset + 5, yoffset + bymargin,
424               xoffset + gps->width - 10 - 2, yoffset + bymargin);
425 gdk_draw_line(widget->window,
426               widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
427               xoffset + 5, yoffset + bymargin - 1,
428               xoffset + gps->width - 10 - 2, yoffset + bymargin - 1);
429
430 if (gps->data->satinview > 0) {
431         /* Left margin - 5pix, Right margin - 5pix */
432         step = (gps->width - 10) / gps->data->satinview;
433
434         for (i = 0; i < gps->data->satinview; i++) {
435                 /* Sat used or not */
436                 gc=(gps->data->sat[i].fix) ? gps->gc1 : gps->gc2;
437
438                 x = 5 + i * step;
439                 snr_height = gps->data->sat[i].snr * gps->height * 0.78f / 100;
440                 y = gps->height * 0.1f + (gps->height * 0.78f - snr_height);
441
442                 /* draw sat rectangle... */
443                 gdk_draw_rectangle(widget->window,
444                                    gc, TRUE,
445                                    xoffset + x,
446                                    yoffset + y, step - 2, snr_height);
447
448                 if (gps->data->sat[i].snr > 0) {
449                         /* ...snr.. */
450                         g_snprintf(tmp, 32, "%02d", gps->data->sat[i].snr);
451                         pango_layout_set_text(gps->layout, tmp, 2);
452                         pango_layout_get_pixel_size(gps->layout, &x1, &y1);
453                         gdk_draw_layout(widget->window,
454                                         widget->style->fg_gc[GTK_STATE_NORMAL],
455                                         xoffset + x + ((step - 2) - x1) / 2,
456                                                 yoffset + y - 15,
457                                                 gps->layout);
458                 }
459
460                 /* ...and sat number */
461                 g_snprintf(tmp, 32, "%02d", gps->data->sat[i].prn);
462                 pango_layout_set_text(gps->layout, tmp, 2);
463                 pango_layout_get_pixel_size(gps->layout, &x1, &y1);
464                 gdk_draw_layout(widget->window,
465                                 widget->style->fg_gc[GTK_STATE_NORMAL],
466                                 xoffset + x + ((step - 2) - x1) / 2,
467                                 yoffset + bymargin + 1,
468                                 gps->layout);
469         }
470 }
471
472 return;
473 }
474
475 static void
476 gtk_gps_paint_by_mode(GtkGps *gps)
477 {
478 g_return_if_fail(GTK_IS_GPS(gps));
479
480 switch (gps->display_mode) {
481         case GTK_GPS_MODE_SIGNAL:
482                 gtk_gps_paint_signals(gps);
483         break;
484         case GTK_GPS_MODE_SKY:
485         default:
486                 gtk_gps_paint_sky(gps);
487         break;
488 }
489 }
490
491 static gboolean
492 gtk_gps_expose(GtkWidget *widget, GdkEventExpose *event)
493 {
494 GtkGps *gps;
495
496 g_return_val_if_fail(GTK_IS_GPS(widget), FALSE);
497 g_return_val_if_fail(event != NULL, FALSE);
498
499 gps=GTK_GPS(widget);
500 gtk_gps_paint_by_mode(gps);
501 return TRUE;
502 }
503
504 void
505 gtk_gps_refresh(GtkWidget *widget)
506 {
507 GtkGps *gps;
508
509 g_return_if_fail(GTK_IS_GPS(widget));
510
511 gps=GTK_GPS(widget);
512 gtk_gps_paint_by_mode(gps);
513 }