示例#1
0
/** Save a row from the QTH list (called by the save function) */
static gboolean save_qth(GtkTreeModel * model, GtkTreePath * path,
                         GtkTreeIter * iter, gpointer data)
{
    qth_t           qth;
    gboolean        def = FALSE;
    gchar          *filename, *confdir;
    gchar          *buff;

    (void)path;
    (void)data;

    gtk_tree_model_get(model, iter,
                       QTH_LIST_COL_DEF, &def,
                       QTH_LIST_COL_NAME, &qth.name,
                       QTH_LIST_COL_LOC, &qth.loc,
                       QTH_LIST_COL_DESC, &qth.desc,
                       QTH_LIST_COL_LAT, &qth.lat,
                       QTH_LIST_COL_LON, &qth.lon,
                       QTH_LIST_COL_ALT, &qth.alt,
                       QTH_LIST_COL_WX, &qth.wx,
                       QTH_LIST_COL_TYPE, &qth.type,
                       QTH_LIST_COL_GPSD_SERVER, &qth.gpsd_server,
                       QTH_LIST_COL_GPSD_PORT, &qth.gpsd_port, -1);

    confdir = get_user_conf_dir();
    filename = g_strconcat(confdir, G_DIR_SEPARATOR_S, qth.name, ".qth", NULL);
    g_free(confdir);

    /* check wehter we are using imperial or metric system;
       in case of imperial we have to convert altitude from
       feet to meters before saving.
     */
    if (sat_cfg_get_bool(SAT_CFG_BOOL_USE_IMPERIAL))
    {
        qth.alt = (guint) FT_TO_M(qth.alt);
    }

    if (qth_data_save(filename, &qth))
    {
        /* saved ok, go on check whether qth is default */
        if (def)
        {
            sat_log_log(SAT_LOG_LEVEL_INFO,
                        _("%s:%d: %s appears to be default QTH"),
                        __FILE__, __LINE__, qth.name);

            buff = g_path_get_basename(filename);
            sat_cfg_set_str(SAT_CFG_STR_DEF_QTH, buff);
            g_free(buff);
        }
    }

    g_free(filename);
    g_free(qth.name);
    g_free(qth.loc);
    g_free(qth.desc);
    g_free(qth.wx);

    return FALSE;
}
示例#2
0
/**
 * Remove .qth files.
 *
 * This function is used to remove any existing .qth file
 * before storing the data from the QTH list.
 */
static void delete_location_files()
{
    GDir           *dir = NULL; /* directory handle */
    GError         *error = NULL;       /* error flag and info */
    gchar          *dirname;    /* directory name */
    const gchar    *filename;   /* file name */
    gchar          *buff;

    /* scan for .qth files in the user config directory and
       add the contents of each .qth file to the list store
     */
    dirname = get_user_conf_dir();
    dir = g_dir_open(dirname, 0, &error);

    if (dir)
    {
        while ((filename = g_dir_read_name(dir)))
        {
            if (g_str_has_suffix(filename, ".qth"))
            {
                buff = g_strconcat(dirname, G_DIR_SEPARATOR_S, filename, NULL);

                /* remove file */
                if (g_remove(buff))
                {
                    sat_log_log(SAT_LOG_LEVEL_ERROR,
                                _("%s:%d: Failed to remove %s"),
                                __FILE__, __LINE__, filename);
                }
                else
                {
                    sat_log_log(SAT_LOG_LEVEL_DEBUG,
                                _("%s:%d: Removed %s"),
                                __FILE__, __LINE__, filename);
                }

                g_free(buff);
            }
        }
    }
    else
    {
        sat_log_log(SAT_LOG_LEVEL_ERROR,
                    _("%s:%d: Failed to open user cfg dir (%s)"),
                    __FILE__, __LINE__, error->message);
        g_clear_error(&error);

    }

    g_free(dirname);
    g_dir_close(dir);
}
示例#3
0
/**
 * Save configuration data.
 * @return 0 on success, 1 if an error occured.
 *
 * This function saves the configuration data currently stored in
 * memory to the gpredict.cfg file.
 */
guint sat_cfg_save()
{
    gchar      *keyfile;
    gchar      *confdir;
    guint       err = 0;
    
    confdir = get_user_conf_dir ();
    keyfile = g_strconcat (confdir, G_DIR_SEPARATOR_S, "gpredict.cfg", NULL);
    err = gpredict_save_key_file( config , keyfile);
    g_free (confdir);

    return err;
}
示例#4
0
/** \brief Load configuration data.
 *  \return 0 if everything OK, 1 otherwise.
 *
 * This function reads the configuration data from gpredict.cfg into
 * memory. This function must be called very early at program start.
 *
 * The the configuration data in memory is already "loaded" the data will
 * be ereased first.
 */
guint sat_cfg_load        ()
{
    gchar  *keyfile,*confdir;
    GError *error = NULL;

    if (config != NULL)
        sat_cfg_close ();

    /* load the configuration file */
    config = g_key_file_new ();
    confdir = get_user_conf_dir ();
    keyfile = g_strconcat (confdir, G_DIR_SEPARATOR_S, "gpredict.cfg", NULL);
    g_free (confdir);

    g_key_file_load_from_file (config, keyfile, G_KEY_FILE_KEEP_COMMENTS, &error);

    g_free (keyfile);

    if (error != NULL) {

        sat_log_log (SAT_LOG_LEVEL_WARN,
                     _("%s: Error reading config file (%s)"),
                     __FUNCTION__, error->message);

        sat_log_log (SAT_LOG_LEVEL_WARN,
                     _("%s: Using built-in defaults"),
                     __FUNCTION__);

        g_clear_error (&error);

        return 1;
    }
    else {
        sat_log_log (SAT_LOG_LEVEL_DEBUG,
                     _("%s: Everything OK."), __FUNCTION__);
    }

    /* if config version is < 1.1; reset SAT_CFG_STR_TLE_FILES */
    guint ver;
    ver = 10*sat_cfg_get_int (SAT_CFG_INT_VERSION_MAJOR) + sat_cfg_get_int (SAT_CFG_INT_VERSION_MINOR);
    if (ver < 11) {
        sat_cfg_reset_str (SAT_CFG_STR_TLE_FILES);
        sat_cfg_set_int (SAT_CFG_INT_VERSION_MAJOR, 1);
        sat_cfg_set_int (SAT_CFG_INT_VERSION_MINOR, 1);
    }


    return 0;
}
示例#5
0
/** \brief Save configuration data.
 *  \return 0 on success, 1 if an error occured.
 *
 * This function saves the configuration data currently stored in
 * memory to the gpredict.cfg file.
 */
