]> err.no Git - mapper/blob - src/gtkcompass.c
Pre-calc size/2
[mapper] / src / gtkcompass.c
1 /*
2  *
3  * GtkCompass: Display heading/compass information
4  * Also bearing to target and next waypoint.
5  *
6  * Copyright (C) 2007 Kaj-Michael Lang
7  * Originanl non-widget version Copyright Cezary Jackiewicz
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the Free
21  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23
24 #include <math.h>
25 #include <stdlib.h>
26
27 #include <glib/gstdio.h>
28 #include <glib-object.h>
29 #include "gtkcompass.h"
30
31 static void gtk_compass_finalize (GObject *object);
32 static void gtk_compass_size_request (GtkWidget *widget, GtkRequisition *requisition);
33 static void gtk_compass_size_allocate (GtkWidget *widget, GtkAllocation *allocate);
34 static gboolean gtk_compass_expose (GtkWidget *widget, GdkEventExpose *event);
35 static void gtk_compass_realize (GtkWidget *widget);
36 static void gtk_compass_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
37 static void gtk_compass_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
38 static void gtk_compass_paint(GtkCompass *compass);
39
40 G_DEFINE_TYPE(GtkCompass, gtk_compass, GTK_TYPE_WIDGET);
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_compass_class_init (GtkCompassClass *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_compass_finalize;
59 object_class->set_property = gtk_compass_set_property;
60 object_class->get_property = gtk_compass_get_property;
61         
62 widget_class->size_request = gtk_compass_size_request;
63 widget_class->expose_event = gtk_compass_expose;
64 widget_class->realize = gtk_compass_realize;
65 widget_class->size_allocate = gtk_compass_size_allocate;
66 }
67
68 static void
69 gtk_compass_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
70 {
71 GtkCompass *compass;
72 g_return_if_fail(GTK_IS_COMPASS(object));
73 compass=GTK_COMPASS(object);
74 switch (prop_id) {
75         default:
76                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
77         break;
78 }
79 }
80
81 static void
82 gtk_compass_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec   *pspec)
83 {
84 GtkCompass *compass;
85 g_return_if_fail(GTK_IS_COMPASS(object));
86 compass=GTK_COMPASS(object);
87 switch (prop_id) {
88         default:
89                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
90         break;
91 }
92 }
93
94 static void
95 gtk_compass_init (GtkCompass *compass)
96 {
97 compass->gc_h=NULL;
98 compass->gc_d=NULL;
99 compass->gc_w=NULL;
100 compass->dest_valid=FALSE;
101 compass->way_valid=FALSE;
102 compass->width=300;
103 compass->height=300;
104 }
105
106 static gboolean 
107 gtk_compass_cb_button_press(GtkWidget * widget, GdkEventButton * event)
108 {
109 GtkCompass *compass;
110
111 compass=GTK_COMPASS(widget);
112
113 return FALSE;
114 }
115
116 GtkWidget*
117 gtk_compass_new(GpsData *data)
118 {
119 GtkCompass *compass;
120 GtkWidget *widget;
121
122 compass=gtk_type_new(gtk_compass_get_type ());
123 widget=GTK_WIDGET(compass);
124 compass->data=data;
125 g_signal_connect(G_OBJECT(widget), "button_press_event", G_CALLBACK(gtk_compass_cb_button_press), NULL);
126 return widget;
127 }
128
129 static void
130 gtk_compass_finalize(GObject *object)
131 {
132 GtkCompass *compass;
133         
134 g_return_if_fail(GTK_IS_COMPASS(object));
135 compass=GTK_COMPASS(object);
136
137 if (GTK_WIDGET(object)->parent && GTK_WIDGET_MAPPED(object)) {
138         gtk_widget_unmap(GTK_WIDGET(object));
139 }
140
141 G_OBJECT_CLASS(gtk_compass_parent_class)->finalize(object);
142 }
143
144 static void
145 gtk_compass_size_request(GtkWidget      *widget, GtkRequisition *requisition)
146 {
147 GtkCompass *compass;
148         
149 g_return_if_fail(GTK_IS_COMPASS(widget));
150 g_return_if_fail(requisition != NULL);
151         
152 compass = GTK_COMPASS (widget);
153         
154 requisition->width=400;
155 requisition->height=300;
156 compass->width=400;
157 compass->height=300;
158 }
159
160 static void
161 gtk_compass_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
162 {
163 GtkCompass *compass;
164
165 g_return_if_fail(GTK_IS_COMPASS(widget));
166 g_return_if_fail(allocation!=NULL);
167
168 compass=GTK_COMPASS(widget);
169
170 widget->allocation=*allocation;
171
172 if (GTK_WIDGET_REALIZED (widget)) {
173         gdk_window_move_resize (widget->window,
174                 allocation->x, allocation->y,
175                 allocation->width, allocation->height);
176 }
177
178 compass->width=allocation->width;
179 compass->height=allocation->height;
180
181 compass->size = MIN(widget->allocation.width, widget->allocation.height);
182 if (widget->allocation.width > widget->allocation.height) {
183         compass->xoffset = (widget->allocation.width - widget->allocation.height) / 2;
184         compass->yoffset = 0;
185 } else {
186         compass->xoffset = 0;
187         compass->yoffset = (widget->allocation.height - widget->allocation.width) / 2;
188 }
189
190 }
191
192 static void 
193 gtk_compass_realize (GtkWidget *widget)
194 {
195 GtkCompass *compass;
196 GdkColor color;
197 GdkWindowAttr attributes;
198 gint attributes_mask;
199
200 g_return_if_fail (GTK_IS_COMPASS(widget));
201 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
202 compass=GTK_COMPASS(widget);
203
204 attributes.x=widget->allocation.x;
205 attributes.y=widget->allocation.y;
206 attributes.width=widget->allocation.width;
207 attributes.height=widget->allocation.height;
208 attributes.wclass=GDK_INPUT_OUTPUT;
209 attributes.window_type=GDK_WINDOW_CHILD;
210 attributes.event_mask=gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
211 attributes.visual=gtk_widget_get_visual(widget);
212 attributes.colormap=gtk_widget_get_colormap(widget);
213
214 attributes_mask=GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
215
216 widget->window=gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributes_mask);
217 widget->style=gtk_style_attach(widget->style, widget->window);
218
219 gdk_window_set_user_data(widget->window, widget);
220 gtk_style_set_background(widget->style, widget->window, GTK_STATE_ACTIVE);
221
222 compass->context=gtk_widget_get_pango_context(widget);
223 compass->layout=pango_layout_new(compass->context);
224 compass->fontdesc=pango_font_description_new();
225 pango_font_description_set_family(compass->fontdesc, "Sans Serif");
226 pango_font_description_set_size(compass->fontdesc, 12*PANGO_SCALE);
227 pango_layout_set_font_description(compass->layout, compass->fontdesc);
228 pango_layout_set_alignment(compass->layout, PANGO_ALIGN_CENTER);
229
230 if (!compass->gc_h) {
231         color.red=0x0000;
232         color.green=0x0000;
233         color.blue=0x0000;
234         compass->gc_h=gdk_gc_new(widget->window);
235         gdk_gc_set_rgb_fg_color(compass->gc_h, &color);
236         gdk_gc_set_line_attributes(compass->gc_h, 2, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
237 }
238
239 if (!compass->gc_d) {
240         color.red=0xffff;
241         color.green=0x0000;
242         color.blue=0xffff;
243         compass->gc_d=gdk_gc_new(widget->window);
244         gdk_gc_set_rgb_fg_color(compass->gc_d, &color);
245         gdk_gc_set_line_attributes(compass->gc_d, 6, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
246 }
247
248 if (!compass->gc_w) {
249         color.red=0x0000;
250         color.green=0xffff;
251         color.blue=0x0000;
252         compass->gc_w=gdk_gc_new(widget->window);
253         gdk_gc_set_rgb_fg_color(compass->gc_w, &color);
254         gdk_gc_set_line_attributes(compass->gc_w, 6, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
255 }
256
257 }
258
259 static void
260 gtk_compass_draw_mark(GtkCompass *compass, GdkGC *gc, gdouble angle, gint size)
261 {
262 GtkWidget *widget;
263 gint hs;
264
265 widget=GTK_WIDGET(compass);
266
267 hs=compass->size/2;
268
269 gdk_draw_line(widget->window,gc,
270         compass->xoffset+hs+((hs-size)*sin(angle)),
271         compass->yoffset+compass->size-((hs-size)*cos(angle)),
272         compass->xoffset+hs+((hs+size)*sin(angle)),
273         compass->yoffset+compass->size-((hs+size)*cos(angle)));
274 }
275
276 static void
277 gtk_compass_paint(GtkCompass *compass)
278 {
279 GtkWidget *widget;
280 guint i, x, y, size, hsize, fs;
281 gint dir;
282 gfloat tmp;
283 gchar *text;
284 gchar htext[8];
285 gint angle[5] = { -90, -45, 0, 45, 90 };
286 gint fsize[5] = { 0, 4, 10, 4, 0 };
287
288 widget=GTK_WIDGET(compass);
289 size=compass->size;
290 #if 1
291 hsize=size/2;
292 #else
293 hsize=0;
294 #endif
295
296 fs=size/42;
297 BOUND(fs, 1, 16);
298
299 pango_context_set_matrix (compass->context, NULL);
300
301 g_snprintf(htext, 8, "%3.0f°", compass->data->heading);
302 pango_font_description_set_size(compass->fontdesc, (10+fs) * PANGO_SCALE);
303 pango_layout_set_font_description(compass->layout, compass->fontdesc);
304 pango_layout_set_text(compass->layout, htext, -1);
305 pango_layout_get_pixel_size(compass->layout, &x, &y);
306
307 gdk_draw_layout(widget->window,
308         compass->gc_h,
309         compass->xoffset+hsize-x/2,
310         compass->yoffset+size-y-2, compass->layout);
311
312 gdk_draw_arc(widget->window,
313         compass->gc_h,
314         FALSE,
315         compass->xoffset, compass->yoffset+hsize, size - fs, size - fs, 0, 64 * 360);
316
317 /* Simple arrow for heading */
318 gdk_draw_line(widget->window,
319         compass->gc_h,
320         compass->xoffset + hsize + 3,
321         compass->yoffset + size - y - 5,
322         compass->xoffset + hsize, compass->yoffset + hsize + 5);
323
324 gdk_draw_line(widget->window,
325         compass->gc_h,
326         compass->xoffset + hsize - 3,
327         compass->yoffset + size - y - 5,
328         compass->xoffset + hsize, compass->yoffset + hsize + 5);
329
330 gdk_draw_line(widget->window,
331         compass->gc_h,
332         compass->xoffset + hsize - 3,
333         compass->yoffset + size - y - 5,
334         compass->xoffset + hsize, compass->yoffset + size - y - 8);
335
336 gdk_draw_line(widget->window,
337         compass->gc_h,
338         compass->xoffset + hsize + 3,
339         compass->yoffset + size - y - 5,
340         compass->xoffset + hsize, compass->yoffset + size - y - 8);
341
342 for (i = 0; i < 5; i++) {
343         PangoMatrix matrix = PANGO_MATRIX_INIT;
344         dir = (gint) (compass->data->heading / 45) * 45 + angle[i];
345
346         switch (dir) {
347         case 0:
348         case 360:
349                 text = "N";
350                 break;
351         case 45:
352         case 405:
353                 text = "NE";
354                 break;
355         case 90:
356                 text = "E";
357                 break;
358         case 135:
359                 text = "SE";
360                 break;
361         case 180:
362                 text = "S";
363                 break;
364         case 225:
365                 text = "SW";
366                 break;
367         case 270:
368         case -90:
369                 text = "W";
370                 break;
371         case 315:
372         case -45:
373                 text = "NW";
374                 break;
375         default:
376                 text = "??";
377                 break;
378         }
379
380         tmp = ((dir - compass->data->heading) * (1.f / 180.f)) * G_PI;
381
382         gtk_compass_draw_mark(compass, compass->gc_h, tmp, 6);
383
384         x = fsize[i];
385         if (abs((guint) (compass->data->heading / 45) * 45 - compass->data->heading)
386             > abs((guint) (compass->data->heading / 45) * 45 + 45 - compass->data->heading) && (i > 0))
387                         x = fsize[i - 1];
388
389         pango_font_description_set_size(compass->fontdesc, (10 + x + fs) * PANGO_SCALE);
390         pango_layout_set_font_description(compass->layout, compass->fontdesc);
391         pango_layout_set_text(compass->layout, text, -1);
392         pango_matrix_rotate (&matrix, -(dir-compass->data->heading));
393         pango_context_set_matrix (compass->context, &matrix);
394         pango_layout_get_pixel_size(compass->layout, &x, &y);
395         x = compass->xoffset + hsize + ((hsize + 15 + fs) * sinf(tmp)) - x / 2,
396     y = compass->yoffset + size - ((hsize + 15 + fs) * cosf(tmp)) - y / 2,
397     gdk_draw_layout(widget->window, compass->gc_h, x, y, compass->layout);
398 }
399
400 if (compass->dest_valid)
401         gtk_compass_draw_mark(compass, compass->gc_d, compass->dest_heading, 10);
402
403 if (compass->way_valid)
404         gtk_compass_draw_mark(compass, compass->gc_w, compass->way_heading, 10);
405
406 return;
407 }
408
409 static gboolean
410 gtk_compass_expose(GtkWidget *widget, GdkEventExpose *event)
411 {
412 GtkCompass *compass;
413
414 g_return_val_if_fail(GTK_IS_COMPASS(widget), FALSE);
415 g_return_val_if_fail(event != NULL, FALSE);
416
417 compass=GTK_COMPASS(widget);
418 gtk_compass_paint(compass);
419 return TRUE;
420 }
421
422 void
423 gtk_compass_refresh(GtkWidget *widget)
424 {
425 GtkCompass *compass;
426
427 g_return_if_fail(GTK_IS_COMPASS(widget));
428
429 compass=GTK_COMPASS(widget);
430 gtk_widget_queue_draw_area(widget, 0, 0, compass->width, compass->height);
431 }
432
433 void 
434 gtk_compass_set_way_heading(GtkWidget *widget, gboolean valid, gfloat heading)
435 {
436 GtkCompass *compass;
437 g_return_if_fail(GTK_IS_COMPASS(widget));
438
439 compass=GTK_COMPASS(widget);
440
441 compass->way_valid=valid;
442 compass->way_heading=heading;
443
444 gtk_widget_queue_draw_area(widget, 0, 0, compass->width, compass->height);
445 }
446
447 void 
448 gtk_compass_set_dest_heading(GtkWidget *widget, gboolean valid, gfloat heading)
449 {
450 GtkCompass *compass;
451 g_return_if_fail(GTK_IS_COMPASS(widget));
452
453 compass=GTK_COMPASS(widget);
454
455 compass->dest_valid=valid;
456 compass->dest_heading=heading;
457
458 compass=GTK_COMPASS(widget);
459 gtk_widget_queue_draw_area(widget, 0, 0, compass->width, compass->height);
460 }
461