1 /*
2   Gpredict: Real-time satellite tracking and orbit prediction program
3 
4   Copyright (C)  2001-2017  Alexandru Csete, OZ9AEC
5                       2011  Charles Suprin, AA1VS
6 
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11 
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16 
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, visit http://www.fsf.org/
19 */
20 
21 /*
22  * Edit ground station details.
23  *
24  * The functions in this unit are used to edit the details of a given
25  * ground station. The editor consists of a simple dialog window containing
26  * widget for the individual setting.
27  *
28  * The functions can be used for editing the details of an existing ground
29  * station or for adding a new location. In the first case, the widgets
30  * in dialog will be preloaded with the existing data, while in the second
31  * case the widgets will be empty and, upon successful completion a new
32  * entry will be added to the GtkTreeView which contains the locations.
33  */
34 
35 #ifdef HAVE_CONFIG_H
36 #include <build-config.h>
37 #endif
38 #include <glib/gi18n.h>
39 #include <glib/gstdio.h>
40 #include <gtk/gtk.h>
41 #include <math.h>
42 
43 #include "gpredict-utils.h"
44 #include "loc-tree.h"
45 #include "locator.h"
46 #include "sat-cfg.h"
47 #include "sat-log.h"
48 #include "sat-pref-qth-data.h"
49 #include "sat-pref-qth-editor.h"
50 
51 /*
52  * Symbolic refs to be used when calling select_location in order
53  * to determine which mode the selection should run in, ie.
54  * select location or select weather station.
55  */
56 enum {
57     SELECTION_MODE_LOC = 1,
58     SELECTION_MODE_WX = 2
59 };
60 
61 extern GtkWidget *window;       /* dialog window defined in sat-pref.c */
62 
63 static GtkWidget *dialog;       /* dialog window */
64 static GtkWidget *name;         /* QTH name */
65 static GtkWidget *location;     /* QTH location */
66 static GtkWidget *desc;         /* QTH description */
67 static GtkWidget *lat, *lon, *alt;      /* LAT, LON and ALT */
68 static GtkWidget *ns, *ew;
69 static GtkWidget *qra;          /* QRA locator */
70 static gulong   latsigid, lonsigid, nssigid, ewsigid, qrasigid;
71 static GtkWidget *wx;           /* weather station */
72 
73 #ifdef HAS_LIBGPS
74 static GtkWidget *type;         /* GPSD type */
75 static GtkWidget *server;       /* GPSD Server */
76 static GtkWidget *port;         /* GPSD Port */
77 #endif
78 
79 
80 /* Update widgets from the currently selected row in the treeview */
update_widgets(GtkTreeView * treeview)81 static void update_widgets(GtkTreeView * treeview)
82 {
83     GtkTreeSelection *selection;        /* the selection in the tree view */
84     GtkTreeModel   *model;      /* the tree model corresponding to the selection */
85     GtkTreeIter     iter;       /* the iter of the selection */
86     gchar          *qthname;    /* location name */
87     gchar          *qthdesc;    /* location description */
88     gchar          *qthloc;     /* location */
89     gdouble         qthlat;     /* latitude */
90     gdouble         qthlon;     /* longitude */
91     guint           qthalt;     /* altitude */
92     gchar          *qthwx;      /* weather station */
93     gchar          *qthgpsdserver;      /* gpsdserver */
94     guint           qthtype;    /* type */
95     guint           qthgpsdport;        /* gpsdport */
96 
97     selection = gtk_tree_view_get_selection(treeview);
98     if (gtk_tree_selection_get_selected(selection, &model, &iter))
99     {
100         /* get values */
101         gtk_tree_model_get(model, &iter,
102                            QTH_LIST_COL_NAME, &qthname,
103                            QTH_LIST_COL_LOC, &qthloc,
104                            QTH_LIST_COL_DESC, &qthdesc,
105                            QTH_LIST_COL_LAT, &qthlat,
106                            QTH_LIST_COL_LON, &qthlon,
107                            QTH_LIST_COL_ALT, &qthalt,
108                            QTH_LIST_COL_WX, &qthwx,
109                            QTH_LIST_COL_GPSD_SERVER, &qthgpsdserver,
110                            QTH_LIST_COL_GPSD_PORT, &qthgpsdport,
111                            QTH_LIST_COL_TYPE, &qthtype, -1);
112 
113         /* update widgets and free memory afterwards */
114         if (qthname)
115         {
116             gtk_entry_set_text(GTK_ENTRY(name), qthname);
117         }
118 
119         if (qthloc)
120         {
121             gtk_entry_set_text(GTK_ENTRY(location), qthloc);
122             g_free(qthloc);
123         }
124 
125         if (qthdesc)
126         {
127             gtk_entry_set_text(GTK_ENTRY(desc), qthdesc);
128             g_free(qthdesc);
129         }
130 
131         if (qthwx)
132         {
133             gtk_entry_set_text(GTK_ENTRY(wx), qthwx);
134             g_free(qthwx);
135         }
136 #ifdef HAS_LIBGPS
137         if (qthgpsdserver)
138         {
139             gtk_entry_set_text(GTK_ENTRY(server), qthgpsdserver);
140             g_free(qthgpsdserver);
141         }
142 #endif
143         if (qthlat < 0.00)
144             gtk_combo_box_set_active(GTK_COMBO_BOX(ns), 1);
145         else
146             gtk_combo_box_set_active(GTK_COMBO_BOX(ns), 0);
147 
148         gtk_spin_button_set_value(GTK_SPIN_BUTTON(lat), fabs(qthlat));
149 
150         if (qthlon < 0.00)
151             gtk_combo_box_set_active(GTK_COMBO_BOX(ew), 1);
152         else
153             gtk_combo_box_set_active(GTK_COMBO_BOX(ew), 0);
154 
155         gtk_spin_button_set_value(GTK_SPIN_BUTTON(lon), fabs(qthlon));
156 
157         gtk_spin_button_set_value(GTK_SPIN_BUTTON(alt), qthalt);
158 #ifdef HAS_LIBGPS
159         gtk_spin_button_set_value(GTK_SPIN_BUTTON(port), qthgpsdport);
160         gtk_combo_box_set_active(GTK_COMBO_BOX(type), qthtype);
161 #endif
162 
163         sat_log_log(SAT_LOG_LEVEL_DEBUG,
164                     _("%s:%d: Loaded %s for editing:\n"
165                       "LAT:%.4f LON:%.4f ALT:%d"),
166                     __FILE__, __LINE__,
167                     (qthname != NULL) ? qthname : "???",
168                     qthlat, qthlon, qthalt);
169 
170         g_free(qthname);
171 
172     }
173     else
174     {
175         sat_log_log(SAT_LOG_LEVEL_ERROR,
176                     _("%s:%d: No ground station selected!"),
177                     __FILE__, __LINE__);
178     }
179 }
180 
181 /**
182  * Clear the contents of all widgets.
183  *
184  * This function is usually called when the user clicks on the CLEAR button
185  *
186  */
clear_widgets()187 static void clear_widgets()
188 {
189     gtk_entry_set_text(GTK_ENTRY(name), "");
190     gtk_entry_set_text(GTK_ENTRY(location), "");
191     gtk_entry_set_text(GTK_ENTRY(desc), "");
192     gtk_entry_set_text(GTK_ENTRY(wx), "");
193     gtk_spin_button_set_value(GTK_SPIN_BUTTON(lat), 0.0);
194     gtk_spin_button_set_value(GTK_SPIN_BUTTON(lon), 0.0);
195     gtk_spin_button_set_value(GTK_SPIN_BUTTON(alt), 0);
196     gtk_entry_set_text(GTK_ENTRY(qra), "");
197 }
198 
199 /**
200  * Apply changes.
201  * @return TRUE if things are ok, FALSE otherwise.
202  *
203  * This function is usually called when the user clicks the OK button.
204  */
apply_changes(GtkTreeView * treeview,gboolean new)205 static gboolean apply_changes(GtkTreeView * treeview, gboolean new)
206 {
207     GtkTreeSelection *selection;        /* selection in the treeview */
208     GtkTreeModel   *model;      /* the tree model corresponding to the selection */
209     GtkListStore   *liststore;  /* the list store corresponding to the model */
210     GtkTreeIter     iter;       /* iter used to add and modify row data */
211     const gchar    *qthname;
212     const gchar    *qthloc;
213     const gchar    *qthdesc;
214     const gchar    *qthwx;
215     const gchar    *qthgpsdserver;
216     gdouble         qthlat;
217     gdouble         qthlon;
218     guint           qthalt;
219     guint           qthtype;
220     guint           qthgpsdport;
221     const gchar    *qthqra;
222 
223     /* get values from dialog box */
224     qthname = gtk_entry_get_text(GTK_ENTRY(name));
225     qthloc = gtk_entry_get_text(GTK_ENTRY(location));
226     qthdesc = gtk_entry_get_text(GTK_ENTRY(desc));
227     qthwx = gtk_entry_get_text(GTK_ENTRY(wx));
228 
229 #ifdef HAS_LIBGPS
230     qthgpsdserver = gtk_entry_get_text(GTK_ENTRY(server));
231 #else
232     qthgpsdserver = "";
233 #endif
234 
235     qthlat = gtk_spin_button_get_value(GTK_SPIN_BUTTON(lat));
236     if (gtk_combo_box_get_active(GTK_COMBO_BOX(ns)))
237         qthlat = -qthlat;
238 
239     qthlon = gtk_spin_button_get_value(GTK_SPIN_BUTTON(lon));
240     if (gtk_combo_box_get_active(GTK_COMBO_BOX(ew)))
241         qthlon = -qthlon;
242 
243     qthalt = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(alt));
244 
245 #ifdef HAS_LIBGPS
246     qthgpsdport = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(port));
247     qthtype = gtk_combo_box_get_active(GTK_COMBO_BOX(type));
248 #else
249     qthgpsdport = 0;
250     qthtype = 0;                // FIXME: should probably use a #define
251 #endif
252 
253     /* get liststore */
254     liststore = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
255 
256     /* if this is a new entry, insert row into model */
257     if (new == TRUE)
258     {
259         gtk_list_store_append(liststore, &iter);
260     }
261     /* otherwise get current selection */
262     else
263     {
264         selection = gtk_tree_view_get_selection(treeview);
265 
266         if (gtk_tree_selection_get_selected(selection, &model, &iter))
267         {
268             liststore = GTK_LIST_STORE(model);
269         }
270         else
271         {
272             /* no selection; internal error */
273             sat_log_log(SAT_LOG_LEVEL_ERROR,
274                         _("%s:%d: Oooops, gpredict encountered an internal "
275                           "error (no selection in qth list)"), __FILE__,
276                         __LINE__);
277 
278             return FALSE;
279         }
280     }
281 
282     /* update values */
283     gtk_list_store_set(liststore, &iter,
284                        QTH_LIST_COL_NAME, qthname,
285                        QTH_LIST_COL_LOC, qthloc,
286                        QTH_LIST_COL_DESC, qthdesc,
287                        QTH_LIST_COL_LAT, qthlat,
288                        QTH_LIST_COL_LON, qthlon,
289                        QTH_LIST_COL_ALT, qthalt,
290                        QTH_LIST_COL_WX, qthwx,
291                        QTH_LIST_COL_GPSD_SERVER, qthgpsdserver,
292                        QTH_LIST_COL_GPSD_PORT, qthgpsdport,
293                        QTH_LIST_COL_TYPE, qthtype, -1);
294 
295     qthqra = gtk_entry_get_text(GTK_ENTRY(qra));
296     gtk_list_store_set(liststore, &iter, QTH_LIST_COL_QRA, qthqra, -1);
297 
298     return TRUE;
299 }
300 
301 /**
302  * Manage name changes.
303  *
304  * This function is called when the contents of the name entry changes.
305  * The primary purpose of this function is to check whether the char length
306  * of the name is greater than zero, if yes enable the OK button of the dialog.
307  */
name_changed(GtkWidget * widget,gpointer data)308 static void name_changed(GtkWidget * widget, gpointer data)
309 {
310     const gchar    *text;
311     gchar          *entry, *end, *j;
312     gint            len, pos;
313 
314     (void)data;                 /* avoid unused parameter compiler warning */
315 
316     /* step 1: ensure that only valid characters are entered
317        (stolen from xlog, tnx pg4i)
318      */
319     entry = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
320     if ((len = g_utf8_strlen(entry, -1)) > 0)
321     {
322         end = entry + g_utf8_strlen(entry, -1);
323         for (j = entry; j < end; ++j)
324         {
325             if (!gpredict_legal_char(*j))
326             {
327                 gdk_beep();
328                 pos = gtk_editable_get_position(GTK_EDITABLE(widget));
329                 gtk_editable_delete_text(GTK_EDITABLE(widget), pos, pos + 1);
330             }
331         }
332     }
333 
334     /* step 2: if name seems all right, enable OK button */
335     text = gtk_entry_get_text(GTK_ENTRY(widget));
336 
337     if (g_utf8_strlen(text, -1) > 0)
338     {
339         gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
340                                           GTK_RESPONSE_OK, TRUE);
341     }
342     else
343     {
344         gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
345                                           GTK_RESPONSE_OK, FALSE);
346     }
347 }
348 
349 /**
350  * Manage SELECT button clicks.
351  *
352  * This function is called when the user clicks on one of the SELECT buttons.
353  * the data parameter contains information about which button has been clicked.
354  */
select_location(GtkWidget * widget,gpointer data)355 static void select_location(GtkWidget * widget, gpointer data)
356 {
357     guint           mode = GPOINTER_TO_UINT(data);
358     guint           flags;
359     gchar          *qthloc;
360     gchar          *qthwx;
361     gfloat          qthlat;
362     gfloat          qthlon;
363     guint           qthalt;
364     gboolean        selected = FALSE;
365 
366     (void)widget;               /* avoid unused parameter compiler warning */
367 
368     switch (mode)
369     {
370         /* We distinguish only between WX mode and "everything else".
371            Although a value != 1 or 2 is definitely a bug, we need to
372            have some sensible fall-back.
373          */
374     case SELECTION_MODE_WX:
375         flags = TREE_COL_FLAG_NAME | TREE_COL_FLAG_WX;
376         break;
377 
378     default:
379         flags = TREE_COL_FLAG_NAME |
380             TREE_COL_FLAG_LAT |
381             TREE_COL_FLAG_LON | TREE_COL_FLAG_ALT | TREE_COL_FLAG_WX;
382 
383         mode = SELECTION_MODE_LOC;
384         break;
385     }
386 
387     selected =
388         loc_tree_create(NULL, flags, &qthloc, &qthlat, &qthlon, &qthalt,
389                         &qthwx);
390 
391     if (selected)
392     {
393         /* update widgets */
394         switch (mode)
395         {
396 
397         case SELECTION_MODE_WX:
398             gtk_entry_set_text(GTK_ENTRY(wx), qthwx);
399             break;
400 
401         case SELECTION_MODE_LOC:
402             gtk_entry_set_text(GTK_ENTRY(location), qthloc);
403             gtk_entry_set_text(GTK_ENTRY(wx), qthwx);
404 
405             gtk_spin_button_set_value(GTK_SPIN_BUTTON(lat),
406                                       (gdouble) fabs(qthlat));
407             if (qthlat < 0.00)
408                 gtk_combo_box_set_active(GTK_COMBO_BOX(ns), 1);
409             else
410                 gtk_combo_box_set_active(GTK_COMBO_BOX(ns), 0);
411 
412             gtk_spin_button_set_value(GTK_SPIN_BUTTON(lon),
413                                       (gdouble) fabs(qthlon));
414             if (qthlon < 0.00)
415                 gtk_combo_box_set_active(GTK_COMBO_BOX(ew), 1);
416             else
417                 gtk_combo_box_set_active(GTK_COMBO_BOX(ew), 0);
418 
419             gtk_spin_button_set_value(GTK_SPIN_BUTTON(alt), qthalt);
420 
421             break;
422 
423         default:
424                /*** FIXME: add some error reporting */
425             break;
426         }
427 
428         /* free some memory */
429         g_free(qthloc);
430         g_free(qthwx);
431     }
432 
433     /* else do nothing; we are finished */
434 }
435 
436 /**
437  * Manage coordinate changes.
438  *
439  * This function is called when the qth coordinates change. The change can
440  * be either one of the spin buttons or the combo boxes. It reads the
441  * coordinates and the calculates the new Maidenhead locator square.
442  */
latlon_changed(GtkWidget * widget,gpointer data)443 static void latlon_changed(GtkWidget * widget, gpointer data)
444 {
445     gchar          *locator;
446     gint            retcode;
447     gdouble         latf, lonf;
448 
449     (void)widget;               /* avoid unused parameter compiler warning */
450     (void)data;                 /* avoid unused parameter compiler warning */
451 
452     locator = g_try_malloc(7);
453 
454     /* no need to check locator != NULL, since hamlib func will do it for us
455        and return RIGEINVAL
456      */
457     lonf = gtk_spin_button_get_value(GTK_SPIN_BUTTON(lon));
458     latf = gtk_spin_button_get_value(GTK_SPIN_BUTTON(lat));
459 
460     /* set the correct sign */
461     if (gtk_combo_box_get_active(GTK_COMBO_BOX(ns)))
462     {
463         /* index 1 => South */
464         latf = -latf;
465     }
466 
467     if (gtk_combo_box_get_active(GTK_COMBO_BOX(ew)))
468     {
469         /* index 1 => Wesr */
470         lonf = -lonf;
471     }
472 
473     retcode = longlat2locator(lonf, latf, locator, 3);
474 
475     if (retcode == RIG_OK)
476     {
477         /* debug message */
478         sat_log_log(SAT_LOG_LEVEL_DEBUG,
479                     _("%s:%s: %.2f %.2f => %s"),
480                     __FILE__, __func__,
481                     gtk_spin_button_get_value(GTK_SPIN_BUTTON(lon)),
482                     gtk_spin_button_get_value(GTK_SPIN_BUTTON(lat)), locator);
483 
484         g_signal_handler_block(qra, qrasigid);
485 
486         gtk_entry_set_text(GTK_ENTRY(qra), locator);
487 
488         g_signal_handler_unblock(qra, qrasigid);
489     }
490     else
491     {
492         /* send an error message and don't update */
493         sat_log_log(SAT_LOG_LEVEL_ERROR,
494                     _("%s:%d: Error converting lon/lat to locator"),
495                     __FILE__, __LINE__);
496     }
497 
498 
499     if (locator)
500         g_free(locator);
501 }
502 
503 /**
504  * Manage locator changes.
505  *
506  * This function is called when the Maidenhead locator is changed.
507  * It will calculate the new coordinates and update the spin butrtons and
508  * the combo boxes.
509  */
qra_changed(GtkEntry * entry,gpointer data)510 static void qra_changed(GtkEntry * entry, gpointer data)
511 {
512     gint            retcode;
513     gdouble         latf, lonf;
514     gchar          *msg;
515 
516     (void)entry;                /* avoid unused parameter compiler warning */
517     (void)data;                 /* avoid unused parameter compiler warning */
518 
519     retcode =
520         locator2longlat(&lonf, &latf, gtk_entry_get_text(GTK_ENTRY(qra)));
521 
522     if (retcode == RIG_OK)
523     {
524 
525         /* debug message */
526         sat_log_log(SAT_LOG_LEVEL_DEBUG,
527                     _("%s:%s: %s => %.2f %.2f"),
528                     __FILE__, __func__,
529                     gtk_entry_get_text(GTK_ENTRY(qra)), lonf, latf);
530 
531         /* block signal emissions for lat/lon widgets */
532         g_signal_handler_block(lat, latsigid);
533         g_signal_handler_block(lon, lonsigid);
534         g_signal_handler_block(ns, nssigid);
535         g_signal_handler_block(ew, ewsigid);
536         g_signal_handler_block(qra, qrasigid);
537 
538         /* update widgets */
539         if (latf < 0.00)
540             gtk_combo_box_set_active(GTK_COMBO_BOX(ns), 1);
541         else
542             gtk_combo_box_set_active(GTK_COMBO_BOX(ns), 0);
543 
544         if (lonf < 0.00)
545             gtk_combo_box_set_active(GTK_COMBO_BOX(ew), 1);
546         else
547             gtk_combo_box_set_active(GTK_COMBO_BOX(ew), 0);
548 
549         gtk_spin_button_set_value(GTK_SPIN_BUTTON(lat), fabs(latf));
550         gtk_spin_button_set_value(GTK_SPIN_BUTTON(lon), fabs(lonf));
551 
552         /* make sure text is upper case */
553         msg = g_ascii_strup(gtk_entry_get_text(GTK_ENTRY(qra)), -1);
554         gtk_entry_set_text(GTK_ENTRY(qra), msg);
555         g_free(msg);
556 
557         /* unblock signal emissions */
558         g_signal_handler_unblock(lat, latsigid);
559         g_signal_handler_unblock(lon, lonsigid);
560         g_signal_handler_unblock(ns, nssigid);
561         g_signal_handler_unblock(ew, ewsigid);
562         g_signal_handler_unblock(qra, qrasigid);
563 
564     }
565     else
566     {
567         /* send an error message and don't update */
568         sat_log_log(SAT_LOG_LEVEL_ERROR,
569                     _("%s:%d: Invalid locator: %s"),
570                     __FILE__, __LINE__, gtk_entry_get_text(GTK_ENTRY(qra)));
571     }
572 
573 }
574 
create_editor_widgets(GtkTreeView * treeview,gboolean new)575 static GtkWidget *create_editor_widgets(GtkTreeView * treeview, gboolean new)
576 {
577     GtkWidget      *table;
578     GtkWidget      *label;
579     GtkWidget      *locbut;
580     GtkWidget      *wxbut;
581 
582     table = gtk_grid_new();
583     gtk_grid_set_column_homogeneous(GTK_GRID(table), FALSE);
584     gtk_grid_set_row_homogeneous(GTK_GRID(table), FALSE);
585     gtk_grid_set_column_spacing(GTK_GRID(table), 5);
586     gtk_grid_set_row_spacing(GTK_GRID(table), 5);
587     gtk_container_set_border_width(GTK_CONTAINER(table), 5);
588 
589     /* QTH name */
590     label = gtk_label_new(_("Name"));
591     g_object_set(label, "xalign", 0.0f, "yalign", 0.5f, NULL);
592     gtk_grid_attach(GTK_GRID(table), label, 0, 0, 1, 1);
593 
594     name = gtk_entry_new();
595     gtk_entry_set_max_length(GTK_ENTRY(name), 25);
596     gtk_widget_set_tooltip_text(name,
597                                 _
598                                 ("Enter a short name for this ground station, "
599                                  "e.g. callsign.\n"
600                                  "Allowed characters: 0..9, a..z, A..Z, - and _"));
601     gtk_grid_attach(GTK_GRID(table), name, 1, 0, 3, 1);
602 
603     /* attach changed signal so that we can enable OK button when
604        a proper name has been entered
605      */
606     g_signal_connect(name, "changed", G_CALLBACK(name_changed), NULL);
607 
608     /* QTH description */
609     label = gtk_label_new(_("Description"));
610     g_object_set(label, "xalign", 0.0f, "yalign", 0.5f, NULL);
611     gtk_grid_attach(GTK_GRID(table), label, 0, 1, 1, 1);
612 
613     desc = gtk_entry_new();
614     gtk_entry_set_max_length(GTK_ENTRY(desc), 256);
615     gtk_widget_set_tooltip_text(desc,
616                                 _("Enter an optional description for this"
617                                   " ground station."));
618     gtk_grid_attach(GTK_GRID(table), desc, 1, 1, 3, 1);
619 
620     /* location */
621     label = gtk_label_new(_("Location"));
622     g_object_set(label, "xalign", 0.0f, "yalign", 0.5f, NULL);
623     gtk_grid_attach(GTK_GRID(table), label, 0, 2, 1, 1);
624 
625     location = gtk_entry_new();
626     gtk_entry_set_max_length(GTK_ENTRY(location), 50);
627     gtk_widget_set_tooltip_text(location,
628                                 _("Optional location of the ground station, "
629                                   "e.g. Copenhagen, Denmark."));
630     gtk_grid_attach(GTK_GRID(table), location, 1, 2, 2, 1);
631 
632     locbut = gtk_button_new_with_label(_("Select"));
633     gtk_widget_set_tooltip_text(locbut, _("Select a location from a list"));
634     g_signal_connect(locbut, "clicked", G_CALLBACK(select_location),
635                      GUINT_TO_POINTER(SELECTION_MODE_LOC));
636     gtk_grid_attach(GTK_GRID(table), locbut, 3, 2, 1, 1);
637 
638 
639     /* latitude */
640     label = gtk_label_new(_("Latitude (\302\260)"));
641     g_object_set(label, "xalign", 0.0f, "yalign", 0.5f, NULL);
642     gtk_grid_attach(GTK_GRID(table), label, 0, 3, 1, 1);
643 
644     lat = gtk_spin_button_new_with_range(0.00, 90.00, 0.0001);
645     gtk_spin_button_set_increments(GTK_SPIN_BUTTON(lat), 0.0001, 1.0);
646     gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(lat), TRUE);
647     gtk_spin_button_set_digits(GTK_SPIN_BUTTON(lat), 4);
648     gtk_widget_set_tooltip_text(lat,
649                                 _("Select the latitude of the ground station "
650                                   "in decimal degrees."));
651     gtk_grid_attach(GTK_GRID(table), lat, 1, 3, 1, 1);
652 
653     ns = gtk_combo_box_text_new();
654     gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(ns), _("North"));
655     gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(ns), _("South"));
656     gtk_combo_box_set_active(GTK_COMBO_BOX(ns), 0);
657     gtk_grid_attach(GTK_GRID(table), ns, 2, 3, 1, 1);
658 
659     /* longitude */
660     label = gtk_label_new(_("Longitude (\302\260)"));
661     g_object_set(label, "xalign", 0.0f, "yalign", 0.5f, NULL);
662     gtk_grid_attach(GTK_GRID(table), label, 0, 4, 1, 1);
663 
664     lon = gtk_spin_button_new_with_range(0.00, 180.00, 0.0001);
665     gtk_spin_button_set_increments(GTK_SPIN_BUTTON(lon), 0.0001, 1.0);
666     gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(lon), TRUE);
667     gtk_spin_button_set_digits(GTK_SPIN_BUTTON(lon), 4);
668     gtk_widget_set_tooltip_text(lon,
669                                 _("Select the longitude of the ground station "
670                                   "in decimal degrees."));
671     gtk_grid_attach(GTK_GRID(table), lon, 1, 4, 1, 1);
672 
673     ew = gtk_combo_box_text_new();
674     gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(ew), _("East"));
675     gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(ew), _("West"));
676     gtk_combo_box_set_active(GTK_COMBO_BOX(ew), 0);
677     gtk_grid_attach(GTK_GRID(table), ew, 2, 4, 1, 1);
678 
679     /* connect lat/lon spinners and combos to callback
680        remember signal id so that we can block signals
681        while doing automatic cross-updates
682      */
683     latsigid = g_signal_connect(lat, "value-changed",
684                                 G_CALLBACK(latlon_changed), NULL);
685     lonsigid = g_signal_connect(lon, "value-changed",
686                                 G_CALLBACK(latlon_changed), NULL);
687     nssigid = g_signal_connect(ns, "changed",
688                                G_CALLBACK(latlon_changed), NULL);
689     ewsigid = g_signal_connect(ew, "changed",
690                                G_CALLBACK(latlon_changed), NULL);
691 
692     /* QRA locator */
693     label = gtk_label_new(_("Locator"));
694     g_object_set(label, "xalign", 0.0f, "yalign", 0.5f, NULL);
695     gtk_grid_attach(GTK_GRID(table), label, 0, 5, 1, 1);
696 
697     qra = gtk_entry_new();
698     gtk_entry_set_max_length(GTK_ENTRY(qra), 6);
699     gtk_widget_set_tooltip_text(qra, _("Maidenhead locator grid."));
700     qrasigid = g_signal_connect(qra, "changed", G_CALLBACK(qra_changed), NULL);
701     gtk_grid_attach(GTK_GRID(table), qra, 1, 5, 1, 1);
702 
703     /* altitude */
704     label = gtk_label_new(_("Altitude"));
705     g_object_set(label, "xalign", 0.0f, "yalign", 0.5f, NULL);
706     gtk_grid_attach(GTK_GRID(table), label, 0, 6, 1, 1);
707 
708     alt = gtk_spin_button_new_with_range(0, 5000, 1);
709     gtk_spin_button_set_increments(GTK_SPIN_BUTTON(alt), 1, 100);
710     gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(alt), TRUE);
711     gtk_widget_set_tooltip_text(alt,
712                                 _("Select the altitude of the ground station "
713                                   "in meters or feet "
714                                   "depending on your settings"));
715     gtk_grid_attach(GTK_GRID(table), alt, 1, 6, 1, 1);
716 
717     if (sat_cfg_get_bool(SAT_CFG_BOOL_USE_IMPERIAL))
718         label = gtk_label_new(_("ft ASL"));
719     else
720         label = gtk_label_new(_("m ASL"));
721 
722     g_object_set(label, "xalign", 0.0f, "yalign", 0.5f, NULL);
723     gtk_grid_attach(GTK_GRID(table), label, 2, 6, 1, 1);
724 
725     /* weather station */
726     label = gtk_label_new(_("Weather St"));
727     g_object_set(label, "xalign", 0.0f, "yalign", 0.5f, NULL);
728     gtk_grid_attach(GTK_GRID(table), label, 0, 7, 1, 1);
729 
730     wx = gtk_entry_new();
731     gtk_entry_set_max_length(GTK_ENTRY(wx), 4);
732     gtk_widget_set_tooltip_text(wx, _("Four letter code for weather station"));
733     gtk_grid_attach(GTK_GRID(table), wx, 1, 7, 2, 1);
734 
735     wxbut = gtk_button_new_with_label(_("Select"));
736     gtk_widget_set_tooltip_text(wxbut, _("Select a weather station"));
737     g_signal_connect(wxbut, "clicked", G_CALLBACK(select_location),
738                      GUINT_TO_POINTER(SELECTION_MODE_WX));
739     gtk_grid_attach(GTK_GRID(table), wxbut, 3, 7, 1, 1);
740 
741 #ifdef HAS_LIBGPS
742     /* GPSD enabled */
743     label = gtk_label_new(_("QTH Type"));
744     g_object_set(label, "xalign", 0.0f, "yalign", 0.5f, NULL);
745     gtk_grid_attach(GTK_GRID(table), label, 0, 8, 1, 1);
746 
747     type = gtk_combo_box_text_new();
748     gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type), _("Static"));
749     gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type), _("GPSD"));
750     gtk_combo_box_set_active(GTK_COMBO_BOX(type), 0);
751     gtk_widget_set_tooltip_text(type,
752                                 _("A qth can be static, ie. it does not "
753                                   "change, or gpsd based for computers with "
754                                   "gps attached."));
755     gtk_grid_attach(GTK_GRID(table), type, 1, 8, 1, 1);
756 
757     /* GPSD Server */
758     label = gtk_label_new(_("GPSD Server"));
759     g_object_set(label, "xalign", 0.0f, "yalign", 0.5f, NULL);
760     gtk_grid_attach(GTK_GRID(table), label, 0, 9, 1, 1);
761 
762     server = gtk_entry_new();
763     gtk_entry_set_max_length(GTK_ENTRY(server), 6000);
764     gtk_widget_set_tooltip_text(server, _("GPSD Server."));
765     gtk_grid_attach(GTK_GRID(table), server, 1, 9, 1, 1);
766 
767     /* GPSD Port */
768     label = gtk_label_new(_("GPSD Port"));
769     g_object_set(label, "xalign", 0.0f, "yalign", 0.5f, NULL);
770     gtk_grid_attach(GTK_GRID(table), label, 0, 10, 1, 1);
771 
772     port = gtk_spin_button_new_with_range(0, 32768, 1);
773     gtk_spin_button_set_increments(GTK_SPIN_BUTTON(port), 1, 100);
774     gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(port), TRUE);
775     gtk_widget_set_tooltip_text(port,
776                                 _("Set the port for GPSD to use. Default for "
777                                   "gpsd is 2947."));
778     gtk_grid_attach(GTK_GRID(table), port, 1, 10, 1, 1);
779 #endif
780 
781     if (!new)
782         update_widgets(treeview);
783 
784     gtk_widget_show_all(table);
785 
786     return table;
787 }
788 
789 /*
790  * Add or edit a QTH entry.
791  *
792  * The parameter new is used to indicate whether a new entry should be
793  * created or just edit the one selected in the treeview.
794  */
sat_pref_qth_editor_run(GtkTreeView * treeview,gboolean new)795 void sat_pref_qth_editor_run(GtkTreeView * treeview, gboolean new)
796 {
797     gint            response;
798     gboolean        finished = FALSE;
799 
800     /* crate dialog and add contents */
801     dialog = gtk_dialog_new_with_buttons(_("Edit ground station data"),
802                                          GTK_WINDOW(window),
803                                          GTK_DIALOG_MODAL |
804                                          GTK_DIALOG_DESTROY_WITH_PARENT,
805                                          "_Clear", GTK_RESPONSE_REJECT,
806                                          "_Cancel", GTK_RESPONSE_CANCEL,
807                                          "_OK", GTK_RESPONSE_OK, NULL);
808 
809     /* disable OK button to begin with */
810     gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
811                                       GTK_RESPONSE_OK, FALSE);
812 
813     gtk_container_add(GTK_CONTAINER
814                       (gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
815                       create_editor_widgets(treeview, new));
816 
817     /* this hacky-thing is to keep the dialog running in case the
818        CLEAR button is plressed. OK and CANCEL will exit the loop
819      */
820     while (!finished)
821     {
822         response = gtk_dialog_run(GTK_DIALOG(dialog));
823 
824         switch (response)
825         {
826         case GTK_RESPONSE_OK:
827             /* OK */
828             if (apply_changes(treeview, new))
829                 finished = TRUE;
830             else
831                 finished = FALSE;
832 
833             break;
834 
835         case GTK_RESPONSE_REJECT:
836             /* CLEAR */
837             clear_widgets();
838             break;
839 
840         default:
841             /* Everything else is considered CANCEL */
842             finished = TRUE;
843             break;
844         }
845     }
846 
847     gtk_widget_destroy(dialog);
848 }
849