Contents of /trunk/src/goto.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 249 - (show annotations)
Thu Jan 21 21:18:33 2010 UTC (14 years, 3 months ago) by harbaum
File MIME type: text/plain
File size: 23485 byte(s)
Espeak and minor features added
1 /*
2 * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3 *
4 * This file is part of GPXView.
5 *
6 * GPXView is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GPXView is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GPXView. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <stdio.h>
21 #include <string.h>
22 #include <math.h>
23
24 #include "gpxview.h"
25
26 #ifdef USE_MAEMO
27 #include "dbus.h"
28 #endif
29
30 #define COMPASS_SIZE 230
31 #define LETTER_SPACE 0.2
32 #define DIAMOND_WIDTH 0.05
33 #define DIAMOND_HEIGHT 0.2
34 #define ARROW_WIDTH 0.3
35 #define ARROW_LENGTH 0.7
36 #define UPDATE_MS 1000 /* gps updates are sent once a second */
37
38 #define SAT_WIDTH 330
39 #define SAT_HEIGHT 60
40
41 #ifdef ESPEAK
42 #include <espeak/speak_lib.h>
43
44 #define ESPEAK_TIMEOUT (15000) // every 15 seconds
45
46 char *espeak_make_string(float dist, gboolean imperial) {
47 char *retval = NULL;
48
49 if(!imperial) {
50 /* distance is in kilometers */
51 if(dist < 2.0/1000.0)
52 retval = g_strdup(_("1 meter"));
53 else if(dist < 1.0)
54 retval = g_strdup_printf(_("%u meters"), (int)(dist*1000));
55 else if(dist < 2.0)
56 retval = g_strdup(_("1 kilometer"));
57 else
58 retval = g_strdup_printf(_("%u kilometers"), (int)dist);
59 } else {
60
61 /* 1 mil = 1760 yd = 5280 ft ... */
62 if(dist < (2.0/5280.0))
63 retval = g_strdup(_("1 foot"));
64 else if(dist < (1.0/1760.0))
65 retval = g_strdup_printf(_("%u feet"), (int)(dist*5280));
66 else if(dist < (2.0/1760.0))
67 retval = g_strdup(_("1 yard"));
68 else if(dist < 1.0)
69 retval = g_strdup_printf(_("%u yard"), (int)(dist*1760));
70 else if(dist < 2.0)
71 retval = g_strdup(_("1 mile"));
72 else
73 retval = g_strdup_printf(_("%u miles"), (int)dist);
74 }
75 return retval;
76 }
77
78 static gboolean espeak_do(gpointer data) {
79 cache_context_t *context = (cache_context_t*)data;
80
81 pos_t *pos = gps_get_pos(context->appdata);
82 if(pos && !isnan(pos->lat) && !isnan(pos->lon) &&
83 !isnan(context->gotoc.pos.lat) && !isnan(context->gotoc.pos.lon)) {
84
85 /* build distance */
86 float dist =
87 gpx_pos_get_distance(*pos, context->gotoc.pos,
88 context->appdata->imperial);
89
90 char *talk = espeak_make_string(dist, context->appdata->imperial);
91
92 unsigned int unique_identifier=0;
93 espeak_Synth(talk, strlen(talk)+1, 0, 0, 0, espeakCHARS_AUTO,
94 &unique_identifier, NULL);
95
96 g_free(talk);
97 }
98
99 return TRUE; // speak again
100 }
101
102 static void espeak_enable(cache_context_t *context, gboolean enable) {
103 if(enable) {
104 g_assert(!context->gotoc.espeak_handler);
105
106 context->gotoc.espeak_handler =
107 gtk_timeout_add(ESPEAK_TIMEOUT, espeak_do, context);
108
109 espeak_do(context);
110
111 } else {
112 if(context->gotoc.espeak_handler) {
113 gtk_timeout_remove(context->gotoc.espeak_handler);
114 context->gotoc.espeak_handler = 0;
115 }
116 }
117 }
118 #endif
119
120 static float rad2deg(float rad) {
121 return fmodf(360.0 + (180.0/M_PI) * rad, 360.0);
122 }
123
124 static void compass_draw(GtkWidget *widget, cache_context_t *context) {
125 float f;
126 int i;
127
128 gint width = widget->allocation.width;
129 gint height = widget->allocation.height;
130 gint diameter = (height < width)?height:width;
131
132 gint xcenter = width/2;
133 gint ycenter = height/2;
134 gint radius = diameter/2;
135
136 /* erase background */
137 gdk_draw_rectangle(context->gotoc.compass_pixmap,
138 widget->style->bg_gc[GTK_STATE_NORMAL], TRUE,
139 0, 0, width, height);
140
141 /* use white rosetta background unless the real background */
142 /* is very bright. Use bright grey then */
143
144 if(widget->style->bg[GTK_STATE_NORMAL].red +
145 widget->style->bg[GTK_STATE_NORMAL].green +
146 widget->style->bg[GTK_STATE_NORMAL].blue < 3*60000) {
147 /* background is very bright, use white */
148 gdk_draw_arc(context->gotoc.compass_pixmap, widget->style->white_gc, TRUE,
149 (width-diameter)/2, (height-diameter)/2, diameter, diameter,
150 0, 360*64);
151 } else {
152 GdkGC *lgrey_gc = gdk_gc_new(context->gotoc.compass_pixmap);
153 gdk_gc_copy(lgrey_gc, widget->style->black_gc);
154 GdkColor lgrey_color;
155 gdk_color_parse("#DDDDDD", &lgrey_color);
156 gdk_gc_set_rgb_fg_color(lgrey_gc, &lgrey_color);
157
158 gdk_draw_arc(context->gotoc.compass_pixmap, lgrey_gc, TRUE,
159 (width-diameter)/2, (height-diameter)/2, diameter, diameter,
160 0, 360*64);
161 }
162
163 /* draw the locked/unlocked icon */
164 gdk_draw_pixbuf(context->gotoc.compass_pixmap,
165 widget->style->fg_gc[GTK_STATE_NORMAL],
166 icon_get(ICON_MISC, context->appdata->compass_locked?0:1),
167 0, 0, (width-diameter)/2 + diameter/32,
168 (height+diameter)/2 - 16 - diameter/32 , 16, 16,
169 GDK_RGB_DITHER_NONE,0,0);
170
171 /* don't update heading if the compass is locked */
172 if(!context->appdata->compass_locked) {
173 int i, valid = 0, cnt=0;
174 double x_sum = 0.0, y_sum = 0.0;
175
176 /* shift heading buffer up one entry and add new value at entry 0 */
177 for(i=MAX_AVERAGE-1;i>0;i--)
178 context->gotoc.head_avg[i] = context->gotoc.head_avg[i-1];
179 context->gotoc.head_avg[0] = gps_get_heading(context->appdata)*M_PI/180.0;
180
181 // printf("Damping = %d\n", context->appdata->compass_damping);
182 // printf("add heading %f\n", rad2deg(context->gotoc.head_avg[0]));
183
184 /* determine number of valid entries */
185 for(i=0;i<MAX_AVERAGE && valid<context->appdata->compass_damping;i++)
186 if(!isnan(context->gotoc.head_avg[i]))
187 valid++;
188
189 /* map back to angle if at least one value has been added */
190 if(valid) {
191 /* map all valid antries onto a circle */
192 for(i=0;i<MAX_AVERAGE && cnt<context->appdata->compass_damping;i++) {
193 if(!isnan(context->gotoc.head_avg[i])) {
194 float weight = 1.0 - ((float)(cnt++)/(float)valid);
195 printf("weight = %f * %f\n",
196 weight, rad2deg(context->gotoc.head_avg[i]));
197 x_sum += weight * sin(context->gotoc.head_avg[i]);
198 y_sum += weight * cos(context->gotoc.head_avg[i]);
199 }
200 }
201
202 printf("%d valid heading entries\n", valid);
203 context->gotoc.heading = atan2(x_sum, y_sum);
204 printf("averaged heading: %f\n",
205 rad2deg(context->gotoc.heading));
206 } else {
207 // printf("no valid heading, keeping old value heading\n");
208 }
209 }
210
211 if(!isnan(context->gotoc.heading)) {
212
213 for(i=0,f=0;f<2*M_PI-M_PI/8;f+=M_PI/4,i++) {
214 float ang = f - context->gotoc.heading;
215
216 if(!(i&1)) {
217 /* draw diamonds */
218 GdkPoint diamond[4];
219
220 #define OUT (1.0-LETTER_SPACE)
221
222 diamond[0].x = xcenter + radius * OUT * sin(ang);
223 diamond[0].y = ycenter + radius * OUT * -cos(ang);
224
225 diamond[1].x = xcenter + radius * (OUT-DIAMOND_HEIGHT/2) *
226 sin(ang+DIAMOND_WIDTH);
227 diamond[1].y = ycenter + radius * (OUT-DIAMOND_HEIGHT/2)*
228 -cos(ang+DIAMOND_WIDTH);
229
230 diamond[2].x = xcenter + radius * (OUT-DIAMOND_HEIGHT) * sin(ang);
231 diamond[2].y = ycenter + radius * (OUT-DIAMOND_HEIGHT) * -cos(ang);
232
233 diamond[3].x = xcenter + radius * (OUT-DIAMOND_HEIGHT/2) *
234 sin(ang-DIAMOND_WIDTH);
235 diamond[3].y = ycenter + radius * (OUT-DIAMOND_HEIGHT/2) *
236 -cos(ang-DIAMOND_WIDTH);
237
238 gdk_draw_polygon(context->gotoc.compass_pixmap,
239 widget->style->black_gc, TRUE,
240 diamond, sizeof(diamond)/sizeof(GdkPoint));
241
242 const char *str[] = { "N", "E", "S", "W" };
243 PangoLayout *layout = gtk_widget_create_pango_layout(widget, _(str[i/2]));
244 int tw, th;
245 pango_layout_get_pixel_size(layout, &tw, &th);
246
247 gdk_draw_layout(context->gotoc.compass_pixmap, widget->style->black_gc,
248 xcenter + radius * (1.0-LETTER_SPACE/2)*sin(ang) - tw/2,
249 ycenter + radius * (1.0-LETTER_SPACE/2)*-cos(ang) - th/2,
250 layout);
251
252 g_object_unref(layout);
253 } else
254 gdk_draw_line(context->gotoc.compass_pixmap, widget->style->black_gc,
255 xcenter + radius * 0.9 * sin(ang),
256 ycenter + radius * 0.9 * -cos(ang),
257 xcenter + radius * 1.0 * sin(ang),
258 ycenter + radius * 1.0 * -cos(ang));
259 }
260
261 /* draw arrow */
262
263 /* setup required colors */
264 GdkGC *arrow_gc = gdk_gc_new(context->gotoc.compass_pixmap);
265 gdk_gc_copy(arrow_gc, widget->style->black_gc);
266 GdkColor arrow_color;
267 gdk_color_parse("#800000", &arrow_color);
268 gdk_gc_set_rgb_fg_color(arrow_gc, &arrow_color);
269
270 GdkPoint arrow[4];
271
272 pos_t *pos = gps_get_pos(context->appdata);
273 if(pos && !isnan(pos->lat) && !isnan(pos->lon)) {
274 context->appdata->gps = *pos; /* save position */
275
276 float arot =
277 gpx_pos_get_bearing(*pos, context->gotoc.pos) *
278 M_PI/180 - context->gotoc.heading;
279
280 arrow[0].x = xcenter + radius * ARROW_LENGTH * sin(arot);
281 arrow[0].y = ycenter + radius * ARROW_LENGTH * -cos(arot);
282
283 arrow[1].x = xcenter + radius * -ARROW_LENGTH * sin(arot+ARROW_WIDTH);
284 arrow[1].y = ycenter + radius * -ARROW_LENGTH * -cos(arot+ARROW_WIDTH);
285
286 arrow[2].x = xcenter + radius * -0.5 * ARROW_LENGTH * sin(arot);
287 arrow[2].y = ycenter + radius * -0.5 * ARROW_LENGTH * -cos(arot);
288
289 arrow[3].x = xcenter + radius * -ARROW_LENGTH * sin(arot-ARROW_WIDTH);
290 arrow[3].y = ycenter + radius * -ARROW_LENGTH * -cos(arot-ARROW_WIDTH);
291
292 gdk_draw_polygon(context->gotoc.compass_pixmap, arrow_gc, TRUE,
293 arrow, sizeof(arrow)/sizeof(GdkPoint));
294 } else {
295 PangoLayout *layout;
296
297 if(context->appdata->use_gps)
298 layout = gtk_widget_create_pango_layout(widget, _("No fix"));
299 else
300 layout = gtk_widget_create_pango_layout(widget, _("GPS disabled"));
301
302 int tw, th;
303 pango_layout_get_pixel_size(layout, &tw, &th);
304
305 gdk_draw_layout(context->gotoc.compass_pixmap, widget->style->black_gc,
306 xcenter - tw/2, ycenter - th/2, layout);
307
308 g_object_unref(layout);
309 }
310 }
311 }
312
313 /* Create a new backing pixmap of the appropriate size */
314 static gint compass_configure_event(GtkWidget *widget,
315 GdkEventConfigure *event, gpointer data) {
316 cache_context_t *context = (cache_context_t*)data;
317
318 if(context->gotoc.compass_pixmap)
319 gdk_pixmap_unref(context->gotoc.compass_pixmap);
320
321 context->gotoc.compass_pixmap = gdk_pixmap_new(widget->window,
322 widget->allocation.width,
323 widget->allocation.height,
324 -1);
325 compass_draw(widget, context);
326 // goto_update(context);
327
328 return TRUE;
329 }
330
331 /* Redraw the screen from the backing pixmap */
332 static gint compass_expose_event(GtkWidget *widget, GdkEventExpose *event,
333 gpointer data) {
334 cache_context_t *context = (cache_context_t*)data;
335
336 gdk_draw_pixmap(widget->window,
337 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
338 context->gotoc.compass_pixmap,
339 event->area.x, event->area.y,
340 event->area.x, event->area.y,
341 event->area.width, event->area.height);
342
343 return FALSE;
344 }
345
346 static void sat_draw(GtkWidget *widget, cache_context_t *context) {
347 gint width = widget->allocation.width;
348 gint height = widget->allocation.height;
349
350 /* erase background */
351 gdk_draw_rectangle(context->gotoc.sat_pixmap,
352 widget->style->bg_gc[GTK_STATE_NORMAL], TRUE,
353 0, 0, width, height);
354
355 gps_sat_t *sat = gps_get_sats(context->appdata);
356 if(sat && sat->num) {
357 /* setup required colors */
358 GdkGC *used_gc = gdk_gc_new(context->gotoc.sat_pixmap);
359 gdk_gc_copy(used_gc, widget->style->black_gc);
360 GdkColor used_color;
361 gdk_color_parse("#008000", &used_color); // green
362 gdk_gc_set_rgb_fg_color(used_gc, &used_color);
363
364 #define SAT_SPACING 3
365 int i, x;
366 int swid = (width-SAT_SPACING*(sat->num-1))/sat->num;
367
368 /* as of xgps, a ss of 40 and more is "plenty" */
369 int max_ss = 40;
370 for(i=0;i<sat->num;i++)
371 if(sat->ss[i] > max_ss) sat->ss[i] = max_ss;
372
373 if(swid > 40) {
374 swid = 40;
375 x = (width-sat->num*swid)/2;
376 } else
377 x = 0;
378
379 for(i=0;i<sat->num;i++) {
380 char str[32];
381 #ifdef USE_MAEMO
382 snprintf(str, sizeof(str), "<span size=\"small\">%d</span>", sat->PRN[i]);
383 PangoLayout *layout = gtk_widget_create_pango_layout(widget, NULL);
384 pango_layout_set_markup(layout, str, strlen(str));
385 #else
386 snprintf(str, sizeof(str), "%d", sat->PRN[i]);
387 PangoLayout *layout = gtk_widget_create_pango_layout(widget, str);
388 #endif
389
390 int tw, th;
391 pango_layout_get_pixel_size(layout, &tw, &th);
392 gdk_draw_layout(context->gotoc.sat_pixmap,
393 widget->style->text_gc[GTK_STATE_NORMAL],
394 x + swid/2 - tw/2, height - th, layout);
395
396 g_object_unref(layout);
397
398 int h = (height-th) * sat->ss[i] / max_ss;
399
400 gdk_draw_rectangle(context->gotoc.sat_pixmap,
401 sat->used[i]?used_gc:widget->style->fg_gc[GTK_STATE_NORMAL],
402 TRUE, x, height-h-th, swid, h);
403
404 x += SAT_SPACING+swid;
405 }
406
407 /* free sat infos */
408 free(sat->PRN);
409 free(sat->ss);
410 free(sat->used);
411 } else {
412 PangoLayout *layout =
413 gtk_widget_create_pango_layout(widget, _("No SAT info"));
414 int tw, th;
415 pango_layout_get_pixel_size(layout, &tw, &th);
416 gdk_draw_layout(context->gotoc.sat_pixmap,
417 widget->style->text_gc[GTK_STATE_NORMAL],
418 (width - tw)/2, (height - th)/2, layout);
419
420 g_object_unref(layout);
421 }
422
423 /* free the sat structure */
424 if(sat)
425 free(sat);
426 }
427
428 /* Create a new backing pixmap of the appropriate size */
429 static gint sat_configure_event(GtkWidget *widget, GdkEventConfigure *event,
430 gpointer data) {
431 cache_context_t *context = (cache_context_t*)data;
432
433 if(context->gotoc.sat_pixmap)
434 gdk_pixmap_unref(context->gotoc.sat_pixmap);
435
436 context->gotoc.sat_pixmap = gdk_pixmap_new(widget->window,
437 widget->allocation.width,
438 widget->allocation.height,
439 -1);
440 sat_draw(widget, context);
441
442 return TRUE;
443 }
444
445 /* Redraw the screen from the backing pixmap */
446 static gint sat_expose_event(GtkWidget *widget, GdkEventExpose *event,
447 gpointer data) {
448 cache_context_t *context = (cache_context_t*)data;
449
450 gdk_draw_pixmap(widget->window,
451 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
452 context->gotoc.sat_pixmap,
453 event->area.x, event->area.y,
454 event->area.x, event->area.y,
455 event->area.width, event->area.height);
456
457 return FALSE;
458 }
459
460 gint goto_destroy_event(GtkWidget *widget, gpointer data ) {
461 cache_context_t *context = (cache_context_t*)data;
462
463 printf("destroying goto view\n");
464
465 #ifdef ESPEAK
466 espeak_enable(context, FALSE);
467 #endif
468
469 /* stop timer */
470 if(context->gotoc.handler_id)
471 gtk_timeout_remove(context->gotoc.handler_id);
472
473 return FALSE;
474 }
475
476 static gboolean goto_update(gpointer data) {
477 cache_context_t *context = (cache_context_t*)data;
478
479 if(context->gotoc.sat_pixmap) {
480 static int sub = 0;
481
482 if(!sub) {
483 /* draw sat view */
484 sat_draw(context->gotoc.sat_area, context);
485 gtk_widget_queue_draw_area(context->gotoc.sat_area, 0,0,
486 context->gotoc.sat_area->allocation.width,
487 context->gotoc.sat_area->allocation.height);
488 }
489
490 if(sub++ == 5) sub = 0;
491 }
492
493 if(context->gotoc.compass_pixmap) {
494 /* draw compass */
495 compass_draw(context->gotoc.compass_area, context);
496 gtk_widget_queue_draw_area(context->gotoc.compass_area, 0,0,
497 context->gotoc.compass_area->allocation.width,
498 context->gotoc.compass_area->allocation.height);
499 }
500
501 pos_t *pos = gps_get_pos(context->appdata);
502 if(pos && !isnan(pos->lat) && !isnan(pos->lon) &&
503 !isnan(context->gotoc.pos.lat) && !isnan(context->gotoc.pos.lon)) {
504 char str[16];
505 gpx_pos_get_distance_str(str, sizeof(str),
506 *pos, context->gotoc.pos,
507 context->appdata->imperial);
508 gtk_label_set_text(GTK_LABEL(context->gotoc.distance_label), str);
509
510 snprintf(str, sizeof(str), _("%.1f°"),
511 gpx_pos_get_bearing(*pos, context->gotoc.pos));
512 gtk_label_set_text(GTK_LABEL(context->gotoc.bearing_label), str);
513 } else {
514 gtk_label_set_text(GTK_LABEL(context->gotoc.distance_label), "-----");
515 gtk_label_set_text(GTK_LABEL(context->gotoc.bearing_label), "-----");
516 }
517
518 float eph = gps_get_eph(context->appdata);
519 if(isnan(eph))
520 gtk_label_set_text(GTK_LABEL(context->gotoc.eph_label), "-----");
521 else {
522 char str[16];
523 /* distance needs to be given in km */
524 if(context->appdata->imperial)
525 eph /= (6371.0/3959.0); // km to miles
526
527 distance_str(str, sizeof(str), eph/1000.0, context->appdata->imperial);
528 gtk_label_set_text(GTK_LABEL(context->gotoc.eph_label), str);
529 }
530
531 return TRUE; // fire again
532 }
533
534 static gboolean compass_clicked_event(GtkWidget *widget, GdkEventButton *event,
535 gpointer user_data) {
536 cache_context_t *context = (cache_context_t*)user_data;
537
538 context->appdata->compass_locked = !context->appdata->compass_locked;
539
540 printf("compass is now %slocked\n",
541 context->appdata->compass_locked?"":"un");
542
543 if(context->gotoc.compass_pixmap) {
544 /* draw compass */
545 compass_draw(context->gotoc.compass_area, context);
546 gtk_widget_queue_draw_area(context->gotoc.compass_area, 0,0,
547 context->gotoc.compass_area->allocation.width,
548 context->gotoc.compass_area->allocation.height);
549 }
550 return FALSE;
551 }
552
553 static void pos_modified(GtkWidget *widget, gpointer data ) {
554 cache_context_t *context = (cache_context_t*)data;
555
556 /* extract position from entries */
557 context->gotoc.pos.lat = lat_entry_get(context->gotoc.lat_entry);
558 context->gotoc.pos.lon = lon_entry_get(context->gotoc.lon_entry);
559
560 goto_update(context);
561 }
562
563 #ifdef ESPEAK
564 static void espeak_clicked(GtkWidget *widget, gpointer data) {
565 cache_context_t *context = (cache_context_t*)data;
566
567 GtkWidget *icon = gtk_button_get_image(GTK_BUTTON(widget));
568 gtk_widget_destroy(icon);
569 context->appdata->espeak.enabled = !context->appdata->espeak.enabled;
570 gtk_button_set_image(GTK_BUTTON(widget), icon_get_widget(ICON_MISC,
571 context->appdata->espeak.enabled?6:7));
572
573 espeak_enable(context, context->appdata->espeak.enabled);
574 }
575 #endif
576
577 GtkWidget *goto_cache(cache_context_t *context) {
578 int i;
579
580 /* clear list used for averaging */
581 for(i=0;i<MAX_AVERAGE;i++)
582 context->gotoc.head_avg[i] = NAN;
583
584 context->gotoc.pos = gpx_cache_pos(context->cache);
585
586 GtkWidget *hbox = gtk_hbox_new(FALSE, 32);
587
588 context->gotoc.compass_area = gtk_drawing_area_new();
589 gtk_drawing_area_size(GTK_DRAWING_AREA(context->gotoc.compass_area),
590 COMPASS_SIZE, COMPASS_SIZE);
591
592 gtk_signal_connect(GTK_OBJECT(context->gotoc.compass_area), "expose_event",
593 (GtkSignalFunc)compass_expose_event, context);
594 gtk_signal_connect(GTK_OBJECT(context->gotoc.compass_area),"configure_event",
595 (GtkSignalFunc)compass_configure_event, context);
596 gtk_signal_connect(GTK_OBJECT(context->gotoc.compass_area),
597 "button_press_event",
598 (GtkSignalFunc)compass_clicked_event, context);
599
600 gtk_widget_set_events(context->gotoc.compass_area, GDK_EXPOSURE_MASK);
601 gtk_widget_add_events(context->gotoc.compass_area, GDK_BUTTON_PRESS_MASK);
602 gtk_box_pack_start_defaults(GTK_BOX(hbox), context->gotoc.compass_area);
603
604 GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
605 GtkWidget *table = gtk_table_new(5, 3, FALSE);
606
607 /* ---------- destination coordinates ------- */
608
609 /* SIZE_SMALL doesn't work here as setting the label returns to normal */
610 gtk_table_attach_defaults(GTK_TABLE(table), left_label_new(_("Latitude:")), 0,1,0,1);
611 context->gotoc.lat_entry = lat_entry_new(context->gotoc.pos.lat);
612 gtk_table_attach_defaults(GTK_TABLE(table), context->gotoc.lat_entry, 1,2,0,1);
613 gtk_table_attach_defaults(GTK_TABLE(table), left_label_new(_("Longitude:")), 0,1,1,2);
614 context->gotoc.lon_entry = lon_entry_new(context->gotoc.pos.lon);
615 gtk_table_attach_defaults(GTK_TABLE(table), context->gotoc.lon_entry, 1,2,1,2);
616
617 g_signal_connect(G_OBJECT(context->gotoc.lat_entry), "changed",
618 G_CALLBACK(pos_modified), context);
619 g_signal_connect(G_OBJECT(context->gotoc.lon_entry), "changed",
620 G_CALLBACK(pos_modified), context);
621
622 gtk_table_attach_defaults(GTK_TABLE(table), preset_coordinate_picker(context->appdata,
623 context->gotoc.lat_entry, context->gotoc.lon_entry), 2,3,0,1);
624
625 gtk_table_attach_defaults(GTK_TABLE(table), goto_coordinate(context->appdata,
626 context->gotoc.lat_entry, context->gotoc.lon_entry), 2,3,1,2);
627
628 gtk_table_set_row_spacing(GTK_TABLE(table), 1, 16);
629 gtk_table_set_col_spacing(GTK_TABLE(table), 0, 16);
630
631 /* -------------- distance label ------------------------- */
632 gtk_table_attach_defaults(GTK_TABLE(table), left_label_new(_("Distance:")), 0,1,2,3);
633 gtk_table_attach_defaults(GTK_TABLE(table),
634 (context->gotoc.distance_label = gtk_label_new("-----")), 1,2,2,3);
635
636 /* -------------- bearing label ------------------------- */
637 gtk_table_attach_defaults(GTK_TABLE(table), left_label_new(_("Bearing:")), 0,1,3,4);
638 gtk_table_attach_defaults(GTK_TABLE(table),
639 (context->gotoc.bearing_label = gtk_label_new("-----")), 1,2,3,4);
640
641 /* -------------- error label ------------------------- */
642 gtk_table_attach_defaults(GTK_TABLE(table), left_label_new(_("Est. error:")), 0,1,4,5);
643 gtk_table_attach_defaults(GTK_TABLE(table),
644 (context->gotoc.eph_label = gtk_label_new("-----")), 1,2,4,5);
645
646 gtk_table_set_row_spacing(GTK_TABLE(table), 4, 16);
647
648 /* -------------- sat view box ------------------------- */
649 GtkWidget *ihbox = gtk_hbox_new(FALSE,0);
650
651 context->gotoc.sat_area = gtk_drawing_area_new();
652 gtk_drawing_area_size(GTK_DRAWING_AREA(context->gotoc.sat_area),
653 SAT_WIDTH, SAT_HEIGHT);
654
655 gtk_signal_connect(GTK_OBJECT(context->gotoc.sat_area), "expose_event",
656 (GtkSignalFunc)sat_expose_event, context);
657 gtk_signal_connect(GTK_OBJECT(context->gotoc.sat_area),"configure_event",
658 (GtkSignalFunc)sat_configure_event, context);
659
660 gtk_widget_set_events(context->gotoc.sat_area, GDK_EXPOSURE_MASK);
661
662 gtk_box_pack_start(GTK_BOX(ihbox), context->gotoc.sat_area, 1,0,0);
663
664 #ifdef ESPEAK
665 GtkWidget *ivbox = gtk_vbox_new(FALSE, 0);
666 GtkWidget *button = button_new();
667 gtk_button_set_image(GTK_BUTTON(button), icon_get_widget(ICON_MISC,
668 context->appdata->espeak.enabled?6:7));
669 gtk_signal_connect (GTK_OBJECT(button), "clicked",
670 GTK_SIGNAL_FUNC(espeak_clicked), context);
671 if(context->appdata->espeak.sample_rate < 0)
672 gtk_widget_set_sensitive(button, FALSE);
673 else
674 if(context->appdata->espeak.enabled)
675 espeak_enable(context, TRUE);
676
677 gtk_box_pack_start(GTK_BOX(ivbox), button, 1,0,0);
678 gtk_box_pack_start(GTK_BOX(ihbox), ivbox, 1,0,0);
679 #endif
680
681 gtk_table_attach_defaults(GTK_TABLE(table), ihbox, 0,3,5,6);
682
683 /* ------------------------------------------------------- */
684
685 gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, FALSE, 0);
686 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
687
688 context->gotoc.handler_id =
689 gtk_timeout_add(UPDATE_MS, goto_update, context);
690
691 return hbox;
692 }
693
694 void goto_coordinate_update(cache_context_t *context) {
695 static pos_t pos = { 0.0, 0.0 };
696
697 if(!context->notes.modified)
698 return;
699
700 pos_t npos = notes_get_pos(context);
701 if(pos_differ(&npos, &pos)) {
702 pos = npos;
703
704 lat_entry_set(context->gotoc.lat_entry, npos.lat);
705 lon_entry_set(context->gotoc.lon_entry, npos.lon);
706 }
707 }