guint sat_cfg_save        ()
{
    gsize       length;
    gsize       written;
    GError     *error = NULL;
    gchar      *cfgstr;
    gchar      *keyfile;
    gchar      *confdir;
    GIOChannel *cfgfile;
    guint       err = 0;

    /* convert configuration data struct to charachter string */
    cfgstr = g_key_file_to_data (config, &length, &error);

    if (error != NULL) {
        sat_log_log (SAT_LOG_LEVEL_ERROR,
                     _("%s: Could not create config data (%s)."),
                     __FUNCTION__, error->message);

        g_clear_error (&error);

        err = 1;
    }
    else {
        /* create and open a file for writing */
        confdir = get_user_conf_dir ();
        keyfile = g_strconcat (confdir, G_DIR_SEPARATOR_S, "gpredict.cfg", NULL);
        g_free (confdir);
        cfgfile = g_io_channel_new_file (keyfile, "w", &error);
        g_free (keyfile);

        if (error != NULL) {
            sat_log_log (SAT_LOG_LEVEL_ERROR,
                         _("%s: Could not create config file (%s)."),
                         __FUNCTION__, error->message);

            g_clear_error (&error);

            err = 1;
        }
        else {
            g_io_channel_write_chars (cfgfile,
                                      cfgstr,
                                      length,
                                      &written,
                                      &error);

            g_io_channel_shutdown (cfgfile, TRUE, NULL);
            g_io_channel_unref (cfgfile);

            if (error != NULL) {
                sat_log_log (SAT_LOG_LEVEL_ERROR,
                             _("%s: Error writing config data (%s)."),
                             __FUNCTION__, error->message);

                g_clear_error (&error);

                err = 1;
            }
            else if (length != written) {
                sat_log_log (SAT_LOG_LEVEL_WARN,
                             _("%s: Wrote only %d out of %d chars."),
                             __FUNCTION__, written, length);

                err = 1;
            }
            else {
                sat_log_log (SAT_LOG_LEVEL_MSG,
                             _("%s: Configuration saved."),
                             __FUNCTION__);

                err = 0;
            }
        }

        g_free (cfgstr);
    }

    return err;
}
示例#6
0
/**
 * Create data storage for QTH list.
 *
 * This function creates the data storage necessary for the
 * list view. The newly created tree model is populated with
 * data from the .qth files in the users config diretory.
 * The individual .qth files are read by the read_qth_file
 * function.
 */
static GtkTreeModel *create_and_fill_model()
{
    GtkListStore   *liststore;  /* the list store data structure */
    GDir           *dir = NULL; /* directory handle */
    GError         *error = NULL;       /* error flag and info */
    gchar          *dirname;    /* directory name */
    const gchar    *filename;   /* file name */
    gchar          *buff;

    /* create a new list store */
    liststore = gtk_list_store_new(QTH_LIST_COL_NUM, G_TYPE_STRING,     // QTH name
                                   G_TYPE_STRING,       // Location
                                   G_TYPE_STRING,       // Description
                                   G_TYPE_DOUBLE,       // Latitude
                                   G_TYPE_DOUBLE,       // Longitude
                                   G_TYPE_INT,  // Altitude
                                   G_TYPE_STRING,       // QRA locator
                                   G_TYPE_STRING,       // Weather station
                                   G_TYPE_BOOLEAN,      // Default
                                   G_TYPE_INT,  //type
                                   G_TYPE_STRING,       //server
                                   G_TYPE_INT   //port
        );

    gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(liststore),
                                         QTH_LIST_COL_NAME,
                                         GTK_SORT_ASCENDING);
    /* scan for .qth files in the user config directory and
       add the contents of each .qth file to the list store
     */
    dirname = get_user_conf_dir();
    dir = g_dir_open(dirname, 0, &error);

    if (dir)
    {
        while ((filename = g_dir_read_name(dir)))
        {
            if (g_str_has_suffix(filename, ".qth"))
            {
                buff = g_strconcat(dirname, G_DIR_SEPARATOR_S, filename, NULL);

                /* read qth file */
                if (read_qth_file(liststore, buff))
                {
                    /* send debug message */
                    sat_log_log(SAT_LOG_LEVEL_DEBUG,
                                _("%s:%d: Read QTH data from %s."),
                                __FILE__, __LINE__, filename);
                }
                else
                {
                    /* error reading the file */
                    sat_log_log(SAT_LOG_LEVEL_ERROR,
                                _("%s:%d: Error reading %s (see prev msg)"),
                                __FILE__, __LINE__, filename);
                }
                g_free(buff);
            }
        }
    }
    else
    {
        sat_log_log(SAT_LOG_LEVEL_ERROR,
                    _("%s:%d: Failed to open user cfg dir (%s)"),
                    __FILE__, __LINE__, error->message);
        g_clear_error(&error);
    }

    g_free(dirname);
    g_dir_close(dir);

    return GTK_TREE_MODEL(liststore);
}
示例#7
0
/** \brief Update TLE files from local files.
 *  \param dir Directory where files are located.
 *  \param filter File filter, e.g. *.txt (not used at the moment!)
 *  \param silent TRUE if function should execute without graphical status indicator.
 *  \param label1 Activity label (can be NULL)
 *  \param label2 Statistics label (can be NULL)
 *  \param progress Pointer to progress indicator.
 *  \param init_prgs Initial value of progress indicator, e.g 0.5 if we are updating
 *                   from network.
 *
 * This function is used to update the TLE data from local files.
 *
 * Functional description: TBD
 *
 */
