Contents of /trunk/src/goto.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 294 - (show annotations)
Wed Aug 18 18:24:19 2010 UTC (13 years, 8 months ago) by harbaum
File MIME type: text/plain
File size: 23840 byte(s)
All icons SVGs
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 cairo_t *cr = gdk_cairo_create(context->gotoc.compass_pixmap);
129
130 gint width = widget->allocation.width;
131 gint height = widget->allocation.height;
132 gint diameter = (height < width)?height:width;
133
134 gint xcenter = width/2;
135 gint ycenter = height/2;
136 gint radius = diameter/2;
137
138 /* erase background */
139 gdk_draw_rectangle(context->gotoc.compass_pixmap,
140 widget->style->bg_gc[GTK_STATE_NORMAL], TRUE,
141 0, 0, width, height);
142
143 /* use white rosetta background unless the real background */
144 /* is very bright. Use bright grey then */
145
146 if(widget->style->bg[GTK_STATE_NORMAL].red +
147 widget->style->bg[GTK_STATE_NORMAL].green +
148 widget->style->bg[GTK_STATE_NORMAL].blue < 3*60000) {
149 /* background is very bright, use white */
150 cairo_set_source_rgb(cr, 1, 1, 1);
151 } else
152 cairo_set_source_rgb(cr, 0.87, 0.87, 0.87);
153
154 cairo_arc(cr, width/2, height/2, diameter/2, 0, 2 * M_PI);
155 cairo_fill(cr);
156
157 /* draw the locked/unlocked icon */
158 gdk_draw_pixbuf(context->gotoc.compass_pixmap,
159 widget->style->fg_gc[GTK_STATE_NORMAL],
160 icon_get(ICON_MISC, context->appdata->compass_locked?0:1),
161 0, 0, (width-diameter)/2 + diameter/64,
162 (height+diameter)/2 - 32 - diameter/64 , 32, 32,
163 GDK_RGB_DITHER_NONE,0,0);
164
165 /* don't update heading if the compass is locked */
166 if(!context->appdata->compass_locked) {
167 int i, valid = 0, cnt=0;
168 double x_sum = 0.0, y_sum = 0.0;
169
170 /* shift heading buffer up one entry and add new value at entry 0 */
171 for(i=MAX_AVERAGE-1;i>0;i--)
172 context->gotoc.head_avg[i] = context->gotoc.head_avg[i-1];
173 context->gotoc.head_avg[0] = gps_get_heading(context->appdata)*M_PI/180.0;
174
175 // printf("Damping = %d\n", context->appdata->compass_damping);
176 // printf("add heading %f\n", rad2deg(context->gotoc.head_avg[0]));
177
178 /* determine number of valid entries */
179 for(i=0;i<MAX_AVERAGE && valid<context->appdata->compass_damping;i++)
180 if(!isnan(context->gotoc.head_avg[i]))
181 valid++;
182
183 /* map back to angle if at least one value has been added */
184 if(valid) {
185 /* map all valid antries onto a circle */
186 for(i=0;i<MAX_AVERAGE && cnt<context->appdata->compass_damping;i++) {
187 if(!isnan(context->gotoc.head_avg[i])) {
188 float weight = 1.0 - ((float)(cnt++)/(float)valid);
189 printf("weight = %f * %f\n",
190 weight, rad2deg(context->gotoc.head_avg[i]));
191 x_sum += weight * sin(context->gotoc.head_avg[i]);
192 y_sum += weight * cos(context->gotoc.head_avg[i]);
193 }
194 }
195
196 printf("%d valid heading entries\n", valid);
197 context->gotoc.heading = atan2(x_sum, y_sum);
198 printf("averaged heading: %f\n",
199 rad2deg(context->gotoc.heading));
200 } else {
201 // printf("no valid heading, keeping old value heading\n");
202 }
203 }
204
205 if(!isnan(context->gotoc.heading)) {
206
207 /* --------------------------- draw sun ---------------------- */
208 float sun_az = astro_get_sun_azimuth(context->appdata);
209 if(!isnan(sun_az)) {
210 float sang = sun_az - context->gotoc.heading;
211
212 cairo_set_source_rgb(cr, 1.0, 0.8, 0.0);
213
214 int sd = diameter/6;
215 int sx = xcenter + (diameter/2 - sd/2) * sin(sang);
216 int sy = ycenter - (diameter/2 - sd/2) * cos(sang);
217
218 int scd = 0.6*sd;
219
220 cairo_arc (cr, sx, sy, scd/2, 0, 2 * M_PI);
221 cairo_fill(cr);
222
223 /* draw sun rays */
224 for(f=0;f<2*M_PI-M_PI/8;f+=M_PI/4) {
225 float ang = f - context->gotoc.heading;
226
227 cairo_move_to(cr, sx + sd * 0.5 * sin(ang),
228 sy + sd * 0.5 * -cos(ang));
229
230 cairo_line_to(cr, sx + sd * 0.2 * sin(ang+0.5),
231 sy + sd * 0.2 * -cos(ang+0.5));
232
233 cairo_line_to(cr, sx + sd * 0.2 * sin(ang-0.5),
234 sy + sd * 0.2 * -cos(ang-0.5));
235
236 cairo_close_path (cr);
237 cairo_fill(cr);
238 }
239 }
240
241 /* ---------------- draw markers -------------------- */
242 cairo_set_source_rgb(cr, 0, 0, 0);
243
244 for(i=0,f=0;f<2*M_PI-M_PI/8;f+=M_PI/4,i++) {
245 float ang = f - context->gotoc.heading;
246
247 if(!(i&1)) {
248 #define OUT (1.0-LETTER_SPACE)
249
250 cairo_move_to(cr, xcenter + radius * OUT * sin(ang),
251 ycenter + radius * OUT * -cos(ang));
252
253 cairo_line_to(cr, xcenter + radius * (OUT-DIAMOND_HEIGHT/2) *
254 sin(ang+DIAMOND_WIDTH),
255 ycenter + radius * (OUT-DIAMOND_HEIGHT/2)*
256 -cos(ang+DIAMOND_WIDTH));
257
258 cairo_line_to(cr, xcenter + radius * (OUT-DIAMOND_HEIGHT) * sin(ang),
259 ycenter + radius * (OUT-DIAMOND_HEIGHT) * -cos(ang));
260
261 cairo_line_to(cr, xcenter + radius * (OUT-DIAMOND_HEIGHT/2) *
262 sin(ang-DIAMOND_WIDTH),
263 ycenter + radius * (OUT-DIAMOND_HEIGHT/2) *
264 -cos(ang-DIAMOND_WIDTH));
265
266 cairo_close_path (cr);
267 cairo_fill(cr);
268
269 const char *str[] = { "N", "E", "S", "W" };
270 PangoLayout *layout =
271 gtk_widget_create_pango_layout(widget, _(str[i/2]));
272 int tw, th;
273 pango_layout_get_pixel_size(layout, &tw, &th);
274
275 gdk_draw_layout(context->gotoc.compass_pixmap, widget->style->black_gc,
276 xcenter + radius * (1.0-LETTER_SPACE/2)*sin(ang) - tw/2,
277 ycenter + radius * (1.0-LETTER_SPACE/2)*-cos(ang) - th/2,
278 layout);
279
280 g_object_unref(layout);
281 } else {
282 cairo_move_to(cr, xcenter + radius * 0.9 * sin(ang),
283 ycenter + radius * 0.9 * -cos(ang));
284
285 cairo_line_to(cr, xcenter + radius * 1.0 * sin(ang),
286 ycenter + radius * 1.0 * -cos(ang));
287
288 cairo_stroke(cr);
289 }
290 }
291
292 /* --------------------------- draw arrow ---------------------- */
293
294 pos_t *pos = gps_get_pos(context->appdata);
295 if(pos && !isnan(pos->lat) && !isnan(pos->lon)) {
296 context->appdata->gps.saved = *pos; /* save position */
297
298 float arot =
299 gpx_pos_get_bearing(*pos, context->gotoc.pos) *
300 M_PI/180 - context->gotoc.heading;
301
302 cairo_move_to(cr, xcenter + radius * ARROW_LENGTH * sin(arot),
303 ycenter + radius * ARROW_LENGTH * -cos(arot));
304
305 cairo_line_to(cr, xcenter + radius * -ARROW_LENGTH * sin(arot+ARROW_WIDTH),
306 ycenter + radius * -ARROW_LENGTH * -cos(arot+ARROW_WIDTH));
307
308 cairo_line_to(cr, xcenter + radius * -0.5 * ARROW_LENGTH * sin(arot),
309 ycenter + radius * -0.5 * ARROW_LENGTH * -cos(arot));
310
311 cairo_line_to(cr, xcenter + radius * -ARROW_LENGTH * sin(arot-ARROW_WIDTH),
312 ycenter + radius * -ARROW_LENGTH * -cos(arot-ARROW_WIDTH));
313
314 cairo_set_source_rgb(cr, 0.5, 0.0, 0.0);
315 cairo_close_path (cr);
316 cairo_fill(cr);
317
318 } else {
319 PangoLayout *layout;
320
321 if(context->appdata->use_gps)
322 layout = gtk_widget_create_pango_layout(widget, _("No fix"));
323 else
324 layout = gtk_widget_create_pango_layout(widget, _("GPS disabled"));
325
326 int tw, th;
327 pango_layout_get_pixel_size(layout, &tw, &th);
328
329 gdk_draw_layout(context->gotoc.compass_pixmap, widget->style->black_gc,
330 xcenter - tw/2, ycenter - th/2, layout);
331
332 g_object_unref(layout);
333 }
334 }
335 cairo_destroy(cr);
336 }
337
338 /* Create a new backing pixmap of the appropriate size */
339 static gint compass_configure_event(GtkWidget *widget,
340 GdkEventConfigure *event, gpointer data) {
341 cache_context_t *context = (cache_context_t*)data;
342
343 if(context->gotoc.compass_pixmap)
344 gdk_pixmap_unref(context->gotoc.compass_pixmap);
345
346 context->gotoc.compass_pixmap = gdk_pixmap_new(widget->window,
347 widget->allocation.width,
348 widget->allocation.height,
349 -1);
350 compass_draw(widget, context);
351 // goto_update(context);
352
353 return TRUE;
354 }
355
356 /* Redraw the screen from the backing pixmap */
357 static gint compass_expose_event(GtkWidget *widget, GdkEventExpose *event,
358 gpointer data) {
359 cache_context_t *context = (cache_context_t*)data;
360
361 gdk_draw_pixmap(widget->window,
362 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
363 context->gotoc.compass_pixmap,
364 event->area.x, event->area.y,
365 event->area.x, event->area.y,
366 event->area.width, event->area.height);
367
368 return FALSE;
369 }
370
371 static void sat_draw(GtkWidget *widget, cache_context_t *context) {
372 gint width = widget->allocation.width;
373 gint height = widget->allocation.height;
374
375 /* erase background */
376 gdk_draw_rectangle(context->gotoc.sat_pixmap,
377 widget->style->bg_gc[GTK_STATE_NORMAL], TRUE,
378 0, 0, width, height);
379
380 gps_sat_t *sat = gps_get_sats(context->appdata);
381 gint sat_num = gps_get_satnum(context->appdata);
382
383 if(sat && sat_num) {
384 /* setup required colors */
385 GdkGC *used_gc = gdk_gc_new(context->gotoc.sat_pixmap);
386 gdk_gc_copy(used_gc, widget->style->black_gc);
387 GdkColor used_color;
388 gdk_color_parse("#008000", &used_color); // green
389 gdk_gc_set_rgb_fg_color(used_gc, &used_color);
390
391 #define SAT_SPACING 3
392 int i, x;
393 int swid = (width-SAT_SPACING*(sat_num-1))/sat_num;
394
395 /* as of xgps, a ss of 40 and more is "plenty" */
396 int max_ss = 40;
397 for(i=0;i<sat_num;i++)
398 if(sat[i].ss > max_ss) sat[i].ss = max_ss;
399
400 if(swid > 40) {
401 swid = 40;
402 x = (width-sat_num*swid)/2;
403 } else
404 x = 0;
405
406 for(i=0;i<sat_num;i++) {
407 char str[32];
408 #ifdef USE_MAEMO
409 snprintf(str, sizeof(str), "<span size=\"small\">%d</span>", sat[i].prn);
410 PangoLayout *layout = gtk_widget_create_pango_layout(widget, NULL);
411 pango_layout_set_markup(layout, str, strlen(str));
412 #else
413 snprintf(str, sizeof(str), "%d", sat[i].prn);
414 PangoLayout *layout = gtk_widget_create_pango_layout(widget, str);
415 #endif
416
417 int tw, th;
418 pango_layout_get_pixel_size(layout, &tw, &th);
419 gdk_draw_layout(context->gotoc.sat_pixmap,
420 widget->style->text_gc[GTK_STATE_NORMAL],
421 x + swid/2 - tw/2, height - th, layout);
422
423 g_object_unref(layout);
424
425 int h = (height-th) * sat[i].ss / max_ss;
426
427 gdk_draw_rectangle(context->gotoc.sat_pixmap,
428 sat[i].used?used_gc:widget->style->fg_gc[GTK_STATE_NORMAL],
429 TRUE, x, height-h-th, swid, h);
430
431 x += SAT_SPACING+swid;
432 }
433
434 } else {
435 PangoLayout *layout =
436 gtk_widget_create_pango_layout(widget, _("No SAT info"));
437 int tw, th;
438 pango_layout_get_pixel_size(layout, &tw, &th);
439 gdk_draw_layout(context->gotoc.sat_pixmap,
440 widget->style->text_gc[GTK_STATE_NORMAL],
441 (width - tw)/2, (height - th)/2, layout);
442
443 g_object_unref(layout);
444 }
445 }
446
447 /* Create a new backing pixmap of the appropriate size */
448 static gint sat_configure_event(GtkWidget *widget, GdkEventConfigure *event,
449 gpointer data) {
450 cache_context_t *context = (cache_context_t*)data;
451
452 if(context->gotoc.sat_pixmap)
453 gdk_pixmap_unref(context->gotoc.sat_pixmap);
454
455 context->gotoc.sat_pixmap = gdk_pixmap_new(widget->window,
456 widget->allocation.width,
457 widget->allocation.height,
458 -1);
459 sat_draw(widget, context);
460
461 return TRUE;
462 }
463
464 /* Redraw the screen from the backing pixmap */
465 static gint sat_expose_event(GtkWidget *widget, GdkEventExpose *event,
466 gpointer data) {
467 cache_context_t *context = (cache_context_t*)data;
468
469 gdk_draw_pixmap(widget->window,
470 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
471 context->gotoc.sat_pixmap,
472 event->area.x, event->area.y,
473 event->area.x, event->area.y,
474 event->area.width, event->area.height);
475
476 return FALSE;
477 }
478
479 gint goto_destroy_event(GtkWidget *widget, gpointer data ) {
480 cache_context_t *context = (cache_context_t*)data;
481
482 printf("destroying goto view\n");
483
484 #ifdef ESPEAK
485 espeak_enable(context, FALSE);
486 #endif
487
488 /* stop timer */
489 if(context->gotoc.handler_id)
490 gtk_timeout_remove(context->gotoc.handler_id);
491
492 return FALSE;
493 }
494
495 static gboolean goto_update(gpointer data) {
496 cache_context_t *context = (cache_context_t*)data;
497
498 if(context->gotoc.sat_pixmap) {
499 static int sub = 0;
500
501 if(!sub) {
502 /* draw sat view */
503 sat_draw(context->gotoc.sat_area, context);
504 gtk_widget_queue_draw_area(context->gotoc.sat_area, 0,0,
505 context->gotoc.sat_area->allocation.width,
506 context->gotoc.sat_area->allocation.height);
507 }
508
509 if(sub++ == 5) sub = 0;
510 }
511
512 if(context->gotoc.compass_pixmap) {
513 /* draw compass */
514 compass_draw(context->gotoc.compass_area, context);
515 gtk_widget_queue_draw_area(context->gotoc.compass_area, 0,0,
516 context->gotoc.compass_area->allocation.width,
517 context->gotoc.compass_area->allocation.height);
518 }
519
520 pos_t *pos = gps_get_pos(context->appdata);
521 if(pos && !isnan(pos->lat) && !isnan(pos->lon) &&
522 !isnan(context->gotoc.pos.lat) && !isnan(context->gotoc.pos.lon)) {
523 char str[16];
524 gpx_pos_get_distance_str(str, sizeof(str),
525 *pos, context->gotoc.pos,
526 context->appdata->imperial);
527 gtk_label_set_text(GTK_LABEL(context->gotoc.distance_label), str);
528
529 snprintf(str, sizeof(str), _("%.1f°"),
530 gpx_pos_get_bearing(*pos, context->gotoc.pos));
531 gtk_label_set_text(GTK_LABEL(context->gotoc.bearing_label), str);
532 } else {
533 gtk_label_set_text(GTK_LABEL(context->gotoc.distance_label), "-----");
534 gtk_label_set_text(GTK_LABEL(context->gotoc.bearing_label), "-----");
535 }
536
537 float eph = gps_get_eph(context->appdata);
538 if(isnan(eph))
539 gtk_label_set_text(GTK_LABEL(context->gotoc.eph_label), "-----");
540 else {
541 char str[16];
542 /* distance needs to be given in km */
543 if(context->appdata->imperial)
544 eph /= (6371.0/3959.0); // km to miles
545
546 distance_str(str, sizeof(str), eph/1000.0, context->appdata->imperial);
547 gtk_label_set_text(GTK_LABEL(context->gotoc.eph_label), str);
548 }
549
550 return TRUE; // fire again
551 }
552
553 static gboolean compass_clicked_event(GtkWidget *widget, GdkEventButton *event,
554 gpointer user_data) {
555 cache_context_t *context = (cache_context_t*)user_data;
556
557 context->appdata->compass_locked = !context->appdata->compass_locked;
558
559 printf("compass is now %slocked\n",
560 context->appdata->compass_locked?"":"un");
561
562 if(context->gotoc.compass_pixmap) {
563 /* draw compass */
564 compass_draw(context->gotoc.compass_area, context);
565 gtk_widget_queue_draw_area(context->gotoc.compass_area, 0,0,
566 context->gotoc.compass_area->allocation.width,
567 context->gotoc.compass_area->allocation.height);
568 }
569 return FALSE;
570 }
571
572 static void pos_modified(GtkWidget *widget, gpointer data ) {
573 cache_context_t *context = (cache_context_t*)data;
574
575 /* extract position from entries */
576 context->gotoc.pos.lat = lat_entry_get(context->gotoc.lat_entry);
577 context->gotoc.pos.lon = lon_entry_get(context->gotoc.lon_entry);
578
579 goto_update(context);
580 }
581
582 #ifdef ESPEAK
583 static void espeak_clicked(GtkWidget *widget, gpointer data) {
584 cache_context_t *context = (cache_context_t*)data;
585
586 GtkWidget *icon = gtk_button_get_image(GTK_BUTTON(widget));
587 gtk_widget_destroy(icon);
588 context->appdata->espeak.enabled = !context->appdata->espeak.enabled;
589 gtk_button_set_image(GTK_BUTTON(widget), icon_get_widget(ICON_MISC,
590 context->appdata->espeak.enabled?6:7));
591
592 espeak_enable(context, context->appdata->espeak.enabled);
593 }
594 #endif
595
596 /* create "goto" tab */
597 GtkWidget *goto_cache(cache_context_t *context) {
598 int i;
599
600 /* clear list used for averaging */
601 for(i=0;i<MAX_AVERAGE;i++)
602 context->gotoc.head_avg[i] = NAN;
603
604 context->gotoc.pos = gpx_cache_pos(context->cache);
605
606 GtkWidget *hbox = portrait_box_new(FALSE, 32);
607
608 context->gotoc.compass_area = gtk_drawing_area_new();
609 gtk_drawing_area_size(GTK_DRAWING_AREA(context->gotoc.compass_area),
610 COMPASS_SIZE, COMPASS_SIZE);
611
612 gtk_signal_connect(GTK_OBJECT(context->gotoc.compass_area), "expose_event",
613 (GtkSignalFunc)compass_expose_event, context);
614 gtk_signal_connect(GTK_OBJECT(context->gotoc.compass_area),"configure_event",
615 (GtkSignalFunc)compass_configure_event, context);
616 gtk_signal_connect(GTK_OBJECT(context->gotoc.compass_area),
617 "button_press_event",
618 (GtkSignalFunc)compass_clicked_event, context);
619
620 gtk_widget_set_events(context->gotoc.compass_area, GDK_EXPOSURE_MASK);
621 gtk_widget_add_events(context->gotoc.compass_area, GDK_BUTTON_PRESS_MASK);
622 gtk_box_pack_start_defaults(GTK_BOX(hbox), context->gotoc.compass_area);
623
624 GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
625 GtkWidget *table = gtk_table_new(5, 3, FALSE);
626
627 /* ---------- destination coordinates ------- */
628
629 /* SIZE_SMALL doesn't work here as setting the label returns to normal */
630 gtk_table_attach_defaults(GTK_TABLE(table), left_label_new(_("Latitude:")), 0,1,0,1);
631 context->gotoc.lat_entry = lat_entry_new(context->gotoc.pos.lat);
632 gtk_table_attach_defaults(GTK_TABLE(table), context->gotoc.lat_entry, 1,2,0,1);
633 gtk_table_attach_defaults(GTK_TABLE(table), left_label_new(_("Longitude:")), 0,1,1,2);
634 context->gotoc.lon_entry = lon_entry_new(context->gotoc.pos.lon);
635 gtk_table_attach_defaults(GTK_TABLE(table), context->gotoc.lon_entry, 1,2,1,2);
636
637 g_signal_connect(G_OBJECT(context->gotoc.lat_entry), "changed",
638 G_CALLBACK(pos_modified), context);
639 g_signal_connect(G_OBJECT(context->gotoc.lon_entry), "changed",
640 G_CALLBACK(pos_modified), context);
641
642 gtk_table_attach_defaults(GTK_TABLE(table), preset_coordinate_picker(context->appdata,
643 context->gotoc.lat_entry, context->gotoc.lon_entry), 2,3,0,1);
644
645 gtk_table_attach_defaults(GTK_TABLE(table), goto_coordinate(context->appdata,
646 context->gotoc.lat_entry, context->gotoc.lon_entry), 2,3,1,2);
647
648 gtk_table_set_row_spacing(GTK_TABLE(table), 1, 16);
649 gtk_table_set_col_spacing(GTK_TABLE(table), 0, 16);
650
651 /* -------------- distance label ------------------------- */
652 gtk_table_attach_defaults(GTK_TABLE(table), left_label_new(_("Distance:")), 0,1,2,3);
653 gtk_table_attach_defaults(GTK_TABLE(table),
654 (context->gotoc.distance_label = gtk_label_new("-----")), 1,2,2,3);
655
656 /* -------------- bearing label ------------------------- */
657 gtk_table_attach_defaults(GTK_TABLE(table), left_label_new(_("Bearing:")), 0,1,3,4);
658 gtk_table_attach_defaults(GTK_TABLE(table),
659 (context->gotoc.bearing_label = gtk_label_new("-----")), 1,2,3,4);
660
661 /* -------------- error label ------------------------- */
662 gtk_table_attach_defaults(GTK_TABLE(table), left_label_new(_("Est. error:")), 0,1,4,5);
663 gtk_table_attach_defaults(GTK_TABLE(table),
664 (context->gotoc.eph_label = gtk_label_new("-----")), 1,2,4,5);
665
666 gtk_table_set_row_spacing(GTK_TABLE(table), 4, 16);
667
668 /* -------------- sat view box ------------------------- */
669 GtkWidget *ihbox = gtk_hbox_new(FALSE,0);
670
671 context->gotoc.sat_area = gtk_drawing_area_new();
672 gtk_drawing_area_size(GTK_DRAWING_AREA(context->gotoc.sat_area),
673 SAT_WIDTH, SAT_HEIGHT);
674
675 gtk_signal_connect(GTK_OBJECT(context->gotoc.sat_area), "expose_event",
676 (GtkSignalFunc)sat_expose_event, context);
677 gtk_signal_connect(GTK_OBJECT(context->gotoc.sat_area),"configure_event",
678 (GtkSignalFunc)sat_configure_event, context);
679
680 gtk_widget_set_events(context->gotoc.sat_area, GDK_EXPOSURE_MASK);
681
682 gtk_box_pack_start(GTK_BOX(ihbox), context->gotoc.sat_area, 1,0,0);
683
684 #ifdef ESPEAK
685 GtkWidget *ivbox = gtk_vbox_new(FALSE, 0);
686 GtkWidget *button = button_new();
687 gtk_button_set_image(GTK_BUTTON(button), icon_get_widget(ICON_MISC,
688 context->appdata->espeak.enabled?6:7));
689 gtk_signal_connect (GTK_OBJECT(button), "clicked",
690 GTK_SIGNAL_FUNC(espeak_clicked), context);
691 if(context->appdata->espeak.sample_rate < 0)
692 gtk_widget_set_sensitive(button, FALSE);
693 else
694 if(context->appdata->espeak.enabled)
695 espeak_enable(context, TRUE);
696
697 gtk_box_pack_start(GTK_BOX(ivbox), button, 1,0,0);
698 gtk_box_pack_start(GTK_BOX(ihbox), ivbox, 1,0,0);
699 #endif
700
701 gtk_table_attach_defaults(GTK_TABLE(table), ihbox, 0,3,5,6);
702
703 /* ------------------------------------------------------- */
704
705 gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, FALSE, 0);
706 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
707
708 context->gotoc.handler_id =
709 gtk_timeout_add(UPDATE_MS, goto_update, context);
710
711 return hbox;
712 }
713
714 void goto_coordinate_update(cache_context_t *context) {
715 static pos_t pos = { 0.0, 0.0 };
716
717 if(!context->notes.modified)
718 return;
719
720 pos_t npos = notes_get_pos(context);
721 if(pos_differ(&npos, &pos)) {
722 pos = npos;
723
724 lat_entry_set(context->gotoc.lat_entry, npos.lat);
725 lon_entry_set(context->gotoc.lon_entry, npos.lon);
726 }
727 }