]> git.street.me.uk Git - andy/viking.git/blob - src/dialog.c
Prevent the program grinding to a halt if trying to deal with thousands of tiles
[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       if ( strlen((gchar*)gtk_entry_get_text ( GTK_ENTRY(nameentry) )) == 0 ) /* TODO: other checks (isalpha or whatever ) */
356         a_dialog_info_msg ( parent, _("Please enter a name for the waypoint.") );
357       else {
358         // NB: No check for unique names - this allows generation of same named entries.
359         gchar *entered_name = g_strdup ( (gchar*)gtk_entry_get_text ( GTK_ENTRY(nameentry) ) );
360
361         /* Do It */
362         ll.lat = convert_dms_to_dec ( gtk_entry_get_text ( GTK_ENTRY(latentry) ) );
363         ll.lon = convert_dms_to_dec ( gtk_entry_get_text ( GTK_ENTRY(lonentry) ) );
364         vik_coord_load_from_latlon ( &(wp->coord), coord_mode, &ll );
365         // Always store in metres
366         switch (height_units) {
367         case VIK_UNITS_HEIGHT_METRES:
368           wp->altitude = atof ( gtk_entry_get_text ( GTK_ENTRY(altentry) ) );
369           break;
370         case VIK_UNITS_HEIGHT_FEET:
371           wp->altitude = VIK_FEET_TO_METERS(atof ( gtk_entry_get_text ( GTK_ENTRY(altentry) ) ));
372           break;
373         default:
374           wp->altitude = atof ( gtk_entry_get_text ( GTK_ENTRY(altentry) ) );
375           g_critical("Houston, we've had a problem. height=%d", height_units);
376         }
377         vik_waypoint_set_comment ( wp, gtk_entry_get_text ( GTK_ENTRY(commententry) ) );
378         vik_waypoint_set_description ( wp, gtk_entry_get_text ( GTK_ENTRY(descriptionentry) ) );
379         vik_waypoint_set_image ( wp, vik_file_entry_get_filename ( VIK_FILE_ENTRY(imageentry) ) );
380         if ( wp->image && *(wp->image) && (!a_thumbnails_exists(wp->image)) )
381           a_thumbnails_create ( wp->image );
382
383         GtkTreeIter iter, first;
384         gtk_tree_model_get_iter_first ( GTK_TREE_MODEL(store), &first );
385         if ( !gtk_combo_box_get_active_iter ( GTK_COMBO_BOX(symbolentry), &iter ) || !memcmp(&iter, &first, sizeof(GtkTreeIter)) ) {
386           vik_waypoint_set_symbol ( wp, NULL );
387         } else {
388           gchar *sym;
389           gtk_tree_model_get ( GTK_TREE_MODEL(store), &iter, 0, (void *)&sym, -1 );
390           vik_waypoint_set_symbol ( wp, sym );
391           g_free(sym);
392         }
393
394         gtk_widget_destroy ( dialog );
395         return entered_name;
396       }
397     }
398     else
399     {
400       ll.lat = convert_dms_to_dec ( gtk_entry_get_text ( GTK_ENTRY(latentry) ) );
401       ll.lon = convert_dms_to_dec ( gtk_entry_get_text ( GTK_ENTRY(lonentry) ) );
402       vik_coord_load_from_latlon ( &(wp->coord), coord_mode, &ll );
403       switch (height_units) {
404       case VIK_UNITS_HEIGHT_METRES:
405         wp->altitude = atof ( gtk_entry_get_text ( GTK_ENTRY(altentry) ) );
406         break;
407       case VIK_UNITS_HEIGHT_FEET:
408         wp->altitude = VIK_FEET_TO_METERS(atof ( gtk_entry_get_text ( GTK_ENTRY(altentry) ) ));
409         break;
410       default:
411         wp->altitude = atof ( gtk_entry_get_text ( GTK_ENTRY(altentry) ) );
412         g_critical("Houston, we've had a problem. height=%d", height_units);
413       }
414       if ( (! wp->comment) || strcmp ( wp->comment, gtk_entry_get_text ( GTK_ENTRY(commententry) ) ) != 0 )
415         vik_waypoint_set_comment ( wp, gtk_entry_get_text ( GTK_ENTRY(commententry) ) );
416       if ( (! wp->description) || strcmp ( wp->description, gtk_entry_get_text ( GTK_ENTRY(descriptionentry) ) ) != 0 )
417         vik_waypoint_set_description ( wp, gtk_entry_get_text ( GTK_ENTRY(descriptionentry) ) );
418       if ( (! wp->image) || strcmp ( wp->image, vik_file_entry_get_filename ( VIK_FILE_ENTRY ( imageentry ) ) ) != 0 )
419       {
420         vik_waypoint_set_image ( wp, vik_file_entry_get_filename ( VIK_FILE_ENTRY(imageentry) ) );
421         if ( wp->image && *(wp->image) && (!a_thumbnails_exists(wp->image)) )
422           a_thumbnails_create ( wp->image );
423       }
424
425       {
426         GtkTreeIter iter, first;
427         gtk_tree_model_get_iter_first ( GTK_TREE_MODEL(store), &first );
428         if ( !gtk_combo_box_get_active_iter ( GTK_COMBO_BOX(symbolentry), &iter ) || !memcmp(&iter, &first, sizeof(GtkTreeIter)) ) {
429           vik_waypoint_set_symbol ( wp, NULL );
430         } else {
431           gchar *sym;
432           gtk_tree_model_get ( GTK_TREE_MODEL(store), &iter, 0, (void *)&sym, -1 );
433           vik_waypoint_set_symbol ( wp, sym );
434           g_free(sym);
435         }
436       }         
437
438       gtk_widget_destroy ( dialog );
439       
440       *updated = TRUE;
441       return NULL;
442     }
443   }
444   gtk_widget_destroy ( dialog );
445   return NULL;
446 }
447
448 static void get_selected_foreach_func(GtkTreeModel *model,
449                                       GtkTreePath *path,
450                                       GtkTreeIter *iter,
451                                       gpointer data)
452 {
453   GList **list = data;
454   gchar *name;
455   gtk_tree_model_get (model, iter, 0, &name, -1);
456   *list = g_list_prepend(*list, name);
457 }
458
459 GList *a_dialog_select_from_list ( GtkWindow *parent, GList *names, gboolean multiple_selection_allowed, const gchar *title, const gchar *msg )
460 {
461   GtkTreeIter iter;
462   GtkCellRenderer *renderer;
463   GtkWidget *view;
464
465   GtkWidget *dialog = gtk_dialog_new_with_buttons (title,
466                                                   parent,
467                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
468                                                   GTK_STOCK_CANCEL,
469                                                   GTK_RESPONSE_REJECT,
470                                                   GTK_STOCK_OK,
471                                                   GTK_RESPONSE_ACCEPT,
472                                                   NULL);
473   /* When something is selected then OK */
474   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
475   GtkWidget *response_w = NULL;
476 #if GTK_CHECK_VERSION (2, 20, 0)
477   /* Default to not apply - as initially nothing is selected! */
478   response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_REJECT );
479 #endif
480   GtkListStore *store = gtk_list_store_new(1, G_TYPE_STRING);
481
482   GtkWidget *scrolledwindow;
483
484   GList *runner = names;
485   while (runner)
486   {
487     gtk_list_store_append(store, &iter);
488     gtk_list_store_set(store, &iter, 0, runner->data, -1);
489     runner = g_list_next(runner);
490   }
491
492   view = gtk_tree_view_new();
493   renderer = gtk_cell_renderer_text_new();
494   // Use the column header to display the message text,
495   // this makes the overall widget allocation simple as treeview takes up all the space
496   GtkTreeViewColumn *column;
497   column = gtk_tree_view_column_new_with_attributes (msg, renderer, "text", 0, NULL );
498   gtk_tree_view_column_set_sort_column_id (column, 0);
499   gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
500
501   gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
502   gtk_tree_selection_set_mode( gtk_tree_view_get_selection(GTK_TREE_VIEW(view)),
503       multiple_selection_allowed ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE );
504   g_object_unref(store);
505
506   scrolledwindow = gtk_scrolled_window_new ( NULL, NULL );
507   gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
508   gtk_container_add ( GTK_CONTAINER(scrolledwindow), view );
509
510   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), scrolledwindow, TRUE, TRUE, 0 );
511   // Ensure a reasonable number of items are shown, but let the width be automatically sized
512   gtk_widget_set_size_request ( dialog, -1, 400) ;
513
514   gtk_widget_show_all ( dialog );
515
516   if ( response_w )
517     gtk_widget_grab_focus ( response_w );
518
519   while ( gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT )
520   {
521     GList *names = NULL;
522     GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
523     gtk_tree_selection_selected_foreach(selection, get_selected_foreach_func, &names);
524     if (names)
525     {
526       gtk_widget_destroy ( dialog );
527       return (names);
528     }
529     a_dialog_error_msg(parent, _("Nothing was selected"));
530   }
531   gtk_widget_destroy ( dialog );
532   return NULL;
533 }
534
535 gchar *a_dialog_new_track ( GtkWindow *parent, GHashTable *tracks, gchar *default_name, gboolean is_route )
536 {
537   GtkWidget *dialog = gtk_dialog_new_with_buttons ( is_route ? _("Add Route") : _("Add Track"),
538                                                   parent,
539                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
540                                                   GTK_STOCK_CANCEL,
541                                                   GTK_RESPONSE_REJECT,
542                                                   GTK_STOCK_OK,
543                                                   GTK_RESPONSE_ACCEPT,
544                                                   NULL);
545   GtkWidget *label = gtk_label_new ( is_route ? _("Route Name:") : _("Track Name:") );
546   GtkWidget *entry = gtk_entry_new ();
547
548   if (default_name)
549     gtk_entry_set_text ( GTK_ENTRY(entry), default_name );
550
551   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), label, FALSE, FALSE, 0);
552   gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), entry, FALSE, FALSE, 0);
553
554   g_signal_connect_swapped ( entry, "activate", G_CALLBACK(a_dialog_response_accept), GTK_DIALOG(dialog) );
555
556   gtk_widget_show ( label );
557   gtk_widget_show ( entry );
558
559   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
560
561   while ( gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT )
562   {
563     const gchar *constname = gtk_entry_get_text ( GTK_ENTRY(entry) );
564     if ( *constname == '\0' )
565       a_dialog_info_msg ( parent, _("Please enter a name for the track.") );
566     else {
567       gchar *name = g_strdup ( constname );
568       gtk_widget_destroy ( dialog );
569       return name;
570     }
571   }
572   gtk_widget_destroy ( dialog );
573   return NULL;
574 }
575
576 /* creates a vbox full of labels */
577 GtkWidget *a_dialog_create_label_vbox ( gchar **texts, int label_count )
578 {
579   GtkWidget *vbox, *label;
580   int i;
581   vbox = gtk_vbox_new( TRUE, 3 );
582
583   for ( i = 0; i < label_count; i++ )
584   {
585     label = gtk_label_new(NULL);
586     gtk_label_set_markup ( GTK_LABEL(label), _(texts[i]) );
587     gtk_box_pack_start ( GTK_BOX(vbox), label, FALSE, TRUE, 5 );
588   }
589   return vbox;
590 }
591
592 gboolean a_dialog_yes_or_no ( GtkWindow *parent, const gchar *message, const gchar *extra )
593 {
594   GtkWidget *dia;
595   dia = gtk_message_dialog_new ( parent,
596                                  GTK_DIALOG_DESTROY_WITH_PARENT,
597                                  GTK_MESSAGE_QUESTION,
598                                  GTK_BUTTONS_YES_NO,
599                                  message, extra );
600
601   if ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_YES )
602   {
603     gtk_widget_destroy ( dia );
604     return TRUE;
605   }
606   else
607   {
608     gtk_widget_destroy ( dia );
609     return FALSE;
610   }
611 }
612
613 static void zoom_spin_changed ( GtkSpinButton *spin, GtkWidget *pass_along[3] )
614 {
615   if ( gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(pass_along[2]) ) )
616     gtk_spin_button_set_value ( 
617         GTK_SPIN_BUTTON(pass_along[GTK_WIDGET(spin) == pass_along[0] ? 1 : 0]),
618         gtk_spin_button_get_value ( spin ) );
619 }
620
621 gboolean a_dialog_custom_zoom ( GtkWindow *parent, gdouble *xmpp, gdouble *ympp )
622 {
623   GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Zoom Factors..."),
624                                                   parent,
625                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
626                                                   GTK_STOCK_CANCEL,
627                                                   GTK_RESPONSE_REJECT,
628                                                   GTK_STOCK_OK,
629                                                   GTK_RESPONSE_ACCEPT,
630                                                   NULL);
631   GtkWidget *table, *label, *xlabel, *xspin, *ylabel, *yspin, *samecheck;
632   GtkWidget *pass_along[3];
633
634   table = gtk_table_new ( 4, 2, FALSE );
635   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), table, TRUE, TRUE, 0 );
636
637   label = gtk_label_new ( _("Zoom factor (in meters per pixel):") );
638   xlabel = gtk_label_new ( _("X (easting): "));
639   ylabel = gtk_label_new ( _("Y (northing): "));
640
641   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 );
642   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 );
643
644   pass_along[2] = samecheck = gtk_check_button_new_with_label ( _("X and Y zoom factors must be equal") );
645   /* TODO -- same factor */
646   /*  samecheck = gtk_check_button_new_with_label ( "Same x/y zoom factor" ); */
647
648   if ( *xmpp == *ympp )
649     gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(samecheck), TRUE );
650
651   gtk_table_attach_defaults ( GTK_TABLE(table), label, 0, 2, 0, 1 );
652   gtk_table_attach_defaults ( GTK_TABLE(table), xlabel, 0, 1, 1, 2 );
653   gtk_table_attach_defaults ( GTK_TABLE(table), xspin, 1, 2, 1, 2 );
654   gtk_table_attach_defaults ( GTK_TABLE(table), ylabel, 0, 1, 2, 3 );
655   gtk_table_attach_defaults ( GTK_TABLE(table), yspin, 1, 2, 2, 3 );
656   gtk_table_attach_defaults ( GTK_TABLE(table), samecheck, 0, 2, 3, 4 );
657
658   gtk_widget_show_all ( table );
659
660   g_signal_connect ( G_OBJECT(xspin), "value-changed", G_CALLBACK(zoom_spin_changed), pass_along );
661   g_signal_connect ( G_OBJECT(yspin), "value-changed", G_CALLBACK(zoom_spin_changed), pass_along );
662
663   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
664
665   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
666   {
667     *xmpp = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(xspin) );
668     *ympp = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(yspin) );
669     gtk_widget_destroy ( dialog );
670     return TRUE;
671   }
672   gtk_widget_destroy ( dialog );
673   return FALSE;
674 }
675
676 static void split_spin_focused ( GtkSpinButton *spin, GtkWidget *pass_along[1] )
677 {
678   gtk_toggle_button_set_active    (GTK_TOGGLE_BUTTON(pass_along[0]), 1);
679 }
680
681 gboolean a_dialog_time_threshold ( GtkWindow *parent, gchar *title_text, gchar *label_text, guint *thr )
682 {
683   GtkWidget *dialog = gtk_dialog_new_with_buttons (title_text, 
684                                                   parent,
685                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
686                                                   GTK_STOCK_CANCEL,
687                                                   GTK_RESPONSE_REJECT,
688                                                   GTK_STOCK_OK,
689                                                   GTK_RESPONSE_ACCEPT,
690                                                   NULL);
691   GtkWidget *table, *t1, *t2, *t3, *t4, *spin, *label;
692   GtkWidget *pass_along[1];
693
694   table = gtk_table_new ( 4, 2, FALSE );
695   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), table, TRUE, TRUE, 0 );
696
697   label = gtk_label_new (label_text);
698
699   t1 = gtk_radio_button_new_with_label ( NULL, _("1 min") );
700   t2 = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(t1), _("1 hour") );
701   t3 = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(t2), _("1 day") );
702   t4 = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(t3), _("Custom (in minutes):") );
703
704   pass_along[0] = t4;
705
706   spin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new ( *thr, 0, 65536, 1, 5, 0 ), 1, 0 );
707
708   gtk_table_attach_defaults ( GTK_TABLE(table), label, 0, 2, 0, 1 );
709   gtk_table_attach_defaults ( GTK_TABLE(table), t1, 0, 1, 1, 2 );
710   gtk_table_attach_defaults ( GTK_TABLE(table), t2, 0, 1, 2, 3 );
711   gtk_table_attach_defaults ( GTK_TABLE(table), t3, 0, 1, 3, 4 );
712   gtk_table_attach_defaults ( GTK_TABLE(table), t4, 0, 1, 4, 5 );
713   gtk_table_attach_defaults ( GTK_TABLE(table), spin, 1, 2, 4, 5 );
714
715   gtk_widget_show_all ( table );
716
717   g_signal_connect ( G_OBJECT(spin), "grab-focus", G_CALLBACK(split_spin_focused), pass_along );
718
719   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
720
721   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
722   {
723     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(t1))) {
724       *thr = 1;
725     } else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(t2))) {
726       *thr = 60;
727     } else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(t3))) {
728       *thr = 60 * 24;
729     } else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(t4))) {
730       *thr = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(spin) );
731     }
732     gtk_widget_destroy ( dialog );
733     return TRUE;
734   }
735   gtk_widget_destroy ( dialog );
736   return FALSE;
737 }
738
739 /**
740  * Dialog to return a positive number via a spinbox within the supplied limits
741  * A return value of zero indicates the dialog was cancelled
742  */
743 guint a_dialog_get_positive_number ( GtkWindow *parent, gchar *title_text, gchar *label_text, guint default_num, guint min, guint max, guint step )
744 {
745   GtkWidget *dialog = gtk_dialog_new_with_buttons (title_text,
746                                                    parent,
747                                                    GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
748                                                    GTK_STOCK_CANCEL,
749                                                    GTK_RESPONSE_REJECT,
750                                                    GTK_STOCK_OK,
751                                                    GTK_RESPONSE_ACCEPT,
752                                                    NULL);
753   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
754   GtkWidget *response_w = NULL;
755 #if GTK_CHECK_VERSION (2, 20, 0)
756   response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
757 #endif
758
759   GtkWidget *table, *spin, *label;
760   guint result = default_num;
761
762   table = gtk_table_new ( 2, 1, FALSE );
763   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), table, TRUE, TRUE, 0 );
764
765   label = gtk_label_new (label_text);
766   spin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new ( default_num, min, max, step, 5, 0 ), 1, 0 );
767
768   gtk_table_attach_defaults ( GTK_TABLE(table), label, 0, 1, 0, 1 );
769   gtk_table_attach_defaults ( GTK_TABLE(table), spin, 0, 1, 1, 2 );
770
771   if ( response_w )
772     gtk_widget_grab_focus ( response_w );
773
774   gtk_widget_show_all ( table );
775
776   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
777   {
778     result = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(spin) );
779     gtk_widget_destroy ( dialog );
780     return result;
781   }
782
783   // Dialog cancelled
784   gtk_widget_destroy ( dialog );
785   return 0;
786 }
787
788 static void about_url_hook (GtkAboutDialog *about,
789                             const gchar    *link,
790                             gpointer        data)
791 {
792   open_url (GTK_WINDOW(about), link);
793 }
794
795 static void about_email_hook (GtkAboutDialog *about,
796                               const gchar    *email,
797                               gpointer        data)
798 {
799   new_email (GTK_WINDOW(about), email);
800 }
801
802 void a_dialog_about ( GtkWindow *parent )
803 {
804   const gchar *program_name = PACKAGE_NAME;
805   const gchar *version = VIKING_VERSION;
806   const gchar *website = VIKING_URL;
807   const gchar *copyright = "2003-2008, Evan Battaglia\n2008-2010, Viking's contributors";
808   const gchar *comments = _("GPS Data and Topo Analyzer, Explorer, and Manager.");
809   const gchar *license = _("This program is free software; you can redistribute it and/or modify "
810                         "it under the terms of the GNU General Public License as published by "
811                         "the Free Software Foundation; either version 2 of the License, or "
812                         "(at your option) any later version."
813                         "\n\n"
814                         "This program is distributed in the hope that it will be useful, "
815                         "but WITHOUT ANY WARRANTY; without even the implied warranty of "
816                         "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the "
817                         "GNU General Public License for more details."
818                         "\n\n"
819                         "You should have received a copy of the GNU General Public License "
820                         "along with this program; if not, write to the Free Software "
821                         "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA");
822
823   gtk_about_dialog_set_url_hook (about_url_hook, NULL, NULL);
824   gtk_about_dialog_set_email_hook (about_email_hook, NULL, NULL);
825   gtk_show_about_dialog (parent,
826         /* TODO do not set program-name and correctly set info for g_get_application_name */
827         "program-name", program_name,
828         "version", version,
829         "website", website,
830         "comments", comments,
831         "copyright", copyright,
832         "license", license,
833         "wrap-license", TRUE,
834         /* logo automatically retrieved via gtk_window_get_default_icon_list */
835         "authors", AUTHORS,
836         "documenters", DOCUMENTERS,
837         "translator-credits", _("Translation is coordinated on http://launchpad.net/viking"),
838         NULL);
839 }
840
841 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)
842 {
843   gchar **s;
844
845   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 );
846   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
847   GtkWidget *response_w = NULL;
848 #if GTK_CHECK_VERSION (2, 20, 0)
849   response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
850 #endif
851   GtkWidget *map_label = gtk_label_new(_("Map type:"));
852   GtkComboBox *map_combo = GTK_COMBO_BOX(gtk_combo_box_new_text());
853   for (s = mapnames; *s; s++)
854     gtk_combo_box_append_text(map_combo, *s);
855   gtk_combo_box_set_active (map_combo, default_map);
856   GtkWidget *zoom_label = gtk_label_new(_("Zoom level:"));
857   GtkComboBox *zoom_combo = GTK_COMBO_BOX(gtk_combo_box_new_text());
858   for (s = zoom_list; *s; s++)
859     gtk_combo_box_append_text(zoom_combo, *s);
860   gtk_combo_box_set_active (zoom_combo, default_zoom);
861
862   GtkTable *box = GTK_TABLE(gtk_table_new(2, 2, FALSE));
863   gtk_table_attach_defaults(box, GTK_WIDGET(map_label), 0, 1, 0, 1);
864   gtk_table_attach_defaults(box, GTK_WIDGET(map_combo), 1, 2, 0, 1);
865   gtk_table_attach_defaults(box, GTK_WIDGET(zoom_label), 0, 1, 1, 2);
866   gtk_table_attach_defaults(box, GTK_WIDGET(zoom_combo), 1, 2, 1, 2);
867
868   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), GTK_WIDGET(box), FALSE, FALSE, 5 );
869
870   if ( response_w )
871     gtk_widget_grab_focus ( response_w );
872
873   gtk_widget_show_all ( dialog );
874   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
875     gtk_widget_destroy(dialog);
876     return FALSE;
877   }
878
879   *selected_map = gtk_combo_box_get_active(map_combo);
880   *selected_zoom = gtk_combo_box_get_active(zoom_combo);
881
882   gtk_widget_destroy(dialog);
883   return TRUE;
884 }
885
886 /**
887  * Display a dialog presenting the license of a map.
888  * Allow to read the license by launching a web browser.
889  */
890 void a_dialog_license ( GtkWindow *parent, const gchar *map, const gchar *license, const gchar *url)
891 {
892   GtkWidget *dialog = gtk_message_dialog_new (parent,
893                                  GTK_DIALOG_DESTROY_WITH_PARENT,
894                                  GTK_MESSAGE_INFO,
895                                  GTK_BUTTONS_OK,
896                                  _("The map data is licensed: %s."),
897                                  license);
898   gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (dialog),
899     _("The data provided by '<b>%s</b>' are licensed under the following license: <b>%s</b>.\n"
900     "Please, read the license before continuing."),
901     map, license);
902 #define RESPONSE_OPEN_LICENSE 600
903   if (url != NULL) {
904     gtk_dialog_add_button (GTK_DIALOG (dialog), _("Open license"), RESPONSE_OPEN_LICENSE);
905   }
906   gint response;
907   do {
908     response = gtk_dialog_run (GTK_DIALOG (dialog));
909     if (response == RESPONSE_OPEN_LICENSE) {
910       open_url (parent, url);
911     }
912   } while (response != GTK_RESPONSE_DELETE_EVENT && response != GTK_RESPONSE_OK);
913   gtk_widget_destroy (dialog);
914 }