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.
23 * @Section_Id:utilities
25 * @short_description: Assorted macros and functions
27 * Collection of macros and functions that do not fit inside any other topic.
35 * Symbolic constant for the right direction (in radians).
43 * Symbolic constant for the down direction (in radians).
51 * Symbolic constant for the left direction (in radians).
59 * Symbolic constant for the up direction (in radians).
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>
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>
92 #include "adg-internal.h"
97 #if GLIB_CHECK_VERSION(2, 16, 0)
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
110 * This is a backward compatibility fallback for GLib
113 * Returns: an integer less than, equal to, or greater than zero, if @s1 is less than, equal to or greater than @s2.
118 g_strcmp0(const char *s1
, const char *s2
)
126 return strcmp(s1
, s2
);
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.
143 adg_is_string_empty(const gchar
*str
)
145 return str
== NULL
|| str
[0] == '\0';
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.
160 adg_is_enum_value(int value
, GType enum_type
)
162 GEnumClass
*enum_class
;
165 enum_class
= g_type_class_ref(enum_type
);
166 g_return_val_if_fail(enum_class
!= NULL
, FALSE
);
170 if (value
>= enum_class
->minimum
&& value
<= enum_class
->maximum
) {
171 GEnumValue
*enum_value
;
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
);
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.
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.
221 adg_string_replace(const gchar
*str
, const gchar
*from
, const gchar
*to
)
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
);
237 result
= g_strdup(str
);
239 while ((ptr
= strstr(result
, from
)) != NULL
) {
242 result
= g_strconcat(old_result
, to
, ptr
+ from_len
, NULL
);
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
264 _adg_dgettext(const gchar
*domain
, const gchar
*msgid
)
266 static gboolean initialized
= FALSE
;
268 if (G_UNLIKELY(!initialized
)) {
270 bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
);
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
),
275 bindtextdomain(GETTEXT_PACKAGE
, path
);
278 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8");
282 #if GLIB_CHECK_VERSION(2, 18, 0)
283 return g_dgettext(domain
, msgid
);
285 return dgettext(domain
, msgid
);
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
306 _adg_dpgettext(const gchar
*domain
, const gchar
*msgctxtid
, gsize msgidoffset
)
308 const gchar
*translation
;
311 translation
= _adg_dgettext(domain
, msgctxtid
);
313 if (translation
== msgctxtid
) {
315 return msgctxtid
+ msgidoffset
;
317 sep
= strchr(msgctxtid
, '|');
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
)
339 * @file: the file to search
340 * @...: a NULL terminated list of paths where to look for
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 * This function has been picked up from the ntdisp project:
348 * http://dev.entidi.com/p/ntdisp/
350 * Returns: a newly allocated string containing the path
351 * or NULL on errors. Free it with g_free() when
357 adg_find_file(const gchar
*file
, ...)
363 va_start(var_args
, file
);
365 while ((base
= va_arg(var_args
, const gchar
*)) != NULL
) {
366 path
= g_build_filename(base
, file
, NULL
);
367 if (g_file_test(path
, G_FILE_TEST_EXISTS
))
377 * @scale: a string identifying the scale
379 * Converts a scale in the form x:y (where x and y are respectively
380 * two positive integers representing the numerator and denominator
381 * of a fraction) into its approximate double representation. Any
382 * garbage following x or y will be silently ignored, meaning that
383 * x+garbage:y+garbage is equivalent to x:y. Furthermore, the postfix
384 * :y can be omitted, in which case (double) x will be returned.
386 * x and y are converted by using atoi(), so refer to your C library
387 * documentation for details on the algorithm used.
389 * Returns: the (possibly approximated) double conversion of @scale or 0 on errors.
392 adg_scale_factor(const gchar
*scale
)
394 gint numerator
, denominator
;
397 g_return_val_if_fail(scale
!= NULL
, 0);
399 numerator
= atoi(scale
);
403 ptr
= strchr(scale
, ':');
407 denominator
= atoi(ptr
+ 1);
408 if (denominator
<= 0)
411 return (gdouble
) numerator
/ (gdouble
) denominator
;
415 * adg_type_from_filename:
416 * @file: the full path to the file
418 * Gets the surface type from @file. The algorithm simply looks to the
419 * file name extension and tries to guess the correct surface type. If the
420 * guess fails, e.g. the extension does not exist or it is not usual, the
421 * function returns <constant>CAIRO_SURFACE_TYPE_XLIB</constant>. This is
422 * the value conventionally used to signal unrecognized file names.
424 * Returns: the surface type of @file or <constant>CAIRO_SURFACE_TYPE_XLIB</constant>.
427 adg_type_from_filename(const gchar
*file
)
429 const gchar
*p_suffix
;
431 cairo_surface_type_t type
;
433 g_return_val_if_fail(file
!= NULL
, CAIRO_SURFACE_TYPE_XLIB
);
435 p_suffix
= strrchr(file
, '.');
436 if (p_suffix
== NULL
)
437 return CAIRO_SURFACE_TYPE_XLIB
;
439 /* Put in suffix the lowercase extension without the leading dot */
440 suffix
= g_ascii_strdown(p_suffix
+ 1, -1);
442 if (strcmp(suffix
, "png") == 0) {
443 type
= CAIRO_SURFACE_TYPE_IMAGE
;
444 } else if (strcmp(suffix
, "svg") == 0) {
445 type
= CAIRO_SURFACE_TYPE_SVG
;
446 } else if (strcmp(suffix
, "pdf") == 0) {
447 type
= CAIRO_SURFACE_TYPE_PDF
;
448 } else if (strcmp(suffix
, "ps") == 0) {
449 type
= CAIRO_SURFACE_TYPE_PS
;
451 type
= CAIRO_SURFACE_TYPE_XLIB
;
461 * A function that does nothing. It can be used as
462 * <constant>/dev/null</constant> when callback are required, e.g. with
463 * g_log_set_default_handler().