sat-pref-rot-editor.c
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 20 #ifdef HAVE_CONFIG_H 21 #include <build-config.h> 22 #endif 23 #include <glib/gi18n.h> 24 #include <glib/gstdio.h> 25 #include <gtk/gtk.h> 26 #include <math.h> 27 28 #include "gpredict-utils.h" 29 #include "rotor-conf.h" 30 #include "sat-cfg.h" 31 #include "sat-log.h" 32 #include "sat-pref-rot-editor.h" 33 34 35 extern GtkWidget *window; /* dialog window defined in sat-pref.c */ 36 static GtkWidget *dialog; /* dialog window */ 37 static GtkWidget *name; /* Configuration name */ 38 static GtkWidget *host; /* host name or IP */ 39 static GtkWidget *port; /* port number */ 40 static GtkWidget *aztype; 41 static GtkWidget *minaz; 42 static GtkWidget *maxaz; 43 static GtkWidget *minel; 44 static GtkWidget *maxel; 45 static GtkWidget *azstoppos; 46 47 /* Update widgets from the currently selected row in the treeview */ 48 static void update_widgets(rotor_conf_t * conf) 49 { 50 /* configuration name */ 51 gtk_entry_set_text(GTK_ENTRY(name), conf->name); 52 53 /* host */ 54 if (conf->host) 55 gtk_entry_set_text(GTK_ENTRY(host), conf->host); 56 57 /* port */ 58 if (conf->port > 1023) 59 gtk_spin_button_set_value(GTK_SPIN_BUTTON(port), conf->port); 60 else 61 gtk_spin_button_set_value(GTK_SPIN_BUTTON(port), 4533); /* hamlib default? */ 62 63 gtk_combo_box_set_active(GTK_COMBO_BOX(aztype), conf->aztype); 64 65 /* az and el limits */ 66 gtk_spin_button_set_value(GTK_SPIN_BUTTON(minaz), conf->minaz); 67 gtk_spin_button_set_value(GTK_SPIN_BUTTON(maxaz), conf->maxaz); 68 gtk_spin_button_set_value(GTK_SPIN_BUTTON(minel), conf->minel); 69 gtk_spin_button_set_value(GTK_SPIN_BUTTON(maxel), conf->maxel); 70 gtk_spin_button_set_value(GTK_SPIN_BUTTON(azstoppos), conf->azstoppos); 71 } 72 73 /* called when the user clicks on the CLEAR button */ 74 static void clear_widgets() 75 { 76 gtk_entry_set_text(GTK_ENTRY(name), ""); 77 gtk_entry_set_text(GTK_ENTRY(host), "localhost"); 78 gtk_spin_button_set_value(GTK_SPIN_BUTTON(port), 4533); /* hamlib default? */ 79 gtk_combo_box_set_active(GTK_COMBO_BOX(aztype), ROT_AZ_TYPE_360); 80 gtk_spin_button_set_value(GTK_SPIN_BUTTON(minaz), 0); 81 gtk_spin_button_set_value(GTK_SPIN_BUTTON(maxaz), 360); 82 gtk_spin_button_set_value(GTK_SPIN_BUTTON(minel), 0); 83 gtk_spin_button_set_value(GTK_SPIN_BUTTON(maxel), 90); 84 gtk_spin_button_set_value(GTK_SPIN_BUTTON(azstoppos), 0); 85 } 86 87 /* 88 * This function is called when the contents of the name entry changes. 89 * The primary purpose of this function is to check whether the char length 90 * of the name is greater than zero, if yes enable the OK button of the dialog. 91 */ 92 static void name_changed(GtkWidget * widget, gpointer data) 93 { 94 const gchar *text; 95 gchar *entry, *end, *j; 96 gint len, pos; 97 98 (void)data; 99 100 /* step 1: ensure that only valid characters are entered 101 (stolen from xlog, tnx pg4i) 102 */ 103 entry = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1); 104 if ((len = g_utf8_strlen(entry, -1)) > 0) 105 { 106 end = entry + g_utf8_strlen(entry, -1); 107 for (j = entry; j < end; ++j) 108 { 109 if (!gpredict_legal_char(*j)) 110 { 111 gdk_display_beep(gdk_display_get_default()); 112 pos = gtk_editable_get_position(GTK_EDITABLE(widget)); 113 gtk_editable_delete_text(GTK_EDITABLE(widget), pos, pos + 1); 114 } 115 } 116 } 117 118 /* step 2: if name seems all right, enable OK button */ 119 text = gtk_entry_get_text(GTK_ENTRY(widget)); 120 121 if (g_utf8_strlen(text, -1) > 0) 122 { 123 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), 124 GTK_RESPONSE_OK, TRUE); 125 } 126 else 127 { 128 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), 129 GTK_RESPONSE_OK, FALSE); 130 } 131 } 132 133 static void aztype_changed_cb(GtkComboBox * box, gpointer data) 134 { 135 gint type = gtk_combo_box_get_active(box); 136 137 (void)data; 138 139 switch (type) 140 { 141 case ROT_AZ_TYPE_360: 142 gtk_spin_button_set_value(GTK_SPIN_BUTTON(minaz), 0.0); 143 gtk_spin_button_set_value(GTK_SPIN_BUTTON(maxaz), 360.0); 144 gtk_spin_button_set_value(GTK_SPIN_BUTTON(azstoppos), 0.0); 145 break; 146 147 case ROT_AZ_TYPE_180: 148 gtk_spin_button_set_value(GTK_SPIN_BUTTON(minaz), -180.0); 149 gtk_spin_button_set_value(GTK_SPIN_BUTTON(maxaz), +180.0); 150 gtk_spin_button_set_value(GTK_SPIN_BUTTON(azstoppos), -180.0); 151 break; 152 153 default: 154 sat_log_log(SAT_LOG_LEVEL_ERROR, 155 _("%s:%s: Invalid AZ rotator type."), __FILE__, __func__); 156 break; 157 } 158 } 159 160 static GtkWidget *create_editor_widgets(rotor_conf_t * conf) 161 { 162 GtkWidget *table; 163 GtkWidget *label; 164 165 table = gtk_grid_new(); 166 gtk_container_set_border_width(GTK_CONTAINER(table), 5); 167 gtk_grid_set_column_spacing(GTK_GRID(table), 5); 168 gtk_grid_set_row_spacing(GTK_GRID(table), 5); 169 170 /* Config name */ 171 label = gtk_label_new(_("Name")); 172 g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL); 173 gtk_grid_attach(GTK_GRID(table), label, 0, 0, 1, 1); 174 175 name = gtk_entry_new(); 176 gtk_entry_set_max_length(GTK_ENTRY(name), 25); 177 gtk_widget_set_tooltip_text(name, 178 _("Enter a short name for this configuration, " 179 " e.g. ROTOR-1.\n" 180 "Allowed characters: 0..9, a..z, A..Z, - and _")); 181 gtk_grid_attach(GTK_GRID(table), name, 1, 0, 3, 1); 182 183 /* attach changed signal so that we can enable OK button when 184 a proper name has been entered 185 */ 186 g_signal_connect(name, "changed", G_CALLBACK(name_changed), NULL); 187 188 /* Host */ 189 label = gtk_label_new(_("Host")); 190 g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL); 191 gtk_grid_attach(GTK_GRID(table), label, 0, 1, 1, 1); 192 193 host = gtk_entry_new(); 194 gtk_entry_set_max_length(GTK_ENTRY(host), 50); 195 gtk_entry_set_text(GTK_ENTRY(host), "localhost"); 196 gtk_widget_set_tooltip_text(host, 197 _("Enter the host where rotctld is running. " 198 "You can use both host name and IP address, " 199 "e.g. 192.168.1.100\n\n" 200 "If gpredict and rotctld are running on the " 201 "same computer, use localhost")); 202 gtk_grid_attach(GTK_GRID(table), host, 1, 1, 3, 1); 203 204 /* port */ 205 label = gtk_label_new(_("Port")); 206 g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL); 207 gtk_grid_attach(GTK_GRID(table), label, 0, 2, 1, 1); 208 209 port = gtk_spin_button_new_with_range(1024, 65535, 1); 210 gtk_spin_button_set_value(GTK_SPIN_BUTTON(port), 4533); 211 gtk_spin_button_set_digits(GTK_SPIN_BUTTON(port), 0); 212 gtk_widget_set_tooltip_text(port, 213 _("Enter the port number where rotctld is " 214 "listening. Default is 4533.")); 215 gtk_grid_attach(GTK_GRID(table), port, 1, 2, 1, 1); 216 217 gtk_grid_attach(GTK_GRID(table), 218 gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), 219 0, 3, 4, 1); 220 221 /* Az-type */ 222 label = gtk_label_new(_("Az type")); 223 g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL); 224 gtk_grid_attach(GTK_GRID(table), label, 0, 4, 1, 1); 225 226 aztype = gtk_combo_box_text_new(); 227 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(aztype), 228 "0\302\260 \342\206\222 180\302\260 \342\206\222 360\302\260"); 229 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(aztype), 230 "-180\302\260 \342\206\222 0\302\260 \342\206\222 +180\302\260"); 231 gtk_combo_box_set_active(GTK_COMBO_BOX(aztype), 0); 232 gtk_widget_set_tooltip_text(aztype, 233 _("Select your azimuth range here. Note that " 234 "gpredict assumes that 0\302\260 is at North " 235 "and + direction is clockwise for both types")); 236 gtk_grid_attach(GTK_GRID(table), aztype, 1, 4, 2, 1); 237 g_signal_connect(G_OBJECT(aztype), "changed", 238 G_CALLBACK(aztype_changed_cb), NULL); 239 240 /* Az and El limits */ 241 label = gtk_label_new(_(" Min Az")); 242 g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL); 243 gtk_grid_attach(GTK_GRID(table), label, 0, 5, 1, 1); 244 minaz = gtk_spin_button_new_with_range(-200, 100, 1); 245 gtk_spin_button_set_value(GTK_SPIN_BUTTON(minaz), 0); 246 gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(minaz), TRUE); 247 gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(minaz), FALSE); 248 gtk_grid_attach(GTK_GRID(table), minaz, 1, 5, 1, 1); 249 250 label = gtk_label_new(_(" Max Az")); 251 g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL); 252 gtk_grid_attach(GTK_GRID(table), label, 2, 5, 1, 1); 253 maxaz = gtk_spin_button_new_with_range(0, 450, 1); 254 gtk_spin_button_set_value(GTK_SPIN_BUTTON(maxaz), 360); 255 gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(maxaz), TRUE); 256 gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(maxaz), FALSE); 257 gtk_grid_attach(GTK_GRID(table), maxaz, 3, 5, 1, 1); 258 259 label = gtk_label_new(_(" Min El")); 260 g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL); 261 gtk_grid_attach(GTK_GRID(table), label, 0, 6, 1, 1); 262 minel = gtk_spin_button_new_with_range(-10, 180, 1); 263 gtk_spin_button_set_value(GTK_SPIN_BUTTON(minel), 0); 264 gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(minel), TRUE); 265 gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(minel), FALSE); 266 gtk_grid_attach(GTK_GRID(table), minel, 1, 6, 1, 1); 267 268 label = gtk_label_new(_(" Max El")); 269 g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL); 270 gtk_grid_attach(GTK_GRID(table), label, 2, 6, 1, 1); 271 maxel = gtk_spin_button_new_with_range(-10, 180, 1); 272 gtk_spin_button_set_value(GTK_SPIN_BUTTON(maxel), 90); 273 gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(maxel), TRUE); 274 gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(maxel), FALSE); 275 gtk_grid_attach(GTK_GRID(table), maxel, 3, 6, 1, 1); 276 277 label = gtk_label_new(_(" Azimuth end stop position")); 278 g_object_set(label, "xalign", 1.0, "yalign", 0.5, NULL); 279 gtk_grid_attach(GTK_GRID(table), label, 1, 7, 2, 1); 280 azstoppos = gtk_spin_button_new_with_range(-180, 360, 1); 281 gtk_spin_button_set_value(GTK_SPIN_BUTTON(azstoppos), 0); 282 gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(azstoppos), TRUE); 283 gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(azstoppos), FALSE); 284 gtk_widget_set_tooltip_text(azstoppos, 285 _("Set the position of the azimuth end stop " 286 "here, where 0\302\260 is at North, " 287 "-180\302\260 is south, etc. " 288 "The default for a 0\302\260 \342\206\222 " 289 "180\302\260 \342\206\222 360\302\260 rotor " 290 "is 0\302\260, and the default for a " 291 "-180\302\260 \342\206\222 0\302\260 " 292 "\342\206\222 +180\302\260 rotor is -180\302\260.")); 293 gtk_grid_attach(GTK_GRID(table), azstoppos, 3, 7, 1, 1); 294 295 if (conf->name != NULL) 296 update_widgets(conf); 297 298 gtk_widget_show_all(table); 299 300 return table; 301 } 302 303 /* Called when the user clicks the OK button */ 304 static gboolean apply_changes(rotor_conf_t * conf) 305 { 306 /* name */ 307 if (conf->name) 308 g_free(conf->name); 309 310 conf->name = g_strdup(gtk_entry_get_text(GTK_ENTRY(name))); 311 312 /* host */ 313 if (conf->host) 314 g_free(conf->host); 315 316 conf->host = g_strdup(gtk_entry_get_text(GTK_ENTRY(host))); 317 318 /* port */ 319 conf->port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(port)); 320 321 /* az type */ 322 conf->aztype = gtk_combo_box_get_active(GTK_COMBO_BOX(aztype)); 323 324 /* az and el ranges */ 325 conf->minaz = gtk_spin_button_get_value(GTK_SPIN_BUTTON(minaz)); 326 conf->maxaz = gtk_spin_button_get_value(GTK_SPIN_BUTTON(maxaz)); 327 conf->minel = gtk_spin_button_get_value(GTK_SPIN_BUTTON(minel)); 328 conf->maxel = gtk_spin_button_get_value(GTK_SPIN_BUTTON(maxel)); 329 330 /* az stop position */ 331 conf->azstoppos = gtk_spin_button_get_value(GTK_SPIN_BUTTON(azstoppos)); 332 333 return TRUE; 334 } 335 336 /** 337 * Add or edit a rotor configuration. 338 * 339 * @param conf Pointer to a rotator configuration. 340 * 341 * If conf->name is not NULL the widgets will be populated with the data. 342 */ 343 void sat_pref_rot_editor_run(rotor_conf_t * conf) 344 { 345 gint response; 346 gboolean finished = FALSE; 347 348 /* create dialog and add contents */ 349 dialog = gtk_dialog_new_with_buttons(_("Edit rotator configuration"), 350 GTK_WINDOW(window), 351 GTK_DIALOG_MODAL | 352 GTK_DIALOG_DESTROY_WITH_PARENT, 353 "_Clear", GTK_RESPONSE_REJECT, 354 "_Cancel", GTK_RESPONSE_CANCEL, 355 "_Ok", GTK_RESPONSE_OK, 356 NULL); 357 358 /* disable OK button to begin with */ 359 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), 360 GTK_RESPONSE_OK, FALSE); 361 362 gtk_container_add(GTK_CONTAINER 363 (gtk_dialog_get_content_area(GTK_DIALOG(dialog))), 364 create_editor_widgets(conf)); 365 366 /* this hacky-thing is to keep the dialog running in case the 367 CLEAR button is plressed. OK and CANCEL will exit the loop 368 */ 369 while (!finished) 370 { 371 response = gtk_dialog_run(GTK_DIALOG(dialog)); 372 373 switch (response) 374 { 375 case GTK_RESPONSE_OK: 376 if (apply_changes(conf)) 377 finished = TRUE; 378 else 379 finished = FALSE; 380 break; 381 382 case GTK_RESPONSE_REJECT: 383 /* CLEAR */ 384 clear_widgets(); 385 break; 386 387 default: 388 /* Everything else is considered CANCEL */ 389 finished = TRUE; 390 break; 391 } 392 } 393 394 gtk_widget_destroy(dialog); 395 }