328 lines
8.4 KiB
C
328 lines
8.4 KiB
C
/*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) version 3.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with the program; if not, see <http://www.gnu.org/licenses/>
|
|
*
|
|
*
|
|
* Authors:
|
|
* Srinivasa Ragavan <sragavan@gnome.org>
|
|
*
|
|
* Copyright (C) 2012 Intel Corporation (www.intel.com)
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include <locale.h>
|
|
|
|
#include "evolution-util.h"
|
|
|
|
/**
|
|
* e_flexible_strtod:
|
|
* @nptr: the string to convert to a numeric value.
|
|
* @endptr: if non-NULL, it returns the character after
|
|
* the last character used in the conversion.
|
|
*
|
|
* Converts a string to a gdouble value. This function detects
|
|
* strings either in the standard C locale or in the current locale.
|
|
*
|
|
* This function is typically used when reading configuration files or
|
|
* other non-user input that should not be locale dependent, but may
|
|
* have been in the past. To handle input from the user you should
|
|
* normally use the locale-sensitive system strtod function.
|
|
*
|
|
* To convert from a double to a string in a locale-insensitive way, use
|
|
* @g_ascii_dtostr.
|
|
*
|
|
* Returns: the gdouble value
|
|
**/
|
|
|
|
gdouble
|
|
e_flexible_strtod (const gchar *nptr,
|
|
gchar **endptr)
|
|
{
|
|
gchar *fail_pos;
|
|
gdouble val;
|
|
struct lconv *locale_data;
|
|
const gchar *decimal_point;
|
|
gint decimal_point_len;
|
|
const gchar *p, *decimal_point_pos;
|
|
const gchar *end = NULL; /* Silence gcc */
|
|
gchar *copy, *c;
|
|
|
|
g_return_val_if_fail (nptr != NULL, 0);
|
|
|
|
fail_pos = NULL;
|
|
|
|
locale_data = localeconv ();
|
|
decimal_point = locale_data->decimal_point;
|
|
decimal_point_len = strlen (decimal_point);
|
|
|
|
g_return_val_if_fail (decimal_point_len != 0, 0);
|
|
|
|
decimal_point_pos = NULL;
|
|
if (!strcmp (decimal_point, "."))
|
|
return strtod (nptr, endptr);
|
|
|
|
p = nptr;
|
|
|
|
/* Skip leading space */
|
|
while (isspace ((guchar) * p))
|
|
p++;
|
|
|
|
/* Skip leading optional sign */
|
|
if (*p == '+' || *p == '-')
|
|
p++;
|
|
|
|
if (p[0] == '0' &&
|
|
(p[1] == 'x' || p[1] == 'X')) {
|
|
p += 2;
|
|
/* HEX - find the (optional) decimal point */
|
|
|
|
while (isxdigit ((guchar) * p))
|
|
p++;
|
|
|
|
if (*p == '.') {
|
|
decimal_point_pos = p++;
|
|
|
|
while (isxdigit ((guchar) * p))
|
|
p++;
|
|
|
|
if (*p == 'p' || *p == 'P')
|
|
p++;
|
|
if (*p == '+' || *p == '-')
|
|
p++;
|
|
while (isdigit ((guchar) * p))
|
|
p++;
|
|
end = p;
|
|
} else if (strncmp (p, decimal_point, decimal_point_len) == 0) {
|
|
return strtod (nptr, endptr);
|
|
}
|
|
} else {
|
|
while (isdigit ((guchar) * p))
|
|
p++;
|
|
|
|
if (*p == '.') {
|
|
decimal_point_pos = p++;
|
|
|
|
while (isdigit ((guchar) * p))
|
|
p++;
|
|
|
|
if (*p == 'e' || *p == 'E')
|
|
p++;
|
|
if (*p == '+' || *p == '-')
|
|
p++;
|
|
while (isdigit ((guchar) * p))
|
|
p++;
|
|
end = p;
|
|
} else if (strncmp (p, decimal_point, decimal_point_len) == 0) {
|
|
return strtod (nptr, endptr);
|
|
}
|
|
}
|
|
/* For the other cases, we need not convert the decimal point */
|
|
|
|
if (!decimal_point_pos)
|
|
return strtod (nptr, endptr);
|
|
|
|
/* We need to convert the '.' to the locale specific decimal point */
|
|
copy = g_malloc (end - nptr + 1 + decimal_point_len);
|
|
|
|
c = copy;
|
|
memcpy (c, nptr, decimal_point_pos - nptr);
|
|
c += decimal_point_pos - nptr;
|
|
memcpy (c, decimal_point, decimal_point_len);
|
|
c += decimal_point_len;
|
|
memcpy (c, decimal_point_pos + 1, end - (decimal_point_pos + 1));
|
|
c += end - (decimal_point_pos + 1);
|
|
*c = 0;
|
|
|
|
val = strtod (copy, &fail_pos);
|
|
|
|
if (fail_pos) {
|
|
if (fail_pos > decimal_point_pos)
|
|
fail_pos =
|
|
(gchar *) nptr + (fail_pos - copy) -
|
|
(decimal_point_len - 1);
|
|
else
|
|
fail_pos = (gchar *) nptr + (fail_pos - copy);
|
|
}
|
|
|
|
g_free (copy);
|
|
|
|
if (endptr)
|
|
*endptr = fail_pos;
|
|
|
|
return val;
|
|
}
|
|
|
|
/**
|
|
* e_ascii_dtostr:
|
|
* @buffer: A buffer to place the resulting string in
|
|
* @buf_len: The length of the buffer.
|
|
* @format: The printf-style format to use for the
|
|
* code to use for converting.
|
|
* @d: The double to convert
|
|
*
|
|
* Converts a double to a string, using the '.' as
|
|
* decimal_point. To format the number you pass in
|
|
* a printf-style formating string. Allowed conversion
|
|
* specifiers are eEfFgG.
|
|
*
|
|
* If you want to generates enough precision that converting
|
|
* the string back using @g_strtod gives the same machine-number
|
|
* (on machines with IEEE compatible 64bit doubles) use the format
|
|
* string "%.17g". If you do this it is guaranteed that the size
|
|
* of the resulting string will never be larger than
|
|
* @G_ASCII_DTOSTR_BUF_SIZE bytes.
|
|
*
|
|
* Returns: the pointer to the buffer with the converted string
|
|
**/
|
|
gchar *
|
|
e_ascii_dtostr (gchar *buffer,
|
|
gint buf_len,
|
|
const gchar *format,
|
|
gdouble d)
|
|
{
|
|
struct lconv *locale_data;
|
|
const gchar *decimal_point;
|
|
gint decimal_point_len;
|
|
gchar *p;
|
|
gint rest_len;
|
|
gchar format_char;
|
|
|
|
g_return_val_if_fail (buffer != NULL, NULL);
|
|
g_return_val_if_fail (format[0] == '%', NULL);
|
|
g_return_val_if_fail (strpbrk (format + 1, "'l%") == NULL, NULL);
|
|
|
|
format_char = format[strlen (format) - 1];
|
|
|
|
g_return_val_if_fail (format_char == 'e' || format_char == 'E' ||
|
|
format_char == 'f' || format_char == 'F' ||
|
|
format_char == 'g' || format_char == 'G',
|
|
NULL);
|
|
|
|
if (format[0] != '%')
|
|
return NULL;
|
|
|
|
if (strpbrk (format + 1, "'l%"))
|
|
return NULL;
|
|
|
|
if (!(format_char == 'e' || format_char == 'E' ||
|
|
format_char == 'f' || format_char == 'F' ||
|
|
format_char == 'g' || format_char == 'G'))
|
|
return NULL;
|
|
|
|
g_snprintf (buffer, buf_len, format, d);
|
|
|
|
locale_data = localeconv ();
|
|
decimal_point = locale_data->decimal_point;
|
|
decimal_point_len = strlen (decimal_point);
|
|
|
|
g_return_val_if_fail (decimal_point_len != 0, NULL);
|
|
|
|
if (strcmp (decimal_point, ".")) {
|
|
p = buffer;
|
|
|
|
if (*p == '+' || *p == '-')
|
|
p++;
|
|
|
|
while (isdigit ((guchar) * p))
|
|
p++;
|
|
|
|
if (strncmp (p, decimal_point, decimal_point_len) == 0) {
|
|
*p = '.';
|
|
p++;
|
|
if (decimal_point_len > 1) {
|
|
rest_len = strlen (p + (decimal_point_len - 1));
|
|
memmove (
|
|
p, p + (decimal_point_len - 1),
|
|
rest_len);
|
|
p[rest_len] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
/**
|
|
* e_builder_get_widget:
|
|
* @builder: a #GtkBuilder
|
|
* @widget_name: name of a widget in @builder
|
|
*
|
|
* Gets the widget named @widget_name. Note that this function does not
|
|
* increment the reference count of the returned widget. If @widget_name
|
|
* could not be found in the @builder<!-- -->'s object tree, a run-time
|
|
* warning is emitted since this usually indicates a programming error.
|
|
*
|
|
* This is a convenience function to work around the awkwardness of
|
|
* #GtkBuilder returning #GObject pointers, when the vast majority of
|
|
* the time you want a #GtkWidget pointer.
|
|
*
|
|
* If you need something from @builder other than a #GtkWidget, or you
|
|
* want to test for the existence of some widget name without incurring
|
|
* a run-time warning, use gtk_builder_get_object().
|
|
*
|
|
* Returns: the widget named @widget_name, or %NULL
|
|
**/
|
|
GtkWidget *
|
|
e_builder_get_widget (GtkBuilder *builder,
|
|
const gchar *widget_name)
|
|
{
|
|
GObject *object;
|
|
|
|
g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL);
|
|
g_return_val_if_fail (widget_name != NULL, NULL);
|
|
|
|
object = gtk_builder_get_object (builder, widget_name);
|
|
if (object == NULL) {
|
|
g_warning ("Could not find widget '%s'", widget_name);
|
|
return NULL;
|
|
}
|
|
|
|
return GTK_WIDGET (object);
|
|
}
|
|
|
|
/**
|
|
* e_load_ui_builder_definition:
|
|
* @builder: a #GtkBuilder
|
|
* @basename: basename of the UI definition file
|
|
*
|
|
* Loads a UI definition into @builder from Evolution's UI directory.
|
|
* Failure here is fatal, since the application can't function without
|
|
* its UI definitions.
|
|
**/
|
|
void
|
|
e_load_ui_builder_definition (GtkBuilder *builder,
|
|
const gchar *basename)
|
|
{
|
|
gchar *filename;
|
|
GError *error = NULL;
|
|
|
|
g_return_if_fail (GTK_IS_BUILDER (builder));
|
|
g_return_if_fail (basename != NULL);
|
|
|
|
filename = g_build_filename (EVOLUTION_UIDIR, basename, NULL);
|
|
gtk_builder_add_from_file (builder, filename, &error);
|
|
g_free (filename);
|
|
|
|
if (error != NULL) {
|
|
g_error ("%s: %s", basename, error->message);
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|