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