GUI: Move .ui files from goffice resources to glib resources
[gnumeric.git] / src / gutils.c
blob2657a3e0ef0dc30559e6e3db65fd8889902edbe5
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * utils.c: Various utility routines that do not depend on the GUI of Gnumeric
5 * Authors:
6 * Miguel de Icaza (miguel@gnu.org)
7 * Jukka-Pekka Iivonen (iivonen@iki.fi)
8 * Zbigniew Chyla (cyba@gnome.pl)
9 */
10 #include <gnumeric-config.h>
11 #include <glib/gi18n-lib.h>
12 #include "gnumeric.h"
13 #include "gutils.h"
14 #include "gnumeric-paths.h"
16 #include "sheet.h"
17 #include "ranges.h"
18 #include "mathfunc.h"
19 #include "workbook-view.h"
20 #include "workbook.h"
22 #include <goffice/goffice.h>
24 #include <stdlib.h>
25 #include <math.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <locale.h>
32 #include <gsf/gsf-impl-utils.h>
33 #include <gsf/gsf-doc-meta-data.h>
34 #include <gsf/gsf-timestamp.h>
36 #define SHEET_SELECTION_KEY "sheet-selection"
37 #define SSCONVERT_SHEET_SET_KEY "ssconvert-sheets"
39 static char *gnumeric_lib_dir;
40 static char *gnumeric_data_dir;
41 static char *gnumeric_locale_dir;
42 static char *gnumeric_usr_dir;
43 static char *gnumeric_usr_dir_unversioned;
44 static char *gnumeric_extern_plugin_dir;
45 static GSList *gutils_xml_in_docs;
47 static gboolean
48 running_in_tree (void)
50 const char *argv0 = g_get_prgname ();
52 if (!argv0)
53 return FALSE;
55 /* Sometime we see, e.g., "lt-gnumeric" as basename. */
57 char *base = g_path_get_basename (argv0);
58 gboolean has_lt_prefix = (strncmp (base, "lt-", 3) == 0);
59 g_free (base);
60 if (has_lt_prefix)
61 return TRUE;
64 /* Look for ".libs" as final path element. */
66 const char *dotlibs = strstr (argv0, ".libs/");
67 if (dotlibs &&
68 (dotlibs == argv0 || G_IS_DIR_SEPARATOR (dotlibs[-1])) &&
69 strchr (dotlibs + 6, G_DIR_SEPARATOR) == NULL)
70 return TRUE;
73 return FALSE;
76 static gboolean gutils_inited = FALSE;
78 void
79 gutils_init (void)
81 char const *home_dir;
83 // This function will end up being called twice in normal operation:
84 // once from gnm_pre_parse_init and once from gnm_init. Introspection
85 // will not get the first.
86 if (gutils_inited)
87 return;
89 #ifdef G_OS_WIN32
90 gchar *dir = g_win32_get_package_installation_directory_of_module (NULL);
91 gnumeric_lib_dir = g_build_filename (dir, "lib",
92 "gnumeric", GNM_VERSION_FULL,
93 NULL);
94 gnumeric_data_dir = g_build_filename (dir, "share",
95 "gnumeric", GNM_VERSION_FULL,
96 NULL);
97 gnumeric_locale_dir = g_build_filename (dir, "share", "locale", NULL);
98 gnumeric_extern_plugin_dir = g_build_filename
99 (dir, "lib", "gnumeric", GNM_API_VERSION, "plugins",
100 NULL);
101 g_free (dir);
102 #else
103 if (running_in_tree ()) {
104 const char *argv0 = g_get_prgname ();
105 char *dotlibs = g_path_get_dirname (argv0);
106 char *top = g_build_filename (dotlibs, "..", "../", NULL);
107 char *plugins = g_build_filename (top, PLUGIN_SUBDIR, NULL);
108 if (g_file_test (plugins, G_FILE_TEST_IS_DIR))
109 gnumeric_lib_dir =
110 go_filename_simplify (top, GO_DOTDOT_SYNTACTIC,
111 FALSE);
112 g_free (top);
113 g_free (plugins);
114 g_free (dotlibs);
115 if (0) g_printerr ("Running in-tree\n");
118 if (!gnumeric_lib_dir)
119 gnumeric_lib_dir = g_strdup (GNUMERIC_LIBDIR);
120 gnumeric_data_dir = g_strdup (GNUMERIC_DATADIR);
121 gnumeric_locale_dir = g_strdup (GNUMERIC_LOCALEDIR);
122 gnumeric_extern_plugin_dir = g_strdup (GNUMERIC_EXTERNPLUGINDIR);
123 #endif
124 home_dir = g_get_home_dir ();
125 gnumeric_usr_dir_unversioned = home_dir
126 ? g_build_filename (home_dir, ".gnumeric", NULL)
127 : NULL;
128 gnumeric_usr_dir = gnumeric_usr_dir_unversioned
129 ? g_build_filename (gnumeric_usr_dir_unversioned, GNM_VERSION_FULL, NULL)
130 : NULL;
132 gutils_inited = TRUE;
135 void
136 gutils_shutdown (void)
138 GSList *l;
140 g_free (gnumeric_lib_dir);
141 gnumeric_lib_dir = NULL;
142 g_free (gnumeric_data_dir);
143 gnumeric_data_dir = NULL;
144 g_free (gnumeric_locale_dir);
145 gnumeric_locale_dir = NULL;
146 g_free (gnumeric_usr_dir);
147 gnumeric_usr_dir = NULL;
148 g_free (gnumeric_usr_dir_unversioned);
149 gnumeric_usr_dir_unversioned = NULL;
150 g_free (gnumeric_extern_plugin_dir);
151 gnumeric_extern_plugin_dir = NULL;
153 for (l = gutils_xml_in_docs; l; l = l->next) {
154 GsfXMLInDoc **pdoc = l->data;
155 gsf_xml_in_doc_free (*pdoc);
156 *pdoc = NULL;
158 g_slist_free (gutils_xml_in_docs);
159 gutils_xml_in_docs = NULL;
162 char const *
163 gnm_sys_lib_dir (void)
165 return gnumeric_lib_dir;
168 char const *
169 gnm_sys_data_dir (void)
171 return gnumeric_data_dir;
174 char const *
175 gnm_sys_extern_plugin_dir (void)
177 return gnumeric_extern_plugin_dir;
180 char const *
181 gnm_locale_dir (void)
183 return gnumeric_locale_dir;
186 char const *
187 gnm_usr_dir (gboolean versioned)
189 return versioned ? gnumeric_usr_dir : gnumeric_usr_dir_unversioned;
193 static gboolean
194 all_ascii (const char *s)
196 while ((guchar)*s < 0x7f) {
197 if (*s)
198 s++;
199 else
200 return TRUE;
202 return FALSE;
206 * Like strto[ld], but...
207 * 1. handles non-ascii characters
208 * 2. disallows 0x000.0p+00 and 0.0d+00
209 * 3. ensures sane errno on exit
211 gnm_float
212 gnm_utf8_strto (const char *s, char **end)
214 const char *p;
215 int sign;
216 char *dummy_end;
217 GString *ascii;
218 GString const *decimal = go_locale_get_decimal ();
219 gboolean seen_decimal = FALSE;
220 gboolean seen_digit = FALSE;
221 size_t spaces = 0;
222 gnm_float res;
223 int save_errno;
225 if (all_ascii (s)) {
226 res = gnm_strto (s, end);
227 goto handle_denormal;
230 ascii = g_string_sized_new (100);
232 if (!end)
233 end = &dummy_end;
235 p = s;
236 while (g_unichar_isspace (g_utf8_get_char (p))) {
237 p = g_utf8_next_char (p);
238 spaces++;
241 sign = go_unichar_issign (g_utf8_get_char (p));
242 if (sign) {
243 g_string_append_c (ascii, "-/+"[sign + 1]);
244 p = g_utf8_next_char (p);
247 do {
248 if (strncmp (p, decimal->str, decimal->len) == 0) {
249 if (seen_decimal)
250 break;
251 seen_decimal = TRUE;
252 go_string_append_gstring (ascii, decimal);
253 p += decimal->len;
254 } else if (g_unichar_isdigit (g_utf8_get_char (p))) {
255 g_string_append_c (ascii, '0' + g_unichar_digit_value (g_utf8_get_char (p)));
256 p = g_utf8_next_char (p);
257 seen_digit = TRUE;
258 } else
259 break;
260 } while (1);
262 if (!seen_digit) {
263 /* No conversion, bail to gnm_strto for nan etc. */
264 g_string_free (ascii, TRUE);
265 return gnm_strto (s, end);
268 if (*p == 'e' || *p == 'E') {
269 int sign;
271 g_string_append_c (ascii, 'e');
272 p = g_utf8_next_char (p);
274 sign = go_unichar_issign (g_utf8_get_char (p));
275 if (sign) {
276 g_string_append_c (ascii, "-/+"[sign + 1]);
277 p = g_utf8_next_char (p);
279 while (g_unichar_isdigit (g_utf8_get_char (p))) {
280 g_string_append_c (ascii, '0' + g_unichar_digit_value (g_utf8_get_char (p)));
281 p = g_utf8_next_char (p);
285 res = gnm_strto (ascii->str, end);
286 save_errno = errno;
287 *end = g_utf8_offset_to_pointer
288 (s, spaces + g_utf8_pointer_to_offset (ascii->str, *end));
289 g_string_free (ascii, TRUE);
291 errno = save_errno;
293 handle_denormal:
294 save_errno = errno;
295 if (res != 0 && gnm_abs (res) < GNM_MIN)
296 errno = 0;
297 else
298 errno = save_errno;
300 return res;
304 * Like strtol, but...
305 * 1. handles non-ascii characters
306 * 2. assumes base==10
307 * 3. ensures sane errno on exit
309 long
310 gnm_utf8_strtol (const char *s, char **end)
312 const char *p;
313 int sign;
314 char *dummy_end;
315 unsigned long res = 0, lim, limd;
317 if (!end)
318 end = &dummy_end;
320 p = s;
321 while (g_unichar_isspace (g_utf8_get_char (p)))
322 p = g_utf8_next_char (p);
324 sign = go_unichar_issign (g_utf8_get_char (p));
325 if (sign)
326 p = g_utf8_next_char (p);
327 if (sign < 0) {
328 lim = (-(unsigned long)LONG_MIN) / 10u;
329 limd = (-(unsigned long)LONG_MIN) % 10u;
330 } else {
331 lim = (unsigned long)LONG_MAX / 10u;
332 limd = (unsigned long)LONG_MAX % 10u;
335 if (!g_unichar_isdigit (g_utf8_get_char (p))) {
336 errno = 0;
337 *end = (char *)s;
338 return 0;
341 while (g_unichar_isdigit (g_utf8_get_char (p))) {
342 guint8 dig = g_unichar_digit_value (g_utf8_get_char (p));
343 p = g_utf8_next_char (p);
345 if (res > lim || (res == lim && dig > limd)) {
346 /* Overflow */
347 while (g_unichar_isdigit (g_utf8_get_char (p)))
348 p = g_utf8_next_char (p);
349 *end = (char *)p;
350 errno = ERANGE;
351 return sign < 0 ? LONG_MIN : LONG_MAX;
354 res = res * 10u + dig;
356 *end = (char *)p;
357 errno = 0;
358 return sign < 0 ? (long)-res : (long)res;
363 gnm_regcomp_XL (GORegexp *preg, char const *pattern, int cflags,
364 gboolean anchor_start, gboolean anchor_end)
366 GString *res = g_string_new (NULL);
367 int retval;
369 if (anchor_start)
370 g_string_append_c (res, '^');
372 while (*pattern) {
373 switch (*pattern) {
374 case '*':
375 g_string_append (res, ".*");
376 pattern++;
377 break;
379 case '?':
380 g_string_append_c (res, '.');
381 pattern++;
382 break;
384 case '~':
385 if (pattern[1] == '*' ||
386 pattern[1] == '?' ||
387 pattern[1] == '~')
388 pattern++;
389 /* Fall through */
390 default:
391 pattern = go_regexp_quote1 (res, pattern);
395 if (anchor_end)
396 g_string_append_c (res, '$');
398 retval = go_regcomp (preg, res->str, cflags);
399 g_string_free (res, TRUE);
400 return retval;
404 * gnm_excel_search_impl:
405 * @needle: the pattern to search for, see gnm_regcomp_XL.
406 * @haystack: the string to search in.
407 * @skip: zero-based search start point in characters.
409 * Returns: -1 for a non-match, or zero-based location in
410 * characters.
412 * The is the implementation of Excel's SEARCH function.
413 * However, note that @skip and return value are zero-based.
416 gnm_excel_search_impl (const char *needle, const char *haystack,
417 size_t skip)
419 const char *hay2;
420 size_t i;
421 GORegexp r;
423 for (i = skip, hay2 = haystack; i > 0; i--) {
424 if (*hay2 == 0)
425 return -1;
426 hay2 = g_utf8_next_char (hay2);
429 if (gnm_regcomp_XL (&r, needle, GO_REG_ICASE, FALSE, FALSE) == GO_REG_OK) {
430 GORegmatch rm;
432 switch (go_regexec (&r, hay2, 1, &rm, 0)) {
433 case GO_REG_NOMATCH:
434 break;
435 case GO_REG_OK:
436 go_regfree (&r);
437 return skip +
438 g_utf8_pointer_to_offset (hay2, hay2 + rm.rm_so);
439 default:
440 g_warning ("Unexpected go_regexec result");
442 go_regfree (&r);
443 } else {
444 g_warning ("Unexpected regcomp result");
447 return -1;
451 #if 0
452 static char const *
453 color_to_string (PangoColor color)
455 static char result[100];
456 sprintf (result, "%04x:%04x:%04x", color.red, color.green, color.blue);
457 return result;
460 static const char *
461 enum_name (GType typ, int i)
463 static char result[100];
464 GEnumClass *ec = g_type_class_ref (typ);
466 if (ec) {
467 GEnumValue *ev = g_enum_get_value (ec, i);
468 g_type_class_unref (ec);
470 if (ev && ev->value_nick)
471 return ev->value_nick;
472 if (ev && ev->value_name)
473 return ev->value_name;
476 sprintf (result, "%d", i);
477 return result;
480 static gboolean
481 cb_gnm_pango_attr_dump (PangoAttribute *attr, gpointer user_data)
483 g_print (" start=%u; end=%u\n", attr->start_index, attr->end_index);
484 switch (attr->klass->type) {
485 case PANGO_ATTR_FAMILY:
486 g_print (" family=\"%s\"\n", ((PangoAttrString *)attr)->value);
487 break;
488 case PANGO_ATTR_LANGUAGE:
489 g_print (" language=\"%s\"\n", pango_language_to_string (((PangoAttrLanguage *)attr)->value));
490 break;
491 case PANGO_ATTR_STYLE:
492 g_print (" style=%s\n",
493 enum_name (PANGO_TYPE_STYLE, ((PangoAttrInt *)attr)->value));
494 break;
495 case PANGO_ATTR_WEIGHT:
496 g_print (" weight=%s\n",
497 enum_name (PANGO_TYPE_WEIGHT, ((PangoAttrInt *)attr)->value));
498 break;
499 case PANGO_ATTR_VARIANT:
500 g_print (" variant=%s\n",
501 enum_name (PANGO_TYPE_VARIANT, ((PangoAttrInt *)attr)->value));
502 break;
503 case PANGO_ATTR_STRETCH:
504 g_print (" stretch=%s\n",
505 enum_name (PANGO_TYPE_STRETCH, ((PangoAttrInt *)attr)->value));
506 break;
507 case PANGO_ATTR_UNDERLINE:
508 g_print (" underline=%s\n",
509 enum_name (PANGO_TYPE_UNDERLINE, ((PangoAttrInt *)attr)->value));
510 break;
511 case PANGO_ATTR_STRIKETHROUGH:
512 g_print (" strikethrough=%d\n", ((PangoAttrInt *)attr)->value);
513 break;
514 case PANGO_ATTR_RISE:
515 g_print (" rise=%d\n", ((PangoAttrInt *)attr)->value);
516 break;
517 case PANGO_ATTR_FALLBACK:
518 g_print (" fallback=%d\n", ((PangoAttrInt *)attr)->value);
519 break;
520 case PANGO_ATTR_LETTER_SPACING:
521 g_print (" letter_spacing=%d\n", ((PangoAttrInt *)attr)->value);
522 break;
523 case PANGO_ATTR_SIZE:
524 g_print (" size=%d%s\n",
525 ((PangoAttrSize *)attr)->size,
526 ((PangoAttrSize *)attr)->absolute ? " abs" : "");
527 break;
528 case PANGO_ATTR_SCALE:
529 g_print (" scale=%g\n", ((PangoAttrFloat *)attr)->value);
530 break;
531 case PANGO_ATTR_FOREGROUND:
532 g_print (" foreground=%s\n", color_to_string (((PangoAttrColor *)attr)->color));
533 break;
534 case PANGO_ATTR_BACKGROUND:
535 g_print (" background=%s\n", color_to_string (((PangoAttrColor *)attr)->color));
536 break;
537 case PANGO_ATTR_UNDERLINE_COLOR:
538 g_print (" underline_color=%s\n", color_to_string (((PangoAttrColor *)attr)->color));
539 break;
540 case PANGO_ATTR_STRIKETHROUGH_COLOR:
541 g_print (" strikethrough_color=%s\n", color_to_string (((PangoAttrColor *)attr)->color));
542 break;
543 case PANGO_ATTR_FONT_DESC: {
544 char *desc = pango_font_description_to_string (((PangoAttrFontDesc*)attr)->desc);
545 g_print (" font=\"%s\"\n", desc);
546 g_free (desc);
547 break;
549 default:
550 g_print (" type=%s\n", enum_name (PANGO_TYPE_ATTR_TYPE, attr->klass->type));
553 return FALSE;
556 void
557 gnm_pango_attr_dump (PangoAttrList *list)
559 g_print ("PangoAttrList at %p\n", list);
560 pango_attr_list_filter (list, cb_gnm_pango_attr_dump, NULL);
562 #endif
565 static gboolean
566 cb_gnm_pango_attr_list_equal (PangoAttribute *a, gpointer _sl)
568 GSList **sl = _sl;
569 *sl = g_slist_prepend (*sl, a);
570 return FALSE;
574 * This is a bit of a hack. It might claim a difference even when things
575 * actually are equal. But not the other way around.
577 gboolean
578 gnm_pango_attr_list_equal (PangoAttrList const *l1, PangoAttrList const *l2)
580 if (l1 == l2)
581 return TRUE;
582 else if (l1 == NULL || l2 == NULL)
583 return FALSE;
584 else {
585 gboolean res;
586 GSList *sl1 = NULL, *sl2 = NULL;
587 (void)pango_attr_list_filter ((PangoAttrList *)l1,
588 cb_gnm_pango_attr_list_equal,
589 &sl1);
590 (void)pango_attr_list_filter ((PangoAttrList *)l2,
591 cb_gnm_pango_attr_list_equal,
592 &sl2);
594 while (sl1 && sl2) {
595 const PangoAttribute *a1 = sl1->data;
596 const PangoAttribute *a2 = sl2->data;
597 if (a1->start_index != a2->start_index ||
598 a1->end_index != a2->end_index ||
599 !pango_attribute_equal (a1, a2))
600 break;
601 sl1 = g_slist_delete_link (sl1, sl1);
602 sl2 = g_slist_delete_link (sl2, sl2);
605 res = (sl1 == sl2);
606 g_slist_free (sl1);
607 g_slist_free (sl2);
608 return res;
612 /* ------------------------------------------------------------------------- */
614 struct _GnmLocale {
615 char *num_locale;
616 char *monetary_locale;
619 * gnm_push_C_locale: (skip)
621 * Returns the current locale, and sets the locale and the value-format
622 * engine's locale to 'C'. The caller must call gnm_pop_C_locale to free the
623 * result and restore the previous locale.
625 GnmLocale *
626 gnm_push_C_locale (void)
628 GnmLocale *old = g_new0 (GnmLocale, 1);
630 old->num_locale = g_strdup (go_setlocale (LC_NUMERIC, NULL));
631 go_setlocale (LC_NUMERIC, "C");
632 old->monetary_locale = g_strdup (go_setlocale (LC_MONETARY, NULL));
633 go_setlocale (LC_MONETARY, "C");
634 go_locale_untranslated_booleans ();
636 return old;
640 * gnm_pop_C_locale: (skip)
641 * @locale: #GnmLocale
643 * Frees the result of gnm_push_C_locale and restores the original locale.
645 void
646 gnm_pop_C_locale (GnmLocale *locale)
648 /* go_setlocale restores bools to locale translation */
649 go_setlocale (LC_MONETARY, locale->monetary_locale);
650 g_free (locale->monetary_locale);
651 go_setlocale (LC_NUMERIC, locale->num_locale);
652 g_free (locale->num_locale);
653 g_free (locale);
656 /* ------------------------------------------------------------------------- */
658 gboolean
659 gnm_debug_flag (const char *flag)
661 GDebugKey key;
662 key.key = (char *)flag;
663 key.value = 1;
665 return g_parse_debug_string (g_getenv ("GNM_DEBUG"), &key, 1) != 0;
668 /* ------------------------------------------------------------------------- */
670 void
671 gnm_string_add_number (GString *buf, gnm_float d)
673 size_t old_len = buf->len;
674 double d2;
675 static int digits;
677 if (digits == 0) {
678 gnm_float l10 = gnm_log10 (FLT_RADIX);
679 digits = (int)gnm_ceil (GNM_MANT_DIG * l10) +
680 (l10 == (int)l10 ? 0 : 1);
683 g_string_append_printf (buf, "%.*" GNM_FORMAT_g, digits - 1, d);
684 d2 = gnm_strto (buf->str + old_len, NULL);
686 if (d != d2) {
687 g_string_truncate (buf, old_len);
688 g_string_append_printf (buf, "%.*" GNM_FORMAT_g, digits, d);
692 /* ------------------------------------------------------------------------- */
694 void
695 gnm_insert_meta_date (GODoc *doc, char const *name)
697 GValue *value = g_new0 (GValue, 1);
698 GTimeVal tm;
699 GsfTimestamp *ts = gsf_timestamp_new ();
701 g_get_current_time (&tm);
702 tm.tv_usec = 0L;
704 gsf_timestamp_set_time (ts, tm.tv_sec);
705 g_value_init (value, GSF_TIMESTAMP_TYPE);
706 gsf_timestamp_to_value (ts, value);
707 gsf_timestamp_free (ts);
709 gsf_doc_meta_data_insert (go_doc_get_meta_data (doc),
710 g_strdup (name),
711 value);
714 /* ------------------------------------------------------------------------- */
717 * gnm_object_get_bool:
718 * @o: #GObject
719 * @name: property name
721 * Returns: the value of @o's boolean property @name.
723 gboolean
724 gnm_object_get_bool (gpointer o, const char *name)
726 gboolean b;
727 g_object_get (o, name, &b, NULL);
728 return b;
732 * gnm_object_has_readable_prop:
733 * @obj: #GObject
734 * @property: property name
735 * @typ: property's type or %G_TYPE_NONE. (Exact type, not is-a.)
736 * @pres: (out) (optional): location to store property value.
738 * Returns: %TRUE if @obj has a readable property named @property
739 * of type @typ.
741 gboolean
742 gnm_object_has_readable_prop (gconstpointer obj, const char *property,
743 GType typ, gpointer pres)
745 GObjectClass *klass;
746 GParamSpec *spec;
748 if (!obj)
749 return FALSE;
751 klass = G_OBJECT_GET_CLASS (G_OBJECT (obj));
752 spec = g_object_class_find_property (klass, property);
753 if (!spec ||
754 !(G_PARAM_READABLE & spec->flags) ||
755 (typ != G_TYPE_NONE && spec->value_type != typ))
756 return FALSE;
758 if (pres)
759 g_object_get (G_OBJECT (obj), property, pres, NULL);
760 return TRUE;
763 /* ------------------------------------------------------------------------- */
765 gint
766 gnm_float_equal (gnm_float const *a, const gnm_float *b)
768 return (*a == *b);
771 guint
772 gnm_float_hash (gnm_float const *d)
774 int expt;
775 gnm_float mant = gnm_frexp (gnm_abs (*d), &expt);
776 guint h = ((guint)(0x80000000u * mant)) ^ expt;
777 if (*d >= 0)
778 h ^= 0x55555555;
779 return h;
782 /* ------------------------------------------------------------------------- */
784 struct cb_compare {
785 GnmHashTableOrder order;
786 gpointer user;
789 static gint
790 cb_compare (gconstpointer a_, gconstpointer b_, gpointer user_data)
792 struct cb_compare *user = user_data;
793 gpointer *a = (gpointer )a_;
794 gpointer *b = (gpointer )b_;
796 return user->order (a[0], a[1], b[0], b[1], user->user);
801 * gnm_hash_table_foreach_ordered:
802 * @h: Hash table
803 * @callback: (scope async): #GHFunc
804 * @order: (scope async): Ordering function
805 * @user: user data for callback and order
807 * Like g_hash_table_foreach, but with an ordering imposed.
809 void
810 gnm_hash_table_foreach_ordered (GHashTable *h,
811 GHFunc callback,
812 GnmHashTableOrder order,
813 gpointer user)
815 unsigned ui;
816 GPtrArray *data;
817 struct cb_compare u;
818 GHashTableIter hiter;
819 gpointer key, value;
821 /* Gather all key-value pairs */
822 data = g_ptr_array_new ();
823 g_hash_table_iter_init (&hiter, h);
824 while (g_hash_table_iter_next (&hiter, &key, &value)) {
825 g_ptr_array_add (data, key);
826 g_ptr_array_add (data, value);
829 /* Sort according to given ordering */
830 u.order = order;
831 u.user = user;
832 g_qsort_with_data (data->pdata,
833 data->len / 2, 2 * sizeof (gpointer),
834 cb_compare,
835 &u);
837 /* Call user callback with all pairs */
838 for (ui = 0; ui < data->len; ui += 2)
839 callback (g_ptr_array_index (data, ui),
840 g_ptr_array_index (data, ui + 1),
841 user);
843 /* Clean up */
844 g_ptr_array_free (data, TRUE);
847 /* ------------------------------------------------------------------------- */
849 void
850 gnm_xml_in_doc_dispose_on_exit (GsfXMLInDoc **pdoc)
852 gutils_xml_in_docs = g_slist_prepend (gutils_xml_in_docs, pdoc);
855 /* ------------------------------------------------------------------------- */
858 * gnm_file_saver_get_sheet:
859 * @fs: #GOFileSaver
860 * @wbv: #WorkbookView
862 * For a single-sheet saver, this function determines what sheet to save.
864 * Returns: (transfer none): the sheet to export
866 Sheet *
867 gnm_file_saver_get_sheet (GOFileSaver const *fs, WorkbookView const *wbv)
869 Workbook *wb;
870 GPtrArray *sel;
872 g_return_val_if_fail (GO_IS_FILE_SAVER (fs), NULL);
873 g_return_val_if_fail (go_file_saver_get_save_scope (fs) ==
874 GO_FILE_SAVE_SHEET, NULL);
875 g_return_val_if_fail (GNM_IS_WORKBOOK_VIEW (wbv), NULL);
877 wb = wb_view_get_workbook (wbv);
879 sel = g_object_get_data (G_OBJECT (wb), SHEET_SELECTION_KEY);
880 if (sel) {
881 if (sel->len == 1)
882 return g_ptr_array_index (sel, 0);
883 g_critical ("Someone messed up sheet selection");
886 return wb_view_cur_sheet (wbv);
890 * gnm_file_saver_get_sheets:
891 * @fs: #GOFileSaver
892 * @wbv: #WorkbookView
893 * @default_all: If %TRUE, all sheets will be selected by default; if %FALSE,
894 * this function will return %NULL if no sheets were explicitly selected.
896 * For a workbook-scope saver, this function determines what sheets to save.
898 * Returns: (transfer container) (element-type Sheet): the sheets to export
900 * Note: the return value should be unreffed, not freed.
902 GPtrArray *
903 gnm_file_saver_get_sheets (GOFileSaver const *fs,
904 WorkbookView const *wbv,
905 gboolean default_all)
907 Workbook *wb;
908 GPtrArray *sel, *sheets;
910 g_return_val_if_fail (GO_IS_FILE_SAVER (fs), NULL);
911 g_return_val_if_fail (go_file_saver_get_save_scope (fs) ==
912 GO_FILE_SAVE_WORKBOOK, NULL);
913 g_return_val_if_fail (GNM_IS_WORKBOOK_VIEW (wbv), NULL);
915 wb = wb_view_get_workbook (wbv);
916 sel = g_object_get_data (G_OBJECT (wb), SHEET_SELECTION_KEY);
917 sheets = g_object_get_data (G_OBJECT (wb), SSCONVERT_SHEET_SET_KEY);
918 if (sel)
919 g_ptr_array_ref (sel);
920 else if (sheets)
921 sel = g_ptr_array_ref (sheets);
922 else if (default_all) {
923 int i;
924 sel = g_ptr_array_new ();
925 for (i = 0; i < workbook_sheet_count (wb); i++) {
926 Sheet *sheet = workbook_sheet_by_index (wb, i);
927 g_ptr_array_add (sel, sheet);
931 return sel;
934 gboolean
935 gnm_file_saver_common_export_option (GOFileSaver const *fs,
936 Workbook const *wb,
937 const char *key, const char *value,
938 GError **err)
940 if (err)
941 *err = NULL;
943 g_return_val_if_fail (GO_IS_FILE_SAVER (fs), FALSE);
944 g_return_val_if_fail (GNM_IS_WORKBOOK (wb), FALSE);
945 g_return_val_if_fail (key != NULL, FALSE);
946 g_return_val_if_fail (value != NULL, FALSE);
948 if (strcmp (key, "sheet") == 0) {
949 GPtrArray *sheets;
950 Sheet *sheet = workbook_sheet_by_name (wb, value);
952 if (!sheet) {
953 if (err)
954 *err = g_error_new (go_error_invalid (), 0,
955 _("Unknown sheet \"%s\""),
956 value);
957 return TRUE;
960 sheets = g_object_get_data (G_OBJECT (wb), SSCONVERT_SHEET_SET_KEY);
961 if (!sheets) {
962 sheets = g_ptr_array_new ();
963 g_object_set_data_full (G_OBJECT (wb),
964 SSCONVERT_SHEET_SET_KEY,
965 sheets,
966 (GDestroyNotify)g_ptr_array_unref);
968 g_ptr_array_add (sheets, sheet);
970 return FALSE;
973 if (err)
974 *err = g_error_new (go_error_invalid (), 0,
975 _("Invalid export option \"%s\" for format %s"),
976 key,
977 go_file_saver_get_id (fs));
979 return TRUE;