]> git.street.me.uk Git - andy/viking.git/blob - src/dialog.c
Use elevation values in creating waypoints from Wikipedia.
[andy/viking.git] / src / dialog.c
1 /*
2  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3  *
4  * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5  * Copyright (C) 2008, Hein Ragas <viking@ragas.nl>
6  * Copyright (C) 2010, Rob Norris <rw_norris@hotmail.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "viking.h"
29 #include "thumbnails.h"
30 #include "garminsymbols.h"
31 #include "degrees_converters.h"
32 #include "authors.h"
33 #include "documenters.h"
34 #include "vikgoto.h"
35 #include "util.h"
36
37 #include <glib/gi18n.h>
38
39 #include <stdlib.h>
40 #include <string.h>
41 #include <ctype.h>
42
43 void a_dialog_msg ( GtkWindow *parent, gint type, const gchar *info, const gchar *extra )
44 {
45   GtkWidget *msgbox = gtk_message_dialog_new ( parent, GTK_DIALOG_DESTROY_WITH_PARENT, type, GTK_BUTTONS_OK, info, extra );
46   gtk_dialog_run ( GTK_DIALOG(msgbox) );
47   gtk_widget_destroy ( msgbox );
48 }
49
50 gboolean a_dialog_goto_latlon ( GtkWindow *parent, struct LatLon *ll, const struct LatLon *old )
51 {
52   GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Go to Lat/Lon"),
53                                                   parent,
54                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
55                                                   GTK_STOCK_CANCEL,
56                                                   GTK_RESPONSE_REJECT,
57                                                   GTK_STOCK_OK,
58                                                   GTK_RESPONSE_ACCEPT,
59                                                   NULL);
60   GtkWidget *latlabel, *lonlabel;
61   GtkWidget *lat, *lon;
62   gchar *tmp_lat, *tmp_lon;
63
64   latlabel = gtk_label_new (_("Latitude:"));
65   lat = gtk_entry_new ();
66   tmp_lat = g_strdup_printf ( "%f", old->lat );
67   gtk_entry_set_text ( GTK_ENTRY(lat), tmp_lat );
68   g_free ( tmp_lat );
69
70   lonlabel = gtk_label_new (_("Longitude:"));
71   lon = gtk_entry_new ();
72   tmp_lon = g_strdup_printf ( "%f", old->lon );
73   gtk_entry_set_text ( GTK_ENTRY(lon), tmp_lon );
74   g_free ( tmp_lon );
75
76   gtk_widget_show ( latlabel );
77   gtk_widget_show ( lonlabel );
78   gtk_widget_show ( lat );
79   gtk_widget_show ( lon );
80
81   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), latlabel,  FALSE, FALSE, 0);
82   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), lat, FALSE, FALSE, 0);
83   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), lonlabel,  FALSE, FALSE, 0);
84   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), lon,  FALSE, FALSE, 0);
85
86   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
87
88   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
89   {
90     ll->lat = convert_dms_to_dec ( gtk_entry_get_text ( GTK_ENTRY(lat) ) );
91     ll->lon = convert_dms_to_dec ( gtk_entry_get_text ( GTK_ENTRY(lon) ) );
92     gtk_widget_destroy ( dialog );
93     return TRUE;
94   }
95
96   gtk_widget_destroy ( dialog );
97   return FALSE;
98 }
99
100 gboolean a_dialog_goto_utm ( GtkWindow *parent, struct UTM *utm, const struct UTM *old )
101 {
102   GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Go to UTM"),
103                                                   parent,
104                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
105                                                   GTK_STOCK_CANCEL,
106                                                   GTK_RESPONSE_REJECT,
107                                                   GTK_STOCK_OK,
108                                                   GTK_RESPONSE_ACCEPT,
109                                                   NULL);
110   GtkWidget *norlabel, *easlabel, *nor, *eas;
111   GtkWidget *zonehbox, *zonespin, *letterentry;
112   gchar *tmp_eas, *tmp_nor;
113   gchar tmp_letter[2];
114
115   norlabel = gtk_label_new (_("Northing:"));
116   nor = gtk_entry_new ();
117   tmp_nor = g_strdup_printf("%ld", (long) old->northing );
118   gtk_entry_set_text ( GTK_ENTRY(nor), tmp_nor );
119   g_free ( tmp_nor );
120
121   easlabel = gtk_label_new (_("Easting:"));
122   eas = gtk_entry_new ();
123   tmp_eas = g_strdup_printf("%ld", (long) old->easting );
124   gtk_entry_set_text ( GTK_ENTRY(eas), tmp_eas );
125   g_free ( tmp_eas );
126
127   zonehbox = gtk_hbox_new ( FALSE, 0 );
128   gtk_box_pack_start ( GTK_BOX(zonehbox), gtk_label_new ( _("Zone:") ), FALSE, FALSE, 5 );
129   zonespin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new ( old->zone, 1, 60, 1, 5, 0 ), 1, 0 );
130   gtk_box_pack_start ( GTK_BOX(zonehbox), zonespin, TRUE, TRUE, 5 );
131   gtk_box_pack_start ( GTK_BOX(zonehbox), gtk_label_new ( _("Letter:") ), FALSE, FALSE, 5 );
132   letterentry = gtk_entry_new ();
133   gtk_entry_set_max_length ( GTK_ENTRY(letterentry), 1 );
134   gtk_entry_set_width_chars ( GTK_ENTRY(letterentry), 2 );
135   tmp_letter[0] = old->letter;
136   tmp_letter[1] = '\0';
137   gtk_entry_set_text ( GTK_ENTRY(letterentry), tmp_letter );
138   gtk_box_pack_start ( GTK_BOX(zonehbox), letterentry, FALSE, FALSE, 5 );
139
140   gtk_widget_show ( norlabel );
141   gtk_widget_show ( easlabel );
142   gtk_widget_show ( nor );
143   gtk_widget_show ( eas );
144
145   gtk_widget_show_all ( zonehbox );
146
147   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), norlabel, FALSE, FALSE, 0);
148   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), nor, FALSE, FALSE, 0);
149   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), easlabel,  FALSE, FALSE, 0);
150   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), eas,  FALSE, FALSE, 0);
151   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zonehbox,  FALSE, FALSE, 0);
152
153   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
154
155   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
156   {
157     const gchar *letter;
158     utm->northing = atof ( gtk_entry_get_text ( GTK_ENTRY(nor) ) );
159     utm->easting = atof ( gtk_entry_get_text ( GTK_ENTRY(eas) ) );
160     utm->zone = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(zonespin) );
161     letter = gtk_entry_get_text ( GTK_ENTRY(letterentry) );
162     if (*letter)
163        utm->letter = toupper(*letter);
164     gtk_widget_destroy ( dialog );
165     return TRUE;
166   }
167
168   gtk_widget_destroy ( dialog );
169   return FALSE;
170 }
171
172 void a_dialog_response_accept ( GtkDialog *dialog )
173 {
174   gtk_dialog_response ( dialog, GTK_RESPONSE_ACCEPT );
175 }
176
177 static void symbol_entry_changed_cb(GtkWidget *combo, GtkListStore *store)
178 {
179   GtkTreeIter iter;
180   gchar *sym;
181
182   if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter))
183     return;
184
185   gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 0, (void *)&sym, -1 );
186   /* Note: symm is NULL when "(none)" is select (first cell is empty) */
187   gtk_widget_set_tooltip_text(combo, sym);
188   g_free(sym);
189 }
190
191 /* Specify if a new waypoint or not */
192 /* If a new waypoint then it uses the default_name for the suggested name allowing the user to change it.
193     The name to use is returned
194    When an existing waypoint the default name is shown but is not allowed to be changed and NULL is returned
195  */
196 /* todo: less on this side, like add track */
197 gchar *a_dialog_waypoint ( GtkWindow *parent, gchar *default_name, VikWaypoint *wp, VikCoordMode coord_mode, gboolean is_new, gboolean *updated )
198 {
199   GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Waypoint Properties"),
200                                                    parent,
201                                                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
202                                                    GTK_STOCK_CANCEL,
203                                                    GTK_RESPONSE_REJECT,
204                                                    GTK_STOCK_OK,
205                                                    GTK_RESPONSE_ACCEPT,
206                                                    NULL);
207   struct LatLon ll;
208   GtkWidget *latlabel, *lonlabel, *namelabel, *latentry, *lonentry, *altentry, *altlabel, *nameentry=NULL;
209   GtkWidget *commentlabel, *commententry, *descriptionlabel, *descriptionentry, *imagelabel, *imageentry, *symbollabel, *symbolentry;
210   GtkListStore *store;
211
212   gchar *lat, *lon, *alt;
213
214   vik_coord_to_latlon ( &(wp->coord), &ll );
215
216   lat = g_strdup_printf ( "%f", ll.lat );
217   lon = g_strdup_printf ( "%f", ll.lon );
218   vik_units_height_t height_units = a_vik_get_units_height ();
219   switch (height_units) {
220   case VIK_UNITS_HEIGHT_METRES:
221     alt = g_strdup_printf ( "%f", wp->altitude );
222     break;
223   case VIK_UNITS_HEIGHT_FEET:
224     alt = g_strdup_printf ( "%f", VIK_METERS_TO_FEET(wp->altitude) );
225     break;
226   default:
227     alt = g_strdup_printf ( "%f", wp->altitude );
228     g_critical("Houston, we've had a problem. height=%d", height_units);
229   }
230
231   *updated = FALSE;
232
233   namelabel = gtk_label_new (_("Name:"));
234   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), namelabel, FALSE, FALSE, 0);
235   if ( is_new )
236   {
237     // New waypoint, so name is still changeable
238     nameentry = gtk_entry_new ();
239     if ( default_name )
240       gtk_entry_set_text( GTK_ENTRY(nameentry), default_name );
241     g_signal_connect_swapped ( nameentry, "activate", G_CALLBACK(a_dialog_response_accept), GTK_DIALOG(dialog) );
242   }
243   else {
244     // Existing waypoint - so can not change the name this way - but at least can see it
245     if ( default_name )
246       nameentry = gtk_label_new ( default_name );
247   }
248   if ( nameentry )
249     gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), nameentry, FALSE, FALSE, 0);
250
251   latlabel = gtk_label_new (_("Latitude:"));
252   latentry = gtk_entry_new ();
253   gtk_entry_set_text ( GTK_ENTRY(latentry), lat );
254   g_free ( lat );
255
256   lonlabel = gtk_label_new (_("Longitude:"));
257   lonentry = gtk_entry_new ();
258   gtk_entry_set_text ( GTK_ENTRY(lonentry), lon );
259   g_free ( lon );
260
261   altlabel = gtk_label_new (_("Altitude:"));
262   altentry = gtk_entry_new ();
263   gtk_entry_set_text ( GTK_ENTRY(altentry), alt );
264   g_free ( alt );
265
266   commentlabel = gtk_label_new (_("Comment:"));
267   commententry = gtk_entry_new ();
268   gchar *cmt =  NULL;
269   // Auto put in some kind of 'name' as a comment if one previously 'goto'ed this exact location
270   cmt = a_vik_goto_get_search_string_for_this_place(VIK_WINDOW(parent));
271   if (cmt)
272     gtk_entry_set_text(GTK_ENTRY(commententry), cmt);
273
274   descriptionlabel = gtk_label_new (_("Description:"));
275   descriptionentry = gtk_entry_new ();
276
277   imagelabel = gtk_label_new (_("Image:"));
278   imageentry = vik_file_entry_new (GTK_FILE_CHOOSER_ACTION_OPEN);
279
280   {
281     GtkCellRenderer *r;
282     symbollabel = gtk_label_new (_("Symbol:"));
283     GtkTreeIter iter;
284
285     store = gtk_list_store_new(3, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_STRING);
286     symbolentry = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
287     gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(symbolentry), 6);
288
289     g_signal_connect(symbolentry, "changed", G_CALLBACK(symbol_entry_changed_cb), store);
290     gtk_list_store_append (store, &iter);
291     gtk_list_store_set (store, &iter, 0, NULL, 1, NULL, 2, _("(none)"), -1);
292     a_populate_sym_list(store);
293
294     r = gtk_cell_renderer_pixbuf_new ();
295     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (symbolentry), r, FALSE);
296     gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (symbolentry), r, "pixbuf", 1, NULL);
297
298     r = gtk_cell_renderer_text_new ();
299     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (symbolentry), r, FALSE);
300     gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (symbolentry), r, "text", 2, NULL);
301
302     if ( !is_new && wp->symbol ) {
303       gboolean ok;
304       gchar *sym;
305       for (ok = gtk_tree_model_get_iter_first ( GTK_TREE_MODEL(store), &iter ); ok; ok = gtk_tree_model_iter_next ( GTK_TREE_MODEL(store), &iter)) {
306         gtk_tree_model_get ( GTK_TREE_MODEL(store), &iter, 0, (void *)&sym, -1 );
307         if (sym && !strcmp(sym, wp->symbol)) {
308           g_free(sym);
309           break;
310         } else {
311           g_free(sym);
312         }
313       }
314       // Ensure is it a valid symbol in the given symbol set (large vs small)
315       // Not all symbols are available in both
316       // The check prevents a Gtk Critical message
317       if ( iter.stamp )
318         gtk_combo_box_set_active_iter(GTK_COMBO_BOX(symbolentry), &iter);
319     }
320   }
321
322   if ( !is_new && wp->comment )
323     gtk_entry_set_text ( GTK_ENTRY(commententry), wp->comment );
324
325   if ( !is_new && wp->description )
326     gtk_entry_set_text ( GTK_ENTRY(descriptionentry), wp->description );
327
328   if ( !is_new && wp->image )
329     vik_file_entry_set_filename ( VIK_FILE_ENTRY(imageentry), wp->image );
330
331
332   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), latlabel, FALSE, FALSE, 0);
333   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), latentry, FALSE, FALSE, 0);
334   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), lonlabel, FALSE, FALSE, 0);
335   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), lonentry, FALSE, FALSE, 0);
336   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), altlabel, FALSE, FALSE, 0);
337   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), altentry, FALSE, FALSE, 0);
338   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), commentlabel, FALSE, FALSE, 0);
339   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), commententry, FALSE, FALSE, 0);
340   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), descriptionlabel, FALSE, FALSE, 0);
341   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), descriptionentry, FALSE, FALSE, 0);
342   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), imagelabel, FALSE, FALSE, 0);
343   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), imageentry, FALSE, FALSE, 0);
344   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), symbollabel, FALSE, FALSE, 0);
345   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), GTK_WIDGET(symbolentry), FALSE, FALSE, 0);
346
347   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
348
349   gtk_widget_show_all ( GTK_DIALOG(dialog)->vbox );
350
351   while ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
352   {
353     if ( is_new )
354     {
355       gchar *entered_name = g_strdup ( (gchar*)gtk_entry_get_text ( GTK_ENTRY(nameentry) ) );
356       if ( strlen(entered_name) == 0 ) /* TODO: other checks (isalpha or whatever ) */
357         a_dialog_info_msg ( parent, _("Please enter a name for the waypoint.") );
358       else {
359
360         // NB: No check for unique names - this allows generation of same named entries.
361
362         /* Do It */
363         ll.lat = convert_dms_to_dec ( gtk_entry_get_text ( GTK_ENTRY(latentry) ) );
364         ll.lon = convert_dms_to_dec ( gtk_entry_get_text ( GTK_ENTRY(lonentry) ) );
365         vik_coord_load_from_latlon ( &(wp->coord), coord_mode, &ll );
366         // Always store in metres
367         switch (height_units) {
368         case VIK_UNITS_HEIGHT_METRES:
369           wp->altitude = atof ( gtk_entry_get_text ( GTK_ENTRY(altentry) ) );
370           break;
371         case VIK_UNITS_HEIGHT_FEET:
372           wp->altitude = VIK_FEET_TO_METERS(atof ( gtk_entry_get_text ( GTK_ENTRY(altentry) ) ));
373           break;
374         default:
375           wp->altitude = atof ( gtk_entry_get_text ( GTK_ENTRY(altentry) ) );
376           g_critical("Houston, we've had a problem. height=%d", height_units);
377         }
378         vik_waypoint_set_comment ( wp, gtk_entry_get_text ( GTK_ENTRY(commententry) ) );
379         vik_waypoint_set_description ( wp, gtk_entry_get_text ( GTK_ENTRY(descriptionentry) ) );
380         vik_waypoint_set_image ( wp, vik_file_entry_get_filename ( VIK_FILE_ENTRY(imageentry) ) );
381         if ( wp->image && *(wp->image) && (!a_thumbnails_exists(wp->image)) )
382           a_thumbnails_create ( wp->image );
383
384         GtkTreeIter iter, first;
385         gtk_tree_model_get_iter_first ( GTK_TREE_MODEL(store), &first );
386         if ( !gtk_combo_box_get_active_iter ( GTK_COMBO_BOX(symbolentry), &iter ) || !memcmp(&iter, &first, sizeof(GtkTreeIter)) ) {
387           vik_waypoint_set_symbol ( wp, NULL );
388         } else {
389           gchar *sym;
390           gtk_tree_model_get ( GTK_TREE_MODEL(store), &iter, 0, (void *)&sym, -1 );
391           vik_waypoint_set_symbol ( wp, sym );
392           g_free(sym);
393         }
394
395         gtk_widget_destroy ( dialog );
396         return entered_name;
397       }
398     }
399     else
400     {
401       ll.lat = convert_dms_to_dec ( gtk_entry_get_text ( GTK_ENTRY(latentry) ) );
402       ll.lon = convert_dms_to_dec ( gtk_entry_get_text ( GTK_ENTRY(lonentry) ) );
403       vik_coord_load_from_latlon ( &(wp->coord), coord_mode, &ll );
404       switch (height_units) {
405       case VIK_UNITS_HEIGHT_METRES:
406         wp->altitude = atof ( gtk_entry_get_text ( GTK_ENTRY(altentry) ) );
407         break;
408       case VIK_UNITS_HEIGHT_FEET:
409         wp->altitude = VIK_FEET_TO_METERS(atof ( gtk_entry_get_text ( GTK_ENTRY(altentry) ) ));
410         break;
411       default:
412         wp->altitude = atof ( gtk_entry_get_text ( GTK_ENTRY(altentry) ) );
413         g_critical("Houston, we've had a problem. height=%d", height_units);
414       }
415       if ( (! wp->comment) || strcmp ( wp->comment, gtk_entry_get_text ( GTK_ENTRY(commententry) ) ) != 0 )
416         vik_waypoint_set_comment ( wp, gtk_entry_get_text ( GTK_ENTRY(commententry) ) );
417       if ( (! wp->description) || strcmp ( wp->description, gtk_entry_get_text ( GTK_ENTRY(descriptionentry) ) ) != 0 )
418         vik_waypoint_set_description ( wp, gtk_entry_get_text ( GTK_ENTRY(descriptionentry) ) );
419       if ( (! wp->image) || strcmp ( wp->image, vik_file_entry_get_filename ( VIK_FILE_ENTRY ( imageentry ) ) ) != 0 )
420       {
421         vik_waypoint_set_image ( wp, vik_file_entry_get_filename ( VIK_FILE_ENTRY(imageentry) ) );
422         if ( wp->image && *(wp->image) && (!a_thumbnails_exists(wp->image)) )
423           a_thumbnails_create ( wp->image );
424       }
425
426       {
427         GtkTreeIter iter, first;
428         gtk_tree_model_get_iter_first ( GTK_TREE_MODEL(store), &first );
429         if ( !gtk_combo_box_get_active_iter ( GTK_COMBO_BOX(symbolentry), &iter ) || !memcmp(&iter, &first, sizeof(GtkTreeIter)) ) {
430           vik_waypoint_set_symbol ( wp, NULL );
431         } else {
432           gchar *sym;
433           gtk_tree_model_get ( GTK_TREE_MODEL(store), &iter, 0, (void *)&sym, -1 );
434           vik_waypoint_set_symbol ( wp, sym );
435           g_free(sym);
436         }
437       }         
438
439       gtk_widget_destroy ( dialog );
440       
441       *updated = TRUE;
442       return NULL;
443     }
444   }
445   gtk_widget_destroy ( dialog );
446   return NULL;
447 }
448
449 static void get_selected_foreach_func(GtkTreeModel *model,
450                                       GtkTreePath *path,
451                                       GtkTreeIter *iter,
452                                       gpointer data)
453 {
454   GList **list = data;
455   gchar *name;
456   gtk_tree_model_get (model, iter, 0, &name, -1);
457   *list = g_list_prepend(*list, name);
458 }
459
460 GList *a_dialog_select_from_list ( GtkWindow *parent, GList *names, gboolean multiple_selection_allowed, const gchar *title, const gchar *msg )
461 {
462   GtkTreeIter iter;
463   GtkCellRenderer *renderer;
464   GtkWidget *view;
465
466   GtkWidget *dialog = gtk_dialog_new_with_buttons (title,
467                                                   parent,
468                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
469                                                   GTK_STOCK_CANCEL,
470                                                   GTK_RESPONSE_REJECT,
471                                                   GTK_STOCK_OK,
472                                                   GTK_RESPONSE_ACCEPT,
473                                                   NULL);
474   /* When something is selected then OK */
475   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
476   GtkWidget *response_w = NULL;
477 #if GTK_CHECK_VERSION (2, 20, 0)
478   /* Default to not apply - as initially nothing is selected! */
479   response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_REJECT );
480 #endif
481   GtkListStore *store = gtk_list_store_new(1, G_TYPE_STRING);
482
483   GtkWidget *scrolledwindow;
484
485   GList *runner = names;
486   while (runner)
487   {
488     gtk_list_store_append(store, &iter);
489     gtk_list_store_set(store, &iter, 0, runner->data, -1);
490     runner = g_list_next(runner);
491   }
492
493   view = gtk_tree_view_new();
494   renderer = gtk_cell_renderer_text_new();
495   // Use the column header to display the message text,
496   // this makes the overall widget allocation simple as treeview takes up all the space
497   GtkTreeViewColumn *column;
498   column = gtk_tree_view_column_new_with_attributes (msg, renderer, "text", 0, NULL );
499   gtk_tree_view_column_set_sort_column_id (column, 0);
500   gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
501
502   gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
503   gtk_tree_selection_set_mode( gtk_tree_view_get_selection(GTK_TREE_VIEW(view)),
504       multiple_selection_allowed ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE );
505   g_object_unref(store);
506
507   scrolledwindow = gtk_scrolled_window_new ( NULL, NULL );
508   gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
509   gtk_container_add ( GTK_CONTAINER(scrolledwindow), view );
510
511   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), scrolledwindow, TRUE, TRUE, 0 );
512   // Ensure a reasonable number of items are shown, but let the width be automatically sized
513   gtk_widget_set_size_request ( dialog, -1, 400) ;
514
515   gtk_widget_show_all ( dialog );
516
517   if ( response_w )
518     gtk_widget_grab_focus ( response_w );
519
520   while ( gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT )
521   {
522     GList *names = NULL;
523     GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
524     gtk_tree_selection_selected_foreach(selection, get_selected_foreach_func, &names);
525     if (names)
526     {
527       gtk_widget_destroy ( dialog );
528       return (names);
529     }
530     a_dialog_error_msg(parent, _("Nothing was selected"));
531   }
532   gtk_widget_destroy ( dialog );
533   return NULL;
534 }
535
536 gchar *a_dialog_new_track ( GtkWindow *parent, GHashTable *tracks, gchar *default_name, gboolean is_route )
537 {
538   GtkWidget *dialog = gtk_dialog_new_with_buttons ( is_route ? _("Add Route") : _("Add Track"),
539                                                   parent,
540                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
541                                                   GTK_STOCK_CANCEL,
542                                                   GTK_RESPONSE_REJECT,
543                                                   GTK_STOCK_OK,
544                                                   GTK_RESPONSE_ACCEPT,
545                                                   NULL);
546   GtkWidget *label = gtk_label_new ( is_route ? _("Route Name:") : _("Track Name:") );
547   GtkWidget *entry = gtk_entry_new ();
548
549   if (default_name)
550     gtk_entry_set_text ( GTK_ENTRY(entry), default_name );
551
552   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), label, FALSE, FALSE, 0);
553   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), entry, FALSE, FALSE, 0);
554
555   g_signal_connect_swapped ( entry, "activate", G_CALLBACK(a_dialog_response_accept), GTK_DIALOG(dialog) );
556
557   gtk_widget_show ( label );
558   gtk_widget_show ( entry );
559
560   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
561
562   while ( gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT )
563   {
564     const gchar *constname = gtk_entry_get_text ( GTK_ENTRY(entry) );
565     if ( *constname == '\0' )
566       a_dialog_info_msg ( parent, _("Please enter a name for the track.") );
567     else {
568       gchar *name = g_strdup ( constname );
569       gtk_widget_destroy ( dialog );
570       return name;
571     }
572   }
573   gtk_widget_destroy ( dialog );
574   return NULL;
575 }
576
577 /* creates a vbox full of labels */
578 GtkWidget *a_dialog_create_label_vbox ( gchar **texts, int label_count )
579 {
580   GtkWidget *vbox, *label;
581   int i;
582   vbox = gtk_vbox_new( TRUE, 3 );
583
584   for ( i = 0; i < label_count; i++ )
585   {
586     label = gtk_label_new(NULL);
587     gtk_label_set_markup ( GTK_LABEL(label), _(texts[i]) );
588     gtk_box_pack_start ( GTK_BOX(vbox), label, FALSE, TRUE, 5 );
589   }
590   return vbox;
591 }
592
593 gboolean a_dialog_yes_or_no ( GtkWindow *parent, const gchar *message, const gchar *extra )
594 {
595   GtkWidget *dia;
596   dia = gtk_message_dialog_new ( parent,
597                                  GTK_DIALOG_DESTROY_WITH_PARENT,
598                                  GTK_MESSAGE_QUESTION,
599                                  GTK_BUTTONS_YES_NO,
600                                  message, extra );
601
602   if ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_YES )
603   {
604     gtk_widget_destroy ( dia );
605     return TRUE;
606   }
607   else
608   {
609     gtk_widget_destroy ( dia );
610     return FALSE;
611   }
612 }
613
614 static void zoom_spin_changed ( GtkSpinButton *spin, GtkWidget *pass_along[3] )
615 {
616   if ( gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(pass_along[2]) ) )
617     gtk_spin_button_set_value ( 
618         GTK_SPIN_BUTTON(pass_along[GTK_WIDGET(spin) == pass_along[0] ? 1 : 0]),
619         gtk_spin_button_get_value ( spin ) );
620 }
621
622 gboolean a_dialog_custom_zoom ( GtkWindow *parent, gdouble *xmpp, gdouble *ympp )
623 {
624   GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Zoom Factors..."),
625                                                   parent,
626                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
627                                                   GTK_STOCK_CANCEL,
628                                                   GTK_RESPONSE_REJECT,
629                                                   GTK_STOCK_OK,
630                                                   GTK_RESPONSE_ACCEPT,
631                                                   NULL);
632   GtkWidget *table, *label, *xlabel, *xspin, *ylabel, *yspin, *samecheck;
633   GtkWidget *pass_along[3];
634
635   table = gtk_table_new ( 4, 2, FALSE );
636   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), table, TRUE, TRUE, 0 );
637
638   label = gtk_label_new ( _("Zoom factor (in meters per pixel):") );
639   xlabel = gtk_label_new ( _("X (easting): "));
640   ylabel = gtk_label_new ( _("Y (northing): "));
641
642   pass_along[0] = xspin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new ( *xmpp, VIK_VIEWPORT_MIN_ZOOM, VIK_VIEWPORT_MAX_ZOOM, 1, 5, 0 ), 1, 8 );
643   pass_along[1] = yspin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new ( *ympp, VIK_VIEWPORT_MIN_ZOOM, VIK_VIEWPORT_MAX_ZOOM, 1, 5, 0 ), 1, 8 );
644
645   pass_along[2] = samecheck = gtk_check_button_new_with_label ( _("X and Y zoom factors must be equal") );
646   /* TODO -- same factor */
647   /*  samecheck = gtk_check_button_new_with_label ( "Same x/y zoom factor" ); */
648
649   if ( *xmpp == *ympp )
650     gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(samecheck), TRUE );
651
652   gtk_table_attach_defaults ( GTK_TABLE(table), label, 0, 2, 0, 1 );
653   gtk_table_attach_defaults ( GTK_TABLE(table), xlabel, 0, 1, 1, 2 );
654   gtk_table_attach_defaults ( GTK_TABLE(table), xspin, 1, 2, 1, 2 );
655   gtk_table_attach_defaults ( GTK_TABLE(table), ylabel, 0, 1, 2, 3 );
656   gtk_table_attach_defaults ( GTK_TABLE(table), yspin, 1, 2, 2, 3 );
657   gtk_table_attach_defaults ( GTK_TABLE(table), samecheck, 0, 2, 3, 4 );
658
659   gtk_widget_show_all ( table );
660
661   g_signal_connect ( G_OBJECT(xspin), "value-changed", G_CALLBACK(zoom_spin_changed), pass_along );
662   g_signal_connect ( G_OBJECT(yspin), "value-changed", G_CALLBACK(zoom_spin_changed), pass_along );
663
664   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
665
666   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
667   {
668     *xmpp = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(xspin) );
669     *ympp = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(yspin) );
670     gtk_widget_destroy ( dialog );
671     return TRUE;
672   }
673   gtk_widget_destroy ( dialog );
674   return FALSE;
675 }
676
677 static void split_spin_focused ( GtkSpinButton *spin, GtkWidget *pass_along[1] )
678 {
679   gtk_toggle_button_set_active    (GTK_TOGGLE_BUTTON(pass_along[0]), 1);
680 }
681
682 gboolean a_dialog_time_threshold ( GtkWindow *parent, gchar *title_text, gchar *label_text, guint *thr )
683 {
684   GtkWidget *dialog = gtk_dialog_new_with_buttons (title_text, 
685                                                   parent,
686                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
687                                                   GTK_STOCK_CANCEL,
688                                                   GTK_RESPONSE_REJECT,
689                                                   GTK_STOCK_OK,
690                                                   GTK_RESPONSE_ACCEPT,
691                                                   NULL);
692   GtkWidget *table, *t1, *t2, *t3, *t4, *spin, *label;
693   GtkWidget *pass_along[1];
694
695   table = gtk_table_new ( 4, 2, FALSE );
696   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), table, TRUE, TRUE, 0 );
697
698   label = gtk_label_new (label_text);
699
700   t1 = gtk_radio_button_new_with_label ( NULL, _("1 min") );
701   t2 = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(t1), _("1 hour") );
702   t3 = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(t2), _("1 day") );
703   t4 = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(t3), _("Custom (in minutes):") );
704
705   pass_along[0] = t4;
706
707   spin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new ( *thr, 0, 65536, 1, 5, 0 ), 1, 0 );
708
709   gtk_table_attach_defaults ( GTK_TABLE(table), label, 0, 2, 0, 1 );
710   gtk_table_attach_defaults ( GTK_TABLE(table), t1, 0, 1, 1, 2 );
711   gtk_table_attach_defaults ( GTK_TABLE(table), t2, 0, 1, 2, 3 );
712   gtk_table_attach_defaults ( GTK_TABLE(table), t3, 0, 1, 3, 4 );
713   gtk_table_attach_defaults ( GTK_TABLE(table), t4, 0, 1, 4, 5 );
714   gtk_table_attach_defaults ( GTK_TABLE(table), spin, 1, 2, 4, 5 );
715
716   gtk_widget_show_all ( table );
717
718   g_signal_connect ( G_OBJECT(spin), "grab-focus", G_CALLBACK(split_spin_focused), pass_along );
719
720   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
721
722   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
723   {
724     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(t1))) {
725       *thr = 1;
726     } else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(t2))) {
727       *thr = 60;
728     } else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(t3))) {
729       *thr = 60 * 24;
730     } else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(t4))) {
731       *thr = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(spin) );
732     }
733     gtk_widget_destroy ( dialog );
734     return TRUE;
735   }
736   gtk_widget_destroy ( dialog );
737   return FALSE;
738 }
739
740 /**
741  * Dialog to return a positive number via a spinbox within the supplied limits
742  * A return value of zero indicates the dialog was cancelled
743  */
744 guint a_dialog_get_positive_number ( GtkWindow *parent, gchar *title_text, gchar *label_text, guint default_num, guint min, guint max, guint step )
745 {
746   GtkWidget *dialog = gtk_dialog_new_with_buttons (title_text,
747                                                    parent,
748                                                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
749                                                    GTK_STOCK_CANCEL,
750                                                    GTK_RESPONSE_REJECT,
751                                                    GTK_STOCK_OK,
752                                                    GTK_RESPONSE_ACCEPT,
753                                                    NULL);
754   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
755   GtkWidget *response_w = NULL;
756 #if GTK_CHECK_VERSION (2, 20, 0)
757   response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
758 #endif
759
760   GtkWidget *table, *spin, *label;
761   guint result = default_num;
762
763   table = gtk_table_new ( 2, 1, FALSE );
764   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), table, TRUE, TRUE, 0 );
765
766   label = gtk_label_new (label_text);
767   spin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new ( default_num, min, max, step, 5, 0 ), 1, 0 );
768
769   gtk_table_attach_defaults ( GTK_TABLE(table), label, 0, 1, 0, 1 );
770   gtk_table_attach_defaults ( GTK_TABLE(table), spin, 0, 1, 1, 2 );
771
772   if ( response_w )
773     gtk_widget_grab_focus ( response_w );
774
775   gtk_widget_show_all ( table );
776
777   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
778   {
779     result = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(spin) );
780     gtk_widget_destroy ( dialog );
781     return result;
782   }
783
784   // Dialog cancelled
785   gtk_widget_destroy ( dialog );
786   return 0;
787 }
788
789 static void about_url_hook (GtkAboutDialog *about,
790                             const gchar    *link,
791                             gpointer        data)
792 {
793   open_url (GTK_WINDOW(about), link);
794 }
795
796 static void about_email_hook (GtkAboutDialog *about,
797                               const gchar    *email,
798                               gpointer        data)
799 {
800   new_email (GTK_WINDOW(about), email);
801 }
802
803 void a_dialog_about ( GtkWindow *parent )
804 {
805   const gchar *program_name = PACKAGE_NAME;
806   const gchar *version = VIKING_VERSION;
807   const gchar *website = VIKING_URL;
808   const gchar *copyright = "2003-2008, Evan Battaglia\n2008-2010, Viking's contributors";
809   const gchar *comments = _("GPS Data and Topo Analyzer, Explorer, and Manager.");
810   const gchar *license = _("This program is free software; you can redistribute it and/or modify "
811                         "it under the terms of the GNU General Public License as published by "
812                         "the Free Software Foundation; either version 2 of the License, or "
813                         "(at your option) any later version."
814                         "\n\n"
815                         "This program is distributed in the hope that it will be useful, "
816                         "but WITHOUT ANY WARRANTY; without even the implied warranty of "
817                         "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the "
818                         "GNU General Public License for more details."
819                         "\n\n"
820                         "You should have received a copy of the GNU General Public License "
821                         "along with this program; if not, write to the Free Software "
822                         "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA");
823
824   gtk_about_dialog_set_url_hook (about_url_hook, NULL, NULL);
825   gtk_about_dialog_set_email_hook (about_email_hook, NULL, NULL);
826   gtk_show_about_dialog (parent,
827         /* TODO do not set program-name and correctly set info for g_get_application_name */
828         "program-name", program_name,
829         "version", version,
830         "website", website,
831         "comments", comments,
832         "copyright", copyright,
833         "license", license,
834         "wrap-license", TRUE,
835         /* logo automatically retrieved via gtk_window_get_default_icon_list */
836         "authors", AUTHORS,
837         "documenters", DOCUMENTERS,
838         "translator-credits", _("Translation is coordinated on http://launchpad.net/viking"),
839         NULL);
840 }
841
842 gboolean a_dialog_map_n_zoom(GtkWindow *parent, gchar *mapnames[], gint default_map, gchar *zoom_list[], gint default_zoom, gint *selected_map, gint *selected_zoom)
843 {
844   gchar **s;
845
846   GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Download along track"), parent, 0, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL );
847   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
848   GtkWidget *response_w = NULL;
849 #if GTK_CHECK_VERSION (2, 20, 0)
850   response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
851 #endif
852   GtkWidget *map_label = gtk_label_new(_("Map type:"));
853   GtkComboBox *map_combo = GTK_COMBO_BOX(gtk_combo_box_new_text());
854   for (s = mapnames; *s; s++)
855     gtk_combo_box_append_text(map_combo, *s);
856   gtk_combo_box_set_active (map_combo, default_map);
857   GtkWidget *zoom_label = gtk_label_new(_("Zoom level:"));
858   GtkComboBox *zoom_combo = GTK_COMBO_BOX(gtk_combo_box_new_text());
859   for (s = zoom_list; *s; s++)
860     gtk_combo_box_append_text(zoom_combo, *s);
861   gtk_combo_box_set_active (zoom_combo, default_zoom);
862
863   GtkTable *box = GTK_TABLE(gtk_table_new(2, 2, FALSE));
864   gtk_table_attach_defaults(box, GTK_WIDGET(map_label), 0, 1, 0, 1);
865   gtk_table_attach_defaults(box, GTK_WIDGET(map_combo), 1, 2, 0, 1);
866   gtk_table_attach_defaults(box, GTK_WIDGET(zoom_label), 0, 1, 1, 2);
867   gtk_table_attach_defaults(box, GTK_WIDGET(zoom_combo), 1, 2, 1, 2);
868
869   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), GTK_WIDGET(box), FALSE, FALSE, 5 );
870
871   if ( response_w )
872     gtk_widget_grab_focus ( response_w );
873
874   gtk_widget_show_all ( dialog );
875   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
876     gtk_widget_destroy(dialog);
877     return FALSE;
878   }
879
880   *selected_map = gtk_combo_box_get_active(map_combo);
881   *selected_zoom = gtk_combo_box_get_active(zoom_combo);
882
883   gtk_widget_destroy(dialog);
884   return TRUE;
885 }
886
887 /**
888  * Display a dialog presenting the license of a map.
889  * Allow to read the license by launching a web browser.
890  */
891 void a_dialog_license ( GtkWindow *parent, const gchar *map, const gchar *license, const gchar *url)
892 {
893   GtkWidget *dialog = gtk_message_dialog_new (parent,
894                                  GTK_DIALOG_DESTROY_WITH_PARENT,
895                                  GTK_MESSAGE_INFO,
896                                  GTK_BUTTONS_OK,
897                                  _("The map data is licensed: %s."),
898                                  license);
899   gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (dialog),
900     _("The data provided by '<b>%s</b>' are licensed under the following license: <b>%s</b>.\n"
901     "Please, read the license before continuing."),
902     map, license);
903 #define RESPONSE_OPEN_LICENSE 600
904   if (url != NULL) {
905     gtk_dialog_add_button (GTK_DIALOG (dialog), _("Open license"), RESPONSE_OPEN_LICENSE);
906   }
907   gint response;
908   do {
909     response = gtk_dialog_run (GTK_DIALOG (dialog));
910     if (response == RESPONSE_OPEN_LICENSE) {
911       open_url (parent, url);
912     }
913   } while (response != GTK_RESPONSE_DELETE_EVENT && response != GTK_RESPONSE_OK);
914   gtk_widget_destroy (dialog);
915 }