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