void tle_update_from_files (const gchar *dir, const gchar *filter,
                            gboolean silent, GtkWidget *progress,
                            GtkWidget *label1, GtkWidget *label2)
{
    static GMutex tle_file_in_progress;

    GHashTable  *data;        /* hash table with fresh TLE data */
    GDir        *cache_dir;   /* directory to scan fresh TLE */
    GDir        *loc_dir;     /* directory for gpredict TLE files */
    GError      *err = NULL;
    gchar       *text;
    gchar       *ldname;
    gchar       *userconfdir;
    const gchar *fnam;
    guint        num = 0;
    guint        updated,updated_tmp;
    guint        skipped,skipped_tmp;
    guint        nodata,nodata_tmp;
    guint        newsats = 0;
    guint        total,total_tmp;
    gdouble      fraction = 0.0;
    gdouble      start = 0.0;

    (void) filter; /* avoid unused parameter compiler warning */

    if (g_mutex_trylock(&tle_file_in_progress) == FALSE)
    {
        sat_log_log (SAT_LOG_LEVEL_ERROR,
                     _("%s: A TLE update process is already running. Aborting."),
                     __FUNCTION__);

        return;
    }

    /* create hash table */
    data = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, free_new_tle);

    /* open directory and read files one by one */
    cache_dir = g_dir_open (dir, 0, &err);

    if (err != NULL) {

        /* send an error message */
        sat_log_log (SAT_LOG_LEVEL_ERROR,
                     _("%s: Error opening directory %s (%s)"),
                     __FUNCTION__, dir, err->message);

        /* insert error message into the status string, too */
        if (!silent && (label1 != NULL)) {
            text = g_strdup_printf (_("<b>ERROR</b> opening directory %s\n%s"),
                                    dir, err->message);

            gtk_label_set_markup (GTK_LABEL (label1), text);
            g_free (text);

        }

        g_clear_error (&err);
        err = NULL;
    }
    else {

        /* scan directory for tle files */
        while ((fnam = g_dir_read_name (cache_dir)) != NULL) {
            /* check that we got a TLE file */
            if (is_tle_file(dir, fnam)) {
                
                /* status message */
                if (!silent && (label1 != NULL)) {
                    text = g_strdup_printf (_("Reading data from %s"), fnam);
                    gtk_label_set_text (GTK_LABEL (label1), text);
                    g_free (text);

                    /* Force the drawing queue to be processed otherwise there will
                        not be any visual feedback, ie. frozen GUI
                        - see Gtk+ FAQ http://www.gtk.org/faq/#AEN602
                    */
                    while (g_main_context_iteration (NULL, FALSE));

                    /* give user a chance to follow progress */
                    g_usleep (G_USEC_PER_SEC / 100);
                }

                /* now, do read the fresh data */
                num = read_fresh_tle (dir, fnam, data);
            } else {
                num = 0;
            }

            if (num < 1) {
                sat_log_log (SAT_LOG_LEVEL_ERROR,
                             _("%s: No valid TLE data found in %s"),
                             __FUNCTION__, fnam);
            }
            else {
                sat_log_log (SAT_LOG_LEVEL_INFO,
                             _("%s: Read %d sats from %s into memory"),
                             __FUNCTION__, num, fnam);
            }
        }

        /* close directory since we don't need it anymore */
        g_dir_close (cache_dir);

        /* now we load each .sat file and update if we have new data */
        userconfdir = get_user_conf_dir ();
        ldname = g_strconcat (userconfdir, G_DIR_SEPARATOR_S, "satdata", NULL);
        g_free (userconfdir);

        /* open directory and read files one by one */
        loc_dir = g_dir_open (ldname, 0, &err);

        if (err != NULL) {

            /* send an error message */
            sat_log_log (SAT_LOG_LEVEL_ERROR,
                         _("%s: Error opening directory %s (%s)"),
                         __FUNCTION__, dir, err->message);

            /* insert error message into the status string, too */
            if (!silent && (label1 != NULL)) {
                text = g_strdup_printf (_("<b>ERROR</b> opening directory %s\n%s"),
                                        dir, err->message);

                gtk_label_set_markup (GTK_LABEL (label1), text);
                g_free (text);
            }

            g_clear_error (&err);
            err = NULL;
        }
        else {
            /* clear statistics */
            updated = 0;
            skipped = 0;
            nodata = 0;
            total = 0;

            /* get initial value of progress indicator */
            if (progress != NULL)
                start = gtk_progress_bar_get_fraction (GTK_PROGRESS_BAR (progress));

            /* This is insane but I don't know how else to count the number of sats */
            num = 0;
            while ((fnam = g_dir_read_name (loc_dir)) != NULL) {
                /* only consider .sat files */
                if (g_str_has_suffix (fnam, ".sat")) {
                    num++;
                }
            }

            g_dir_rewind (loc_dir);

            /* update TLE files one by one */
            while ((fnam = g_dir_read_name (loc_dir)) != NULL) {
                /* only consider .sat files */
                if (g_str_has_suffix (fnam, ".sat")) {

                    /* clear stat bufs */
                    updated_tmp = 0;
                    skipped_tmp = 0;
                    nodata_tmp = 0;
                    total_tmp = 0;

                    /* update TLE data in this file */
                    update_tle_in_file (ldname, fnam, data, 
                                        &updated_tmp,
                                        &skipped_tmp,
                                        &nodata_tmp,
                                        &total_tmp);

                    /* update statistics */
                    updated += updated_tmp;
                    skipped += skipped_tmp;
                    nodata  += nodata_tmp;
                    total   = updated+skipped+nodata;

                    if (!silent) {

                        if (label1 != NULL) {
                            gtk_label_set_text (GTK_LABEL (label1),
                                                _("Updating data..."));
                        }

                        if (label2 != NULL) {
                            text = g_strdup_printf (_("Satellites updated:\t %d\n"\
                                                      "Satellites skipped:\t %d\n"\
                                                      "Missing Satellites:\t %d\n"),
                                                    updated, skipped, nodata);
                            gtk_label_set_text (GTK_LABEL (label2), text);
                            g_free (text);
                        }

                        if (progress != NULL) {
                            /* two different calculations for completeness depending on whether 
                               we are adding new satellites or not. */
                            if (sat_cfg_get_bool (SAT_CFG_BOOL_TLE_ADD_NEW)) {
                                /* In this case we are possibly processing more than num satellites
                                   How many more? We do not know yet.  Worst case is g_hash_table_size more.
                                   
                                   As we update skipped and updated we can reduce the denominator count
                                   as those are in both pools (files and hash table). When we have processed 
                                   all the files, updated and skipped are completely correct and the progress 
                                   is correct. It may be correct sooner if the missed satellites are the 
                                   last files to process.
                                   
                                   Until then, if we eliminate the ones that are updated and skipped from being 
                                   double counted, our progress will shown will always be less or equal to our 
                                   true progress since the denominator will be larger than is correct.
                                   
                                   Advantages to this are that the progress bar does not stall close to 
                                   finished when there are a large number of new satellites.
                                */
                                fraction = start + (1.0-start) * ((gdouble) total) / 
                                    ((gdouble) num + g_hash_table_size(data) - updated - skipped);
                            } else {
                                /* here we only process satellites we have have files for so divide by num */
                                fraction = start + (1.0-start) * ((gdouble) total) / ((gdouble) num);
                            }
                            gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress),
                                                           fraction);

                        }

                        /* update the gui only every so often to speed up the process */
                        /* 47 was selected empirically to balance the update looking smooth but not take too much time. */
                        /* it also tumbles all digits in the numbers so that there is no obvious pattern. */
                        /* on a developer machine this improved an update from 5 minutes to under 20 seconds. */
                        if (total%47 == 0) {
                            /* Force the drawing queue to be processed otherwise there will
                               not be any visual feedback, ie. frozen GUI
                               - see Gtk+ FAQ http://www.gtk.org/faq/#AEN602
                            */
                            while (g_main_context_iteration (NULL, FALSE));
                        
                            /* give user a chance to follow progress */
                            g_usleep (G_USEC_PER_SEC / 1000);
                        }
                    }
                }
            }
            
            /* force gui update */
            while (g_main_context_iteration (NULL, FALSE));


            /* close directory handle */
            g_dir_close (loc_dir);
            
            /* see if we have any new sats that need to be added */
            if (sat_cfg_get_bool (SAT_CFG_BOOL_TLE_ADD_NEW)) {
                
                newsats = add_new_sats (data);
                
                if (!silent && (label2 != NULL)) {
                    text = g_strdup_printf (_("Satellites updated:\t %d\n"\
                                              "Satellites skipped:\t %d\n"\
                                              "Missing Satellites:\t %d\n"\
                                              "New Satellites:\t\t %d"),
                                            updated, skipped, nodata, newsats);
                    gtk_label_set_text (GTK_LABEL (label2), text);
                    g_free (text);

                }
                
                sat_log_log (SAT_LOG_LEVEL_INFO,
                             _("%s: Added %d new satellites to local database"),
                             __FUNCTION__, newsats);
            }

            /* store time of update if we have updated something */
            if ((updated > 0) || (newsats > 0)) {
                GTimeVal tval;
                
                g_get_current_time (&tval);
                sat_cfg_set_int (SAT_CFG_INT_TLE_LAST_UPDATE, tval.tv_sec);
            }

        }

        g_free (ldname);

        sat_log_log (SAT_LOG_LEVEL_INFO,
                     _("%s: TLE elements updated."),
                     __FUNCTION__);
    }

    /* destroy hash tables */
    g_hash_table_destroy (data);

    g_mutex_unlock(&tle_file_in_progress);
}
示例#8
0
/** \brief Update TLE files from network.
 *  \param silent TRUE if function should execute without graphical status indicator.
 *  \param progress Pointer to a GtkProgressBar progress indicator (can be NULL)
 *  \param label1 GtkLabel for activity string.
 *  \param label2 GtkLabel for statistics string.
 */
