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