3 * GtkGps: Display GPS information
4 * Copyright (C) 2007 Kaj-Michael Lang
5 * Originanl non-widget version Copyright Cezary Jackiewicz
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.
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.
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.
24 #include <glib/gstdio.h>
25 #include <glib-object.h>
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);
38 G_DEFINE_TYPE(GtkGps, gtk_gps, GTK_TYPE_WIDGET);
42 #define BOUND(x, a, b) { \
50 gtk_gps_class_init(GtkGpsClass *class)
52 GObjectClass *object_class;
53 GtkWidgetClass *widget_class;
55 object_class = (GObjectClass*) class;
56 widget_class = (GtkWidgetClass*) class;
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;
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;
69 gtk_gps_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
73 g_return_if_fail(GTK_IS_GPS(object));
78 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
84 gtk_gps_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
88 g_return_if_fail(GTK_IS_GPS(object));
93 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
99 gtk_gps_init(GtkGps *gps)
111 gtk_gps_cb_button_press(GtkWidget * widget, GdkEventButton * event)
117 if (event->button==1) {
118 switch (gps->display_mode) {
119 case GTK_GPS_MODE_SKY:
120 gps->display_mode=GTK_GPS_MODE_SIGNAL;
122 case GTK_GPS_MODE_SIGNAL:
124 gps->display_mode=GTK_GPS_MODE_SKY;
128 gtk_widget_queue_draw_area(widget, 0, 0, gps->width, gps->height);
133 gtk_gps_new(GtkGpsMode display_mode, GpsData *data)
138 gps=gtk_type_new(gtk_gps_get_type ());
139 gps->display_mode=display_mode;
141 widget=GTK_WIDGET(gps);
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;
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);
157 gps->esid=g_timeout_add(1000,(GSourceFunc)gtk_gps_refresh_cb, gps);
159 return GTK_WIDGET(gps);
163 gtk_gps_finalize(GObject *object)
167 g_return_if_fail(GTK_IS_GPS(object));
170 g_source_remove(gps->esid);
171 if (GTK_WIDGET(object)->parent && GTK_WIDGET_MAPPED(object)) {
172 gtk_widget_unmap(GTK_WIDGET(object));
175 G_OBJECT_CLASS(gtk_gps_parent_class)->finalize(object);
179 gtk_gps_size_request(GtkWidget *widget, GtkRequisition *requisition)
183 g_return_if_fail(GTK_IS_GPS(widget));
184 g_return_if_fail(requisition!=NULL);
186 gps = GTK_GPS (widget);
188 requisition->width=300;
191 if (gps->display_mode==GTK_GPS_MODE_SKY) {
192 requisition->height=400;
195 requisition->height=200;
202 gtk_gps_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
206 g_return_if_fail(GTK_IS_GPS(widget));
207 g_return_if_fail(allocation!=NULL);
209 gps=GTK_GPS (widget);
211 widget->allocation = *allocation;
213 if (GTK_WIDGET_REALIZED (widget)) {
214 gdk_window_move_resize (widget->window,
215 allocation->x, allocation->y,
216 allocation->width, allocation->height);
219 gps->width=allocation->width;
220 gps->height=allocation->height;
221 gps->size=MIN(gps->width, gps->height);
223 gps->fs=gps->size/22;
224 BOUND(gps->fs, 10, 24);
226 pango_font_description_set_size(gps->fontdesc, (gps->fs-3)*PANGO_SCALE);
227 pango_layout_set_font_description(gps->layout, gps->fontdesc);
231 gtk_gps_realize (GtkWidget *widget)
235 GdkWindowAttr attributes;
236 gint attributes_mask;
238 g_return_if_fail (GTK_IS_GPS(widget));
239 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
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);
252 attributes_mask=GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
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);
257 gdk_window_set_user_data(widget->window, widget);
258 gtk_style_set_background(widget->style, widget->window, GTK_STATE_ACTIVE);
265 gps->gc_s=gdk_gc_new(widget->window);
266 gdk_gc_set_rgb_fg_color(gps->gc_s, &color);
274 gps->gc_sf=gdk_gc_new(widget->window);
275 gdk_gc_set_rgb_fg_color(gps->gc_sf, &color);
282 gps->gc_w=gdk_gc_new(widget->window);
283 gdk_gc_set_rgb_fg_color(gps->gc_w, &color);
288 gtk_gps_paint_sky(GtkGps *gps)
292 guint i, x, y, size, halfsize, xoffset, yoffset;
293 guint x1, y1, x0, y0;
296 guint line[12]={0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330};
298 widget=GTK_WIDGET(gps);
300 g_return_if_fail(gps->data);
302 if (!GTK_WIDGET_MAPPED(widget))
308 size=gps->size-PADDING;
310 if (gps->width>gps->height) {
311 xoffset=x0+(gps->width-gps->height)/2;
315 yoffset=y0+(gps->height-gps->width)/2;
319 gdk_draw_arc(widget->window,
320 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
322 xoffset + 2, yoffset + 2, size - 4, size - 4, 0, 64 * 360);
325 gdk_draw_arc(widget->window,
326 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
328 xoffset + size / 6, yoffset + size / 6,
329 size / 6 * 4, size / 6 * 4, 0, 64 * 360);
332 gdk_draw_arc(widget->window,
333 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
335 xoffset + size / 6 * 2, yoffset + size / 6 * 2,
336 size / 6 * 2, size / 6 * 2, 0, 64 * 360);
338 for (i = 0; i < 12; i++) {
341 tmp = (line[i] * (1.f / 180.f)) * G_PI;
345 gdk_draw_line(widget->window,
346 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
347 xoffset + halfsize + (halfsize - 2) * sint,
348 yoffset + halfsize - (halfsize - 2) * cost,
349 xoffset + halfsize - (halfsize - 2) * sint,
350 yoffset + halfsize + (halfsize - 2) * cost);
352 /* Skip angle text if there is no space */
357 g_snprintf(buffer, 16, "N");
359 g_snprintf(buffer, 16, "%d°", line[i]);
360 pango_layout_set_text(gps->layout, buffer, -1);
361 pango_layout_get_pixel_size(gps->layout, &x, &y);
362 gdk_draw_layout(widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
363 (xoffset + halfsize + (halfsize - size / 12) * sint) - x / 2,
364 (yoffset + halfsize - (halfsize - size / 12) * cost) - y / 2,
370 tmp = (30 * (1.f / 180.f)) * G_PI;
372 g_snprintf(buffer, 16, "30°");
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 * 2 * sinf(tmp)) - x / 2,
377 (yoffset + halfsize - size / 6 * 2 * cosf(tmp)) - y / 2,
381 g_snprintf(buffer, 16, "60°");
382 pango_layout_set_text(gps->layout, buffer, -1);
383 pango_layout_get_pixel_size(gps->layout, &x, &y);
384 gdk_draw_layout(widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
385 (xoffset + halfsize + size / 6 * sinf(tmp)) - x / 2,
386 (yoffset + halfsize - size / 6 * cosf(tmp)) - y / 2,
390 for (i=0;i<gps->data->satinview;i++) {
391 /* Sat used or not */
392 gc=(gps->data->sat[i].fix==TRUE) ? gps->gc_sf : gps->gc_s;
394 tmp = (gps->data->sat[i].azimuth * (1.f / 180.f)) * G_PI;
395 x = xoffset + halfsize + (90 - gps->data->sat[i].elevation) * halfsize / 90 * sinf(tmp);
396 y = yoffset + halfsize - (90 - gps->data->sat[i].elevation) * halfsize / 90 * cosf(tmp);
398 gdk_draw_arc(widget->window, gc, TRUE, x-gps->fs, y-gps->fs, gps->fs+8, gps->fs+8, 0, 64 * 360);
400 g_snprintf(buffer, 6, "%02d", gps->data->sat[i].prn);
401 pango_layout_set_text(gps->layout, buffer, -1);
402 pango_layout_get_pixel_size(gps->layout, &x1, &y1);
403 gdk_draw_layout(widget->window, gps->gc_w, x-gps->fs/2-x1/2+2, y-gps->fs/2-y1/2+2, gps->layout);
410 gtk_gps_paint_signals(GtkGps *gps)
414 guint step, i, snr_height, bymargin, xoffset, yoffset;
418 widget=GTK_WIDGET(gps);
420 if (!GTK_WIDGET_MAPPED(widget))
426 /* Bootom margin - 12% */
427 bymargin = gps->height * 0.88f;
430 gdk_draw_line(widget->window,
431 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
432 xoffset + 5, yoffset + bymargin,
433 xoffset + gps->width - 10 - 2, yoffset + bymargin);
434 gdk_draw_line(widget->window,
435 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
436 xoffset + 5, yoffset + bymargin - 1,
437 xoffset + gps->width - 10 - 2, yoffset + bymargin - 1);
439 if (gps->data->satinview==0)
442 /* Left margin - 5pix, Right margin - 5pix */
443 step = (gps->width - 10) / gps->data->satinview;
445 for (i=0;i<gps->data->satinview;i++) {
446 /* Sat used or not */
447 gc=(gps->data->sat[i].fix) ? gps->gc_sf : gps->gc_s;
450 snr_height = gps->data->sat[i].snr * gps->height * 0.78f / 100;
451 y = gps->height * 0.1f + (gps->height * 0.78f - snr_height);
453 /* draw sat rectangle... */
454 gdk_draw_rectangle(widget->window,
457 yoffset + y, step - 2, snr_height);
459 if (gps->data->sat[i].snr>0) {
461 g_snprintf(tmp, 32, "%02d", gps->data->sat[i].snr);
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,
471 /* ...and sat number */
472 g_snprintf(tmp, 32, "%02d", gps->data->sat[i].prn);
473 pango_layout_set_text(gps->layout, tmp, 2);
474 pango_layout_get_pixel_size(gps->layout, &x1, &y1);
475 gdk_draw_layout(widget->window,
476 widget->style->fg_gc[GTK_STATE_NORMAL],
477 xoffset + x + ((step - 2) - x1) / 2,
478 yoffset + bymargin + 1,
485 gtk_gps_paint_by_mode(GtkGps *gps)
487 g_return_if_fail(GTK_IS_GPS(gps));
489 switch (gps->display_mode) {
490 case GTK_GPS_MODE_SIGNAL:
491 gtk_gps_paint_signals(gps);
493 case GTK_GPS_MODE_SKY:
495 gtk_gps_paint_sky(gps);
501 gtk_gps_refresh_cb(GtkWidget *widget)
505 g_return_val_if_fail(GTK_IS_GPS(widget), FALSE);
507 if ((GTK_WIDGET_MAPPED(widget)==FALSE) || (GTK_WIDGET_VISIBLE(widget)==FALSE))
511 gtk_widget_queue_draw_area(widget, 0, 0, gps->width, gps->height);
516 gtk_gps_expose(GtkWidget *widget, GdkEventExpose *event)
520 g_return_val_if_fail(GTK_IS_GPS(widget), FALSE);
521 g_return_val_if_fail(event != NULL, FALSE);
524 gtk_gps_paint_by_mode(gps);
529 gtk_gps_refresh(GtkWidget *widget)
533 g_return_if_fail(GTK_IS_GPS(widget));
536 gtk_widget_queue_draw_area(widget, 0, 0, gps->width, gps->height);