void tle_update_from_network (gboolean   silent,
                              GtkWidget *progress,
                              GtkWidget *label1,
                              GtkWidget *label2)
{
    static GMutex tle_in_progress;

    gchar       *server;
    gchar       *proxy = NULL;
    gchar       *files_tmp;
    gchar      **files;
    guint        numfiles,i;
    gchar       *curfile;
    gchar       *locfile;
    gchar       *userconfdir;
    CURL        *curl;
    CURLcode     res;
    gdouble      fraction,start=0;
    FILE        *outfile;
    GDir        *dir;
    gchar       *cache;
    const gchar *fname;
    gchar       *text;
    GError      *err = NULL;
    guint        success = 0; /* no. of successfull downloads */ 

    /* bail out if we are already in an update process */
    if (g_mutex_trylock(&tle_in_progress) == FALSE)
    {
        sat_log_log (SAT_LOG_LEVEL_ERROR,
                     _("%s: A TLE update process is already running. Aborting."),
                     __FUNCTION__);

        return;
    }

    /* get server, proxy, and list of files */
    server = sat_cfg_get_str (SAT_CFG_STR_TLE_SERVER);
    proxy  = sat_cfg_get_str (SAT_CFG_STR_TLE_PROXY);
    files_tmp = sat_cfg_get_str (SAT_CFG_STR_TLE_FILES);
    files = g_strsplit (files_tmp, ";", 0);
    numfiles = g_strv_length (files);

    if (numfiles < 1) {
        sat_log_log (SAT_LOG_LEVEL_ERROR,
                     _("%s: No files to fetch from network."),
                     __FUNCTION__);

        /* set activity string, so user knows why nothing happens */
        if (!silent && (label1 != NULL)) {
            gtk_label_set_text (GTK_LABEL (label1),
                                _("No files to fetch from network"));
        }
    }
    else {

        /* initialise progress bar */
        if (!silent && (progress != NULL))
            start = gtk_progress_bar_get_fraction (GTK_PROGRESS_BAR (progress));

        /* initialise curl */
        curl = curl_easy_init();
        if (proxy != NULL)
            curl_easy_setopt (curl, CURLOPT_PROXY, proxy);

        curl_easy_setopt (curl, CURLOPT_USERAGENT, "gpredict/curl");
        curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, 10);

        /* get files */
        for (i = 0; i < numfiles; i++) {

            /* set URL */
            curfile = g_strconcat (server, files[i], NULL);
            curl_easy_setopt (curl, CURLOPT_URL, curfile);

            /* set activity message */
            if (!silent && (label1 != NULL)) {

                text = g_strdup_printf (_("Fetching %s"), files[i]);
                gtk_label_set_text (GTK_LABEL (label1), text);
                g_free (text);

                /* Force the drawing queue to be processed otherwise there will
                    not be any visual feedback, ie. frozen GUI
                    - see Gtk+ FAQ http://www.gtk.org/faq/#AEN602
                */
                while (g_main_context_iteration (NULL, FALSE));
            }

            /* create local cache file */
            userconfdir = get_user_conf_dir ();
            locfile = g_strconcat (userconfdir, G_DIR_SEPARATOR_S,
                                   "satdata", G_DIR_SEPARATOR_S,
                                   "cache", G_DIR_SEPARATOR_S,
                                   files[i], NULL);
            outfile = g_fopen (locfile, "wb");
            if (outfile != NULL) {
                curl_easy_setopt (curl, CURLOPT_WRITEDATA, outfile);
                curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, my_write_func);
                
                /* get file */
                res = curl_easy_perform (curl);
                
                if (res != CURLE_OK) {
                    sat_log_log (SAT_LOG_LEVEL_ERROR,
                                 _("%s: Error fetching %s (%s)"),
                                 __FUNCTION__, curfile, curl_easy_strerror (res));
                }
                else {
                    sat_log_log (SAT_LOG_LEVEL_INFO,
                                 _("%s: Successfully fetched %s"),
                                 __FUNCTION__, curfile);
                    success++;
                }
                fclose (outfile);

            } else {
                sat_log_log (SAT_LOG_LEVEL_INFO,
                             _("%s: Failed to open %s preventing update"),
                                         __FUNCTION__, locfile);
            }
            /* update progress indicator */
            if (!silent && (progress != NULL)) {

                /* complete download corresponds to 50% */
                fraction = start + (0.5-start) * i / (1.0 * numfiles);
                gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress), fraction);

                /* Force the drawing queue to be processed otherwise there will
                    not be any visual feedback, ie. frozen GUI
                    - see Gtk+ FAQ http://www.gtk.org/faq/#AEN602
                */
                while (g_main_context_iteration (NULL, FALSE));

            }

            g_free (userconfdir);
            g_free (curfile);
            g_free (locfile);
        }

        curl_easy_cleanup (curl);

        /* continue update if we have fetched at least one file */
        if (success > 0) {
            sat_log_log (SAT_LOG_LEVEL_INFO,
                         _("%s: Fetched %d files from network; updating..."),
                         __FUNCTION__, success);
            /* call update_from_files */
            cache = sat_file_name ("cache");
            tle_update_from_files (cache, NULL, silent, progress, label1, label2);
            g_free (cache);

        }
        else {
            sat_log_log (SAT_LOG_LEVEL_ERROR,
                         _("%s: Could not fetch any new TLE files from network; aborting..."),
                         __FUNCTION__);
        }

    }

    /* clear cache and memory */
    g_free (server);
    g_strfreev (files);
    g_free (files_tmp);
    if (proxy != NULL)
        g_free (proxy);

    /* open cache */
    cache = sat_file_name ("cache");
    dir = g_dir_open (cache, 0, &err);

    if (err != NULL) {
        /* send an error message */
        sat_log_log (SAT_LOG_LEVEL_ERROR,
                     _("%s: Error opening %s (%s)"),
                     __FUNCTION__, dir, err->message);
        g_clear_error (&err);
    }
    else {
        /* delete files in cache one by one */
        while ((fname = g_dir_read_name (dir)) != NULL) {

            locfile = g_strconcat (cache, G_DIR_SEPARATOR_S,
                                   fname, NULL);

            g_remove (locfile);
            g_free (locfile);
        }
        /* close cache */
        g_dir_close (dir);
    }

    g_free (cache);

    g_mutex_unlock(&tle_in_progress);

}
示例#9
0
/**
 * Update TRSP files from network.
 *
 * @param silent TRUE if function should execute without graphical status indicator.
 * @param progress Pointer to a GtkProgressBar progress indicator (can be NULL)
 * @param label1 GtkLabel for activity string.
 * @param label2 GtkLabel for statistics string.
 */
