/ src / sat-pref-rot-editor.c
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  }