]> git.street.me.uk Git - andy/viking.git/blob - src/dialog.c
Merge branch 'i18n-launchpad'
[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, GHashTable *waypoints, 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, *commentlabel, 
209     *commententry, *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   cmt = a_vik_goto_get_search_string_for_this_place(VIK_WINDOW(parent));
270   if (cmt)
271     gtk_entry_set_text(GTK_ENTRY(commententry), cmt);
272
273   imagelabel = gtk_label_new (_("Image:"));
274   imageentry = vik_file_entry_new (GTK_FILE_CHOOSER_ACTION_OPEN);
275
276   {
277     GtkCellRenderer *r;
278     symbollabel = gtk_label_new (_("Symbol:"));
279     GtkTreeIter iter;
280
281     store = gtk_list_store_new(3, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_STRING);
282     symbolentry = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
283     gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(symbolentry), 6);
284
285     g_signal_connect(symbolentry, "changed", G_CALLBACK(symbol_entry_changed_cb), store);
286     gtk_list_store_append (store, &iter);
287     gtk_list_store_set (store, &iter, 0, NULL, 1, NULL, 2, _("(none)"), -1);
288     a_populate_sym_list(store);
289
290     r = gtk_cell_renderer_pixbuf_new ();
291     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (symbolentry), r, FALSE);
292     gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (symbolentry), r, "pixbuf", 1, NULL);
293
294     r = gtk_cell_renderer_text_new ();
295     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (symbolentry), r, FALSE);
296     gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (symbolentry), r, "text", 2, NULL);
297
298     if ( !is_new && wp->symbol ) {
299       gboolean ok;
300       gchar *sym;
301       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)) {
302         gtk_tree_model_get ( GTK_TREE_MODEL(store), &iter, 0, (void *)&sym, -1 );
303         if (sym && !strcmp(sym, wp->symbol)) {
304           g_free(sym);
305           break;
306         } else {
307           g_free(sym);
308         }
309       }
310       // Ensure is it a valid symbol in the given symbol set (large vs small)
311       // Not all symbols are available in both
312       // The check prevents a Gtk Critical message
313       if ( iter.stamp )
314         gtk_combo_box_set_active_iter(GTK_COMBO_BOX(symbolentry), &iter);
315     }
316   }
317
318   if ( !is_new && wp->comment )
319     gtk_entry_set_text ( GTK_ENTRY(commententry), wp->comment );
320
321   if ( !is_new && wp->image )
322     vik_file_entry_set_filename ( VIK_FILE_ENTRY(imageentry), wp->image );
323
324
325   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), latlabel, FALSE, FALSE, 0);
326   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), latentry, FALSE, FALSE, 0);
327   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), lonlabel, FALSE, FALSE, 0);
328   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), lonentry, FALSE, FALSE, 0);
329   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), altlabel, FALSE, FALSE, 0);
330   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), altentry, FALSE, FALSE, 0);
331   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), commentlabel, FALSE, FALSE, 0);
332   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), commententry, FALSE, FALSE, 0);
333   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), imagelabel, FALSE, FALSE, 0);
334   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), imageentry, FALSE, FALSE, 0);
335   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), symbollabel, FALSE, FALSE, 0);
336   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), GTK_WIDGET(symbolentry), FALSE, FALSE, 0);
337
338   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
339
340   gtk_widget_show_all ( GTK_DIALOG(dialog)->vbox );
341
342   while ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
343   {
344     if ( is_new )
345     {
346       const gchar *entered_name = gtk_entry_get_text ( GTK_ENTRY(nameentry) );
347       if ( strlen(entered_name) == 0 ) /* TODO: other checks (isalpha or whatever ) */
348         a_dialog_info_msg ( parent, _("Please enter a name for the waypoint.") );
349       else {
350
351         gchar *use_name = g_strdup ( entered_name );
352
353         if ( g_hash_table_lookup ( waypoints, use_name ) && !a_dialog_yes_or_no ( parent, _("The waypoint \"%s\" exists, do you want to overwrite it?"), use_name ) )
354           g_free ( use_name );
355         else
356         {
357           /* Do It */
358           ll.lat = convert_dms_to_dec ( gtk_entry_get_text ( GTK_ENTRY(latentry) ) );
359           ll.lon = convert_dms_to_dec ( gtk_entry_get_text ( GTK_ENTRY(lonentry) ) );
360           vik_coord_load_from_latlon ( &(wp->coord), coord_mode, &ll );
361           // Always store in metres
362           switch (height_units) {
363           case VIK_UNITS_HEIGHT_METRES:
364             wp->altitude = atof ( gtk_entry_get_text ( GTK_ENTRY(altentry) ) );
365             break;
366           case VIK_UNITS_HEIGHT_FEET:
367             wp->altitude = VIK_FEET_TO_METERS(atof ( gtk_entry_get_text ( GTK_ENTRY(altentry) ) ));
368             break;
369           default:
370             wp->altitude = atof ( gtk_entry_get_text ( GTK_ENTRY(altentry) ) );
371             g_critical("Houston, we've had a problem. height=%d", height_units);
372           }
373           vik_waypoint_set_comment ( wp, gtk_entry_get_text ( GTK_ENTRY(commententry) ) );
374           vik_waypoint_set_image ( wp, vik_file_entry_get_filename ( VIK_FILE_ENTRY(imageentry) ) );
375           if ( wp->image && *(wp->image) && (!a_thumbnails_exists(wp->image)) )
376             a_thumbnails_create ( wp->image );
377
378           {
379             GtkTreeIter iter, first;
380             gtk_tree_model_get_iter_first ( GTK_TREE_MODEL(store), &first );
381             if ( !gtk_combo_box_get_active_iter ( GTK_COMBO_BOX(symbolentry), &iter ) || !memcmp(&iter, &first, sizeof(GtkTreeIter)) ) {
382               vik_waypoint_set_symbol ( wp, NULL );
383             } else {
384               gchar *sym;
385               gtk_tree_model_get ( GTK_TREE_MODEL(store), &iter, 0, (void *)&sym, -1 );
386               vik_waypoint_set_symbol ( wp, sym );
387               g_free(sym);
388             }
389           }             
390
391           gtk_widget_destroy ( dialog );
392           return use_name;
393         }
394       } /* else (valid name) */
395     }
396     else
397     {
398       ll.lat = convert_dms_to_dec ( gtk_entry_get_text ( GTK_ENTRY(latentry) ) );
399       ll.lon = convert_dms_to_dec ( gtk_entry_get_text ( GTK_ENTRY(lonentry) ) );
400       vik_coord_load_from_latlon ( &(wp->coord), coord_mode, &ll );
401       switch (height_units) {
402       case VIK_UNITS_HEIGHT_METRES:
403         wp->altitude = atof ( gtk_entry_get_text ( GTK_ENTRY(altentry) ) );
404         break;
405       case VIK_UNITS_HEIGHT_FEET:
406         wp->altitude = VIK_FEET_TO_METERS(atof ( gtk_entry_get_text ( GTK_ENTRY(altentry) ) ));
407         break;
408       default:
409         wp->altitude = atof ( gtk_entry_get_text ( GTK_ENTRY(altentry) ) );
410         g_critical("Houston, we've had a problem. height=%d", height_units);
411       }
412       if ( (! wp->comment) || strcmp ( wp->comment, gtk_entry_get_text ( GTK_ENTRY(commententry) ) ) != 0 )
413         vik_waypoint_set_comment ( wp, gtk_entry_get_text ( GTK_ENTRY(commententry) ) );
414       if ( (! wp->image) || strcmp ( wp->image, vik_file_entry_get_filename ( VIK_FILE_ENTRY ( imageentry ) ) ) != 0 )
415       {
416         vik_waypoint_set_image ( wp, vik_file_entry_get_filename ( VIK_FILE_ENTRY(imageentry) ) );
417         if ( wp->image && *(wp->image) && (!a_thumbnails_exists(wp->image)) )
418           a_thumbnails_create ( wp->image );
419       }
420
421       {
422         GtkTreeIter iter, first;
423         gtk_tree_model_get_iter_first ( GTK_TREE_MODEL(store), &first );
424         if ( !gtk_combo_box_get_active_iter ( GTK_COMBO_BOX(symbolentry), &iter ) || !memcmp(&iter, &first, sizeof(GtkTreeIter)) ) {
425           vik_waypoint_set_symbol ( wp, NULL );
426         } else {
427           gchar *sym;
428           gtk_tree_model_get ( GTK_TREE_MODEL(store), &iter, 0, (void *)&sym, -1 );
429           vik_waypoint_set_symbol ( wp, sym );
430           g_free(sym);
431         }
432       }         
433
434       gtk_widget_destroy ( dialog );
435       
436       *updated = TRUE;
437       return NULL;
438     }
439   }
440   gtk_widget_destroy ( dialog );
441   return NULL;
442 }
443
444 static void get_selected_foreach_func(GtkTreeModel *model,
445                                       GtkTreePath *path,
446                                       GtkTreeIter *iter,
447                                       gpointer data)
448 {
449   GList **list = data;
450   gchar *name;
451   gtk_tree_model_get (model, iter, 0, &name, -1);
452   *list = g_list_prepend(*list, name);
453 }
454
455 GList *a_dialog_select_from_list ( GtkWindow *parent, GList *names, gboolean multiple_selection_allowed, const gchar *title, const gchar *msg )
456 {
457   GtkTreeIter iter;
458   GtkCellRenderer *renderer;
459   GtkWidget *view;
460
461   GtkWidget *dialog = gtk_dialog_new_with_buttons (title,
462                                                   parent,
463                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
464                                                   GTK_STOCK_CANCEL,
465                                                   GTK_RESPONSE_REJECT,
466                                                   GTK_STOCK_OK,
467                                                   GTK_RESPONSE_ACCEPT,
468                                                   NULL);
469   /* When something is selected then OK */
470   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
471   GtkWidget *response_w = NULL;
472 #if GTK_CHECK_VERSION (2, 20, 0)
473   /* Default to not apply - as initially nothing is selected! */
474   response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_REJECT );
475 #endif
476   GtkListStore *store = gtk_list_store_new(1, G_TYPE_STRING);
477
478   GtkWidget *scrolledwindow;
479
480   GList *runner = names;
481   while (runner)
482   {
483     gtk_list_store_append(store, &iter);
484     gtk_list_store_set(store, &iter, 0, runner->data, -1);
485     runner = g_list_next(runner);
486   }
487
488   view = gtk_tree_view_new();
489   renderer = gtk_cell_renderer_text_new();
490   // Use the column header to display the message text,
491   // this makes the overall widget allocation simple as treeview takes up all the space
492   gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW(view), -1, msg, renderer, "text", 0, NULL);
493   gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
494   gtk_tree_view_set_headers_visible( GTK_TREE_VIEW(view), TRUE);
495   gtk_tree_selection_set_mode( gtk_tree_view_get_selection(GTK_TREE_VIEW(view)),
496       multiple_selection_allowed ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE );
497   g_object_unref(store);
498
499   scrolledwindow = gtk_scrolled_window_new ( NULL, NULL );
500   gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
501   gtk_container_add ( GTK_CONTAINER(scrolledwindow), view );
502
503   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), scrolledwindow, TRUE, TRUE, 0 );
504   // Ensure a reasonable number of items are shown, but let the width be automatically sized
505   gtk_widget_set_size_request ( dialog, -1, 400) ;
506
507   gtk_widget_show_all ( dialog );
508
509   if ( response_w )
510     gtk_widget_grab_focus ( response_w );
511
512   while ( gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT )
513   {
514     GList *names = NULL;
515     GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
516     gtk_tree_selection_selected_foreach(selection, get_selected_foreach_func, &names);
517     if (names)
518     {
519       gtk_widget_destroy ( dialog );
520       return (names);
521     }
522     a_dialog_error_msg(parent, _("Nothing was selected"));
523   }
524   gtk_widget_destroy ( dialog );
525   return NULL;
526 }
527
528 gchar *a_dialog_new_track ( GtkWindow *parent, GHashTable *tracks, gchar *default_name )
529 {
530   GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Add Track"),
531                                                   parent,
532                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
533                                                   GTK_STOCK_CANCEL,
534                                                   GTK_RESPONSE_REJECT,
535                                                   GTK_STOCK_OK,
536                                                   GTK_RESPONSE_ACCEPT,
537                                                   NULL);
538   GtkWidget *label = gtk_label_new ( _("Track Name:") );
539   GtkWidget *entry = gtk_entry_new ();
540
541   if (default_name)
542     gtk_entry_set_text ( GTK_ENTRY(entry), default_name );
543
544   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), label, FALSE, FALSE, 0);
545   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), entry, FALSE, FALSE, 0);
546
547   g_signal_connect_swapped ( entry, "activate", G_CALLBACK(a_dialog_response_accept), GTK_DIALOG(dialog) );
548
549   gtk_widget_show ( label );
550   gtk_widget_show ( entry );
551
552   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
553
554   while ( gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT )
555   {
556     const gchar *constname = gtk_entry_get_text ( GTK_ENTRY(entry) );
557     if ( *constname == '\0' )
558       a_dialog_info_msg ( parent, _("Please enter a name for the track.") );
559     else {
560       gchar *name = g_strdup ( constname );
561
562       if ( g_hash_table_lookup( tracks, name ) && !a_dialog_yes_or_no ( parent, _("The track \"%s\" exists, do you want to overwrite it?"), gtk_entry_get_text ( GTK_ENTRY(entry) ) ) )
563       {
564         g_free ( name );
565       }
566       else
567       {
568         gtk_widget_destroy ( dialog );
569         return name;
570       }
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 }