void trsp_update_from_network(gboolean silent,
                              GtkWidget * progress,
                              GtkWidget * label1, GtkWidget * label2)
{
    static GMutex   trsp_in_progress;

    gchar          *server;
    gchar          *proxy = NULL;
    gchar          *freq_file;
    gchar          *file_url;
    gchar          *locfile_trsp;
    gchar          *userconfdir;
    CURL           *curl;
    CURLcode        res;
    gdouble         fraction;
    FILE           *outfile;
    gchar          *cache;
    gchar          *text;

    guint           success = 0;        /* no. of successfull downloads */

    /* bail out if we are already in an update process */
    if (g_mutex_trylock(&trsp_in_progress) == FALSE)
    {
        sat_log_log(SAT_LOG_LEVEL_ERROR,
                    _("%s: A frequency update process is already running"),
                    __func__);
        return;
    }

    /* get list with modes */
    modes_update_from_network();

    /* get server, proxy, and list of files */
    //server = sprintf("%stransmitters/?format=json", sat_cfg_get_str(SAT_CFG_STR_TRSP_SERVER));
    server = sat_cfg_get_str(SAT_CFG_STR_TRSP_SERVER);
    proxy = sat_cfg_get_str(SAT_CFG_STR_TRSP_PROXY);
    freq_file = sat_cfg_get_str(SAT_CFG_STR_TRSP_FREQ_FILE);

    /* initialise curl */
    curl = curl_easy_init();
    if (proxy != NULL)
        curl_easy_setopt(curl, CURLOPT_PROXY, proxy);

    curl_easy_setopt(curl, CURLOPT_USERAGENT, "gpredict/curl");
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10);

    /* get files */
    /* set URL */
    file_url = g_strconcat(server, freq_file, NULL);
    curl_easy_setopt(curl, CURLOPT_URL, file_url);
    sat_log_log(SAT_LOG_LEVEL_INFO,
                _("%s: Ready to fetch transponder list from %s"),
                __func__, file_url);

    /* set activity message */
    if (!silent && (label1 != NULL))
    {

        text = g_strdup_printf(_("Fetching %s"), "transmitters.json");
        gtk_label_set_text(GTK_LABEL(label1), text);
        g_free(text);

        /* Force the drawing queue to be processed otherwise there will
           not be any visual feedback, ie. frozen GUI
           - see Gtk+ FAQ http://www.gtk.org/faq/#AEN602
         */
        while (g_main_context_iteration(NULL, FALSE));
    }

    /* create local cache file */
    userconfdir = get_user_conf_dir();

    locfile_trsp = g_strconcat(userconfdir, G_DIR_SEPARATOR_S, "trsp",
                               G_DIR_SEPARATOR_S, "transmitters.json", NULL);
    sat_log_log(SAT_LOG_LEVEL_INFO, _("%s: File to open %s "), __func__,
                locfile_trsp);

    outfile = g_fopen(locfile_trsp, "wb");
    if (outfile != NULL)
    {
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_write_func);

        /* get file */
        res = curl_easy_perform(curl);

        if (res != CURLE_OK)
        {
            sat_log_log(SAT_LOG_LEVEL_ERROR, _("%s: Error fetching %s (%s)"),
                        __func__, file_url, curl_easy_strerror(res));
        }
        else
        {
            sat_log_log(SAT_LOG_LEVEL_INFO, _("%s: Successfully fetched %s"),
                        __func__, file_url);
            success++;
        }
        fclose(outfile);

    }
    else
    {
        sat_log_log(SAT_LOG_LEVEL_INFO,
                    _("%s: Failed to open %s preventing update"),
                    __func__, locfile_trsp);
    }

    /* update progress indicator */
    if (!silent && (progress != NULL))
    {
        /* complete download corresponds to 50% */
        fraction = 1;
        gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), fraction);

        /* Force the drawing queue to be processed otherwise there will
           not be any visual feedback, ie. frozen GUI
           - see Gtk+ FAQ http://www.gtk.org/faq/#AEN602
         */
        while (g_main_context_iteration(NULL, FALSE));

    }

    g_free(userconfdir);
    g_free(file_url);
    g_free(server);
    g_free(freq_file);
    g_free(proxy);

    curl_easy_cleanup(curl);

    /* continue update if we have fetched at least one file */
    if (success > 0)
    {
        sat_log_log(SAT_LOG_LEVEL_INFO,
                    _("%s: Fetched %d files from network; updating..."),
                    __func__, success);
        /* call update_from_files */
        cache = sat_file_name("cache");
        trsp_update_files(locfile_trsp);
        g_free(cache);
    }
    else
    {
        sat_log_log(SAT_LOG_LEVEL_ERROR,
                    _("%s: Could not fetch frequency files from network"),
                    __func__);
    }

    g_mutex_unlock(&trsp_in_progress);
}
示例#10
0
/** Update MODES files from network. */
void modes_update_from_network()
{
    gchar          *server;
    gchar          *proxy = NULL;
    gchar          *modes_file;
    gchar          *file_url;
    gchar          *locfile;
    gchar          *userconfdir;
    CURL           *curl;
    CURLcode        res;
    FILE           *outfile;
    guint           success = 0;        /* no. of successfull downloads */

    server = sat_cfg_get_str(SAT_CFG_STR_TRSP_SERVER);
    proxy = sat_cfg_get_str(SAT_CFG_STR_TRSP_PROXY);
    modes_file = sat_cfg_get_str(SAT_CFG_STR_TRSP_MODE_FILE);

    /* initialise curl */
    curl = curl_easy_init();
    if (proxy != NULL)
        curl_easy_setopt(curl, CURLOPT_PROXY, proxy);

    curl_easy_setopt(curl, CURLOPT_USERAGENT, "gpredict/curl");
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10);

    /* get files */
    /* set URL */
    file_url = g_strconcat(server, modes_file, NULL);
    curl_easy_setopt(curl, CURLOPT_URL, file_url);
    sat_log_log(SAT_LOG_LEVEL_INFO,
                _("%s: Ready to fetch modes list from %s"),
                __func__, file_url);

    /* create local cache file */
    userconfdir = get_user_conf_dir();
    locfile = g_strconcat(userconfdir, G_DIR_SEPARATOR_S, "trsp",
                          G_DIR_SEPARATOR_S, "modes.json", NULL);
    sat_log_log(SAT_LOG_LEVEL_INFO, _("%s: File to open %s "), __func__,
                locfile);

    outfile = g_fopen(locfile, "wb");
    if (outfile != NULL)
    {
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_write_func);

        /* get file */
        res = curl_easy_perform(curl);

        if (res != CURLE_OK)
        {
            sat_log_log(SAT_LOG_LEVEL_ERROR, _("%s: Error fetching %s (%s)"),
                        __func__, file_url, curl_easy_strerror(res));
        }
        else
        {
            sat_log_log(SAT_LOG_LEVEL_INFO, _("%s: Successfully fetched %s"),
                        __func__, file_url);
            success++;
        }
        fclose(outfile);

    }
    else
    {
        sat_log_log(SAT_LOG_LEVEL_INFO,
                    _("%s: Failed to open %s preventing update"),
                    __func__, locfile);
    }

    g_free(userconfdir);
    g_free(file_url);
    g_free(server);
    g_free(proxy);
    g_free(modes_file);

    curl_easy_cleanup(curl);

    /* continue update if we have fetched at least one file */
    if (success > 0)
    {
        sat_log_log(SAT_LOG_LEVEL_INFO,
                    _("%s: Fetched %d files from network; updating..."),
                    __func__, success);
        //here we can get the modes json file into memory as hashmap..
        //g_free(cache);

    }
    else
    {
        sat_log_log(SAT_LOG_LEVEL_ERROR,
                    _("%s: Could not fetch frequency files from network"),
                    __func__);
    }
}
示例#11
0
void trsp_update_files(gchar * input_file)
{

    FILE           *mfp;        /* transmitter information json file */
    unsigned int    mfplen;     /* size of transmitter information json file */
    int             result;
    FILE           *ffile;      /* transponder output file in gpredict format */
    char           *jsn_object; /* json array will be in this buffer before parsing */
    unsigned int    idx;	/* object index in JSON-Array */
    new_mode_t     *nmode;
    new_trsp_t     *ntrsp;
    GHashTable     *modes_hash; /* hash table to store modes */
    GHashTable     *trsp_hash;  /* hash table to store satellite list to generate trsp files */
    guint          *key = NULL;

    gchar          *userconfdir;
    gchar          *trspfile;
    gchar          *modesfile;
    gchar          *trspfolder;

    /* force decimal mark to dot when parsing JSON file */
    setlocale(LC_NUMERIC, "C");

    modes_hash =
        g_hash_table_new_full(g_int_hash, g_int_equal, g_free, free_new_mode);
    trsp_hash =
        g_hash_table_new_full(g_int_hash, g_int_equal, g_free, free_new_trsp);

    userconfdir = get_user_conf_dir();
    trspfolder = g_strconcat(userconfdir, G_DIR_SEPARATOR_S, "trsp", NULL);
    modesfile = g_strconcat(trspfolder, "/modes.json", NULL);

    mfp = fopen(modesfile, "r");

    if (mfp != NULL)
    {
        mfplen = 0;
        fseek(mfp, 0, SEEK_END);
        mfplen = ftell(mfp);
        rewind(mfp);
        jsn_object = g_malloc(mfplen);
        result = fread(jsn_object, mfplen, 1, mfp);

        if (result == 1)
        {
            const nx_json  *json = nx_json_parse(jsn_object, 0);

            if (json)
            {
                idx = 0;
                while (1)
                {
                    const nx_json  *json_obj = nx_json_item(json, idx++);
                    struct modes    m_modes;

                    if (json_obj->type == NX_JSON_NULL)
                        break;

                    m_modes.id = nx_json_get(json_obj, "id")->int_value;
                    strncpy(m_modes.name,
                            nx_json_get(json_obj, "name")->text_value, 79);
                    m_modes.name[79] = 0;

                    /* add data to hash table */
                    key = g_try_new0(guint, 1);
                    *key = m_modes.id;

                    nmode = g_hash_table_lookup(modes_hash, key);

                    if (nmode == NULL)
                    {
                        /* create new_mode structure */
                        nmode = g_try_new(new_mode_t, 1);
                        nmode->modnum = m_modes.id;
                        nmode->modname = g_strdup(m_modes.name);

                        g_hash_table_insert(modes_hash, key, nmode);

                    }

                    sat_log_log(SAT_LOG_LEVEL_INFO, _("MODE %d %s"),
                                m_modes.id, m_modes.name);
                }               // while(1)
                nx_json_free(json);
            }                   // if(json)
        }                       // if(result == 1)
        g_free(jsn_object);
        fclose(mfp);
    }                           // if(mfp)

//    guint num = 0;
//    printf("---------- PRINTING MODES LIST ------- \n");
//    g_hash_table_foreach (modes_hash, check_and_print_mode, &num);

    mfp = fopen(input_file, "r");
    if (mfp != NULL)
    {
        mfplen = 0;
        fseek(mfp, 0, SEEK_END);
        mfplen = ftell(mfp);
        rewind(mfp);
        jsn_object = g_malloc(mfplen);
        result = fread(jsn_object, mfplen, 1, mfp);

        if (result == 1)
        {
            const nx_json  *json = nx_json_parse(jsn_object, 0);

            if (json)
            {
                idx = 0;
                while (1)
                {
                    const nx_json  *json_obj = nx_json_item(json, idx++);
                    struct transponder m_trsp;

                    if (json_obj->type == NX_JSON_NULL)
                        break;

                    strncpy(m_trsp.description,
                            nx_json_get(json_obj, "description")->text_value, 79);
                    m_trsp.description[79] = 0;
                    m_trsp.catnum =
                        nx_json_get(json_obj, "norad_cat_id")->int_value;
                    m_trsp.uplink_low =
                        nx_json_get(json_obj, "uplink_low")->int_value;
                    m_trsp.uplink_high =
                        nx_json_get(json_obj, "uplink_high")->int_value;
                    m_trsp.downlink_low =
                        nx_json_get(json_obj, "downlink_low")->int_value;
                    m_trsp.downlink_high =
                        nx_json_get(json_obj, "downlink_high")->int_value;

                    key = g_try_new0(guint, 1);
                    *key = nx_json_get(json_obj, "mode_id")->int_value;
                    nmode = g_hash_table_lookup(modes_hash, key);
                    if (nmode != NULL)
                        sprintf(m_trsp.mode, "%s", nmode->modname);
                    else
                        sprintf(m_trsp.mode, "%lli",
                                nx_json_get(json_obj, "mode_id")->int_value);

                    m_trsp.invert = nx_json_get(json_obj, "invert")->int_value;
                    m_trsp.baud = nx_json_get(json_obj, "baud")->dbl_value;
                    m_trsp.alive = nx_json_get(json_obj, "alive")->int_value;

                    //strcpy(m_trsp.uuid,nx_json_get(json_obj,          "uuid")->text_value);
                    //sat_log_log(SAT_LOG_LEVEL_DEBUG, _(">>> Preparing information for transponders of cat_id %d <<<"), m_trsp.catnum, __func__);
                    ////sat_log_log (SAT_LOG_LEVEL_INFO, _("         uuid : %s"), m_trsp.uuid, __func__);
                    //sat_log_log(SAT_LOG_LEVEL_DEBUG, _("  description : %s"), m_trsp.description, __func__);
                    //sat_log_log(SAT_LOG_LEVEL_DEBUG, _("        alive : %s"), m_trsp.alive ? "true" : "false", __func__);
                    //sat_log_log(SAT_LOG_LEVEL_DEBUG, _("   uplink_low : %Ld"),m_trsp.uplink_low, __func__);
                    //sat_log_log(SAT_LOG_LEVEL_DEBUG, _("  uplink_high : %Ld"),m_trsp.uplink_high, __func__);
                    //sat_log_log(SAT_LOG_LEVEL_DEBUG, _(" downink_low  : %Ld"),m_trsp.downlink_low, __func__);
                    //sat_log_log(SAT_LOG_LEVEL_DEBUG, _("downlink_high : %Ld"),m_trsp.downlink_high, __func__);
                    //sat_log_log(SAT_LOG_LEVEL_DEBUG, _("         Mode : %s"), m_trsp.mode, __func__);
                    //sat_log_log(SAT_LOG_LEVEL_DEBUG, _("       Invert : %s"), m_trsp.invert ? "true" : "false", __func__);
                    // sat_log_log(SAT_LOG_LEVEL_DEBUG, _("         Baud : %lf"),m_trsp.baud, __func__);
                    //sat_log_log(SAT_LOG_LEVEL_DEBUG, _(" norad_cat_id : %Ld"),m_trsp.catnum, __func__);
                    char            m_catnum[20];

                    sprintf(m_catnum, "%d", m_trsp.catnum);
                    trspfile = g_strconcat(trspfolder, G_DIR_SEPARATOR_S,
                                           m_catnum, ".trsp", NULL);
                    //sat_log_log(SAT_LOG_LEVEL_DEBUG, _("%s: Writing to file : %s "), __func__, trspfile);

                    //first lets delete the file we already have, to make space for the new version
                    /* check existence and add data to hash table for future check */
                    key = g_try_new0(guint, 1);
                    *key = m_trsp.catnum;
                    ntrsp = g_hash_table_lookup(trsp_hash, key);

                    if (ntrsp == NULL)
                    {
                        /* create new_trsp structure */
                        ntrsp = g_try_new(new_trsp_t, 1);
                        ntrsp->catnum = m_trsp.catnum;
                        ntrsp->numtrsp = 1;     //our first insertion of transponder to this file
                        g_hash_table_insert(trsp_hash, key, ntrsp);
                        g_remove(trspfile);

                    }
                    else
                    {
                        //TODO: increase number of transponders here
                        //ntrsp->numtrsp += 1; //number of transponder info in this file
                        //g_hash_table_replace(trsp_hash, key, ntrsp);
                    }

                    //now lets write the new version
                    ffile = g_fopen(trspfile, "a");
                    if (ffile != NULL)
                    {
                        fprintf(ffile, "\n[%s]\n", m_trsp.description);
                        if (m_trsp.uplink_low > 0)
                            fprintf(ffile, "UP_LOW=%lld\n",
                                    m_trsp.uplink_low);
                        if (m_trsp.uplink_high > 0)
                            fprintf(ffile, "UP_HIGH=%lld\n",
                                    m_trsp.uplink_high);
                        if (m_trsp.downlink_low > 0)
                            fprintf(ffile, "DOWN_LOW=%lld\n",
                                    m_trsp.downlink_low);
                        if (m_trsp.downlink_high > 0)
                            fprintf(ffile, "DOWN_HIGH=%lld\n",
                                    m_trsp.downlink_high);
                        fprintf(ffile, "MODE=%s\n",
                                m_trsp.mode);
                        if (m_trsp.baud > 0.0)
                            fprintf(ffile, "BAUD=%.0f\n",
                                    m_trsp.baud);
                        if (m_trsp.invert)
                            fprintf(ffile, "INVERT=%s\n",
                                    "true");
                        fclose(ffile);
                    }
                    else
                    {
                        sat_log_log(SAT_LOG_LEVEL_ERROR,
                                    _
                                    ("%s: Could not open trsp file for write"),
                                    __func__);
                    }
                }               // while(1)
                nx_json_free(json);
            }                   // if(json)
        }                       // if(result == 1)
        g_free(jsn_object);
        fclose(mfp);
    }                           // if(mfp)

    g_hash_table_destroy(modes_hash);
}
示例#12
0
GtkWidget      *sat_pref_debug_create()
{
    GtkWidget      *vbox;       /* vbox containing the list part and the details part */
    GtkWidget      *hbox;
    GtkWidget      *rbut;
    GtkWidget      *label;
    GtkWidget      *butbox;
    gchar          *msg;
    gchar          *confdir;

    vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
    gtk_box_set_homogeneous(GTK_BOX(vbox), FALSE);
    gtk_container_set_border_width(GTK_CONTAINER(vbox), 20);
    gtk_box_set_spacing(GTK_BOX(vbox), 10);

    /* debug level */
    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
    gtk_box_set_homogeneous(GTK_BOX(hbox), FALSE);

    label = gtk_label_new(_("Debug level:"));
    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);

    level = gtk_combo_box_text_new();
    gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(level),
                                   _("Level 0: None"));
    gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(level),
                                   _("Level 1: Error"));
    gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(level),
                                   _("Level 2: Warning"));
    gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(level),
                                   _("Level 3: Info"));
    gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(level),
                                   _("Level 4: Debug"));
    gtk_combo_box_set_active(GTK_COMBO_BOX(level),
                             sat_cfg_get_int(SAT_CFG_INT_LOG_LEVEL));
    g_signal_connect(G_OBJECT(level), "realize",
                     G_CALLBACK(gpredict_set_combo_tooltips),
                     _("Select the debug level. The higher the level, "
                       "the more messages will be logged."));
    g_signal_connect(G_OBJECT(level), "changed", G_CALLBACK(state_change_cb),
                     NULL);

    gtk_box_pack_start(GTK_BOX(hbox), level, FALSE, FALSE, 10);
    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);

    /* clean frequency */
    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
    gtk_box_set_homogeneous(GTK_BOX(hbox), FALSE);

    label = gtk_label_new(_("Delete log files older than:"));
    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);

    age = gtk_combo_box_text_new();
    gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(age),
                                   _("Always delete"));
    gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(age), _("1 day"));
    gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(age), _("1 week"));
    gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(age), _("1 month"));
    select_age();
    g_signal_connect(G_OBJECT(age), "realize",
                     G_CALLBACK(gpredict_set_combo_tooltips),
                     _("Select how often gpredict should delete "
                       "old log files."));
    g_signal_connect(G_OBJECT(age), "changed", G_CALLBACK(state_change_cb),
                     NULL);
    gtk_box_pack_start(GTK_BOX(hbox), age, FALSE, FALSE, 10);
    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);

    /* separator */
    gtk_box_pack_start(GTK_BOX(vbox),
                       gtk_separator_new(GTK_ORIENTATION_HORIZONTAL),
                       FALSE, FALSE, 0);

    /* info label */
    confdir = get_user_conf_dir();
    msg =
        g_strdup_printf(_
                        ("Gpredict stores all run-time messages in the %s%slogs%s\n"
                         "directory. The current log file is called gpredict.log and the file is\n"
                         "always kept until the next execution, so that you can examine it in case\n"
                         "of a failure. If old log files are kept, they are called gpredict-XYZ.log\n"
                         "where XYZ is a unique timestamp."), confdir,
                        G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S);
    label = gtk_label_new(msg);
    gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
    gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
    g_free(msg);

    /* reset button */
    rbut = gtk_button_new_with_label(_("Reset"));
    gtk_widget_set_tooltip_text(rbut,
                                _("Reset settings to the default values."));
    g_signal_connect(G_OBJECT(rbut), "clicked", G_CALLBACK(reset_cb), NULL);
    butbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
    gtk_button_box_set_layout(GTK_BUTTON_BOX(butbox), GTK_BUTTONBOX_END);
    gtk_box_pack_end(GTK_BOX(butbox), rbut, FALSE, TRUE, 10);

    gtk_box_pack_end(GTK_BOX(vbox), butbox, FALSE, TRUE, 0);

    return vbox;
}