Update Spanish translation
[gnumeric.git] / src / gutils.c
blob0560687ffc6cb549a45266bc537b80b1845191f5
1 /*
2 * utils.c: Various utility routines that do not depend on the GUI of Gnumeric
4 * Authors:
5 * Miguel de Icaza (miguel@gnu.org)
6 * Jukka-Pekka Iivonen (iivonen@iki.fi)
7 * Zbigniew Chyla (cyba@gnome.pl)
8 */
9 #include <gnumeric-config.h>
10 #include <glib/gi18n-lib.h>
11 #include <gnumeric.h>
12 #include <gutils.h>
13 #include <gnumeric-paths.h>
15 #include <sheet.h>
16 #include <ranges.h>
17 #include <mathfunc.h>
18 #include <workbook-view.h>
19 #include <workbook.h>
21 #include <goffice/goffice.h>
23 #include <stdlib.h>
24 #include <math.h>
25 #include <string.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <locale.h>
31 #include <gsf/gsf-impl-utils.h>
32 #include <gsf/gsf-doc-meta-data.h>
33 #include <gsf/gsf-timestamp.h>
35 #define SHEET_SELECTION_KEY "sheet-selection"
36 #define SSCONVERT_SHEET_SET_KEY "ssconvert-sheets"
38 static char *gnumeric_lib_dir;
39 static char *gnumeric_data_dir;
40 static char *gnumeric_locale_dir;
41 static char *gnumeric_usr_dir;
42 static char *gnumeric_usr_dir_unversioned;
43 static char *gnumeric_extern_plugin_dir;
44 static GSList *gutils_xml_in_docs;
46 static char *
47 running_in_tree (void)
49 const char *argv0 = g_get_prgname ();
51 if (!argv0)
52 return NULL;
54 /* Look for ".libs" as final path element. */
56 const char *dotlibs = strstr (argv0, ".libs/");
57 if (dotlibs &&
58 (dotlibs == argv0 || G_IS_DIR_SEPARATOR (dotlibs[-1])) &&
59 strchr (dotlibs + 6, G_DIR_SEPARATOR) == NULL) {
60 size_t l = dotlibs - argv0;
61 char *res = g_strndup (argv0, l);
63 while (l > 0 && G_IS_DIR_SEPARATOR (res[l - 1]))
64 res[--l] = 0;
65 while (l > 0 && !G_IS_DIR_SEPARATOR (res[l - 1]))
66 res[--l] = 0;
67 while (l > 0 && G_IS_DIR_SEPARATOR (res[l - 1]))
68 res[--l] = 0;
70 return res;
75 const char *builddir = g_getenv ("GNM_TEST_TOP_BUILDDIR");
76 if (builddir)
77 return g_strdup (builddir);
80 return NULL;
83 static gboolean gutils_inited = FALSE;
85 void
86 gutils_init (void)
88 char const *home_dir;
89 char *top_builddir;
91 // This function will end up being called twice in normal operation:
92 // once from gnm_pre_parse_init and once from gnm_init. Introspection
93 // will not get the first.
94 if (gutils_inited)
95 return;
97 #ifdef G_OS_WIN32
98 gchar *dir = g_win32_get_package_installation_directory_of_module (NULL);
99 gnumeric_lib_dir = g_build_filename (dir, "lib",
100 "gnumeric", GNM_VERSION_FULL,
101 NULL);
102 gnumeric_data_dir = g_build_filename (dir, "share",
103 "gnumeric", GNM_VERSION_FULL,
104 NULL);
105 gnumeric_locale_dir = g_build_filename (dir, "share", "locale", NULL);
106 gnumeric_extern_plugin_dir = g_build_filename
107 (dir, "lib", "gnumeric", GNM_API_VERSION, "plugins",
108 NULL);
109 g_free (dir);
110 #else
111 top_builddir = running_in_tree ();
112 if (top_builddir) {
113 gnumeric_lib_dir =
114 go_filename_simplify (top_builddir, GO_DOTDOT_SYNTACTIC,
115 FALSE);
116 if (gnm_debug_flag ("in-tree"))
117 g_printerr ("Running in-tree [%s]\n", top_builddir);
118 g_free (top_builddir);
121 if (!gnumeric_lib_dir)
122 gnumeric_lib_dir = g_strdup (GNUMERIC_LIBDIR);
123 gnumeric_data_dir = g_strdup (GNUMERIC_DATADIR);
124 gnumeric_locale_dir = g_strdup (GNUMERIC_LOCALEDIR);
125 gnumeric_extern_plugin_dir = g_strdup (GNUMERIC_EXTERNPLUGINDIR);
126 #endif
127 home_dir = g_get_home_dir ();
128 gnumeric_usr_dir_unversioned = home_dir
129 ? g_build_filename (home_dir, ".gnumeric", NULL)
130 : NULL;
131 gnumeric_usr_dir = gnumeric_usr_dir_unversioned
132 ? g_build_filename (gnumeric_usr_dir_unversioned, GNM_VERSION_FULL, NULL)
133 : NULL;
135 gutils_inited = TRUE;
138 void
139 gutils_shutdown (void)
141 GSList *l;
143 g_free (gnumeric_lib_dir);
144 gnumeric_lib_dir = NULL;
145 g_free (gnumeric_data_dir);
146 gnumeric_data_dir = NULL;
147 g_free (gnumeric_locale_dir);
148 gnumeric_locale_dir = NULL;
149 g_free (gnumeric_usr_dir);
150 gnumeric_usr_dir = NULL;
151 g_free (gnumeric_usr_dir_unversioned);
152 gnumeric_usr_dir_unversioned = NULL;
153 g_free (gnumeric_extern_plugin_dir);
154 gnumeric_extern_plugin_dir = NULL;
156 for (l = gutils_xml_in_docs; l; l = l->next) {
157 GsfXMLInDoc **pdoc = l->data;
158 gsf_xml_in_doc_free (*pdoc);
159 *pdoc = NULL;
161 g_slist_free (gutils_xml_in_docs);
162 gutils_xml_in_docs = NULL;
165 char const *
166 gnm_sys_lib_dir (void)
168 return gnumeric_lib_dir;
171 char const *
172 gnm_sys_data_dir (void)
174 return gnumeric_data_dir;
177 char const *
178 gnm_sys_extern_plugin_dir (void)
180 return gnumeric_extern_plugin_dir;
183 char const *
184 gnm_locale_dir (void)
186 return gnumeric_locale_dir;
189 char const *
190 gnm_usr_dir (gboolean versioned)
192 return versioned ? gnumeric_usr_dir : gnumeric_usr_dir_unversioned;
196 static gboolean
197 all_ascii (const char *s)
199 while ((guchar)*s < 0x7f) {
200 if (*s)
201 s++;
202 else
203 return TRUE;
205 return FALSE;
209 * Like strto[ld], but...
210 * 1. handles non-ascii characters
211 * 2. disallows 0x000.0p+00 and 0.0d+00
212 * 3. ensures sane errno on exit
214 gnm_float
215 gnm_utf8_strto (const char *s, char **end)
217 const char *p;
218 int sign;
219 char *dummy_end;
220 GString *ascii;
221 GString const *decimal = go_locale_get_decimal ();
222 gboolean seen_decimal = FALSE;
223 gboolean seen_digit = FALSE;
224 size_t spaces = 0;
225 gnm_float res;
226 int save_errno;
228 if (all_ascii (s)) {
229 res = gnm_strto (s, end);
230 goto handle_denormal;
233 ascii = g_string_sized_new (100);
235 if (!end)
236 end = &dummy_end;
238 p = s;
239 while (g_unichar_isspace (g_utf8_get_char (p))) {
240 p = g_utf8_next_char (p);
241 spaces++;
244 sign = go_unichar_issign (g_utf8_get_char (p));
245 if (sign) {
246 g_string_append_c (ascii, "-/+"[sign + 1]);
247 p = g_utf8_next_char (p);
250 do {
251 if (strncmp (p, decimal->str, decimal->len) == 0) {
252 if (seen_decimal)
253 break;
254 seen_decimal = TRUE;
255 go_string_append_gstring (ascii, decimal);
256 p += decimal->len;
257 } else if (g_unichar_isdigit (g_utf8_get_char (p))) {
258 g_string_append_c (ascii, '0' + g_unichar_digit_value (g_utf8_get_char (p)));
259 p = g_utf8_next_char (p);
260 seen_digit = TRUE;
261 } else
262 break;
263 } while (1);
265 if (!seen_digit) {
266 /* No conversion, bail to gnm_strto for nan etc. */
267 g_string_free (ascii, TRUE);
268 return gnm_strto (s, end);
271 if (*p == 'e' || *p == 'E') {
272 int sign;
274 g_string_append_c (ascii, 'e');
275 p = g_utf8_next_char (p);
277 sign = go_unichar_issign (g_utf8_get_char (p));
278 if (sign) {
279 g_string_append_c (ascii, "-/+"[sign + 1]);
280 p = g_utf8_next_char (p);
282 while (g_unichar_isdigit (g_utf8_get_char (p))) {
283 g_string_append_c (ascii, '0' + g_unichar_digit_value (g_utf8_get_char (p)));
284 p = g_utf8_next_char (p);
288 res = gnm_strto (ascii->str, end);
289 save_errno = errno;
290 *end = g_utf8_offset_to_pointer
291 (s, spaces + g_utf8_pointer_to_offset (ascii->str, *end));
292 g_string_free (ascii, TRUE);
294 errno = save_errno;
296 handle_denormal:
297 save_errno = errno;
298 if (res != 0 && gnm_abs (res) < GNM_MIN)
299 errno = 0;
300 else
301 errno = save_errno;
303 return res;
307 * Like strtol, but...
308 * 1. handles non-ascii characters
309 * 2. assumes base==10
310 * 3. ensures sane errno on exit
312 long
313 gnm_utf8_strtol (const char *s, char **end)
315 const char *p;
316 int sign;
317 char *dummy_end;
318 unsigned long res = 0, lim, limd;
320 if (!end)
321 end = &dummy_end;
323 p = s;
324 while (g_unichar_isspace (g_utf8_get_char (p)))
325 p = g_utf8_next_char (p);
327 sign = go_unichar_issign (g_utf8_get_char (p));
328 if (sign)
329 p = g_utf8_next_char (p);
330 if (sign < 0) {
331 lim = (-(unsigned long)LONG_MIN) / 10u;
332 limd = (-(unsigned long)LONG_MIN) % 10u;
333 } else {
334 lim = (unsigned long)LONG_MAX / 10u;
335 limd = (unsigned long)LONG_MAX % 10u;
338 if (!g_unichar_isdigit (g_utf8_get_char (p))) {
339 errno = 0;
340 *end = (char *)s;
341 return 0;
344 while (g_unichar_isdigit (g_utf8_get_char (p))) {
345 guint8 dig = g_unichar_digit_value (g_utf8_get_char (p));
346 p = g_utf8_next_char (p);
348 if (res > lim || (res == lim && dig > limd)) {
349 /* Overflow */
350 while (g_unichar_isdigit (g_utf8_get_char (p)))
351 p = g_utf8_next_char (p);
352 *end = (char *)p;
353 errno = ERANGE;
354 return sign < 0 ? LONG_MIN : LONG_MAX;
357 res = res * 10u + dig;
359 *end = (char *)p;
360 errno = 0;
361 return sign < 0 ? (long)-res : (long)res;
366 gnm_regcomp_XL (GORegexp *preg, char const *pattern, int cflags,
367 gboolean anchor_start, gboolean anchor_end)
369 GString *res = g_string_new (NULL);
370 int retval;
372 if (anchor_start)
373 g_string_append_c (res, '^');
375 while (*pattern) {
376 switch (*pattern) {
377 case '*':
378 g_string_append (res, ".*");
379 pattern++;
380 break;
382 case '?':
383 g_string_append_c (res, '.');
384 pattern++;
385 break;
387 case '~':
388 if (pattern[1] == '*' ||
389 pattern[1] == '?' ||
390 pattern[1] == '~')
391 pattern++;
392 /* Fall through */
393 default:
394 pattern = go_regexp_quote1 (res, pattern);
398 if (anchor_end)
399 g_string_append_c (res, '$');
401 retval = go_regcomp (preg, res->str, cflags);
402 g_string_free (res, TRUE);
403 return retval;
407 * gnm_excel_search_impl:
408 * @needle: the pattern to search for, see gnm_regcomp_XL.
409 * @haystack: the string to search in.
410 * @skip: zero-based search start point in characters.
412 * Returns: -1 for a non-match, or zero-based location in
413 * characters.
415 * The is the implementation of Excel's SEARCH function.
416 * However, note that @skip and return value are zero-based.
419 gnm_excel_search_impl (const char *needle, const char *haystack,
420 size_t skip)
422 const char *hay2;
423 size_t i;
424 GORegexp r;
426 for (i = skip, hay2 = haystack; i > 0; i--) {
427 if (*hay2 == 0)
428 return -1;
429 hay2 = g_utf8_next_char (hay2);
432 if (gnm_regcomp_XL (&r, needle, GO_REG_ICASE, FALSE, FALSE) == GO_REG_OK) {
433 GORegmatch rm;
435 switch (go_regexec (&r, hay2, 1, &rm, 0)) {
436 case GO_REG_NOMATCH:
437 break;
438 case GO_REG_OK:
439 go_regfree (&r);
440 return skip +
441 g_utf8_pointer_to_offset (hay2, hay2 + rm.rm_so);
442 default:
443 g_warning ("Unexpected go_regexec result");
445 go_regfree (&r);
446 } else {
447 g_warning ("Unexpected regcomp result");
450 return -1;
454 #if 0
455 static char const *
456 color_to_string (PangoColor color)
458 static char result[100];
459 sprintf (result, "%04x:%04x:%04x", color.red, color.green, color.blue);
460 return result;
463 static const char *
464 enum_name (GType typ, int i)
466 static char result[100];
467 GEnumClass *ec = g_type_class_ref (typ);
469 if (ec) {
470 GEnumValue *ev = g_enum_get_value (ec, i);
471 g_type_class_unref (ec);
473 if (ev && ev->value_nick)
474 return ev->value_nick;
475 if (ev && ev->value_name)
476 return ev->value_name;
479 sprintf (result, "%d", i);
480 return result;
483 static gboolean
484 cb_gnm_pango_attr_dump (PangoAttribute *attr, gpointer user_data)
486 g_print (" start=%u; end=%u\n", attr->start_index, attr->end_index);
487 switch (attr->klass->type) {
488 case PANGO_ATTR_FAMILY:
489 g_print (" family=\"%s\"\n", ((PangoAttrString *)attr)->value);
490 break;
491 case PANGO_ATTR_LANGUAGE:
492 g_print (" language=\"%s\"\n", pango_language_to_string (((PangoAttrLanguage *)attr)->value));
493 break;
494 case PANGO_ATTR_STYLE:
495 g_print (" style=%s\n",
496 enum_name (PANGO_TYPE_STYLE, ((PangoAttrInt *)attr)->value));
497 break;
498 case PANGO_ATTR_WEIGHT:
499 g_print (" weight=%s\n",
500 enum_name (PANGO_TYPE_WEIGHT, ((PangoAttrInt *)attr)->value));
501 break;
502 case PANGO_ATTR_VARIANT:
503 g_print (" variant=%s\n",
504 enum_name (PANGO_TYPE_VARIANT, ((PangoAttrInt *)attr)->value));
505 break;
506 case PANGO_ATTR_STRETCH:
507 g_print (" stretch=%s\n",
508 enum_name (PANGO_TYPE_STRETCH, ((PangoAttrInt *)attr)->value));
509 break;
510 case PANGO_ATTR_UNDERLINE:
511 g_print (" underline=%s\n",
512 enum_name (PANGO_TYPE_UNDERLINE, ((PangoAttrInt *)attr)->value));
513 break;
514 case PANGO_ATTR_STRIKETHROUGH:
515 g_print (" strikethrough=%d\n", ((PangoAttrInt *)attr)->value);
516 break;
517 case PANGO_ATTR_RISE:
518 g_print (" rise=%d\n", ((PangoAttrInt *)attr)->value);
519 break;
520 case PANGO_ATTR_FALLBACK:
521 g_print (" fallback=%d\n", ((PangoAttrInt *)attr)->value);
522 break;
523 case PANGO_ATTR_LETTER_SPACING:
524 g_print (" letter_spacing=%d\n", ((PangoAttrInt *)attr)->value);
525 break;
526 case PANGO_ATTR_SIZE:
527 g_print (" size=%d%s\n",
528 ((PangoAttrSize *)attr)->size,
529 ((PangoAttrSize *)attr)->absolute ? " abs" : "");
530 break;
531 case PANGO_ATTR_SCALE:
532 g_print (" scale=%g\n", ((PangoAttrFloat *)attr)->value);
533 break;
534 case PANGO_ATTR_FOREGROUND:
535 g_print (" foreground=%s\n", color_to_string (((PangoAttrColor *)attr)->color));
536 break;
537 case PANGO_ATTR_BACKGROUND:
538 g_print (" background=%s\n", color_to_string (((PangoAttrColor *)attr)->color));
539 break;
540 case PANGO_ATTR_UNDERLINE_COLOR:
541 g_print (" underline_color=%s\n", color_to_string (((PangoAttrColor *)attr)->color));
542 break;
543 case PANGO_ATTR_STRIKETHROUGH_COLOR:
544 g_print (" strikethrough_color=%s\n", color_to_string (((PangoAttrColor *)attr)->color));
545 break;
546 case PANGO_ATTR_FONT_DESC: {
547 char *desc = pango_font_description_to_string (((PangoAttrFontDesc*)attr)->desc);
548 g_print (" font=\"%s\"\n", desc);
549 g_free (desc);
550 break;
552 default:
553 g_print (" type=%s\n", enum_name (PANGO_TYPE_ATTR_TYPE, attr->klass->type));
556 return FALSE;
559 void
560 gnm_pango_attr_dump (PangoAttrList *list)
562 g_print ("PangoAttrList at %p\n", list);
563 pango_attr_list_filter (list, cb_gnm_pango_attr_dump, NULL);
565 #endif
568 static gboolean
569 cb_gnm_pango_attr_list_equal (PangoAttribute *a, gpointer _sl)
571 GSList **sl = _sl;
572 *sl = g_slist_prepend (*sl, a);
573 return FALSE;
577 * This is a bit of a hack. It might claim a difference even when things
578 * actually are equal. But not the other way around.
580 gboolean
581 gnm_pango_attr_list_equal (PangoAttrList const *l1, PangoAttrList const *l2)
583 if (l1 == l2)
584 return TRUE;
585 else if (l1 == NULL || l2 == NULL)
586 return FALSE;
587 else {
588 gboolean res;
589 GSList *sl1 = NULL, *sl2 = NULL;
590 (void)pango_attr_list_filter ((PangoAttrList *)l1,
591 cb_gnm_pango_attr_list_equal,
592 &sl1);
593 (void)pango_attr_list_filter ((PangoAttrList *)l2,
594 cb_gnm_pango_attr_list_equal,
595 &sl2);
597 while (sl1 && sl2) {
598 const PangoAttribute *a1 = sl1->data;
599 const PangoAttribute *a2 = sl2->data;
600 if (a1->start_index != a2->start_index ||
601 a1->end_index != a2->end_index ||
602 !pango_attribute_equal (a1, a2))
603 break;
604 sl1 = g_slist_delete_link (sl1, sl1);
605 sl2 = g_slist_delete_link (sl2, sl2);
608 res = (sl1 == sl2);
609 g_slist_free (sl1);
610 g_slist_free (sl2);
611 return res;
615 /* ------------------------------------------------------------------------- */
617 struct _GnmLocale {
618 char *num_locale;
619 char *monetary_locale;
622 * gnm_push_C_locale: (skip)
624 * Returns the current locale, and sets the locale and the value-format
625 * engine's locale to 'C'. The caller must call gnm_pop_C_locale to free the
626 * result and restore the previous locale.
628 GnmLocale *
629 gnm_push_C_locale (void)
631 GnmLocale *old = g_new0 (GnmLocale, 1);
633 old->num_locale = g_strdup (go_setlocale (LC_NUMERIC, NULL));
634 go_setlocale (LC_NUMERIC, "C");
635 old->monetary_locale = g_strdup (go_setlocale (LC_MONETARY, NULL));
636 go_setlocale (LC_MONETARY, "C");
637 go_locale_untranslated_booleans ();
639 return old;
643 * gnm_pop_C_locale: (skip)
644 * @locale: #GnmLocale
646 * Frees the result of gnm_push_C_locale and restores the original locale.
648 void
649 gnm_pop_C_locale (GnmLocale *locale)
651 /* go_setlocale restores bools to locale translation */
652 go_setlocale (LC_MONETARY, locale->monetary_locale);
653 g_free (locale->monetary_locale);
654 go_setlocale (LC_NUMERIC, locale->num_locale);
655 g_free (locale->num_locale);
656 g_free (locale);
659 /* ------------------------------------------------------------------------- */
661 gboolean
662 gnm_debug_flag (const char *flag)
664 GDebugKey key;
665 key.key = (char *)flag;
666 key.value = 1;
668 return g_parse_debug_string (g_getenv ("GNM_DEBUG"), &key, 1) != 0;
671 /* ------------------------------------------------------------------------- */
673 void
674 gnm_string_add_number (GString *buf, gnm_float d)
676 size_t old_len = buf->len;
677 double d2;
678 static int digits;
680 if (digits == 0) {
681 gnm_float l10 = gnm_log10 (FLT_RADIX);
682 digits = (int)gnm_ceil (GNM_MANT_DIG * l10) +
683 (l10 == (int)l10 ? 0 : 1);
686 g_string_append_printf (buf, "%.*" GNM_FORMAT_g, digits - 1, d);
687 d2 = gnm_strto (buf->str + old_len, NULL);
689 if (d != d2) {
690 g_string_truncate (buf, old_len);
691 g_string_append_printf (buf, "%.*" GNM_FORMAT_g, digits, d);
695 /* ------------------------------------------------------------------------- */
697 void
698 gnm_insert_meta_date (GODoc *doc, char const *name)
700 GValue *value = g_new0 (GValue, 1);
701 GTimeVal tm;
702 GsfTimestamp *ts = gsf_timestamp_new ();
704 g_get_current_time (&tm);
705 tm.tv_usec = 0L;
707 gsf_timestamp_set_time (ts, tm.tv_sec);
708 g_value_init (value, GSF_TIMESTAMP_TYPE);
709 gsf_timestamp_to_value (ts, value);
710 gsf_timestamp_free (ts);
712 gsf_doc_meta_data_insert (go_doc_get_meta_data (doc),
713 g_strdup (name),
714 value);
717 /* ------------------------------------------------------------------------- */
720 * gnm_object_get_bool:
721 * @o: #GObject
722 * @name: property name
724 * Returns: the value of @o's boolean property @name.
726 gboolean
727 gnm_object_get_bool (gpointer o, const char *name)
729 gboolean b;
730 g_object_get (o, name, &b, NULL);
731 return b;
735 * gnm_object_has_readable_prop:
736 * @obj: #GObject
737 * @property: property name
738 * @typ: property's type or %G_TYPE_NONE. (Exact type, not is-a.)
739 * @pres: (out) (optional): location to store property value.
741 * Returns: %TRUE if @obj has a readable property named @property
742 * of type @typ.
744 gboolean
745 gnm_object_has_readable_prop (gconstpointer obj, const char *property,
746 GType typ, gpointer pres)
748 GObjectClass *klass;
749 GParamSpec *spec;
751 if (!obj)
752 return FALSE;
754 klass = G_OBJECT_GET_CLASS (G_OBJECT (obj));
755 spec = g_object_class_find_property (klass, property);
756 if (!spec ||
757 !(G_PARAM_READABLE & spec->flags) ||
758 (typ != G_TYPE_NONE && spec->value_type != typ))
759 return FALSE;
761 if (pres)
762 g_object_get (G_OBJECT (obj), property, pres, NULL);
763 return TRUE;
766 /* ------------------------------------------------------------------------- */
768 gint
769 gnm_float_equal (gnm_float const *a, const gnm_float *b)
771 return (*a == *b);
774 guint
775 gnm_float_hash (gnm_float const *d)
777 int expt;
778 gnm_float mant = gnm_frexp (gnm_abs (*d), &expt);
779 guint h = ((guint)(0x80000000u * mant)) ^ expt;
780 if (*d >= 0)
781 h ^= 0x55555555;
782 return h;
785 /* ------------------------------------------------------------------------- */
787 struct cb_compare {
788 GnmHashTableOrder order;
789 gpointer user;
792 static gint
793 cb_compare (gconstpointer a_, gconstpointer b_, gpointer user_data)
795 struct cb_compare *user = user_data;
796 gpointer *a = (gpointer )a_;
797 gpointer *b = (gpointer )b_;
799 return user->order (a[0], a[1], b[0], b[1], user->user);
804 * gnm_hash_table_foreach_ordered:
805 * @h: Hash table
806 * @callback: (scope call): #GHFunc
807 * @order: (scope call): Ordering function
808 * @user: user data for callback and order
810 * Like g_hash_table_foreach, but with an ordering imposed.
812 void
813 gnm_hash_table_foreach_ordered (GHashTable *h,
814 GHFunc callback,
815 GnmHashTableOrder order,
816 gpointer user)
818 unsigned ui;
819 GPtrArray *data;
820 struct cb_compare u;
821 GHashTableIter hiter;
822 gpointer key, value;
824 /* Gather all key-value pairs */
825 data = g_ptr_array_new ();
826 g_hash_table_iter_init (&hiter, h);
827 while (g_hash_table_iter_next (&hiter, &key, &value)) {
828 g_ptr_array_add (data, key);
829 g_ptr_array_add (data, value);
832 /* Sort according to given ordering */
833 u.order = order;
834 u.user = user;
835 g_qsort_with_data (data->pdata,
836 data->len / 2, 2 * sizeof (gpointer),
837 cb_compare,
838 &u);
840 /* Call user callback with all pairs */
841 for (ui = 0; ui < data->len; ui += 2)
842 callback (g_ptr_array_index (data, ui),
843 g_ptr_array_index (data, ui + 1),
844 user);
846 /* Clean up */
847 g_ptr_array_free (data, TRUE);
850 /* ------------------------------------------------------------------------- */
852 void
853 gnm_xml_in_doc_dispose_on_exit (GsfXMLInDoc **pdoc)
855 gutils_xml_in_docs = g_slist_prepend (gutils_xml_in_docs, pdoc);
858 /* ------------------------------------------------------------------------- */
861 * gnm_file_saver_get_sheet:
862 * @fs: #GOFileSaver
863 * @wbv: #WorkbookView
865 * For a single-sheet saver, this function determines what sheet to save.
867 * Returns: (transfer none): the sheet to export
869 Sheet *
870 gnm_file_saver_get_sheet (GOFileSaver const *fs, WorkbookView const *wbv)
872 Workbook *wb;
873 GPtrArray *sel;
875 g_return_val_if_fail (GO_IS_FILE_SAVER (fs), NULL);
876 g_return_val_if_fail (go_file_saver_get_save_scope (fs) ==
877 GO_FILE_SAVE_SHEET, NULL);
878 g_return_val_if_fail (GNM_IS_WORKBOOK_VIEW (wbv), NULL);
880 wb = wb_view_get_workbook (wbv);
882 sel = g_object_get_data (G_OBJECT (wb), SHEET_SELECTION_KEY);
883 if (sel) {
884 if (sel->len == 1)
885 return g_ptr_array_index (sel, 0);
886 g_critical ("Someone messed up sheet selection");
889 return wb_view_cur_sheet (wbv);
893 * gnm_file_saver_get_sheets:
894 * @fs: #GOFileSaver
895 * @wbv: #WorkbookView
896 * @default_all: If %TRUE, all sheets will be selected by default; if %FALSE,
897 * this function will return %NULL if no sheets were explicitly selected.
899 * This function determines what sheets to save.
901 * Returns: (transfer container) (element-type Sheet): the sheets to export
903 * Note: the return value should be unreffed, not freed.
905 GPtrArray *
906 gnm_file_saver_get_sheets (GOFileSaver const *fs,
907 WorkbookView const *wbv,
908 gboolean default_all)
910 Workbook *wb;
911 GPtrArray *sel, *sheets;
912 GOFileSaveScope save_scope;
914 g_return_val_if_fail (GO_IS_FILE_SAVER (fs), NULL);
915 g_return_val_if_fail (GNM_IS_WORKBOOK_VIEW (wbv), NULL);
917 save_scope = go_file_saver_get_save_scope (fs);
918 wb = wb_view_get_workbook (wbv);
919 sel = g_object_get_data (G_OBJECT (wb), SHEET_SELECTION_KEY);
920 sheets = g_object_get_data (G_OBJECT (wb), SSCONVERT_SHEET_SET_KEY);
921 if (sel)
922 g_ptr_array_ref (sel);
923 else if (sheets)
924 sel = g_ptr_array_ref (sheets);
925 else if (save_scope != GO_FILE_SAVE_WORKBOOK) {
926 sel = g_ptr_array_new ();
927 g_ptr_array_add (sel, wb_view_cur_sheet (wbv));
928 } else if (default_all) {
929 int i;
930 sel = g_ptr_array_new ();
931 for (i = 0; i < workbook_sheet_count (wb); i++) {
932 Sheet *sheet = workbook_sheet_by_index (wb, i);
933 g_ptr_array_add (sel, sheet);
937 return sel;
940 gboolean
941 gnm_file_saver_common_export_option (GOFileSaver const *fs,
942 Workbook const *wb,
943 const char *key, const char *value,
944 GError **err)
946 if (err)
947 *err = NULL;
949 g_return_val_if_fail (GO_IS_FILE_SAVER (fs), FALSE);
950 g_return_val_if_fail (GNM_IS_WORKBOOK (wb), FALSE);
951 g_return_val_if_fail (key != NULL, FALSE);
952 g_return_val_if_fail (value != NULL, FALSE);
954 if (strcmp (key, "sheet") == 0) {
955 GPtrArray *sheets;
956 Sheet *sheet = workbook_sheet_by_name (wb, value);
958 if (!sheet) {
959 if (err)
960 *err = g_error_new (go_error_invalid (), 0,
961 _("Unknown sheet \"%s\""),
962 value);
963 return TRUE;
966 sheets = g_object_get_data (G_OBJECT (wb), SSCONVERT_SHEET_SET_KEY);
967 if (!sheets) {
968 sheets = g_ptr_array_new ();
969 g_object_set_data_full (G_OBJECT (wb),
970 SSCONVERT_SHEET_SET_KEY,
971 sheets,
972 (GDestroyNotify)g_ptr_array_unref);
974 g_ptr_array_add (sheets, sheet);
976 return FALSE;
979 if (err)
980 *err = g_error_new (go_error_invalid (), 0,
981 _("Invalid export option \"%s\" for format %s"),
982 key,
983 go_file_saver_get_id (fs));
985 return TRUE;