/ src / gpredict-utils.c
gpredict-utils.c
  1  /*
  2    Gpredict: Real-time satellite tracking and orbit prediction program
  3  
  4    Copyright (C)  2001-2019  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  #define _GNU_SOURCE
 20  #ifdef HAVE_CONFIG_H
 21  #include <build-config.h>
 22  #endif
 23  #include <ctype.h>
 24  #include <glib/gi18n.h>
 25  #include <glib/gstdio.h>
 26  #include <gtk/gtk.h>
 27  
 28  #include "compat.h"
 29  #include "gpredict-utils.h"
 30  #include "sat-log.h"
 31  #include "strnatcmp.h"
 32  
 33  
 34  
 35  /**
 36   * Create a horizontal pixmap button.
 37   *
 38   * The text will be placed to the right of the image.
 39   * file is only the icon name, not the full path.
 40   */
 41  GtkWidget      *gpredict_hpixmap_button(const gchar * file, const gchar * text,
 42                                          const gchar * tooltip)
 43  {
 44      GtkWidget      *button;
 45      GtkWidget      *image;
 46      GtkWidget      *box;
 47      gchar          *path;
 48  
 49      path = icon_file_name(file);
 50      image = gtk_image_new_from_file(path);
 51      g_free(path);
 52      box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
 53      gtk_box_set_homogeneous(GTK_BOX(box), FALSE);
 54      gtk_box_pack_start(GTK_BOX(box), image, TRUE, TRUE, 0);
 55      if (text != NULL)
 56          gtk_box_pack_start(GTK_BOX(box), gtk_label_new(text), TRUE, TRUE, 0);
 57  
 58      button = gtk_button_new();
 59      gtk_widget_set_tooltip_text(button, tooltip);
 60      gtk_container_add(GTK_CONTAINER(button), box);
 61  
 62      return button;
 63  }
 64  
 65  /**
 66   * Create a vertical pixmap button.
 67   *
 68   * The text will be placed under the image.
 69   * file is only the icon name, not the full path.
 70   */
 71  GtkWidget      *gpredict_vpixmap_button(const gchar * file, const gchar * text,
 72                                          const gchar * tooltip)
 73  {
 74      GtkWidget      *button;
 75      GtkWidget      *image;
 76      GtkWidget      *box;
 77      gchar          *path;
 78  
 79      path = icon_file_name(file);
 80      image = gtk_image_new_from_file(path);
 81      g_free(path);
 82      box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
 83      gtk_box_set_homogeneous(GTK_BOX(box), FALSE);
 84      gtk_box_pack_start(GTK_BOX(box), image, TRUE, TRUE, 0);
 85      if (text != NULL)
 86          gtk_box_pack_start(GTK_BOX(box), gtk_label_new(text), TRUE, TRUE, 0);
 87  
 88      button = gtk_button_new();
 89      gtk_widget_set_tooltip_text(button, tooltip);
 90      gtk_container_add(GTK_CONTAINER(button), box);
 91  
 92      return button;
 93  }
 94  
 95  /**
 96   * Create a horizontal pixmap button using stock pixmap.
 97   *
 98   * The text will be placed to the right of the image.
 99   * The icon size will be GTK_ICON_SIZE_BUTTON.
100   */
101  GtkWidget      *gpredict_hstock_button(const gchar * stock_id,
102                                         const gchar * text,
103                                         const gchar * tooltip)
104  {
105      GtkWidget      *button;
106      GtkWidget      *image;
107      GtkWidget      *box;
108  
109      image = gtk_image_new_from_icon_name(stock_id, GTK_ICON_SIZE_BUTTON);
110      box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
111      gtk_box_set_homogeneous(GTK_BOX(box), FALSE);
112      gtk_box_pack_start(GTK_BOX(box), image, TRUE, TRUE, 0);
113      if (text != NULL)
114          gtk_box_pack_start(GTK_BOX(box), gtk_label_new(text), TRUE, TRUE, 0);
115  
116      button = gtk_button_new();
117      gtk_widget_set_tooltip_text(button, tooltip);
118      gtk_container_add(GTK_CONTAINER(button), box);
119  
120      return button;
121  }
122  
123  /**
124   * Create and set tooltips for GtkComboBox.
125   *
126   * @param text  Pointer to the desired tooltip text.
127   *
128   * This function creates and sets the tooltips for the specified widget.
129   * This function is called by the \a grig_set_combo_tooltips function which
130   * is must be used as callback for the "realize" signal of the GtkComboBox.
131   */
132  static void set_combo_tooltip(GtkWidget * combo, gpointer text)
133  {
134      /* if current child is a button we have BINGO! */
135      if (GTK_IS_BUTTON(combo))
136          gtk_widget_set_tooltip_text(combo, text);
137  }
138  
139  /**
140   * Create and set tooltips for GtkComboBox.
141   *
142   * @param combo The GtkComboBox widget.
143   * @param text  Pointer to the desired tooltip text.
144   *
145   * This function creates and sets the tooltips for the specified widget.
146   * The interface is implemented such that this function can be connected
147   * directly to the @a realized signal of the GtkComboBox.
148   *
149   * Actually, this function only loops over all the children of the GtkComboBox
150   * and calls the set_combo_tooltip internal function.
151   *
152   * @note This works only if the function is actually used as callback for the
153   *       @a realize signal og the GtkComboBox.
154   *
155   * @note This great trick has been pointed out by Matthias Clasen, he has done the
156   *       the same for the filter combo in the new GtkFileChooser
157   *       ref: gtkfilechooserdefault.c:3151 in Gtk+ 2.5.5
158   */
159  void gpredict_set_combo_tooltips(GtkWidget * combo, gpointer text)
160  {
161      /* for each child in the container call the internal
162         function which actually creates the tooltips.
163       */
164      gtk_container_forall(GTK_CONTAINER(combo), set_combo_tooltip, text);
165  
166  }
167  
168  gint gpredict_file_copy(const gchar * in, const gchar * out)
169  {
170      gchar          *contents;
171      gboolean        status = 0;
172      GError         *err = NULL;
173      gsize           ulen;
174      gssize          slen;
175  
176      g_return_val_if_fail(in != NULL, 1);
177      g_return_val_if_fail(out != NULL, 1);
178  
179      /* read source file */
180      if (!g_file_get_contents(in, &contents, &ulen, &err))
181      {
182          sat_log_log(SAT_LOG_LEVEL_ERROR, "%s: %s", __func__, err->message);
183          g_clear_error(&err);
184  
185          status = 1;
186      }
187      else
188      {
189          /* write contents to new file */
190          slen = (gssize) ulen;
191  
192          if (!g_file_set_contents(out, contents, slen, &err))
193          {
194              sat_log_log(SAT_LOG_LEVEL_ERROR, "%s: %s", __func__, err->message);
195              g_clear_error(&err);
196  
197              status = 1;
198          }
199  
200          g_free(contents);
201      }
202  
203      return status;
204  }
205  
206  /**
207   * Create a miniature pixmap button with no relief.
208   *
209   * Pixmapfile is only the icon name, not the full path.
210   */
211  GtkWidget      *gpredict_mini_mod_button(const gchar * pixmapfile,
212                                           const gchar * tooltip)
213  {
214      GtkWidget      *button;
215      GtkWidget      *image;
216      gchar          *path;
217  
218      path = icon_file_name(pixmapfile);
219      image = gtk_image_new_from_file(path);
220      g_free(path);
221  
222      button = gtk_button_new();
223      gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
224      gtk_widget_set_tooltip_text(button, tooltip);
225      gtk_container_add(GTK_CONTAINER(button), image);
226  
227      return button;
228  }
229  
230  /**
231   * Convert a BCD colour to a GdkColor structure.
232   * 
233   * @param rgb The source colour in 0xRRGGBB form.
234   * @param color Pointer to an existing GdkColor structure.
235   */
236  void rgb2gdk(guint rgb, GdkColor * color)
237  {
238      guint16         r, g, b;
239      guint           tmp;
240  
241      /* sanity checks */
242      if (color == NULL)
243      {
244          sat_log_log(SAT_LOG_LEVEL_ERROR,
245                      _("%s:%s: %s called with color = NULL"),
246                      __FILE__, __LINE__, __func__);
247          return;
248      }
249  
250      /* red */
251      tmp = rgb & 0xFF0000;
252      r = (guint16) (tmp >> 16);
253  
254      /* green */
255      tmp = rgb & 0x00FF00;
256      g = (guint16) (tmp >> 8);
257  
258      /* blue */
259      tmp = rgb & 0x0000FF;
260      b = (guint16) tmp;
261  
262      /* store colours */
263      color->red = 257 * r;
264      color->green = 257 * g;
265      color->blue = 257 * b;
266  }
267  
268  /**
269   * Convert a BCD colour to a GdkColor structure.
270   *
271   * @param rgba The source colour in 0xRRGGBBAA form.
272   * @param color Pointer to an existing GdkColor structure.
273   * @param alpha Pointer to where the alpha channel value should be stored
274   */
275  void rgba2gdk(guint rgba, GdkColor * color, guint16 * alpha)
276  {
277      guint16         r, g, b;
278      guint           tmp;
279  
280      /* sanity checks */
281      if (color == NULL)
282      {
283          sat_log_log(SAT_LOG_LEVEL_ERROR,
284                      _("%s:%s: %s called with color = NULL"),
285                      __FILE__, __LINE__, __func__);
286          return;
287      }
288      if (alpha == NULL)
289      {
290          sat_log_log(SAT_LOG_LEVEL_ERROR,
291                      _("%s:%s: %s called with alpha = NULL"),
292                      __FILE__, __LINE__, __func__);
293          return;
294      }
295  
296      /* red */
297      tmp = rgba & 0xFF000000;
298      r = (guint16) (tmp >> 24);
299  
300      /* green */
301      tmp = rgba & 0x00FF0000;
302      g = (guint16) (tmp >> 16);
303  
304      /* blue */
305      tmp = rgba & 0x0000FF00;
306      b = (guint16) (tmp >> 8);
307  
308      /* alpha channel */
309      *alpha = (guint16) (257 * (rgba & 0x000000FF));
310  
311      /* store colours */
312      color->red = 257 * r;
313      color->green = 257 * g;
314      color->blue = 257 * b;
315  }
316  
317  /**
318   * Convert GdkColor to BCD colour.
319   *
320   * @param color The GdkColor structure.
321   * @param rgb Pointer to where the 0xRRGGBB encoded colour should be stored.
322   */
323  void gdk2rgb(const GdkColor * color, guint * rgb)
324  {
325      guint           r, g, b;
326      guint16         tmp;
327  
328      /* sanity checks */
329      if (color == NULL)
330      {
331          sat_log_log(SAT_LOG_LEVEL_ERROR,
332                      _("%s:%s: %s called with color = NULL"),
333                      __FILE__, __LINE__, __func__);
334          return;
335      }
336      if (rgb == NULL)
337      {
338          sat_log_log(SAT_LOG_LEVEL_ERROR,
339                      _("%s:%s: %s called with rgb = NULL"),
340                      __FILE__, __LINE__, __func__);
341          return;
342      }
343  
344      /* red */
345      tmp = color->red / 257;
346      r = (guint) (tmp << 16);
347  
348      /* green */
349      tmp = color->green / 257;
350      g = (guint) (tmp << 8);
351  
352      /* blue */
353      tmp = color->blue / 257;
354      b = (guint) tmp;
355  
356      *rgb = (r | g | b);
357  }
358  
359  /**
360   * Convert GdkColor and alpha channel to BCD colour.
361   *
362   * @param color The GdkColor structure.
363   * @param alpha The value of the alpha channel.
364   * @param rgb Pointer to where the 0xRRGGBBAA encoded colour should be stored.
365   */
366  void gdk2rgba(const GdkColor * color, guint16 alpha, guint * rgba)
367  {
368      guint           r, g, b, a;
369      guint16         tmp;
370  
371      /* sanity checks */
372      if (color == NULL)
373      {
374          sat_log_log(SAT_LOG_LEVEL_ERROR,
375                      _("%s:%s: %s called with color = NULL"),
376                      __FILE__, __LINE__, __func__);
377          return;
378      }
379      if (rgba == NULL)
380      {
381          sat_log_log(SAT_LOG_LEVEL_ERROR,
382                      _("%s:%s: %s called with rgba = NULL"),
383                      __FILE__, __LINE__, __func__);
384          return;
385      }
386  
387      /* red */
388      tmp = color->red / 257;
389      r = (guint) (tmp << 24);
390  
391      /* green */
392      tmp = color->green / 257;
393      g = (guint) (tmp << 16);
394  
395      /* blue */
396      tmp = color->blue / 257;
397      b = (guint) (tmp << 8);
398  
399      /* alpha */
400      tmp = alpha / 257;
401      a = (guint) tmp;
402  
403      *rgba = (r | g | b | a);
404  }
405  
406  /**
407   * Convert GdkColor to RRGGBB hex format (for Pango Markup).
408   *
409   * @param color The GdkColor structure.
410   * @return A newly allocated character string.
411   */
412  gchar          *rgba2html(guint rgba)
413  {
414      gchar          *col;
415      guint8          r, g, b;
416      guint           tmp;
417  
418      tmp = rgba & 0xFF000000;
419      r = (guint8) (tmp >> 24);
420  
421      /* green */
422      tmp = rgba & 0x00FF0000;
423      g = (guint8) (tmp >> 16);
424  
425      /* blue */
426      tmp = rgba & 0x0000FF00;
427      b = (guint8) (tmp >> 8);
428  
429      col = g_strdup_printf("%X%X%X", r, g, b);
430  
431      return col;
432  }
433  
434  /**
435   * String comparison function
436   * @param s1 first string
437   * @param s2 second string
438   */
439  int gpredict_strcmp(const char *s1, const char *s2)
440  {
441  #if 0
442      gchar          *a, *b;
443      int             retval;
444  
445      a = g_ascii_strup(s1, -1);
446      b = g_ascii_strup(s2, -1);
447  
448      retval = strverscmp(a, b);
449      g_free(a);
450      g_free(b);
451      return retval;
452  #else
453      return strnatcasecmp(s1, s2);
454  #endif
455  }
456  
457  /**
458   * Substring finding function
459   *
460   * @param s1 the larger string
461   * @param s2 the substring that we are searching for.
462   * @return pointer to the substring location
463   *  
464   *  this is a substitute for strcasestr which is a gnu c extension and not available everywhere.
465   */
466  char           *gpredict_strcasestr(const char *s1, const char *s2)
467  {
468      size_t          s1_len = strlen(s1);
469      size_t          s2_len = strlen(s2);
470  
471      while (s1_len >= s2_len)
472      {
473          if (strncasecmp(s1, s2, s2_len) == 0)
474              return (char *)s1;
475  
476          s1++;
477          s1_len--;
478      }
479  
480      return NULL;
481  }
482  
483  /**
484   * Save a GKeyFile structure to a file
485   *
486   * @param cfgdata is a pointer to the GKeyFile.
487   * @param filename is a pointer the filename string.
488   * @return 1 on error and zero on success.
489   *
490   */
491  gboolean gpredict_save_key_file(GKeyFile * cfgdata, const char *filename)
492  {
493      GError         *error = NULL; 
494  
495      if (!g_key_file_save_to_file(cfgdata, filename, &error))
496      {
497          sat_log_log(SAT_LOG_LEVEL_ERROR,
498                      _("%s: Error writing config data (%s)."),
499                      __func__,
500                      error != NULL ? error->message : "unknown error");
501          g_clear_error(&error);
502          return 1;
503      }
504  
505      return 0;
506  }
507  
508  
509  /**
510   * Check if \c ch is an alpha-num; in range \c "[0-9a-zA-F]".
511   * Or \c "ch == '-'" or \c "ch == '_'".
512   *
513   * @param ch the character code to check.
514   * @return TRUE if okay.
515   */
516  gboolean gpredict_legal_char(int ch)
517  {
518      if (g_ascii_isalnum(ch) || ch == '-' || ch == '_')
519          return (TRUE);
520      return (FALSE);
521  }
522  
523  
524  /* Convert a 0xRRGGBBAA encoded config integer to a GdkRGBA structure */
525  void rgba_from_cfg(guint cfg_rgba, GdkRGBA * gdk_rgba)
526  {
527      if (gdk_rgba == NULL)
528      {
529          sat_log_log(SAT_LOG_LEVEL_ERROR,
530                      _("%s called with gdk_rgba = NULL"), __func__);
531          return;
532      }
533  
534      gdk_rgba->red = ((cfg_rgba >> 24) & 0xFF) / 255.0;
535      gdk_rgba->green = ((cfg_rgba >> 16) & 0xFF) / 255.0;
536      gdk_rgba->blue = ((cfg_rgba >> 8) & 0xFF) / 255.0;
537      gdk_rgba->alpha = (cfg_rgba & 0xFF) / 255.0;
538  }
539  
540  /* convert GdkRGBA struct to 0xRRGGBBAA formatted config integer */
541  guint rgba_to_cfg(const GdkRGBA * gdk_rgba)
542  {
543      guint           cfg_int = 0;
544  
545      if (gdk_rgba == NULL)
546      {
547          sat_log_log(SAT_LOG_LEVEL_ERROR,
548                      _("%s called with gdk_rgba = NULL"), __func__);
549          return 0;
550      }
551  
552      cfg_int = ((guint) (gdk_rgba->red * 255.0)) << 24;
553      cfg_int += ((guint) (gdk_rgba->green * 255.0)) << 16;
554      cfg_int += ((guint) (gdk_rgba->blue * 255.0)) << 8;
555      cfg_int += (guint) (gdk_rgba->alpha * 255.0);
556  
557      return cfg_int;
558  }
559  
560  /* Convert a 0xRRGGBB encoded config integer to a GdkRGBA structure (no alpha channel) */
561  void rgb_from_cfg(guint cfg_rgb, GdkRGBA * gdk_rgba)
562  {
563      if (gdk_rgba == NULL)
564      {
565          sat_log_log(SAT_LOG_LEVEL_ERROR,
566                      _("%s called with gdk_rgba = NULL"), __func__);
567          return;
568      }
569  
570      gdk_rgba->red = ((cfg_rgb >> 16) & 0xFF) / 255.0;
571      gdk_rgba->green = ((cfg_rgb >> 8) & 0xFF) / 255.0;
572      gdk_rgba->blue = (cfg_rgb & 0xFF) / 255.0;
573      gdk_rgba->alpha = 1.0;
574  }
575  
576  /* convert GdkRGBA struct to 0xRRGGBB formatted config integer, ignoring the alpha channel */
577  guint rgb_to_cfg(const GdkRGBA * gdk_rgba)
578  {
579      guint           cfg_int = 0;
580  
581      if (gdk_rgba == NULL)
582      {
583          sat_log_log(SAT_LOG_LEVEL_ERROR,
584                      _("%s called with gdk_rgba = NULL"), __func__);
585          return 0;
586      }
587  
588      cfg_int = ((guint) (gdk_rgba->red * 255.0)) << 16;
589      cfg_int += ((guint) (gdk_rgba->green * 255.0)) << 8;
590      cfg_int += (guint) (gdk_rgba->blue * 255.0);
591  
592      return cfg_int;
593  }