doc: introspection clean up
[adg.git] / src / adg / adg-utils.c
bloba718e9a38edd1fa8f894cc5862583922c66aa985
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2015 Nicola Fontana <ntd at entidi.it>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
21 /**
22 * SECTION:adg-utils
23 * @Section_Id:utilities
24 * @title: Utilities
25 * @short_description: Assorted macros and functions
27 * Collection of macros and functions that do not fit inside any other topic.
29 * Since: 1.0
30 **/
32 /**
33 * ADG_DIR_RIGHT:
35 * Symbolic constant for the right direction (in radians).
37 * Since: 1.0
38 **/
40 /**
41 * ADG_DIR_DOWN:
43 * Symbolic constant for the down direction (in radians).
45 * Since: 1.0
46 **/
48 /**
49 * ADG_DIR_LEFT:
51 * Symbolic constant for the left direction (in radians).
53 * Since: 1.0
54 **/
56 /**
57 * ADG_DIR_UP:
59 * Symbolic constant for the up direction (in radians).
61 * Since: 1.0
62 **/
64 /**
65 * ADG_UTF8_DIAMETER:
67 * String constant that embeds a UTF-8 encoded diameter (U+2300).
68 * It can be used to prefix diameter quotes, such as:
70 * <informalexample><programlisting language="C">
71 * adg_dim_set_value(dim, ADG_UTF8_DIAMETER "<>");
72 * </programlisting></informalexample>
74 * Since: 1.0
75 **/
77 /**
78 * ADG_UTF8_DEGREE:
80 * String constant that embeds a UTF-8 encoded degree symbol (U+00B0).
81 * It is used to suffix by the default implementation of #AdgADim to
82 * suffix the set value, but can be also used manually:
84 * <informalexample><programlisting language="C">
85 * adg_dim_set_value(dim, "<>" ADG_UTF8_DEGREE);
86 * </programlisting></informalexample>
88 * Since: 1.0
89 **/
92 #include "adg-internal.h"
93 #include <string.h>
94 #include <limits.h>
97 #if GLIB_CHECK_VERSION(2, 16, 0)
98 #else
99 /**
100 * g_strcmp0:
101 * @s1: a C string or <constant>NULL</constant>
102 * @s2: another C string or <constant>NULL</constant>
104 * Compares @s1 and @s2 like strcmp(). Handles
105 * <constant>NULL</constant> gracefully by sorting it
106 * before non-<constant>NULL</constant> strings.
107 * Comparing two <constant>NULL</constant> pointers
108 * returns 0.
110 * This is a backward compatibility fallback for GLib
111 * prior to 2.16.0.
113 * Returns: an integer less than, equal to, or greater than zero, if @s1 is less than, equal to or greater than @s2.
115 * Since: 1.0
118 g_strcmp0(const char *s1, const char *s2)
120 if (!s1)
121 return -(s1 != s2);
123 if (!s2)
124 return s1 != s2;
126 return strcmp(s1, s2);
128 #endif
131 * adg_is_string_empty:
132 * @str: the subject string
134 * Checks if @str is an empty string, that is if is
135 * <constant>NULL</constant> or if its first character
136 * is <constant>'\0'</constant>.
138 * Returns: <constant>TRUE</constant> if @str is an empty string, <constant>FALSE</constant> otherwise.
140 * Since: 1.0
142 gboolean
143 adg_is_string_empty(const gchar *str)
145 return str == NULL || str[0] == '\0';
149 * adg_is_enum_value:
150 * @value: the enum value to check
151 * @enum_type: a #GEnum based type
153 * Checks if @value is a valid @enum_type value.
155 * Returns: <constant>TRUE</constant> if @value is a valid @enum_type, <constant>FALSE</constant> otherwise.
157 * Since: 1.0
159 gboolean
160 adg_is_enum_value(int value, GType enum_type)
162 GEnumClass *enum_class;
163 gboolean found;
165 enum_class = g_type_class_ref(enum_type);
166 g_return_val_if_fail(enum_class != NULL, FALSE);
168 found = FALSE;
170 if (value >= enum_class->minimum && value <= enum_class->maximum) {
171 GEnumValue *enum_value;
172 guint n;
174 for (n = 0; !found && n < enum_class->n_values; ++n) {
175 enum_value = enum_class->values + n;
176 found = value == enum_value->value;
180 g_type_class_unref(enum_class);
182 return found;
186 * adg_is_boolean_value:
187 * @value: the gboolean value to check
189 * Checks if @value is a valid #gboolean value, that is if it is
190 * <constant>TRUE</constant> or <constant>FALSE</constant>.
191 * No other values are accepted.
193 * Returns: <constant>TRUE</constant> if @value is a valid #gboolean, <constant>FALSE</constant> otherwise.
195 * Since: 1.0
197 gboolean
198 adg_is_boolean_value(gboolean value)
200 return value == TRUE || value == FALSE;
204 * adg_string_replace:
205 * @str: the original string
206 * @from: the substring to replace
207 * @to: the replacement string
209 * Replaces @from with @to inside @str and returns the result as a
210 * newly allocated string.
212 * @str and @from must be non-null valid C strings while @to can be
213 * <constant>NULL</constant>, in which case an empty string
214 * (<constant>""</constant>) will be implied.
216 * Returns: a newly allocated string to be freed with g_free() or <constant>NULL</constant> on errors.
218 * Since: 1.0
220 gchar *
221 adg_string_replace(const gchar *str, const gchar *from, const gchar *to)
223 gchar *result;
224 int from_len;
225 gchar *ptr, *old_result;
227 g_return_val_if_fail(str != NULL, NULL);
228 g_return_val_if_fail(from != NULL, NULL);
230 from_len = strlen(from);
232 g_return_val_if_fail(from_len > 0, NULL);
234 if (to == NULL)
235 to = "";
237 result = g_strdup(str);
239 while ((ptr = strstr(result, from)) != NULL) {
240 *ptr = '\0';
241 old_result = result;
242 result = g_strconcat(old_result, to, ptr + from_len, NULL);
243 g_free(old_result);
246 return result;
250 * _adg_dgettext:
251 * @domain: the translation domain to use, or
252 * <constant>NULL</constant> to use the domain set
253 * with <function>textdomain</function>
254 * @msgid: message to translate
256 * A variant of dgettext() (or of g_dgettext(), if available) that
257 * initialize the ADG localization infrastructure.
259 * Returns: The translated string
261 * Since: 1.0
263 const gchar *
264 _adg_dgettext(const gchar *domain, const gchar *msgid)
266 static gboolean initialized = FALSE;
268 if (G_UNLIKELY(!initialized)) {
269 #ifdef G_OS_UNIX
270 bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
271 #else
272 /* On windows, LOCALEDIR is relative to the installation path */
273 gchar *path = g_build_filename(g_win32_get_package_installation_directory_of_module(NULL),
274 LOCALEDIR, NULL);
275 bindtextdomain(GETTEXT_PACKAGE, path);
276 g_free(path);
277 #endif
278 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
279 initialized = TRUE;
282 #if GLIB_CHECK_VERSION(2, 18, 0)
283 return g_dgettext(domain, msgid);
284 #else
285 return dgettext(domain, msgid);
286 #endif
290 * _adg_dpgettext:
291 * @domain: the translation domain to use, or
292 * <constant>NULL</constant> to use the domain set with
293 * <function>textdomain</function>
294 * @msgctxtid: a combined message context and message id, separated
295 * by a \004 character
296 * @msgidoffset: the offset of the message id in @msgctxid
298 * This function is basically a duplicate of g_dpgettext() but using
299 * _adg_dgettext() internally instead of g_dgettext().
301 * Returns: The translated string
303 * Since: 1.0
305 const gchar *
306 _adg_dpgettext(const gchar *domain, const gchar *msgctxtid, gsize msgidoffset)
308 const gchar *translation;
309 gchar *sep;
311 translation = _adg_dgettext(domain, msgctxtid);
313 if (translation == msgctxtid) {
314 if (msgidoffset > 0)
315 return msgctxtid + msgidoffset;
317 sep = strchr(msgctxtid, '|');
319 if (sep) {
320 /* try with '\004' instead of '|', in case
321 * xgettext -kQ_:1g was used
323 gchar *tmp = g_alloca(strlen(msgctxtid) + 1);
324 strcpy(tmp, msgctxtid);
325 tmp[sep - msgctxtid] = '\004';
327 translation = _adg_dgettext(domain, tmp);
329 if (translation == tmp)
330 return sep + 1;
334 return translation;
338 * adg_find_file:
339 * @file: the file to search
340 * @...: a NULL terminated list of paths where to look for
341 * file existence.
343 * Searches @file in the provided paths and returns the full
344 * path to the first existing match. The check is performed
345 * using g_file_test() with the G_FILE_TEST_EXISTS test.
347 * The result should be freed with g_free() when no longer needed.
349 * Returns: a newly allocated string containing the path or <constant>NULL</constant> if not found or on errors.
351 * Since: 1.0
353 gchar *
354 adg_find_file(const gchar *file, ...)
356 va_list var_args;
357 gchar *path;
358 const gchar *base;
360 g_return_val_if_fail(file != NULL, NULL);
362 va_start(var_args, file);
364 while ((base = va_arg(var_args, const gchar *)) != NULL) {
365 path = g_build_filename(base, file, NULL);
366 if (g_file_test(path, G_FILE_TEST_EXISTS))
367 return path;
368 g_free(path);
371 return NULL;
375 * adg_scale_factor:
376 * @scale: a string identifying the scale
378 * Converts a scale in the form x:y (where x and y are respectively
379 * two positive integers representing the numerator and denominator
380 * of a fraction) into its approximate double representation. Any
381 * garbage following x or y will be silently ignored, meaning that
382 * x+garbage:y+garbage is equivalent to x:y. Furthermore, the postfix
383 * :y can be omitted, in which case (double) x will be returned.
385 * x and y are converted by using atoi(), so refer to your C library
386 * documentation for details on the algorithm used.
388 * Returns: the (possibly approximated) double conversion of @scale or 0 on errors.
390 * Since: 1.0
392 gdouble
393 adg_scale_factor(const gchar *scale)
395 gint numerator, denominator;
396 const gchar *ptr;
398 g_return_val_if_fail(scale != NULL, 0);
400 numerator = atoi(scale);
401 if (numerator <= 0)
402 return 0;
404 ptr = strchr(scale, ':');
405 if (ptr == NULL)
406 return numerator;
408 denominator = atoi(ptr + 1);
409 if (denominator <= 0)
410 return 0;
412 return (gdouble) numerator / (gdouble) denominator;
416 * adg_type_from_filename:
417 * @file: the full path to the file
419 * Gets the surface type from @file. The algorithm simply looks to the
420 * file name extension and tries to guess the correct surface type. If the
421 * guess fails, e.g. the extension does not exist or it is not usual, the
422 * function returns <constant>CAIRO_SURFACE_TYPE_XLIB</constant>. This is
423 * the value conventionally used to signal unrecognized file names.
425 * Returns: (type gint): the surface type of @file
426 * or <constant>CAIRO_SURFACE_TYPE_XLIB</constant>.
428 * Since: 1.0
430 cairo_surface_type_t
431 adg_type_from_filename(const gchar *file)
433 const gchar *p_suffix;
434 gchar *suffix;
435 cairo_surface_type_t type;
437 g_return_val_if_fail(file != NULL, CAIRO_SURFACE_TYPE_XLIB);
439 p_suffix = strrchr(file, '.');
440 if (p_suffix == NULL)
441 return CAIRO_SURFACE_TYPE_XLIB;
443 /* Put in suffix the lowercase extension without the leading dot */
444 suffix = g_ascii_strdown(p_suffix + 1, -1);
446 if (strcmp(suffix, "png") == 0) {
447 type = CAIRO_SURFACE_TYPE_IMAGE;
448 } else if (strcmp(suffix, "svg") == 0) {
449 type = CAIRO_SURFACE_TYPE_SVG;
450 } else if (strcmp(suffix, "pdf") == 0) {
451 type = CAIRO_SURFACE_TYPE_PDF;
452 } else if (strcmp(suffix, "ps") == 0) {
453 type = CAIRO_SURFACE_TYPE_PS;
454 } else {
455 type = CAIRO_SURFACE_TYPE_XLIB;
458 g_free(suffix);
459 return type;
463 * adg_nop:
465 * A function that does nothing. It can be used as
466 * <constant>/dev/null</constant> when callback are required, e.g. with
467 * g_log_set_default_handler().
469 * Since: 1.0
471 void
472 adg_nop(void)